VYPR
High severityOSV Advisory· Published Oct 22, 2025· Updated Apr 15, 2026

CVE-2025-62611

CVE-2025-62611

Description

aiomysql is a library for accessing a MySQL database from the asyncio. Prior to version 0.3.0, the client-side settings are not checked before sending local files to MySQL server, which allows obtaining arbitrary files from the client using a rogue server. It is possible to create a rogue MySQL server that emulates authorization, ignores client flags and requests arbitrary files from the client by sending a LOAD_LOCAL instruction packet. This issue has been patched in version 0.3.0.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
aiomysqlPyPI
< 0.3.00.3.0

Affected products

1

Patches

1
32c4520dae37

Merge pull request #1044 from Nothing4You/local-infile

https://github.com/aio-libs/aiomysqlRichard SchwabOct 21, 2025via ghsa
4 files changed · +51 4
  • aiomysql/connection.py+6 1 modified
    @@ -245,7 +245,8 @@ def __init__(self, host="localhost", user=None, password="",
     
             self._encoding = charset_by_name(self._charset).encoding
     
    -        if local_infile:
    +        self._local_infile = bool(local_infile)
    +        if self._local_infile:
                 client_flag |= CLIENT.LOCAL_FILES
     
             client_flag |= CLIENT.CAPABILITIES
    @@ -1203,6 +1204,10 @@ def _read_ok_packet(self, first_packet):
             self.has_next = ok_packet.has_next
     
         async def _read_load_local_packet(self, first_packet):
    +        if not self.connection._local_infile:
    +            raise RuntimeError(
    +                "**WARN**: Received LOAD_LOCAL packet but local_infile option is false."
    +            )
             load_packet = LoadLocalPacketWrapper(first_packet)
             sender = LoadLocalFile(load_packet.filename, self.connection)
             try:
    
  • CHANGES.txt+5 0 modified
    @@ -19,6 +19,11 @@ next (unreleased)
     * | Bump setuptools to >=80, setuptools-scm to  >=7, <10.
       | setuptools-scm must be at least 9.2.0 for consistent hash lengths of non-release builds.
     
    +* | Properly check whether loading of local files is enabled #1044
    +  | Loading local data now requires using the `local_infile` parameter, passing just the client flag through `client_flag` is no longer supported.
    +  | Fixes `GHSA-r397-ff8c-wv2g <https://github.com/aio-libs/aiomysql/security/advisories/GHSA-r397-ff8c-wv2g>`_
    +  | Thanks to @KonstantAnxiety for reporting this.
    +
     0.2.0 (2023-06-11)
     ^^^^^^^^^^^^^^^^^^
     
    
  • docs/connection.rst+8 3 modified
    @@ -46,8 +46,8 @@ Example::
                 client_flag=0, cursorclass=Cursor, init_command=None,
                 connect_timeout=None, read_default_group=None,
                 autocommit=False, echo=False
    -            ssl=None, auth_plugin='', program_name='',
    -            server_public_key=None, loop=None)
    +            local_infile=False, loop=None, ssl=None, auth_plugin='',
    +            program_name='', server_public_key=None)
     
         A :ref:`coroutine <coroutine>` that connects to MySQL.
     
    @@ -71,7 +71,8 @@ Example::
             See `pymysql.converters`.
         :param use_unicode: whether or not to default to unicode strings.
         :param  client_flag: custom flags to send to MySQL. Find
    -        potential values in `pymysql.constants.CLIENT`.
    +        potential values in `pymysql.constants.CLIENT`. Refer to the
    +        `local_infile` parameter for enabling loading of local data.
         :param cursorclass: custom cursor class to use.
         :param str init_command: initial SQL statement to run when connection is
             established.
    @@ -81,6 +82,10 @@ Example::
             file.
         :param autocommit: Autocommit mode. None means use server default.
             (default: ``False``)
    +    :param local_infile: Boolean to enable the use of `LOAD DATA LOCAL`
    +        command. This also enables the corresponding `client_flag`. aiomysql
    +        does not perform any validation of files requested by the server. Do
    +        not use this with untrusted servers. (default: ``False``)
         :param ssl: Optional SSL Context to force SSL
         :param auth_plugin: String to manually specify the authentication
             plugin to use, i.e you will want to use mysql_clear_password
    
  • tests/test_load_local.py+32 0 modified
    @@ -2,7 +2,9 @@
     import os
     from unittest.mock import patch, MagicMock
     
    +import aiomysql
     import pytest
    +from pymysql.constants import CLIENT
     from pymysql.err import OperationalError
     
     
    @@ -81,3 +83,33 @@ async def test_load_warnings(cursor, table_local_file):
         with warnings.catch_warnings(record=True) as w:
             await cursor.execute(sql)
         assert "Incorrect integer value" in str(w[-1].message)
    +
    +
    +@pytest.mark.run_loop
    +async def test_load_local_disabled(mysql_params, table_local_file):
    +    # By setting the client flag, the server will be informed that we support
    +    # loading local files. This validates that the client side check catches
    +    # the server attempting to read files from us without having this
    +    # explicitly enabled on the connection. The local_infile parameter sets
    +    # the client flag, but not the other way round.
    +    params = mysql_params.copy()
    +    params["local_infile"] = False
    +    if "client_flag" in params:
    +        params["client_flag"] |= CLIENT.LOCAL_FILES
    +    else:
    +        params["client_flag"] = CLIENT.LOCAL_FILES
    +
    +    async with aiomysql.connect(**params) as conn:
    +        async with conn.cursor() as cursor:
    +            # Test load local infile with a valid file
    +            filename = os.path.join(os.path.dirname(os.path.realpath(__file__)),
    +                                    'fixtures',
    +                                    'load_local_data.txt')
    +            with pytest.raises(
    +                RuntimeError,
    +                match="Received LOAD_LOCAL packet but local_infile option is false",
    +            ):
    +                await cursor.execute(
    +                    ("LOAD DATA LOCAL INFILE '{0}' INTO TABLE " +
    +                     "test_load_local FIELDS TERMINATED BY ','").format(filename)
    +                )
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.