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:

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:

We are executing commands as hal who doesn’t have the user flag:

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:

There are 4 users on the box: duke, hal, margo and theplague:

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:

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.

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:

So we have a buffer overflow, I downloaded the binary on my machine:

checksec shows that NX (non-executable stack) is enabled so we will do a ret2libc attack:

ASLR is enabled so will need to get around that:

I downloaded libc from the box:

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):

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:

Then we need to load the binary file and libc in the exploit:

We can use the ROP() function to load the rop gadgets from the binary:

exploit.py:

I created a function called leak() that takes 4 arguments (the process, the loaded binary file, libc and the rop gadgets):

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:

Now we can simply construct the payload:

Let’s test it:
exploit.py:

It’s working fine, to calculate the address of libc we will simply subtract libc.sym["__libc_start_main"] from the leaked address:

Exploitation: ret2libc

Similar to the function leak() I created another function called shell() that takes the same arguments as leak():

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:

Then we will construct the payload:

And finally we will send the payload and switch into interactive mode:

Let’s test it:
exploit.py:

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:

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:

Then we will construct the payload and send it:

We will call this function before calling shell() so our final exploit will be:
exploit.py:

And we owned root !
That’s it , Feedback is appreciated !