Malicious Bytecode Peter Cawley <lua@corsix.org> Lua Workshop - - PowerPoint PPT Presentation

malicious bytecode
SMART_READER_LITE
LIVE PREVIEW

Malicious Bytecode Peter Cawley <lua@corsix.org> Lua Workshop - - PowerPoint PPT Presentation

Mitigating the Danger of Malicious Bytecode Peter Cawley <lua@corsix.org> Lua Workshop 2011 A Common Pattern 1. Create sandbox 2. Load user-supplied Lua (byte|source) code 3. Run code in sandbox A Common Pattern 1. Create sandbox 2.


slide-1
SLIDE 1

Mitigating the Danger of Malicious Bytecode

Peter Cawley <lua@corsix.org> Lua Workshop 2011

slide-2
SLIDE 2

A Common Pattern

  • 1. Create sandbox
  • 2. Load user-supplied Lua (byte|source) code
  • 3. Run code in sandbox
slide-3
SLIDE 3

A Common Pattern

  • 1. Create sandbox
  • 2. Load user-supplied Lua (byte|source) code
  • 3. Run code in sandbox

Sandbox Blacklist

  • s.*

io.* debug.* package.loadlib package.loaders[3] package.loaders[4]

slide-4
SLIDE 4

A Common Pattern

  • 1. Create sandbox
  • 2. Load user-supplied Lua (byte|source) code
  • 3. Run code in sandbox

Sandbox Whitelist string.gsub table.sort

slide-5
SLIDE 5

A Common Pattern

  • 1. Create sandbox
  • 2. Load user-supplied Lua (byte|source) code
  • 3. Run code in sandbox
  • Arbitrary native code

execution*

Sandbox Whitelist string.gsub table.sort

* At least for Lua 5.1.4 on x86 Windows (even with DEP and ASLR)

slide-6
SLIDE 6

Bytecode

Bytecode GETGLOBAL r0, print LOADK r1, “Lua” CALL r0, 1, 0 Source code print “Lua” Virtual machine load call

slide-7
SLIDE 7

Bytecode

Bytecode GETGLOBAL r0, print LOADK r1, “Lua” CALL r0, 1, 0 Source code print “Lua” Virtual machine load call Serialised bytecode \27Lua\x51\0\1\4\4\4\8\0\7\0\0\0\61stdin \0\1\0\0\0\1\0\0\0\0\0\0\2\4\0\0\0\5\0\0 \0\65\64\0\0\28\64\00\1\30\0\128\0\2\0\0 \0\4\6\0\0\0print\0\4\4\0\0\0Lua\0\0\0\0 \0\4\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0 \0\0\0\0\0\0\0\0\0 dump

slide-8
SLIDE 8

Logical TValue

slide-9
SLIDE 9

Physical TValue

slide-10
SLIDE 10

C API abusing a TValue

void lua_rawget(lua_State* L, int idx) { TValue* t = index2adr(L, idx); api_check(L, ttistable(t)); L->top[–1] = *luaH_get(hvalue(t), L->top - 1); }

slide-11
SLIDE 11

C API abusing a TValue

void lua_rawget(lua_State* L, int idx) { TValue* t = index2adr(L, idx); api_check(L, ttistable(t)); L->top[–1] = *luaH_get(hvalue(t), L->top - 1); } int table.sort(lua_State* L) { luaL_checktype(L, 1, LUA_TTABLE); /* ... */ lua_rawget(L, 1); /* ... */ }

slide-12
SLIDE 12

C API abusing a TValue

void lua_rawget(lua_State* L, int idx) { TValue* t = index2adr(L, idx); api_check(L, ttistable(t)); L->top[–1] = *luaH_get(hvalue(t), L->top - 1); } int table.sort(lua_State* L) { luaL_checktype(L, 1, LUA_TTABLE); /* ... call comparison function ... */ lua_rawget(L, 1); /* ... call comparison function ... */ }

slide-13
SLIDE 13

Virtual Machine abusing a TValue

for x = init, limit, step do print(x) end GETGLOBAL init GETGLOBAL limit GETGLOBAL step FORPREP GETGLOBAL print MOVE x CALL FORLOOP

slide-14
SLIDE 14

Function Calls

