Update (12/May/2022) : I reported below observations to the PyScript team and they have confirmed that these are expected behavior. Since the emscripten filesystem is in the user’s browser memory itself, no harm in being able to list files, access files or write to arbitrary locations. Refer this issue.

Introduction

Vulnerabilities I found in PyScript.

Vulnerability 1: File System Browsing

Using the glob module which is part of Python Standard Library, the Emscripten filesystem can be browsed.

Browsing ‘/’

import glob

glob.glob("/*")

Output:

['/tmp', '/home', '/dev', '/proc', '/lib']

Screenshot:

FS

Browsing ‘/home’

PoC:

import glob

glob.glob("/home/*")

Output:

['/home/web_user', '/home/pyodide']

Screenshot:

HOME

Browsing ‘/lib’

PoC:

import glob

glob.glob("/lib/*")

Output:

['/lib/python3.10']

Screenshot:

lib

Browsing ‘/lib/python3.10/’

PoC:

import glob

glob.glob("/lib/python3.10/*")

Output:

['/lib/python3.10/asyncio', '/lib/python3.10/collections', '/lib/python3.10/concurrent', '/lib/python3.10/ctypes', '/lib/python3.10/tzdata-2022.1.dist-info', '/lib/python3.10/email', '/lib/python3.10/encodings', '/lib/python3.10/html', '/lib/python3.10/http', '/lib/python3.10/importlib', '/lib/python3.10/json', '/lib/python3.10/logging', '/lib/python3.10/multiprocessing', '/lib/python3.10/pydoc_data', '/lib/python3.10/site-packages', '/lib/python3.10/sqlite3', '/lib/python3.10/tzdata', '/lib/python3.10/unittest', '/lib/python3.10/urllib', '/lib/python3.10/wsgiref', '/lib/python3.10/xml', '/lib/python3.10/xmlrpc', '/lib/python3.10/zoneinfo', '/lib/python3.10/__future__.py', '/lib/python3.10/__phello__.foo.py', '/lib/python3.10/_aix_support.py', '/lib/python3.10/_bootsubprocess.py', '/lib/python3.10/_collections_abc.py', '/lib/python3.10/_compat_pickle.py', '/lib/python3.10/_compression.py', '/lib/python3.10/_markupbase.py', '/lib/python3.10/_py_abc.py', '/lib/python3.10/_pydecimal.py', '/lib/python3.10/_pyio.py', '/lib/python3.10/_sitebuiltins.py', '/lib/python3.10/_strptime.py', '/lib/python3.10/_threading_local.py', '/lib/python3.10/_weakrefset.py', '/lib/python3.10/abc.py', '/lib/python3.10/aifc.py', '/lib/python3.10/antigravity.py', '/lib/python3.10/argparse.py', '/lib/python3.10/ast.py', '/lib/python3.10/asynchat.py', '/lib/python3.10/asyncore.py', '/lib/python3.10/base64.py', '/lib/python3.10/bdb.py', '/lib/python3.10/binhex.py', '/lib/python3.10/bisect.py', '/lib/python3.10/bz2.py', '/lib/python3.10/cProfile.py', '/lib/python3.10/calendar.py', '/lib/python3.10/cgi.py', '/lib/python3.10/cgitb.py', '/lib/python3.10/chunk.py', '/lib/python3.10/cmd.py', '/lib/python3.10/code.py', '/lib/python3.10/codecs.py', '/lib/python3.10/codeop.py', '/lib/python3.10/colorsys.py', '/lib/python3.10/compileall.py', '/lib/python3.10/configparser.py', '/lib/python3.10/contextlib.py', '/lib/python3.10/contextvars.py', '/lib/python3.10/copy.py', '/lib/python3.10/copyreg.py', '/lib/python3.10/crypt.py', '/lib/python3.10/csv.py', '/lib/python3.10/dataclasses.py', '/lib/python3.10/datetime.py', '/lib/python3.10/decimal.py', '/lib/python3.10/difflib.py', '/lib/python3.10/dis.py', '/lib/python3.10/doctest.py', '/lib/python3.10/enum.py', '/lib/python3.10/filecmp.py', '/lib/python3.10/fileinput.py', '/lib/python3.10/fnmatch.py', '/lib/python3.10/fractions.py', '/lib/python3.10/ftplib.py', '/lib/python3.10/functools.py', '/lib/python3.10/genericpath.py', '/lib/python3.10/getopt.py', '/lib/python3.10/getpass.py', '/lib/python3.10/gettext.py', '/lib/python3.10/glob.py', '/lib/python3.10/graphlib.py', '/lib/python3.10/gzip.py', '/lib/python3.10/hashlib.py', '/lib/python3.10/heapq.py', '/lib/python3.10/hmac.py', '/lib/python3.10/imaplib.py', '/lib/python3.10/imghdr.py', '/lib/python3.10/imp.py', '/lib/python3.10/inspect.py', '/lib/python3.10/io.py', '/lib/python3.10/ipaddress.py', '/lib/python3.10/keyword.py', '/lib/python3.10/linecache.py', '/lib/python3.10/locale.py', '/lib/python3.10/lzma.py', '/lib/python3.10/mailbox.py', '/lib/python3.10/mailcap.py', '/lib/python3.10/mimetypes.py', '/lib/python3.10/modulefinder.py', '/lib/python3.10/netrc.py', '/lib/python3.10/nntplib.py', '/lib/python3.10/ntpath.py', '/lib/python3.10/nturl2path.py', '/lib/python3.10/numbers.py', '/lib/python3.10/opcode.py', '/lib/python3.10/operator.py', '/lib/python3.10/optparse.py', '/lib/python3.10/os.py', '/lib/python3.10/pathlib.py', '/lib/python3.10/pdb.py', '/lib/python3.10/pickle.py', '/lib/python3.10/pickletools.py', '/lib/python3.10/pipes.py', '/lib/python3.10/pkgutil.py', '/lib/python3.10/platform.py', '/lib/python3.10/plistlib.py', '/lib/python3.10/poplib.py', '/lib/python3.10/posixpath.py', '/lib/python3.10/pprint.py', '/lib/python3.10/profile.py', '/lib/python3.10/pstats.py', '/lib/python3.10/pty.py', '/lib/python3.10/py_compile.py', '/lib/python3.10/pyclbr.py', '/lib/python3.10/pydoc.py', '/lib/python3.10/queue.py', '/lib/python3.10/quopri.py', '/lib/python3.10/random.py', '/lib/python3.10/re.py', '/lib/python3.10/reprlib.py', '/lib/python3.10/rlcompleter.py', '/lib/python3.10/runpy.py', '/lib/python3.10/sched.py', '/lib/python3.10/secrets.py', '/lib/python3.10/selectors.py', '/lib/python3.10/shelve.py', '/lib/python3.10/shlex.py', '/lib/python3.10/shutil.py', '/lib/python3.10/signal.py', '/lib/python3.10/site.py', '/lib/python3.10/smtpd.py', '/lib/python3.10/smtplib.py', '/lib/python3.10/sndhdr.py', '/lib/python3.10/socket.py', '/lib/python3.10/sre_parse.py', '/lib/python3.10/socketserver.py', '/lib/python3.10/sre_compile.py', '/lib/python3.10/sre_constants.py', '/lib/python3.10/ssl.py', '/lib/python3.10/stat.py', '/lib/python3.10/statistics.py', '/lib/python3.10/string.py', '/lib/python3.10/stringprep.py', '/lib/python3.10/struct.py', '/lib/python3.10/subprocess.py', '/lib/python3.10/sunau.py', '/lib/python3.10/symtable.py', '/lib/python3.10/sysconfig.py', '/lib/python3.10/tabnanny.py', '/lib/python3.10/tarfile.py', '/lib/python3.10/telnetlib.py', '/lib/python3.10/tempfile.py', '/lib/python3.10/textwrap.py', '/lib/python3.10/this.py', '/lib/python3.10/threading.py', '/lib/python3.10/timeit.py', '/lib/python3.10/token.py', '/lib/python3.10/tokenize.py', '/lib/python3.10/trace.py', '/lib/python3.10/traceback.py', '/lib/python3.10/tracemalloc.py', '/lib/python3.10/tty.py', '/lib/python3.10/types.py', '/lib/python3.10/typing.py', '/lib/python3.10/uu.py', '/lib/python3.10/uuid.py', '/lib/python3.10/warnings.py', '/lib/python3.10/wave.py', '/lib/python3.10/weakref.py', '/lib/python3.10/xdrlib.py', '/lib/python3.10/zipapp.py', '/lib/python3.10/zipfile.py', '/lib/python3.10/zipimport.py', '/lib/python3.10/LICENSE.txt', '/lib/python3.10/_sysconfigdata__emscripten_wasm32-emscripten.py', '/lib/python3.10/webbrowser.py', '/lib/python3.10/distutils']

