Skip to content

Commit 086510f

Browse files
authored
1 parent 7e13119 commit 086510f

File tree

1 file changed

+64
-63
lines changed

1 file changed

+64
-63
lines changed

addons/sourcemod/scripting/FixSprayExploit.sp

Lines changed: 64 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
* Spray Exploit Fixer
3-
* Copyright (C) 2024 Silvers
3+
* Copyright (C) 2025 Silvers
44
*
55
* This program is free software: you can redistribute it and/or modify
66
* it under the terms of the GNU General Public License as published by
@@ -32,19 +32,26 @@
3232
========================================================================================
3333
Change Log:
3434
35-
2.25 (28-Jan-2024)
36-
- Fixed memory leak caused by clearing StringMap/ArrayList data instead of deleting.
37-
2.24 (17-Jul-2023)
38-
- Now less check of GetClientAuthId by storing them. Thanks to ".Rushaway".
35+
2.25 (04-Jan-2025)
36+
- Changes to the recent exploit fix. Thanks to "Madness (null138)" for fixing and reporting.
3937
40-
2.23 (01-Apr-2023)
41-
- Now only TestClient depending cvar
42-
- Now ban lenght can be adjusted via cvar
38+
2.24 (14-Dec-2024)
39+
- Added forward "OnSprayExploit" for 3rd party plugins. Requested by ".Rushaway".
40+
- Fixed another Spray exploit. Thanks to "Madness (null138)" for fixing and reporting.
4341
44-
2.22 (19-Feb-2023)
45-
- Now prevents even more log spamming duplicate entries. Thanks to ".Rushaway" for reporting.
42+
2.23 (05-Nov-2024) - Update by ".Rushaway"
43+
- Added cvar "spray_exploit_fixer_punish" to specify which exploits to test for.
44+
- Added cvar "spray_exploit_fixer_bantime" to set the ban length.
45+
- Now less checks of GetClientAuthId by storing them.
46+
- Switch to Steam3 format for AuthID.
47+
- Fixed g_smWaiting not removing data for unverified clients.
48+
- Prevent g_smWaiting not removing data if client was already disconnected.
49+
- LogAction now print infos even if client is not verified.
50+
51+
2.22 (28-Jan-2024)
52+
- Fixed memory leak caused by clearing StringMap/ArrayList data instead of deleting.
4653
47-
2.21 (22-Jan-2023)
54+
2.21 (19-Feb-2023)
4855
- Now prevents even more log spamming duplicate entries. Thanks to ".Rushaway" for reporting.
4956
5057
2.20 (20-Jan-2023)
@@ -116,7 +123,7 @@
116123
- Changes to fix warnings when compiling on SourceMod 1.11.
117124
118125
2.2 (30-Jun-2021)
119-
- Fixed another Spray exploit. Thanks to "Madness (null138)" for fixing and reporting.
126+
- Fixed another spray exploit. Thanks to "Madness (null138)" for fixing and reporting.
120127
121128
2.1 (31-Mar-2021)
122129
- Added a check for "sm_sprays_allowed" in the command admin_overrides.cfg to only allow specific flag groups to use sprays.
@@ -180,7 +187,7 @@
180187

181188

182189
#define MAX_READ 50
183-
#define TIMEOUT_LOG 5.0
190+
#define TIMEOUT_LOG 10.0
184191
#define PATH_BACKUP "backup_sprays"
185192

186193
int g_iVal[] = {86,84,70,0,7,0,0,0,42,0,0,0,42,0,0,0,42,42,42,42,42,42,42,42,42,42,42,0,0,0,0,0,0,0,0,0};
@@ -204,6 +211,7 @@ bool g_bProc;
204211
bool g_bDecal;
205212
bool g_bSourceBans;
206213
bool g_bMaterialAdmin;
214+
GlobalForward g_hExploit;
207215

208216

209217

@@ -235,6 +243,8 @@ public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max
235243
g_iEngine = GetEngineVersion();
236244
g_bLate = late;
237245

246+
g_hExploit = new GlobalForward("OnSprayExploit", ET_Ignore, Param_Cell, Param_Cell, Param_Cell);
247+
238248
return APLRes_Success;
239249
}
240250

