1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【FE-CTF2023】Writeup

Last updated at Posted at 2023-10-30

初めに

どうも、クソ雑魚のなんちゃてエンジニアです。
本記事は FE-CTF 2023 の2題のWrtiteupとなります。

2題だけ解けたので記載します。
スクリーンショット 2023-10-30 095803.png

[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.
image.png

接続してみろってことね。
接続してみる。
image.png
あーこれevalするやつだ。というわけで解いてみる。
*xと表現されていたり、べき乗が^と表現されていたり、平方根がsqrtと表現されていたりしたのでreplaceもしておく。いったんのsolverは以下だ。

solv.py
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を返される。
image.png
進数表現を合わせて返さなければいけないようだ。
修正したコードは以下だ。

solv.py
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しないといけない。
image.png
何もわからんときはGPTさんにぶん投げます(以下の資料を参考にどうぞ)。

image.png
そんなこんなでできたsolverは以下です。

solv.py
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())

これで行けるかと思うと何やら変な文字が入ってきてます。
image.png
nullaって??まぁ0で補完します。

solv.py
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())

これで行けるかと思ったがまたエラーをはいてくる。
image.png
16進数とか色んな進数表現ととローマ数字が混在してきた。
...if分岐の順番を変えます。ローマ数字以外はそのままeval出来たはずなので。

solv.py
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())

これでフラグゲットだぜ!
スクリーンショット 2023-10-29 012501.png

[Rev] Abacus - Minimus

The expressiveness of C combined with the ease of reasoning of Haskell. Wait, am I doing this right?
image.png

Ghidra

バイナリが渡されるので実行してみる。
スクリーンショット 2023-10-30 220911.png
フラグ判定する実行ファイルのようです。
Ghidraに食わせます。
image.png
numの中身はこんな感じです。
image.png
evalの中身はこんな感じ
image.png
assertの中身はこんな感じでpoofに飛ぶと間違いの表示になるみたい。
image.png
image.png

正味ここら辺のデコンパイルされた関数を深ぼっても意味わからなかったのでgdbで動的デバックを実行します。
mainで参照しているこのdataが気になるのでこの数字の羅列は覚えておこうと思います。
ここら辺はGUESSですね。
image.png

gdb

フラグ判定にflag{test}とか適当に代入してステップ実行します。
最初に気になるcmpがありました。
ん?入力文字とraxを比較している?普通に合うわけないが??終端文字判定か??
image.png
2文字目もcmpしてる。
image.png
間違うところの文字列も比較している?
image.png
ここから全ての文字を比較した後(右手のEnter何千回と押した...)に何やら先ほどのデータの先頭にあった0xC50xa3が出現しているところを発見する。
image.png
ん?0x66fの文字も見えるぞ?もう少しステップ見てみるか。
image.png
次のdataの0xcfがRAXに入って前のdataがRBXに入っている。これをXORすると?
image.png
あ、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は気にしないでください。)

solv.py
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='')

上記回せばフラグをゲットできます。
image.png

まとめ

Revが根性ゲーでした。何回Enter押したか覚えてません...
楽しかったです。ありがとうございました。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?