NullByte CTF – Walk Through

This is a writeup of the NullByte CTF challenge which can be found on VulnHub.

I really wasn’t sure what to do next after the last challenge, but this one looked as good as any!

I ultimately headed down the slightly wrong path at the end here, but I learned a lesson from that in itself. Also I learned about manual, blind SQL Injection rather than using SQLMap to do all the dirty work, so that was nice.


VirtualBox VM

Like the Minotaur CTF challenge, an error on first boot was encountered. Easily resolved by changing the network interface to my local PenTest network.

Finding the box.

Not as exciting as what happens in most clubs on a Saturday night, but just a good-old-nmap scan.


Here she be! We’ll take her home, and probe her gently.

HTTPD running on the standard Port 80, RPCBIND running on 111, so there may be a way to get a file onto the VM using NFS, and SSH running on 777 – a curious port number choice. The correlation between the port number and a completely readable/writeable/executable file isn’t lost on me – however 🙂


Connecting to HTTPD reveals the following web page.


So, not much to go on. Haven’t yet check the metadata on that image, but nothing in the HTML source.

Scanning HTTPD

A dirb scan reveals a couple of potential entry points!




A couple of directories, ‘javascript’ and ‘uploads’, neither of which are especially interesting but the ‘uploads’ directory responds with a non-Apache standard message so that may be worthy of further investigation.

PHPMyAdmin investigation

Dirb turned up a HTML docs directory which reveals the version of the PMA installation; 4.2.12.

Searchsploit turns up one item of interest, but this is a denial of service. Unsure if that will be of help.httpd_phpmyadmin_version

From here, I wasn’t sure which path to take. I did a number of scans of the website further to the basic one above which turned up PMA; following the text on the page I searched Google for the text “laws of harmony” and scraped the content into a wordlist with Cewl.

That also turned up nothing I didn’t already know about.

Then, before heading to bed, I lined up Hydra against SSH running on port 777 to try and brute force, using the scraped word list from the Google search result above. This resulted in errors in the morning stating the SSH server does not support password auth – unsure why this took 227250 tries to tell me though!


During an especially poor night with my 2yo daughter, I remembered the tips from the NullByte CTF description:

Hints: Use your lateral thinking skills, maybe you’ll need to write some code.

So, I need to think outside the box, and this is the area I need to practice most if I want to succeed in this area. At first I thought to myself that perhaps there is a data hidden in the ‘non-Apache’ error page, but that wasn’t the case.

But then I remembered the image on the front page, and that it could very well have EXIF data attached.


Success! A hint. Try to browse to the directory, first:


The source indicates this is a very simple form:


First guess at that would be to use the string ‘kzMb5nVYJw’. No joy. This looks like a job for Hydra! First, connect Firefox to Burp, and capture:


Hydra was easy to setup and discovered the password in moments. It sure helps when it’s dictionary based 🙂


The login ‘1234’ was just a furfie added to keep Hydra happy. It wasn’t sent to the application as you can possibly see (under that damn dark square that xfce4-screenshooter gives me about 2/3rds of the time!)

Using that in the web browser gives us:


But, regardless of what I put in the box, so far at least, the result is ‘Fetched data successfully’. As it’s apparently searching usernames (but apparently fails at that, unless this box truly doesn’t have a ‘root’ account), that could be vulnerable to a shell code injection, perhaps?

Injecting a couple of shell commands in which failed, but then an error was encountered. Ahh! SQLi!

With a basic injection of “; an error is returned which reveals that a wildcard search is taking place:


This is looking for any user that starts with the search string. We can poison that easily, by searching for %, effectively this should match all users, and then return all results in the database:



So, now we have some intel I guess it’s called in the infosec world. What can we do with it? We can try these against the PMA interface, and try to get full access that way, or perhaps there is an SSH user for each of these.

Again, I’m not exactly sure which way is the most likely to get a result so we’ll try both avenues.

Throwing in some further SQLi reveals the database is named ‘seth’:


So, we’ll probe a little more with some UNIONS; also extended the size of the field with firefox developer tools to make it easier to use 🙂

Actual string used:

" UNION SELECT null as id, null as username, TABLE_NAME  FROM information_schema.tables WHERE table_schema='seth' -- "%

Note the starting quote, this is important.




So, that appears to mean that the table is named users, and is the only table in the database ‘seth’.

All very exciting, but it’s not exactly a shell now, is it. I wonder if there are passwords in this table?

SQL used:

" UNION SELECT null, user, pass FROM users -- "%

Quote at the start important, once again.


BANG, MOTHERFUCKER! (Sorry. My first manual SQLi that did something meaningful, I’m happy, ok?!)


Now we have something! A password hash. I would guess it’s a MySQL password hash, but let’s just try it as it is. It could even be plaintext.

Nope. No such luck with the PMA interface.

A search on reveals that is the plaintext for the md5 hash “c779332c18e4be252532f0b72858a9af” which is interesting, but not particularly useful as far as I can tell at the moment. In my Google search I also saw some results which relate to this specific CTF – I’ve not looked at any other walkthroughs to this point, so I skipped those results, but I guess that means I’m on the right path!

But, what do do with this hash? The length is too long to be MD5, so initially I thought this could be SHA1. SHA2, perhaps. Hashcat threw an error when I tried to use it as a SHA1. That lead me to check the hash a little more closely: the string is 43 bytes long, which is 344 bits; that doesn’t sound like a very common hash. Could it simply be base64 encoded?


PHP seems to think so. A 32 bit hash is likely going to be MD5.

MD5 Cracker agrees:


