首页
论坛
课程
招聘
KCTF2020秋季赛 第八题 惊天阴谋
2020-12-6 20:47 2912

KCTF2020秋季赛 第八题 惊天阴谋

2020-12-6 20:47
2912

处理逻辑

# 用户名+序列号形式, 序列号格式为16进制小写, 
# 用户名记为user, 序列号分两部分记为sn1, sn2, 
sn1=hex2bin(sn1), len(sn1)==32
sn2=hex2bin(sn2), len(sn2)==0xC0
md=md5(user)
md1=md5(md[0:8])
md2=md5(md[8:16])

# >>>> 处理sn1

# u32 v[8]
# 注意tlscallback中的初始化
# .text:004054B0 mov dword_6E2771, 0F496B3AFh
for i in range(8):
    v[i]=sub_402EE0(v[i])  # 这个只有位移与异或操作, 用z3来解就行了, 注意右移操作要用LShR就行了

# sub_00404010, Crypt系列API, 这里求kernel32基址的shellcode有问题, 所以不能在win7上正常运行
aes_key=sha256('1_L0V3_BXS_F0REVER!')
aes_iv='\x00'*16
aes_cbc_encrypt(aes_key, aes_iv, v) 

# u8 v[32]
# u8 buf[16]
for i in range(16):
    buf[i] = v[2*i]
    assert v[2*i]+0x7F == v[2*i+1]

# sub_004033B0
# .data:006ECD70 aes_sbox 与标准的不一样
aes_key='Wo YongYuan XiHuan KanXun LunTan'
assert aes_encrypt(aes_key, buf) == md1  # 

# >>>> 处理sn2

# sub_4041A0, 输入0xC0字节, 输出为32字节
s = to_binary_string(s)
buf = []
for i in range(0x200):
    # 每次取3bit
    buf[i] = s[3*i:3*i+3]
    buf[i] = (buf[i]-2)<<1
A=[
    [-1, -1, -1, 1, 1, -1, 1, 1],
    [-1, -1, 1, -1, 1, 1, 1, -1],
    [-1, 1, -1, 1, 1, 1, -1, -1],
    [-1, 1, -1, -1, -1, -1, 1, -1],
]
# buf为64个8*1的矩阵, 每个矩阵记为X, A*X=B
# B(4*1)可以确定结果中的4个bit
# 因为A矩阵不存在逆矩阵, 所以(已知A,B)求X存在很多解(8个变量, 4个方程)
# 但是如果用伪逆矩阵求解的话, 最终得到的序列号会与题目提供的相一致

# sub_404860, 解 模N-16元1次 方程, 输入32字节, 输出16字节

# sub_404D90, 与md2计算后==固定值


脚本

import hashlib
import struct
from Crypto.Cipher import AES
from z3 import *
import gmpy2 as gp
import numpy as np


g_005D0810 = None


def hex2bin(s):
    return s.decode('hex')


def bin2hex(s):
    return s.encode('hex')


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 sha256(s):
    md = hashlib.sha256()
    md.update(s)
    return md.digest()


def md5(s):
    md = hashlib.md5()
    md.update(s)
    return md.digest()


def aes_cbc_encrypt(key, iv, s):
    c = AES.new(key, AES.MODE_CBC, iv)
    return c.encrypt(s)


def aes_cbc_decrypt(key, iv, s):
    c = AES.new(key, AES.MODE_CBC, iv)
    return c.decrypt(s)


def aes_decrypt(s):
    # sbox: 006ECD70
    if s == hex2bin('510b3d82cb5577b5dc1a908233c12a36'):
        # KCTF
        return hex2bin('1F93C53327F92AD79198AF2EF597CA4A')
    if s == hex2bin('99f3546a852db6b344f2eb399160887a'):
        return hex2bin('60E94A7359AF5293E7C7553F805FE48A')
    s = bin2hex(s)
    # print s
    p = os.popen("aes.exe decrypt %s" % s)
    s = p.readline()
    # print s
    return hex2bin(s)


def ror8(v, shift):
    shift &= 7
    if shift == 0:
        return v
    return ((v >> shift) | (v << (8 - shift))) & 0xFF


def rol8(v, shift):
    shift &= 7
    if shift == 0:
        return v
    return ((v << shift) | (v >> (8 - shift))) & 0xFF


def get_bit(v, i):
    return (v >> i) & 1


def isbitset(v, i):
    return get_bit(v, i) == 1


def high4(v):
    return (v >> 4) & 0x0F


def low4(v):
    return v & 0x0F


