SCTF2024-ez_cython出题分享
bobo-giraffe 发表于 四川 技术文章 595浏览 · 2024-11-12 11:56

SCTF2024-ez_cython出题分享

出题思路

因为cython本身的语言特性,类似于加了一个强混淆,就将关键的魔改xxtea加密算法和密钥和密文打包在生成的pyd文件中

这里我将key的获取留了一个后门,封包成一个类的get,可以通过import库的导入进行分析

ez_cython的wp

1.解包

简单的解包得到pyc文件

反编译pyc文件(线上方便点)

可以看到这里是导入的一个库,关键的check函数是sub14514函数(在cython打包的pyd文件库中,无符号表),所以逆向分析pyd文件即可

2.传参

避免验证的时候一次传入一个参数的麻烦

import cy

def str_hex(input_str):
    return [ord(char) for char in input_str]

def main():
    while True:
        input_str = input("input:")
        if input_str == '':
            break

        guess_hex = str_hex(input_str)

        if cy.sub14514(guess_hex):
            print(f"ok")
            break
        else:
            print(f"no")

if __name__ == '__main__':
    main()

可以列出所有的导入库

import pefile

def import_check(pyd_file):
    pe = pefile.PE(pyd_file)
    for entry in pe.DIRECTORY_ENTRY_IMPORT:
        print(f"Module: {entry.dll.decode('utf-8')}")
        for imp in entry.imports:
            print(f"  Function: {imp.name.decode('utf-8') if imp.name else None}")

import_check('cy.cp38-win_amd64.pyd')
Module: python38.dll
  Function: PyErr_SetString
  Function: PyObject_GetIter
  Function: PyNumber_Add
  Function: _Py_CheckRecursiveCall
  Function: PyDict_Next
  Function: PyErr_Format
  Function: PyDict_Type
  Function: PyObject_RichCompare
  Function: PyTuple_Type
  Function: _Py_FalseStruct
  Function: PyFloat_Type
  Function: PyModule_NewObject
  Function: PyMethod_Type
  Function: PyLong_Type
  Function: PyType_IsSubtype
  Function: PyNumber_Subtract
  Function: PyExc_OverflowError
  Function: PyCode_NewWithPosOnlyArgs
  Function: _Py_Dealloc
  Function: PyTuple_GetItem
  Function: PyImport_GetModuleDict
  Function: PyModule_GetDict
  Function: PyObject_Free
  Function: PyErr_ExceptionMatches
  Function: PyNumber_And
  Function: PyObject_GC_Del
  Function: PyObject_ClearWeakRefs
  Function: PyObject_Not
  Function: PyUnicode_AsUTF8
  Function: PyUnicode_FromFormat
  Function: PyList_New
  Function: PyNumber_Rshift
  Function: PyImport_AddModule
  Function: PyType_Ready
  Function: PyObject_GetAttrString
  Function: PyErr_Clear
  Function: PyTuple_GetSlice
  Function: PyUnicode_Decode
  Function: _PyObject_GenericGetAttrWithDict
  Function: PyDict_SetItem
  Function: PyDict_New
  Function: PyUnicode_Type
  Function: _PyDict_GetItem_KnownHash
  Function: PyNumber_Index
  Function: PyMem_Free
  Function: PyExc_StopIteration
  Function: PyList_Type
  Function: PyErr_NoMemory
  Function: PyDict_GetItemString
  Function: PyObject_GetItem
  Function: PyModuleDef_Init
  Function: PyObject_GC_Track
  Function: PyBytes_FromStringAndSize
  Function: PyNumber_Long
  Function: PyUnicode_Compare
  Function: PyExc_TypeError
  Function: PyNumber_Lshift
  Function: PyMem_Realloc
  Function: PyObject_IsTrue
  Function: PyExc_NameError
  Function: PyTuple_Pack
  Function: _PyUnicode_Ready
  Function: PyMem_Malloc
  Function: PyExc_IndexError
  Function: PyExc_ImportError
  Function: _Py_TrueStruct
  Function: PyExc_SystemError
  Function: PyObject_SetItem
  Function: _PyObject_GC_New
  Function: PyNumber_TrueDivide
  Function: PyUnicode_FromString
  Function: PyObject_Size
  Function: PyObject_Call
  Function: PyType_Type
  Function: PyUnicode_FromStringAndSize
  Function: _PyObject_GetDictPtr
  Function: PyExc_AttributeError
  Function: PyFloat_FromDouble
  Function: PyLong_FromLongLong
  Function: PyDict_Size
  Function: PyDict_SetItemString
  Function: PyLong_FromString
  Function: PyTuple_New
  Function: _Py_NoneStruct
  Function: PyObject_GetAttr
  Function: Py_GetVersion
  Function: PyInterpreterState_GetID
  Function: PyNumber_Xor
  Function: PyObject_Hash
  Function: PyObject_GC_UnTrack
  Function: PyLong_FromLong
  Function: PyObject_SetAttrString
  Function: PyMethod_New
  Function: PyExc_RuntimeError
  Function: _PyThreadState_UncheckedGet
  Function: PyTraceBack_Here
  Function: PyObject_GenericGetAttr
  Function: PyLong_FromSsize_t
  Function: PyErr_Occurred
  Function: PyImport_ImportModuleLevelObject
  Function: _PyDict_SetItem_KnownHash
  Function: PyLong_AsSsize_t
  Function: PyFrame_New
  Function: PyExc_RuntimeWarning
  Function: PyErr_WarnEx
  Function: PyErr_GivenExceptionMatches
  Function: PyCode_NewEmpty
  Function: _Py_CheckRecursionLimit
  Function: PyThreadState_Get
  Function: PyOS_snprintf
  Function: PyCFunction_Type
  Function: PyUnicode_InternFromString
  Function: PyObject_SetAttr
  Function: PyList_Append
  Function: PyBaseObject_Type
