1
0
mirror of https://github.com/SoftEtherVPN/SoftEtherVPN.git synced 2024-12-26 01:59:53 +03:00

Fix Vulnerability: CVE-2023-25774 TALOS-2023-1743

SoftEther VPN vpnserver ConnectionAccept () denial of service vulnerability
This commit is contained in:
Daiyuu Nobori 2023-10-09 17:13:41 +02:00 committed by Davide Beatrici
parent 3b932f5fee
commit 35077deaf1
5 changed files with 309 additions and 10 deletions

View File

@ -726,9 +726,8 @@ void AdminWebProcPost(CONNECTION *c, SOCK *s, HTTP_HEADER *h, UINT post_data_siz
if (RecvAll(s, data, post_data_size, s->SecureMode))
{
c->JsonRpcAuthed = true;
#ifndef GC_SOFTETHER_OSS
RemoveDosEntry(c->Listener, s);
#endif // GC_SOFTETHER_OSS
// Divide url_target into URL and query string
StrCpy(url, sizeof(url), url_target);
@ -767,9 +766,8 @@ void AdminWebProcGet(CONNECTION *c, SOCK *s, HTTP_HEADER *h, char *url_target)
}
c->JsonRpcAuthed = true;
#ifndef GC_SOFTETHER_OSS
RemoveDosEntry(c->Listener, s);
#endif // GC_SOFTETHER_OSS
// Divide url_target into URL and query string
StrCpy(url, sizeof(url), url_target);
@ -1199,9 +1197,7 @@ void JsonRpcProcOptions(CONNECTION *c, SOCK *s, HTTP_HEADER *h, char *url_target
c->JsonRpcAuthed = true;
#ifndef GC_SOFTETHER_OSS
RemoveDosEntry(c->Listener, s);
#endif // GC_SOFTETHER_OSS
AdminWebSendBody(s, 200, "OK", NULL, 0, NULL, NULL, NULL, h);
}
@ -1228,9 +1224,7 @@ void JsonRpcProcGet(CONNECTION *c, SOCK *s, HTTP_HEADER *h, char *url_target)
c->JsonRpcAuthed = true;
#ifndef GC_SOFTETHER_OSS
RemoveDosEntry(c->Listener, s);
#endif // GC_SOFTETHER_OSS
// Divide url_target into URL and query string
StrCpy(url, sizeof(url), url_target);
@ -1357,9 +1351,7 @@ void JsonRpcProcPost(CONNECTION *c, SOCK *s, HTTP_HEADER *h, UINT post_data_size
c->JsonRpcAuthed = true;
#ifndef GC_SOFTETHER_OSS
RemoveDosEntry(c->Listener, s);
#endif // GC_SOFTETHER_OSS
if (json_req == NULL || json_req_object == NULL)
{

View File

@ -322,6 +322,34 @@ void DecrementNoSsl(CEDAR *c, IP *ip, UINT num_dec)
UnlockList(c->NonSslList);
}
// Check whether the specified IP address is in Non-SSL connection list
bool IsInNoSsl(CEDAR *c, IP *ip)
{
bool ret = false;
// Validate arguments
if (c == NULL || ip == NULL)
{
return false;
}
LockList(c->NonSslList);
{
NON_SSL *n = SearchNoSslList(c, ip);
if (n != NULL)
{
if (n->EntryExpires > Tick64() && n->Count > NON_SSL_MIN_COUNT)
{
n->EntryExpires = Tick64() + (UINT64)NON_SSL_ENTRY_EXPIRES;
ret = true;
}
}
}
UnlockList(c->NonSslList);
return ret;
}
// Add new entry to Non-SSL connection list
bool AddNoSsl(CEDAR *c, IP *ip)
{
@ -704,6 +732,47 @@ void DelConnection(CEDAR *cedar, CONNECTION *c)
UnlockList(cedar->ConnectionList);
}
// Get the number of unestablished connections
UINT GetUnestablishedConnections(CEDAR *cedar)
{
UINT i, ret;
// Validate arguments
if (cedar == NULL)
{
return 0;
}
ret = 0;
LockList(cedar->ConnectionList);
{
for (i = 0;i < LIST_NUM(cedar->ConnectionList);i++)
{
CONNECTION *c = LIST_DATA(cedar->ConnectionList, i);
switch (c->Type)
{
case CONNECTION_TYPE_CLIENT:
case CONNECTION_TYPE_INIT:
case CONNECTION_TYPE_LOGIN:
case CONNECTION_TYPE_ADDITIONAL:
switch (c->Status)
{
case CONNECTION_STATUS_ACCEPTED:
case CONNECTION_STATUS_NEGOTIATION:
case CONNECTION_STATUS_USERAUTH:
ret++;
break;
}
break;
}
}
}
UnlockList(cedar->ConnectionList);
return ret + Count(cedar->AcceptingSockets);
}
// Add connection to Cedar
void AddConnection(CEDAR *cedar, CONNECTION *c)
{

View File

@ -1022,6 +1022,7 @@ void DelHubEx(CEDAR *c, HUB *h, bool no_lock);
void StopAllHub(CEDAR *c);
void StopAllConnection(CEDAR *c);
void AddConnection(CEDAR *cedar, CONNECTION *c);
UINT GetUnestablishedConnections(CEDAR *cedar);
void DelConnection(CEDAR *cedar, CONNECTION *c);
void SetCedarCipherList(CEDAR *cedar, char *name);
void InitCedar();
@ -1046,6 +1047,7 @@ bool AddNoSsl(CEDAR *c, IP *ip);
void DecrementNoSsl(CEDAR *c, IP *ip, UINT num_dec);
void DeleteOldNoSsl(CEDAR *c);
NON_SSL *SearchNoSslList(CEDAR *c, IP *ip);
bool IsInNoSsl(CEDAR *c, IP *ip);
void FreeTinyLog(TINY_LOG *t);
void WriteTinyLog(TINY_LOG *t, char *str);
TINY_LOG *NewTinyLog();

View File

@ -17,6 +17,7 @@
#include "Mayaqua/Memory.h"
#include "Mayaqua/Object.h"
#include "Mayaqua/Str.h"
#include "Mayaqua/Tick64.h"
static bool disable_dos = false;
static UINT max_connections_per_ip = DEFAULT_MAX_CONNECTIONS_PER_IP;
@ -181,6 +182,11 @@ void TCPAcceptedThread(THREAD *t, void *param)
ConnectionAccept(c);
flag1 = c->flag1;
if (c->JsonRpcAuthed)
{
RemoveDosEntry(r, s);
}
// Release
SLog(r->Cedar, "LS_CONNECTION_END_1", c->Name);
ReleaseListener(c->Listener);
@ -221,6 +227,46 @@ void TCPAccepted(LISTENER *r, SOCK *s)
num_clients_from_this_ip = GetNumIpClient(&s->RemoteIP);
#ifdef USE_DOS_ATTACK_DETECTION
if (disable_dos == false && r->DisableDos == false && r->Protocol != LISTENER_INPROC)
{
UINT max_uec, now_uec;
// DOS attack check
if (CheckDosAttack(r, s) == false)
{
Debug("DOS Attack 1 !!\n");
IPToStr(tmp, sizeof(tmp), &s->RemoteIP);
SLog(r->Cedar, "LS_LISTENER_DOS", r->Port, tmp, s->RemotePort);
return;
}
if (StrCmpi(s->UnderlayProtocol, SOCK_UNDERLAY_NATIVE_V6) == 0 ||
StrCmpi(s->UnderlayProtocol, SOCK_UNDERLAY_NATIVE_V4) == 0)
{
if (IsInNoSsl(r->Cedar, &s->RemoteIP))
{
Debug("DOS Attack 2 !!\n");
IPToStr(tmp, sizeof(tmp), &s->RemoteIP);
SLog(r->Cedar, "LS_LISTENER_DOS", r->Port, tmp, s->RemotePort);
return;
}
}
if (num_clients_from_this_ip > GetMaxConnectionsPerIp())
{
Debug("DOS Attack 3 !!\n");
IPToStr(tmp, sizeof(tmp), &s->RemoteIP);
SLog(r->Cedar, "LS_LISTENER_DOS", r->Port, tmp, s->RemotePort);
return;
}
max_uec = GetMaxUnestablishedConnections();
now_uec = GetUnestablishedConnections(cedar);
if (now_uec > max_uec)
{
Debug("DOS Attack 4 !!\n");
SLog(r->Cedar, "LS_LISTENER_MAXUEC", max_uec, now_uec);
return;
}
}
#endif // USE_DOS_ATTACK_DETECTION
IPToStr(tmp, sizeof(tmp), &s->RemoteIP);
@ -239,6 +285,169 @@ void TCPAccepted(LISTENER *r, SOCK *s)
ReleaseThread(t);
}
// Remove a DOS entry
bool RemoveDosEntry(LISTENER *r, SOCK *s)
{
DOS *d;
bool ok = false;
// Validate arguments
if (r == NULL || s == NULL)
{
return false;
}
LockList(r->DosList);
{
// Delete old entries from the DOS attack list
RefreshDosList(r);
// Search the table
d = SearchDosList(r, &s->RemoteIP);
if (d != NULL)
{
Delete(r->DosList, d);
Free(d);
ok = true;
}
}
UnlockList(r->DosList);
return ok;
}
// Check whether this is a DOS attack
bool CheckDosAttack(LISTENER *r, SOCK *s)
{
DOS *d;
bool ok = true;
// Validate arguments
if (r == NULL || s == NULL)
{
return false;
}
LockList(r->DosList);
{
// Delete old entries from the DOS attack list
RefreshDosList(r);
// Search the table
d = SearchDosList(r, &s->RemoteIP);
if (d != NULL)
{
// There is a entry already
// This should mean being under a DOS attack
d->LastConnectedTick = Tick64();
d->CurrentExpireSpan = MIN(d->CurrentExpireSpan * (UINT64)2, DOS_TABLE_EXPIRES_MAX);
d->AccessCount++;
if (d->AccessCount > DOS_TABLE_MAX_LIMIT_PER_IP)
{
ok = false;
}
}
else
{
// Create a new entry
d = ZeroMalloc(sizeof(DOS));
d->CurrentExpireSpan = (UINT64)DOS_TABLE_EXPIRES_FIRST;
d->FirstConnectedTick = d->LastConnectedTick = Tick64();
d->AccessCount = 1;
d->DeleteEntryTick = d->FirstConnectedTick + (UINT64)DOS_TABLE_EXPIRES_TOTAL;
Copy(&d->IpAddress, &s->RemoteIP, sizeof(IP));
Add(r->DosList, d);
}
}
UnlockList(r->DosList);
return ok;
}
// Delete old entries from the DOS attack list
void RefreshDosList(LISTENER *r)
{
// Validate arguments
if (r == NULL)
{
return;
}
if (r->DosListLastRefreshTime == 0 ||
(r->DosListLastRefreshTime + (UINT64)DOS_TABLE_REFRESH_INTERVAL) <= Tick64())
{
UINT i;
LIST *o;
r->DosListLastRefreshTime = Tick64();
o = NewListFast(NULL);
for (i = 0;i < LIST_NUM(r->DosList);i++)
{
DOS *d = LIST_DATA(r->DosList, i);
if ((d->LastConnectedTick + d->CurrentExpireSpan) <= Tick64() ||
(d->DeleteEntryTick <= Tick64()))
{
Add(o, d);
}
}
for (i = 0;i < LIST_NUM(o);i++)
{
DOS *d = LIST_DATA(o, i);
Delete(r->DosList, d);
Free(d);
}
ReleaseList(o);
}
}
// Search the DOS attack list by the IP address
DOS *SearchDosList(LISTENER *r, IP *ip)
{
DOS *d, t;
// Validate arguments
if (r == NULL || ip == NULL)
{
return NULL;
}
Copy(&t.IpAddress, ip, sizeof(IP));
d = Search(r->DosList, &t);
if (d != NULL)
{
if ((d->LastConnectedTick + d->CurrentExpireSpan) <= Tick64() ||
(d->DeleteEntryTick <= Tick64()))
{
// Delete old entries
Delete(r->DosList, d);
Free(d);
return NULL;
}
}
return d;
}
// Comparison of DOS attack list entries
int CompareDos(void *p1, void *p2)
{
DOS *d1, *d2;
if (p1 == NULL || p2 == NULL)
{
return 0;
}
d1 = *(DOS **)p1;
d2 = *(DOS **)p2;
if (d1 == NULL || d2 == NULL)
{
return 0;
}
return CmpIpAddr(&d1->IpAddress, &d2->IpAddress);
}
// UDP listener main loop
void ListenerUDPMainLoop(LISTENER *r)
@ -653,6 +862,13 @@ void CleanupListener(LISTENER *r)
return;
}
// Release the DOS attack list
for (i = 0;i < LIST_NUM(r->DosList);i++)
{
DOS *d = LIST_DATA(r->DosList, i);
Free(d);
}
ReleaseList(r->DosList);
if (r->Sock != NULL)
{
@ -802,6 +1018,7 @@ LISTENER *NewListenerEx5(CEDAR *cedar, UINT proto, UINT port, THREAD_PROC *proc,
r->Port = port;
r->Event = NewEvent();
r->DosList = NewList(CompareDos);
r->LocalOnly = local_only;
r->ShadowIPv6 = shadow_ipv6;

View File

@ -10,12 +10,24 @@
#include "CedarType.h"
#include "Mayaqua/MayaType.h"
#include "Mayaqua/Kernel.h"
#include "Mayaqua/Network.h"
// Function to call when receiving a new connection
typedef void (NEW_CONNECTION_PROC)(CONNECTION *c);
// DOS attack list
struct DOS
{
IP IpAddress; // IP address
UINT64 FirstConnectedTick; // Time which a client connects at the first time
UINT64 LastConnectedTick; // Time which a client connected at the last time
UINT64 CurrentExpireSpan; // Current time-out period of this record
UINT64 DeleteEntryTick; // Time planned to delete this entry
UINT AccessCount; // The number of accesses
};
// Listener structure
struct LISTENER
@ -31,6 +43,8 @@ struct LISTENER
volatile bool Halt; // Halting flag
UINT Status; // State
LIST *DosList; // DOS attack list
UINT64 DosListLastRefreshTime; // Time that the DOS list is refreshed at the last
THREAD_PROC *ThreadProc; // Thread procedure
void *ThreadParam; // Thread parameters
@ -105,6 +119,11 @@ void FreeDynamicListener(DYNAMIC_LISTENER *d);
bool ListenerRUDPRpcRecvProc(RUDP_STACK *r, UDPPACKET *p);
void ListenerSetProcRecvRpcEnable(bool b);
int CompareDos(void *p1, void *p2);
DOS *SearchDosList(LISTENER *r, IP *ip);
void RefreshDosList(LISTENER *r);
bool CheckDosAttack(LISTENER *r, SOCK *s);
bool RemoveDosEntry(LISTENER *r, SOCK *s);
#endif // LISTENER_H