본문 바로가기

Pwnable/공부

Windows 32bit 환경에서 버퍼오버플로우 기초 실습

오늘은 BOF를 통해서 계산기를 띄우는 걸 해볼 것이다. 

BOF 실습을 위한 프로그램을 제작한다.

test.txt의 파일 사이즈와 test.txt의 내용을 출력하는 프로그램이다.

#include <stdio.h>
#include <string.h>
#include <errno.h>

int get_filesize(FILE *fp){
  int filesize;
  fseek(fp, 0, SEEK_END);
  filesize = ftell(fp);
  fseek(fp, 0, SEEK_SET);
  return filesize;
}
int main() {
  FILE *fp;
  char buf[64];
  int filesize;
    
  fp = fopen("test.txt", "rb");
  filesize = get_filesize(fp);
  if (filesize == -1) {
    return 0;
  }
  printf("filesize : %d\n", filesize);
    
  fread(buf, filesize, 1, fp);
  buf[filesize] = 0x00;
  fclose(fp);
  printf("%s\n", buf);
    
  return 0;
}

대충 이런 프로그램이다.

 

우리가 할 BOF를 그림으로 설명하면 다음과 같다.

임의의 값을 할당된 변수 공간보다 더 많이 넣어서 RET의 값을 우리가 원하는 값으로 변조해야 한다.

하지만 우리는 RET의 시작 위치를 모르기 때문에 RET의 시작 위치를 찾아야 한다.

 

변수 배열만큼 a를 채워 넣고 Immunity Debugger로 확인해본다.

20바이트만큼 더 간 위치에 RET가 있다.

그러면 a는 84개만큼 채워져야 RET시작 위치에 접근할 수 있다.

 

이제 계산기를 띄우기 위해서는 다른 프로세스를 실행시키는 함수인 WinExec함수의 주소를 알아야 한다.

WinExec함수는 kernel32.dll에 있는 함수이기 때문에, 거의 모든 프로그램에서 쓸 수 있다고 봐도 무방하다.

kernel32.dll의 주소를 알고 있다고 가정하고 진행하겠습니다.

추후 LDR 구조체에서 kernel32.dll 주소 찾는 거 할 예정

 

WinExec 주소 찾는 법

Immunity Debugger에서 Win + G 누르고 WinExec 넣고 검색하면 WinExec가 로드 된 주소로 이동한다.

 

내 컴퓨터에서 WinExec함수는 0x7708DAB0에 로드되어 있다.

그러면 a * 84 + WinExec의 주소를 넣어줘야 하는데,

intel cpu 에선 little endian 방식을 사용하기 때문에 B0DA0877로 넣어줘야 한다.

더보기

endian : byte를 배열하는 방법

little endian : 하위 데이터가 먼저 나옴.

 

이제 WinExec함수의 인자 값을 넣어줘야 하는데, 한 과정을 더 거쳐야 한다.

leave 명령어에서 esp레지스터가 쉘 코드를 가리키게 되고, 

ret 명령어에서 pop eip를 하기 때문에 원하는 인자를 넣어주려면 쓰레기 값을 4byte만큼 더 넣어줘야 한다.

이 과정을 fake ebp 기법이라고 한다.

 

이제 진짜 함수에 인자를 전달할 차례이다.

msdn에서 WinExec의 인자를 확인한다.

첫 번째 인자는 실행할 프로그램의 이름이고, 두 번째 인자는 디스플레이 옵션이다.

우리는 계산기를 띄울 거기 때문에 첫 번째 인자에 calc를 넣고 두 번째 인자에 1을 넣을 것이다.

보통 텍스트를 로드해놓고 인자로 그 텍스트의 주소를 주기 때문에

calc가 로드된 주소 + 00000001 + calc 이렇게 작성한다.

 

이제 좀 더 쉽게 BOF를 하기 위해 파이썬으로 쉘 코드를 써주는 프로그램을 만든다.

f = open("test.txt", "wb")
payload = b'\x61'*84 
payload += b'\xb0\xda\x08\x77' #WinExec의 주소값
payload += b'AAAA' # fake ebp
payload += b'\x24\xFF\x40\x02' #calc 주소
payload += b'\x01\x00\x00\x00' #little endian
payload += b'calc\x00'
f.write(payload)
f.close()

 

잘 작성됐는지 확인해본다.

뒷 부분이 이상하게 보이는 이유는 메모장이 16진수를 아스키로 인식해서 그렇다.

 

잘 작성됐다.

 

이제 프로그램을 시작해본다.

성공!

소스코드에 계산기를 실행시키는 구문이 없는데도 계산기가 띄워졌다.

'Pwnable > 공부' 카테고리의 다른 글

포너블 공부용 사이트 정리  (0) 2020.10.16