@@ -271,17 +281,17 @@ public void OnPluginStart()
271281
}
272282

273283
CreateConVar( "spray_exploit_fixer", PLUGIN_VERSION, "Spray Exploit Fixer plugin version.", FCVAR_DONTRECORD);
274-
g_hCvarPunish = CreateConVar( "spray_exploit_fixer_punish_client", "0", "0=Off. 1=PlayerDecal. 2=FileCheck. 3=Both. Run the TestClient function");
284+
g_hCvarPunish = CreateConVar( "spray_exploit_fixer_punish", "3", "0=Off. 1=PlayerDecal. 2=FileCheck. 3=Both. Which exploits to test for.");
275285
if( g_iEngine != Engine_TF2 )
276286
{
277-
g_hCvarBan = CreateConVar( "spray_exploit_fixer_ban", "0", "0=Off. 1=Ban users who trigger invalid sprays (may still be some false positives).");
278-
g_hCvarKick = CreateConVar( "spray_exploit_fixer_kick", "0", "0=Off. 1=Kick users who trigger invalid sprays (may still be some false positives).");
279-
g_hCvarBanTime = CreateConVar( "spray_exploit_fixer_bantime", "5", "0=Perm. Ban time (in minutes)");
287+
g_hCvarBan = CreateConVar( "spray_exploit_fixer_ban", "0", "0=Off. 1=Ban users who trigger invalid sprays (may still be some false positives).");
288+
g_hCvarKick = CreateConVar( "spray_exploit_fixer_kick", "0", "0=Off. 1=Kick users who trigger invalid sprays (may still be some false positives).");
289+
g_hCvarBanTime = CreateConVar( "spray_exploit_fixer_bantime", "5", "0=Permanent. Ban time (in minutes).");
280290
}
281-
g_hCvarLog = CreateConVar( "spray_exploit_fixer_log", "1", "Logging saved to sourcemod/logs/spray_downloads.log: 0=Off. 1=Log all user uploads. 2=Log invalid sprays only.");
282-
g_hCvarMsg = CreateConVar( "spray_exploit_fixer_msg", "1", "Print to server console: 0=Off. 1=Missing sprays and invalid sprays. 2=Only invalid sprays.");
283-
g_hCvarPath = CreateConVar( "spray_exploit_fixer_path", g_sDownloads, "Path to the downloads folder of sprays. Add /cc/ if sprays are stored in individual 2 character folders. Must contain trailing / slash.");
284-
AutoExecConfig(true, "spray_exploit_fixer");
291+
g_hCvarLog = CreateConVar( "spray_exploit_fixer_log", "1", "Logging saved to sourcemod/logs/spray_downloads.log: 0=Off. 1=Log all user uploads. 2=Log invalid sprays only.");
292+
g_hCvarMsg = CreateConVar( "spray_exploit_fixer_msg", "1", "Print to server console: 0=Off. 1=Missing sprays and invalid sprays. 2=Only invalid sprays.");
293+
g_hCvarPath = CreateConVar( "spray_exploit_fixer_path", g_sDownloads, "Path to the downloads folder of sprays. Add /cc/ if sprays are stored in individual 2 character folders. Must contain trailing / slash.");
294+
AutoExecConfig(true, "spray_exploit_fixer");
285295
g_hCvarPath.AddChangeHook(ConVarChanged_Cvars);
286296

287297
g_smChecked = new StringMap();
@@ -326,11 +336,11 @@ public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3
326336
public void OnClientPutInServer(int client)
327337
{
328338
char sSteamID[64];
329-
GetClientAuthId(client, AuthId_Steam2, sSteamID, sizeof(sSteamID));
339+
GetClientAuthId(client, AuthId_Steam3, sSteamID, sizeof(sSteamID));
330340
FormatEx(g_sAuth[client], sizeof(g_sAuth[]), "%s", sSteamID);
331341

332342
char sSteamIDUnverified[32];
333-
GetClientAuthId(client, AuthId_Steam2, sSteamIDUnverified, sizeof(sSteamIDUnverified), false);
343+
GetClientAuthId(client, AuthId_Steam3, sSteamIDUnverified, sizeof(sSteamIDUnverified), false);
334344
FormatEx(g_sAuthUnverified[client], sizeof(g_sAuthUnverified[]), "%s", sSteamIDUnverified);
335345
}
336346

