mirror of
https://github.com/SoftEtherVPN/SoftEtherVPN.git
synced 2024-11-25 10: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:
parent
3b932f5fee
commit
35077deaf1
@ -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)
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user