I played PlaidCTF 2019 as a member of insecure
. Our team got 261pts and reached the 116th place. I don't feel it's a good result. All the challenges I tried were very difficult but also a lot of fun.
- [Misc 10pts(368 solves)] docker
- [Misc 100pts (306 solves)]can you guess me
- [Misc 150pts(97 solves)]A Whaley Good Joke
[Misc 10pts(368 solves)] docker
docker pull whowouldeverguessthis/public
I ran docker pull
and docker run --it public bash
as the description says. Then I found the file flag
in /root
but the content was I'm sorry, but your princess is in another castle.
I wondered that I'd get the solution if I could see how this container image was created. So, I ran docker image history
and found that the flag was overwritten during the container creation.
$ docker image history whowouldeverguessthis/public --no-trunc IMAGE CREATED CREATED BY SIZE COMMENT sha256:969996089570ead17d586e6b940c8cb0375aba7bd329076cbe2a2fc18653b8d9 6 hours ago /bin/sh -c echo "I'm sorry, but your princess is in another castle" > /flag 50B <missing> 6 hours ago /bin/sh -c echo "PCTF{well_it_isnt_many_points_what_did_you_expect}" > /flag 51B <missing> 2 months ago /bin/sh -c #(nop) CMD ["bash"] 0B <missing> 2 months ago /bin/sh -c #(nop) ADD file:34b9952e66cb98287bc41fab82739375fe6c43f38ed3b893e98a99035b494770 in / 68.9MB
[Misc 100pts (306 solves)]can you guess me
Here's the source to a guessing game: here
You can access the server at
As I read the distributed python script, I found that I had to construct a python code with 10 or less than 10 types of characters which must be evaluated as the same value of secret_value_for_password
in order to get the flag. Unfortunately print(flag)
was invalid because it had 11 types of characters. I tried some other ways to dump the variables and found print(vars())
. It has just 10 types of characters and it'll dump all the variables.
$ nc canyouguessme.pwni.ng 12349 ____ __ __ ____ __ __ / ___|__ _ _ _\ \ / /__ _ _ / ___|_ _ ___ ___ ___| \/ | ___ | | / _` | '_ \ V / _ \| | | | | _| | | |/ _ \/ __/ __| |\/| |/ _ \ | |__| (_| | | | | | (_) | |_| | |_| | |_| | __/\__ \__ \ | | | __/ \____\__,_|_| |_|_|\___/ \__,_|\____|\__,_|\___||___/___/_| |_|\___| Input value: print(vars()) {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x7fe9664399e8>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': '/home/guessme/can-you-guess-me.py', '__cached__': None, 'exit': <built-in function exit>, 'secret_value_for_password': 'not even a number; this is a damn string; and it has all 26 characters of the alphabet; abcdefghijklmnopqrstuvwxyz; lol', 'flag': 'PCTF{hmm_so_you_were_Able_2_g0lf_it_down?_Here_have_a_flag}', 'exec': <function exec at 0x7fe966382158>, 'val': 0, 'inp': 'print(vars())', 'count_digits': 10} Nope. Better luck next time.
Well done.
[Misc 150pts(97 solves)]A Whaley Good Joke
You'll have a whale of a time with this one! I couldn't decide what I wanted the flag to be so I alternated adding and removing stuff in waves until I got something that looked good. Can you dive right in and tell me what was so punny?
We were given a tar.gz
file which had various sha256-string name jsons/directories, manifest.json
, and repositories
. After doing a search, I found they were created by docker save
. So, I tried docker load
but it failed because some layer names were filled with ???
. When I checked the contents archived in layer.tar
for each layer directories, I found /root/flag.sh
as shown below.
#!/bin/bash for i in {1..32} do test -f $i if [[ $? -ne 0 ]] then echo "Missing file $i - no flag for you!" exit fi done echo pctf{1_b3t$(cat 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32)}
In order to get the flag, we have to concatenate the files from 1 to 32. Other layer.tar
files contained some files such as /root/1
, /root/20
and so on. However, they were duplicated since there were some processes which overwrote some files. So, I decided to do a search for the candidates of the flag by brute force.
Thus I decided to search the candidates of the flag by brute force.
for d in `ls`; do if [[ -d "$d" ]]; then tar xf "$d/layer.tar" -C "$d" fi done
import glob import re paths = [p for p in glob.glob("**", recursive=True) if re.search(r'root/[0-9]+$', p) and not p.startswith('workspace')] table = {} for p in paths: n = int(re.findall(r'root/([0-9]+)$', p)[0]) with open(p) as f: x = f.read() if n not in table: table[n] = set() table[n].add(x) def f(i, e, s): if i > e: print(s) return for x in table[i]: f(i+1, e, s+x)
Of course it'd be hard to do a search for 32 characters so I did a search word by word.
Eventually I got the most plausible flag: pctf{1_b3t_4_couldnt_c0nt4in3r_ur_l4ught3r}
but it didn't work. So, I posted the previous script to our team Slack. Then my teammate id:ptr-yudai submitted the flag pctf{1_b3t_u_couldnt_c0nt4in3r_ur_l4ught3r}
and it was correct.