Thread Tools Display Modes
08-31-11, 03:25 PM   #1
tecu
An Aku'mai Servant
AddOn Author - Click to view addons
Join Date: Apr 2006
Posts: 30
tecu tries to learn - c++, lua - creating a sandbox

(sorry if this is too off-topic)

i have a ridiculous amount of free time at the moment (unemployed), so i figured i'd be productive and spend some of it learning things. to that end i'm working on a project that includes opengl, c++, and lua. doing this is a little silly: my skill level with opengl and c++ is basically at the beginner level, and the project itself doesn't have a solid goal beyond 'learn stuff'; but you probably don't care about all that.

anyway, i spent a short time yesterday trying to figure out the basics of integrating lua and c++ before thinking, 'I NEED TO CREATE A SANDBOX.' and what i'm interested in creating (and why i'm asking this here) is something similar to the wow lua environment:

- include base, math, string, and table libs (though no coroutine)
- disable reading from or writing to anything outside the lua environment

so, after reading the entire internet, i've seen examples of how to create custom environment tables and how to use them for loaded script files. i mostly undrestand these, but it seems like to do this right (while still maintaining the featureset i want) would require knowledge i don't have yet and far more effort than what it would take to, say, just nuking anything in the global table that isn't in my whitelist:

Code:
//!! = things to deal with if using a custom env table
//!  = create replacement tables for these if using a custom env table
const std::string whitelist_keys[] = {
	"_VERSION",
	"_G",			//!!	point to new env table
	"assert",
	"error",
	"getfenv",		//!!	exclude or create custom func
	"getmetatable",		//!!	exclude or create custom func
	"ipairs",
	"loadstring",		//!!	wizards can get to the real _G with this
	"next",
	"pairs",
	"pcall",
	"print",
	"rawequal",
	"rawget",
	"rawset",
	"select",
	"setfenv",		//!!	exclude or create custom func
	"setmetatable",		//!!	this is flagged as unsafe but i don't know why
	"tonumber",
	"tostring",
	"type",
	"unpack",
	"xpcall",
	// libs:
	"math",			//!	create replacement table
	"string",		//!	''
	"table"			//!	''
};
so to do that i'd just do
Code:
lua_pushnil( state );
lua_setglobal( state, "collectgarbage" );
and so on for whatever i find in the global table that is not in my whitelist.

QUESTIONS:
- where's a good place to ask this sort of question? i was thinking of setting up camp on stackoverflow or something.
- will what i described be reasonably secure? i'll probably limit the max number of operations or max execution time too, but is there anything else i should watch out for?
- are there any big drawbacks that come to mind when messing with the global table like this?
- any other thoughts?

wellp. thanks for taking the time to read this, and thanks in advance for any feedback!
  Reply With Quote
08-31-11, 07:16 PM   #2
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,313
From what I know of the Lua environment that WoW uses. WoW initially used Lua 5.0 in vanilla and upgraded to 5.1 in BC. As an open source scripting engine, the source code is available for free download at http://www.lua.org. It's written in pure ANSI C, so you should easily be able to import it into C++.

There are only a few modifications the WoW API made to the core Lua engine:
  • The entire IO and OS libraries have been removed.
  • loadstring() does not accept precompiled Lua binaries.
  • Tracking of assignments to global and table entries for taint detection. (as returned by issecurevariable())
__________________
WoWInterface AddOns
"All I want is a pretty girl, a decent meal, and the right to shoot lightning at fools."
-Anders (Dragon Age: Origins - Awakening)

Last edited by SDPhantom : 08-31-11 at 07:23 PM.
  Reply With Quote
08-31-11, 09:46 PM   #3
Torhal
A Pyroguard Emberseer
 
Torhal's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2008
Posts: 1,196
You can just hang outside in the sun all day, tossing a ball around, or you can sit at your computer and do something that matters.
-Cartman (South Park S4E01: Make Love, Not Warcraft)
That would actually be season 10, episode 8.
__________________
Whenever someone says "pls" because it's shorter than "please", I say "no" because it's shorter than "yes".

Author of NPCScan and many other AddOns.
  Reply With Quote
08-31-11, 11:52 PM   #4
tecu
An Aku'mai Servant
AddOn Author - Click to view addons
Join Date: Apr 2006
Posts: 30
thanks for the reply, sdphantom.

the libraries are easy enough, i'll just load the ones i want.

thanks for the tip on loadstring, i'll take that out of my whitelist. i'll also have to check to make sure my c++ code does not load bytecode, too.

and taint's too far down the road for me to worry about yet
  Reply With Quote
09-01-11, 12:03 AM   #5
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,313
Originally Posted by Torhal View Post
That would actually be season 10, episode 8.
Good to see another South Park fan, lol
I was sure it was the premiere of a season, but even that one episode I have on my PSP agrees with you.



Originally Posted by tecu View Post
thanks for the tip on loadstring, i'll take that out of my whitelist. i'll also have to check to make sure my c++ code does not load bytecode, too.
If I remember right, Lua binary code is always prefixed with the ESC char (byte 27). You can check for this and have it refuse to load if that's the case. I think the WoW API has it fail silently, I'll have to check later.
__________________
WoWInterface AddOns
"All I want is a pretty girl, a decent meal, and the right to shoot lightning at fools."
-Anders (Dragon Age: Origins - Awakening)
  Reply With Quote
