XCTF Final Jail
本文最后更新于 398 天前,其中的信息可能已经有所发展或是发生改变。
  • 当事件不为这个三类值时,触发exit退出进程,那么则想过能否将exit通过内存修改为其他函数,不让进程退出;很巧,之前看过一个大跌写的文章,似乎用在这里很合适;简单总结即为通过ctypes覆盖到sys._exit的内存,替换为其余函数;但是存在两个问题,首先此题中获取不到ctypes,其次,即便成功覆盖,也无法绕过死循环while(1)(之前看的时候手动引入了ctypes而且也没看到死循环,属实是眼瞎的不行了

    唉,好nb的一个题,先写个搓出来的poc,具体原理等期末结束再细写

    Audited

    • server.py
    #!python3.12
    # run on python 3.12.4
    import sys
    import os
    import json
    
    assert sys.version.startswith("3.12.4")
    
    test_code = input("Enter the function in json: ")
    sys.stdin.close()
    
    if len(test_code) > 2540:
        exit(1)
    test_code: json = json.loads(test_code)
    del json
    
    test_code["co_consts"] = tuple(tuple(a) if isinstance(
        a, list) else a for a in test_code["co_consts"])
    test_code["co_names"] = tuple(tuple(a) if isinstance(
        a, list) else a for a in test_code["co_names"])
    test_code["co_varnames"] = tuple(tuple(a) if isinstance(
        a, list) else a for a in test_code["co_varnames"])
    
    
    def check_ele(ele, inner=False):
        if ele is None:
            pass
        elif isinstance(ele, int):
            # some random magic numbers w/o any meaning
            if ele not in range(-0xd000, 0x50000):
                exit(1)
        elif isinstance(ele, str):
            if any((ord(a) not in (list(range(ord('0'), ord('9') + 1)) + list(range(ord('a'), ord('z') + 1)) + list(range(ord('A'), ord('Z') + 1)) + [95])) for a in ele):
                exit(1)
            elif len(ele) > 242:
                exit(1)
        elif isinstance(ele, tuple):
            if inner:
                exit(1)
            for a in ele:
                check_ele(a, True)
        else:
            exit(1)
    
    
    for ele in test_code["co_consts"] + test_code["co_names"]:
        check_ele(ele)
    
    del check_ele
    
    
    def test(): pass
    
    
    test.__code__ = test.__code__.replace(co_code=bytes.fromhex(test_code["co_code"]),
                                          co_consts=test_code["co_consts"],
                                          co_names=test_code["co_names"],
                                          co_stacksize=test_code["co_stacksize"],
                                          co_nlocals=test_code["co_nlocals"],
                                          co_varnames=test_code["co_varnames"])
    
    del test_code
    
    def auditHookHandler(e):
        def handler(x, _):
            if not (x == "object.__getattr__" or x == "object.__setattr__" or x == "code.__new__"):
                e(1) #looololooo
                while(1): pass
        return handler
    
    
    sys.addaudithook(auditHookHandler(os._exit))
    del sys
    
    test()
    
    # free flag?! :)
    os.system("cat /flag")
    • 首先看逻辑,接受输入参数,转换为json,然后从test_code中取出co_*的值,通过CodeType.replace将函数的行为替换为你输入的值,需要传递下列值
    test_code = {
        "co_code": str,
        "co_consts": tuple,
        "co_names": tuple,
        "co_stacksize": int,
        "co_nlocals": int,
        "co_varnames": tuple
    }
    • 限制主要三个部分:限制长度;限制intstr的值;通过audithook限制event事件触发
    if len(test_code) > 2540:
        exit(1)
    def check_ele(ele, inner=False):
        if ele is None:
            pass
        elif isinstance(ele, int):
            # some random magic numbers w/o any meaning
            if ele not in range(-0xd000, 0x50000):
                exit(1)
        elif isinstance(ele, str):
            if any((ord(a) not in (list(range(ord('0'), ord('9') + 1)) + list(range(ord('a'), ord('z') + 1)) + list(range(ord('A'), ord('Z') + 1)) + [95])) for a in ele):
                exit(1)
            elif len(ele) > 242:
                exit(1)
        elif isinstance(ele, tuple):
            if inner:
                exit(1)
            for a in ele:
                check_ele(a, True)
        else:
            exit(1)
    def auditHookHandler(e):
        def handler(x, _):
            if not (x == "object.__getattr__" or x == "object.__setattr__" or x == "code.__new__"):
                e(1) #looololooo
                while(1): pass
        return handler
    • 此外还有个比较隐晦的限制
    test_code = input("Enter the function in json: ")
    ...
    test_code: json = json.loads(test_code)
    • 自定义函数后,尝试获取到的属性有时并不能够被转换为json,自然,此种代码不能通过loads读入
    def t():
        def test():
            pass
        
    test_code = {
        "co_code": t.__code__.co_code.hex(),
        "co_consts": t.__code__.co_consts,
        "co_names": t.__code__.co_names,
        "co_stacksize": t.__code__.co_stacksize,
        "co_nlocals": t.__code__.co_nlocals,
        "co_varnames": t.__code__.co_varnames
    }
    
    print(test_code)
    test_code = json.dumps(test_code)
    
    """
    {'co_code': '9700640184007d007900', 'co_consts': (None, <code object test at 0x000002998E21C440, file "C:\Users\JBN\Downloads\test.py", line 12>), 'co_names': (), 'co_stacksize': 1, 'co_nlocals': 1, 'co_varnames': ('test',)}
    Traceback (most recent call last):
      File "C:\Users\JBN\Downloads\test.py", line 44, in <module>
        test_code = json.dumps(test_code)
                    ^^^^^^^^^^^^^^^^^^^^^
      File "C:\JBNRZ\Applications\anaconda\envs\python3.12\Lib\json\__init__.py", line 231, in dumps
        return _default_encoder.encode(obj)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "C:\JBNRZ\Applications\anaconda\envs\python3.12\Lib\json\encoder.py", line 200, in encode
        chunks = self.iterencode(o, _one_shot=True)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "C:\JBNRZ\Applications\anaconda\envs\python3.12\Lib\json\encoder.py", line 258, in iterencode
        return _iterencode(o, 0)
               ^^^^^^^^^^^^^^^^^
      File "C:\JBNRZ\Applications\anaconda\envs\python3.12\Lib\json\encoder.py", line 180, in default
        raise TypeError(f'Object of type {o.__class__.__name__} '
    TypeError: Object of type code is not JSON serializable
    """
    • 此题的关键在于hook函数的绕过,先提一嘴一开始尝试过的路子
    def auditHookHandler(e):
        def handler(x, _):
            if not (x == "object.__getattr__" or x == "object.__setattr__" or x == "code.__new__"):
                e(1) #looololooo
                while(1): pass
        return handler
    • 当事件不为这个三类值时,触发exit退出进程,那么则想过能否将exit通过内存修改为其他函数,不让进程退出;很巧,之前看过一个大跌写的文章,似乎用在这里很合适;简单总结即为通过ctypes覆盖到sys._exit的内存,替换为其余函数;但是存在两个问题,首先此题中获取不到ctypes,其次,即便成功覆盖,也无法绕过死循环while(1)(之前看的时候手动引入了ctypes而且也没看到死循环,属实是眼瞎的不行了
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