def high8(v):
    return (v >> 8) & 0xFF


def low8(v):
    return v & 0xFF


def make16(h, l):
    return (h << 8) | l


def get_u8(ary, offset):
    if type(ary) == str:
        return ord(ary[offset])
    return ary[offset]


def get_u32(ary, offset):
    v = 0
    v |= get_u8(ary, offset)
    v |= get_u8(ary, offset + 1) << 8
    v |= get_u8(ary, offset + 2) << 16
    v |= get_u8(ary, offset + 3) << 24
    return v


def va2fileoffset(v):
    return v - 0x005CF000 + 0x001CD400


def x_404d90_rev(md):
    v3 = [
        0x1A, 0x1C, 0x11, 0x18, 0x08, 0x0C, 0x19, 0x01,
        0x20, 0x0B, 0x07, 0x10, 0x17, 0x02, 0x1D, 0x15,
        0x14, 0x1B, 0x16, 0x12, 0x05, 0x1E, 0x0A, 0x00,
        0x09, 0x03, 0x13, 0x0D, 0x04, 0x06, 0x0E, 0x1F,
    ]
    v2 = [
        0x15, 0x17, 0x16, 0x14, 0x13, 0x0C, 0x04, 0x12,
        0x03, 0x06, 0x10, 0x0E, 0x0A, 0x18, 0x1C, 0x0F,
        0x1F, 0x00, 0x0B, 0x05, 0x08, 0x1A, 0x0D, 0x20,
        0x1E, 0x1D, 0x11, 0x09, 0x19, 0x02, 0x01, 0x1B,
    ]
    v1 = [
        0x03, 0xEE, 0xEC, 0x11, 0x14, 0x0E, 0x05, 0x0C,
        0x03, 0xED, 0xF7, 0x05, 0x00, 0xF6, 0x00, 0xE7,
        0x00, 0xE8, 0xEF, 0x0B, 0xF5, 0x03, 0x04, 0xFF,
        0x00, 0x16, 0x06, 0xF4, 0xEF, 0x18, 0x09, 0xF9,
    ]
    r = [
        0x15, 0x09, 0x0C, 0x1F, 0x1C, 0x13, 0x16, 0x19,
        0x1D, 0x03, 0x10, 0x0F, 0x01, 0x02, 0x1E, 0x06,
    ]
    s = ''
    for i in range(16):
        s += chr(rol8((r[i] + ord(md[i]) - v1[i]) & 0xFF, v3[i]) ^ v2[i])
    return s


def x_4041a0_rev(dst):
    results = [-8, 8]
    A = np.matrix([
        [-1, -1, -1, 1, 1, -1, 1, 1],
        [-1, -1, 1, -1, 1, 1, 1, -1],
        [-1, 1, -1, 1, 1, 1, -1, -1],
        [-1, 1, -1, -1, -1, -1, 1, -1],
    ], dtype=long)
    # pseudo-inverse
    A_INV = np.linalg.pinv(A)
    r_bits = ''
    for i in range(8):
        for j in range(7, -1, -1):
            B = np.matrix([
                [results[get_bit(ord(dst[i]), j)]],
                [results[get_bit(ord(dst[i + 8]), j)]],
                [results[get_bit(ord(dst[i + 16]), j)]],
                [results[get_bit(ord(dst[i + 24]), j)]],
            ], dtype=long)
            X = (A_INV * B).tolist()
            for x in X:
                v = x[0]
                if v < 0:
                    v = int(v - 0.5)
                else:
                    v = int(v + 0.5)
                r_bits += bin(v / 2 + 2)[2:].rjust(3, '0')
    s = ''
    for i in range(0, len(r_bits), 8):
        s += chr(int(r_bits[i:i + 8], 2))
    return s


def x_402ee0_init():
    global g_005D0810
    offset = va2fileoffset(0x005D0810)
    g_005D0810 = bytearray(load_file('KCTF-KeyME.exe')[offset:])
    offset = va2fileoffset(0x006E2771) - offset
    val = 0xF496B3AF
    g_005D0810[offset] = val & 0xFF
    g_005D0810[offset + 1] = (val >> 8) & 0xFF
    g_005D0810[offset + 2] = (val >> 16) & 0xFF
    g_005D0810[offset + 3] = (val >> 24) & 0xFF
    return


