Wargame

[Beginner] Baby-BOF

sonotri._. 2025. 11. 5. 21:57

01. BOF란?

버퍼에 허용할 수 있는 양의 데이터보다 더 많은 값이 저장되어 버퍼가 넘치는 취약점


02. 소스 코드 분석

먼저 살펴볼 것은 win() 함수이다. flag를 출력하는 함수로 우리는 이 함수가 실행되게 해야한다.

void win () {
  char flag[100] = {0,}; //flag 버퍼을 100byte만큼 0으로 채운다
  int fd;
  puts ("You mustn't be here! It's a vulnerability!");

  fd = open ("./flag", O_RDONLY); //flag 파일을 읽기 모드로 열기
  read(fd, flag, 0x60); //open으로 연 파일을 참조, flag 파일을 최대 96byte 데이터를 읽어 flag 버퍼에 저장
  puts(flag); //flag 버퍼 출력
  exit(0);
  }

 
이제 main함수 내부로 진입하자. 16바이트 버퍼 name이 선언되었다.

char name[16]; //16byte 버퍼로 총 16개의 char(1byte)를 저장할 수 있음
int age[20] ;
=> 20개의 int형(4바이트) 데이터를 연속으로 저장할 수 있는 80바이트짜리의 연속된 메모리 버퍼를 만드는 선언

 
0x%lx => 0x(16진수), %lx(unsigned long)을 16진수(x)로,,,라는 의미이다. 그리고 c에서는 함수 이름 자체가 해당 함수의 시작 주소를 의미하기에 win을 printf에 넘김으로서 win 함수의 메모리 주소를 출력할 수 있다.

  printf ("the main function doesn't call win function (0x%lx)!\n", win); //win 함수의 주소값을 출력

 
scanf함수의 %s는 입력 길이를 제한하지 않으며 공백 문자가 들어올 때까지 계속 입력을 받기에 오버플로우가 발생할 수 있다(사용하면 안됨).

  printf ("name: ");
  scanf ("%15s", name); //%s(string)으로 name을 15바이트만큼 받아오기

 
idx는 for문을 돌면서 0~16(0x10) 전까지 증가하여 총 16번 실행된다. name + idx *8는 name(name 주소)에 idx *8을 더한 값으로 long타입 16진수(%lx)로 출력된다.  name+8, name+16,...으로 8바이트씩 주소가 출력되는 것이다. *(long*)(name + idx*8)는 name+8, name+16 주소를 포인터로 참조하여, 해당 주소에 적힌 값을 16폭으로 long타입 데이터를 16진수로 출력한다.

  printf ("GM GA GE GV %s!!\n: ", name); //우리가 입력한 name을 %s로 출력
  printf ("|  addr\t\t|  value\t\t|\n"); 
  for (idx = 0; idx < 0x10; idx++) {
    printf ("|  %lx\t|  %16lx\t|\n", name + idx *8, *(long*)(name + idx*8));
  }

나는 name에 d를 입력했는데 d는 아스키코드로 나타내면 64이다. 그리고 15번 d를 입력해서 value값을 보면 64가 15개 있는걸 볼 수 있다. 그리고 name 주소값에서 시작했기 때문에 2번째 라인까지는 name의 값이 저장되는 주소인것을 알 수 있다.

 
이후 사용자로부터 valuecount를 입력 받는다.

  printf ("hex value: ");
  scanf ("%lx%c", &value); 

  printf ("integer count: ");
  scanf ("%d%c", &count);

 
그리고 입력받은 value와 count값을 기반으로 아래 코드가 실행된다. value값으로 입력한 값이 *(long*)(name+idx*8)에 저장된 값을 덮어 쓴다는 의미이다. 여기서 BOF를 터트릴 수 있다. value에 win() 함수의 주소를 저장해두면 return시 win함수의 주소로 이동하여 win 함수가 실행되도록 할 수 있기 때문이다.

  for (idx = 0; idx < count; idx++) {
    *(long*)(name+idx*8) = value;
  }
  for (idx = 0; idx < 0x10; idx++) {
    printf ("|  %lx\t|  %16lx\t|\n", name + idx *8, *(long*)(name + idx*8));
  }

https://miaami.tistory.com/4