Hack the Box - Ellingson
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.
As usual, let's start it off with an
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.
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
-m 1800 and the trusty old rockyou wordlist we manage to crack two of the hashes - for
theplague and for
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.
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
libcversion running on Ellingson.
- Find a way to leak the address using
- Calculate the offset based on leaked address to leverage
uid=0to 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.
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.
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 libmakes the exploit easier than my kludgey way of doing it.
- Explore further the functionality of
pwn liband 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.