L'altro giorno mi sono imbattuto in un programma che mi permetteva di selezionare una serie di query predefinite e poi comunicava con un server per recuperare la risposta alla query selezionata. Ho iniziato a chiedermi quando mi sono reso conto che il programma non richiedeva mai una password per l'autenticazione con il server. È venuto fuori che la password era incorporata nel programma. L'autore ha spiegato che andava bene perché 1) era un file binario, quindi nessuno poteva leggerlo e 2) se fossero riusciti a leggerlo, non sarebbero stati in grado di trovare la stringa della password.
Nessuna di queste ragioni è valida e vorrei utilizzare questo post per dimostrarlo. Il mio programma di esempio scrive semplicemente la "password" sul terminale, ma l'unica differenza tra esso e un programma di produzione reale è il numero di stringhe che un utente malintenzionato dovrebbe cercare.
#include <stdio.h>
#include <string.h>
main ()
{
char password [9] = {"secret"};
printf ("This is the password: %sn", password);
}
|
| Figura 1 – Programma di base con password incorporata |
Quando viene eseguito, visualizza
x1 Questa è la password: segreto pronto 14:12:16 |
| Figura 2 – Esecuzione del programma di base |
Per visualizzare le stringhe nel modulo del programma, il nostro utente malintenzionato può utilizzare il comando strings dalla directory >system>gnu_library>bin. Il parametro -n5 limita l'output alle stringhe di 5 caratteri o più. Ho troncato l'output (. . . .) per abbreviare la visualizzazione, ma è possibile vedere che la password è una delle prime stringhe visualizzate. Avere la password in prossimità di parole chiave come "login" o "password" o di un ID utente identificabile come "admin", "root" o "sql" rende la ricerca più facile.
>system>gnu_library>bin>strings -n5 x1.pm
bind, Release 17.1.beta.be
phx_vos#
Noah_Davids.CAC
Pre-release
Questa è la password: %s
secret
s$start_c_program
_preemption_cleanup
_exit.
. . . .
|
| Figura 3 – Utilizzo del comando GNU strings per trovare tutte le stringhe più lunghe di 4 caratteri nel modulo di programma |
Se il comando strings non è disponibile, il nostro utente malintenzionato può semplicemente visualizzare il modulo del programma. Immagino che tutti noi lo abbiamo fatto per sbaglio e, anche se non è molto elegante, in questo modo viene visualizzata la password. Ho nuovamente troncato l'output e anche in questo caso è possibile vedere la password.
d x1.pm
%phx_vos#m16_mas>SysAdmin>Noah_Davids>x1.pm 11-01-13 14:13:59 mst
`00`01`00`1Abind, Release 17.1.beta.be`00`00`00`00`00`00`00`04ctp
`00`00`00`00`0
+`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`0
0phx_vos
+#`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`
00`00Noa.
. . .
+`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`C
D'`8B`C0
+`00`00`00`00Questa è la password: %s
`00`00`00`00`00`00`00secret`00`00`00`00`00`00`00`04`B0`00`00`00`00`01`00
`00`00,`
+`00`00`04`00`00`00`03@`00`FF`F8`FF`FE`00`04main`00`00`83`EC`2C`C7D$$`00.
. . .
|
| Figura 4 – Visualizzazione del modulo del programma per trovare la password |
Esistono diverse alternative all'inserimento della password nel programma.
La password può essere inserita in un file e il programma può leggere il file. Solo alle persone che hanno "necessità di eseguire" viene concesso l'accesso in lettura al file, mentre a tutti gli altri viene negato l'accesso. Ovviamente, "necessità di eseguire" potrebbe non equivalere a "necessità di conoscere la password". Inoltre, c'è sempre il rischio che qualcuno modifichi erroneamente l'accesso.
Un'alternativa è quella di conservare la stringa della password nel programma e limitare semplicemente l'accesso al programma stesso. Anche questa soluzione presenta il problema della "facilità di modifica dell'elenco degli accessi". Se il programma è conservato nella stessa directory di altri programmi che possono essere eseguiti da chiunque, la probabilità di una modifica dell'elenco degli accessi aumenta.
Infine, è possibile conservare la password nel modulo del programma, ma offuscando in qualche modo la stringa. Il programma seguente mostra un modo semplice per farlo.
#include <stdio.h>
#include <string.h>
main ()
{
char obfuscate [9] = {0x0c, 0x81, 0xe2, 0x94, 0xe6, 0x9c};
char key [9] = {127, 228, 129, 230, 131, 232, 133, 234, 135};
char password [9];
int i;
for (i = 0; i < strlen (obfuscate); i++)
password [i] = obfuscate [i] ^ key [i];
password [i] = 0x0;
printf ("This is the password: %sn", password);
}
|
| Figura 5 – Programma con stringa password offuscata |
Come puoi vedere, tutto ciò che fa è prendere i caratteri offuscati e sottoporli a un'operazione XOR con un altro insieme di caratteri chiave per creare la password. L'esecuzione del programma produce lo stesso output del primo programma.
x2 Questa è la password: segreto pronto 14:12:51 |
| Figura 6 – Esecuzione del programma offuscato |
Ma una ricerca delle stringhe non mostra nulla che assomigli alla password.
>system>gnu_library>bin>strings -n5 x1.pm bind, Release 17.1.beta.be phx_vos# Noah_Davids.CAC Pre-release Questa è la password: %s s$start_c_program _preemption_cleanup _exit. . . . |
| Figura 7 – Il comando strings non mostra più la password |
E anche se visualizzando il file verranno mostrati i caratteri della password offuscati e i caratteri chiave, è necessario sapere esattamente dove cercare per trovarli e anche come sono combinati per creare la password.
d x2.pm
%phx_vos#m16_mas>SysAdmin>Noah_Davids>x2.pm 11-01-13 14:15:27 mst
. . . .
+`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`CD'`8B`
C0`00`0
+0`00`00Questa è la password: %s
`00`00`00`00`00`00`00`0C`81`E2`94`E6`9C`00`00`00`00`00`00`00`00`00`00`7F
`E4`81`
+E6`83`E8`85`EA`87`00`00`00`00`04`B0`00`00`00`00`01`00`00`00`00`00`00`0
4`01`B0
+`00`03@`00`FF`F0`00`08`FF`FE`00`04main`00`00`83`EC`89$X`89t$T`89|$P`C
7D$L`00
|
| Figura 8 – Il modulo di visualizzazione del programma non mostra più la password riconoscibile |
Questa soluzione non è ancora perfetta, poiché il modulo del programma può essere smontato per capire come viene generata la password. Tuttavia, smontare il programma richiede una sofisticazione molto maggiore rispetto alla semplice visualizzazione del modulo del programma.
Sono indeciso se inserire la stringa della password offuscata in un file e poi proteggere il file tramite un elenco di accesso. Da un lato, se la password dovesse essere modificata, questa sarebbe una soluzione molto migliore rispetto all'inserimento della stringa della password offuscata nel programma. Dall'altro lato, sospetto che, anche se nel file sono presenti altre opzioni di configurazione, inserire la stringa nel file ridurrà il numero di potenziali stringhe di password che un utente malintenzionato potrebbe cercare. Naturalmente dovrà comunque ottenere l'accesso al file e poi capire la tecnica di offuscamento e la chiave.
Un ultimo punto: come ho generato i caratteri della password offuscata? Il bello dell'operazione XOR è che ((A XOR B) XOR B) è uguale a A, quindi ho semplicemente scritto un breve programma per prendere la password originale, eseguire l'operazione XOR sui caratteri con la mia chiave e stampare il risultato.
#include <stdio.h>
#include <string.h>
main ()
{
char password [9];
char key [9] = {127, 228, 129, 230, 131, 232, 133, 234, 135};
char newPW [9];
int i;
strcpy (password, "secret");
for (i = 0; i < strlen (password); i++)
{
newPW [i] = password [i] ^ key [i];
printf ("%xn", newPW [i]);
}
}
|
| Figura 9 – Programma per generare password offuscate |