def x_402ee0_rev(dst):
    global g_005D0810
    solver = Solver()
    src = BitVec('src', 32)
    state = [0] * 16
    state[15] = src
    ary = [0x0123, 0x4567, 0x89AB, 0xCDEF, 0x0F1E, 0x2D3C, 0x4B5A, 0x6978]
    i = 0
    while True:
        v0 = get_u32(g_005D0810, i)
        # u32 v0
        # u8 v1
        # u8 pad[pad_len]
        # u8 v2
        # u8 v3
        ary_idx = (get_bit(v0, 0x1B) << 2) | (get_bit(v0, 0x13) << 1) | get_bit(v0, 8)
        pad_len = (get_bit(v0, 0x10) << 1) | get_bit(v0, 7)
        v1 = get_u8(g_005D0810, i + 4)
        v2 = get_u8(g_005D0810, i + 5 + pad_len)
        v3 = get_u8(g_005D0810, i + 6 + pad_len)
        t = high8(ary[ary_idx]) ^ v1
        dst_idx = high4(t)
        op_idx = low4(t)
        op = low8(ary[ary_idx]) ^ v2
        ary = ary[1:]
        ary.append(make16(v1, v2))
        i += 7 + pad_len
        if isbitset(op, 6):
            state[dst_idx] &= v0
        elif isbitset(op, 5):
            # DO NOT USE state[dst_idx] >>= v3
            state[dst_idx] = LShR(state[dst_idx], v3)
        elif isbitset(op, 4):
            state[dst_idx] <<= v3
        elif isbitset(op, 3):
            state[dst_idx] ^= state[op_idx]
        elif isbitset(op, 2):
            state[dst_idx] = state[op_idx]
        elif isbitset(op, 1):
            state[dst_idx] <<= 1
        if i >= 0x111F71:
            break
    solver.add(simplify(state[14] == dst))
    if solver.check() == unsat:
        raise RuntimeError('unsat')
    m = solver.model()
    return m[src].as_long()


def x_404860_rev(dst):
    src = ''
    base = [
        0xE9, 0x88, 0xBD, 0x84, 0x9D, 0x64, 0xC4, 0xB9,
        0x8A, 0xDE, 0x5A, 0x65, 0x73, 0xE5, 0xA1, 0x61,
    ]
    var = [ord(_) for _ in dst]
    modulus = 0xFF8F
    for i in range(16):
        co = [0] * 16
        for k in range(16):
            co[k] = gp.powmod(base[i], 15 - k, modulus)
        v = 0
        for k in range(16):
            v += co[k] * var[k]
        v %= modulus
        src += struct.pack('H', v)
    return src


def test():
    x_402ee0_init()
    user = '141E8F96636898EF'
    user = 'KCTF'
    md = md5(user)
    md1 = md5(md[0:8])
    md2 = md5(md[8:16])
    s = aes_decrypt(md1)
    s = ''.join([chr(ord(ch)) + chr((ord(ch)+0x7F) & 0xFF) for ch in s])
    s = aes_cbc_decrypt(sha256('1_L0V3_BXS_F0REVER!'), '\x00'*16, s)
    if user == '141E8F96636898EF':
        r1 = hex2bin('dd8c5dd5bff047cb86c51fc808254c0950ebd2d9c7e6b34679a6cda7f96ea0f4')
    elif user == 'KCTF':
        r1 = hex2bin('83bde72e2806ce3c8f424e242559a66314bb37008d62a69bfd1a960f8e4102c8')
    else:
        r1 = ''
        for i in range(0, len(s), 4):
            v = struct.unpack('I', s[i:i + 4])[0]
            v = x_402ee0_rev(v)
            r1 += struct.pack('I', v)
    r = ''
    r += bin2hex(r1)
    s = x_404d90_rev(md2)
    s = x_404860_rev(s)
    s = x_4041a0_rev(s)
    r2 = bin2hex(s)
    r += r2
    print r
    # 83bde72e2806ce3c8f424e242559a66314bb37008d62a69bfd1a960f8e4102c850a28948c68b49a70989a28b89a28b6614d24982996614d249a7096d34c26d10522c345241a69b6d105208a69949a70950a28949829941a69b48a21b50a28925146289a28b48c68b50a2896614d241a69b89a28b49829948c68b49829989a28b89a28b41a69b48a21b25146248c68b48c68b25146249a7092538d241a69b2c34522538d241a69b65345489a28b48a21b6534542538d248c68b6614d26d105250a2896d105249a70948c68b6d105208a69941a69b89a28b50a28941a69b2d14d0
    return


test()



[公告]名企招聘!

最后于 2020-12-6 21:32 被风间仁编辑 ,原因:
收藏
点赞1
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回