• Flare-On4 解题复现


    01

    是一个 html 页面, 用开发者工具看看,发现是简单的 js 加密。

    猜测加密算法可逆,试着用 PyvragFvqrYbtvafNerRnfl@syner-ba.pbz 作为输入,然后调试 ,得到 flagClientSideLoginsAreEasy@flare-on.com

    02

    程序逻辑如下

    首先 获取输入, 然后 调用 check 进行判断, 下面分析 check 函数

    通过异或操作加密我们的输入, 首先获取一个固定的初始 key , 后面每一步 key 从输入中取,获取到密文后就和 程序中已有的密文做对比。

    那么 flag 应该就是程序里面那段密文解密后的字符串, 对加密算法求反,写出解密的 idapython 脚本

    import idc
    
    encoded_data = get_bytes(0x403000, 0x27)
    
    key = 0x4
    flag = ""
    
    i = 0x26
    
    while i >= 0:
        key = ord(encoded_data[i])^key
        flag = chr(key) + flag
        i = i - 1
    
    print flag
    

    由于 key 是输入中来的,所以这里的 key 应该是解密后的数据。得出 flag

    R_y0u_H0t_3n0ugH_t0_1gn1t3@flare-on.com
    

    03

    分析

    程序首先监听 2222 端口,然后接收 4 个字节

    然后用刚接收的 4 个字节的其中一个字节作为 key , 对 0x40107C 开始的 0x79 字节的代码进行解密,然后校验解密后的数据,校验成功继续执行,如果不成功则退出。

    所以想要继续分析,首先得解出解密代码的 key , key 的大小为 1 个字节,255 中可能。爆破之即可。

    解密

    借助 unicorn

    把解密逻辑用 python 实现, 然后把 校验解密结果的代码用 unicorn 模拟运行,然后整合一下爆破出正确的解密 key

    import binascii
    import struct
    from unicorn import *
    from unicorn.x86_const import *
    
    
    def list_to_str(arr):
        res = ""
        for i in arr:
            res += chr(i)
        return res
    
    
    verify_code = list_to_str([
        0x55, 0x8B, 0xEC, 0x51, 0x8B, 0x55, 0x0C, 0xB9, 0xFF, 0x00, 0x00, 0x00, 0x89, 0x4D, 0xFC, 0x85,
        0xD2, 0x74, 0x51, 0x53, 0x8B, 0x5D, 0x08, 0x56, 0x57, 0x6A, 0x14, 0x58, 0x66, 0x8B, 0x7D, 0xFC,
        0x3B, 0xD0, 0x8B, 0xF2, 0x0F, 0x47, 0xF0, 0x2B, 0xD6, 0x0F, 0xB6, 0x03, 0x66, 0x03, 0xF8, 0x66,
        0x89, 0x7D, 0xFC, 0x03, 0x4D, 0xFC, 0x43, 0x83, 0xEE, 0x01, 0x75, 0xED, 0x0F, 0xB6, 0x45, 0xFC,
        0x66, 0xC1, 0xEF, 0x08, 0x66, 0x03, 0xC7, 0x0F, 0xB7, 0xC0, 0x89, 0x45, 0xFC, 0x0F, 0xB6, 0xC1,
        0x66, 0xC1, 0xE9, 0x08, 0x66, 0x03, 0xC1, 0x0F, 0xB7, 0xC8, 0x6A, 0x14, 0x58, 0x85, 0xD2, 0x75,
        0xBB, 0x5F, 0x5E, 0x5B, 0x0F, 0xB6, 0x55, 0xFC, 0x8B, 0xC1, 0xC1, 0xE1, 0x08, 0x25, 0x00, 0xFF,
        0x00, 0x00, 0x03, 0xC1, 0x66, 0x8B, 0x4D, 0xFC, 0x66, 0xC1, 0xE9, 0x08, 0x66, 0x03, 0xD1, 0x66,
        0x0B, 0xC2, 0x8B, 0xE5, 0x5D
    ])
    
    encoded_code = list_to_str(
        [0x33, 0xE1, 0xC4, 0x99, 0x11, 0x06, 0x81, 0x16, 0xF0, 0x32, 0x9F, 0xC4, 0x91, 0x17, 0x06, 0x81,
         0x14, 0xF0, 0x06, 0x81, 0x15, 0xF1, 0xC4, 0x91, 0x1A, 0x06, 0x81, 0x1B, 0xE2, 0x06, 0x81, 0x18,
         0xF2, 0x06, 0x81, 0x19, 0xF1, 0x06, 0x81, 0x1E, 0xF0, 0xC4, 0x99, 0x1F, 0xC4, 0x91, 0x1C, 0x06,
         0x81, 0x1D, 0xE6, 0x06, 0x81, 0x62, 0xEF, 0x06, 0x81, 0x63, 0xF2, 0x06, 0x81, 0x60, 0xE3, 0xC4,
         0x99, 0x61, 0x06, 0x81, 0x66, 0xBC, 0x06, 0x81, 0x67, 0xE6, 0x06, 0x81, 0x64, 0xE8, 0x06, 0x81,
         0x65, 0x9D, 0x06, 0x81, 0x6A, 0xF2, 0xC4, 0x99, 0x6B, 0x06, 0x81, 0x68, 0xA9, 0x06, 0x81, 0x69,
         0xEF, 0x06, 0x81, 0x6E, 0xEE, 0x06, 0x81, 0x6F, 0xAE, 0x06, 0x81, 0x6C, 0xE3, 0x06, 0x81, 0x6D,
         0xEF, 0x06, 0x81, 0x72, 0xE9, 0x06, 0x81, 0x73, 0x7C])
    
    
    def decode_bytes(i):
        decoded_bytes = ""
        for byte in encoded_code:
            decoded_bytes += chr(((ord(byte) ^ i) + 0x22) & 0xFF)
        return decoded_bytes
    
    
    def emulate_checksum(decoded_bytes):
        # establish memory addresses for checksum code, stack, and decoded bytes
        address = 0
        stack_addr = 0x10000
        dec_bytes_addr = 0x20000
    
        # write checksum code and decoded bytes into memory
        mu = Uc(UC_ARCH_X86, UC_MODE_32)
        mu.mem_map(address, 2 * 1024 * 1024)
        mu.mem_write(address, verify_code)
        mu.mem_write(dec_bytes_addr, decoded_bytes)
        # place the address of decoded bytes and size on the stack
        mu.reg_write(UC_X86_REG_ESP, stack_addr)
        mu.mem_write(stack_addr + 4, struct.pack('<I', dec_bytes_addr))   # arg1 , address
        mu.mem_write(stack_addr + 8, struct.pack('<I', 0x79))  # arg2 , len
    
        # emulate and read result in AX
        mu.emu_start(address, address + len(verify_code))
        checksum = mu.reg_read(UC_X86_REG_AX)
        return checksum
    
    
    for i in range(256):
        checksum = emulate_checksum(decode_bytes(i))
        if checksum & 0xffff == 0xFB5E:
            print(hex(i))
            break`
    
    

    其中 verify_code 不需要 ret 指令,因为我们只需要函数的返回值。

    最后得到的 key0xa2, 然后在调试的时候,设置正常的 key,解密代码后发现是一段复制语句,调试 得到 flag`

    flag

    et_tu_brute_force@flare-on.com
    

    借助 frida

    # -*- coding:utf-8 -*-
    from __future__ import print_function
    import frida
    from time import sleep
    retval = 0
    is_ret = 0
    
    
    def list_to_str(arr):
        res = ""
        for i in arr:
            res += chr(i)
        return res
    
    def str_to_list(string):
        res = []
        for i in string:
            res.append(ord(i))
        return res
    
    
    encoded_code_array = [0x33, 0xE1, 0xC4, 0x99, 0x11, 0x06, 0x81, 0x16, 0xF0, 0x32, 0x9F, 0xC4, 0x91, 0x17, 0x06, 0x81,
                          0x14, 0xF0, 0x06, 0x81, 0x15, 0xF1, 0xC4, 0x91, 0x1A, 0x06, 0x81, 0x1B, 0xE2, 0x06, 0x81, 0x18,
                          0xF2, 0x06, 0x81, 0x19, 0xF1, 0x06, 0x81, 0x1E, 0xF0, 0xC4, 0x99, 0x1F, 0xC4, 0x91, 0x1C, 0x06,
                          0x81, 0x1D, 0xE6, 0x06, 0x81, 0x62, 0xEF, 0x06, 0x81, 0x63, 0xF2, 0x06, 0x81, 0x60, 0xE3, 0xC4,
                          0x99, 0x61, 0x06, 0x81, 0x66, 0xBC, 0x06, 0x81, 0x67, 0xE6, 0x06, 0x81, 0x64, 0xE8, 0x06, 0x81,
                          0x65, 0x9D, 0x06, 0x81, 0x6A, 0xF2, 0xC4, 0x99, 0x6B, 0x06, 0x81, 0x68, 0xA9, 0x06, 0x81, 0x69,
                          0xEF, 0x06, 0x81, 0x6E, 0xEE, 0x06, 0x81, 0x6F, 0xAE, 0x06, 0x81, 0x6C, 0xE3, 0x06, 0x81, 0x6D,
                          0xEF, 0x06, 0x81, 0x72, 0xE9, 0x06, 0x81, 0x73, 0x7C]
    
    encoded_code = list_to_str(encoded_code_array)
    
    def on_message(message, data):
        global retval,is_ret
        retval = message['payload']
        is_ret = 1
    
    
    def decode_bytes(i):
        decoded_bytes = ""
        for byte in encoded_code:
            decoded_bytes += chr(((ord(byte) ^ i) + 0x22) & 0xFF)
        return decoded_bytes
    
    
    def main():
        global retval, is_ret
        session = frida.attach("greek_to_me.exe")
        for i in range(256):
            script = session.create_script("""
                var verify_code = ptr('0x4011E6');
                var f = new NativeFunction(verify_code, 'int', ['pointer', 'int']);
                var save_address = ptr('0x40107C');
                Memory.writeByteArray(save_address, {})
                send(f(save_address, 121));
            """.format(str_to_list(decode_bytes(i))))
            script.on('message', on_message)
            script.load()
    
            while is_ret != 1:  # 等待远程函数执行完
                sleep(0.2)
            is_ret = 0
    
            if retval & 0xffff == 0xFB5E:
                print(hex(i))
                break
    
        session.detach()
    
    if __name__ == '__main__':
        main()
    
    

    每次解密code后,直接用 frida 调用进程里面的校验函数,通过这样可以爆破出 key

    最后附一个导出光标所在函数的二进制代码的 idapython 脚本

    
    import idaapi
    
    def list_to_str(arr):
        res = ""
        for i in arr:
            res += chr(i)
        return res
    
    def str_to_list(string):
        res = []
        for i in string:
            res.append(ord(i))
        return res
    
    compiled_functions = {}
    def ida_run_python_function(func_name):
        if func_name not in compiled_functions:
            ida_func_name = "py_%s" % func_name
            idaapi.CompileLine('static %s() { RunPythonStatement("%s()"); }' 
                % (ida_func_name, func_name))
            compiled_functions[func_name] = ida_func_name
        return ida_func_name
    
    
    
    def GetFunctionCode():
        func_start = get_func_attr(here(), FUNCATTR_START)
        func_end = get_func_attr(here(), FUNCATTR_END)
        func_name = GetFunctionName(func_start)
        data = get_bytes(func_start, func_end - func_start)
        with open(func_name, "wb") as fp:
            fp.write(data)
        
        with open(func_name + ".list", "w") as fp:
            fp.write(str(str_to_list(data)))
        
        
        Message("Write code of %s done!!!
    " %(func_name))
    
    
    AddHotkey("Ctrl+Shift+A", ida_run_python_function("GetFunctionCode"));
    

    参考

    http://blog.nsfocus.net/flare-onchallenge4th/

  • 相关阅读:
    15、TSA数据上传(https://www.ncbi.nlm.nih.gov/genbank/tsaguide/#SP)
    14、SRA数据上传
    14、批量处理文件
    .net mvc 利用分部视图局部刷新.
    观察者模式(Observer)
    内存和性能
    DOM中的事件对象(event)
    HTML事件处理程序
    惰性载入函数
    Comet之SSE(Server
  • 原文地址:https://www.cnblogs.com/hac425/p/9752840.html
Copyright © 2020-2023  润新知