local t = {“go”, “a”} table.sort(t, function(lhs, rhs) return #lhs < #rhs end)

slide-15
SLIDE 15

Function Calls

local t = {“go”, “a”} table.sort(t, function(lhs, rhs) return #lhs < #rhs end)

{“go”, “a”} r0 table.sort r1 {“go”, “a”} r2 function r3 r4 r5 r6 r7 r8 r9

slide-16
SLIDE 16

Function Calls

local t = {“go”, “a”} table.sort(t, function(lhs, rhs) return #lhs < #rhs end)

{“go”, “a”} table.sort {“go”, “a”} 1 function 2

slide-17
SLIDE 17

Function Calls

local t = {“go”, “a”} table.sort(t, function(lhs, rhs) return #lhs < #rhs end)

{“go”, “a”} table.sort {“go”, “a”} 1 function 2 “go”

  • 5

“a”

  • 4

function

  • 3

“a”

  • 2

“go”

  • 1
slide-18
SLIDE 18

Function Calls

local t = {“go”, “a”} table.sort(t, function(lhs, rhs) return #lhs < #rhs end)

{“go”, “a”} table.sort {“go”, “a”} function “go” “a” function “a” r0 “go” r1 r2

slide-19
SLIDE 19

Function Calls

local t = {“go”, “a”} table.sort(t, function(lhs, rhs) return #lhs < #rhs end)

{“go”, “a”} table.sort {“go”, “a”} function “go” “a” function “a” r0 “go” r1 false r2

slide-20
SLIDE 20

Function Calls

local t = {“go”, “a”} table.sort(t, function(lhs, rhs) return #lhs < #rhs end)

{“go”, “a”} table.sort {“go”, “a”} 1 function 2 “go”

  • 3

“a”

  • 2

false

  • 1

“a” “go” false

slide-21
SLIDE 21

Function Calls

local t = {“go”, “a”} table.sort(t, function(lhs, rhs) return #lhs < #rhs end)

{“a”, “go”} table.sort {“a”, “go”} 1 function 2 “go” “a” false “a” “go” false

slide-22
SLIDE 22

Function Calls

local t = {“go”, “a”} table.sort(t, function(lhs, rhs) return #lhs < #rhs end)

{“a”, “go”} r0 table.sort r1 {“a”, “go”} r2 function r3 “go” r4 “a” r5 false r6 “a” r7 “go” r8 false r9

slide-23
SLIDE 23

Upvalues

local x = 10 local count = function() x = x + 1 return x end

10 (function) upvalue #0 GETUPVAL ADD SETUPVAL GETUPVAL RETURN

slide-24
SLIDE 24

Upvalues

\27Lua\x51 ... (malicious bytecode here) ...

10 (function) upvalue #0 GETUPVAL ADD SETUPVAL GETUPVAL RETURN

slide-25
SLIDE 25

Malicious Bytecode Catalogue

  • Violating type assumptions in the VM

– FORLOOP – SETLIST in 5.2

  • Emulating debug.[gs]etlocal

– Reading leftover locals – Promiscuous upvalues

  • Violating type assumptions in the C API

– lua_(next|raw[gs]eti?) – lua_[gs]etuservalue in 5.2

slide-26
SLIDE 26

Mitigation Catalogue

  • Don’t load bytecode

– First byte decimal 27 – load(ld, source, "t" [, env]) in 5.2

  • Compile with LUA_USE_APICHECK (*)
  • Static analysis and verification of bytecode

(*) Makes exploitation harder, doesn’t prevent information leakage attacks, may not save you.

slide-27
SLIDE 27

Static Analysis, Blunt Approach

  • Violating type assumptions in the VM

– For each stack slot, at each VM instruction, determine a set of possible types

  • Emulating debug.[gs]etlocal

– Ensure stack slots are safely readable – For each stack slot, at each VM instruction, determine if it could be an upvalue – Segregating calls from upvalues

slide-28
SLIDE 28

Static Type Analysis

function example(x) if x then x = 3.14 else x = “pi” end return x end .parameter r0 TEST r0; JMP $+2 LOADK r0, k0 JMP $+1 LOADK r0, k1 RETURN r0

slide-29
SLIDE 29

Static Type Analysis

function example(x) if x then x = 3.14 else x = “pi” end return x end

TEST r0 LOADK r0 3.14 LOADK r0 “pi” RETURN r0

slide-30
SLIDE 30

Static Type Analysis

function example(x) if x then x = 3.14 else x = “pi” end return x end

TEST r0 LOADK r0 3.14 LOADK r0 “pi” RETURN r0 * num str {num, str}

slide-31
SLIDE 31

Static Analysis Prerequisites

  • Decode and understand each instruction
  • Ensure control flow doesn’t leave
  • Valid (register|constant|…) indices
  • Verify some VM assumptions, like:

– TEST instructions are followed by a JMP – Boolean constants are either 0 or 1

  • Instructions which produce or consume a variable

number of values must come in pairs

slide-32
SLIDE 32

Static Analysis, Subtle Approach

  • Violating type assumptions in the VM

– Protect loop control variables – Perform runtime table type checks

  • Emulating debug.[gs]etlocal

– At each VM instruction, split the stack into locals / temporary / unused Upvalues Calls Unreadable

slide-33
SLIDE 33

Static Analysis, Subtle Approach

  • Debug information embedded within bytecode

– Gives size of the local region at each instruction – Specifies which locals are loop control variables

  • The temporary region always grows into the next

available unused stack slot

  • The local region always grows to absorb a temporary
  • Backward jumps are to locations with no temporaries
  • Forward jumps merge to the smallest of the

temporary ranges

slide-34
SLIDE 34

“Practical” Static Analysis

require "lbcv" lbcv.verify(ld) lbcv.load(ld [, source [, mode]])

slide-35
SLIDE 35

Questions?

Peter Cawley <lua@corsix.org> Lua Workshop 2011