2015-10-23 11 views
49

In alcuni exploit per ottenere la shell di root, vedo spesso una tale puntatore:Spiegazione di un puntatore nel codice exploit

int i; 
unsigned *p = *(unsigned**)(((unsigned long)&i) & ~8191); 

Qualcuno può spiegare questo puntatore un po '? Penso che 8191 sia la dimensione dello stack del kernel. p punti nella parte inferiore dello stack del kernel ? Ecco come viene usato puntatore p:

int i; 
unsigned *p = *(unsigned**)(((unsigned long)&i) & ~8191); 
for (i = 0; i < 1024-13; i++) { 
    if (p[0] == uid && p[1] == uid && 
     p[2] == uid && p[3] == uid && 
     p[4] == gid && p[5] == gid && 
     p[6] == gid && p[7] == gid) { 
      p[0] = p[1] = p[2] = p[3] = 0; 
      p[4] = p[5] = p[6] = p[7] = 0; 
      p = (unsigned *) ((char *)(p + 8) + sizeof(void *)); 
      p[0] = p[1] = p[2] = ~0; 
      break; 
     } 
    p++; 
} 
+3

Il valore '8191' in binario è' 1111111111111', e il tipo 'long' è di 32 bit. Penso di darti una risposta decisa, avremmo bisogno di vedere come viene usato il puntatore '* p'. L'operatore '&' è probabilmente un po 'maschera di qualche tipo. –

+0

@TimBiegeleisen Grazie per la risposta. L'ho modificato – HuangJie

risposta

54

Il codice prende l'indirizzo della variabile locale i per ottenere un puntatore in stack frame corrente. Quindi, allinea l'indirizzo alla pagina 8K (questo è ciò che si fa con x & ~8191: 8191 è 2^13 - 1 che significa che ~8191 è tutti tranne i 13 bit bassi, quindi ANDing con un numero cancellerà i 13 bit bassi, vale a dire allineare il numero al multiplo inferiore più vicino di 2^13, in altre parole, allineare al limite di 8K).

Quindi prende questo indirizzo e lo interpreta come puntatore a un puntatore e carica da esso l'indirizzo puntato. Vedere Understanding the getting of task_struct pointer from process kernel stack per ulteriori informazioni.

Dopo di che, si tenta di individuare una specifica struttura memorizzato da qualche parte dopo che l'indirizzo: Sembra attraverso i seguenti 1024-13unsigned s, cercando di trovare un posto nella memoria in cui sono memorizzate le informazioni processo in corso (probabilmente): Quando trova un pezzo di memoria contenente più copie dell'UID e del GID correnti, presume che l'abbia trovato. In tal caso, lo modifica in modo che il processo corrente ottenga UID e GID 0, rendendo il processo in esecuzione su root (in più memorizza tutti quelli nei seguenti flag di capacità).

Cfr. struct cred.

8

Ho intenzione di postare ancora un'altra risposta perché c'è davvero qualcosa da aggiungere qui.

unsigned *p = *(unsigned**)(((unsigned long)&i) & ~8191); 

risultati in p è il puntatore all'inizio del blocco di memoria di 8192 byte di memoria. Tuttavia, il codice è sbagliato. Se p è sopra INT_MAX (che può essere o sarà castato a unsigned, non unsigned long), i bit alti vengono tagliati via dalla maschera. codice corretto è il seguente:

unsigned *p = *(unsigned**)(((ptrdiff_t)&i) & ~(ptrdiff_t)8191); 

o utilizzando uintptr_t:

unsigned *p = *(unsigned**)(((uintptr_t)&i) & ~(uintptr_t)8191U); 

È necessario associare a intero e di nuovo al puntatore per il codice funzioni; comunque per garantire che un puntatore int-sized richieda l'uso di ptrdiff_t (ricordiamo che il comportamento firmato e non firmato è esattamente lo stesso per le operazioni bit a bit). Per quanto riguarda il motivo per cui non li scrivono con costanti esadecimali, a chi importa. I ragazzi che fanno questo tipo di cose conoscono i loro poteri di 2 a memoria. Potrebbe essere più veloce leggere 8191 e poi 0x1FFF.

+1

Per scrupolosa correttezza ISO, 'uintptr_t' invece di' ptrdiff_t' in entrambi i punti. Tutti gli ABI Linux, tuttavia, garantiscono 'sizeof (unsigned long) == sizeof (T *)' per tutti T. Quindi la modifica * minimal * per rendere il codice corretto nel contesto di un exploit del kernel Linux è semplicemente '& ~ 8191UL 'invece di' & ~ 8191'. – zwol

+0

Quando '~ 8191', di tipo' int', viene usato in '&' -expression con un 'unsigned long' sul lato sinistro, viene applicata l'estensione del segno.Quindi, i bit alti non verranno tolti dalla maschera. Questa è la stessa ragione per cui 'uint64_t x = -1;' imposta tutti i 64 bit su 1. – mortehu

+1

@mortehu: sono stato masterizzato usando quel frammento altrove. Se int ha meno bit di unsigned long le conversioni fanno la cosa sbagliata. – Joshua

Problemi correlati