advances in attacking linux kernel
17 Ottobre 2006
Net & System Security
Pisa, Italia
Pierre Falda [email protected]
//
introduzione
_In questo intervento vedremo come sia possibile
manomettere un sistema linux in modalità completamente
nascosta sfruttando l’hardware sottostante come
complice.
_I concetti di base sono validi per qualsiasi sistema
operativo
_Come prerequisiti sono richieste unicamentemte un
minimo di dimestichezza col linguaggio C ed Assembly
oltre ad alcune nozioni base di sistemi operativi
Pierre Falda [email protected]
//
sommario
01// Panoramica
02// Terminologia
03// Uso ed abuso di sys call table e kmem
04// Cenni sul rilevamento
05// Caratteristiche abusabili dell’hardware
06// Abuso dei meccanismi di linux
07// conclusioni
Pierre Falda [email protected]
01// Panoramica
Pierre Falda [email protected]
//
01
panoramica
Partiremo dall’analisi generale di alcune tecniche di
manomissione già note fino ad arrivare a vedere, piu o
meno approfonditamente, come sia possibile fonderle per
ottenere un potente nuovo motore di occultamento delle
informazioni. L’esposizione avrà lo scopo di fornire
principalmente gli strumenti concettuali per
comprendere la struttura, il funzionamento nonché la
pericolosità di software di questo tipo.
Pierre Falda [email protected]
02// Terminologia
Pierre Falda [email protected]
//
02
terminologia
Syscalls: funzioni che ci offre il kernel per interfacciarci
con le sue funzionalità
Sys call table: è una tabella residente a kspace
contenente gli indirizzi delle syscall
Kmem: /dev/kmem è un character device che fornisce
un'immagine della memoria virtuale del kernel.
Interrupt: è un evento sincrono od asincrono che altera la
sequenza di istruzioni eseguite dal processore
Exception: segnale lanciato dalla CPU per segnalare un
evento eccezionale
Interrupt Handler: funzione che si fa carico della gestione
di un determinato interrupt
Pierre Falda [email protected]
//
02
terminologia
Interrupt Descriptor Table: tabella che associa ciascun
interrupt col suo interrupt handler e le relative informazioni
di supporto
Breakpoint: è una qualsiasi locazione all’interno del
programma scelta dallo sviluppatore dove l’esecuzione
del programma stesso viene fermata
Virtual File System: Il virtual file system e' un layer del
kernel che si occupa di gestire tutte le syscall legate ad un
filesystem
IA-32: Architettura dei processori intel a 32 bit
Pierre Falda [email protected]
03// Uso ed abuso di sys call table e kmem
Pierre Falda [email protected]
//
03
come viene utilizzata la sct?
Pierre Falda [email protected]
//
03
come viene utilizzata la sct?
Pierre Falda [email protected]
//
03
abuso della sys call table
La prima generazione di LKM maligni modificava la s.c.t.
...
Sys_exit
0xc0123456
Sys_fork
0xc0789101
Sys_read
0xc0112131
Sys_write
Hacked
Write
0xc0415161
0xbadc0ded
...
0xc0123456: int sys_exit(int)
…
..
0xc0789101: int sys_fork(void)
…
..
0xc0112131: int sys_read(int,void*,int)
…
..
0xc0415161: int sys_write(int,void*,int)
…
..
0xbadc0ded: int hacked_write(int,void*,void)
Pierre Falda [email protected]
//
03
abuso della sys call table
PRO:
_Estremamente semplice da realizzare
_Permette un’altissima portabilità
CONTRO:
_Semplice da rilevare
_Semplice rilevare quanto nascosto tramite questa tecnica
in quanto non si opera sulle informazioni vere e proprie
ma solo sulla loro visualizzazione.
PROGRAMMA:
_Adore
Pierre Falda [email protected]
//
03
abuso DI KMEM
_Una generazione piu recente di kernel malware ha
riadottato l’utilizzo parziale di questa tecnica per questioni
implementative e di portabilità
_/dev/kmem , come precedentemente detto, fornisce
un'immagine della memoria virtuale del kernel.
Normalmente utilizzato da X (ovvero il server grafico) puo
essere sfruttato anche per inserire dati a piacere, tramite
opportune tecniche
_L’attacco non viene piu portato a termine tramite l’ausilio
di lkm ma iniettando direttamente del codice a kernel
space
Pierre Falda [email protected]
//
03
abuso DI KMEM
PRO:
_L’attacco non viene piu portato a termine tramite l’ausilio
di lkm ma iniettando direttamente del codice a kernel
space
CONTRO:
_Lo sviluppo del relativo software è piu complesso e
laborioso
PROGRAMMA:
_Suckit
Pierre Falda [email protected]
//
03
SUCKIT
_Crea una propria sys call table contenente puntatori alle
proprie syscall maligne
_Non modifica la sys call table del sistema ma sostituisce
il suo indirizzo nello handler dell’interrupt 0x80
_Si inietta in memoria tramite kmem
Pierre Falda [email protected]
//
03
SUCKIT
ed 0d dc ba
Pierre Falda [email protected]
badc0ded
04// Cenni sul rilevamento
Pierre Falda [email protected]
//
04
cenni sul rilevamento
_Alcuni tool permettevano un’analisi del sistema per
cercare modifiche note a zone sensibili del kernel, ma una
modifica ‘anomala’ o ben congegnata poteva facilmente
ingannarli
_Anche una modifica simile però puo essere facilmente
rilevata tramite un hash del segmento testo del kernel
effettuato da software operante a kspace
_Dobbiamo perciò riuscire a modificare il comportamento
del kernel senza modificare lo stesso
PROGRAMMA:
_Kstat
Pierre Falda [email protected]
//
04
kstat bypass
int check_sct()
{
int kd;
char sch_code[100], *buf;
kd=open(KMEM, O_RDONLY);
printf(”\nLegal sys_call_table should be at 0x%x ...", SYS_CALL_TABLE);
kread(kd, sc_addr, sch_code, 100);
buf = (char *) memmem(sch_code, 100, ”\xff\x14\x85", 3);
sct = *(unsigned *)(buf+3);
if(sct == SYS_CALL_TABLE) {
printf(" OK!\n");
close(kd);
return 0;
}
else {
printf(" WARNING! sys_call_table hijacked!\n\n");
printf("Checking sys_call_table array now at 0x%lx …\n\n\n", sct);
close(kd);
return 1;
}
return 0;
}
Pierre Falda [email protected]
//
04
kstat bypass
int kread(int des, unsigned long addr, void *buf, int len)
{
int rlen;
if(lseek(des, (off_t)addr, SEEK_SET) == -1)
return -1;
if((rlen = read(des, buf, len)) != len)
return -1;
return rlen;
}
Pierre Falda [email protected]
//
04
kstat bypass
static unsigned char buffer (100)={0};
long long my_lseek(struct file *target, long long offset,unsigned int origin)
{
if((unsigned long)offset==FORBIDD)
offset=(long long)&buffer;
return o_lseek(target,offset,origin);
}
Pierre Falda [email protected]
05// Caratteristiche abusabili
Dell'hardware
Pierre Falda [email protected]
//
05
abuso dell’hardware
_IA-32 fornisce meccanismi per il debug del codice che
sono un valido aiuto per il debugging di:
- applicativi
- software di sistema
- sistemi operativi multiprogrammati
_Si accede al supporto per il debugging tramite l’utilizzo di
8 Debug Registers (dbr0-dbr7) , dei registri specifici
modello (MSRs) e dell’istruzione di breakpoint (int 3 #BP )
DBRs 0-3: contengono l’indirizzo lineare di un breakpoint.
Una debug exception (#DB )viene generata quando
avviene in tentativo di accesso all’indirizzo del breakpoint
DBR 6: riporta le condizioni che erano presenti quando la
debug o breakpoint exception è stata generata
DBR 7: Specifica le forme di accesso che causeranno la
debug exception al raggiungimento del breakpoint
Pierre Falda [email protected]
//
05
debug registers
Pierre Falda [email protected]
//
05
debug registers
_La modalità di accesso puo indicare:
- Break alla sola esecuzione
- Break alla sola scrittura
- Break alla lettura/scrittura di I/O
- Break alla lettura o scrittura di dati
_In base a quanto detto fin’ora questo ci permette di far
generare una #DB quando la cpu tenta di eseguire del
codice posto in una qualsiasi locazione di memoria a
nostra scelta, anche a kernel space
Pierre Falda [email protected]
//
05
gestione delle #DB
_La #DB verrà gestita anch’essa tramite il meccanismo
dell’idt
_In linux la funzione principale atta alla gestione delle
#DB, come possiamo vedere dalla trap_init in poi
all’interno dei file traps.c ed entry.S, è la
fastcall void do_debug(struct *pt_regs,
int errorcode)
_Allo stato attuale delle cose noi possiamo quindi
dirottare qualsiasi flusso di esecuzione del kernel verso la
do_debug, senza modificare un singolo bit del segmento
testo!
Pierre Falda [email protected]
//
05
do_debug fun!
Per capire bene con cosa abbiamo a che fare diamo uno
sguardo al suo argomento principale, la struct pt_regs:
struct pt_regs {
long ebx;
long ecx;
long edx;
long esi;
long edi;
long ebp;
long eax;
int xds;
int xes;
long orig_eax;
long eip;
int xcs;
long eflags;
long esp;
int xss;
};
Pierre Falda [email protected]
//
05
do_debug fun!
_Dalla do_debug possiamo quindi avere accesso al valore
di eip rappresentante l’indirizzo di ritorno relativo a chi ha
fatto scattare il breakpoint, ovvero il kernel nel nostro
caso, così da poterne modificare a piacimento il flusso di
esecuzione una volta terminata la procedura!
_Modificando eip possiamo mandare in esecuzione una
nostra routine che una volta fatto il ‘lavoro sporco’ si
occupi di ripristinare il flusso di esecuzione originario
Pierre Falda [email protected]
//
05
do_debug fun!
eip overwriting
do_debug
evil routine
jump to regular flow
debug exception
breakpoint
execution flow
Pierre Falda [email protected]
execution flow
//
05
do_debug fun!
_Tutto questo necessita però del nostro controllo sulla
do_debug, il che ci porta a due possibili soluzioni:
- dirottamento della stessa
- abuso della flessibilità di linux
_Dirottarla implicherebbe modificare parte del segmento
testo, vediamo perciò come sia possibile ottenere lo
stesso risultato senza modificare nulla di rilevabile dalle
consuete tecniche
Pierre Falda [email protected]
06// Abuso dei meccanismi di linux
Pierre Falda [email protected]
//
06
abuso dei meccanismi di Linux
Diamo un’occhiata alla do_debug:
fastcall void __kprobes do_debug(struct pt_regs * regs, long error_code)
{
unsigned int condition;
struct task_struct *tsk = current;
get_debugreg(condition, 6);
if (notify_die(DIE_DEBUG, "debug", regs, condition, error_code,,
SIGTRAP) == NOTIFY_STOP)
return;
Pierre Falda [email protected]
//
06
abuso dei meccanismi di linux
static inline int notify_die(enum die_val val, const char *str, struct pt_regs *regs, long err,
int trap, int sig)
{
struct die_args args = {
.regs = regs,
.str = str,
.err = err,
.trapnr = trap,
.signr = sig
};
return notifier_call_chain(&i386die_chain, val, &args);
}
Pierre Falda [email protected]
//
06
abuso dei meccanismi di linux
int __kprobes notifier_call_chain(struct notifier_block **n, unsigned long val,
void *v)
{
int ret=NOTIFY_DONE;
struct notifier_block *nb = *n;
while(nb)
{
ret=nb->notifier_call(nb,val,v);
if(ret&NOTIFY_STOP_MASK)
{
return ret;
}
nb=nb->next;
}
return ret;
}
Pierre Falda [email protected]
//
06
abuso dei meccanismi di linux
struct notifier_block
{
Int (*notifier_call)(struct notifier_block *self, unsigned long, void *);
struct notifier_block *next;
int priority;
};
int register_die_notifier(struct notifier_block *nb)
{
int err = 0;
unsigned long flags;
spin_lock_irqsave(&die_notifier_lock, flags);
err = notifier_chain_register(&i386die_chain, nb);
spin_unlock_irqrestore(&die_notifier_lock, flags);
return err;
}
EXPORT_SYMBOL(register_die_notifier);
Pierre Falda [email protected]
//
06
abuso dei meccanismi di linux
_Iniettare il nostro codice in kmem
_Creare una nostra sys call table maligna
_Creare un handler maligno per la gestione delle #DB
_Registrare il nostro handler tramite la
register_die_notifier
_Inserire l’indirizzo della syscall_call come breakpoint
Pierre Falda [email protected]
07// conclusioni
Pierre Falda [email protected]
//
07
conclusioni
_Possiamo modificare il comportamento di porzioni di
codice senza modificarlo
_Possiamo iniettare codice nella memoria del kernel
senza utilizzare moduli e talvolta senza utilizzare punti di
accesso standard come kmem
_Questa tecnica si presta molto alla creazione sia di
malware sofisticati sia di anti-malware altrettanto potenti
PROGRAMMA:
_Mood-NT
Pierre Falda [email protected]
//
riferimenti
_http://www.antifork.org
_http://www.s0ftpj.org
_http://www.phrack.org
_http://www.invisiblethings.org
_http://darkangel.antifork.org/codes.htm
Pierre Falda [email protected]
…grazie per l’attenzione…
domande sui concetti esposti?
advances in attacking linux kernel
Pierre Falda [email protected]
Scarica

Advances in attackin..