mirror of
https://github.com/rehlds/rehlds.git
synced 2025-01-01 01:25:38 +03:00
Implemented reduction of impact caused by zip-bomb exploit
Added network security CVars: - sv_net_incoming_decompression (0-1) Enables or disables incoming data decompression - sv_net_incoming_decompression_max_ratio (0.0 - 100.0) Sets max allowed ratio between compressed and decompressed data. (A ratio close to 90 indicates large uncompressed data with low entropy) - sv_net_incoming_decompression_max_size (16-65536) Adjusts max size of output data after decompression.
This commit is contained in:
parent
7fcec97af4
commit
332c0e0323
@ -55,6 +55,9 @@ This means that plugins that do binary code analysis (Orpheu for example) probab
|
||||
<li>sv_rehlds_local_gametime <1|0> // A feature of local gametime which decrease "lags" if you run same map for a long time. Default: 0
|
||||
<li>sv_use_entity_file // Use custom entity file for a map. Path to an entity file will be "maps/[map name].ent". 0 - use original entities. 1 - use .ent files from maps directory. 2 - use .ent files from maps directory and create new .ent file if not exist.
|
||||
<li>sv_usercmd_custom_random_seed // When enabled server will populate an additional random seed independent of the client. Default: 0
|
||||
<li>sv_net_incoming_decompression <1|0> // When enabled server will decompress of incoming compressed file transfer payloads. Default: 1
|
||||
<li>sv_net_incoming_decompression_max_ratio <0|100> // Sets the max allowed ratio between compressed and uncompressed data for file transfer. (A ratio close to 90 indicates large uncompressed data with low entropy) Default: 80.0
|
||||
<li>sv_net_incoming_decompression_max_size <16|65536> // Sets the max allowed size for decompressed file transfer data. Default: 65536 bytes
|
||||
</ul>
|
||||
</details>
|
||||
|
||||
|
@ -36,6 +36,11 @@ cvar_t net_showpackets = { "net_showpackets", "0", 0, 0.0f, nullptr};
|
||||
cvar_t net_showdrop = { "net_showdrop", "0", 0, 0.0f, nullptr};
|
||||
cvar_t net_drawslider = { "net_drawslider", "0", 0, 0.0f, nullptr};
|
||||
cvar_t net_chokeloopback = { "net_chokeloop", "0", 0, 0.0f, nullptr};
|
||||
|
||||
cvar_t sv_net_incoming_decompression = { "sv_net_incoming_decompression", "1", 0, 1.0f, nullptr };
|
||||
cvar_t sv_net_incoming_decompression_max_ratio = { "sv_net_incoming_decompression_max_ratio", "75.0", 0, 75.0f, nullptr };
|
||||
cvar_t sv_net_incoming_decompression_max_size = { "sv_net_incoming_decompression_max_size", "65536", 0, 65536.0f, nullptr };
|
||||
|
||||
cvar_t sv_filetransfercompression = { "sv_filetransfercompression", "1", 0, 0.0f, nullptr};
|
||||
cvar_t sv_filetransfermaxsize = { "sv_filetransfermaxsize", "10485760", 0, 0.0f, nullptr};
|
||||
|
||||
@ -1431,6 +1436,9 @@ qboolean Netchan_CopyNormalFragments(netchan_t *chan)
|
||||
|
||||
p = chan->incomingbufs[FRAG_NORMAL_STREAM];
|
||||
|
||||
chan->incomingbufs[FRAG_NORMAL_STREAM] = nullptr;
|
||||
chan->incomingready[FRAG_NORMAL_STREAM] = FALSE;
|
||||
|
||||
SZ_Clear(&net_message);
|
||||
MSG_BeginReading();
|
||||
|
||||
@ -1468,27 +1476,68 @@ qboolean Netchan_CopyNormalFragments(netchan_t *chan)
|
||||
}
|
||||
|
||||
SZ_Clear(&net_message);
|
||||
|
||||
chan->incomingbufs[FRAG_NORMAL_STREAM] = nullptr;
|
||||
chan->incomingready[FRAG_NORMAL_STREAM] = FALSE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
#endif // REHLDS_FIXES
|
||||
|
||||
qboolean success = TRUE;
|
||||
|
||||
if (*(uint32 *)net_message.data == MAKEID('B', 'Z', '2', '\0'))
|
||||
{
|
||||
// Determine whether decompression of compressed data is allowed
|
||||
#ifdef REHLDS_FIXES
|
||||
if (!sv_net_incoming_decompression.value && ((chan->player_slot - 1) == host_client - g_psvs.clients) &&
|
||||
// compressed data is expected only after requesting resource list
|
||||
!host_client->m_sendrescount)
|
||||
{
|
||||
success = FALSE;
|
||||
Con_Printf("%s: Incoming compressed data disallowed from %s\n", __func__, host_client->name);
|
||||
}
|
||||
#endif
|
||||
|
||||
char uncompressed[65536];
|
||||
unsigned int uncompressedSize = 65536;
|
||||
BZ2_bzBuffToBuffDecompress(uncompressed, &uncompressedSize, (char*)net_message.data + 4, net_message.cursize - 4, 1, 0);
|
||||
Q_memcpy(net_message.data, uncompressed, uncompressedSize);
|
||||
net_message.cursize = uncompressedSize;
|
||||
unsigned int uncompressedSize = clamp((int)sv_net_incoming_decompression_max_size.value, 16, 65536); // valid range (16 - 65536) bytes
|
||||
unsigned int compressedSize = net_message.cursize - 4;
|
||||
|
||||
// Decompress net buffer data
|
||||
if (success && (BZ2_bzBuffToBuffDecompress(uncompressed, &uncompressedSize, (char *)net_message.data + 4, compressedSize, 1, 0) == BZ_OK))
|
||||
{
|
||||
#ifdef REHLDS_FIXES
|
||||
// Check for an abnormal size ratio between compressed and uncompressed data
|
||||
if (sv_net_incoming_decompression_max_ratio.value > 0 && compressedSize < uncompressedSize)
|
||||
{
|
||||
float ratio = ((float)(uncompressedSize - compressedSize) / uncompressedSize) * 100.0f;
|
||||
if (ratio >= sv_net_incoming_decompression_max_ratio.value)
|
||||
{
|
||||
if (chan->player_slot == 0)
|
||||
Con_Printf("%s: Incoming abnormal uncompressed size with ratio %.2f\n", __func__, ratio);
|
||||
else
|
||||
Con_Printf("%s: Incoming abnormal uncompressed size with ratio %.2f from %s\n", __func__, ratio, g_psvs.clients[chan->player_slot - 1].name);
|
||||
|
||||
success = FALSE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Copy uncompressed data back to the net buffer
|
||||
Q_memcpy(net_message.data, uncompressed, uncompressedSize);
|
||||
net_message.cursize = uncompressedSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
// malformed data or compressed data exceeding sv_net_incoming_decompression_max_size
|
||||
success = FALSE;
|
||||
}
|
||||
|
||||
// Drop client if decompression was unsuccessful
|
||||
if (!success && ((chan->player_slot - 1) == host_client - g_psvs.clients))
|
||||
{
|
||||
SV_DropClient(host_client, FALSE, "Malformed/abnormal compressed data");
|
||||
SZ_Clear(&net_message);
|
||||
}
|
||||
}
|
||||
|
||||
chan->incomingbufs[FRAG_NORMAL_STREAM] = nullptr;
|
||||
chan->incomingready[FRAG_NORMAL_STREAM] = FALSE;
|
||||
|
||||
return TRUE;
|
||||
return success;
|
||||
}
|
||||
|
||||
qboolean Netchan_CopyFileFragments(netchan_t *chan)
|
||||
@ -1824,6 +1873,11 @@ void Netchan_Init(void)
|
||||
Cvar_RegisterVariable(&net_chokeloopback);
|
||||
Cvar_RegisterVariable(&net_drawslider);
|
||||
Cvar_RegisterVariable(&sv_filetransfercompression);
|
||||
#ifdef REHLDS_FIXES
|
||||
Cvar_RegisterVariable(&sv_net_incoming_decompression);
|
||||
Cvar_RegisterVariable(&sv_net_incoming_decompression_max_ratio);
|
||||
Cvar_RegisterVariable(&sv_net_incoming_decompression_max_size);
|
||||
#endif
|
||||
Cvar_RegisterVariable(&sv_filetransfermaxsize);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user