Anti SteamID 'Spoofing' Measures

Hello all.

As you may, or may not, be aware of, GMod is currently vulnerable to players using malicious tools to “spoof” their SteamIDs by dumping and swapping them with other player’s SteamID data during the connection process. Some tools of this nature go by the name of Serenity and Tranquility.

With a stolen SteamID, it’s possible for a malicious user to bypass bans on a server, and even aquire administrative ability.

Currently Tranquility is capable of SteamID swapping on the latest engine update with relative ease.

To this end, ComWalk and I have been working in tandem to upgrade gatekeeper with the functionality required to disallow users with swapped SteamIDs from connecting to your server.

For a little technical insight, the process of connecting to a game server works like so:

  1. Client A initiates connection to Game Server X by sending their SteamID authentication data to the game server.
  2. Client A sends their real (not spoofed) SteamID authentication data to the Steam backend for remote validation.
  3. Game Server X sends Client A’s recieved (potentially stolen) SteamID authentication data to the backend for validation.
  4. Valve’s backend processes all the data and determines whether to allow the client or not.

What follows next is a series of steps handled properly in other Source Engine 2009 games (TF2, DoD:S, CS:S), but NOT in GMod.

  1. If the validation data from Client A and Game Server X match on Valve’s backend, Client A is allowed to connect, and Game Server X receives a callback notifying the server to allow the client.
    -or-
  2. If the validation does not match, Game Server X receives a callback to tell the server to deny the client from connecting.

In GMod, this step is handled improperly. The server will receive these callbacks, but not do anything with them. Meaning a client faking their SteamID will be allowed free reign on a GMod server.

So using this knowledge, ComWalk and I have beefed up gatekeeper to process these callbacks, and allow lua to manage what to do with the players connecting.

Here’s some example code that should stop anyone using Tranquility dead in their tracks.

[cpp]
require(“gatekeeper”)

TranqUsers = {}
TranqNextThink = CurTime()
TranqEnabled = true // we assume we’re connected to steam when we launch

// how long to wait for an approval callback before kicking the client
// 10 can be too short (it’s proven too short on GMT, at least)
// but in my experience most clients are approved within seconds of joining
TranqTimeout = CreateConVar(“at_timeout”, “30”, FCVAR_NOTIFY)

local function KickUser(steam, reason)
local tranqTable = TranqUsers[steam] // tranqTable should never be nil
local userId = gatekeeper.GetUserByAddress(tranqTable.IP)