Screenshot:

lib py

Browsing ‘/dev/’

PoC:

import glob

glob.glob("/dev/*")

Output:

['/dev/null', '/dev/tty', '/dev/tty1', '/dev/random', '/dev/urandom', '/dev/shm', '/dev/stdin', '/dev/stdout', '/dev/stderr']

Screenshot:

dev

Vulnerability 2: Arbitrary File Creation

We can create and read arbitrary files in the Emscripten filesystem directories.

Proof of Concept 1

PoC showing creating a file in /tmp directory - /tmp/testfile

import glob

with open("/tmp/testfile.txt", "w") as f:
	f.write("PoC Written By Rizal aka UB3RSiCK")
	f.close()

glob.glob("/tmp/*")

Output:

['/tmp/testfile.txt']

Screenshot:

temp file create

Proof of Concept 2

Create a file in /tmp/testfile.txt with content PoC Written By Rizal aka UB3RSiCK, verifies the file creation, reads the file content and displays within a div named “SampleElement”

<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>
import glob

with open("/tmp/testfile.txt", "w") as f:
	f.write("PoC Written By Rizal aka UB3RSiCK")
	f.close()

glob.glob("/tmp/*")

content = ''.join(glob.glob("/tmp/*"))

with open("/tmp/testfile.txt", "r") as f:
	content = content + " : " +  f.read()
	f.close()

pyscript.write("SampleElement", content)

		</py-script> </body>
</html>

Output:

/tmp/testfile.txt : PoC Written By Rizal aka UB3RSiCK

Screenshot:

temp file create read

Proof of Concept 3

Creating a python module in /lib/python3.10 and executing arbitrary python code.

<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>
import glob

with open("/lib/python3.10/rizalmodule.py", "w") as f:
	f.write("print('Arbitrary Python Module Creation')")
	f.close()

glob.glob("/lib/python3.10/rizalmodule.*")
		</py-script> </body>
</html>

Output:

['/lib/python3.10/rizalmodule.py']

Screenshot:

py module create

Importing the created module to execute code within.

<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>
import glob

with open("/lib/python3.10/rizalmodule.py", "w") as f:
	f.write("print('Arbitrary Python Module Creation')")
	f.close()

import rizalmodule

		</py-script> </body>
</html>

Output:

Arbitrary Python Module Creation

Screenshot:

py module create execute