|
Hi,
me and my friends have formed a team called Nil Armstrong to look for
vulnerabilities in Lua these days. Then we found the bytecode vulnerability
still exists and crashes due to type confusion.
As
you know, lua doesn’t have verification of the bytecode.
This
causes constants to be out of bounds when performing LOADK bytecode.
This
has been mentioned before. like http://lua-users.org/lists/lua-l/2016-09/msg00191.html
Nevertheless,
we would like to inform you that a vulnerability that work on lua 5.2.4 also
applies to the latest version 5.4.4.
By
referring to the previous exploit code and modifying a part, you can see that
the code is still applied.
[previous
exploit code : https://github.com/erezto/lua-sandbox-escape]
If
you plan to modify it, please refer to the root cause and poc code below.
-------------------------------------------------------------------------------------------------------------------------- [PoC]
https://github.com/Lua-Project/lua-5.4.4-sandbox-escape --------------------------------------------------------------------------------------------------------------------------
[Root
cause]
lvm.c,
luaV_execute function vmcase(OP_LOADK)
{ TValue *rb = k + GETARG_Bx(i); setobj2s(L, ra, rb); vmbreak; }
There
isn’t verification of instruction I and GETARG_BX.
-------------------------------------------------------------------------------------------------------------------------- Second.
We found SEGV crash on lua 5.4.4.
Analyzing
this, we think it was because of type confusion.
Version
: Lua
.5.4.4, git hash 0e5071b5fbcc244d9f8c4bae82e327ad59bccc3f Ubuntu
20.04.3 LTS glibc
2.31
PoC: --------------------------------------------------------------------------------------------------------------------------
collectgarbage('stop') debug.sethook(function
() end, "r") setmetatable(debug.getregistry(),
{__mode = 'kv'}) collectgarbage() --------------------------------------------------------------------------------------------------------------------------
How
to reproduce: -------------------------------------------------------------------------------------------------------------------------- ./lua
poc.lua --------------------------------------------------------------------------------------------------------------------------
Stack
dump: --------------------------------------------------------------------------------------------------------------------------
#0 getgeneric (t=<optimized out>,
key=0x5555555999a0, deadok=deadok@entry=0) at ltable.c:303 #1 0x000055555556ca35 in luaH_get (t=<optimized
out>, key=<optimized out>) at ltable.c:801 #2 0x000055555555e7ed in lua_rawget
(L=L@entry=0x5555555992a8, idx=idx@entry=-2) at lapi.c:740 #3 0x000055555557bb26 in hookf
(L=0x5555555992a8, ar=0x7fffffffd550) at ldblib.c:328 #4 0x0000555555561991 in luaD_hook
(L=L@entry=0x5555555992a8, event=event@entry=1, line=line@entry=-1,
ftransfer=<optimized out>, ntransfer=<optimized out>) at ldo.c:331 #5 0x0000555555561a60 in rethook
(L=0x5555555992a8, ci=0x5555555a0ce0, nres=nres@entry=1) at ldo.c:377 #6 0x0000555555561d87 in luaD_poscall
(L=L@entry=0x5555555992a8, ci=ci@entry=0x5555555a0ce0, nres=1) at ldo.c:464 #7 0x00005555555620f2 in luaD_precall
(L=0x5555555992a8, func=0x555555599970, nresults=0) at ldo.c:545 #8 0x0000555555570224 in luaV_execute
(L=L@entry=0x5555555992a8, ci=<optimized out>) at lvm.c:1636 … --------------------------------------------------------------------------------------------------------------------------
[Root
cause] -------------------------------------------------------------------------------------------------------------------------- in
hookf function,
/* **
Call hook function registered at hook table for the current **
thread (if there is one) */ static
void hookf (lua_State *L, lua_Debug *ar) { static const char *const hooknames[] = {"call", "return",
"line", "count", "tail call"}; lua_getfield(L, LUA_REGISTRYINDEX, HOOKKEY); lua_pushthread(L); if (lua_rawget(L, -2) == LUA_TFUNCTION)
{ /* is there a hook function? */ lua_pushstring(L,
hooknames[(int)ar->event]); /* push
event name */ if (ar->currentline >= 0) lua_pushinteger(L,
ar->currentline); /* push current
line */ else lua_pushnil(L); lua_assert(lua_getinfo(L, "lS",
ar)); lua_call(L, 2, 0); /* call hook function */ } }
It
calls lua_getfield(L, LUA_REGISTRYINDEX, HOOKKEY).
The
auxgetstr function is called in lua_getfield. At this time, the function finds
the table with HOOKKEY as the key value.
if
it doesn’t exist, it creates a TString variable with HOOKKEY as a string and
pushes it to L->top.
The
pushed value is then used in lua_rawget. At this time, the data type of the
variable returned after calling the index2value function in the gettable
function that is received as a Table datatype is TString.
After
that, type check is done through the api_check macro, but it is not reflected
in the binary in the default build environment.
This
causes type confusion between TString variables and Table variables.
--------------------------------------------------------------------------------------------------------------------------
Found
by: Kang woosun. |
Attachment:
signature.asc
Description: Message signed with OpenPGP