Exploit Fix Guide

Hello,
I recently found out how to take a look at clientside code of other servers and decided to take a look at how secure their server is.
I have visited ~40 darkrp servers so far, the results are quite sad (only visited servers having sv_allowcslua 1):
~34 servers had a possible money exploit. See 1.
~6 servers had a possible lua code injection. See 5.
~4 servers had a possible admin feature exploit. See 2.
1 server had the SQL password stored in clientside code. See 3 (Another 3 servers had it in the CSLua as well but it wasn’t the real info)

After this I was extremely shocked on how insecure those servers actually are.
I obviously told all the server owners of those servers where it occured and how to fix it so their servers should be somewhat secure by now.
Unfortunately I can’t visit all server nor find all exploits, so I have created this little tutorial on how exploits occur and how to avoid/fix them.
I have found all of the following examples on atleast one of the servers I visited but I paraphrased it a lot so it can’t just be abused.

Please note: This is not being intended as an exploit guide it should rather serve as a guide how to fix/prevent exploits, these kind of exploits just have to stop from being put into code, and I hope that some coders who read this won’t do the same mistakes.

All of the examples shown (except for the exploits) are serverside lua code, the fix is also serverside lua code.
All of the examples are paraphrased, they can’t be used to actually exploit on servers

1. Money exploits:
1.1.



net.Receive("FinishContract", function(len, ply)
	ply:GiveCash(net.ReadDouble())
end)


It is obvious that the server does not verify the reward being sent to the client
How to exploit it:



net.Start("FinishContract")
net.WriteDouble(10000000000000) 
net.SendToServer()


How to fix it:
Don’t rely on the client to send you how much he is being rewarded, save the amount that he gets in the StartContract net message and don’t screw that up (see next example)



net.Receive("FinishContract", function(len, ply)
	//This is just some dummy code
	local customer = net.ReadEntity();
	if(ply.accepted_contracts[customer] && checkIfContractReallyFulfilled) then
		ply:GiveCash(accepted_contracts[customer].Cash)
		ply.accepted_contracts[customer] = nil
	end
end)


1.2



net.Receive("StartContract", function(len, ply)
	local contractor = net.ReadEntity()
	local customer = net.ReadEntity()
	local amount = net.ReadDouble()
	customer:TakeCash(amount)
	contractor:GiveCash(amount)
end)


How to exploit it:



net.Start("StartContract")
net.WriteEntity(LocalPlayer())
net.WriteEntity(SomeOtherPlayer)
net.WriteDouble(10000000000000) 
net.SendToServer()


This will force another player to pay you money.

How to fix it:
If you already know one parameter can only be the player who is sending the net message, use the Player argument of net.Receive in your serverside code.



net.Receive("StartContract", function(len, ply)
	//This is just some dummy code
	local offeredmoney = net.ReadUInt(32);
	if(!ply:HasCash(offeredmoeny) || offeredmoney < 1000) then return end
	ply:TakeCash(offeredmoney);
	table.insert(GAMEMODE.AvailableContracts, {Cash = offeredmoney, Customer = ply}) //Contractors now have to accept offers rather than being forced into them
end)


1.3



net.Receive("WithdrawMoney", function(len, ply)
	local amount = net.ReadInt(32)
	if(amount < ply:GetBankMoney()) then
		ply:TakeBank(amount)
		ply:GiveCash(amount)
	end
end)


How to exploit it:



net.Start("WithdrawMoney")
net.WriteInt(-2000000000, 32)
net.SendToServer()


How to fix it:
Use net.WriteUInt and net.ReadUInt instead or check that the amount is positive.



net.Receive("WithdrawMoney", function(len, ply)
	local amount = net.ReadUInt(32)
	if(amount < ply:GetBankMoney()) then
		ply:TakeBank(amount)
		ply:GiveCash(amount)
	end
end)


1.4



net.Receive("BuyCar", function(len, ply)
	local model = net.ReadString()
	local script = net.ReadString()
	ply:TakeCash(net.ReadInt(32))
	...//spawn car stuff
end)


How to exploit it:



net.Start("BuyCar")
net.WriteString("")
net.WriteString("")
net.WriteInt(-500000000,32)
net.SendToServer()


How to fix it:
The server should know how much the car costs, don’t rely on any client information.
Create a table of all the cars and the prices on the server and use that to calculate the price the player has to pay.
Do the same thing for the model and the script as well so you don’t have a bunch of not working error cars spawned on your server.
Even if you use WriteUInt in this example you could still get a car for free.



net.Receive("BuyCar", function(len, ply)
	local car = CAR_DATABASE[net.ReadUInt(32)] //Client sends the ID of the car
	if(!car) then return end
	if(!ply:HasCash(car.Price)) then return end
	ply:TakeCash(car.Price)
	...//spawn car stuff
end)


1.5.



net.Receive("change_sex", function(len, ply)
	ply:TakeCash(20000)
	...//change sex stuff
end)


The serverside code does not check if the player has enough money and just removes 20k anyways.

How to exploit it:
Use this function 107374 times until you reach ~Int32Min(−2.147.483.648),
use it once more and thanks to an integer overflow you now have Int32Max(2.147.483.647)

How to fix:
Check that the client has enough money to pay for this.



net.Receive("change_sex", function(len, ply)
	if(!ply:HasCash(20000)) then return end
	ply:TakeCash(20000)
	...//change sex stuff
end)


2. Using admin features without being an admin
Basically all of the following are the result of the serverside not checking whether or not the client actually is an admin.
2.1.



net.Receive("give_me_weapon", function(len, ply)
	ply:Give(net.ReadString())
end)


How to exploit it:



