初めに
どうも、クソ雑魚のなんちゃてエンジニアです。
本記事は FE-CTF 2023 の2題のWrtiteupとなります。
[OMGACM] What Giefs?
Now look, we're going to be dealing with some real serious stuff today. If you haven't heard of it it's called math! And without it, none of us would even exist.
接続してみろってことね。
接続してみる。
あーこれeval
するやつだ。というわけで解いてみる。
*
をx
と表現されていたり、べき乗が^
と表現されていたり、平方根がsqrt
と表現されていたりしたのでreplace
もしておく。いったんのsolverは以下だ。
from pwn import *
import math
context.log_level = "debug"
p = remote("what-giefs.hack.fe-ctf.dk", 1337)
for _ in range(100):
p.recvuntil(b'What giefs ')
cal = p.recvuntil(b'?')[:-1]
p.recvuntil(b'\n')
cal = cal.replace(b' x ', b'*').replace(b'^', b'**').replace(b'sqrt', b'math.sqrt')
p.sendline(str(int(eval(cal))))
print(p.recvrepeat().decode())
これで実行すればわかるが、0x
で16進数表現や0b
での2進数表現、0o
の8進数表現も途中から交じってくる。それに対して10進数表現で返すと、以下のようにAssertionError
を返される。
進数表現を合わせて返さなければいけないようだ。
修正したコードは以下だ。
from pwn import *
import math
context.log_level = "debug"
p = remote("what-giefs.hack.fe-ctf.dk", 1337)
for _ in range(100):
p.recvuntil(b'What giefs ')
cal = p.recvuntil(b'?')[:-1]
p.recvuntil(b'\n')
cal = cal.replace(b' x ', b'*').replace(b'^', b'**').replace(b'sqrt', b'math.sqrt')
if b'0x' in cal:
p.sendline(str(hex(int(eval(cal)))))
elif b'0b' in cal:
p.sendline(str(bin(int(eval(cal)))))
elif b'0o' in cal:
p.sendline(str(oct(int(eval(cal)))))
else:
p.sendline(str(int(eval(cal))))
print(p.recvrepeat().decode())
これでもエラーを返される。ローマ数字をeval
しないといけない。
何もわからんときはGPTさんにぶん投げます(以下の資料を参考にどうぞ)。
from pwn import *
import math, re
context.log_level = "debug"
p = remote("what-giefs.hack.fe-ctf.dk", 1337)
def roman_to_arabic(roman):
roman_dict = {'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100, 'D': 500, 'M': 1000}
arabic = 0
prev_value = 0
for numeral in reversed(roman):
value = roman_dict[numeral]
if value < prev_value:
arabic -= value
else:
arabic += value
prev_value = value
return arabic
def arabic_to_roman(arabic):
roman_dict = {1: 'I', 4: 'IV', 5: 'V', 9: 'IX', 10: 'X', 40: 'XL', 50: 'L', 90: 'XC', 100: 'C', 400: 'CD', 500: 'D', 900: 'CM', 1000: 'M'}
if arabic < 0:
arabic = -arabic
sign = '-'
else:
sign = ''
roman = ''
for value in sorted(roman_dict.keys(), reverse=True):
while arabic >= value:
roman += roman_dict[value]
arabic -= value
return sign + '0r' + roman
for _ in range(100):
p.recvuntil(b'What giefs ')
cal = p.recvuntil(b'?')[:-1]
p.recvuntil(b'\n')
cal = cal.replace(b' x ', b'*').replace(b'^', b'**').replace(b'sqrt', b'math.sqrt')
if b'0x' in cal:
p.sendline(str(hex(int(eval(cal)))))
elif b'0b' in cal:
p.sendline(str(bin(int(eval(cal)))))
elif b'0o' in cal:
p.sendline(str(oct(int(eval(cal)))))
elif b'0r' in cal:
cal_string = cal.decode()
pattern = r'0r[IVXLCDM]+'
matches = re.findall(pattern, cal_string)
for match in matches:
arabic_value = roman_to_arabic(match[2:])
cal_string = cal_string.replace(match, str(arabic_value), 1)
p.sendline(str(arabic_to_roman(int(eval(cal_string)))))
else:
p.sendline(str(int(eval(cal))))
print(p.recvrepeat().decode())
これで行けるかと思うと何やら変な文字が入ってきてます。
nulla
って??まぁ0で補完します。
from pwn import *
import math, re
context.log_level = "debug"
p = remote("what-giefs.hack.fe-ctf.dk", 1337)
def roman_to_arabic(roman):
roman_dict = {'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100, 'D': 500, 'M': 1000}
arabic = 0
prev_value = 0
for numeral in reversed(roman):
value = roman_dict[numeral]
if value < prev_value:
arabic -= value
else:
arabic += value
prev_value = value
return arabic
def arabic_to_roman(arabic):
roman_dict = {1: 'I', 4: 'IV', 5: 'V', 9: 'IX', 10: 'X', 40: 'XL', 50: 'L', 90: 'XC', 100: 'C', 400: 'CD', 500: 'D', 900: 'CM', 1000: 'M'}
if arabic < 0:
arabic = -arabic
sign = '-'
else:
sign = ''
roman = ''
for value in sorted(roman_dict.keys(), reverse=True):
while arabic >= value:
roman += roman_dict[value]
arabic -= value
return sign + '0r' + roman
for _ in range(100):
p.recvuntil(b'What giefs ')
cal = p.recvuntil(b'?')[:-1]
p.recvuntil(b'\n')
cal = cal.replace(b' x ', b'*').replace(b'^', b'**').replace(b'sqrt', b'math.sqrt').replace(b'nulla', b'0')
if b'0x' in cal:
p.sendline(str(hex(int(eval(cal)))))
elif b'0b' in cal:
p.sendline(str(bin(int(eval(cal)))))
elif b'0o' in cal:
p.sendline(str(oct(int(eval(cal)))))
elif b'0r' in cal:
cal_string = cal.decode()
pattern = r'0r[IVXLCDM]+'
matches = re.findall(pattern, cal_string)
for match in matches:
arabic_value = roman_to_arabic(match[2:])
cal_string = cal_string.replace(match, str(arabic_value), 1)
p.sendline(str(arabic_to_roman(int(eval(cal_string)))))
else:
p.sendline(str(int(eval(cal))))
print(p.recvrepeat().decode())
これで行けるかと思ったがまたエラーをはいてくる。
16進数とか色んな進数表現ととローマ数字が混在してきた。
...if分岐の順番を変えます。ローマ数字以外はそのままeval出来たはずなので。
from pwn import *
import math, re
context.log_level = "debug"
p = remote("what-giefs.hack.fe-ctf.dk", 1337)
def roman_to_arabic(roman):
roman_dict = {'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100, 'D': 500, 'M': 1000}
arabic = 0
prev_value = 0
for numeral in reversed(roman):
value = roman_dict[numeral]
if value < prev_value:
arabic -= value
else:
arabic += value
prev_value = value
return arabic
def arabic_to_roman(arabic):
roman_dict = {1: 'I', 4: 'IV', 5: 'V', 9: 'IX', 10: 'X', 40: 'XL', 50: 'L', 90: 'XC', 100: 'C', 400: 'CD', 500: 'D', 900: 'CM', 1000: 'M'}
if arabic < 0:
arabic = -arabic
sign = '-'
else:
sign = ''
roman = ''
for value in sorted(roman_dict.keys(), reverse=True):
while arabic >= value:
roman += roman_dict[value]
arabic -= value
return sign + '0r' + roman
for _ in range(100):
p.recvuntil(b'What giefs ')
cal = p.recvuntil(b'?')[:-1]
p.recvuntil(b'\n')
cal = cal.replace(b' x ', b'*').replace(b'^', b'**').replace(b'sqrt', b'math.sqrt').replace(b'nulla', b'0')
if b'0r' in cal:
cal_string = cal.decode()
pattern = r'0r[IVXLCDM]+'
matches = re.findall(pattern, cal_string)
for match in matches:
arabic_value = roman_to_arabic(match[2:])
cal_string = cal_string.replace(match, str(arabic_value), 1)
p.sendline(str(arabic_to_roman(int(eval(cal_string)))))
elif b'0x' in cal:
p.sendline(str(hex(int(eval(cal)))))
elif b'0b' in cal:
p.sendline(str(bin(int(eval(cal)))))
elif b'0o' in cal:
p.sendline(str(oct(int(eval(cal)))))
else:
p.sendline(str(int(eval(cal))))
print(p.recvrepeat().decode())
[Rev] Abacus - Minimus
The expressiveness of C combined with the ease of reasoning of Haskell. Wait, am I doing this right?
Ghidra
バイナリが渡されるので実行してみる。
フラグ判定する実行ファイルのようです。
Ghidraに食わせます。
num
の中身はこんな感じです。
evalの中身はこんな感じ
assertの中身はこんな感じでpoofに飛ぶと間違いの表示になるみたい。
正味ここら辺のデコンパイルされた関数を深ぼっても意味わからなかったのでgdbで動的デバックを実行します。
mainで参照しているこのdata
が気になるのでこの数字の羅列は覚えておこうと思います。
ここら辺はGUESSですね。
gdb
フラグ判定にflag{test}
とか適当に代入してステップ実行します。
最初に気になるcmpがありました。
ん?入力文字とraxを比較している?普通に合うわけないが??終端文字判定か??
2文字目もcmpしてる。
間違うところの文字列も比較している?
ここから全ての文字を比較した後(右手のEnter何千回と押した...)に何やら先ほどのデータの先頭にあった0xC5
と0xa3
が出現しているところを発見する。
ん?0x66
のf
の文字も見えるぞ?もう少しステップ見てみるか。
次のdataの0xcf
がRAXに入って前のdataがRBXに入っている。これをXORすると?
あ、RAXに出てきた。完全に理解した。前後の文字でXORしたものがフラグだ!
data
の文字を持ってきます。
gdb-peda$ telescope 0x555555558050
0000| 0x555555558050 --> 0xb1d3b2c9aecfa3c5
0008| 0x555555558058 --> 0xe889eac7b4c1a2c3
0016| 0x555555558060 --> 0xf98aff9cfd8fed8c
0024| 0x555555558068 --> 0x8484
0032| 0x555555558070 --> 0xffffffff00000003
0040| 0x555555558078 --> 0xffffffffffffffff
0048| 0x555555558080 --> 0x0
0056| 0x555555558088 --> 0x0
gdb-peda$
このデータからSolverを書きます。(Errorは気にしないでください。)
from functools import reduce
from pwn import *
param=reduce(lambda x,y: x+p64(y), [0xb1d3b2c9aecfa3c5,0xe889eac7b4c1a2c3,0xf98aff9cfd8fed8c,0x8484], b'')
for i in range(len(param)):
print(chr(param[i+1]^param[i]), end='')
まとめ
Revが根性ゲーでした。何回Enter押したか覚えてません...
楽しかったです。ありがとうございました。