This year's picoCTF competition took place from March 12th to March 26th. Our team of 2 solved 43/47 challenges, with a total score of 7325/9225. We achieved 111th place out of 6957 teams globally.
This is the first post out of 5 for picoCTF 2024, one for each of the categories in this year's competition.
- General Skills
- Cryptography
- Web
- Reverse Engineering
- Binary Exploitation
Table of contents
Open Table of contents
END_TOC
We can use a simple ssh
command to connect to the server:
$ ssh [email protected] -p55352
Host key fingerprint is SHA256:4S9EbTSSRZm32I+cdM5TyzthpQryv5kudRP9PIKT7XQ
+--[ED25519 256]--+
| .+=o |
| .+o.. |
| o o+ . . |
| o o. + o +|
| S o+B.=+|
| ....+=+OEo|
| .o.o+o+o+|
| .o .+ o |
| +*. .|
+----[SHA256]-----+
[email protected]'s password:
Welcome ctf-player, here's your flag: picoCTF{s3cur3_c0nn3ct10n_8969f7d3}
Connection to titan.picoctf.net closed.
Unzipping the challenge file shows us a directory with a .git
subdirectory:
$ unzip challenge.zip
Archive: challenge.zip
creating: drop-in/
creating: drop-in/.git/
creating: drop-in/.git/branches/
inflating: drop-in/.git/description
<...>
creating: drop-in/.git/refs/
creating: drop-in/.git/refs/heads/
extracting: drop-in/.git/refs/heads/master
creating: drop-in/.git/refs/tags/
extracting: drop-in/.git/HEAD
inflating: drop-in/.git/config
creating: drop-in/.git/objects/
creating: drop-in/.git/objects/pack/
creating: drop-in/.git/objects/info/
creating: drop-in/.git/objects/ed/
extracting: drop-in/.git/objects/ed/5937346e2f3d04b2481120ac1eb6fc92e9f975
creating: drop-in/.git/objects/eb/
extracting: drop-in/.git/objects/eb/fc561e7c4130f03f3a668ce0609195788f1592
creating: drop-in/.git/objects/66/
extracting: drop-in/.git/objects/66/03cb4ff0c4ea293798c03a32e0d78d5ab12ca2
creating: drop-in/.git/objects/d5/
extracting: drop-in/.git/objects/d5/52d1ecd2d83fa2e65b6724d1ff73b45a7d59b7
creating: drop-in/.git/objects/0c/
extracting: drop-in/.git/objects/0c/1ab266b7a3a1cd099bb509f82b7a2d03aecd03
creating: drop-in/.git/objects/38/
extracting: drop-in/.git/objects/38/99edb7f3110d613c72ad40083fd8feeef703d0
inflating: drop-in/.git/index
extracting: drop-in/.git/COMMIT_EDITMSG
creating: drop-in/.git/logs/
inflating: drop-in/.git/logs/HEAD
creating: drop-in/.git/logs/refs/
creating: drop-in/.git/logs/refs/heads/
inflating: drop-in/.git/logs/refs/heads/master
extracting: drop-in/message.txt
We can see that there is a message.txt
, and multiple objects referenced in the .git/objects
directory. message.txt
's contents aren't useful:
$ bat message.txt -p
TOP SECRET
Let's check git log
to see what's happening:
$ git log
commit 3899edb7f3110d613c72ad40083fd8feeef703d0 (HEAD -> master)
Author: picoCTF <[email protected]m>
Date: Sat Mar 9 21:09:58 2024 +0000
remove sensitive info
commit 6603cb4ff0c4ea293798c03a32e0d78d5ab12ca2
Author: picoCTF <[email protected]m>
Date: Sat Mar 9 21:09:58 2024 +0000
create flag
We can checkout the first commit and then get the flag:
$ git checkout 6603cb4ff0c4ea293798c03a32e0d78d5ab12ca2
Note: switching to '6603cb4ff0c4ea293798c03a32e0d78d5ab12ca2'.
$ bat message.txt -p
picoCTF{s@n1t1z3_9539be6b}
Once again, the zip has a message.txt
and a .git
directory:
$ unzip challenge.zip
Archive: challenge.zip
creating: drop-in/
inflating: drop-in/message.txt
creating: drop-in/.git/
<...>
$ cd drop-in
$ bat message.txt -p
This is what I was working on, but I'd need to look at my commit history to know why...
Based on the message, we know we need to use git log
:
$ git log
commit 3339c144a0c78dc2fbd3403d2fb37d3830be5d94 (HEAD -> master)
Author: picoCTF <[email protected]m>
Date: Sat Mar 9 21:10:22 2024 +0000
picoCTF{t1m3m@ch1n3_d3161c0f}
This time instead of a message.txt
, we get a message.py
:
$ unzip challenge.zip
Archive: challenge.zip
creating: drop-in/
extracting: drop-in/message.py
creating: drop-in/.git/
<...>
$ cd drop-in
$ bat -p message.py
print("Hello, World!"
Let's check git log:
$ git log
commit 8cc3930896bb01ae046bc08c382bd30772918ff5 (HEAD -> master)
Author: picoCTF <[email protected]m>
Date: Sat Mar 9 21:09:06 2024 +0000
important business work
commit 6dbd8d326a2f0c9fe7f0011c8e60448b9accc6ff
Author: picoCTF <[email protected]m>
Date: Sat Mar 9 21:09:06 2024 +0000
important business work
commit 2e8970529c41058a68aae8bc04ef7a2d53ce0d8a
Author: picoCTF <[email protected]m>
Date: Sat Mar 9 21:09:06 2024 +0000
important business work
commit 135020e8b96565248b604cb42ae54e256e8fc48a
Author: picoCTF <[email protected]m>
Date: Sat Mar 9 21:09:06 2024 +0000
important business work
commit a95fbac033f190b3fb1066727ea01d7d4be362b5
Author: picoCTF <[email protected]m>
Date: Sat Mar 9 21:09:06 2024 +0000
important business work
commit e6b8b174bf1ce6361ff29096579d2752616cb6f2
Author: picoCTF <[email protected]m>
Date: Sat Mar 9 21:09:06 2024 +0000
important business work
...
That's a lot of commits! Let's try to grep
for the flag:
$ git log | grep picoCTF{
Author: picoCTF{@sk_th3_1nt3rn_b64c4705} <[email protected]m>
This time, there are multiple branches:
$ unzip challenge.zip
Archive: challenge.zip
creating: drop-in/
creating: drop-in/.git/
<...>
creating: drop-in/.git/refs/
creating: drop-in/.git/refs/heads/
extracting: drop-in/.git/refs/heads/main
creating: drop-in/.git/refs/heads/feature/
extracting: drop-in/.git/refs/heads/feature/part-1
extracting: drop-in/.git/refs/heads/feature/part-2
extracting: drop-in/.git/refs/heads/feature/part-3
<...>
creating: drop-in/.git/logs/
inflating: drop-in/.git/logs/HEAD
creating: drop-in/.git/logs/refs/
creating: drop-in/.git/logs/refs/heads/
inflating: drop-in/.git/logs/refs/heads/main
creating: drop-in/.git/logs/refs/heads/feature/
inflating: drop-in/.git/logs/refs/heads/feature/part-1
inflating: drop-in/.git/logs/refs/heads/feature/part-2
inflating: drop-in/.git/logs/refs/heads/feature/part-3
inflating: drop-in/flag.py
$ cd drop-in
$ bat -p flag.py
print("Printing the flag...")
Let's check the contents of flag.py
in each of the branches:
$ git show feature/part-{1..3}:flag.py
print("Printing the flag...")
print("picoCTF{t3@mw0rk_", end='')print("Printing the flag...")
print("m@k3s_th3_dr3@m_", end='')print("Printing the flag...")
print("w0rk_6c06cec1}")
Combining the flag strings, we get picoCTF{t3@mw0rk_m@k3s_th3_dr3@m_w0rk_6c06cec1}
.
We can connect to the server and play the game manually:
$ ssh -p 54716 [email protected]
The authenticity of host '[atlas.picoctf.net]:54716 ([18.217.83.136]:54716)' can't be established.
ED25519 key fingerprint is SHA256:M8hXanE8l/Yzfs8iuxNsuFL4vCzCKEIlM/3hpO13tfQ.
+--[ED25519 256]--+
| |
| . . |
| . . = + |
| + o + . = + . |
| = * + S. o + |
| . . + o.ooo+. o |
|. .o +ooo.. .|
|. . ..o.oo+.oE.o.|
| . . ...oo.o= .o|
+----[SHA256]-----+
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '[atlas.picoctf.net]:54716' (ED25519) to the list of known hosts.
[email protected]'s password:
Welcome to the Binary Search Game!
I'm thinking of a number between 1 and 1000.
Enter your guess: 500
Higher! Try again.
Enter your guess: 750
Higher! Try again.
Enter your guess: 875
Higher! Try again.
Enter your guess: 940
Higher! Try again.
Enter your guess: 970
Higher! Try again.
Enter your guess: 990
Higher! Try again.
Enter your guess: 1000
Lower! Try again.
Enter your guess: 995
Lower! Try again.
Enter your guess: 992
Higher! Try again.
Enter your guess: 993
Congratulations! You guessed the correct number: 993
Here's your flag: picoCTF{g00d_gu355_ee8225d0}
Connection to atlas.picoctf.net closed.
Again, we can just play the game with the help of cyberchef:
$ nc titan.picoctf.net 62401
Welcome to the Endian CTF!
You need to find both the little endian and big endian representations of a word.
If you get both correct, you will receive the flag.
Word: netnd
Enter the Little Endian representation: 646e74656e
Correct Little Endian representation!
Enter the Big Endian representation: 6e65746e64
Correct Big Endian representation!
Congratulations! You found both endian representations correctly!
Your Flag is: picoCTF{3ndi4n_sw4p_su33ess_817b7cfe}
Not sure why this is 200 points.
Let's see what information is being leaked:
$ nc tethys.picoctf.net 59710
SSH-2.0-OpenSSH_7.6p1 My_Passw@rd_@1234
Seems like the SSH server header and a password. Let's connect to the application:
$ nc tethys.picoctf.net 50678
*************************************
**************WELCOME****************
*************************************
what is the password?
My_Passw@rd_@1234
What is the top cyber security conference in the world?
DEFCON
the first hacker ever was known for phreaking(making free phone calls), who was it?
John Draper
player@challenge:~$ ls -la
ls -la
total 20
drwxr-xr-x 1 player player 20 Mar 9 16:39 .
drwxr-xr-x 1 root root 20 Mar 9 16:39 ..
-rw-r--r-- 1 player player 220 Apr 4 2018 .bash_logout
-rw-r--r-- 1 player player 3771 Apr 4 2018 .bashrc
-rw-r--r-- 1 player player 807 Apr 4 2018 .profile
-rw-r--r-- 1 player player 114 Feb 7 17:25 banner
-rw-r--r-- 1 root root 13 Feb 7 17:25 text
After quickly googling for the first phreaker, we can get a shell on the server. Let's see what we can find:
player@challenge:~$ cat banner
cat banner
*************************************
**************WELCOME****************
*************************************
player@challenge:~$ cat text
cat text
keep digging
player@challenge:~$ cd /root
cd /root
player@challenge:/root$ ls
ls
flag.txt script.py
player@challenge:/root$ cat flag.txt
cat flag.txt
cat: flag.txt: Permission denied
player@challenge:/root$ ls -la
ls -la
total 16
drwxr-xr-x 1 root root 6 Mar 9 16:39 .
drwxr-xr-x 1 root root 29 Mar 12 18:06 ..
-rw-r--r-- 1 root root 3106 Apr 9 2018 .bashrc
-rw-r--r-- 1 root root 148 Aug 17 2015 .profile
-rwx------ 1 root root 46 Mar 9 16:39 flag.txt
-rw-r--r-- 1 root root 1317 Feb 7 17:25 script.py
Seems like we're meant to read /root/flag.txt
. Unfortunately, only root has permission to read it. Let's see if there's anything interesting in script.py
:
player@challenge:/root$ cat script.py
cat script.py
import os
import pty
incorrect_ans_reply = "Lol, good try, try again and good luck\n"
if __name__ == "__main__":
try:
with open("/home/player/banner", "r") as f:
print(f.read())
except:
print("*********************************************")
print("***************DEFAULT BANNER****************")
print("*Please supply banner in /home/player/banner*")
print("*********************************************")
try:
request = input("what is the password? \n").upper()
while request:
if request == 'MY_PASSW@RD_@1234':
text = input("What is the top cyber security conference in the world?\n").upper()
if text == 'DEFCON' or text == 'DEF CON':
output = input(
"the first hacker ever was known for phreaking(making free phone calls), who was it?\n").upper()
if output == 'JOHN DRAPER' or output == 'JOHN THOMAS DRAPER' or output == 'JOHN' or output== 'DRAPER':
scmd = 'su - player'
pty.spawn(scmd.split(' '))
else:
print(incorrect_ans_reply)
else:
print(incorrect_ans_reply)
else:
print(incorrect_ans_reply)
break
except:
KeyboardInterrupt
The program opens /home/player/banner
and outputs it at the start. Based on the hint, we know that we are meant to use symlinks. If we can change /home/player/banner
to be a symlink, we can point it to /root/flag.txt
and cause the program to read it for us.
player@challenge:/proc$ cd ~
cd ~
player@challenge:~$ ls -l
ls -l
total 8
-rw-r--r-- 1 player player 114 Feb 7 17:25 banner
-rw-r--r-- 1 root root 13 Feb 7 17:25 text
player@challenge:~$ rm banner
rm banner
player@challenge:~$ ln -s /root/flag.txt banner
ln -s /root/flag.txt banner
player@challenge:~$ ll
ll
total 20
drwxr-xr-x 1 player player 41 Mar 12 18:16 ./
drwxr-xr-x 1 root root 20 Mar 9 16:39 ../
-rw------- 1 player player 5 Mar 12 18:11 .bash_history
-rw-r--r-- 1 player player 220 Apr 4 2018 .bash_logout
-rw-r--r-- 1 player player 3771 Apr 4 2018 .bashrc
-rw-r--r-- 1 player player 807 Apr 4 2018 .profile
lrwxrwxrwx 1 player player 14 Mar 12 18:16 banner -> /root/flag.txt*
-rw-r--r-- 1 root root 13 Feb 7 17:25 text
player@challenge:~$ ^[
And now if we connect to the server again:
$ nc tethys.picoctf.net 50678
picoCTF{b4nn3r_gr4bb1n9_su((3sfu11y_68ca8b23}
what is the password?
^C
Connecting to the server puts us into a bash jail, with all letters being blocked:
SansAlpha$ a
SansAlpha: Unknown character detected
SansAlpha$ A
SansAlpha: Unknown character detected
SansAlpha$ ""
bash: : command not found
Let's see what files are in the current directory:
SansAlpha$ **
bash: blargh: command not found
SansAlpha$ **/*
bash: blargh/flag.txt: Permission denied
Seems like we will need to read blargh/flag.txt
somehow. But how can we use cat
without using the letters c
, a
, and t
? A quick google search brings us to a page called launch bash without using any letters. The second answer uses a redirection of a bash "command not found" error message to stdout, then using brace expressions to extract the bash
string from the start.
Using this method, we can get a bunch of letters into a variable:
SansAlpha$ _1="$(- 2>&1)"
bash: warning: setlocale: LC_ALL: cannot change locale (en_US.UTF-8)
SansAlpha$ "$_1"
bash: bash: -: command not found: command not found
Now that we have bash: -: command not found: command not found
, we have enough letters to use cat
. We can use "Substring Expansion" in order to extract each letter that we need, in the form ${parameter:offset:length}
.
Here are the characters we need:
c
at position 9a
at position 1t
at position 19
Putting it together:
SansAlpha$ _2="${_1:9:1}${_1:1:1}${_1:19:1}"
SansAlpha$ $_2 **/*
return 0 picoCTF{7h15_mu171v3r53_15_m4dn355_36a674c0}Alpha-9, a distinctive layer within the Calastran multiverse, stands as a
sanctuary realm offering individuals a rare opportunity for rebirth and
introspection. Positioned as a serene refuge between the higher and lower
Layers, Alpha-9 serves as a cosmic haven where beings can start anew,
unburdened by the complexities of their past lives. The realm is characterized
by ethereal landscapes and soothing energies that facilitate healing and
self-discovery. Quantum Resonance Wells, unique to Alpha-9, act as conduits for
individuals to reflect on their past experiences from a safe and contemplative
distance. Here, time flows differently, providing a respite for those seeking
solace and renewal. Residents of Alpha-9 find themselves surrounded by an
atmosphere of rejuvenation, encouraging personal growth and the exploration of
untapped potential. While the layer offers a haven for introspection, it is not
without its challenges, as individuals must confront their past and navigate
the delicate equilibrium between redemption and self-acceptance within this
tranquil cosmic retreat.
And this is proof that any device is good enough for CTFs:
Conclusion
Overall, this year's General Skills challenges were very underwhelming. The only one that I enjoyed was SansAlpha, which taught me how to use bash error strings to construct commands. I felt that this category was acting like a git tutorial this year and I would loved to have see some more creative and interesting challenges. I also felt that a lot of the challenges in this category suffered from point inflation, and did not accurately reflect the difficulty of these challenges.