-
Notifications
You must be signed in to change notification settings - Fork 1
security(AcceptInput): sanitize player names to prevent server command injection #18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -56,7 +56,7 @@ public Plugin myinfo = | |
| name = "ServerCommandFilter", | ||
| author = "BotoX, .Rushaway, koen", | ||
| description = "Filters server commands using user-defined rules for maps (point_servercommand/VScript)", | ||
| version = "1.2.0", | ||
| version = "1.3.0", | ||
| url = "https://github.com/srcdslab/sm-plugin-ServerCommandFilter" | ||
| }; | ||
|
|
||
|
|
@@ -188,35 +188,53 @@ public MRESReturn AcceptInput(int pThis, Handle hReturn, Handle hParams) | |
| if(!StrEqual(szInputName, "Command", true)) | ||
| return MRES_Ignored; | ||
|
|
||
| int bReplaced = 0; | ||
| int client = 0; | ||
| if(!DHookIsNullParam(hParams, 2)) | ||
| client = DHookGetParam(hParams, 2); | ||
|
|
||
| char sCommand[COMMAND_SIZE]; | ||
| DHookGetParamObjectPtrString(hParams, 4, 0, ObjectValueType_String, sCommand, sizeof(sCommand)); | ||
|
|
||
| int bReplaced = 0; | ||
| if (SanitizeSayCommand(sCommand, sizeof(sCommand))) | ||
| bReplaced = 1; | ||
|
|
||
| if(client > 0 && client <= MaxClients && IsClientInGame(client)) | ||
| { | ||
| char sName[MAX_NAME_LENGTH]; | ||
| GetClientName(client, sName, sizeof(sName)); | ||
|
|
||
| char sSteamId[32]; | ||
| GetClientAuthId(client, AuthId_Engine, sSteamId, sizeof(sSteamId)); | ||
| if (StrContains(sCommand, "!activator") != -1) | ||
| { | ||
| if (StrContains(sCommand, "!activator.name") != -1) | ||
| { | ||
| char sName[MAX_NAME_LENGTH]; | ||
| GetClientName(client, sName, sizeof(sName)); | ||
| SanitizePlayerName(sName, sizeof(sName)); | ||
| bReplaced += ReplaceString(sCommand, sizeof(sCommand), "!activator.name", sName, false); | ||
| } | ||
|
|
||
| char sUserID[32]; | ||
| FormatEx(sUserID, sizeof(sUserID), "#%d", GetClientUserId(client)); | ||
| if (StrContains(sCommand, "!activator.steamid") != -1) | ||
| { | ||
| char sSteamId[32]; | ||
| GetClientAuthId(client, AuthId_Engine, sSteamId, sizeof(sSteamId)); | ||
| bReplaced += ReplaceString(sCommand, sizeof(sCommand), "!activator.steamid", sSteamId, false); | ||
| } | ||
|
|
||
| char sTeam[32]; | ||
| if(GetClientTeam(client) == CS_TEAM_CT) | ||
| strcopy(sTeam, sizeof(sTeam), "@ct"); | ||
| else if(GetClientTeam(client) == CS_TEAM_T) | ||
| strcopy(sTeam, sizeof(sTeam), "@t"); | ||
| if (StrContains(sCommand, "!activator.team") != -1) | ||
| { | ||
| char sTeam[32]; | ||
| int iTeam = GetClientTeam(client); | ||
| if(iTeam == CS_TEAM_CT) | ||
| strcopy(sTeam, sizeof(sTeam), "@ct"); | ||
| else if(iTeam == CS_TEAM_T) | ||
| strcopy(sTeam, sizeof(sTeam), "@t"); | ||
| else strcopy(sTeam, sizeof(sTeam), "@spec"); | ||
|
|
||
| bReplaced += ReplaceString(sCommand, sizeof(sCommand), "!activator.team", sTeam, false); | ||
| } | ||
|
|
||
| bReplaced += ReplaceString(sCommand, sizeof(sCommand), "!activator.name", sName, false); | ||
| bReplaced += ReplaceString(sCommand, sizeof(sCommand), "!activator.steamid", sSteamId, false); | ||
| bReplaced += ReplaceString(sCommand, sizeof(sCommand), "!activator.team", sTeam, false); | ||
| bReplaced += ReplaceString(sCommand, sizeof(sCommand), "!activator", sUserID, false); | ||
| char sUserID[32]; | ||
| FormatEx(sUserID, sizeof(sUserID), "#%d", GetClientUserId(client)); | ||
| bReplaced += ReplaceString(sCommand, sizeof(sCommand), "!activator", sUserID, false); | ||
| } | ||
Rushaway marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| Action iAction = ValidateCommand(sCommand, "point_servercommand"); | ||
|
|
@@ -228,14 +246,39 @@ public MRESReturn AcceptInput(int pThis, Handle hReturn, Handle hParams) | |
| } | ||
| else if(iAction == Plugin_Changed || bReplaced) | ||
| { | ||
| ServerCommand(sCommand); | ||
| ServerCommand("%s", sCommand); | ||
| DHookSetReturn(hReturn, true); | ||
| return MRES_Supercede; | ||
| } | ||
|
|
||
| return MRES_Ignored; | ||
| } | ||
|
|
||
| /** | ||
| * Sanitizes "say" and "say_team" commands to prevent command injection via player names. | ||
| * Returns true if the command was modified. | ||
| */ | ||
| bool SanitizeSayCommand(char[] sCommand, int maxlen) | ||
| { | ||
| // Only sanitize chat commands | ||
| if (strncmp(sCommand, "say ", 4, false) != 0 && strncmp(sCommand, "say_team ", 9, false) != 0) | ||
| return false; | ||
|
|
||
| return SanitizePlayerName(sCommand, maxlen); | ||
| } | ||
|
Comment on lines
+261
to
+268
|
||
|
|
||
| /** | ||
| * Sanitizes a player name to prevent command injection. | ||
| */ | ||
| bool SanitizePlayerName(char[] sName, int maxlen) | ||
| { | ||
| bool bChanged = false; | ||
| bChanged |= ReplaceString(sName, maxlen, ";", " ", false) > 0; | ||
| bChanged |= ReplaceString(sName, maxlen, "\"", "", false) > 0; | ||
| bChanged |= ReplaceString(sName, maxlen, "\n", " ", false) > 0; | ||
| bChanged |= ReplaceString(sName, maxlen, "\r", " ", false) > 0; | ||
| return bChanged; | ||
| } | ||
|
|
||
| /** | ||
| * Generic validation function that can be used by both AcceptInput and SetValue | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.