wtorek, 7 stycznia 2025

PicoCTF - Buffer overflow 0

W tym poście chciałbym opisać sposób rozwiązania zadania Buffer overflow 0 z picoCTF.


Wraz z zadaniem został udostępniony kod:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <signal.h>
  5.  
  6. #define FLAGSIZE_MAX 64
  7.  
  8. char flag[FLAGSIZE_MAX];
  9.  
  10. void sigsegv_handler(int sig) {
  11.   printf("%s\n", flag);
  12.   fflush(stdout);
  13.   exit(1);
  14. }
  15.  
  16. void vuln(char *input){
  17.   char buf2[16];
  18.   strcpy(buf2, input);
  19. }
  20.  
  21. int main(int argc, char **argv){
  22.  
  23.   FILE *f = fopen("flag.txt","r");
  24.   if (f == NULL) {
  25.     printf("%s %s", "Please create 'flag.txt' in this directory with your",
  26.                     "own debugging flag.\n");
  27.     exit(0);
  28.   }
  29.  
  30.   fgets(flag,FLAGSIZE_MAX,f);
  31.   signal(SIGSEGV, sigsegv_handler); // Set up signal handler
  32.  
  33.   gid_t gid = getegid();
  34.   setresgid(gid, gid, gid);
  35.  
  36.  
  37.   printf("Input: ");
  38.   fflush(stdout);
  39.   char buf1[100];
  40.   gets(buf1);
  41.   vuln(buf1);
  42.   printf("The program will exit now\n");
  43.   return 0;
  44. }

W pętli głównej programu następuje odczytanie z pliku danych dotyczących szukanej flagi. Jej wyświetlenie następuje gdy zostanie wywołany błąd SIGSEGV (błąd segmentacji, niepoprawny dostęp do pamięci). 

Więc aby odczytać dane należy przekroczyć dane w buforze buf2 (16 elementowym) zdefiniowany w funkcji vuln. Do niej podawana jest tablica 100 elementowa. Kopiowanie danych następuje za pomocą funkcji strcpy. Przekazuje ona dane z bufora input do buf2 bez kontroli rozmiaru buf2. 

  1. -picoctf@webshell:~$ nc saturn.picoctf.net <port>
  2. Input: AAAAAAAAAAAAAAAAAAAA
  3. picoCTF{xxxxxxxxx_xxxxx_xxxx_xxx_xxxxxxxx}

Wywołanie błędu SEGVAULT następuje po wprowadzeniu do buf2 minimum 20 znaków. Za buf2 w pamięci będzie znajdował się adres powrotu z funkcji. Więc aby go w pełni nadpisać należy wprowadzić dodatkowe 4 bajty danych ponad wielkość bufora. 

Gdy chcemy ochronić kod przed takimi sytuacjami należy zawsze sprawdzać wielkość bufora jaki przekazujemy do funkji. Dla tego przypadku będzie to wielkość wskaźnika, która musi zostać obliczona po przekazaniu do funckji np. przez obliczenie danych do znaku końca linii, bądź podawana jako argument do funkcji.