首页
论坛
课程
招聘
KCTF2020秋季赛 第十题 终焉之战
2020-12-11 01:08 2980

KCTF2020秋季赛 第十题 终焉之战

2020-12-11 01:08
2980

从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

[公告] 推荐好文功能上线,分享知识还可以得雪币!推荐一篇文章获得20雪币!

最后于 2020-12-16 20:21 被风间仁编辑 ,原因:
收藏
点赞3
打赏
分享
最新回复 (1)
雪    币: 15711
活跃值: 活跃值 (11549)
能力值: (RANK:75 )
在线值:
发帖
回帖
粉丝
Editor 活跃值 2020-12-15 17:19
2
0
tql!
游客
登录 | 注册 方可回帖
返回