09-02-11, 05:26 PM   #6
tecu
An Aku'mai Servant
AddOn Author - Click to view addons
Join Date: Apr 2006
Posts: 30
hey guys. thought i'd share what i have so far, if anyone's interested.

this is test code, i still need to:
- create file path validation
- prevent bytecode from loading
- decide on what goes into the whitelist
- decide what libraries i want
- actually incorporate all of this into a class

but i thought it was worth sharing for MORE FEEDBACK. and hey maybe it'd help anyone else who is interested, too.

main.cpp:
Code:
// mylua_test-2: main.cpp

#include <lua5.1/lua.hpp>

#include <iostream>
#include <string>

using namespace std;

string whitelist[] = {
	"_G",
	"ipairs",
	"next",
	"pairs",
	"print",
	"tostring"
};

int num_whitelist = (int)( sizeof(whitelist) / sizeof(whitelist[0]) );

bool isGlobalOkay( const string& global )
{
	for( int i = 0; i < num_whitelist; i++ )
	{
		if( whitelist[i] == global )
			return true;
	}
	return false;
}

bool isFileOkay( const string& filename )
{
	// TODO: check to see that file path is 'okay'
	// TODO: check for bytecode (see: LUA_SIGNATURE)
	return true;
}

void scrubGlobalEnvironment( lua_State *state )
{
	lua_pushnil( state );	// first key
	while( lua_next( state, LUA_GLOBALSINDEX ) != 0 )
	{
		if( lua_type( state, -2 ) == LUA_TSTRING )
		{
			if( !isGlobalOkay( lua_tostring( state, -2 ) ) )
			{
				cout << "killing key: " << lua_tostring(state, -2) << endl;
				// stack: -2 = key, -1 = value
				lua_pop( state, 1 );
				// stack: -1 = key
				lua_pushvalue( state, -1 );
				// stack: -2 = key, -1 = key
				lua_pushnil( state );
				// stack: -3 = key, -2 = key, -1 = nil
				lua_rawset( state, LUA_GLOBALSINDEX );
				// stack: -1 = key
			}
			else
			{
				cout << "key " << lua_tostring(state, -2) << " okay" << endl;
				lua_pop( state, 1 ); // for lua_next
			}
		}
		else
		{
			cout << "what." << endl;
			// not a string.  this should never happen?  i think?
			lua_pop( state, 1 ); // for lua_next
		}
	}
}

lua_State *createRestrictedLuaState()
{
	// create new state
	lua_State *state = luaL_newstate();
	
	// load libraries, eg base:
	lua_pushcfunction( state, luaopen_base );
	lua_call( state, 0, 0);
	
	// nil globals
	scrubGlobalEnvironment( state );
	
	return state;
}

int main( int argc, const char* argv[] )
{
	lua_State *state = createRestrictedLuaState();
	
	if( argc > 1 )
	{
		string filename = argv[1];
		
		if( isFileOkay( filename ) )
		{
			luaL_loadfile( state, filename.c_str() );
			// TODO: MAGIC
			lua_pcall( state, 0, 0, 0 );
		}
	}
	
	lua_close( state );
	return 0;
}
test.lua:
Code:
#!/usr/bin/env lua

print'hello from test.lua'

print'_G:'
for k, v in pairs(_G) do
	print(k, v)
end
output of ./main test.lua
Code:
killing key: xpcall
killing key: coroutine
killing key: newproxy
key _G okay
killing key: gcinfo
key pairs okay
key ipairs okay
killing key: _VERSION
killing key: setfenv
killing key: unpack
key tostring okay
killing key: tonumber
killing key: pcall
killing key: getfenv
killing key: rawset
killing key: type
killing key: setmetatable
key next okay
killing key: select
killing key: assert
killing key: load
killing key: rawget
killing key: loadstring
killing key: getmetatable
killing key: rawequal
killing key: dofile
killing key: collectgarbage
key print okay
killing key: error
killing key: loadfile
hello from test.lua
_G:
_G	table: 0x8ac7d0
pairs	function: 0x8ad1a0
ipairs	function: 0x8ad100
tostring	function: 0x8ad960
next	function: 0x8ad6c0
print	function: 0x8adb00
it looks okay!

PS: the TODO: MAGIC is there to remind me that i may want to create a second, lua-based sandbox for each script i load so that my c++/lua interface is consistent.
  Reply With Quote
09-04-11, 04:16 AM   #7
Torhal
A Pyroguard Emberseer
 
Torhal's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2008
Posts: 1,196
Killing things like "type", "select", "tonumber", and quite a few other things will severely hamper your sandbox environment. If you want it to be like WoW's environment, why not check to see what WoW allows?
__________________
Whenever someone says "pls" because it's shorter than "please", I say "no" because it's shorter than "yes".

Author of NPCScan and many other AddOns.
  Reply With Quote
09-04-11, 01:06 PM   #8
tecu
An Aku'mai Servant
AddOn Author - Click to view addons
Join Date: Apr 2006
Posts: 30
you're right, of course. to keep the example simple, i only selected what i thought i would use to scan _G and print its contents. what i actually use will probably end up looking like the whitelist in my first post (without loadstring).

in the end, though, it probably won't be exactly like wow, and i'm okay with that as long as i can run untrusted code 'safely'.
  Reply With Quote

WoWInterface » General Discussion » Chit-Chat » tecu tries to learn - c++, lua - creating a sandbox

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off