
SQLite 3.53.4 - Hoch - Lokal - UAF - rtree last_insert_rowid 0 node hash
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · sqlite-rtree-rowid0
Klasse: UAF Reichweite: Lokal
SQLite 3.53.4 (b09c88c14082339b66c7b7158d609a771e64ca69, SOURCE_ID 2026-07-24 19:02:57 bf7c7f30031888f4e796e429ab3978879485813aaca6f641c7b33e4e09459bcc) - Hwaci
nodeWrite hasht einen neuen rtree-Knoten mit iNode==0, wenn sqlite3_last_insert_rowid() nach einem INSERT in %_node, das dennoch OK meldet, 0 bleibt. nodeHashDelete überspringt 0. nodeRelease nach SplitNode gibt den RtreeNode frei, während der Hash ihn noch hält. Ein späteres nodeHashLookup ist eine Heap-Use-after-free. Dasselbe nodeWrite liegt auf dem geopoly-Pfad.
Kein nachgewiesenes RCE. ASan READ in nodeHashLookup. Der spätere branch-3.53-Fix ist SQLITE_CORRUPT_VTAB, wenn die zugewiesene Knotennummer noch 0 ist. Diese Prüfung fehlt in diesem GA.
| ID | noch keine CVE |
| Klasse | UAF (Heap-Use-after-free eines RtreeNode; kein nachgewiesenes RCE) |
| Reichweite | Lokal (nicht vertrauenswürdiges SQL oder nicht vertrauenswürdige .db auf einer rtree/geopoly-Tabelle) |
| CWE | CWE-416 |
| CVSS | Hoch: 8.1 CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H |
| Produkt | SQLite rtree / geopoly (SQLITE_ENABLE_RTREE) |
| Betroffen | 3.53.4 (b09c88c) |
| Auth | Angreifer-SQL oder ein eingeschleuster Shadow-Table-Trigger / WITHOUT ROWID %_node |
| Lizenz | GNU Affero GPL v3.0 |
| Lab | nur 127.0.0.1:18890. ASan-Nachweis. Keine Exploit-Payload. |
Auf einer rtree- oder geopoly-Tabelle dafür sorgen, dass ein Knoten-INSERT OK meldet, während last_insert_rowid() 0 bleibt. Dokumentierte Wege: %_node durch WITHOUT ROWID ersetzen oder BEFORE INSERT RAISE(IGNORE) auf der Shadow-Tabelle platzieren und dann neu öffnen, damit die letzte rowid der Verbindung 0 ist. INSERT bis ein Knoten-Split erfolgt. Das rechte Geschwister wird als Knoten 0 gehasht, freigegeben und bleibt auffindbar.
Der Host-Prozess verwendet dann einen freigegebenen RtreeNode. Das Lab ist AddressSanitizer heap-use-after-free. Kontrolle über den Instruction Pointer ist nicht in diesem Paket. Der Absturz schon.
SQLITE_VTAB_INNOCUOUS ist gesetzt, daher läuft rtree auch mit trusted_schema off. Der Standard-Compile setzt SQLITE_DEFAULT_DEFENSIVE nicht. Ein vorab eingeschleuster Trigger in einer nicht vertrauenswürdigen DB-Datei läuft weiterhin, wenn rtrees eigenes pWriteNode-INSERT ausgeführt wird.
Zwanzig Source-Hunts von https://github.com/sqlite/sqlite Tag https://github.com/sqlite/sqlite/tree/version-3.53.4. Der kanonische Tree ist https://sqlite.org/src. GitHub ist ein Fossil-Mirror. 3.54.0 ist noch ein Entwurf. Dieser Überrest ist typisch für das zusätzliche -DSQLITE_ENABLE_RTREE (offizieller empfohlener Compile, Windows nmake OPTS, autoconf-Fallback und fuzzcheck setzen es alle).
if( pNode->iNode==0 && rc==SQLITE_OK ){
pNode->iNode = sqlite3_last_insert_rowid(pRtree->db);
nodeHashInsert(pRtree, pNode);
}
static void nodeHashDelete(Rtree *pRtree, RtreeNode *pNode){
RtreeNode **pp;
if( pNode->iNode!=0 ){
pp = &pRtree->aHash[nodeHash(pNode->iNode)];
for( ; (*pp)!=pNode; pp = &(*pp)->pNext){ assert(*pp); }
*pp = pNode->pNext;
pNode->pNext = 0;
}
}
nodeAcquire schlägt im Hash nach, bevor iNode<=0 abgelehnt wird. Ein Treffer auf dem freigegebenen Null-Eintrag ist die UAF. Ein Fehlschlag bei iNode<=0 ist SQLITE_CORRUPT_VTAB, was ein anderer Pfad ist.
Bereits festgehaltene Irrwege: %_rowid last_insert_rowid 0 für diesen Bug halten (das weist der Zelle rowid 0 zu, nicht die Hash-UAF); SQLITE_CORRUPT_VTAB bei diesem Pin erwarten (das ist der spätere Fix); 50 Zellen füllen, sodass kein Split erfolgt; den Trigger platzieren und nicht neu öffnen; Lookaside, das die Freigabe vor ASan verbirgt; den Absturz RCE nennen.
cd lab
./run.sh
Amalgamation 3.53.4 mit -DSQLITE_ENABLE_RTREE=1 und -fsanitize=address. Vendor rtreeI.test 2.0-2.2: 51 Zeilen füllen (RTREE_MAXCELLS), BEFORE INSERT RAISE(IGNORE) auf %_node, last_insert_rowid()==0 neu öffnen, die 52. Zeile INSERTen (Split), dann SELECT *. Kontroll-Split ohne den Trigger ist OK. Bindung an 127.0.0.1:18890. Harness ist lab/harness.c.
control-split=ok
reopen-last-rowid=0
witness-before-split=SQLITE-RTREE-ROWID0-WITNESS
ERROR: AddressSanitizer: heap-use-after-free
READ of size 8 ... nodeHashLookup
freed by ... nodeRelease / SplitNode
SUCCESS sqlite-rtree-rowid0 how=asan-uaf-nodehash SQLITE-RTREE-ROWID0-WITNESS
Wenn nach sqlite3_last_insert_rowid() iNode noch 0 ist, SQLITE_CORRUPT_VTAB zurückgeben und kein nodeHashInsert ausführen. branch-3.53 11ce330557 / Git 63b6d705 (2026-09-24) macht das. Der Test erwartet "database disk image is malformed". Dieses GA nicht.
b09c88c14082339b66c7b7158d609a771e64ca69)2026-07-24 19:02:57 bf7c7f30031888f4e796e429ab3978879485813aaca6f641c7b33e4e09459bccnodeWrite, nodeHashDelete, nodeRelease, nodeAcquire, SplitNode11ce330557 (2026-09-24, Claude / Ada Logics)