Jump to content

Recommended Posts

So the whole whitelist thing with remoteExec is cool, and we can prevent people from executing anything but functions we have pre-defined on remote machines.

 

Even though we can stop someone from executing this code on remote machines, how about players executing commands locally with global effect?

 

Say there are some important global variables on the server that are not made public and are used for keeping track of things like "money" that each player has to spend on vehicles and whatnot.  How do we prevent clients from using publicVariable to broadcast a new value and overwrite the variable on the server?  I also have some variables that are critical to the mission actually working, and if someone publicVariabled something with the same variable name and overwrote the variable on the server it would all come crashing down.

 

well, here is my approach, but it seems inefficient.  I don't even know if this is necessary.

 

 

I am doing this only on the server during initialization to create a randomized string and store it in parsingNamespace so that it is protected from publicVariable.  

#define _nums ["1","2","3","4","5","6","7","8","9","0"]
_keyCode = "_";
for "_i" from 1 to 10 do {
  _keyCode = (_keyCode + (selectRandom _nums));
};
parsingNamespace setVariable ["JWL_keyCode",_keyCode];

then when I initialize the variables or later need to retrieve them or set them, I just do this:

missionNamespace setVariable [("MyVariableName" + (parsingNamespace getVariable "JWL_keyCode")),_someValue];

//or this to get it

missionNamespace getVariable ("MyVariableName" + (parsingNamespace getVariable "JWL_keyCode"));

I don't have to call get or set very often as most of the variables are arrays and I can just keep a reference to them as a local variable in the script and alter them from there, but I can access it from other scripts if needed.  

 

So I guess the idea is that all these variables have names with this randomized string appended to it, and only the server knows what this string is, so you can't really overwrite the variables without knowing this string.  So even if you looked at the script, you wouldn't have a way of knowing any of the variable names because they are randomized when the mission is initialized.  

 

Since none of the variables with randomized names are ever broadcast to the clients, there wouldn't be a way for clients to get the randomized string at runtime either.  Instead, if the variable needs to be broadcast, a copy is made without the randomized name and that is broadcast instead.

 

So I guess nobody could wipe variables critical to the mission scripts working, or set the variable that contains their current amount of money to 4 trillion or something.  

 

 

 

Am I just wasting my own time?  Should I even be worried about this stuff?

Share this post


Link to post
Share on other sites

Want fairly random strings? use toString with rounded random numbers.

https://community.bistudio.com/wiki/toString

 

I came up with a solution that I think is probably better.  

 

I execVM this code from initServer.sqf

