본문으로 바로가기

level 1 문제 해결 ( /bin/ExecuteMe 해석 )

category Security/리버싱 2017. 11. 27. 21:59

- /bin/ExecuteMe 

ExecuteMe가 어떻게 이루어져 있기에 level1이 실행했을때 level2 권한을 가지고 명령어를 실행하는지 확인한다.


- gdb 

소스코드가 없으므로 실행파일을 가지고 disassemble 과정을 거쳐 확인한다. 


- 확인


[level1@ftz level1]$ gdb /bin/ExecuteMe

 

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

Dump of assembler code for function main:

0x08048488 <main+0>:    push   %ebp

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

0x0804848b <main+3>:    sub    $0x28,%esp

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

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

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

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

0x0804849b <main+19>:   push   $0x8048680

0x080484a0 <main+24>:   call   0x8048358 <system>

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

0x080484a8 <main+32>:   sub    $0xc,%esp

0x080484ab <main+35>:   push   $0x804868f

0x080484b0 <main+40>:   call   0x8048378 <chdir>

0x080484b5 <main+45>:   add    $0x10,%esp

0x080484b8 <main+48>:   sub    $0xc,%esp

0x080484bb <main+51>:   push   $0x80486a0

0x080484c0 <main+56>:   call   0x80483a8 <printf>

0x080484c5 <main+61>:   add    $0x10,%esp

0x080484c8 <main+64>:   sub    $0xc,%esp

0x080484cb <main+67>:   push   $0x80486e0

0x080484d0 <main+72>:   call   0x80483a8 <printf>

0x080484d5 <main+77>:   add    $0x10,%esp

---Type <return> to continue, or q <return> to quit---

0x080484d8 <main+80>:   sub    $0xc,%esp

0x080484db <main+83>:   push   $0x8048720

0x080484e0 <main+88>:   call   0x80483a8 <printf>

0x080484e5 <main+93>:   add    $0x10,%esp

0x080484e8 <main+96>:   sub    $0xc,%esp

0x080484eb <main+99>:   push   $0x8048760

0x080484f0 <main+104>:  call   0x80483a8 <printf>

0x080484f5 <main+109>:  add    $0x10,%esp

0x080484f8 <main+112>:  sub    $0xc,%esp

0x080484fb <main+115>:  push   $0x8048782

0x08048500 <main+120>:  call   0x80483a8 <printf>

0x08048505 <main+125>:  add    $0x10,%esp

0x08048508 <main+128>:  sub    $0x4,%esp

0x0804850b <main+131>:  pushl  0x8049948

0x08048511 <main+137>:  push   $0x1e

0x08048513 <main+139>:  lea    0xffffffd8(%ebp),%eax

0x08048516 <main+142>:  push   %eax

0x08048517 <main+143>:  call   0x8048368 <fgets>

0x0804851c <main+148>:  add    $0x10,%esp

0x0804851f <main+151>:  lea    0xffffffd8(%ebp),%eax

0x08048522 <main+154>:  sub    $0x8,%esp

0x08048525 <main+157>:  push   $0x804879c

0x0804852a <main+162>:  push   %eax

---Type <return> to continue, or q <return> to quit---

0x0804852b <main+163>:  call   0x8048388 <strstr>

0x08048530 <main+168>:  add    $0x10,%esp

0x08048533 <main+171>:  test   %eax,%eax

0x08048535 <main+173>:  je     0x8048551 <main+201>

0x08048537 <main+175>:  sub    $0xc,%esp

0x0804853a <main+178>:  push   $0x80487c0

0x0804853f <main+183>:  call   0x80483a8 <printf>

0x08048544 <main+188>:  add    $0x10,%esp

0x08048547 <main+191>:  sub    $0xc,%esp

0x0804854a <main+194>:  push   $0x0

0x0804854c <main+196>:  call   0x80483c8 <exit>

0x08048551 <main+201>:  lea    0xffffffd8(%ebp),%eax

0x08048554 <main+204>:  sub    $0x8,%esp

