はまやんはまやんはまやん

hamayanhamayan's blog

Grey Cat The Flag 2025 Writeups

[Forensics] Layer Cake

Layer cake is so good. I have an mp3 file all about layer cake. Maybe you can find the flag there?

layer cake.mp3というファイルが与えられる。まず、配布されたファイルの形式を確認してみる。

$ file layer\ cake.mp3
layer cake.mp3: MPEG ADTS, layer III, v1, 192 kbps, 44.1 kHz, JntStereo

一見するとMP3ファイルのように見えますが、hexdumpで内容を確認してみると

$ hd layer\ cake.mp3 | head -n 20
00000000  ff fb 68 30 14 00 00 00  00 00 e1 91 be 5a 00 00  |..h0.........Z..|
00000010  00 00 00 00 00 00 00 00  00 00 07 00 20 00 6c 61  |............ .la|
00000020  79 65 72 73 2f 75 78 0b  00 01 04 00 00 00 00 04  |yers/ux.........|
00000030  00 00 00 00 55 54 0d 00  07 26 85 39 68 a3 86 39  |....UT...&.9h..9|
00000040  68 26 85 39 68 50 4b 03  04 14 00 00 00 00 00 e1  |h&.9hPK.........|
00000050  91 be 5a 00 00 00 00 00  00 00 00 00 00 00 00 10  |..Z.............|
00000060  00 20 00 6c 61 79 65 72  73 2f 64 6f 63 50 72 6f  |. .layers/docPro|
00000070  70 73 2f 75 78 0b 00 01  04 00 00 00 00 04 00 00  |ps/ux...........|

PKというマジックバイトやlayers/というディレクトリ名が見えることから、これはZIPファイルっぽい。ファイルをunzipしてみると色々解凍できて、Microsoft Word文書(.docx)っぽいものが出てくる。適当にgrepするとword/styles.xml内でフラグを発見した。

<w:style w:type="paragraph" w:styleId="Heading5"><!-- grey{s0_f3w_lay3r5_w00p5} --><w:name w:val="heading 5"/>

[Forensics] Connection Issues

One of our employees was browsing the web when he suddenly lost connection! Can you help him figure out why?

chall.pcapが与えられる。ネットワークが使えないというのがヒントっぽい。適当に目grepすると以下のようなのが見つかる。

#1409: 192.168.100.1 is at bc:24:11:74:12:33 (duplicate use of 192.168.100.1 detected!)
#1575: 192.168.100.1 is at bc:24:11:74:12:33 (duplicate use of 192.168.100.1 detected!)

ARP Spoofingか。これは通信ができなくなったというのも分かる。ということはARPに着目すればよさそうなので、ARPを適当に確認していくと、ARPパケットに異常な追加データが埋め込まれているのが見える。tsharkでダンプしてみる。

tshark -r chall.pcap -Y "arp and frame.len > 42" -x

なんか末尾にbase64エンコードされた文字列っぽいのが見える。適当に集める。

01: Z3JleXtk → grey{d
02: MWRfMV9q → 1d_1_j
03: dXM3X2dl → us7_ge
04: N19wMDFz → 7_p01s
05: b24zZH0= → on3d}

これを結合するとフラグになる。

[Crypto] Uwusignatures

As an uwu girl, I decided to make this digital signature scheme to share my signatures with everyone!
I'll only show you half of my signature though, because I'm shy...
Surely, no one would steal from a cutie like myself... right?

ソースコードは以下。

from Crypto.Util.number import *
import json
import hashlib

KEY_LENGTH = 2048
FLAG = "grey{fakeflagfornow}"