Module: KERNEL32.dll
  Function: GetSystemTimeAsFileTime
  Function: RtlCaptureContext
  Function: RtlLookupFunctionEntry
  Function: RtlVirtualUnwind
  Function: UnhandledExceptionFilter
  Function: SetUnhandledExceptionFilter
  Function: GetCurrentProcess
  Function: TerminateProcess
  Function: IsProcessorFeaturePresent
  Function: QueryPerformanceCounter
  Function: GetCurrentProcessId
  Function: GetCurrentThreadId
  Function: InitializeSListHead
  Function: DisableThreadLibraryCalls
  Function: IsDebuggerPresent
Module: VCRUNTIME140.dll
  Function: strrchr
  Function: __C_specific_handler
  Function: memset
  Function: memcpy
  Function: __std_type_info_destroy_list
  Function: memcmp
Module: api-ms-win-crt-runtime-l1-1-0.dll
  Function: _initterm_e
  Function: _seh_filter_dll
  Function: _configure_narrow_argv
  Function: _initialize_narrow_environment
  Function: _initialize_onexit_table
  Function: _initterm
  Function: _execute_onexit_table
  Function: _cexit

根据这里的xor,shift,可以大致猜测是一个tea家族

3.DATA

在ida中逆向分析pyd文件,可以发现对于key的获取

也可以找到key的生成,DATA异或0xAA

key:

83, 121, 67, 49, 48, 86, 101, 82, 102, 48, 82, 86, 101, 114

常量定义

可以通过注⼊cy类,获取运算过程(学到了)

import cy

class Symbol:
    def __init__(self, name):
        self.name = name

    def __repr__(self):
        return self.name

    def __rshift__(self, other):
        if isinstance(other, Symbol):
            expression = Symbol(f"({self.name} >> {other.name})")
        else:
            expression = Symbol(f"({self.name} >> {other})")
        return expression

    def __lshift__(self, other):
        if isinstance(other, Symbol):
            expression = Symbol(f"({self.name} << {other.name})")
        else:
            expression = Symbol(f"({self.name} << {other})")
        return expression

    def __rxor__(self, other):
        if isinstance(other, Symbol):
            expression = Symbol(f"({self.name} ^ {other.name})")
        else:
            expression = Symbol(f"({self.name} ^ {other})")
        return expression

    def __xor__(self, other):
        if isinstance(other, Symbol):
            expression = Symbol(f"({self.name} ^ {other.name})")
        else:
            expression = Symbol(f"({self.name} ^ {other})")
        return expression

    def __add__(self, other):
        if isinstance(other, Symbol):
            expression = Symbol(f"({self.name} + {other.name})")
        else:
            expression = Symbol(f"({self.name} + {other})")
        return expression

    def __and__(self, other):
        if isinstance(other, Symbol):
            expression = Symbol(f"({self.name} & {other.name})")
        else:
            expression = Symbol(f"({self.name} & {other})")
        return expression