0x08048557 <main+207>:  push   $0x80487e8

0x0804855c <main+212>:  push   %eax

0x0804855d <main+213>:  call   0x8048388 <strstr>

0x08048562 <main+218>:  add    $0x10,%esp

0x08048565 <main+221>:  test   %eax,%eax

0x08048567 <main+223>:  je     0x8048583 <main+251>

0x08048569 <main+225>:  sub    $0xc,%esp

0x0804856c <main+228>:  push   $0x8048800

0x08048571 <main+233>:  call   0x80483a8 <printf>

0x08048576 <main+238>:  add    $0x10,%esp

---Type <return> to continue, or q <return> to quit---

0x08048579 <main+241>:  sub    $0xc,%esp

0x0804857c <main+244>:  push   $0x0

0x0804857e <main+246>:  call   0x80483c8 <exit>

0x08048583 <main+251>:  sub    $0xc,%esp

0x08048586 <main+254>:  push   $0x8048826

0x0804858b <main+259>:  call   0x80483a8 <printf>

0x08048590 <main+264>:  add    $0x10,%esp

0x08048593 <main+267>:  sub    $0x8,%esp

0x08048596 <main+270>:  push   $0xbba

0x0804859b <main+275>:  push   $0xbba

0x080485a0 <main+280>:  call   0x80483b8 <setreuid>

0x080485a5 <main+285>:  add    $0x10,%esp

0x080485a8 <main+288>:  sub    $0xc,%esp

0x080485ab <main+291>:  lea    0xffffffd8(%ebp),%eax

0x080485ae <main+294>:  push   %eax

0x080485af <main+295>:  call   0x8048358 <system>

0x080485b4 <main+300>:  add    $0x10,%esp

0x080485b7 <main+303>:  leave

0x080485b8 <main+304>:  ret

0x080485b9 <main+305>:  nop

0x080485ba <main+306>:  nop

0x080485bb <main+307>:  nop

End of assembler dump.

(gdb)

 


- 분석
$ export LANG=C

 

 0x0804849b <main+19>:   push   $0x8048680

0x080484a0 <main+24>:   call   0x8048358 <system>

 

> system 이라는 함수가 실행 된 것 같다. 


$ man system

 

 SYNOPSIS

       #include <stdlib.h>


       int system(const char *string);


DESCRIPTION

       system()  executes  a command specified in string by calling /bin/sh -c

       string, and returns after the command has been completed.  During  exe-

       cution  of the command, SIGCHLD will be blocked, and SIGINT and SIGQUIT

       will be ignored.


 

> /bin/sh-c [string]  : string을 쉘로 실행해주는 함수

> string 이라는 하나의 파라미터(매개변수)값을 가진다. ( 바로 위 push 로 입력받은 값 )


 

 (gdb) x/s 0x8048680

0x8048680 <_IO_stdin_used+28>:   "/usr/bin/clear"

 

 > push의 주소를 x/s ( 문자형식으로 출력) 명령어를 사용해보니 clear 명령어임을 확인할 수 있다. 


같은 방법으로 어떤 함수가 실행 됬는지 확인한다. 


 

0x080484ab <main+35>:   push   $0x804868f

0x080484b0 <main+40>:   call   0x8048378 <chdir>

 

 


$ man chdir

 

 SYNOPSIS

       #include <unistd.h>


       int chdir(const char *path);

       int fchdir(int fd);


DESCRIPTION

       chdir changes the current directory to that specified in path.


       fchdir  is  identical  to chdir, only that the directory is given as an

       open file descriptor.


 

> 디렉토리를 변경하는 명령어


 

 (gdb) x/s 0x804868f

0x804868f <_IO_stdin_used+43>:   "/home/level2"


 

> path의 값은 level2의 홈 디렉토리



 

0x080484bb <main+51>:   push   $0x80486a0

0x080484c0 <main+56>:   call   0x80483a8 <printf>

............

0x080484cb <main+67>:   push   $0x80486e0