@@ -343,23 +353,17 @@ public void OnClientConnected(int client)
343353

344354
public void OnClientDisconnect(int client)
345355
{
346-
if( !IsFakeClient(client) )
347-
{
348-
if( IsClientConnected(client) )
349-
{
350-
//static char auth[32];
351-
//GetClientAuthId(client, AuthId_Steam2, auth, sizeof(auth), false);
352-
g_smWaiting.Remove(g_sAuthUnverified[client]);
353-
}
354-
355-
g_smChecked.Remove(g_sPath1[client]);
356-
g_smReceive.Remove(g_sPath1[client]);
357-
g_smChecked.Remove(g_sPath2[client]);
358-
g_smReceive.Remove(g_sPath2[client]);
359-
}
356+
if( IsFakeClient(client) ) return;
357+
358+
g_smWaiting.Remove(g_sAuthUnverified[client]);
359+
g_smChecked.Remove(g_sPath1[client]);
360+
g_smReceive.Remove(g_sPath1[client]);
361+
g_smChecked.Remove(g_sPath2[client]);
362+
g_smReceive.Remove(g_sPath2[client]);
360363

361-
FormatEx(g_sAuth[client], sizeof(g_sAuth[]), "");
362-
FormatEx(g_sAuthUnverified[client], sizeof(g_sAuthUnverified[]), "");
364+
g_sAuth[client][0] = 0;
365+
g_sAuth[client][6] = 0;
366+
g_sAuthUnverified[client][0] = 0;
363367

364368
/*
365369
static char sPath[PLATFORM_MAX_PATH];
@@ -426,6 +430,7 @@ public void OnClientDisconnect(int client)
426430
public void OnMapEnd()
427431
{
428432
MoveSprays();
433+
429434
// .Clear() is creating a memory leak
430435
// g_smReceive.Clear();
431436
// g_smWaiting.Clear();
@@ -742,6 +747,10 @@ Action PlayerDecal(const char[] te_name, const int[] Players, int numClients, fl
742747
return Plugin_Continue;
743748
}
744749

750+
// Here because of error: "No TempEntity call is in progress" - Must have taken too long in this function when placed with the vPos use below and become invalid
751+
float vPos[3];
752+
TE_ReadVector("m_vecOrigin", vPos);
753+
745754
g_sFilename[0] = 0;
746755
GetPlayerDecalFile(client, g_sFilename, sizeof(g_sFilename));
747756

@@ -767,16 +776,10 @@ Action PlayerDecal(const char[] te_name, const int[] Players, int numClients, fl
767776
if( !val )
768777
{
769778
static char auth[64];
770-
//GetClientAuthId(client, AuthId_Steam2, auth, sizeof(auth));
771779
if ( g_sAuth[client][6] == 'I' )
772-
{
773-
//GetClientAuthId(client, AuthId_Steam2, auth, sizeof(auth), false);
774780
Format(auth, sizeof(auth), "Unverified: %s", g_sAuthUnverified[client]);
775-
}
776781
else
777-
{
778782
Format(auth, sizeof(auth), "%s", g_sAuth[client]);
779-
}
780783

781784
if( FileExists(g_sFilename) )
782785
{
@@ -792,17 +795,16 @@ Action PlayerDecal(const char[] te_name, const int[] Players, int numClients, fl
792795
}
793796
else
794797
{
795-
if( GetGameTime() - g_fSprayed[client] > TIMEOUT_LOG && !g_smWaiting.GetValue(auth, val) )
798+
if( GetGameTime() - g_fSprayed[client] > TIMEOUT_LOG && !g_smWaiting.GetValue(g_sAuthUnverified[client], val) )
796799
{
797800
g_fSprayed[client] = GetGameTime();
798-
g_smWaiting.SetValue(auth, true);
801+
g_smWaiting.SetValue(g_sAuthUnverified[client], true);
802+
799803
if( g_hCvarLog.IntValue ) LogCustom("Blocked unchecked spray - missing file: %s from (%N) [%s]", g_sFilename, client, auth);
800804
if( g_hCvarMsg.IntValue == 1 ) PrintToServer("[Spray Exploit] Blocked unchecked spray - missing file: %s from (%N) [%s]", g_sFilename, client, auth);
801805
}
802806
}
803807

804-
float vPos[3];
805-
TE_ReadVector("m_vecOrigin", vPos);
806808
DataPack hPack = new DataPack();
807809
hPack.WriteCell(GetClientUserId(client));
808810
hPack.WriteFloat(vPos[0]);
@@ -888,13 +890,13 @@ void TestClient(int client)
888890
else
889891
BanClient(client, iDuration, BANFLAG_AUTO, "Invalid spray");
890892

891-
LogAction(client, -1, "[Spray Exploit] %L Banned %d minutes for invalid Spray", client, iDuration);
893+
LogAction(client, -1, "[Spray Exploit] %N %s was banned %d minutes for invalid Spray", client, g_sAuthUnverified[client], iDuration);
892894
return;
893895
}
894896
else if( g_hCvarKick.IntValue )
895897
{
896898
KickClient(client, "Invalid spray. Please change it");
897-
LogAction(client, -1, "[Spray Exploit] %L was kicked for invalid Spray.", client);
899+
LogAction(client, -1, "[Spray Exploit] %N %s was kicked for invalid Spray.", client, g_sAuthUnverified[client]);
898900
return;
899901
}
900902
}
@@ -939,16 +941,10 @@ public Action OnFileReceive(int client, const char[] sFile)
939941
if( client )
940942
{
941943
static char auth[64];
942-
//GetClientAuthId(client, AuthId_Steam2, auth, sizeof(auth));
943944
if ( g_sAuth[client][6] == 'I' )
944-
{
945-
//GetClientAuthId(client, AuthId_Steam2, auth, sizeof(auth), false);
946945
Format(auth, sizeof(auth), "Unverified: %s", g_sAuthUnverified[client]);
947-
}
948946
else
949-
{
950947
Format(auth, sizeof(auth), "%s", g_sAuth[client]);
951-
}
952948

953949
LogCustom("File received: %s from (%N) [%s]", sFile, client, auth);
954950
}
@@ -1009,16 +1005,10 @@ void FileCheck()
10091005
if( client )
10101006
{
10111007
static char auth[64];
1012-
//GetClientAuthId(client, AuthId_Steam2, auth, sizeof(auth));
10131008
if ( g_sAuth[client][6] == 'I' )
1014-
{
1015-
//GetClientAuthId(client, AuthId_Steam2, auth, sizeof(auth), false);
10161009
Format(auth, sizeof(auth), "Unverified: %s", g_sAuthUnverified[client]);
1017-
}
10181010
else
1019-
{
10201011
Format(auth, sizeof(auth), "%s", g_sAuth[client]);
1021-
}
10221012

10231013
if( g_hCvarLog.IntValue ) LogCustom("Invalid spray: %s from (%N) [%s]", g_sFilename, client, auth);
10241014
if( g_hCvarMsg.IntValue ) PrintToServer("[Spray Exploit] Invalid spray: %s: %02d (%02X <> %02X) from (%N) [%s]", g_sFilename, i, iRead[i], g_iVal[i], client, auth);
@@ -1027,6 +1017,12 @@ void FileCheck()
10271017
if( g_hCvarMsg.IntValue ) PrintToServer("[Spray Exploit] Invalid spray: %s: %02d (%02X <> %02X)", g_sFilename, i, iRead[i], g_iVal[i]);
10281018
}
10291019

1020+
Call_StartForward(g_hExploit);
1021+
Call_PushCell(client);
1022+
Call_PushCell(i);
1023+
Call_PushCell(iRead[i]);
1024+
Call_Finish();
1025+
10301026
if( g_hCvarPunish.IntValue >= 2 )
10311027
TestClient(client);
10321028

@@ -1052,6 +1048,11 @@ int ValFile(int iRead[sizeof(g_iVal)])
10521048
return -1;
10531049
}
10541050

1051+
if( iRead[16] == 80 && iRead[24] > 1 )
1052+
{
1053+
return 24;
1054+
}
1055+
10551056
char bytes[10];
10561057
bool read = true;
10571058
int n;

0 commit comments

Comments
 (0)