Útok pomocí PHP Object Injection

  Exploit

Nedávno objevená zranitelnost ve WordPress 6.4 poukázala na útok pomocí PHP Object Injection (POI). Jedná se o jednu z méně známých, ale vysoce závažných bezpečnostních hrozeb v PHP aplikacích. Tento druh útoku zůstává ve stínu běžnějších hrozeb jako SQL injection nebo Cross-Site Scripting (XSS), přitom má potenciál způsobit vážné škody.

Jak k útoku dochází

Tento druh útoku vyžaduje velmi dobrou znalost cílové aplikace a všech rozšíření. Základem je znát část kódu, kde aplikace deserializuje vstupní data. Útočník se pokusí vytvořit nebo upravit serializovaný řetězec tak, aby obsahoval objekty se škodlivým účinkem.

Když je tento řetězec deserializován, může dojít k automatickému spuštění metod nebo změně vlastností, což může vést k neautorizovaným akcím, jako je přístup k databázi, změna souborů nebo dokonce spuštění škodlivého kódu.

Serializace je proces převodu datové struktury nebo objektu do formátu, který lze uchovat nebo přenášet. Deserializace je opačný proces, který převádí data zpět na původní datovou strukturu nebo objekt.

Příklad serializace

Mějme třídu User a vytvořme instanci této třídy:

class User {
    public $name;
    public $age;

    public function __construct($name, $age) {
        $this->name = $name;
        $this->age = $age;
    }
}

// Vytvoření instance User
$user = new User("Alice", 30);

Nyní serializujeme objekt $user:

$serializedUser = serialize($user);
echo $serializedUser;

Tento kód vypíše serializovaný řetězec, který reprezentuje objekt $user. Výstup by mohl vypadat takto:

O:4:"User":2:{s:4:"name";s:5:"Alice";s:3:"age";i:30;}

Příklad deserializace

Pro deserializaci tohoto řetězce zpět na objekt User použijeme funkci unserialize:

$unserializedUser = unserialize($serializedUser);

// Výpis vlastností objektu
echo $unserializedUser->name; // Alice
echo $unserializedUser->age;  // 30

Jednoduchý příklad útoku POI

Ukážeme si jednoduchý příklad, fiktivního kódu a jak proběhne útok typu POI.

Předpokládejme, že máme následující třídu Logger a skript, který deserializuje data z vstupu a očekává instanci Logger.

class Logger {
    public $logFile;

    function log($message) {
        file_put_contents($this->logFile, $message . "\n", FILE_APPEND);
    }
}

Příklad zranitelnosti

Předpokládejme, že naše aplikace neopatrně deserializuje data z neověřeného vstupu, například z GET nebo POST požadavku. Toto je velká zranitelnost.

// Nebezpečná deserializace
$data = $_GET['data'];
$logger = unserialize($data);

// Kontrola, zda je objekt Logger
if ($logger instanceof Logger) {
    // Zapisuje do logu
    $logger->log("Log záznam");
}

Provedení útoku

Útočník nyní může vytvořit serializovaný objekt, který zneužije tuto zranitelnost.

  1. Přijetí škodlivého kódu: Skript obdrží serializovaný objekt v parametru data z GET požadavku.
  2. Deserializace a aktivace: Skript deserializuje objekt a volá na něm metodu log.
  3. Následky útoku: Volání metody log způsobí, že zadaný text bude zapsán do cílového souboru (/path/to/config.php). Tím může útočník změnit konfiguraci aplikace, zapsat škodlivý kód nebo provést jiné nežádoucí akce.

Kód s útokem je serializovaný řetězec:

O:6:"Logger":1:{s:7:"logFile";s:18:"/path/to/config.php";}

Projdeme si to krok za krokem

Přijetí škodlivého kódu:

Skript obdrží škodlivý kód jako součást vstupu z uživatele, obvykle prostřednictvím HTTP GET nebo POST požadavku. V tomto příkladu je útočný kód předán jako hodnota parametru v URL, například:

http://example.tld/script.php?data=O:6:"Logger":1:{s:7:"logFile";s:18:"/path/to/config.php";}.

Deserializace škodlivého kódu:

Skript používá funkci unserialize() k deserializaci dat přijatých v proměnné $_GET['data']. Tato funkce převede serializovaný řetězec zpět na objekt PHP. V našem příkladu unserialize() vytvoří instanci třídy Logger s vlastností logFile nastavenou na /path/to/config.php.

Konkrétně, co přesně se skrývá za školdivým kódem:

O:6:"Logger":1:{s:7:"logFile";s:18:"/path/to/config.php";}
  • O:6:"Logger":1:: Indikuje, že následující data reprezentují objekt třídy Logger s jednou vlastností.
    • O znamená objekt.
    • 6 je délka názvu třídy, tedy „Logger“.
    • 1 je počet vlastností, které tento objekt má.
  • {s:7:"logFile";s:18:"/path/to/config.php";}: Popisuje vlastnosti objektu.
    • s znamená, že následující data jsou řetězec (string).
    • 7 je délka názvu vlastnosti „logFile“.
    • 18 je délka hodnoty /path/to/config.php.

Když je tento kód deserializován pomocí unserialize(), PHP vytvoří následující:

  • Instance třídy Logger.
  • Tato instance má jednu vlastnost:
    • logFile nastavenou na hodnotu „/path/to/config.php“.

Výsledkem deserializace by byl objekt Database vypadající přibližně takto:

object(Logger)#1 (1) {
  ["logFile"]=>
  string(18) "/path/to/config.php"
}

Aktivace škodlivého kódu:

Pokud skript po deserializaci volá metodu log na deserializovaném objektu, dojde k zapsání textu do souboru určeného vlastností logFile. V našem případě by metoda log zapsala záznam do souboru /path/to/config.php

Realizace útoku:

V tomto bodě se útočníkův záměr realizuje. Pokud skript skutečně volá metodu log na deserializovaném objektu, může to mít za následek přepsání nebo modifikaci důležitého souboru, což může vést k nežádoucím důsledkům.

Důsledky útoku:

Výsledkem je, že útočník dosáhl svého cíle prostřednictvím deserializovaného objektu. V závislosti na tom, co bylo v útočném kódu určeno, mohou být důsledky útoku různé – například od modifikace konfiguračního souboru, přes narušení funkčnosti aplikace, až po plnou kontrolu nad aplikací nebo serverem.

Závěr

V praxi je tedy pro realizaci útoku POP potřeba, aby aplikace obsahovala určitou logiku, která po deserializaci objektu vyvolává volání metod, a tyto metody by pak mohly být zneužity k provádění nechtěných akcí. To znamená, že zranitelnost vzniká kombinací nebezpečné deserializace a dalších aspektů aplikace, které umožňují zneužití deserializovaných objektů.

V samotném WordPress jak se ukázalo přímo zranitelnost nemusí být proveditelná, ale teprve v některém z nespočtu rozšíření už ano.

Napiš komentář