以下の内容はhttps://yocchin.hatenablog.com/entry/2024/07/22/233604より取得しました。


ImaginaryCTF 2024 Writeup

この大会は2024/7/20 4:00(JST)~2024/7/22 4:00(JST)に開催されました。
今回もチームで参戦。結果は1782点で1457チーム中141位でした。
自分で解けた問題をWriteupとして書いておきます。

sanity-check (MISC)

問題にフラグが書いてあった。

ictf{this_isnt_real}

discord (MISC)

Discordに入り、#imaginaryctf-2024チャネルのトピックやメッセージにフラグがあった。

ictf{fake_flag_for_testing}

unoriginal (REVERSING)

Ghidraでデコンパイルする。

undefined8 main(void)

{
  int iVar1;
  long in_FS_OFFSET;
  int local_4c;
  byte local_48 [56];
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  printf("Enter your flag here: ");
  gets((char *)local_48);
  for (local_4c = 0; local_4c < 0x30; local_4c = local_4c + 1) {
    local_48[local_4c] = local_48[local_4c] ^ 5;
  }
  iVar1 = strcmp((char *)local_48,"lfqc~opvqZdkjqm`wZcidbZfm`fn`wZd6130a0`0``761gdx");
  if (iVar1 == 0) {
    puts("Correct!");
  }
  else {
    puts("Incorrect.");
  }
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return 0;
}

5とXORして、"lfqc~opvqZdkjqm`wZcidbZfm`fn`wZd6130a0`0``761gdx"になればよいので、XORで元に戻す。

>>> s = "lfqc~opvqZdkjqm`wZcidbZfm`fn`wZd6130a0`0``761gdx"
>>> ''.join([chr(ord(c) ^ 5) for c in s])
'ictf{just_another_flag_checker_a3465d5e5ee234ba}'
ictf{just_another_flag_checker_a3465d5e5ee234ba}

readme (WEB)

Dockerfileのある行にフラグが設定されていた。

ENV FLAG="ictf{path_normalization_to_the_rescue}"
ictf{path_normalization_to_the_rescue}

bom (FORENSICS)

$ strings chal.txt 
ictf{th4t_isn7_chin3se}
ictf{th4t_isn7_chin3se}

packed (FORENSICS)

$ file routed.pkz 
routed.pkz: Zip archive data, at least v2.0 to extract, compression method=deflate
$ unzip routed.pkz 
Archive:  routed.pkz
  inflating: routed.pkt              
  inflating: cscoptlogo177x111.jpg   
  inflating: secret.png

secret.pngにフラグが書いてあった。

ictf{ab4697882634d4aeb6f21141ea2724d0}

base64 (CRYPTO)

64進数への変換のような処理をしているので、10進数に変換し、文字列にすればフラグになる。

#!/usr/bin/env python3
from Crypto.Util.number import *

q = 64

with open('out.txt', 'r') as f:
    secret_key = eval(f.read().split(' = ')[1])

flag_int =  0
for k in secret_key[::-1]:
    flag_int *= q
    flag_int += k

flag = long_to_bytes(flag_int).decode()
print(flag)
ictf{b4se_c0nv3rs1on_ftw_236680982d9e8449}

integrity (CRYPTO)

RSA暗号で、n、eとctがわかっている。その他に以下の値がわかっている。

signature = pow(flag, crc_hqx(long_to_bytes(d), 42), n)

crc_hqxの値は16ビットのため、65536未満の値になる。この値をe2とすると、以下の2つの値がわかっていることになる。

ct = pow(flag, e, n)
signature = pow(flag, e2, n)

e2のブルートフォースで、Common Modulus Attackで復号し、フラグの形式になるものを探す。

#!/usr/bin/env python3
from Crypto.Util.number import *
import gmpy2

def commom_modulus_attack(c1, c2, e1, e2, n):
    gcd, s1, s2 = gmpy2.gcdext(e1, e2)
    if s1 < 0:
        s1 = - s1
        c1 = gmpy2.invert(c1, n)
    elif s2 < 0:
        s2 = - s2
        c2 = gmpy2.invert(c2, n)

    v = pow(c1, s1, n)
    w = pow(c2, s2, n)
    x = (v * w) % n
    return x

with open('out.txt', 'r') as f:
    params = f.read().splitlines()

e = 65537
n = int(params[0].split(' ')[-1])
ct = int(params[1].split(' ')[-1])
signature = int(params[2].split(' ')[-1])

for e2 in range(65536):
    m = commom_modulus_attack(ct, signature, e, e2, n)
    flag = long_to_bytes(m)
    if flag.startswith(b'ictf{'):
        flag = flag.decode()
        print(flag)
        break
ictf{oops_i_leaked_some_info}

tango (CRYPTO)

