Prevence injection
- Injection útoky, zejména SQL injection, jsou velmi časté
- Pouze malé množství aplikací je vyvíjeno vlastními silami společnosti
- Většina aplikací pochází z externích zdrojů - open source
- Open source aplikace dávají možnost opravit problémy, ale aplikace s uzavřeným zdrojovým kódem ne
- Ty vyžadují jiný přístup k chybám
- K chybám dochází, když aplikace odešle interpretu nedůvěryhodná data
- Tyto chyby jsou velmi rozšířené ve starším kódu
- Často se vyskytují v SQL dotazech, LDAP dotazech, XPath dotazech nebo OS příkazech, či argumentech programů
- Chyby lze snadno odhalit při zkoumání kódu (code review)
- Jsou však obtížně odhalitelné prost řednictvím testování
- Útočník je může najít pomocí skenerů nebo fuzzerů
- Vždy je nejlepší opravit problém v samotném kódu, případně předělat některé části aplikace
- Pokud zdrojový kód není k dispozici nebo je oprava staršího kódu neekonomická, má smysl pouze virtuální oprava
Formy injection zranitelnosti
Dotazovací jazyky
- Nejznámější formou injection je SQL injection
- Útočník má možnost modifikovat existující dotazy do databáze
- Více informací najdete v SQL injection cheat sheetu
- Dotazy založené na LDAP, SOAP, XPath a REST mohou být také náchylné k injection útokům
SQL injection
- Útok spočívá ve vložení částečného nebo úplného SQL dotazu prostřednictvím vstupních dat nebo dat přenášených z prohlížeče do webové aplikace
- Úspěšný útok může získat citlivá data z databáze, měnit ji nebo ji smazat, případně provádět další možné databázové operace
- Cílem útoků je ovlivnit provádění předem definovaných SQL dotazů
- Útoky lze rozdělit do následujících tříd
- Inband
- Data jsou extrahována stejným kanálem, který se používá k injektování SQL kódu
- Jde o nejjednodušší druh útoku
- Data jsou zobrazena přímo na webu aplikace
- Out-of-band
- Data jsou získána jiným kanálem (např. je vygenerován email s výsledky dotazu, který je odeslán útočníkovi)
- Inferential nebo Blind
- Nedochází ke skutečnému přenosu dat
- Útočník je schopen rekonstruovat informace odesláním konkrétních požadavků a pozorováním chování databázového serveru
- Inband
Jak zranitelnost testovat
- Dělejte code review
- Zkontrolujte, zda jsou dotazy do databáze prováděny prostřednictvím prepared statemenets
- Zkontrolujte, zda jsou data před použitím v dotazu sanitizována, pokud jsou prováděny dynamické dotazy
- Soustřeďte se na použití
sp_execute
,execute
neboexec
v rámci uložených procedur (případně funkce související s používanou databází)
- Automatizovaná exploitace
- Většinu uvedených technik je možné automatizovat například pomocí SQLMap nástroje
- Použijte statickou analýzu kódu
- Injection uložené procedury
- Správně upravte uživatelský vstup při dynamickém dotazování v uložené proceduře
- Time delay exploitace
- Spočívá v odeslání injektovaného dotazu
- V případě, že je dotaz v pořádku, může útočník sledovat dobu, za kterou server odpoví
- Pokud dojde k prodlevě, může útočník předpokládat, že je dotaz v pořádku
- V následujícím příkladu útočník zjišťuje, zda je verze MySQL 5 nebo ne (server zpozdí odpověď o 10s)
http://www.example.com/product.php?id=10 AND IF(version() like '5%', sleep(10), 'false'))--
- Out of band exploitace
- Technika je užitečná, když útočník neví nic o výsledku SQL operace
- Spočívá v použití DBMS funkcí k provedení out of band připojení a doručení výsledků injektovaného dotazu jako součást požadavku na server útočníka
Jak zranitelnosti předejít
- Použijte prepared statements
- Zajišťují, že útočník nezmění záměr dotazu ani v případě, že SQL dotaz vloží sám útočník
- V bezpečném příkladu níže není možné zadat
tom' or '1'='1
, protože po jeho zadání útočník hledá uživatelské jméno tohoto tvaru
- Uložené procedury
- Rozdíl mezi prepared statements a uloženými procedurami spočívá v tom, že kód pro uloženou proceduru je definován a uložen v samotné databázi a poté je volán z aplikace
- Obě techniky mají stejnou účinnost při prevenci SQL injection
- Vyberte si přístup, který je pro vaši aplikaci nejrozumější
- Uložené procedury nejsou vždy bezpečné
- Některé konstrukce mají stejný účinek jako parametrizované dotazy, pokud jsou implementovány bezpečně
- Procedura neobsahuje žádné nebezpečné dynamické SQL
- Validujte vstupy na základě seznamu povolených položek
- Různé části SQL dotazu není možné parametrizovat - názvy tabulek, sloupců, indikátor řazení atp.
- Validujte vstup vždy před zpracováním dotazu v takovýchto situacích
- V případě názvů tabulek a sloupců je nejlepší, když tyto hodnoty pocházejí z kódu a ne z uživatelského vstupu a parametrů
- Mapujte a ověřujte hodnoty uživatelských parametrů na očekávané hodnoty v případě, že jsou vyžadovány jako vstup
- Zvažte přepsání daných částí aplikace v případě, že možnost z předchozího bodu používáte - jde o špatný návrh
- Escapujte veškerý uživatelský vstup
- Použijte tuto techniku pouze jako poslední možnost - pokud není možné použít některou z výše zmíněných možností
- Slouží k escapování uživatelského vstupu před vložením do dotazu
- Obvykle se doporučuje pro dodatečnou úpravu staršího kódu
Příklady
- Java prepared statement
PreparedStatement
je implementace parametrizovaného dotazu v jazyce Java- Prakticky všechny jazyky podporují parametrizované dotazování
// This should REALLY be validated too
String custname = request.getParameter("customerName");
// Perform input validation to detect attacks
String query = "SELECT account_balance FROM user_data WHERE user_name = ?";
PreparedStatement pstmt = connection.prepareStatement(query);
pstmt.setString(1, custname);
ResultSet results = pstmt.executeQuery();
- Java uložená procedura
CallableStatement
je implementace uložené procedury v jazyce Java- Procedura
sp_getAccountBalance
by musela být v databázi předem definována a musela by implementovat dotaz výše
// This should REALLY be validated
String custname = request.getParameter("customerName");
try {
CallableStatement cs = connection.prepareCall("{call sp_getAccountBalance(?)}");
cs.setString(1, custname);
ResultSet results = cs.executeQuery();
// Result set handling...
} catch (SQLException se) {
// Logging and error handling...
}
LDAP injection
- Útok používaný ke zneužití aplikací, které konstruují LDAP příkazy na základě uživatelského vstupu
- Pokud aplikace nesanitizuje vstup, je možné modifikovat LDAP příkazy podobně jako u SQL injection
- Útoky mohou vést k udělení oprávnění, neoprávněným dotazům a modifikaci uvnitř LDAP stromu
- LDAP útoky jsou časté kvůli dvěma faktorům
- Absence bezpečnějších parametrizovaných rozhraní pro LDAP dotazy
- Rozšířené používání LDAP protokolu k ověření uživatelů v systémech
Jak zranitelnost testovat
- Dělejte code review
- Zkontrolujte, zda jsou v dotazu na LDAP použity speciální escape znaky
- Automatizovaná exploitace
- Použijte skenovací nástroj OWASP ZAP, které má modul pro detekci LDAP injection
Jak zranitelnosti předejít
- Escapujte všechny proměnné pomocí správné LDAP encoding funkce
- LDAP ukládá názvy způsobem založeným na DN (distinguished name) - jako takový jedinečný identifikátor
cn=Richard Feynman, ou=Physics Department, dc=Caltech, dc=edu
uid=inewton, ou=Mathematics Department, dc=Cambridge, dc=com
- Někdy se používají pro přístup ke zdrojům, jako je uživatelské jméno
- Některé znaky jsou v DN považovány za speciální
\ # + < > , ; " =
včetně počáteční a koncové mezery
- Každý DN přesně ukazuje na jednu položku (jako jeden řádek v databázové tabulce)
- Pro každou položku existuje několik atributů (jako sloupců v databázi)
- Pro vyhledávání uživatelů s určitými atributy je možné využít vyhledávacích filtrů
- Filtry se zapisují v polské notaci (prefixová notace), viz níže
- Vylučte všechna nedůvěryhodná data při sestavování LDAP dotazu
- Escapujte vstup v závislosti na vyhledávacím filtru nebo DN
- LDAP ukládá názvy způsobem založeným na DN (distinguished name) - jako takový jedinečný identifikátor
(&(ou=Physics)(| (manager=cn=Freeman Dyson,ou=Physics,dc=Caltech,dc=edu)
(manager=cn=Albert Einstein,ou=Physics,dc=Princeton,dc=edu) ))
Příklady
- JAVA LDAP escapování
- Znak zpětného lomítka je literál řetězce Java a escapovací znak regulárního výrazu
public String escapeDN (String name) {
// From RFC 2253 and the / character for JNDI
final char[] META_CHARS = {'+', '"', '<', '>', ';', '/'};
String escapedStr = new String(name);
// Backslash is both a Java and an LDAP escape character,
// so escape it first
escapedStr = escapedStr.replaceAll("\\\\\\\\","\\\\\\\\");
// Positional characters - see RFC 2253
escapedStr = escapedStr.replaceAll("\^#","\\\\\\\\#");
escapedStr = escapedStr.replaceAll("\^ | $","\\\\\\\\ ");
for (int i=0 ; i < META_CHARS.length ; i++) {
escapedStr = escapedStr.replaceAll("\\\\" +
META_CHARS[i],"\\\\\\\\" + META_CHARS[i]);
}
return escapedStr;
}
public String escapeSearchFilter (String filter) {
// From RFC 2254
String escapedStr = new String(filter);
escapedStr = escapedStr.replaceAll("\\\\\\\\","\\\\\\\\5c");
escapedStr = escapedStr.replaceAll("\\\\\*","\\\\\\\\2a");
escapedStr = escapedStr.replaceAll("\\\\(","\\\\\\\\28");
escapedStr = escapedStr.replaceAll("\\\\)","\\\\\\\\29");
escapedStr = escapedStr.replaceAll("\\\\" +
Character.toString('\\u0000'), "\\\\\\\\00");
return escapedStr;
}
Skriptovací jazyky
- Všechny skriptovací jazyky mají nějakou formu volání
eval
, kter é za běhu přijímá a provádí kód - Pokud je kód tvořen nezvalidovaným a neescapovaným uživatelským vstupem, může dojít k injektáži kódu
- To útočníkovi umožní změnit logiku aplikace
- Při každém použití skriptovacího jazyka se implementace vyššího jazyka provádí pomocí nižšího jazyka (např. C)
- Pokud má skriptovací jazyk chybu v kódu, lze nasadit Null Byte Injection vektor útoku a získat přístup k dalším oblastem paměti
Operační systémy
- Technika používaná prostřednictvím webového rozhraní k provádění příkazů OS na webovém serveru
- Uživatel zadává příkazy OS prostřednictvím webového rozhraní za účelem jejich spuštění
- Takové zneužití je možné u jakéhokoli rozhraní, které není řádně sanitizováno
- Útočník tak může spouštět příkazy OS, nahrát škodlivé programy nebo získat hesla
Jak zranitelnost testovat
Dělejte code review
- Zkontrolujte, zda jsou volány
execute
metody a zda jsou jako data pro tento příkaz brány neověřené uživatelské vstupy
- Pokud na konec URL parametru, za kterým následuje příkaz OS, přidáte středník, příkaz se provede
%3B
je zakódováno a dekóduje středník- Středník je interpretován jako oddělovač příkazů
http://sensitive/something.php?dir=%3Bcat%20/etc/passwd
- Útok byl úspěšný, pokud aplikace odpoví výstupem z
/etc/passwd
souboru
- Použijte skenery webových aplikací, které injektují různé varianty příkazů a testují odpověď
- Využijte statickou analýzu kódu
Jak zranitelnosti předejít
Parametrizujte
- Použijte mechanismy, které automaticky zajistí oddělení dat a příkazů (např. uvozovky, encoding)
Validujte vstup
- Validujte všechny hodnoty příkazů a argumentů
- Existují různé stupně validace pro příkaz a jeho argumenty
- Validujte příkazy podle seznamu povolených příkazů
- Argumenty těchto příkazů validujte následovně
- Explicitně uveďte povolené argumenty a validujte je
- Použijte allow-list regulární výraz se seznamem povolených znaků a maximální délkou řetězce
- Zajistěte, aby součástí výrazu nebyly metaznaky `& | ; $ > < \ !`` a bílé znaky
- Např. následující regulární výraz povoluje malá písmena a číslice, neobsahuje metaznaky a délka je omezena na 3 - 10 znaků:
^[a-z0-9]{3,10}$
Příklad v jazyce Java
Špatné použití
- Zde je příkaz spolu s argumenty předán jako řetězec, což usnadňuje manipulaci s výrazem a vkládání škodlivých řetězců
ProcessBuilder b = new ProcessBuilder("C:\DoStuff.exe -arg1 -arg2");
Správné použití
- Zde je příkaz, kde je každý argument předáván samostatně
- To usnadňuje validaci a snižuje riziko vložení škodlivých řetězců
ProcessBuilder pb = new ProcessBuilder("TrustedCmd", "TrustedArg1", "TrustedArg2");
Map<String, String> env = pb.environment();
pb.directory(new File("TrustedDir"));
Process p = pb.start();
Síťové protokoly
- Webové aplikace často komunikují se síťovými démony (SMTP, IMAP, FTP)
- Uživatelský vstup se tak stává součástí komunikačního streamu
- Zde je také možné injektovat sekvence příkazů a zněužít tak danou relaci
Pravidla prevence injection
Proveďte správnou validaci vstupů
- Validujte vstup na základě seznamu povolených hodnot
- Nejde ale o úplnou obranu, protože mnoho aplikací vyžaduje ve vstupech speciální znaky
Použijte bezpečné API
- Upřednostňujte použití bezpečného API, které se vyhne interpretu nebo poskytne parametrizované rozhraní
- Pozor na API, jako jsou uložené procedury, která jsou parametrizovaná, ale přesto mohou způsobit injection
Escapujte uživatelský vstup
- Escapujte speciální znaky pomocí specifické syntaxe pro daný interpret, pokud není k dispozici parametrizované API rozhraní