首页
社区
课程
招聘
[原创]KCTF 2024 第四题 神秘信号wp
发表于: 2024-8-21 14:53 3531

[原创]KCTF 2024 第四题 神秘信号wp

2024-8-21 14:53
3531

实际耗时:9:30-14:30

注意到是个pyinstaller打包的PE文件,于是直接上手pyinstxtractor解包

解完之后看到main.pyc,反编译之后如下


然后……事情就变得奇妙了起来

先是在PYZ-00.pyz中没有找到CrackMe这个包,以为是解包中出现了问题
返回头去调试PE文件,更新pyinstxtractor版本
甚至重新配置了pyinstaller的环境,自己打了个包bindiff,以为是在这个工具上做出了什么奇妙的操作
不出意外的,没找到任何破局点

于是,想着,反正是个python打包的进程,我直接调用你运行中的python.dll,运行我的代码算了,然后试图使用frida

所幸,午饭时间将我的这个念想打消了(其实是发现我不会用win环境下的frida)

研究了一阵子pyinstaller打包的PE中的python的执行过程之后,突然发现了盲点

PyEval_EvalCodePyMarshal_ReadObjectFromString

在这种打包方法的Python的执行过程中,先由PyMarshal_ReadObjectFromString读入PE文件中的pyc文件,并将其转化为py_code_object,然后交由PyEval_EvalCode执行

暂且不关注具体的执行过程的话,在PE文件完全可控的情况下,这个pyc文件内容也是完全可控的,我完全可以控制这个文件的内容是什么,于是,在下断点确定读取的pyc文件内容后,我开始了我的操作

构造一个size与原本的main.pyc完全相同的hack.pyc

大概实现以下功能:

就可以看到如下返回值

于是,可以确定这个加密函数是通过base64.pyc中的某个地方完成的构造,将其反编译可以看到如下内容(通过......隐藏了部分过于庞大的内容)

然后,就可以通过import base64的方式,直接使用这个pyc文件,来进行下一步的工作

首先,我尝试爆破字符转换行为

但是发现返回值完全不符合预期,但又有种莫名的base64的感觉,还是个换表的base64

而尝试输入KCTF后产生的返回值也与注入的pyc文件的输出不同

而同时,想exec(input())的测试失败之后,我将目光移到了input函数身上

这下直接学精了,在注入的pyc中做了点手脚,我每次就直接看看每一个函数调用后的结果

while true前的三次print,分别打印了大致正确的key、爆破CrackMe.main所使用的base64基准字符、占位符(保证pyc文件大小相同)

这下终于让我抓住了马脚

每次不同的输入后,input得到的内容也是不同的,但是最后的加密还是稳妥的base64,那就好办了,我直接写个映射表爆破就好了

最终得到flag:

KCTF:Hello World!KCTF

并实现keygen如下:

import CrackMe
 
while True:
    print('(账号密码由字母大小写、数字、!、空格组成)')
    print('请输入账号:')
    h = input()
    z = CrackMe.main(h)
    if len(z) < 20:
        key = 'dZpKdrsiB6cndrGY' + z
    else:
        key = z[0:4] + 'dZpK' + z[4:8] + 'drsi' + z[8:12] + 'B6cn' + z[12:16] + 'drGY' + z[16:]
    print('请输入验证码:')
    h = input()
    m = CrackMe.main(h)
    if key == m:
        print('Success')
        break
    print('Fail')
import CrackMe
 
while True:
    print('(账号密码由字母大小写、数字、!、空格组成)')
    print('请输入账号:')
    h = input()
    z = CrackMe.main(h)
    if len(z) < 20:
        key = 'dZpKdrsiB6cndrGY' + z
    else:
        key = z[0:4] + 'dZpK' + z[4:8] + 'drsi' + z[8:12] + 'B6cn' + z[12:16] + 'drGY' + z[16:]
    print('请输入验证码:')
    h = input()
    m = CrackMe.main(h)
    if key == m:
        print('Success')
        break
    print('Fail')
import CrackMe
print(CrackMe.__dict__)
import CrackMe
print(CrackMe.__dict__)
'__spec__': ModuleSpec(name='base64', loader=<pyimod02_importers.PyiFrozenImporter object at 0x000002388FAA0220>, origin='...\\main\\_internal\\base64.pyc'),
'__cached__': '...\\main\\_internal\\base64.pyc',
'__builtins__': {
                    '__name__': 'builtins',
                    '__doc__': "Built-in functions, exceptions, and other objects.\n\nNoteworthy: None is the `nil' object; Ellipsis represents `...' in slices.",
                    '__package__': '',
                    '__loader__': <class '_frozen_importlib.BuiltinImporter'>,
'__spec__': ModuleSpec(name='base64', loader=<pyimod02_importers.PyiFrozenImporter object at 0x000002388FAA0220>, origin='...\\main\\_internal\\base64.pyc'),
'__cached__': '...\\main\\_internal\\base64.pyc',
'__builtins__': {
                    '__name__': 'builtins',
                    '__doc__': "Built-in functions, exceptions, and other objects.\n\nNoteworthy: None is the `nil' object; Ellipsis represents `...' in slices.",
                    '__package__': '',
                    '__loader__': <class '_frozen_importlib.BuiltinImporter'>,
co_posonlyargcount = main.__code__.replace(
    (1, (),
     b'......', None, '', 0, 'base64(....)', b'', 85, 1, 'little', 3, compile('', '', 'exec').replace(
        1, (), b'......', ('08b', None), '', co_kwonlyargcount=19, co_lnotab=115, co_name=(), co_names=0, co_nlocals=b'', co_posonlyargcount='', co_stacksize=('format',), co_varnames=('to_bytes', 'range', 'len', 'join', 'int'),
        12=12, 0=0, 7=7,
    data=('data', 'encoded_str', 'padding', 'base64_chars', 'ww', 'i', 'chunk', 'binary_str', 'j', 'six_bits', 'a', 'b', b'......', '', 'base64(....)')
)))
co_posonlyargcount = main.__code__.replace(
    (1, (),
     b'......', None, '', 0, 'base64(....)', b'', 85, 1, 'little', 3, compile('', '', 'exec').replace(
        1, (), b'......', ('08b', None), '', co_kwonlyargcount=19, co_lnotab=115, co_name=(), co_names=0, co_nlocals=b'', co_posonlyargcount='', co_stacksize=('format',), co_varnames=('to_bytes', 'range', 'len', 'join', 'int'),
        12=12, 0=0, 7=7,
    data=('data', 'encoded_str', 'padding', 'base64_chars', 'ww', 'i', 'chunk', 'binary_str', 'j', 'six_bits', 'a', 'b', b'......', '', 'base64(....)')
)))
import base64
print(base64.main(b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789! "))
import base64
print(base64.main(b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789! "))
input("KCTF"):
QQMlP7!!
base64.main(b"KCTF")
nBQ6P7!!
input("KCTF"):

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2024-8-21 15:00 被Xierluo编辑 ,原因: 还以为这里发了别人看得到,吓死了
收藏
免费 1
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//