0x080484d0 <main+72>:   call   0x80483a8 <printf>

............

0x080484db <main+83>:   push   $0x8048720

0x080484e0 <main+88>:   call   0x80483a8 <printf>

............

0x080484eb <main+99>:   push   $0x8048760

0x080484f0 <main+104>:  call   0x80483a8 <printf>

............

0x080484fb <main+115>:  push   $0x8048782

0x08048500 <main+120>:  call   0x80483a8 <printf>


 

> printf , 출력문이 연속된다. 

> /bin/ExecuteMe 를 실행 했을때 출력되는 것을 살펴보면 아마 그 글을 출력할 것이다. 


$ /bin/ExecuteMe

 

                


                레벨2의 권한으로 당신이 원하는 명령어를

                한가지 실행시켜 드리겠습니다.

                (단, my-pass 와 chmod는 제외)


                어떤 명령을 실행시키겠습니까?



                [level2@ftz level2]$


 


 

 (gdb) x/s 0x8048782

0x8048782 <_IO_stdin_used+286>:  "\n\n\t\t[level2@ftz level2]$ "


 




 

0x0804850b <main+131>:  pushl  0x8049948 

0x08048511 <main+137>:  push   $0x1e

0x08048513 <main+139>:  lea    0xffffffd8(%ebp),%eax

0x08048516 <main+142>:  push   %eax

0x08048517 <main+143>:  call   0x8048368 <fgets>

 


$ man fgets

 

       #include <stdio.h>

 

        char *fgets(char *s, int size, FILE *stream);  


        fgets() reads in at most one less than size characters from stream  and

       stores  them  into  the buffer pointed to by s.  Reading stops after an

       EOF or a newline.  If a newline is read, it is stored into the  buffer.

       A '\0' is stored after the last character in the buffer.


 

> 입력받는 함수 

> 3개의 파라미터가 필요하기 때문에 3개의 push 


 

 (gdb) x/s 0x8049948

0x8049948 <stdin@@GLIBC_2.0>:    ""

 

> 아직 입력값이 없기 때문에 빈공간으로 남아있다. 

> 스택이 LIFO 이기 때문에 먼저 push된 0x8049948이 마지막 파라미터인 FILE *stream의 변수 값이 된다. 

-> 즉 스택에는 거꾸로 들어가야 읽어 들일 때 순서대로 읽을 수 있다. 

> 0x1e : (10진수) 30 



 

0x08048525 <main+157>:  push   $0x804879c

0x0804852a <main+162>:  push   %eax

---Type <return> to continue, or q <return> to quit---

0x0804852b <main+163>:  call   0x8048388 <strstr>

0x08048530 <main+168>:  add    $0x10,%esp

0x08048533 <main+171>:  test   %eax,%eax

0x08048535 <main+173>:  je     0x8048551 <main+201>

0x08048537 <main+175>:  sub    $0xc,%esp

0x0804853a <main+178>:  push   $0x80487c0

0x0804853f <main+183>:  call   0x80483a8 <printf>

0x08048544 <main+188>:  add    $0x10,%esp

0x08048547 <main+191>:  sub    $0xc,%esp

0x0804854a <main+194>:  push   $0x0

0x0804854c <main+196>:  call   0x80483c8 <exit>

0x08048551 <main+201>:  lea    0xffffffd8(%ebp),%eax

 

 


$ man strstr

 

 SYNOPSIS

       #include <string.h>


       char *strstr(const char *haystack, const char *needle);