Alternatively, would it be possible to simply insert our own user into that table with SQLi? That may be possible, and even required, but now that we know the password we may not need to head down that path.

Does the PMA login now work? No.. No it doesn’t.

SSH? Yes!


Now that I’m on the box, look around a little.

CD to ~, check the .bash_history. I can see some interesting files being listed in the history.


That setuid file is a binary, so modifying it is out of my reach skill-wise at the moment. Perhaps it’s called from cron?

Tried the sudo -s, no dice, tried su eric using ramses password, no dice.

I also notice ImageMagick is on the box, so given the age it’s probably vulnerable to ImageTragick, but it appears to only have the core libraries for the PHP module installed, rather than the whole of ImageMagick. We’ll move on.

While enumerating, I note ‘bob’ has quite a lot of groups and permissions than the ramses user. We should try to get to that account, as it likely has sudo access to root.

Lets inspect those PHP files that allowed the SQL injection some more:


So now we have the MySQL root password.


So that only confirms what we already really new. Lets switch back to the mysql database and see what else we can find.


So, nothing we don’t really already know, except for that debian system maintenance password.

Nothing of note in the phpmyadmin database, the users table is empty..

SO, back to that setuid binary in /var/www/backup. Called procwatch, it does something with /proc. I know, I’m a fucking genius.

Anyway, this I think is the way forward. When it’s executed it prints 3 rows, one of which is a shell. So that’s our way in. But, how?

At this point I have done some research on using a setuid binary to escalate to a root shell. I found one good resource which has helped, but it doesn’t exactly apply to this situation unfortunately. However, it’s made me think about it more, and that’s a good thing.

I ran strings across the procwatch, with nothing of particular interest.

So, now I’ve copied the binary to /tmp and decompiled. Well. Assembly language. I probably should have looked at this before today 🙂


Anyway, now to pour though this and find where, and how, it’s calling ‘sh’ and to see if there is a way I can trick the program into providing a sweet, juicy root shell 😀

So now that I’m back at it, I’m really not exactly sure what’s happening. I’ve been a PHP ‘dev’ for the best part of 15 years. We’re not in Kansas anymore! (office space quote!)

"We're not in Kansas anymore."

Yeah. Really. (laughs

It's on your - (points

Oh! That's, uh, that's uh, my pieces of flair.

I understand this is assembler, which is very very low level. Reading the code I’m seeing some things which I recognise ‘call’ with a pointer, mov, jmp, it all kind of makes sense at a low level.  But, some of the calls are different, like: ‘call 80482d0 <system@plt>’ which I believe is telling the OS to call the code at memory address 80482d0. Googling for system@plt returns a Wikipedia article which talks about the procedure linkage table, and using system@plt as an advanced ‘return-to-plt’ attack to return the execution of the program to a location in the binary. Well. That was my understanding of it; being new to Assembler I’m not sure exactly.

Also, on Wikipedia I note:

“ASCII armoring” is a technique that can be used to obstruct this kind of attack. With ASCII armoring, all the system libraries (e.g. libc) addresses contain a NULL byte (0x00). This is commonly done by placing them in the first 0x01010100 bytes of memory (around 16 MB, dubbed the “ASCII armour region”), as every address up to this value contains at least one NULL byte.

Which is the name of this CTF challenge, obviously, so I recon we’re on the right path 🙂

ASLR is enabled in the VM also, and according to the article this makes an attack of this type unlikely to succeed. But. This is i686. Not amd64. So, ASLR apparently doesn’t matter much here. We’re not licked yet!

So, some further reading on ‘bypassing ASLR part 1‘ shows more information that this appears to be the right path to head down. Righto then. Head down, lets get those hands dirty!

Unfortunately there is no GDB on this system, but I’ve copied that binary to my Kali box where I do have gdb installed. So, following the guide mentioned above I can load the binary into GDB, then disassemble main:


Ok, we have the address of the system@plt call. From what I understand, ‘system’ is the actual function to be called, @plt is the procedure linking table.

Here I did a bunch of reading, as most of this is unlike anything I’ve done before. However, another great guide came up in my searching here over on Trustwave.

So, to find the correct memory addresses to build the string to execute and get a shell, I cloned the ROPgadget GitHub repository:


And as you can see we managed to find, oh, wrong fucking system. They’ll be different on the target VM I guess.

Specifically, all the examples of ROP and ret2libc style exploits I’ve been reading about all have the source for the vulnerable code, and thus can tailor the exploit, to use the exact right size for the start of the payload, and then where to add the memory address to jump to. I don’t have the source (and I won’t in future I’m sure) but for my first ROP I would at least like to have the details of the source and then compile, see what ASM it generates, change the code, read the ASM etc, to get an idea of how the buffer size for a variable for example is defined, and how to read that from pure ASM. I don’t even know if this binary accepts a command line argument, or how to find that out from the ASM. Or, it could be I’m just misunderstanding this part of it, and perhaps I don’t need to care. I’m not sure.

As one last try, I tried to strace the binary, but this didn’t work. Strace isn’t on the VM, I don’t have root so can’t install it, and procwatch doesn’t work on my Kali box, I guess being a 32 bit binary.


So.. this was my first attempt at ROP. Having never seen Assembler until a few hours ago, I think I did OK and it certainly wasn’t as scary as I thought it might be. At this point I think I don’t have any other option but to look at a walk though to knock, hopefully, this last step off.

After walkthrough:

Soo. I had a look at a walkthrough, and I was on kind of the wrong path at the end there. I didn’t even think that someone would write a binary to execute another shell command, but here we are.



So, box rooted, but it was easier than I expected in the end 🙂 Did I try too hard, heading down the ROP path? 🙂