_nums = [];
_nums resize 10;
_nums = "_" + ((_nums apply {str (round (random 9))}) joinString "");
_fnc = format ["
  params [""_varName"",""_value""];
  _var = missionNamespace getVariable _varName;
  if (isNil ""_var"") then [{
    missionNamespace setVariable [_varName,(+ _value),true];
    missionNamespace setVariable [(_varName + ""%1""),(+ _value),false];
    _varName addPublicVariableEventHandler {
      missionNamespace setVariable [(_this select 0),(+ (missionNamespace getVariable ((_this select 0) + ""%1""))),true];
      diag_log format [""WARNING: JWL_fnc_createVariableSecure: Attempt to override variable name """"%2""""."",(_this select 0)];
    };
    true
  },{
    diag_log format [""ERROR: JWL_fnc_createVariableSecure: Variable name """"%2"""" already in use."",_varName];
    false
  }];
",_nums,"%1"];
JWL_fnc_createArraySecure = compileFinal _fnc;
_fnc = format ["
  params [""_varName"",""_value""];
  _var = missionNamespace getVariable (_varName + ""%1"");
  if (isNil ""_var2"") then [{
    diag_log format [""ERROR: JWL_fnc_setVariableSecure: Variable name """"%2"""" is not a secure variable."",_varName];
    false
  },{
    [_var,_value] call JWL_fnc_setArrayData;
    missionNamespace setVariable [_varName,(+ var),true];
    true
  }];
",_nums,"%1"];
JWL_fnc_setArraySecure = compileFinal _fnc;
_fnc = format ["
  params [""_varName""];
  _var = + (missionNamespace getVariable (_varName + ""%1""));
  if (isNil ""_var"") then [{
    diag_log format [""ERROR: JWL_fnc_getVariableSecure: Variable name """"%2"""" is not a secure variable."",_varName];
  },{
    _var
  }];
",_nums,"%1"];
JWL_fnc_getArraySecure = compileFinal _fnc;

So basically, this creates three functions that are read only, and at initialization they are randomized a bit.  

 

JWL_fnc_createArraySecure is the function I use to initialize a global variable using this framework.  It creates a pair of missionNamespace variables.  One is simply given the name specified in the parameter, and the other has a randomized string appended to the name.  This randomized string is randomized at mission initialization and then hardcoded into the functions themselves, and is not stored anywhere but in the function (and of course the variable names themselves once they are created).  The variables are copies of each other, and not pointers to the same array, so changing one will not change the other. When that public variable is overwritten by the publicVariable command, the event handler takes the array in the randomized local variable and copies it to the public variable, thereby overwriting any changes made to it by publicVariable.  Then it logs a warning to the RPT file.

 

JWL_fnc_setArraySecure allows me to set individual elements in the array, even if the element is nested.  It takes parameters in this form:

[_varName,_data] where _varName is a string that references the global array, and data is an array of elements.  _data would look like this [[_value1,_path1],[_value2,_path2],[_value3,_path3]].  Each path is in the form [_index1,_index2,_index3].  It works by getting a pointer to the array in the local randomized variable, editing the array through the pointer, and then copying the array over to the public version of the variable and broadcasting the public array.

 

JWL_fnc_getArraySecure simply retrieves the data from the global array and returns a copy of it, which I use whenever I am working with the data and changing it but do not want to override the array (I have some functions that get these arrays which contain all sorts of stuff, and then strip off some data that I don't need and organize them for other purposes).  This one only really protects the array from changes made locally and makes it easier to see in my code when I am copying or pointing to the arrays, because I don't want to accidently alter a global variable or the randomized backup variable without changing both at the same time.

 

the same principle could probably be applied without the deep copies, I'll have to test and see how publicVariable works with that.  

 

 

 

 

 

But as I said before, is there even a point to doing this at all?  

Share this post


Link to post
Share on other sites

If you are going to that extent you should use cfgFunctions

 

Its a one off function that compiles three other functions.  If I defined the other three functions through cfgFunctions, they wouldn't be randomized now would they?  The point is that it uses format to create a string with some random elements (but that random element is shared across the three functions) and then compiles the functions.  To my knowledge, I cant create a string on the fly and then compileFinal the string into a function when registering it to the functions library.  The "backup" variables would have some hard coded value appended to their names and anyone could de-pbo the mission, look up that value, and circumvent the whole system by publicVariable'ing that backup variable instead. 

 

The point is that the actual function code itself for all three functions is dynamic and will be different during each mission.  By having functions with code that changes itself at the start of each mission, I don't have to store that little randomized string anywhere and risk it getting publicVariable'd by a hacker, which would allow the hacker to override the randomized number with his own number and henceforth overwrite any other server variable.

 

I'm not worried about it being an execVM.  The script is only ever run once and only on the server, one little execVM isn't going to hurt and since I am only running it once there is no reason to load the script into memory as a function and keep it there if I am never calling it again.  As for the three functions that the script compiles, well, they work just as if they were in the functions library, and once the functions are randomized and then compiled, they stay in memory waiting to be called.

Share this post


Link to post
Share on other sites

you can use some defensive programming techniques, sanity checks and holding a copy of the variable as private variable, to protect mission-critical variables from unwanted interference. 

 

The main weakness with randomized variables is detection in BattlEye publicvariable.txt, and worrying about the few who can bypass battleye/antihack isn't worth the trouble IMO. Once they're "inside" and manipulating memory and variables, is a waste of time to protect against.

Share this post


Link to post
Share on other sites

you can use some defensive programming techniques, sanity checks and holding a copy of the variable as private variable, to protect mission-critical variables from unwanted interference. 

 

The main weakness with randomized variables is detection in BattlEye publicvariable.txt, and worrying about the few who can bypass battleye/antihack isn't worth the trouble IMO. Once they're "inside" and manipulating memory and variables, is a waste of time to protect against.

 

 

Interesting but I am not sure I understand.  I have never hosted a server before.  I did some digging and it looks like battleeye blocks publicVariable except when it is used on the exclusions listed in publicVariable.txt.  Which means this is pointless anyways because you can use battleye to block publicVariable.

 

Anyways, the randomized variables should be completely local so why would battleEye need an exclusion for them if the intent is to hide them from publicVariable?

 

Also, do you have any good links that discuss usage of these filters and how battleeye works?

Share this post


Link to post
Share on other sites

security to this extent probably isn't worth it at all. How often do you come across people that are doing malicious things to the server via script? I encountered one in the past year, he spawned a bunch of VR guys and then got global banned on the spot.

 

If anyone was able to get "inside" it probably wouldn't be long before they get caught by battleye, quietly uploading their suspicous activities to the master server.

 

Any script kiddies  were caught loonnnnngggg ago when they tried to get past battleye, and the few that can bypass it and haven't been banned yet, well, if you know what your doing and what to do to avoid detection, you should really be getting a good job rather than hacking the crap out of a game when you could be making big bucks. 

  • Like 1

Share this post


Link to post
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now

×