net.Start("give_me_weapon")
net.WriteString("weapon_rpg");
net.SendToServer()


How to fix:
Check if the client is an admin.



net.Receive("give_me_weapon", function(len, ply)
	if(!ply:IsAdmin()) then return end
	ply:Give(net.ReadString())
end)


2.2



net.Receive("ban_rdm", function(len, ply)
	FindPlayerBySteamid(net.ReadString()):Ban(0, "RDM") //I know that function doesn't exist but w/e
end)


How to exploit it:



net.Start("ban_rdm")
net.WriteString("STEAM_ADMINSTEAM");
net.SendToServer()


How to fix:
Check if the client is an admin.



net.Receive("ban_rdm", function(len, ply)
	if(!ply:IsAdmin()) then return end
	FindPlayerBySteamid(net.ReadString()):Ban(0, "RDM") //I know that function doesn't exist but w/e
end)


3. Find passwords of server



...
SQL_CONFIG = {};
SQL_CONFIG.host = "some_dodgy_host.org";
SQL_CONFIG.database = "db"
SQL_CONFIG.username = "user"
SQL_CONFIG.port = 1337
SQL_CONFIG.password = "hufflepuffle" //not a real password
...


I actually found the database info of one servers I checked in the clientside lua code.

How to exploit it:
Decompress clientside lua code, search for SQL or password.

How to fix:
NEVER AddCsLua a file that contains sensitive information. NEVER!!!
Apparently the evolve adminmod used to CSLua the SQLinfo file, so if you are using that please check if it is doing that.

4. SQL injections
It is extremely simple to prevent SQL injections from happening, simply use escape of your SQL plugin to escape all unsafe strings (usernames, messages, etc. etc.)
If you want to know more simply check the wikipedia page: http://en.wikipedia.org/wiki/SQL_injection

5. Other code injections



...
if(SERVER) then
	net.Receive("sendnotifies", function(len, ply)
		local message = net.ReadString()
		for k, v in pairs(player.GetAll())
			v:SendLua("MsgN(\"Received notification " .. message .. " from player" .. ply:Nick() .. "\")")
		end
	end)
end
...


How to exploit it:



net.Start("sendnotifies")
net.WriteString("hello\") RunConsoleCommand(\"ulx\", \"rcon\", \"quit\") --") --shuts the server down if an admin who can access that command is online
net.SendToServer()


This can be used to run ANY lua code on the clients and is therefore probably the worst of all possible exploits.

How to fix:
Don’t use Player:SendLua, Player:ConCommand, CompileString or RunString on your server with strings that are sent from the client and might be unsafe.

Some advice:
Turn clientside luas off using sv_allowcslua 0 (In darkrp you have to use a config option to disable it, having it enabled by default is BS imo)
Although turning off clientside luas makes it less likely that someone can use lua commands, never rely on that
Never rely on any information sent from the client
Don’t just accept scripts you bought as being solid, check them yourself.
Using anticheats doesn’t make you invulnerable to exploits
Always expect your clientside code to be looked at (it is extremely easy to do that)

If you know about more exploits and want them to be added, please send me a pm including a paraphrased version.

syl0r

Woah…

jesus

great finds, I doubt many server owners will know how to fix them, but at least skids won’t know how to use them either

You should add server-side stuff too. There are a few threads in the support section where an addon has a RunString backdoor.

In short, take extra extra care with net.SendToServer() and don’t AddCSLuaFile files with sensitive data.

Like FPtje did with the DarkRP MySQL info and it took him that long to fix the issue?

I changed the examples to serverside functions rather than clientside ones because they are more informative. I will also include a fixed server version soon.

gmod atms are pathetic and you should never use them. it is extremely easy for me to steal all the passwords for everything in one lua function. Not telling how but keep this in mind

The exploit you used might be included in the list :wink: cough5.cough

Nope

Well, that is the exploit that I found that can be used for the same thing atleast

the fact that scripts can be exploited like this
even scripts written that are sold which are supposedly high quality
how can any programmer not sanitize user input, gee.

This is why I have said in that post where FPJTE, or whatever, called the LANGUAGE feature an exploit that he should never trust user-input and he basically said I was an idiot and that user input can be trusted… It’s not a surprise that all of these exploits are in DarkRP or for DarkRP Addons ( If people learned to code by looking at DarkRP code, they will know nothing of user-input-sanitization )…

This is simply false.

The unexpected behaviour of the surface functions is not a helpful to anyone and it can’t be turned off, what sort of feature is that?

I’m not defending FPtje here, the MySQL exploit was a huge fuck-up on his part. But he never once said that “user input can be undoubtedly trusted”, allowing hashtags isn’t “trusting user input”.

OT: It’s pretty reasonable to suggest that some of addons that have these exploits aren’t just bad coding, but malicious coding. It’s a relatively good way of hiding backdoors, which have been on the more and more frequent since the Workshop came to gmod.

Any place where text is shown to the user, outside of print and PrintTable which are dev functions, the language feature is expected to be used so it can translate to the appropriate language while displaying text to an end user.

Hash-tags aren’t part of any human name I’ve ever come across!

I’ll agree with you on the malicious coding practices; some people may know exactly what they have added in and did it explicitly for that purpose. Some people do that, others just don’t know.

Most of this is just common sense
**don’t trust the client **

Well fuck, now I can’t crash DarkRP economies anymore once these stupid ATM mod developers fix this stuff :frowning:

rip

[editline]25th February 2014[/editline]

that’s my job!!!

[editline]25th February 2014[/editline]

they’re sold on coderhire and the creators literally make thousands, fuck off seriously.

50+% of darkrp servers have the 1.1. type exploit, which is even easier to use than the 1.3. type exploit.