-
-
KCTF2020秋季赛 第十题 终焉之战
-
2020-12-11 01:08 8049
-
从zImage中提取出主程序: /usr/bin/k2020
调试
qemu-arm -g 23946 ./k2020
IDA: 调试器选择 Remote GDB debugger
程序内嵌了个duktape js引擎,验证函数h在js(bytecode形式)中
unsigned char bytecode_0016A0BB[160254]; // 函数 int main(int argc, char **argv) { duk_context *ctx = ctx = duk_create_heap(NULL, NULL, NULL, NULL, NULL); duk_module_duktape_init(ctx); void *buf = duk_push_fixed_buffer(ctx, sizeof(bytecode_0016A0BB)); memcpy(buf, bytecode_0016A0BB, sizeof(bytecode_0016A0BB)); duk_load_function(ctx); duk_push_global_object(ctx); duk_call_method(ctx, 0); duk_push_string(ctx, "h('12345678');"); duk_eval_raw(ctx, NULL, 0, 0x809); unsigned int r = duk_get_boolean(ctx, -1); duk_pop(ctx); printf("r: %d\n", r); duk_destroy_heap(ctx); }
编译duk_cmdline
hxxps://duktape.org/duktape-2.6.0.tar.xz examples\cmdline\duk_cmdline.c extras\module-duktape\duk_module_duktape.c src-noline\duktape.c DUK_F_NO_STDINT_H DUK_CMDLINE_MODULE_SUPPORT
测试下duk_cmdline
duk.exe -i -b func.bc ((o) Duktape 2.6.0 (v2.6.0) duk> typeof(h) = "function" duk> h('12345678') = false duk> _0xb413('0x42') = "4b43544632303230" duk> _0x4b55('0x13','Uev9') = "2b7874ef148937d1" duk> _0x4b55('0x5f','r[8S') = "bqtgz" duk> _0x4b55('0x5a', '&0lI') = "bqziabc"
生成bytecode
duk.exe test.js -c test.bc
反汇编bytecode
tools\dump_bytecode.py debugger\duk_opcodes.yaml
这个自带反汇编工具会把常量单独放到一块, 看起来不太直观, 稍微改造下
import gmpy2 import itertools import os import struct import sys import yaml duk_ops = None file_path = sys.path[0] def load_file(filename): f = open(filename, 'rb') s = f.read() f.close() return s def save_file(filename, s): f = open(filename, 'wb') f.write(s) f.close() return def hex2bin(s): return s.decode('hex') def bin2hex(s): return s.encode('hex') class BinaryReaderBE: def __init__(self, fname): self.buf = load_file(fname) self.offset = 0 return def eof(self): return self.offset >= len(self.buf) def get_offset(self): return self.offset def get_u8(self): v = struct.unpack('B', self.buf[self.offset:self.offset + 1])[0] self.offset += 1 return v def get_u16(self): v = struct.unpack('>H', self.buf[self.offset:self.offset + 2])[0] self.offset += 2 return v def get_u32(self): v = struct.unpack('>L', self.buf[self.offset:self.offset + 4])[0] self.offset += 4 return v def get_double(self): v = struct.unpack('>d', self.buf[self.offset:self.offset + 8])[0] self.offset += 8 return v def get_string(self): strlen = self.get_u32() strdata = self.buf[self.offset:self.offset+strlen] self.offset += strlen return strdata class DukABC: def __init__(self, insn): self.raw = insn self.op = insn & 0xff self.a = (insn >> 8) & 0xff self.b = (insn >> 16) & 0xff self.c = (insn >> 24) & 0xff self.bc = (insn >> 16) & 0xffff self.abc = (insn >> 8) & 0xffffff self.bconst = insn & 0x1 self.cconst = insn & 0x2 return class DukFunction: def __init__(self, br): # type:(BinaryReaderBE) -> None count_insn = br.get_u32() count_const = br.get_u32() count_func = br.get_u32() self.nregs = br.get_u16() self.nargs = br.get_u16() br.get_u32() # start_line br.get_u32() # end_line br.get_u32() # compfunc_flags self.ary_insn = [] # type:list[DukABC] self.ary_const = [] self.ary_func = [] # type:list[DukFunction] for i in itertools.count(): if i >= count_insn: break self.ary_insn.append(DukABC(br.get_u32())) for i in itertools.count(): if i >= count_const: break const_type = br.get_u8() if const_type == 0x00: self.ary_const.append(br.get_string()) elif const_type == 0x01: # self.ary_const.append(int(br.get_double())) else: raise Exception('invalid constant type: %d' % const_type) for i in itertools.count(): if i >= count_func: break self.ary_func.append(DukFunction(br)) br.get_u32() # length self.name = self.sanitize_string(br.get_string()) br.get_string() # filename br.get_string() # pc2line self.varmap = {} while True: var_name = br.get_string() if var_name == '': break var_reg = br.get_u32() self.varmap[var_reg] = var_name num_formals = br.get_u32() if num_formals != 0xffffffff: for i in itertools.count(): if i >= num_formals: break br.get_string() # name return def sanitize_string(self, s): r = '' for ch in s: v = ord(ch) if v < 0x20 or v > 0x7e or v == 0x22 or v == 0x27: r += '\\x%02x' % v else: r += ch name_map = { '_0x142e73': 'cmpn', '_0x26b88f': 'assert', '_0x3953e3': 'BN', '_0x5ee993': 'imaskn', } if r in name_map: r = name_map[r] return '\'%s\'' % r def get_const(self, i): v = self.ary_const[i] if type(v) == str: return self.sanitize_string(v) return str(v) def get_reg(self, i): return 'r%d' % i def get_var_map(self, reg): if len(self.varmap) != 0: if reg in self.varmap: return '%s=%s' % (self.varmap[reg], self.get_reg(reg)) return '' def disasm(self, func_id, indent): # add {} so editors can FOLD/UNFOLD print('%sfunction %s() { // name=%s, args=%d' % (indent, 'Function_%d' % func_id, self.name, self.nargs)) # for var_reg in self.varmap.keys(): # print('%s// VarMap[%d]=%s' % (indent, var_reg, self.varmap[var_reg])) for i in xrange(len(self.ary_insn)): self.disasm_insn(i, indent) for i in xrange(len(self.ary_func)): self.ary_func[i].disasm(i, indent + ' ') print('%s}' % indent) return def disasm_insn(self, i, indent): global duk_ops pc = i args = [] insn = self.ary_insn[i] op = duk_ops[insn.op] a = insn.a b = insn.b c = insn.c bc = insn.bc abc = insn.abc bconst = insn.bconst cconst = insn.cconst comments = [] # varmap should only be applied when dst_reg == var_reg, but we just apply varmap to all if 'args' in op: for j in xrange(len(op['args'])): if op['args'][j] == 'A_R': args.append(self.get_reg(a)) comments.append(self.get_var_map(a)) elif op['args'][j] == 'A_RI': args.append(self.get_reg(a)) comments.append(self.get_var_map(a)) elif op['args'][j] == 'A_C': args.append(self.get_const(a)) elif op['args'][j] == 'A_H': continue elif op['args'][j] == 'A_I': args.append(str(a)) elif op['args'][j] == 'A_B': args.append('true' if a else 'false') elif op['args'][j] == 'B_RC': if bconst: args.append(self.get_const(b)) else: args.append(self.get_reg(b)) comments.append(self.get_var_map(b)) elif op['args'][j] == 'B_R': args.append(self.get_reg(b)) comments.append(self.get_var_map(b)) elif op['args'][j] == 'B_RI': args.append(self.get_reg(b)) comments.append(self.get_var_map(b)) elif op['args'][j] == 'B_C': args.append(self.get_const(b)) elif op['args'][j] == 'B_H': continue elif op['args'][j] == 'B_I': args.append(str(b)) elif op['args'][j] == 'C_RC': if cconst: args.append(self.get_const(c)) else: args.append(self.get_reg(c)) comments.append(self.get_var_map(c)) elif op['args'][j] == 'C_R': args.append(self.get_reg(c)) comments.append(self.get_var_map(c)) elif op['args'][j] == 'C_RI': args.append(self.get_reg(c)) comments.append(self.get_var_map(c)) elif op['args'][j] == 'C_C': args.append(self.get_const(c)) elif op['args'][j] == 'C_H': continue elif op['args'][j] == 'C_I': args.append(str(c)) elif op['args'][j] == 'BC_R': args.append(self.get_reg(bc)) comments.append(self.get_var_map(bc)) elif op['args'][j] == 'BC_C': args.append(self.get_const(bc)) elif op['args'][j] == 'BC_H': continue elif op['args'][j] == 'BC_I': args.append(str(bc)) elif op['args'][j] == 'ABC_H': continue elif op['args'][j] == 'ABC_I': args.append(str(abc)) elif op['args'][j] == 'BC_LDINT': args.append(str(bc - (1 << 15))) elif op['args'][j] == 'BC_LDINTX': args.append(str(bc)) elif op['args'][j] == 'ABC_JUMP': pc_add = abc - (1 << 23) + 1 pc_dst = pc + pc_add args.append(str(pc_dst)) else: args.append('?') if len(args) > 0: res = '%-12s %s' % (op['name'], ', '.join(args)) else: res = op['name'] valid_comments = [] for comment in comments: if comment != '': valid_comments.append(comment) if len(valid_comments) > 0: res += ' // %s' % ', '.join(valid_comments) print('%s %06d: %s' % (indent, i, res)) return def duk_init(): global duk_ops fn = os.path.join(file_path, 'duk_opcodes.yaml') with open(fn) as f: duk_ops = yaml.load(f, Loader=yaml.SafeLoader)['opcodes'] return def test1(): duk_init() br = BinaryReaderBE(os.path.join(file_path, 'test.bc')) br.get_u8() func = DukFunction(br) func.disasm(0, '') return def test2(): n = 0xc021a1277f8b7ee7 d = 0x1EA97 h = 0x4b43544632303230 k = 0x1E636A6AD7 r = 0x4116EA4E6FE3A3C7 # k*G s = (h + r * d) * gmpy2.invert(k, n) % n print('flag: %x' % s) return test1() # test2()
测试下反汇编
/ test.js ary = { a:'12345', b:'67890' } */ function Function_0() { // name='global', args=0 000000: LDUNDEF r0 000001: NEWOBJ 2, r1 000002: LDCONST r2, 'a' 000003: LDCONST r3, '12345' 000004: LDCONST r4, 'b' 000005: LDCONST r5, '67890' 000006: MPUTOBJ r1, r2, 4 000007: PUTVAR r1, 'ary' 000008: LDREG r0, r1 000009: RETREG r0 }
内嵌的js(使用hxxps://www.jsjiami.com/处理过)反汇编(简化后), 功能为ecdsa签名验证
function Function_0() { // name='global', args=0 function Function_4() { // name='', args=2 000000: CLOSURE r2, 16 // _0x3c4bd8=r2 000001: CLOSURE r3, 17 // _0x443985=r3 000002: CLOSURE r4, 18 // _0x4653f8=r4 000003: CLOSURE r5, 19 // _0x14ab88=r5 000004: CLOSURE r6, 20 // _0x371323=r6 000005: CLOSURE r7, 21 // _0x26acf4=r7 000006: CLOSURE r8, 22 // _0x3e3825=r8 000007: CLOSURE r9, 23 // _0x37288a=r9 000008: CLOSURE r10, 24 // _0x6ea185=r10 000009: CLOSURE r11, 25 // _0x389a22=r11 000010: CLOSURE r12, 26 // _0x5b72da=r12 000011: CLOSURE r13, 27 // _0x3f0439=r13 000012: GETVAR r14, '_0x4b55' // _0x5aa897=r14 ... 000048: CSREG r14, r38 // _0x5aa897=r14 000049: LDCONST r40, '0x13' 000050: LDCONST r41, 'Uev9' 000051: CALL0 2, r38 // _0x4b55('0x13','Uev9')='2b7874ef148937d1' 000052: LDCONST r39, 'Nivrl' 000053: LDCONST r40, '496412a7502c4cd5' 000054: LDCONST r41, 'AxYnU' 000055: LDCONST r42, 'c021a1277f8b7ee7' 000056: MPUTOBJ r24, r25, 18 // Nivrl='496412a7502c4cd5', AxYnU='c021a1277f8b7ee7' 000057: LDREG r15, r24 // _0x1d6a0f=r15 000058: CSREG r2, r24 // _0x3c4bd8=r2 000059: LDREG r26, r15 // _0x1d6a0f=r15 000060: CSREG r14, r27 // _0x5aa897=r14 000061: LDCONST r29, '0x93' 000062: LDCONST r30, 'tglZ' 000063: CALL0 2, r27 000064: GETPROP_RR r26, r26, r27 000065: LDINT r27, 16 000066: CALL0 2, r24 000067: LDREG r16, r24 // _0x45d09e=r16, BN('2b7874ef148937d1', 16) 000068: CSREG r2, r25 // _0x3c4bd8=r2 000069: LDREG r27, r15 // _0x1d6a0f=r15 000070: GETPROP_RC r27, r27, 'Nivrl' 000071: LDINT r28, 16 000072: CALL0 2, r25 000073: LDREG r17, r25 // _0x158e32=r17, BN('496412a7502c4cd5', 16) 000074: CSREG r2, r26 // _0x3c4bd8=r2 000075: LDCONST r28, '4d8acf91aac0ba96' 000076: LDINT r29, 16 000077: CALL0 2, r26 000078: LDREG r18, r26 // _0x9114ff=r18 000079: CSREG r2, r27 // _0x3c4bd8=r2 000080: LDCONST r29, '79e9ac784679a36d' 000081: LDINT r30, 16 000082: CALL0 2, r27 000083: LDREG r19, r27 // _0x6d6511=r19, BN('79e9ac784679a36d', 16) 000084: CSREG r2, r28 // _0x3c4bd8=r2 000085: LDCONST r30, 'C021A12750820335' 000086: LDINT r31, 16 000087: CALL0 2, r28 000088: LDREG r20, r28 // _0xbe4903=r20, BN('C021A12750820335', 16) 000089: CSREG r2, r29 // _0x3c4bd8=r2 000090: LDREG r31, r15 // _0x1d6a0f=r15 000091: GETPROP_RC r31, r31, 'AxYnU' 000092: LDINT r32, 16 000093: CALL0 2, r29 000094: LDREG r21, r29 // _0x160c05=r21, BN('c021a1277f8b7ee7', 16) 000095: CSREG r2, r30 // _0x3c4bd8=r2 000096: LDINT r32, 0 000097: CALL0 1, r30 000098: LDREG r22, r30 // _0x14d425=r22 000099: CSREG r2, r31 // _0x3c4bd8=r2 000100: LDINT r33, 1 000101: CALL0 1, r31 000102: LDREG r23, r31 // _0x3ac321=r23 000103: LDREG r24, r1 // _0x1755c1=r1 000104: PUTPROP_CR r24, 'gggggh', r2 // _0x3c4bd8=r2, gggggh=BN 000105: LDREG r25, r1 // _0x1755c1=r1 000106: PUTPROP_CR r25, 'bbccdde', r13 // _0x3f0439=r13, bbccdde=Function_27 000107: RETUNDEF function Function_27(flag) { // name='_0x3f0439', args=1 000000: GETVAR r1, '_0xb413' 000001: LDREG r14, r0 000002: LDREG r16, r14 000003: GETPROPC_RC r15, r16, 'gte' 000004: GETVAR r17, '_0x160c05' 000005: CALL0 1, r15 000006: IFTRUE_R r15 // flag < _0x160c05 000007: JUMP 11 000008: NEWARR 0, r14 000009: LNOT r14, r14 000010: RETREG r14 000011: CSVAR_CR r14, '_0x3c4bd8' 000012: LDCONST r16, 'b520ee455c4e5c68' 000013: LDINT r17, 16 000014: CALL0 2, r14 000015: LDREG r2, r14 // r2=BN('b520ee455c4e5c68', 16) 000016: CSVAR_CR r15, '_0x3c4bd8' 000017: LDCONST r17, '8d2984eb2d7fd1ce' 000018: LDINT r18, 16 000019: CALL0 2, r15 000020: LDREG r3, r15 // r3=BN('8d2984eb2d7fd1ce', 16) 000021: GETVAR r16, '_0x1d6a0f' 000022: LDREG r18, r16 000023: GETPROPC_RC r17, r18, 'kXpuM' 000024: GETVAR r19, '_0x3c4bd8' 000025: LDCONST r20, 130516937431 000026: LDINT r21, 10 000027: CALL0 3, r17 000028: LDREG r4, r17 000029: CSVAR_CR r18, '_0x26acf4' 000030: LDREG r20, r4 000031: CALL0 1, r18 000032: GETPROP_RC r5, r18, 0 // r5=(BN('130516937431', 10)*G).x 000033: CSVAR_CR r19, '_0x3c4bd8' 000034: CSREG r1, r21 000035: LDCONST r23, '0x42' 000036: CALL0 1, r21 000037: LDINT r22, 16 000038: CALL0 2, r19 000039: LDREG r6, r19 // r6=BN('4b43544632303230', 16) 000040: CSVAR_CR r20, '_0x371323' 000041: LDREG r22, r0 000042: GETVAR r23, '_0x160c05' 000043: CALL0 2, r20 000044: LDREG r7, r20 // r7=invert(flag, _0x160c05) 000045: CSVAR_CR r21, '_0x4653f8' 000046: LDREG r23, r6 000047: LDREG r24, r7 000048: GETVAR r25, '_0x160c05' 000049: CALL0 3, r21 000050: LDREG r8, r21 // r8=mul(r6, r7, _0x160c05) 000051: CSVAR_CR r22, '_0x4653f8' 000052: LDREG r24, r5 000053: LDREG r25, r7 000054: GETVAR r26, '_0x160c05' 000055: CALL0 3, r22 000056: LDREG r9, r22 // r9=mul(r5, r7, _0x160c05) 000057: CSVAR_CR r23, '_0x5b72da' 000058: LDREG r25, r2 000059: LDREG r26, r3 000060: CALL0 2, r23 000061: LDREG r10, r23 // r10=Point(r2, r3) 000062: CSVAR_CR r24, '_0x5b72da' 000063: GETVAR r26, '_0x9114ff' 000064: GETVAR r27, '_0x6d6511' 000065: CALL0 2, r24 000066: LDREG r11, r24 // r11=Point(_0x9114ff, _0x6d6511) 000067: CSVAR_CR r25, '_0x389a22' 000068: CSVAR_CR r27, '_0x443985' 000069: LDREG r29, r11 000070: LDREG r30, r8 000071: CALL0 2, r27 // r27=PointMul(r11, r8) 000072: CSVAR_CR r28, '_0x443985' 000073: LDREG r30, r10 000074: LDREG r31, r9 000075: CALL0 2, r28 // r28=PointMul(r10, r9) 000076: CALL0 2, r25 000077: LDREG r12, r25 // r12=PointAdd(r27, r28) 000078: CSVAR_CR r26, '_0x3e3825' 000079: LDREG r28, r12 000080: CALL0 1, r26 000081: LDREG r13, r26 // r13=_0x3e3825(r12); 000082: LDREG r14, r5 000083: LDREG r16, r14 000084: GETPROPC_RC r15, r16, 'eq' 000085: LDREG r17, r13 000086: GETPROP_RC r17, r17, 0 000087: CALL1 1, r15 000088: RETREG r15 // return r5 == r13.x; 000089: RETUNDEF } } function Function_5(flag) { // name='h', args=1 000000: CSVAR_CR r2, 'gggggh' 000001: LDREG r4, r0 000002: LDINT r5, 16 000003: CALL0 2, r2 000004: LDREG r1, r2 000005: CSVAR_CR r2, 'bbccdde' 000006: LDREG r4, r1 000007: CALL1 1, r2 000008: RETREG r2 // return bbccdde(gggggh(flag, 16)); 000009: RETUNDEF } }
ecdsa
签名: r,s A: 2b7874ef148937d1 B: 496412a7502c4cd5 P: C021A12750820335 Q: c021a1277f8b7ee7 (order) G: (4d8acf91aac0ba96, 79e9ac784679a36d) (base) R: (b520ee455c4e5c68, 8d2984eb2d7fd1ce) (pub) h: 4b43544632303230 (hash) k: 1E636A6AD7 (random) r: (k*G).x=4116EA4E6FE3A3C7 (sign.r) s: flag (sign.s) 验证 u: h/s v: r/s v = u *G + w *R = (h/s) *G + (r/s) * R v == r 签名 先算出私钥, d*G=R, d = 1EA97 s = (h + r * d) / k = 2296dc2f09144713 -------------------------------- x64 ECDLP Solver v0.2a by MR.HAANDI Elliptic Curve defined by y^2 = x^3 + 3132382111026722769*x + 5288372372253723861 over GF(13844523919740109621) k*G=K G=[5587506512248486550,8784742180741292909] K=[13051693701788490856,10171807379008508366] Order(K)=13844523920529260263 Initializing rho solver k*G=K G=[5587506512248486550,8784742180741292909] K=[13051693701788490856,10171807379008508366] k=125591
[培训]二进制漏洞攻防(第3期);满10人开班;模糊测试与工具使用二次开发;网络协议漏洞挖掘;Linux内核漏洞挖掘与利用;AOSP漏洞挖掘与利用;代码审计。
最后于 2020-12-16 20:21
被风间仁编辑
,原因:
赞赏
他的文章
KCTF2022春季赛 第三题 石像病毒
8224
KCTF2022春季赛 第二题 末日邀请
15351
KCTF2021秋季赛 第二题 迷失丛林
17879
KCTF2020秋季赛 第十题 终焉之战
8050
KCTF2020秋季赛 第九题 命悬一线
5787
看原图