본문 바로가기

Learning/└◆Reversing

06_Level6 -> Level7[FTZ] signal() 함수 취약점 살펴보기


 ■ Level6 -> Level7 


 

 

■ 목적

signal() 함수의 취약점.

 

시그널(signal)?

하나의 프로세스(Process)가 다른프로세스(Process)에게 보내는 비동기적 알림 이벤트 메세지

 

 

 

■ Level6 풀이

 

level6 사용자 로그인

-> ID/PASS : level6/what the hell

 

Last login: Mon Aug 25 08:41:35 from localhost.localdomain

 

 

hint - 인포샵 bbs의 텔넷 접속 메뉴에서 많이 사용되던 해킹 방법이다.


<ENTER>


  #####################################

  ##                                 ##

  ##         텔넷 접속 서비스        ##

  ##                                 ##

  ##                                 ##

  ##     1. 하이텔     2. 나우누리   ##

  ##     3. 천리안                   ##

  ##                                 ##

  #####################################


접속하고 싶은 bbs를 선택하세요 : 1

Trying 203.245.15.76...

 

Level 6은 로그인을 하자마자 힌트가 출력된다.

-> 엔터를 누르면 화면 하단에 보이는 창으로 들어가진다.

-> 잠시 기다리면 접속이 끊어진다.(20초 정도)

 

1~3번 어느 것을 눌러도 시간이 지나면 종료된다.

이 프로그램을 실행하여 진행은 할 수 없는 것 같다.

 

하지만 이런 텔넷 접속 서비스도 하나의 프로그램이기 때문에 프로세스를 종료할 수 있다.

Ctrl + C 를 사용하자.

  

Last login: Mon Aug 25 08:41:35 from localhost.localdomain

 

 

hint - 인포샵 bbs의 텔넷 접속 메뉴에서 많이 사용되던 해킹 방법이다.


<ENTER>


  #####################################

  ##                                 ##

  ##         텔넷 접속 서비스        ##

  ##                                 ##

  ##                                 ##

  ##     1. 하이텔     2. 나우누리   ##

  ##     3. 천리안                   ##

  ##                                 ##

  #####################################


접속하고 싶은 bbs를 선택하세요 : <Ctrl + C>

Can't use ctrl+c

Can't use ctrl+c

Can't use ctrl+c

 

Ctrl + C 가 먹히질 않는다.

시그널디 처리가 되어 있다는 것을 알 수 있다. 

 

접속이 끊어지는 이유는 윈도우 원본 운영체제 CMD창에서 nslookup으로 위 IP를 검색해 보면 알 수 있다.

(Trying 203.245.15.76...)

현제 하이텔,나우누리,천리안의 PC 통신 BBS 서비스가 폐지되어 있기 때문이다.

위 출력내용의 원리는 pc가 부팅이되고 로그인이 되면 악성 프로그램이 실행되고 시그널 트래핑에 의해

프로그램을 종료 하지 못한채 pc의 제어권을 잃게 되고 프로그램안에서 빠져나갈수 없게 되는 것이다.

 

 

시그널 함수 사용법 (시그널 트래핑)


-----------------------------------------------------------------------------

함수 사용법                              함수 사용예                      설명

-----------------------------------------------------------------------------

signal(시그널번호, SIG_DFL)      signal(SIGINT, SIG_DFL)    SIGINT 시그널 실행

signal(시그널번호, SIG_IGN)       signal(SIGQUIT, SIG_IGN)  SIGQUIT 시그널 무시

signal(시그널번호, handler함수)  signal(SIGINT, handler)     SIGINT(CTRL + C)가 입력되면 

                                                                                     handler() 함수를 실행

-----------------------------------------------------------------------------

Ctrl + C 등 프로그램을 종료하는 시그널을 사용시 그 시그널을 무시하는 방법이다.

 

그럼 어떻게 로그인하자마자 프로그램이 실행 된 것일까?

 

[참고] 쉘이 인식할수 있는 해석기
변수값을 집어넣어 ; 기호를 이용해 패스워드를 크랙

로그인시 PASSWD ; cat /etc/passwd;

 

이런 것을 인터럽트 라고 한다.


pc방 로그인 인터페이스를 예로 들수 있다. 

