Headline
CVE-2023-32634: TALOS-2023-1755 || Cisco Talos Intelligence Group
An authentication bypass vulnerability exists in the CiRpcServerThread() functionality of SoftEther VPN 5.01.9674 and 4.41-9782-beta. An attacker can perform a local man-in-the-middle attack to trigger this vulnerability.
SUMMARY
An authentication bypass vulnerability exists in the CiRpcServerThread() functionality of SoftEther VPN 5.01.9674 and 4.41-9782-beta. An attacker can perform a local man-in-the-middle attack to trigger this vulnerability.
CONFIRMED VULNERABLE VERSIONS
The versions below were either tested or verified to be vulnerable by Talos or confirmed to be vulnerable by the vendor.
SoftEther VPN 4.41-9782-beta
SoftEther VPN 5.01.9674
PRODUCT URLS
SoftEther VPN - https://www.softether.org/
CVSSv3 SCORE
7.8 - CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
CWE
CWE-300 - Channel Accessible by Non-Endpoint (‘Man-in-the-Middle’)
DETAILS
SoftEther is a multi-platform VPN project that provides both server and client code to connect over a variety of VPN protocols, including Wireguard, PPTP, SSTP, L2TP, etc. SoftEther has a variety of features for both enterprise and personal use, and enables Nat Traversal out-of-the-box for remote-access setups behind firewalls.
Within the SoftEtherVPN client there exists some interesting architectural decisions, first and foremost being the fact that the SoftEtherVPN client itself consists of an RPC client and an RPC server. While the process differs on Linux and Windows, there are some core common elements. The VPN client’s RPC server binds to a given TCP port within the range of 9931-9936, on all interfaces, and then the VPN client’s RPC client connects to that port to send the VPN client commands. For ease of reference, we will be referring to these components as the RPC client and RPC server, but just know that these are both contained within the client-side portion of SoftEtherVPN.
For this particular vulnerability, let us examine exactly how the server does network configuration on Linux:
// RPC server thread
void CiRpcServerThread(THREAD *thread, void *param)
{
CLIENT *c;
SOCK *listener;
UINT i;
LIST *thread_list;
// Validate arguments
if (thread == NULL || param == NULL)
{
return;
}
c = (CLIENT *)param;
// RPC connection list
c->RpcConnectionList = NewList(NULL);
// Open the port
listener = NULL;
for (i = CLIENT_CONFIG_PORT;i < (CLIENT_CONFIG_PORT + 5);i++) // 9931; 9936 // [1]
{
listener = Listen(i); // [2]
if (listener != NULL)
{
break;
}
}
if (listener == NULL)
{
// Error
Alert(CEDAR_PRODUCT_STR " VPN Client RPC Port Open Failed.", CEDAR_CLIENT_STR);
return;
}
At [1], we see that we loop over the specific listen ports. At [2], we try to create a listener. Assuming that the listener fails, the RPC Server just keeps increasing the port until it finds an open one. But this begs the question of how the RPC Client knows which port to connect to, and we find the answer in CcConnectRpcEx:
REMOTE_CLIENT *CcConnectRpcEx(char *server_name, char *password, bool *bad_pass, bool *no_remote, UCHAR *key, UINT *key_error_code, bool shortcut_disconnect, UINT wait_retry)
{
// [...]
#ifdef OS_WIN32
// read the current port number from the registry of the localhost
if (StrCmpi(server_name, "localhost") == 0)
{
reg_port = MsRegReadIntEx2(REG_LOCAL_MACHINE, CLIENT_WIN32_REGKEYNAME, CLIENT_WIN32_REGVALUE_PORT, false, true); // [3]
reg_pid = MsRegReadIntEx2(REG_LOCAL_MACHINE, CLIENT_WIN32_REGKEYNAME, CLIENT_WIN32_REGVALUE_PID, false, true);
if (reg_pid != 0)
{
if (MsIsServiceRunning(GC_SVC_NAME_VPNCLIENT) == false)
{
reg_port = 0;
}
}
else
{
reg_port = 0;
}
}
if (reg_port != 0)
{
s = Connect(server_name, reg_port); // [4]
if (s != NULL)
{
goto L_TRY;
}
}
#endif // OS_WIN32
At least for Windows, assuming our server is running on local host, then a registry key is grabbed at [3] which contains the correct port to connect to [4]. But if the SoftEther VPN service is not running, or we are connecting remotely, or if we are using a Linux RPC client, then we hit the following code:
port_start = CLIENT_CONFIG_PORT - 1;
RETRY:
port_start++;
if (port_start >= (CLIENT_CONFIG_PORT + 5))
{
return NULL;
}
ok = false;
while (true)
{
for (i = port_start;i < (CLIENT_CONFIG_PORT + 5);i++) // [5]
{
if (CheckTCPPort(server_name, i)) // [6]
{
ok = true;
break;
}
// [...]
}
if (ok == false)
{
if (key_error_code)
{
*key_error_code = ERR_CONNECT_FAILED;
}
return NULL;
}
port_start = i;
s = Connect(server_name, i); // [7]
Again, we iterate over our specific port range at [5] and try to see if the TCP port is open at [6]. If the TCP port is closed, it is not considered an error unless all ports are closed. Assuming we’ve found a valid open port, we connect at [7].
With all this in mind, a vulnerability is apparent. Assume that a malicious user on a computer that’s expected to be running a SoftEtherVPN client binds to the first port available (9930 or 9931, which is possible without admin privileges), which the RPC server would normally bind to. When a normal SoftEtherVPN client user starts up their client, nothing errors out, since the RPC server would bind to the next available port after the malicious MitM port. The legitimate RPC client meanwhile would connect automatically to the malicious man-in-the-middle port without ever knowing their mistake and would send traffic unencrypted through the man-in-the-middle.
Assuming this occurs, locally or remotely, the attacker has gained a large attack surface. Aside from potentially exploiting other bugs within the RPC client and RPC server, the attacker would have the same access to the VPN client, potentially gaining access to sensitive networks. Alternatively, they could subsequently set up a man-in-the-middle attack against the VPN network itself, potentially leading to further escalation into the network. This could be done crudely either by adding a malicious trusted CA to the SoftEtherVPN client (which would write to the disk), or by intercepting and manipulating the network traffic between the RPC server and RPC client, such that untrusted CA certificates on the remote VPN side were automatically trusted without popping up a message.
Crash Information
$ python2 decept.py 127.0.0.1 9931 127.0.0.1 9932 -l tcp -r tcp --timeout .1 --dont_kill
[<_<] Decept proxy/sniffer [>_>]
[*.*] Listening on 127.0.0.1:9931
[$.$] local:tcp|remote:tcp
[>.>] 12:15:37.349951 Received Connection from ('127.0.0.1', 52198)
[>.>] 12:15:37.467882 Received Connection from ('127.0.0.1', 52208)
0000 00 00 00 01 f9 6c ea 19 8a d1 dd 56 17 ac 08 4a .....l.....V...J
0010 3d 92 c6 10 77 08 c0 ef =...w...
[o.o] 12:15:37.626257 Sent 24 bytes to remote (127.0.0.1:52208->127.0.0.1:9932)
0000 00 00 00 00 ....
[o.o] 12:15:37.733871 Sent 4 bytes to local (127.0.0.1:52208<-127.0.0.1:9932)
0000 00 00 00 31 00 00 00 01 00 00 00 0e 66 75 6e 63 ...1........func
0010 74 69 6f 6e 5f 6e 61 6d 65 00 00 00 02 00 00 00 tion_name.......
0020 01 00 00 00 10 47 65 74 43 6c 69 65 6e 74 56 65 .....GetClientVe
0030 72 73 69 6f 6e rsion
[o.o] 12:15:37.841662 Sent 53 bytes to remote (127.0.0.1:52208->127.0.0.1:9932)
0000 00 00 01 c3 00 00 00 0b 00 00 00 16 43 6c 69 65 ............Clie
0010 6e 74 42 75 69 6c 64 49 6e 66 6f 53 74 72 69 6e ntBuildInfoStrin
0020 67 00 00 00 02 00 00 00 01 00 00 00 30 43 6f 6d g...........0Com
0030 70 69 6c 65 64 20 32 30 32 33 2f 30 31 2f 31 38 piled 2023/01/18
0040 20 31 37 3a 33 35 3a 34 37 20 62 79 20 74 68 69 17:35:47 by thi
0050 65 66 79 20 61 74 20 6b 69 6c 6c 6d 65 00 00 00 efy at killme...
0060 0f 43 6c 69 65 6e 74 42 75 69 6c 64 49 6e 74 00 .ClientBuildInt.
0070 00 00 00 00 00 00 01 00 00 14 3c 00 00 00 12 43 ..........<....C
0080 6c 69 65 6e 74 50 72 6f 64 75 63 74 4e 61 6d 65 lientProductName
0090 00 00 00 02 00 00 00 01 00 00 00 26 53 6f 66 74 ...........&Soft
00a0 45 74 68 65 72 20 56 50 4e 20 43 6c 69 65 6e 74 Ether VPN Client
00b0 20 44 65 76 65 6c 6f 70 65 72 20 45 64 69 74 69 Developer Editi
00c0 6f 6e 00 00 00 0d 43 6c 69 65 6e 74 56 65 72 49 on....ClientVerI
00d0 6e 74 00 00 00 00 00 00 00 01 00 00 01 f6 00 00 nt..............
00e0 00 14 43 6c 69 65 6e 74 56 65 72 73 69 6f 6e 53 ..ClientVersionS
00f0 74 72 69 6e 67 00 00 00 02 00 00 00 01 00 00 00 tring...........
0100 23 56 65 72 73 69 6f 6e 20 35 2e 30 32 20 42 75 #Version 5.02 Bu
0110 69 6c 64 20 35 31 38 30 20 20 20 28 45 6e 67 6c ild 5180 (Engl
0120 69 73 68 29 00 00 00 0f 49 73 56 67 63 53 75 70 ish)....IsVgcSup
0130 70 6f 72 74 65 64 00 00 00 00 00 00 00 01 00 00 ported..........
0140 00 00 00 00 00 14 49 73 56 4c 61 6e 4e 61 6d 65 ......IsVLanName
0150 52 65 67 75 6c 61 74 65 64 00 00 00 00 00 00 00 Regulated.......
0160 01 00 00 00 00 00 00 00 07 4f 73 54 79 70 65 00 .........OsType.
0170 00 00 00 00 00 00 01 00 00 0c 1c 00 00 00 0a 50 ...............P
0180 72 6f 63 65 73 73 49 64 00 00 00 00 00 00 00 01 rocessId........
0190 00 00 00 00 00 00 00 0c 53 68 6f 77 56 67 63 4c ........ShowVgcL
01a0 69 6e 6b 00 00 00 00 00 00 00 01 00 00 00 00 00 ink.............
01b0 00 00 09 43 6c 69 65 6e 74 49 64 00 00 00 02 00 ...ClientId.....
01c0 00 00 01 00 00 00 00 .......
[o.o] 12:15:37.949690 Sent 455 bytes to local (127.0.0.1:52208<-127.0.0.1:9932)
VENDOR RESPONSE
The vendor issued an advisory: https://www.softether.org/9-about/News/904-SEVPN202301
TIMELINE
2023-06-12 - Vendor Disclosure
2023-06-30 - Vendor Patch Release
2023-10-12 - Public Release
Discovered by Lilith >_> of Cisco Talos.
Related news
Attackers could exploit these vulnerabilities in the SoftEther VPN solution for individual and enterprise users to force users to drop their connections or execute arbitrary code on the targeted machine.
An information disclosure vulnerability exists in the ClientConnect() functionality of SoftEther VPN 5.01.9674. A specially crafted network packet can lead to a disclosure of sensitive information. An attacker can perform a man-in-the-middle attack to trigger this vulnerability.