• [PSA] Exploit for servers linked with MySQL
    90 replies, posted
[B][U]The issue:[/U][/B] On practically any server that uses a mysql database to link two or more servers you can use -multirun to log into both servers and manipulate things by overwriting your data with a second instance of Garry's Mod. There is no convar I or anyone I've asked knows of that can disallow people to use multirun on your servers. There is no easy way to fix this that doesn't require most mysql based addons to be heavily modified or recoded. [B][U]Examples: [/U][/B]1. Join two linked DarkRP servers, drop all your money on server A, drop $1 on server B, reconnect to server A, pickup your dropped money and you have now duped your wallet. [URL="https://github.com/FPtje/DarkRP/blob/master/gamemode/modules/base/sv_data.lua#L369"]See here.[/URL] 2. Join two linked servers with pointshop, spend all your points on both servers effectively doubling your points. Even though it is done what most would call the [URL="https://github.com/adamdburton/pointshop/blob/master/lua/pointshop/providers/tmysql.lua#L97"]proper[/URL] way it is still [URL="https://github.com/adamdburton/pointshop/blob/50afa56a4bb5efc53532cdcbabf32935cb23cd1c/lua/pointshop/sv_player_extension.lua#L144"]vulnerable[/URL][URL="https://github.com/adamdburton/pointshop/blob/master/lua/pointshop/providers/tmysql.lua#L97"]. [/URL] KingofBeast and I made a quick addon that will kick multirun accounts (Hopefully Rubat or Willox will add a convar/hook in the future so hacky fixes like this aren't needed): tmysql3(lol): [url]https://github.com/SuperiorServers/NoMultirun/tree/tmysql3[/url] tmysql4: [URL]https://github.com/SuperiorServers/NoMultirun[/URL]
[QUOTE=StonedPenguin;51604374](Hopefully Rubat or Willox will add a convar/hook in the future so hacky fixes like this aren't needed)[/QUOTE] If/when they do, I really hope it's enabled by default.
:snip:
Haha, clever!
Surely your script will break if the server crashes?
[QUOTE=jamie1130;51604566]Surely your script will break if the server crashes?[/QUOTE] I suppose if for some reason checkactive ran before setactive completed it'd be possible but very unlikely, fixed it anyway.
Lmfao
Very helpful, thanks.
[QUOTE=StonedPenguin;51604374][B][U]The issue:[/U][/B] On practically any server that uses a mysql database to link two or more servers you can use -multirun to log into both servers and manipulate things by overwriting your data with a second instance of Garry's Mod. There is no convar I or anyone I've asked knows of that can disallow people to use multirun on your servers. There is no easy way to fix this that doesn't require most mysql based addons to be heavily modified or recoded. [B][U]Examples: [/U][/B]1. Join two linked DarkRP servers, drop all your money on server A, drop $1 on server B, reconnect to server A, pickup your dropped money and you have now duped your wallet. [URL="https://github.com/FPtje/DarkRP/blob/master/gamemode/modules/base/sv_data.lua#L369"]See here.[/URL] 2. Join two linked servers with pointshop, spend all your points on both servers effectively doubling your points. Even though it is done what most would call the [URL="https://github.com/adamdburton/pointshop/blob/master/lua/pointshop/providers/tmysql.lua#L97"]proper[/URL] way it is still [URL="https://github.com/adamdburton/pointshop/blob/50afa56a4bb5efc53532cdcbabf32935cb23cd1c/lua/pointshop/sv_player_extension.lua#L144"]vulnerable[/URL][URL="https://github.com/adamdburton/pointshop/blob/master/lua/pointshop/providers/tmysql.lua#L97"]. [/URL] KingofBeast and I made a quick addon that will kick multirun accounts (Hopefully Rubat or Willox will add a convar/hook in the future so hacky fixes like this aren't needed): [URL]https://github.com/SuperiorServers/NoMultirun[/URL][/QUOTE] This kind of exploit has been around for ages, used to see people do similar stuff back in 2010. If you never accounted for instances like this at any point in the design of your gamemode and you intended to run multiple servers linked to any sort of database then you really messed up. I can only imagine the terrible "global ban" code other people make if this comes to a surprise to most people. Duplication exploits are pretty much the worst of the worst and should always be kept in mind. I assume you'd know your code will fail up terribly if you shut down a server and your players attempt to join your other servers, or in the case that it crashes or if you restart the map? You really should be dropping these players LONG before they get to initialspawn as spamming some binds while joining before you get kicked off a timer really isn't difficult.
Apparently Zarp already fixed this problem, if you try to do it with them they fail your steam auth.
[QUOTE=FlyPiggyBanks;51604828]Apparently Zarp already fixed this problem, if you try to do it with them they fail your steam auth.[/QUOTE] Yeah, they give a fake Steam auth message in CheckPassword and do all of the verification work there. It's actually way too early of a window in-case their other server shuts down or crashes -- PlayerAuth is the earliest safe time.
[QUOTE=code_gs;51604853]Yeah, they give a fake Steam auth message in CheckPassword and do all of the verification work there. It's actually way too early of a window in-case their other server shuts down or crashes -- PlayerAuth is the earliest safe time.[/QUOTE] Same thing with Supiorer Servers.
[QUOTE=slayer3032;51604790]This kind of exploit has been around for ages, used to see people do similar stuff back in 2010. If you never accounted for instances like this at any point in the design of your gamemode and you intended to run multiple servers linked to any sort of database then you really messed up. I can only imagine the terrible "global ban" code other people make if this comes to a surprise to most people. Duplication exploits are pretty much the worst of the worst and should always be kept in mind.[/QUOTE] Unfortunately just about nothing accounts for this functionality in Garry's Mod, not even the most popular addons and gamemodes. [QUOTE=slayer3032;51604790]I assume you'd know your code will fail up terribly if you shut down a server and your players attempt to join your other servers, or in the case that it crashes or if you restart the map?[/QUOTE] It's [del]not[/del] wasn't intended to kick you from the server you're joining but rather the server you were last on so in the case of a crash it'd just ignore it. [QUOTE=slayer3032;51604790]You really should be dropping these players LONG before they get to initialspawn as spamming some binds while joining before you get kicked off a timer really isn't difficult. [/QUOTE] Totally valid point, I didn't think of that. Fixed it.
[QUOTE=slayer3032;51604790]If you never accounted for instances like this at any point in the design of your gamemode and you intended to run multiple servers linked to any sort of database [b]then you really messed up[/b].[/QUOTE] - Preventing this would require the servers linked to the same database to communicate with one another about which players are in which server. - A hack solution could be to check whether data isn't being changed behind a server's back, e.g. when updating the player's money, check whether the previous value in the database is the same as the player's money before the update. - Properly solving this would require the servers to communicate changes to the database, to have everything run in sync. All three solutions have serious downsides. All of them are based around the specific existence of multirun and require increasingly much effort to implement. Never mind the serious downsides of the three individual solutions: - Disallowing players to join two servers seems unnecessary, since the only reason for disallowing it is the server's inability to properly deal with it. - The hack solution is a bitch to implement because of race conditions (e.g. two updates shortly after each other from the same server) - MySQL is ill equipped to send push messages about updates. This problem is non-trivial, and the solutions have terrible downsides. This is not a case of having [i]really messed up[/i], it's a tradeoff. Not solving it if you don't know about multirun is a perfectly valid tradeoff.
[QUOTE=FPtje;51605175]- Preventing this would require the servers linked to the same database to communicate with one another about which players are in which server. - A hack solution could be to check whether data isn't being changed behind a server's back, e.g. when updating the player's money, check whether the previous value in the database is the same as the player's money before the update. - Properly solving this would require the servers to communicate changes to the database, to have everything run in sync. All three solutions have serious downsides. All of them are based around the specific existence of multirun and require increasingly much effort to implement. Never mind the serious downsides of the three individual solutions: - Disallowing players to join two servers seems unnecessary, since the only reason for disallowing it is the server's inability to properly deal with it. - The hack solution is a bitch to implement because of race conditions (e.g. two updates shortly after each other from the same server) - MySQL is ill equipped to send push messages about updates. This problem is non-trivial, and the solutions have terrible downsides. This is not a case of having [i]really messed up[/i], it's a tradeoff. Not solving it if you don't know about multirun is a perfectly valid tradeoff.[/QUOTE] A sockets based system would be trivial and solve the problem without any downsides. Player joins server - server queries other servers to check if they have the same player on there - take appropriate action.
[QUOTE=Teddi Orange;51606518]A sockets based system would be trivial and solve the problem without any downsides. Player joins server - server queries other servers to check if they have the same player on there - take appropriate action.[/QUOTE] What in the world is "trivial" to you? If something is "trivial" you should be able to solve it with the standard library, and even then, preferably easily.
[QUOTE=Apickx;51606739]What in the world is "trivial" to you? If something is "trivial" you should be able to solve it with the standard library, and even then, preferably easily.[/QUOTE] The issue primarily affects those using a multiserver platform, typically via SQL (or noSQL if that takes your fancy) meaning you're already outside of the standard realm of the gmod library. This means you're most likely going to be having to fix the issue without use of the standard library and thankfully there [URL="https://gist.github.com/AbigailBuccaneer/5943024"]is a list of socket libraries you can use.[/URL] (oh and GLsock was succeeded on that list by [url=https://facepunch.com/showthread.php?t=1234005]GLSock2[/url])
[QUOTE=FPtje;51605175]stuff[/QUOTE] why not just use a socket module so the 2 servers can talk directly to each other about which players are online granted that's a bit of a pain in the ass in itself since it requires manually setting up connections between all of your servers but if you got as far as making multi SQL linked servers you should be able to figure it out
[QUOTE=legendofrobbo;51606954]why not just use a socket module so the 2 servers can talk directly to each other about which players are online granted that's a bit of a pain in the ass in itself since it requires manually setting up connections between all of your servers but if you got as far as making multi SQL linked servers you should be able to figure it out[/QUOTE] There's nothing difficult about having "multi SQL linked servers", you just tell everyone to connect to the same database. On the other hand, having connections between servers is a little more difficult. If you have multiple servers, in different locations, the correct way is to communicate over the internet. If you have multiple servers all on the same intranet, you use different addresses, but it's the same. If your gmod servers are all on one physical server, the "correct" way is with (os dependent) pipes or shared memory. You could set it up like the above, but then you need to figure out ports for everything. Also, how should I keep track of players? Should I have every server send a request to every other server to see if the player is there? Should I have a master "who is online" list on one server, and everyone queries that?
Another solution for this problem is to use a php script to fire rcon commands to notify your other server(s) when a player is authed. Then you can use the game.KickID function to insure the steamid is not connected to any of your other servers at the same time. This solution has been working for us for a while now. Also thank you for taking the time to make this thread. Hopefully now that this has been publicly released, one of the devs will actauly implement a proper fix. Despite the fact RB was notified about this months ago.
[QUOTE=Apickx;51607019]There's nothing difficult about having "multi SQL linked servers", you just tell everyone to connect to the same database. On the other hand, having connections between servers is a little more difficult. If you have multiple servers, in different locations, the correct way is to communicate over the internet. If you have multiple servers all on the same intranet, you use different addresses, but it's the same. If your gmod servers are all on one physical server, the "correct" way is with (os dependent) pipes or shared memory. You could set it up like the above, but then you need to figure out ports for everything. Also, how should I keep track of players? Should I have every server send a request to every other server to see if the player is there? Should I have a master "who is online" list on one server, and everyone queries that?[/QUOTE] Again, with sockets it's easy Base code to bind a socket in GLsock2 [lua] //Define our packet type, udp is easier. local transmissionSock = GLSock(GLSOCK_TYPE_UDP) //Bind it all. transmissionSock:Bind(ip,port, CallbackForWhenWeBound) [/lua] Basic callback for when we've successfully bound. [lua] //maxPacketSize ideal choices are 512 or 1024 local function CallbackForWhenWeBound(strHandle, numError) print("UDP Broadcast port bound.") transmissionSock:ReadFrom(maxPacketSize, FunctionForReadingUDPData) end [/lua] The actual code which does the legwork for us. [lua] local function FunctionForReadingUDPData(objSocket, stringSenderIP, numSenderPort, objBuffer, numError) if numError == 15 then //Usually occurs when something is already bound to this socket. print("It appears something is already bound to this port! Use TCP View to see what it is!") transmissionSock:Cancel() transmissionSock:Destroy() return end if (numError != GLSOCK_ERROR_SUCCESS) then print("An invalid packet hit us! Dropping.") //Simply discard the packet if it hasn't arrived properly transmissionSock:Cancel() transmissionSock:ReadFrom(maxPacketSize, FunctionForReadingUDPData) return end local _, strContent = objBuffer:ReadString() if !strContent then print("We have no content. Cancelling connection and reading again.") transmissionSock:Cancel() transmissionSock:ReadFrom(maxPacketSize, FunctionForReadingUDPData) return end print(Format("Content has been received. Content is: %s", strContent)) if !content then transmissionSock:Cancel() transmissionSock:ReadFrom(maxPacketSize, FunctionForReadingUDPData) return end //Handle your data here, etc etc. local steamID64 = strContent //Assuming we only sent the raw SteamID64 for k, v in pairs(player.GetAll()) do if v:SteamID64() == steamID64 then v:Kick("Krieger, get your clones out of here!") break end end //And then we go back to listening. print("Now continuing to read data.") transmissionSock:ReadFrom(maxPacketSize, ReadUDPData) end [/lua] That should be enough to get you started. I'd recommend passing a token around as well as UDP isn't secure enough on its own.
I used sockets to take care of this general issue in the past, but I'll probably stack it with this fix to be completely sure there no exploiting. Basically had the server send a player's steam ID to all servers using sockets and if a player with that steam ID exists on another server it results in a kick. EDIT: Post above mine is a great example
I never knew people were this smart. Have seen people do this glitch though .Many servers need this add on though :D
Correct me if I'm wrong because I scimmed this thread but cant multirun clients only connect to insecure servers? And if that's the case, why do we care about servers without VAC enabled?
[QUOTE=YourStalker;51608449]Correct me if I'm wrong because I scimmed this thread but cant multirun clients only connect to insecure servers? And if that's the case, why do we care about servers without VAC enabled?[/QUOTE] -multirun isn't the only way to get two gmod instances authenticated with the same steam account
[QUOTE=Teddi Orange;51607081]Again, with sockets it's easy [lua] *code* [/lua] That should be enough to get you started. I'd recommend passing a token around as well as UDP isn't secure enough on its own.[/QUOTE] Can you elaborate on how to do a token? I seems a pissed off kid can kick everyone from all my servers now.
[QUOTE=Teddi Orange;51606518]A sockets based system would be trivial and solve the problem without any downsides. Player joins server - server queries other servers to check if they have the same player on there - take appropriate action.[/QUOTE] Without any downsides? How about this one: [quote= possible future DarkRP update message] Hey server owners with multiple servers, you can now [i]optionally[/i] install a socket module on your server to solve the very specific exploit about people joining two servers at the same time! To set it up, enter the ip addresses of all your servers in the config files. All this effort will have no effect on the game play, it just solves an exploit. Trust me, it's important. [/quote] Besides, sockets are just a way of implementing options 1 and 3, the first of which has literally already been implemented by OP using just the actual database connection. The sockets could then only be useful to synchronise data between servers to fully support one player being on two servers. I'm pretty god damn sure that this isn't trivial to implement, especially if you have to account for shit like two updates from both servers at the same time. Sockets are trivially not a practical solution to this problem.
[QUOTE=Apickx;51608741]Can you elaborate on how to do a token? I seems a pissed off kid can kick everyone from all my servers now.[/QUOTE] Pass a (secret) string around that your servers use to authenticate alongside the SteamID. [QUOTE=FPtje;51608978]Without any downsides? How about this one: Besides, sockets are just a way of implementing options 1 and 3, the first of which has literally already been implemented by OP using just the actual database connection. The sockets could then only be useful to synchronise data between servers to fully support one player being on two servers. I'm pretty god damn sure that this isn't trivial to implement, especially if you have to account for shit like two updates from both servers at the same time. Sockets are trivially no practical solution to this problem.[/QUOTE] They are trivial - Unlike a database that you frequently have to check, check and recheck again sockets are there listening all the time. Server 1 goes "Hey, I've just had player with SteamID xxx join, you guys should check it out!" Servers 2 through to n go "righto 1, lets see if we have this player on our servers, and if we do lets drop him!". There's no point ever letting a player join multiple servers because then it becomes all about tracking sessions and a huge pain. It's simpler to enforce that if you're online and playing, you're kept track of in one location. I'm fairly sure -multrun used to actually require sv_lan 1 to be enabled but maybe I'm wrong in that regard. #3 is a non-issue because we're not using mysql?
Demanding server owners to install and configure socket modules for something that not only can be, but already has been implemented for the things they already have installed and configured is utterly ridiculous.
[QUOTE=FPtje;51608994]Demanding server owners to install and configure socket modules for something that not only can be, but already has been implemented for the things they already have installed and configured is utterly ridiculous.[/QUOTE] No one is demanding anything. Sockets are just a more efficient, but optional method of solving the issue. It's doing a direct handshake versus having a middleman.
Sorry, you need to Log In to post a reply to this thread.