Hi!
I wrote a custom programming language for my game so people could make and code their own games within my game.
I tested every way I could think of to run unregulated Luau code, and a large number of the ways (8/18) allow an exploit to run code.
-
What do you want to achieve? How can I prevent this from happening?
-
What is the issue? I said what it was above.
-
What solutions have you tried so far? I haven’t been able to come up with anything.
Here’s the code.
It is a little messy. Sorry.
parser:
local parser = {}
local namespaces = {
["echo"] = [[print( --=/?&4939769926_TEXT&/=-- ) ]];--vuln
["--"] = [[-- --=/?&4939769926_TEXT&/=-- ]];
["var"] = [[local --=/?&4939769926_NAME&/=-- = --=/?&4939769926_VALUE&/=-- ]];--vuln
["close"] = [[end]];
["setProperty"] = [[internal.setPropertyInternalFunction( --=/?&4939769926_ARGS&/=-- )]];--vuln
["destroy"] = [[internal.destroyInternalFunction( --=/?&4939769926_ARGS&/=-- )]];--vuln
["instantiate"] = [[internal.createInstanceInternalFunction( --=/?&4939769926_ARGS&/=-- )]];--vuln
["if"] = [[if --=/?&4939769926_ONE&/=-- --=/?&4939769926_OPER&/=-- --=/?&4939769926_TWO&/=-- then]];
["elif"] = [[elseif --=/?&4939769926_ONE&/=-- --=/?&4939769926_OPER&/=-- --=/?&4939769926_TWO&/=-- then]];
["else"] = [[else]];
["func"] = [[function --=/?&4939769926_NAME&/=-- ( --=/?&4939769926_ARGS&/=-- )]];
["runfunc"] = [[--=/?&4939769926_NAME&/=-- ( --=/?&4939769926_ARGS&/=-- )]];--vuln
["while"] = [[while --=/?&4939769926_PARAM&/=-- do]];
["forpairs"] = [[for --=/?&4939769926_INDEX&/=-- ,--=/?&4939769926_OBJECT&/=-- in pairs ( --=/?&4939769926_TABLE&/=-- ) do]];
["for"] = [[for --=/?&4939769926_INDEXVAR&/=--=--=/?&4939769926_START&/=-- ,--=/?&4939769926_MAX&/=-- ,--=/?&4939769926_INC&/=-- do]];
["getchild"] = [[--=/?&4939769926_ARG1&/=-- = internal.getChildrenInternalFunction( --=/?&4939769926_ARG2&/=-- )]];--vuln
["wait"] = [[wait( --=/?&4939769926_TIME&/=-- )]];--vuln
["loadchar"] = [[internal.loadCharacterInternalFunction( --=/?&4939769926_ARGS&/=-- )]];--vuln
}
local datatypes = {
["null"] = "nil";
}
local operands = {
["is"] = "==";
["isnt"] = "~=";
["gthan"] = ">";
["lthan"] = "<";
["gthanoreq"] = ">=";
["lthanoreq"] = "<=";
}
--How does this parser work?
--It essentially translates the code here to Lua. Thats it.
function parser:parse(code)
--Remove all spaces from code, if they aren't between quotes
code = code:gsub("[\n\r]", " ")
local between = false
local newCode = ""
for _,char in pairs(string.split(code,"")) do
if char == '"' then
if between == true then
between = false
else
between = true
end
end
if between == false then
newCode = newCode..char:gsub("%s+","")
else
newCode = newCode..char
end
end
code = newCode
--Split all of the code by the ; symbol.
local lines = string.split(code,";")
local emptyScript = [[--Lang.
--By jahoobas in 2025
--Required Dependencies
local lang = game:GetService("ReplicatedStorage"):WaitForChild("Lang")
local internal = require(lang:WaitForChild("internal"))
--START SCRIPT--
]]
for _,line in pairs(lines) do
local newline = ""
--1. split by |
local one = string.split(line,"|")
--2. translate key word to Lua
--Check if it has anything else
if #one > 1 then
if namespaces[one[1]]:match("%-%-=/%?%&4939769926_TEXT%&/=%-%- ") then
local oneinlua = namespaces[one[1]]:gsub("%-%-=/%?%&4939769926_TEXT%&/=%-%-", one[2])
newline = newline..oneinlua
elseif namespaces[one[1]]:match("%-%-=/%?%&4939769926_NAME%&/=%-%- ") then
local two = string.split(one[2],":")
local pre = namespaces[one[1]]:gsub("%-%-=/%?%&4939769926_NAME%&/=%-%-", two[1])
--check if it has a value
if namespaces[one[1]]:match("%-%-=/%?%&4939769926_VALUE%&/=%-%- ") then
local isDT = false
for _,val in pairs(datatypes) do
if val == datatypes[two[2]] then
isDT = true
end
end
if not isDT then
newline = newline..pre:gsub("%-%-=/%?%&4939769926_VALUE%&/=%-%-", two[2])
else
newline = newline..pre:gsub("%-%-=/%?%&4939769926_VALUE%&/=%-%-", datatypes[two[2]])
end
elseif namespaces[one[1]]:match("%-%-=/%?%&4939769926_ARGS%&/=%-%- ") then
newline = newline..pre:gsub("%-%-=/%?%&4939769926_ARGS%&/=%-%-", two[2])
end
elseif namespaces[one[1]]:match("%-%-=/%?%&4939769926_ARGS%&/=%-%- ") and one[1] ~= "getchild" then
local oneinlua = namespaces[one[1]]:gsub("%-%-=/%?%&4939769926_ARGS%&/=%-%-", one[2])
newline = newline..oneinlua
elseif namespaces[one[1]]:match("%-%-=/%?%&4939769926_ONE%&/=%-%- ") then
--Is an if statement
local two = string.split(one[2],"/")
local pre = namespaces[one[1]]:gsub("%-%-=/%?%&4939769926_ONE%&/=%-%-", two[1])
local pre2 = pre:gsub("%-%-=/%?%&4939769926_OPER%&/=%-%-", operands[two[2]])
newline = newline..pre2:gsub("%-%-=/%?%&4939769926_TWO%&/=%-%-", two[3])
elseif namespaces[one[1]]:match("%-%-=/%?%&4939769926_INDEX%&/=%-%- ") then
--Is an if statement
local two = string.split(one[2],",")
local pre = namespaces[one[1]]:gsub("%-%-=/%?%&4939769926_INDEX%&/=%-%-", two[1])
local pre2 = pre:gsub("%-%-=/%?%&4939769926_OBJECT%&/=%-%-", two[2])
newline = newline..pre2:gsub("%-%-=/%?%&4939769926_TABLE%&/=%-%-", two[3])
elseif namespaces[one[1]]:match("%-%-=/%?%&4939769926_START%&/=%-%- ") then
--Is an if statement
local two = string.split(one[2],",")
local pre = namespaces[one[1]]:gsub("%-%-=/%?%&4939769926_MAX%&/=%-%-", two[3])
local pre2 = pre:gsub("%-%-=/%?%&4939769926_START%&/=%-%-", two[2])
local pre3 = pre2:gsub("%-%-=/%?%&4939769926_INDEXVAR%&/=%-%-", two[1])
newline = newline..pre3:gsub("%-%-=/%?%&4939769926_INC%&/=%-%-", two[4])
elseif namespaces[one[1]]:match("%-%-=/%?%&4939769926_TIME%&/=%-%- ") then
local oneinlua = namespaces[one[1]]:gsub("%-%-=/%?%&4939769926_TIME%&/=%-%-", one[2])
newline = newline..oneinlua
elseif namespaces[one[1]]:match("%-%-=/%?%&4939769926_PARAM%&/=%-%- ") then
local oneinlua = namespaces[one[1]]:gsub("%-%-=/%?%&4939769926_PARAM%&/=%-%-", one[2])
newline = newline..oneinlua
elseif one[1] == "getchild" then
--Specific to this function
local two = string.split(one[2],",")
local pre = namespaces[one[1]]:gsub("%-%-=/%?%&4939769926_ARG1%&/=%-%-", two[1])
local pre2 = pre:gsub("%-%-=/%?%&4939769926_ARG2%&/=%-%-", two[2])
newline = newline..pre2
end
else
if one[1] ~= "" then
--Is just an alone keyword
newline = namespaces[one[1]]
end
end
emptyScript = emptyScript.."\n"..newline
end
emptyScript = emptyScript.."\n\n --END SCRIPT--\n\n--By jahoobas in 2025"
return emptyScript
end
return parser
internal:
local internal = {}
function internal.setPropertyInternalFunction(obj : Instance, prop, val)
local disallowed = {
obj:FindFirstAncestor("ReplicatedStorage") or obj == game:GetService("ReplicatedStorage");
obj:FindFirstAncestor("ReplicatedFirst") or obj == game:GetService("ReplicatedFirst");
obj:FindFirstAncestor("ServerScriptService") or obj == game:GetService("ServerScriptService");
obj:FindFirstAncestor("ServerStorage") or obj == game:GetService("ServerStorage");
obj:FindFirstAncestor("StarterPlayer") or obj == game:GetService("StarterPlayer");
obj:FindFirstAncestor("TextChatService") or obj == game:GetService("TextChatService");
}
--Regulate what we're changing
local allowed = true
for i,e in pairs(disallowed) do
if e then
allowed = false
end
end
if allowed then
obj[prop] = val
else
error("Fatal Error: function setProperty; Access is denied.")
end
end
function internal.destroyInternalFunction(obj : Instance)
local disallowed = {
obj:FindFirstAncestor("ReplicatedStorage") or obj == game:GetService("ReplicatedStorage");
obj:FindFirstAncestor("ReplicatedFirst") or obj == game:GetService("ReplicatedFirst");
obj:FindFirstAncestor("ServerScriptService") or obj == game:GetService("ServerScriptService");
obj:FindFirstAncestor("ServerStorage") or obj == game:GetService("ServerStorage");
obj:FindFirstAncestor("StarterPlayer") or obj == game:GetService("StarterPlayer");
obj:FindFirstAncestor("TextChatService") or obj == game:GetService("TextChatService");
}
--Regulate what we're destroying
local allowed = true
for i,e in pairs(disallowed) do
if e then
allowed = false
end
end
if allowed then
obj:Destroy()
else
error("Fatal Error: function destroy; Access is denied.")
end
end
function internal.createInstanceInternalFunction(parent,className)
local disallowed = {
parent:FindFirstAncestor("ReplicatedStorage") or parent == game:GetService("ReplicatedStorage");
parent:FindFirstAncestor("ReplicatedFirst") or parent == game:GetService("ReplicatedFirst");
parent:FindFirstAncestor("ServerScriptService") or parent == game:GetService("ServerScriptService");
parent:FindFirstAncestor("ServerStorage") or parent == game:GetService("ServerStorage");
parent:FindFirstAncestor("StarterPlayer") or parent == game:GetService("StarterPlayer");
parent:FindFirstAncestor("TextChatService") or parent == game:GetService("TextChatService");
}
--Regulate what we're destroying
local allowed = true
for i,e in pairs(disallowed) do
if e then
allowed = false
end
end
if allowed then
local new = Instance.new(className)
new.Parent = parent
return new
else
error("Fatal Error: function instantiate; Access is denied.")
end
end
function internal.getChildrenInternalFunction(parent)
local disallowed = {
parent:FindFirstAncestor("ReplicatedStorage") or parent == game:GetService("ReplicatedStorage");
parent:FindFirstAncestor("ReplicatedFirst") or parent == game:GetService("ReplicatedFirst");
parent:FindFirstAncestor("ServerScriptService") or parent == game:GetService("ServerScriptService");
parent:FindFirstAncestor("ServerStorage") or parent == game:GetService("ServerStorage");
parent:FindFirstAncestor("StarterPlayer") or parent == game:GetService("StarterPlayer");
parent:FindFirstAncestor("TextChatService") or parent == game:GetService("TextChatService");
}
--Regulate what we're destroying
local allowed = true
for i,e in pairs(disallowed) do
if e then
allowed = false
end
end
if allowed then
return parent:GetChildren()
else
error("Fatal Error: function getchild; Access is denied.")
end
end
function internal.loadCharacterInternalFunction(player)
local disallowed = {
not player:IsA("Player")
}
--Regulate what we're destroying
local allowed = true
for i,e in pairs(disallowed) do
if e then
allowed = false
end
end
if allowed then
return player:LoadCharacter()
else
error("Fatal Error: function loadchar; Argument 1 is not of class 'Player'.")
end
end
return internal
mainmodule:
local lang = {}
function lang:exec(code)
local translated = require(script.Parent:WaitForChild("parser")):parse(code)
loadstring(translated)()
end
return lang
Thank you if you are able to help me solve this.