サーバの処理概要は以下の通り。

・KEY: ランダムな32バイト文字列
・以下繰り返し
 ・option: 入力
 ・optionが"E"の場合
  ・command: 入力
  ・encrypt_command(command)
   ・commandの長さが3以外の場合、optionの入力からやり直し
   ・cipher = Salsa20.new(key=KEY)
   ・nonce = cipher.nonce
   ・data = json.dumps({'user': 'user', 'command': command, 'nonce': [ランダム8バイト文字列の16進数表記文字列]}).encode('ascii')
   ・checksum: dataのCRC32の値を文字列にしたもの
   ・ciphertext = cipher.encrypt(data)
   ・nonce + checksum + ciphertextを16進数表記で表示
 ・optionが"R"の場合
  ・packet: 入力
  ・run_command(packet)
   ・packet: packetをhexデコード
   ・nonce: packetの先頭8バイト
   ・checksum: packetの8バイト目から11バイト目までを数値化したもの
   ・ciphertext: packetの12バイト目以降
   ・cipher = Salsa20.new(key=KEY, nonce=nonce)
   ・plaintext = cipher.decrypt(ciphertext)
   ・plaintextのCRC32がchecksumと一致していない場合、optionの入力からやり直し
   ・data: plaintextをjsonとしてパースしたデータ
   ・user: dataの"user"データ
   ・command: dataの"command"データ
   ・commandが"nop"の場合、対応するメッセージを表示
   ・commandが"sts"の場合
    ・userが"user"または"root"にない場合、対応するメッセージを表示
    ・userが"user"または"root"の場合、対応するメッセージを表示
   ・commandが"flag"の場合
    ・userが"root"以外の場合、対応するメッセージを表示
    ・userが"root"の場合、フラグを表示
 ・optionが"Q"の場合、終了

Salsa20はkeyとnonceが同じ場合、平文と暗号文のXORは同じになる。
適当な3文字のコマンドを指定し、以下のデータを暗号化する。

{"user": "user", "command": "sts", "nonce": "xxxxxxxxxxxxxxxx(16進数)"} 

作成したいデータは以下のデータの暗号化データ。nonceは使わないので不要。

{"user": "root", "command": "flag"}

あとはSalsa20のnonceを同じにすれば、XORが同じになることから、暗号文を生成して送信すればよい。

#!/usr/bin/env python3
import socket
from Crypto.Util.number import *
from zlib import crc32
from Crypto.Util.strxor import strxor

def recvuntil(s, tail):
    data = b''
    while True:
        if tail in data:
            return data.decode()
        data += s.recv(1)

data1 = b'{"user": "user", "command": "sts", "nonce": "0000000000000000"}'
data2 = b'{"user": "root", "command": "flag"}'

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('tango.chal.imaginaryctf.org', 1337))

data = recvuntil(s, b'\n').rstrip()
print(data)
data = recvuntil(s, b'\n').rstrip()
print(data)

data = recvuntil(s, b'> ')
print(data + 'E')
s.sendall(b'E\n')
data = recvuntil(s, b': ')
print(data + 'sts')
s.sendall(b'sts\n')
data = recvuntil(s, b'\n').rstrip()
print(data)
packet = bytes.fromhex(data.split(' ')[-1])
nonce = packet[:8]
checksum = bytes_to_long(packet[8:12])
ciphertext = packet[12:]

key = strxor(data1, ciphertext)[:len(data2)]
ciphertext = strxor(data2, key)
checksum = long_to_bytes(crc32(data2))
packet = (nonce + checksum + ciphertext).hex()

data = recvuntil(s, b'> ')
print(data + 'R')
s.sendall(b'R\n')
data = recvuntil(s, b': ')
print(data + packet)
s.sendall(packet.encode() + b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)

実行結果は以下の通り。

== proof-of-work: disabled ==
Welcome to the Tango server! What would you like to do?
[E]ncrypt a command
[R]un a command
[Q]uit
> E
Your command: sts
Your encrypted packet is: e2e6c43b2f4be3869dd001a812dde02fb3114297cc78f42c637e8722208c706ea5fdc74f8a420ebe8b9ef1c6d0c530fec0d0cb0774b01578482cbbfc0410621f6bc4db9a3c4a7e9f88a4de
[E]ncrypt a command
[R]un a command
[Q]uit
> R
Your encrypted packet (hex): e2e6c43b2f4be3863feb6cae12dde02fb3114297cc78f33069788722208c706ea5fdc74f8a420ebe8b8be9d495cb6d
ictf{F0xtr0t_L1m4_4lph4_G0lf}
ictf{F0xtr0t_L1m4_4lph4_G0lf}



以上の内容はhttps://yocchin.hatenablog.com/entry/2024/07/22/233604より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14