if userId then
	gatekeeper.Drop(userId, "SteamID validation failed (" .. tostring( reason ) .. ")

")
end

TranqUsers[steam] = nil

end

// this hook is called when the server (re)connects to steam
hook.Add(“GSSteamConnected”, “AntiTranqConnected”, function()
TranqEnabled = true

Msg("Connected to Steam! Anti-Tranquility enabled.

")
end)

// this hook gets called when the server loses it’s connection to steam
hook.Add(“GSSteamDisconnected”, “AntiTranqDisconnected”, function(reason)
TranqEnabled = false
TranqUsers = {}

Msg("Disconnected from Steam: ", reason, ", Anti-Tranquility disabled.

")
end)

// client is approved by the backend
// this is called within a few seconds for valid clients
// and isn’t called at all for clients spoofing their authentication data
hook.Add(“GSClientApprove”, “AntiTranqApprove”, function( steam )
Msg("Approved: ", steam, "
")

TranqUsers[steam] = nil

end )

// client was denied by the backend
// this isn’t called right away
// in my tests, this was called approximately 5 minutes after a spoofed client connected
// it’s best to handle the lack of an approval callback, but this is a Just In Case scenario
hook.Add(“GSClientDeny”, “AntiTranqDeny”, function(steam, reason, msg)
Msg("Denying ", steam, " (Reason: ", reason , "): ", msg, "
")

KickUser(steam, reason)

end)

// this hook is called when a player connects to the server
// this is similar to gatekeeper’s PlayerPasswordAuth callback, except it does not expect a return value to disconnect the client
hook.Add(“GSPlayerAuth”, “AntiTranqConnect”, function(name, pass, steam, ip)
if tonumber(steam) then return end

Msg("Auth: ", steam, "

")

TranqUsers[steam] = {
	IP = ip,
	ConnectTime = CurTime(),
}

end)

hook.Add(“Think”, “AntiTranqThink”, function()
if TranqNextThink >= CurTime() or not TranqEnabled then return end

for k,v in pairs( TranqUsers ) do	
	local diff = CurTime() - v.ConnectTime
	
	if diff > (TranqTimeout:GetInt() or 30) then
		KickUser(k, "timeout") // no approval after the timeout, boot them out
	end		
end

TranqNextThink = CurTime() + 1

end)[/cpp]

This code requires the latest build of gatekeeper, available here:

It is worth noting that this isn’t fool-proof; if a steam connection is not present when the level is loaded, you will find that all clients are kicked for a timeout. This is unfortunate, but unavoidable without a significant amount of further work, and we feel that it is better that legitimate clients are unable to join while a connection to the Steam servers is re-established than malicious clients be given free reign.

Let’s see, so Tranquility lasted a total of what… three days? Cheers!

Edit: It’s worth noting that this is a lot better than the posted solutions for dealing with serenity, as it doesn’t require clients to mark their profiles public. As long as the server has a connection to the Steam servers, you’re golden.

This is still a problem?
Garry! :argh:

Hunts, I think fixed this and released it to the public a while ago…

Hunts’ method was more of a hack. His method requires having the client to have their steam community profile public, and relies on SC actually being available.

This is closer to the true handling that the game server itself would follow.

what about other Source games?

In other source games VAC will stop this

What can I do?

Wait for valve to fix it

VALVe won’t fix.

I sent the dudes at Valve an email about it

It was never intended to be a permanent solution. Good that one was finally released.

I wouldn’t go that far. Up until now, they haven’t had any kind of POC of the exploit, so despite them almost certainly being aware of the issue, nothing has been done about it. Now that they’ve been given everything they need to replicate this attack themselves, there’s no reason to believe that they’ll continue to ignore the issue.

This isn’t really something that garry can address, it’s a weakness of Steam’s client authentication protocol. This problem existed in V14, but wasn’t really an issue because only a handful of people ever figured it out in enough detail to misuse. V15, owing in part to its simplicity, has none of the roadblocks that V14 had.

Other games have had the advantage of being VAC secure. In other games, such as CS:S or TF2, what prevents this exploit from being particularly useful isn’t the proper use of the callbacks but the fact that the VAC connection isn’t properly made by the client (the client has no idea that it’s actually impersonating somebody else; that’s all done by exploit which sits in the middle), causing the client to be eventually dropped. Since VAC hasn’t and likely never will have any effect on garrysmod, the switch to V15 that happened with #84 left a pretty wide hole open.

There’s really no way, at this point, that they can justify not fixing this. The impact on games that used V15 before was limited. The impact on garrysmod is not limited at all. As for how it can be fixed–I’m not entirely certain. Without severely changing how V15 works, it’s insecure unless a persistent connection to the Steam servers is maintained, but as anybody who has hosted a server for any length of time can testify, it’s not uncommon for that connection to be lost, and as near as I can tell, it doesn’t attempt to ‘catch up’ on any tickets it wasn’t able to validate because of it.

This is not meant to be a permanent solution, just a better temporary one. As mentioned in the OP, this method has flaws. Without more directly accessing ISteamGameServer this script will, under select conditions, deny legitimate clients and allow spoofed clients through. This is something that in order to be handled properly needs to be handled by Valve. If Valve states that they have no intention of fixing this it might be worth doing but I’d rather give them the benefit of the doubt.

-snip-

I think this needs some attention.

send another one and about their stupid master servers being spammed by fake servers.

Says the guy who used to sell serenity and use it regulary…
Just sayin’

Serenity is dead.
Tranquility is not.

On the other hand yes, It needs attention.

-snip-

CombineGuru, you are Seth obviosly.The guy who befriended Slob187 and who is selling an aimbot called SethHack for gmod and who used to sell Serenity.

-snip-