DESCRIPTION

       The  strstr() function finds the first occurrence of the substring nee-

       dle in the string haystack.  The terminating `\0'  characters  are  not

       compared.


 

> 두 개의 매개변수를 비교한다.  \0은 같지 않을 때의 캐릭터이다. 


 

 (gdb) x/s 0x804879c

0x804879c <_IO_stdin_used+312>:  "my-pass"


 

> 비교하는 스트링 하나는 내가 입력한 명령어 하나는 my-pass 이다. 


 

 (gdb) x/s 0x80487c0

0x80487c0 <_IO_stdin_used+348>:  "\n\t\tmy-pass 명령은 사용할 수 없습니다.\n\n"


 

> 비교하여 같으면 printf 함수를 실행한 후 exit 로 종료한다. 

> 같지 않으면 main+201로 점프한다. 


다음 반복되는 strstr은 /bin/ExecuteMe 의 출력문을 보아 아마 chmod 명령어를 위와 같은 조건으로 판별하는 것 같다. 



 

0x08048596 <main+270>:  push   $0xbba

0x0804859b <main+275>:  push   $0xbba

0x080485a0 <main+280>:  call   0x80483b8 <setreuid>

0x080485a5 <main+285>:  add    $0x10,%esp

0x080485a8 <main+288>:  sub    $0xc,%esp

0x080485ab <main+291>:  lea    0xffffffd8(%ebp),%eax

0x080485ae <main+294>:  push   %eax

0x080485af <main+295>:  call   0x8048358 <system>

 


$ man streuid

 

 


SYNOPSIS

       #include <sys/types.h>

       #include <unistd.h>


       int setreuid(uid_t ruid, uid_t euid);

       int setregid(gid_t rgid, gid_t egid);


DESCRIPTION

       setreuid  sets  real  and  effective  user  IDs of the current process.

       Unprivileged users may only set the real user ID to the real user ID or

       the  effective  user  ID, and may only set the effective user ID to the

       real user ID, the effective user ID or the saved user ID.


       Supplying a value of -1 for either the real or effective user ID forces

       the system to leave that ID unchanged.


       If  the  real user ID is set or the effective user ID is set to a value

       not equal to the previous real user ID, the saved user ID will  be  set

       to the new effective user ID.


       Completely  analogously, setregid sets real and effective group ID's of

       the current process, and all of the above holds with "group" instead of

       "user".


 

>  real uid 와 effective uid 를 변경한다. 

> push 값이 0xbba(3002) 이다. 


[level1@ftz level1]$ id level2

 

 uid=3002(level2) gid=3002(level2) groups=3002(level2)

 

> level2 의 uid임을 확인 할 수 있다. 


>system 함수는 처음에 확인한 것과 같이 쉘로 명령어를 실행 시켜준다. 

> setreuid로 권한이 level2이 된 것으로 입력한 명령어를 실행 시켜 준다. 



- 의사 코드 복원

분석한 내용으로는 처음에 /bin/clear를 하고 level2의 홈디렉토리로 옮긴 뒤, 몇 가지 글을 출력하고 

입력받는 명령어를 my-pass와 chmod를 조건문으로 가려낸 후 setreuid로 권한 상승후 system으로 실행한다. 

 

#include<stdio.h>

#include<string.h>

#include<unistd.h>

#include<stdlib.h>


int main(void)

{

   char input[30];

   

   system("/bin/clear");

   printf(" 레벨 2의 권한으로 명령어를 실행 시키겠습니다"\n);

   printf(" ( 단 my-pass와 chmod는 제외 ) \n");

   printf(" [level2@ftz level2]$ ");


   fgets(input, sizeof(input), stdin);


   if(strstr(input, "my-pass") != \0 )

   {

      printf( " my-pass 명령어는 사용할 수 없습니다. " );

      exit(0);

    }

   if(strstr(input,"chmod") != \0 )

   {

      printf("chmod 명령어는 사용할 수 없습니다. ");

      exit(0);

    }


    setreuid(3002,3002);

    system(input);

}

 

> 출력문이 완벽하지는 않지만 같은 동작을 하는 소스를 유추해 낼 수 있다. 

'Security > 리버싱' 카테고리의 다른 글

Level2 문제 분석/응용  (0) 2017.11.28
Level2 문제 해결  (0) 2017.11.28
level 1 문제 해결  (0) 2017.11.27
GDB 사용법  (1) 2017.11.27
리버싱 실습환경 구성  (0) 2017.11.27