Introduction
Ellingson was a very interesting box personally. Marked as hard by Hackthebox it involved web enumeration and python console abuse for initial foothold, finding sensitive backup files and hashcat cracking for user pivot, finally into a ROP based overflow exploit for root priviledge escalation. Ellingson was the second ROP based exploit machine I've worked on within HTB. The first machine, still an active box and will remain unnamed until that writeup is released, was more simplistic than Ellingson and acted as a good base to understand the concepts. This machine required more advanced tactics involving a ret2libc approach and while I developed the exploit manually for the first machine I took the opportunity here to work with a more advanced framework - python's pwn lib.
Initial Recon
As usual, let's start it off with an nmap
enumeration.
Nothing out of the usual so let's take a look at the web service and see what we can find. Looks like a company page. Before we start poking around manually we make sure to kick off burp and get a better idea of the layout.
After taking a look at the various pages, there was some interesting information on the various articles. There was some discussion about previous security incidents and some common password warnings.
It seems we're loading the various articles by just going to the reference page number url. Let's see what happens when we try to go to a page that doesn't exist like "4".
Ok some error messages and leakage indicating that at least python is installed on the machine. While scrolling through the error messages I accidentally moved my over to the side and noticed a terminal icon pop up. I thought that seemed quite weird, so looking further into it I quickly realized this was actually a python console, running as the user hal!
With some initial poking around I realized that there were four users (or at least for homedirectories). Since we were running as hal we had access to his homedir. Unfortunately, no user flag present there, however we did have access to his .ssh
folder and subsequently authorized_keys
. Let's add our own in there and see if we can get access properly through ssh.
And with that we now have proper shell access.
User exploitation
Since hal wasn't the true "user" in this case we know we still need to pivot to one of the remaining users. As as first step let's get some recon scripts over to the host.
Unfortunately nothing really pops out as a result of running it so back to the drawing board. After scratching my head for a bit I realized something that was right in my face from the initial shell access.
That's right, hal is also part of the adm group. Now what does that give us as far as information.
Just like that looks like we have the password hashes for the four users! Time to format a file and see if we can crack this with hashcat
.
Kicking off hashcat
using -m 1800
and the trusty old rockyou wordlist we manage to crack two of the hashes - for theplague
and for margo
.
I tried ssh'ing in as theplague
first, unfortunately that password seemed to have been changed since the backup was taken. Alright, let's give margo
a chance.
Bam! User in the books, let's move on to root.
Root exploitation
Having moved the privesc scripts over from the previous pivot I decided to rerun them and see if anything else comes up now that we are running as margo
. This time I noticed a process binary garbage
I wasn't familiar with. Looks like it also has SUID set. A quick search online didn't return anything for /usr/bin/garbage
. That seems promising, let's take a closer look.
Hum...ok so need to supply a password. Let's see if we can overflow it.
SUID set, check. Overflowable, check. I think we have ourselves an attack vector. For ease of investigation let's grab a copy of the binary and bring it over to our own machine. Before diving into gdb
I opened up ghidra
to see what can be found with a decompile. We see the password it is searching for in the clear.
Now, let's try the password and see if we get any further.
Enter the ROP
While interesting, the process itself doesn't seem to do anything useful. Let's go back to our local version and attach gdb
this time with a unique pattern created to perform the overlfow.
So we know that the exact offset is 136 and from then on we control the stack. I initially forgot to what types of restrictions the binary had. Let's quickly do that as well.
Alright so the stack is non-executable. Since we control the stack and NX is set this seems like ROP territory. Unfortunately looking at the ghidra
decompile there doesn't seem to be anything locally sourcable like system()
within the binary. Last check, with our margo
console let's see if ASLR is set - sure enough it is.
Our understanding so far:
- ASLR is set - code from external libraries will not always be at the same memory address.
- We control the stack.
- NX is set - we cannot put executable code onto the stack.
- No locally sourced functions within the binary seem interesting.
While there aren't any system()
style calls we can leverage (i.e. that would be at the same address offsets we can call) there are several puts()
calls being made. Since this is part of libc
we may be able to leverage this to our advantage. From having ASLR set unfortunately we know that the library will load at a different address each time. Although the adress will be randomized, it's only the base that is randomized. Since we are calling puts()
we will have insight into libc
. If we can get an exact copy of the version being used, we can use the puts()
offset to calculate the base address of libc
and ultimately be able to call any function within it. With that said, our approach comes down to:
- Get a copy of the
libc
version running on Ellingson. - Find a way to leak the address using
puts()
. - Calculate the offset based on leaked address to leverage
system()
. - Add
system()
asuid=0
to our ROP chain.
The first part was simple enough. As margo
we were able to copy the libc
library on to our local machine. Combined with the garbage
binary we have everything we need to try and get this to work.
At this point I could continue to develop the exploit manually. Since I did that with the other HTB machine I wanted to explore other ways of getting this done. During my research I saw several references to python's pwn lib
library and decided I would take the opportunity to learn how to use it. pwn lib intro and pwn lib ROP reference documentation was invaluable in understanding how the library worked.
PWN lib
Let's get the initial template setup. We have margo
's password so let's connect to the host and call the binary.
So far so good, let's start adding the ROP section. Our initial effort is to call puts()
and leak the address. We find a pop rdi,ret
gadget then add libc's puts
address from the global offset table to the stack, then call puts()
to print the address. Finally we call the main function to loop the execution flow back and avoid having the binary break.
Perfect. Now let's see if we can print out the address and run it a few times to see it in action.
Great! Now that we have the offsetted puts()
address we can come up with our second ROP chain, this time hopefully to execute system()
and pop our root shell.
And with that, Ellingson is in the books.
Extra notes
After the pain of manually developing the ROP exploit for the other active box I was shook at how easy and intuitive pwn lib
really was. I definitely suggest going through both approaches to understand the concepts at a deeper level but once you understand how it works this library definitely helps speed up the process.
Personally I'm giving myself a few follow ups from having this box completed:
- Go back to the other ROP exploit and see if using
pwn lib
makes the exploit easier than my kludgey way of doing it. - Explore further the functionality of
pwn lib
and what else it can offer / become more familiar with the various uses. - Follow up my first buffer overflow presentation with a second one covering ROP and more advanced topics.
Thanks folks. Until next time.