class Uwu:
    def __init__(self, keylen):
        self.p = getPrime(keylen)
        self.g = getRandomRange(1, self.p)
        self.x = getRandomRange(2, self.p) # x is private key
        self.y = pow(self.g, self.x, self.p) # y is public key
        self.k = getRandomRange(1, self.p)
        while GCD(self.k, self.p - 1) != 1:
            self.k = getRandomRange(1, self.p)
        print(f"{self.p :} {self.g :} {self.y :}")
        print(f"k: {self.k}")
    def hash_m(self, m):
        sha = hashlib.sha256()
        sha.update(long_to_bytes(m))
        return bytes_to_long(sha.digest())
    def sign(self, m):
        assert m > 0
        assert m < self.p
        h = self.hash_m(m)
        r = pow(self.g, self.k, self.p)
        s = ((h - self.x * r) * pow(self.k, -1, self.p - 1)) % (self.p - 1) 
        return (r, s)
    def verify(self, m, signature):
        r, s = signature
        assert r >= 1
        assert r < self.p
        h = self.hash_m(m)
        lhs = pow(self.g, h, self.p)
        rhs = (pow(self.y, r, self.p) * pow(r, s, self.p)) % self.p
        return lhs == rhs 

def main():
    print("Welcome to my super uwu secure digital signature scheme!")
    uwu = Uwu(KEY_LENGTH)
    sign_count = 0   
    while True:
        print("1. Show me some of your cutesy patootie signatures!")
        print("2. Get some of my uwu signatures (max 2)")
        choice = int(input("> "))
        if choice == 1:
            data = json.loads(input("Send me a message and a signature: "))
            m, r, s = data["m"], data["r"], data["s"]
            if m == bytes_to_long(b"gib flag pls uwu"):
                if uwu.verify(m, (r, s)):
                    print("Very cutesy, very mindful, very demure!")
                    print(FLAG)
                    exit()
                else:
                    print("Very cutesy, but not very mindful")
                    exit()
            else:
                print("Not very cutesy")
                exit()
        elif choice == 2:
            if sign_count >= 2:
                print("Y-Y-You'd steal from poor me? U_U")
                exit()
            data = json.loads(input("Send me a message: "))
            m = data["m"]
            if type(m) is not int or m == bytes_to_long(b"gib flag pls uwu"):
                print("Y-Y-You'd trick poor me? U_U")
                exit()
            r, s = uwu.sign(m)
            print(f"Here's your uwu signature! {s :}")
            sign_count += 1
        else:
            print("Not very smart of you OmO")
            exit()

if __name__ == "__main__":
    main()

ElGamal 署名が実装されている。コードを詳しく見ると、脆弱性が2つある。

  1. ノンス再利用: k がコンストラクタで一度だけ生成され、全ての署名で同じ値が使われる
  2. デバッグ情報漏洩: k の値がコンソールに出力される

ノンスが再利用されているので、そこからkを求めることができるのだが、kは与えられているのでそれを使うことにしよう。

  1. 1つの署名を取得
  2. k は既に分かっているので直接 r = g^k mod p を計算
  3. 署名方程式から x*r を逆算
  4. 目標メッセージの署名を偽造

という流れでいい。以下のようなコードでフラグが得られる。

from Crypto.Util.number import *
import hashlib
import json
from pwn import *

def hash_m(m):
    sha = hashlib.sha256()
    sha.update(long_to_bytes(m))
    return bytes_to_long(sha.digest())

r = remote("challs2.nusgreyhats.org", 33301)

r.recvline()  # Welcome message
    
pub_line = r.recvline().decode().strip()
p, g, y = map(int, pub_line.split())

k_line = r.recvline().decode().strip()
k = int(k_line.split(":")[1].strip())

r.sendlineafter(b"> ", b"2")  # 署名取得
m1 = 1337
r.recvuntil(b": ")
r.sendline(json.dumps({"m": m1}).encode())
    
sig1_line = r.recvline().decode()
s1 = int(sig1_line.split("!")[-1].strip())

h1 = hash_m(m1)
k_inv = pow(k, -1, p - 1)

r_calced = pow(g, k, p)
target_msg = bytes_to_long(b"gib flag pls uwu")
target_hash = hash_m(target_msg)
x_r = (h1 - (s1 * k) % (p - 1)) % (p - 1)
target_s = ((target_hash - x_r) * k_inv) % (p - 1)

r.sendlineafter(b"> ", b"1")  # 署名検証
forge_data = {"m": target_msg, "r": r_calced, "s": target_s}
r.sendlineafter(b": ", json.dumps(forge_data).encode())
r.interactive()