picoCTF 2022 Write Up
We participated in picoCTF 2022 held in March 2022. Last year I competed as an individual, but this year I am competing as Team TaruTaru. Taru Taru was in 595th place with 9200 points. I have to work harder...
I wrote Write Up about the problem I solved. I have quite a bit of overlap with my teammates. The actual code I used is on GitHub. There is no readability at all, so if you have any concerns, please feel free to ask.
Binary Exploitation
buffer overflow 0 (100pts)
Distributed vuln.c
include
int main(int argc, char **argv){
//略
signal(SIGSEGV, sigsegv_handler);
//略
}
There exists a function called , and the contents of the sigsegv_handler
are
void sigsegv_handler(int sig) {
printf("%s\
", flag);
fflush(stdout);
exit(1);
}
It has become.
So it seems that if you raise a Segmentation Fault during execution, the sigsegv_handler
will be executed and the flag will be output.
The contents of the vuln
function executed within the main
function are
void vuln(char *input){
char buf2[16];
strcpy(buf2, input);
}
and assume that input is less than 16 characters, but this input is used in main
function
int main(int argc, char **argv){
//略
char buf1[100];
gets(buf1);
vuln(buf1);
//略
}
It is possible to pass up to 100 characters (including null characters) in the form of. Therefore, if you send a large number of characters to the program, a Segmentation Fault will occur and a flag will be displayed.
CVE-XXXX-XXXX (100pts)
If you do a Google search on "Windows Print Spooler Service CVE", the IPA website comes up first. The CVE number is CVE-2021-34527. Is the genre of this issue Binary Explotion???
RPS (200pts)
Play rock-paper-scissors with the server, and if you win 5 games in a row, you will get a flag. The judgment of victory or defeat is
char* loses[3] = {"paper", "scissors", "rock"};
//中略
if (strstr(player_turn, loses[computer_turn])) {
puts("You win! Play again?");
return true;
} else {
puts("Seems like you didn't win this time. Play again?");
return false;
}
It is implemented in:
palyer_turn
is a string of user input, and computer_turn
is a random number of 0~2.
strstr(const char *s1, const char *s2)
returns the starting position of the string s1 when it contains the string s2, so if you enter rockscissorspaper
as input, it will be judged as a victory no matter what the opponent's hand is.
Repeat this 5 times and the flag will appear.
Cryptography
basic-mod1 (100pts)
It seems that you can calculate the number mod37 described in message.txt
and replace it with A-Z if its value is 0-25, 0-9 if it is 26-35, and _ if it is 36.
Write a script and run it.
print("picoCTF{", end="")
with open("message.txt", "r") as fp:
\tfor n in fp.read().strip().split(" "):
\t\tn = int(n)
\t\tn = n%37
\t\tif n==36:
\t\t\tprint("_", end="")
\t\telif n>=26:
\t\t\tprint(n-26, end="")
\t\telse:
\t\t\tprint(chr(ord('a')+n), end="")
print("}")
basic-mod2 (100pts)
Calculate the inverse of the number in mod41 described in message.txt
and replace it with A-Z if the value is 1-26, 0-9 if it is 27-36, and _ if it is 37.
x*y \\equiv 1\\ (mod\\ n)
When holds, y is called the inverse (in mod n) of x. There is an algorithm that efficiently computes the inverse source, but with mod 41, it is OK to calculate it by full search.
def get_inv(i):
for j in range(1, 41):
if (i*j)%41==1:
return j
return 999
print("picoCTF{", end="")
with open("message.txt", "r") as fp:
for n in fp.read().strip().split(" "):
n = int(n)
n = get_inv(n)
if n==37:
print("_", end="")
elif n>=27:
print(n-27, end="")
else:
print(chr(ord('a')+n-1), end="")
print("}")
rail-fence (100pts)
You can decode it in CyberChef.
substitution0 (100pts)
A single substitution cipher.
At the end of the sentence
Pmj tuec xg: fxslSPT{...}
would obviously replace The flag is: picoCTF{...}
.
The flag consists of uppercase letters, but the rest of the sentence is only lowercase, so it seems impossible to specify the replacement destination of the uppercase case if the substitution is different in the case, so maybe the case substitution destination is the same... I feel it. Or rather, I hope so.
All that's left is to find a word like that and identify the replacement destination one letter at a time.
substitution1 (100pts)
It is also a single transliteration cipher.
eqs coxa dj: zdifIEC{...}
would have replaced the flag is: picoCTF{...}
. I will replace the rest with my spirit.
substitution2 (100pts)
The moment I see the ciphertext, I feel like I'm going to get a kick out of it, but I hold back and take a sip of my coffee. From the flow so far, it is a single transliteration cipher, and the gvjnxqceupemzMGN{...}
is a substitute for the theflagispicoCTF{...}
.
The rest is spirit. It's the spirit.
They don't know anything about frequency analysis.
Vigenere (100pts)
I was able to decode it in CyberChef.
diffie-hellman (200pts)
Alice and Bob shared a secret value in a Diffie Hermann key exchange and used that value to encrypt plaintext with the Caesar cipher. Since the value used for the Diffie Hermann key exchange is small across the board, it seems that it can be easily decrypted, but even if you do not think about such a small difficulty, the last is Caesar cipher, so it is OK if you combine it with force work.
It was C4354R_C1PH3R_15_4_817_0U7D473D_84AA1DA8
that seemed to have meaning as plain text, so if you wrap it with picoCTF{}
, it becomes a flag.
Forensics
Enhance! (100pts)
When I open the distributed SVG file in a text editor
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:0.00352781px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.26458332;"
x="107.43014"
y="132.08501"
id="text3723"><tspan
sodipodi:role="line"
x="107.43014"
y="132.08501"
style="font-size:0.00352781px;line-height:1.25;fill:#ffffff;stroke-width:0.26458332;"
id="tspan3748">p </tspan><tspan
sodipodi:role="line"
x="107.43014"
y="132.08942"
style="font-size:0.00352781px;line-height:1.25;fill:#ffffff;stroke-width:0.26458332;"
id="tspan3754">i </tspan><tspan
sodipodi:role="line"
x="107.43014"
y="132.09383"
style="font-size:0.00352781px;line-height:1.25;fill:#ffffff;stroke-width:0.26458332;"
id="tspan3756">c </tspan><tspan
sodipodi:role="line"
x="107.43014"
y="132.09824"
style="font-size:0.00352781px;line-height:1.25;fill:#ffffff;stroke-width:0.26458332;"
id="tspan3758">o </tspan><tspan
sodipodi:role="line"
x="107.43014"
y="132.10265"
style="font-size:0.00352781px;line-height:1.25;fill:#ffffff;stroke-width:0.26458332;"
id="tspan3760">C </tspan><tspan
sodipodi:role="line"
x="107.43014"
y="132.10706"
style="font-size:0.00352781px;line-height:1.25;fill:#ffffff;stroke-width:0.26458332;"
id="tspan3762">T </tspan><tspan
sodipodi:role="line"
x="107.43014"
y="132.11147"
style="font-size:0.00352781px;line-height:1.25;fill:#ffffff;stroke-width:0.26458332;"
id="tspan3764">F { 3 n h 4 n </tspan><tspan
sodipodi:role="line"
x="107.43014"
y="132.11588"
style="font-size:0.00352781px;line-height:1.25;fill:#ffffff;stroke-width:0.26458332;"
id="tspan3752">c 3 d _ a a b 7 2 9 d d }</tspan></text>
You can see the flag being filled in.
File types (100pts)
The extension of the distributed file is .pdf
, but when I check the file type with file
command,
% file Flag.pdf
Flag.pdf: shell archive text
is displayed. This is
% sh Flag.pdf
It can be defrosted by .
In this way, if you repeatedly examine the file type with file
command → unzip it in the corresponding application, you will eventually get
7069636f4354467b66316c656e406d335f6d406e3170756c407431306e5f
6630725f3062326375723137795f33633739633562617d0a
You will get a text file with the contents of . Apparently it looks like Hex, so when I decoded it in CyberChef it showed up flags.
Lookey here (100pts)
% cat anthem.flag.txt | grep pico
we think that the men of picoCTF{[email protected]_4c479940}
Packets Primer (100pts)
When I read the distributed pcap file with Wireshark, I got a flag.
Redaction gone wrong (100pts)
Some parts of the document are blacked out, but you can read them by dragging them, copying them, and then pasting them into a text editor.
Sleuthkit Intro (100pts)
When I run mmls
% mmls disk.img
DOS Partition Table
Offset Sector: 0
Units are in 512-byte sectors
Slot Start End Length Description
000: Meta 0000000000 0000000000 0000000001 Primary Table (#0)
001: ------- 0000000000 0000002047 0000002048 Unallocated
002: 000:000 0000002048 0000204799 0000202752 Linux (0x83)
It comes to.
When you access the server, it asks for What is the size of the Linux partition in the given disk image?
, so you can send a 202752 to get the flag.
Eavesdrop (300pts)
Under construction...
SideChannel (400pts)
A binary called pin_checker
is distributed and when you run it, it asks you to "Please enter your 8-digit PIN code:". When I tried typing "000000000", I got "Access denied." It was displayed. Entering the appropriate 8-digit PIN code will probably succeed in Access.
The title of the problem is "SideChannel" and the hint is "Read about timing-based side-channel attacks." And from what is written, the PIN code (part of ?) It seems that the execution time will change depending on whether it matches the correct answer or not. It seems that the correct PIN code is considered to be an 8-digit number, so there are only $10^8$ of 99999999 from 0000000000. Let's try it all out.
First, if you look at the binaries in strings, you'll see a message that says "Access granted. You may use your PIN to log into the master server." You can see that the message is prepared. If you enter the correct PIN code, you will see this message.
After that, assemble an appropriate shell and perform a full search until the correct answer message comes out.
% seq 0 99999999 | xargs -t -P1024 -r -I @ sh -c 'echo @ && printf "%08d\
" "@" | ./pin_checker ' > log.txt
This shell script is a script that "performs 1024 parallel processing of "displaying the number from 0 to 99999999 and then passing it to the pin_checker
(by filling in 0 in 8 digits)". When I ran parallel execution on the 32-core, 64-thread AMD Ryzen ThreadRipper 3970X, it looked like it would be over in about four days. In fact, after 2 days, I received the above correct answer message.
Note that the script above does not do any sort of exclusive control when writing to the log.txt
, so other threads are writing a lot between when the correct PIN code is written to the file and when the correct answer message is written. That said, it's safe to assume that the correct PIN code is also written near the correct answer.
% cat log.txt | grep -3 granted
Please enter your 8-digit PIN code:
8
Checking PIN...
Access granted. You may use your PIN to log into the master server.
48390594
Please enter your 8-digit PIN code:
8
Apparently, the correct PIN code is around the 48390594. Again, explore this area without parallel processing.
% seq 48389600 48390600 | xargs -t -r -I@ sh -c 'echo @ && printf "%08d\
" "@" | ./pin_checker ' > log2.txt
% cat log2.txt | grep -5 granted
Access denied.
48390513
Please enter your 8-digit PIN code:
8
Checking PIN...
Access granted. You may use your PIN to log into the master server.
48390514
Please enter your 8-digit PIN code:
8
Checking PIN...
Access denied.
The correct PIN code was 48390513. Then you can send it to the server and get the flag. I feel like I did all this last year. Seriously.
Revers Engineering
file-run1 (100pts)
% strings run | grep pico
picoCTF{U51N6_Y0Ur_F1r57_F113_9bc52b6b}
file-run2 (100pts)
% strings run | grep pico
picoCTF{F1r57_4rgum3n7_be0714da}
I didn't need to do it...
GDB Test Drive (100pts)
After downloading the distributed binary, the flag was displayed when I ran the command as described in the problem statement.
patchme.py (100pts)
When I run the distributed python file, it asks for Please enter correct password fo flag:
. If you look at the contents of the file
if( user_pw == "ak98" + \\
"-=90" + \\
"adfjhgj321" + \\
"sleuth9000"):
Since there is a notation that , you can get a flag by entering ak98-=90adfjhgj321sleuth9000
as pw.
Safe Opener (100pts)
The distributed Java file contains
String encodedkey = "cGwzYXMzX2wzdF9tM18xbnQwX3RoM19zYWYz";
and in the SafeOpener class
Base64.Encoder encoder = Base64.getEncoder();
You can also see the notation that says. Apparently, it compares the input encoded by b64 with the encodedkey
, so I decoded it and it became pl3as3_l3t_m3_1nt0_th3_saf3
. This is enclosed in picoCTF{}
and is a flag.
unpackme.py (100pts)
Insert print(plain)
between the plain = f.decrypt(payload)
and exec(plain.decode())
of the unpackme.py
to see what is running in the exec()
.
When I ran it, it contained a flag in the plain
.
bloat.py (100pts)
When I run the distributed python file
It is said that it is Please enter correct password for flag:
.
The contents are subtly obfuscated and frustrating, but apparently
if arg432 == a[71]+a[64]+a[79]+a[79]+a[88]+a[66]+a[71]+a[64]+a[77]+a[66]+a[68]:
It seems to be judging the password in the part.
So, if you insert print(a[71]+a[64]+a[79]+a[79]+a[88]+a[66]+a[71]+a[64]+a[77]+a[66]+a[68])
at an appropriate position and display the contents, you can see that this value is happychance
.
After that, the flag is displayed by entering this value again.
Fresh Java (200pts)
Reverses binaries distributed using Java's decompiler. I use Docker
% docker run -it --rm -v $PWD:/mnt kwart/jd-cli /mnt/KeygenMe.class > decode.java
Got the reverse result as: Looking at the contents,
//略
if (str.charAt(2) != 'c') {
System.out.println("Invalid key");
return;
}
if (str.charAt(1) != 'i') {
System.out.println("Invalid key");
return;
}
if (str.charAt(0) != 'p') {
System.out.println("Invalid key");
return;
}
and the flags are compared to the correct answer letter by character, so you can get the flag by connecting them.
Bbbbloat (300pts)
I was asked to run the given Binary and was told What's my favorite number?
if I put in the appropriate numbers, it would be Sorry, that's not it!
.
I decompiled Binary with Ghidra
printf("What\\'s my favorite number? ");
__isoc99_scanf();
if (local_48 == 0x86187) {
__s = (char *)FUN_00101249(0,&local_38);
fputs(__s,stdout);
putchar(10);
free(__s);
}
else {
puts("Sorry, that\\'s not it!");
}
Since I saw the result, I entered 549255 (=0x86187) and the flag was displayed.
Web Exploitation
Includes (100pts)
The first half of the flag was written in the style.css
of the web page, and the second half was written in the script.js
.
Inspect HTML (100pts)
Flags are listed in the comments in the HTML file of the web page.
Local Authority (100pts)
The login form was displayed, so when I logged in with the appropriate ID and PW, I was skipped to the login.php
. On this page, we are reading a file called secure.js
, and its contents are
function checkPassword(username, password)
{
if( username === 'admin' && password === 'strongPassword098765' )
{
return true;
}
else
{
return false;
}
}
Because it is a fierce yaba implementation, when I logged in again with this id and pw, the flag was displayed.
Search source (100pts)
Essentially, it's no different from Includes, but it's hard to look for it with your eyes because of the amount of sentences. At first
% wget -r http://saturn.picoctf.net:58133/
As a web page to keep at hand. By adding -r
options, you can save links such as js and css at the same time.
Continued
% grep -rl pico
./saturn.picoctf.net:58133/css/style.css
Locate the file that contains the string pico
as. Apparently the ./saturn.picoctf.net:58133/css/style.css
contains flags.
At last
% cat ./saturn.picoctf.net:58133/css/style.css | grep pico
/** banner_main picoCTF{1nsp3ti0n_0f_w3bpag3s_587d12b8} **/
Get flagged as:
Forbidden Paths (200pts)
Enter ../../../../flag.txt
as Filename to display the flag.
Power Cookie (200pts)
Since a cookie called isAdmin: 0
is set, I changed it to 1
and the flag was displayed.
A Chrome extension called EditThisCookie is useful for rewriting cookies.
Secrets (200pts)
Since it says We have several pages hidden.
in the problem statement, there is probably a page that is not linked. Use the DIRB to run Brute Force.
% dirb http://saturn.picoctf.net:61481/
-----------------
DIRB v2.22
By The Dark Raver
-----------------
START_TIME: Sun Mar 20 07:40:05 2022
URL_BASE: http://saturn.picoctf.net:61481/
WORDLIST_FILES: /usr/share/dirb/wordlists/common.txt
-----------------
GENERATED WORDS: 4612
---- Scanning URL: http://saturn.picoctf.net:61481/ ----
+ http://saturn.picoctf.net:61481/index.html (CODE:200|SIZE:1023)
==> DIRECTORY: http://saturn.picoctf.net:61481/secret/
---- Entering directory: http://saturn.picoctf.net:61481/secret/ ----
==> DIRECTORY: http://saturn.picoctf.net:61481/secret/assets/
==> DIRECTORY: http://saturn.picoctf.net:61481/secret/hidden/
+ http://saturn.picoctf.net:61481/secret/index.html (CODE:200|SIZE:468)
---- Entering directory: http://saturn.picoctf.net:61481/secret/assets/ ----
---- Entering directory: http://saturn.picoctf.net:61481/secret/hidden/ ----
+ http://saturn.picoctf.net:61481/secret/hidden/index.html (CODE:200|SIZE:2118)
-----------------
END_TIME: Sun Mar 20 08:39:18 2022
DOWNLOADED: 18448 - FOUND: 3
drib found a page called http://saturn.picoctf.net:61481/secret/hidden/index.html
. I'll try to access it because it's kind of like that.
Then you can see that it is loading a resource called http://saturn.picoctf.net:61481/secret/hidden/superhidden/login.css
.
So I went to http://saturn.picoctf.net:61481/secret/hidden/superhidden/
and found a flag in the page with the same color as the background.
SQL Direct (200pts)
Connects to the specified SQL server, examines the table list, and displays the contents of the table.
pico=# \\dt
List of relations
Schema | Name | Type | Owner
--------+-------+-------+----------
public | flags | table | postgres
(1 row)
pico=# select * from flags;
id | firstname | lastname | address
----+-----------+-----------+----------------------------------------
1 | Luke | Skywalker | picoCTF{L3arN_S0m3_5qL_t0d4Y_31fd14c0}
2 | Leia | Organa | Alderaan
3 | Han | Solo | Corellia
(3 rows)
SQLiLite (300pts)
You can log in by setting the appropriate value for pw with Username as the ' or 1=1 ;--
. Also, when I looked at the page in the developer tools, it was flagged.