class AList:
    def __init__(self, nums):
        self.nums = [Symbol(str(num)) for num in nums]

    def __getitem__(self, key):
        return self.nums[key]

    def copy(self):
        return AList(self.nums)

    def __len__(self):
        return len(self.nums)

    def __setitem__(self, key, value):
        print(f"new_{self.nums[key]} = {value}")
        self.nums[key] = Symbol(f"new_{self.nums[key].name}")

    def __eq__(self, other):
        print(f"{self.nums} == {other}")
        return self.nums == other

inp = AList([f"a[{i}]" for i in range(32)])
res = cy.sub14514(inp)

if __name__ == '__main__':
    print(res)

enc:

4108944556, 3404732701, 1466956825, 788072761, 1482427973, 782926647, 1635740553, 4115935911, 2820454423, 3206473923, 1700989382, 2460803532, 2399057278, 968884411, 1298467094, 1786305447, 3953508515, 2466099443, 4105559714, 779131097, 288224004, 3322844775, 4122289132, 2089726849, 656452727, 3096682206, 2217255962, 680183044, 3394288893, 697481839, 1109578150, 2272036063

4.加密分析

通过对程序的逆向分析,我们找到关键函数以及生成的对象的定位

其中对xor和shr等api的交叉引用,可以分析出sub50804是xxtea的一个MX函数

def shift(a, b, c, d, e, f):
    return ((((a >> 3) ^ (b << 3)) + ((b >> 4) ^ (a << 2))) ^ ((b ^ c) + (d[(e & 2) ^ f] ^ a)))

sub50520是XXTEA加密逻辑核心函数,经过大致的分析可以推断是一个xxtea的加密

分析得到实现流程:

def encrypt(data, key):
    delta = 0x9E3779B9
    n = len(data)
    rounds = int(4 + 60 / n)
    sum = 0
    last = data[n - 1]
    for _ in range(rounds):
        sum = (sum + delta) & 0xFFFFFFFF
        e = (sum >> 3) & 3
        for p in range(n - 1):
            next = data[p + 1]
            data[p] = (data[p] + shift(last, next, sum, key, p, e)) & 0xFFFFFFFF
            last = data[p]
        next = data[0]
        data[n - 1] = (data[n - 1] + shift(last, next, sum, key, n - 1, e)) & 0xFFFFFFFF
        last = data[n - 1]
    return data

sub14514实际上是加密的check部分,利用前面得到的enc,key直接解密即可

5.EXP

xxtea的密钥只取前四位即可

def shift(a, b, c, d, e, f):
    return ((((a >> 3) ^ (b << 3)) + ((b >> 4) ^ (a << 2))) ^ ((b ^ c) + (d[(e & 2) ^ f] ^ a))) & 0xFFFFFFFF

def dec(data, key):
    delta = 0x9E3779B9
    n = len(data)
    rounds = int(4 + 60 / n)
    sum = (rounds * delta) & 0xFFFFFFFF
    first = data[0]
    for _ in range(rounds):
        e = (sum >> 3) & 3
        for p in range(n - 1, 0, -1):
            prev = data[p - 1]
            data[p] = (data[p] - shift(prev, first, sum, key, p, e)) & 0xFFFFFFFF
            first = data[p]
        prev = data[n - 1]
        data[0] = (data[0] - shift(prev, first, sum, key, 0, e)) & 0xFFFFFFFF
        first = data[0]
        sum = (sum - delta) & 0xFFFFFFFF
    return data
enc = [
    4108944556, 3404732701, 1466956825, 788072761, 1482427973, 
    782926647, 1635740553, 4115935911, 2820454423, 3206473923, 
    1700989382, 2460803532, 2399057278, 968884411, 1298467094, 
    1786305447, 3953508515, 2466099443, 4105559714, 779131097, 
    288224004, 3322844775, 4122289132, 2089726849, 656452727, 
    3096682206, 2217255962, 680183044, 3394288893, 697481839, 
    1109578150, 2272036063
]
key = [83, 121, 67, 49]
dec_data = dec(enc, key)
asc = ''.join(chr(value) for value in dec_data)
print(asc)
0 条评论
某人
表情
可输入 255