|
I did some simple debugging by inserting 'printf()' into the following call chain: ----------------- luaG_runerror()->luaG_errormsg()->ccall()...luaG_runerror()->luaG_errormsg()->CRASH! ----------------- And got this: ----------------- [luaG_runerror()] attempt to call a number value [luaG_errormsg()] L->errfunc: 0xa0 [luaG_errormsg()] L->top: 0x5555555979e0 [ccall()] L->nCcalls: 0x50004 LUAI_MAXCCALLS: 0xc8 [luaG_runerror()] attempt to index a nil value (global 't') [luaG_errormsg()] L->errfunc: 0xa0 [luaG_errormsg()] L->top: 0x555555597ab0 [ccall()] L->nCcalls: 0x60005 LUAI_MAXCCALLS: 0xc8 [luaG_runerror()] attempt to index a nil value (global 't') [luaG_errormsg()] L->errfunc: 0xa0 [luaG_errormsg()] L->top: 0x555555597b80 [ccall()] L->nCcalls: 0x70006 LUAI_MAXCCALLS: 0xc8 ...... [luaG_runerror()] attempt to index a nil value (global 't') [luaG_errormsg()] L->errfunc: 0xa0 [luaG_errormsg()] L->top: 0x5555555b35f0 [ccall()] L->nCcalls: 0xc900c8 LUAI_MAXCCALLS: 0xc8 [luaG_runerror()] C stack overflow [luaG_errormsg()] L->errfunc: 0xa0 [luaG_errormsg()] L->top: 0x5555555b3620 ----------------- Then I do the following analysis (as you said before): When the first error that 'attempt to call a number value' occour, program will run into error handling function provide by 'xpcall'. At this point a second error that 'attempt to index a nil value' occurred. Because this error occurs in an error handling function, a recursive error handling is triggered. Everytime 'ccall' was called, it will check `l_unlikely(getCcalls(L) >= LUAI_MAXCCALLS` which makes sure that the stack buffer is not overflowed. Until this condition is met, the program will call the 'luaE_checkcstack' function. This function will execute the following statement: `luaG_runerror(L, "C stack overflow");`. And program goes into luaG_runerror again... Bad things happen: 'luaG_runerror()->luaG_errormsg()' will push some value to lua stack (actually the tail of the heap memory allocated by glibc) but will not grow the stack. Eventually this overflow overwrites zero value to the 'prev_inuse' field of the 'prevsize' of the next heap chunk. This will cause 'double free' crash report of glibc (not the real double free). This is the mem status before the overwrite: ---------------- pwndbg> x/10gx 0x5555555b3620-0x10 0x5555555b3610: 0x000055555559eea0 0x0000000000000044 <- tail of old stack (heap chunk) 0x5555555b3620: 0x0000000000000000 0x0000000000000051 <- next heap chunk 0x5555555b3630: 0x00005555555a8c60 0x4b88da7300001014 0x5555555b3640: 0x0000000000000029 0x2074706d65747461 0x5555555b3650: 0x7865646e69206f74 0x76206c696e206120 ---------------- This is the mem status after the overwrite: ---------------- 0x5555555b3610: 0x000055555559eea0 0x0000000000000044 <- tail of old stack (heap chunk) 0x5555555b3620: 0x000055555559eea0 0x0000000000000044 <- next heap chunk (damaged) 0x5555555b3630: 0x00005555555a8c60 0x4b88da7300001014 0x5555555b3640: 0x0000000000000029 0x2074706d65747461 0x5555555b3650: 0x7865646e69206f74 0x76206c696e206120 ---------------- This is my brief analysis of the bugs. Due to the vigilance of security researchers, if the context and the harmful data of the overwrite are carefully constructed, it could lead to sandbox escape and command execution (I can try to prove it if necessary). 发件人: Roberto Ierusalimschy The problem seems to be the use of the EXTRA_STACK at luaG_errormsg. luaG_errormsg calls luaD_callnoyield, which calls luaD_precall, which checks the stack and grows it if needed. However, there can be an error before that, in luaE_checkcstack. If luaE_checkcstack raises an error (C stack overflow), then luaG_errormsg will be called again without any stack check in-between, and then it will again assume EXTRA_STACK and that may cause a buffer overflow. -- Roberto |