보통 pc방에서 컴퓨터를 부팅하면 지렁이가 지나가고 잠시후에 바탕화면을 출력하기 전에 pc방에서 제어하는

로그인 프로그램이 실행된다.

부팅되기전 프로그램이 실행 되고 제어권을 획득하여(환경파일 안에 프로그램을 넣어서 로그인 하면 동작하게)

 

제어 로그온 프로그램이 실행되면 그 프로그램 안에 프로그램을 종료하는 명령어를 무시하게 하는 시그널 함수가 존재한다.

따라서 프로그램이 부팅이 되면 그 프로그램을 종료할수 없게 되고 pc의 제어권은 그 프로그램을 관리하는 계산대 pc로 가게되는 원리이다.

이런 것을 시그널 트래핑 이라고 한다.

 

이 것은 환경파일을 생각해 보면 되는데

부팅을 하고 여러가지를 순차적으로 실행하게 되는데 위 제어 프로그램이 실행되기 전 단계에서는

프로그램에 의해 시그널 트래핑을 할 수 없다. 아직 프로그램이 실행되지 않았기 때문에 그 프로그램 안에 코드가 동작을 하기 전 단계인 것이다.

 

로그인 후 프로그램이 실행되면 시그널 트래핑에 의해
프로그램을 종료할 수 없고 제어권을 프로그램에게 넘긴다.
/etc/profile -> ~/.bashprofile -> ~/.bashrc 순차적으로 프로그램들이 실행되는데

시그널 트래핑이 존재하는 프로그램이 실행 되기 전은 시그널 명령을 할 수 있다.


이 환경파일 구조의 원리를 생각하면

pc방 컴퓨터가 부팅이되고 로그온 프로그램이 실행 되기전 그 중간 지점에서 Delete와 같은 시그널을 주어 동작을 멈추고

Ctrl + Art + ESC를 통해 로그온 프로그램의 프로세스를 종료하면 pc관리자에게 제어받지 않고 로그온을 하지않고 사용할 수 있게 되는 것이다.

(실제로 그런 행동을 하여서는 안된다.. 그저 원리설명을 하기 위한 예를 든 것이다.)

 

이제 LEVEL 6의 목적과 의도를 이해 하는가?

 

BBS 화면은 가짜 모듈에 해당한다. 이 메뉴를 우회할 수 있는 뭔가가 있을 것이다.

프로그램을 우회할 수 있는 시점을 확인하기 위해서 수많은 시그널을 무작위로 입력해 본다.

 

Last login: Mon Aug 25 08:41:35 from localhost.localdomain

 

 

hint - 인포샵 bbs의 텔넷 접속 메뉴에서 많이 사용되던 해킹 방법이다.


<CTRL + C>

[level6@ftz level6]$

 

BBS 모듈에 접속되기 전 상태에서는 시그널 명령이 정상적으로 작동한다.

 

$ ls

 hint  password  public_html  tmp  tn

 

평소에 못보던 파일이 몇개 있다.

passwd 라는 파일이 있으니 우선 확인해 본다.

 

 

$ cat password

 Level7 password is "come together".

 

이번 레벨의 핵심은 시스템 인터럽트 이다.

시스템 인터럽트란 프로세스 간에 주고받는 신호인데 시스템 개발에 중요한 요소 중 하나이다.

인터럽트를 발생하기 위해서는 시그널에 대해 알아야 한다.

 

시그널에 관한 설정은 보안 관리에서 좀더 유연하게 관리를 할 수 있는 방법중 하나이다.

 

저 프로그램이 로그온 하자마자 동작하는 원리를 알아보자

확인하기 위해 .bashrc 파일을 확인해 보았다.

$ cat .bashrc

# .bashrc

# User specific aliases and functions

# Source global definitions
if [ -f /etc/bashrc ]; then
        . /etc/bashrc
fi
export PS1="[\u@\h \W]\$ "
./tn
logout 

 

위에 passwd 말고 tn이라는 파일이 하나 더 있었던 것을 기억하는가?

그리고 텔넷 로그인에 시도했다가 종료되는 것은 아마 logout이 있기 때문인 것 같다.

 

실제로 ./tn을 실행해보면 첫 로그인시 나오는 힌트와 텔넷 서비스 인것을 알 수 있다.

 

$ file tn

