H6x - Secure Luau Sandboxing Library (Moved to GitHub)

Ah alright thank you. Could I see an example with this using your tool (supposedly using ExecuteString)?

Also, when setting a environment variable to a instance, it returns nil. Printing ownerplr by itself print’s the player instance.


image

I believe the issue is that you are trying to send the function arguments to the client directly (and your functions aren’t technically sandboxed so I think you are directly sending some fake values created by the sandbox to the client instead of the real ones)

You will have more luck doing something like this to tostring all the arguments before sending them to the client for the best compatibility:

local function sendLog(logType: "print" | "warn" | "error", ...)
	local args = table.pack(...)
	for index=1, args.n do end
		args[index] = tostring(args[index]) -- Convert argument to a string
	end
	logRemote:FireClient(ownerplr, logType, table.unpack(args, 1, args.n))
	-- You could also use `table.concat(args, " ")` here which might be a bit better for bandwidth/performance and might be a bit easier to work with but only to a minor extent
end

-- For error you will probably also want some special functionality since errors are a bit different
error = function(message, logLevel: number)
	-- If the log level is not zero we want to push it forward by one so the stack trace doesn't include the fake error function
	if logLevel ~= 0 then
		logLevel = (logLevel or 1) + 1
	end

	-- Create the stack trace and send it
	sendLog("error", debug.traceback(tostring(message), logLevel))

	-- In order to preserve expected behaviour the real error function should still get called (so stuff like xpcall still works, and the thread still dies)
	error(message, logLevel)
end
2 Likes

Oh actually that might be the issue. Also, I had trouble indexing some other functions such as wait(). Is this something I have to manually implement in my environment too?

I am actually not sure what is happening here exactly (I haven’t worked with H6x in a while), but I suspect it’s just because the environment you are passing doesn’t include it. IIRC the second argument to Environment.new is the full environment rather than extra stuff. You could either manually specify all the values you want the sandbox to have (might be more desirable for future-proofing if you are concerned about Roblox potentially adding functions that allow users to do undesirable stuff, e.g. debug.info allowed for sandbox escapes in many sandboxes/games when it became enabled), or you could also give your environment an __index that points to some real environment:

h6x.Environment.new(sandbox, setmetatable({ ... stuff ... }, {
	__index = getfenv();
}))
1 Like

Ah alright, thank you. Is there a difference between doing __index = getfenv() and just getfenv() by itself when setting the metamethod?

I don’t think I understand completely but IIRC by default, Environment.new wraps the environment you pass in the second argument so I think you could just pass getfenv() like this: h6x.Environment.new(sandbox, getfenv()) and user code won’t be able to modify getfenv or access the metatable of it directly, but I would still probably suggest using __index just for a little bit better control, that way you don’t need to pollute getfenv with your custom print/warn/error functions.

I believe the second argument is essentially the “base” environment (the thing to pull values from by default) but I believe I made sure that user code will not modify it so __newindex won’t trigger and the environment table you pass won’t get modified (so you could even safely table.freeze it I think).

1 Like

I’ve personally used __index = self with all my meta-objects, however I haven’t seen a difference between defining just self as the metamethod.

Also, judging by how many files included in your module, I can definitely tell you can’t remember all of it.

By the way, have you benchmarked speed and performance when compiling before?

Oh I see. This only works when you put __index into it. I don’t think there is any reason that you can’t do that, but it would mean that user code would also have a global variable called __index.

Definitely. I believe it has also been over a year now since I have worked on H6x I think aside from a few minor updates to support new Roblox features like __iter. I started working on this project called Chlorine which was meant to be a successor to H6x but I didn’t complete it. I believe it is a lot nicer than H6x but it also lacks some cool features that H6x has since I just never got around to implementing. I think Chlorine is ultimately the better of the two, and I think it is a lot more clean to use and the code is a lot neater too but H6x also has some benefits that Chlorine doesn’t ofc.

Some day if I come back to these I think I will end up going a different route again and going for another rewrite altogether. My skills as a programmer have changed a lot between working on both of these projects. That has been kind of the story of all of these projects, I write them from scratch and they are better than the previous iteration, but then my ability to work on them is limited and I end up writing another better iteration on them again later, and it repeats haha.

Chlorine is the third iteration of these projects with H3x being the first, and H6x being the second, and with each one, many flaws and features are implemented better, the code is cleaner, etc.

From what I can recall, I have not done any real benchmarks (and I don’t really have any numbers), I’ve only done some crude performance tests just out of curiosity. But, I do know that all of my different script sandbox libraries will definitely add a lot of overhead to some things.

Every operation (__index, __newindex, function calls, etc) incur additional memory and performance costs since each one runs a little bit of code and adds new tables and functions. I believe Chlorine is much better about memory and performance overhead.

I also still plan to update and support H6x and Chlorine both when security issues are reported, or when Roblox updates come around (e.g. with __iter), but, neither sees enough use for security issues to be something that are investigated or reported by users much. I believe I’ve only seen one or two reports across H3x, H6x, and Chlorine combined (I also never “officially” released Chlorine since I never technically finished it)

1 Like

Oh, there seems to be another issue too

If I did something like ``Instance.new(‘Part’,workspace) it works perfectly fine
image