tn: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.2.5, dynamically linked (uses shared libs), not stripped

 

사용자 로그인 --> 환경파일 .bashrc(./tn) --> tn 프로그램 실행(번호) --> 천리안(telnet) 

 


 

■ 의사코드

 

$ gdb tn

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

Dump of assembler code for function main:

0x080484f8 <main+0>:    push %ebp

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

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

0x080484fe <main+6>:    sub $0xc,%esp

0x08048501 <main+9>:    push $0x80486f2          "cat hint" 

0x08048506 <main+14>:   call 0x8048384 <system>  /* int system(const char *string); */

0x0804850b <main+19>:   add $0x10,%esp            멈춰 놓았다.

0x0804850e <main+22>:   call 0x8048354 <getchar> /* int getchar(void); */

0x08048513 <main+27>:   sub $0xc,%esp

0x08048516 <main+30>:   push $0x80486fb          "clear" 

0x0804851b <main+35>:   call 0x8048384 <system>

0x08048520 <main+40>:   add $0x10,%esp

0x08048523 <main+43>:   sub $0xc,%esp

0x08048526 <main+46>:   push $0x8048720          "\n ", '#' <repeats 37 times>, "\n"

0x0804852b <main+51>:   call 0x80483c4 <printf> /* int printf(const char *format, ...);

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

0x08048533 <main+59>:   sub $0xc,%esp

0x08048536 <main+62>:   push $0x8048760          "  ##", ' ' <repeats 33 times>, "##\n"

0x0804853b <main+67>:   call 0x80483c4 <printf>

0x08048540 <main+72>:   add $0x10,%esp

0x08048543 <main+75>:   sub $0xc,%esp

0x08048546 <main+78>:   push $0x80487a0

0x0804854b <main+83>:   call 0x80483c4 <printf>

0x08048550 <main+88>:   add $0x10,%esp

0x08048553 <main+91>:   sub $0xc,%esp

0x08048556 <main+94>:   push $0x8048760

0x0804855b <main+99>:   call 0x80483c4 <printf>

0x08048560 <main+104>:  add $0x10,%esp

0x08048563 <main+107>:  ub $0xc,%esp

0x08048566 <main+110>:  push $0x8048760

0x0804856b <main+115>:  call 0x80483c4 <printf>

0x08048570 <main+120>:  add $0x10,%esp

0x08048573 <main+123>:  sub $0xc,%esp

0x08048576 <main+126>:  push $0x80487e0

0x0804857b <main+131>:  call 0x80483c4 <printf>

0x08048580 <main+136>:  add $0x10,%esp

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

0x08048586 <main+142>:  push $0x8048820

0x0804858b <main+147>:  call 0x80483c4 <printf>

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

0x08048593 <main+155>:  sub $0xc,%esp

0x08048596 <main+158>:  push $0x8048760

0x0804859b <main+163>:  call 0x80483c4 <printf>

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

0x080485a3 <main+171>:  sub $0xc,%esp

0x080485a6 <main+174>:  push $0x8048860

0x080485ab <main+179>:  call 0x80483c4 <printf>

0x080485b0 <main+184>:  add $0x10,%esp

0x080485b3 <main+187>:  sub $0x8,%esp

0x080485b6 <main+190>:  push $0x80484e0          (signal번호 handler함수)

0x080485bb <main+195>:  push $0x2                2번 시그널 <CTRL + C >

0x080485bd <main+197>:  call 0x8048374 <signal>  void (*signal(int signum, void                                                              (*handler)(int)))(int);

0x080485c2 <main+202>:  add $0x10,%esp           main+195 이후로는 ctrl + c 가 안먹힌다.

0x080485c5 <main+205>:  sub $0xc,%esp            (시그널 트래핑)

0x080485c8 <main+208>:  push $0x80488a0    

0x080485cd <main+213>:  call 0x80483c4 <printf>

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

0x080485d5 <main+221>:  sub $0x8,%esp

0x080485d8 <main+224>:  lea 0xfffffffc(%ebp),%eax

0x080485db <main+227>:  push %eax

0x080485dc <main+228>:  push $0x80488c3         입력 받아야 들어가는 값

0x080485e1 <main+233>:  call 0x8048394 <scanf>  int scanf(const char *format, ...);

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

0x080485e9 <main+241>:  cmpl $0x1,0xfffffffc(%ebp)     1을 선택시 비교

0x080485ed <main+245>:  jne 0x80485ff <main+263>       점프구문

0x080485ef <main+247>:  sub $0xc,%esp

0x080485f2 <main+250>:  push $0x80488c6         telnet 203.245.15.76

0x080485f7 <main+255>:  call 0x8048384 <system>

0x080485fc <main+260>:  add $0x10,%esp

0x080485ff <main+263>:  cmpl $0x2,0xfffffffc(%ebp)   2번을 선택하면 동작

0x08048603 <main+267>:  jne 0x8048615 <main+285>     그렇지 않으면 285로 점프

0x08048605 <main+269>:  sub $0xc,%esp

0x08048608 <main+272>:  push $0x80488db         telnet 203.238.129.97

0x0804860d <main+277>:  call 0x8048384 <system>

0x08048612 <main+282>:  add $0x10,%esp

0x08048615 <main+285>:  cmpl $0x3,0xfffffffc(%ebp)   3번을 선택하면 동작

0x08048619 <main+289>:  jne 0x804862b <main+307>     그렇지 않으면 307로 점프

0x0804861b <main+291>:  sub $0xc,%esp

0x0804861e <main+294>:  push $0x80488f1         telnet 210.120.128.180

0x08048623 <main+299>:  call 0x8048384 <system>

0x08048628 <main+304>:  add $0x10,%esp

0x0804862b <main+307>:  cmpl $0x1,0xfffffffc(%ebp)

0x0804862f <main+311>:  je 0x804864d <main+341>

0x08048631 <main+313>:  cmpl $0x2,0xfffffffc(%ebp)

0x08048635 <main+317>:  je 0x804864d <main+341>

0x08048637 <main+319>:  cmpl $0x3,0xfffffffc(%ebp)

0x0804863b <main+323>:  je 0x804864d <main+341>

0x0804863d <main+325>:  sub $0xc,%esp

0x08048640 <main+328>:  push $0x8048920

0x08048645 <main+333>:  call 0x80483c4 <printf>

0x0804864a <main+338>:  add $0x10,%esp

0x0804864d <main+341>:  leave

0x0804864e <main+342>:  ret

0x0804864f <main+343>:  nop

End of assembler dump.

(gdb) x/s 0x80486f2

0x80486f2 <_IO_stdin_used+46>:  "cat hint"

(gdb) x/s 0x80486fb

0x80486fb <_IO_stdin_used+55>:  "clear"

(gdb) x/s 0x8048720

0x8048720 <_IO_stdin_used+92>:  "\n ", '#' <repeats 37 times>, "\n"

(gdb) x/s 0x8048760

0x8048760 <_IO_stdin_used+156>: " ##", ' ' <repeats 33 times>, "##\n"

(gdb) x/s 0x80487a0

0x80487a0 <_IO_stdin_used+220>: " ## 텔넷 접속 서비스 ##\n"

(gdb) x/s 0x8048760

0x8048760 <_IO_stdin_used+156>: " ##", ' ' <repeats 33 times>, "##\n"

(gdb) x/s 0x8048760

0x8048760 <_IO_stdin_used+156>: " ##", ' ' <repeats 33 times>, "##\n"

(gdb) x/s 0x80487e0

0x80487e0 <_IO_stdin_used+284>: " ## 1. 하이텔 2. 나우누리 ##\n"

(gdb) x/s 0x8048820

0x8048820 <_IO_stdin_used+348>: " ## 3. 천리안", ' ' <repeats 19 times>, "##\n"

(gdb) x/s 0x8048760

0x8048760 <_IO_stdin_used+156>: " ##", ' ' <repeats 33 times>, "##\n"

(gdb) x/s 0x8048860

0x8048860 <_IO_stdin_used+412>: " ", '#' <repeats 37 times>, "\n"

(gdb) x/s 0x80484e0

0x80484e0 <sig_func>: "U\211?203?b\203?fh?206\004\b?\203?020U\211?\203?b\203?fh?206\004\b??\203?020??\203?fh?206\004\b??\203?020\203?fh \207\004\b?224?\203?020\203?fh`\207\004\b?204?\203?020\203?fh?207\004\b??\203?020\203?fh`\207\004\b??\203?020\203?fh`\207\004\b??\203?020\203?fh?207\004\b??\203?020\203?fh \210\004\b??\203?020\203?\fh`\207\004\b??\203?020\203?fh`"...

(gdb) disas sig_func

Dump of assembler code for function sig_func:

0x080484e0 <sig_func+0>:  push %ebp

0x080484e1 <sig_func+1>:  mov %esp,%ebp

0x080484e3 <sig_func+3>:  sub $0x8,%esp

0x080484e6 <sig_func+6>:  sub $0xc,%esp

0x080484e9 <sig_func+9>:  push $0x80486e0

0x080484ee <sig_func+14>: call 0x80483c4 <printf>

0x080484f3 <sig_func+19>: add $0x10,%esp

0x080484f6 <sig_func+22>: leave

0x080484f7 <sig_func+23>: ret

End of assembler dump.

(gdb) x/s 0x80486e0

0x80486e0 <_IO_stdin_used+28>: "Can't use ctrl+c\n"

(gdb) x/s 0x80488a0

0x80488a0 <_IO_stdin_used+476>: "\n접속하고 싶은 bbs를 선택하세요 : "

(gdb) x/s 0x80488c3

0x80488c3 <_IO_stdin_used+511>: "%d"

(gdb) x/s 0x80488c6

0x80488c6 <_IO_stdin_used+514>: "telnet 203.245.15.76"

(gdb) x/s 0x80488db

0x80488db <_IO_stdin_used+535>: "telnet 203.238.129.97"

(gdb) x/s 0x80488f1

0x80488f1 <_IO_stdin_used+557>: "telnet 210.120.128.180"

(gdb) x/s 0x8048920

0x8048920 <_IO_stdin_used+604>: "잘못 입력하셨습니다. 접속을 종료합니다.\n"

(gdb) quit

 

 

■ 복원 의사코드

 

$ vi tn.c

#include <stdio.h>

#include <signal.h>


void sig_func(int signo)

{

        printf("Can't use ctrl+c\n");

}


int main()

{

        char input;

        int select, i;


        system("cat hint");

        input = getchar();

        system("clear");


        printf("#####################################\n");

        printf("##                                 ##\n");

        printf("##        텔넷 접속 서비스         ##\n");

        printf("##                                 ##\n");

        printf("##     1. 하이텔   2. 나우누리     ##\n");

        printf("##     3. 천리안                   ##\n");

        printf("##                                 ##\n");

        printf("#####################################\n");


        for(i=1; i<32; i++)         32번까지의 시그널 트래핑을 비교하는 구문

        {

                if(i == SIGINT) signal(i, sig_func);

                else signal(i, SIG_IGN);

        }


        printf("\n접속하고 싶은 bbs를 선택하세요 : ");

        switch(input)

        {

                case 1: system("telnet 203.245.15.76"); break;

                case 2: system("telnet 203.238.129.97"); break;

                case 3: system("telnet 210.120.128.180"); break;

                default:

                        if(input !=1 && input !=2 && input !=3)

                                printf("잘못 입력하셨습니다. 접속을 종료합니다.\n");

        }

        return 0;

}

 

■ 원본 소스코드

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <string.h>

#include <signal.h>


void sig_func( int sig )

{

        printf( "Can't use ctrl+c\n" );

}


int main(){


    int i;


    system("cat hint");

    getchar();

    system("clear");

    printf("\n  #####################################\n");

    printf("  ##                                 ##\n");

    printf("  ##         텔넷 접속 서비스        ##\n");

    printf("  ##                                 ##\n");

    printf("  ##                                 ##\n");

    printf("  ##     1. 하이텔     2. 나우누리   ##\n");

    printf("  ##     3. 천리안                   ##\n");

    printf("  ##                                 ##\n");

    printf("  #####################################\n");


    signal( SIGINT, sig_func );

    printf("\n접속하고 싶은 bbs를 선택하세요 : ");

    scanf( "%d", &i );


    if( i==1 ) system("telnet 203.245.15.76");

    if( i==2 ) system("telnet 203.238.129.97");

    if( i==3 ) system("telnet 210.120.128.180");

    if( i!=1 && i!=2 && i!=3 )

        printf("잘못 입력하셨습니다. 접속을 종료합니다.\n");


}