
SQLite 3.53.4 - High - Local - UAF - rtree last_insert_rowid 0 node hash
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · sqlite-rtree-rowid0
Classe: UAF Reach: Locale
SQLite 3.53.4 (b09c88c14082339b66c7b7158d609a771e64ca69, SOURCE_ID 2026-07-24 19:02:57 bf7c7f30031888f4e796e429ab3978879485813aaca6f641c7b33e4e09459bcc) - Hwaci
nodeWrite calcola l'hash di un nuovo nodo rtree con iNode==0 quando sqlite3_last_insert_rowid() rimane 0 dopo un INSERT in %_node che riporta comunque OK. nodeHashDelete salta 0. nodeRelease dopo SplitNode libera il RtreeNode mentre l'hash lo contiene ancora. Successivamente nodeHashLookup è un heap use-after-free. Lo stesso nodeWrite è sul percorso geopoly.
RCE non dimostrata. ASan READ in nodeHashLookup. La successiva correzione branch-3.53 è SQLITE_CORRUPT_VTAB quando il numero di nodo assegnato è ancora 0. Quel controllo non è presente in questa GA.
| ID | nessuna CVE al momento |
| Classe | UAF (heap use-after-free di RtreeNode; RCE non dimostrata) |
| Reach | Locale (SQL non attendibile o .db non attendibile su una tabella rtree/geopoly) |
| CWE | CWE-416 |
| CVSS | Alto: 8.1 CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H |
| Prodotto | SQLite rtree / geopoly (SQLITE_ENABLE_RTREE) |
| Affetto | 3.53.4 (b09c88c) |
| Auth | SQL dell'attaccante o un trigger piantato sulla shadow table / %_node WITHOUT ROWID |
| Licenza | GNU Affero GPL v3.0 |
| Lab | solo 127.0.0.1:18890. Testimone ASan. Nessun payload di exploit. |
Su una tabella rtree o geopoly, fare in modo che l'INSERT del nodo riporti OK mentre last_insert_rowid() rimane 0. Modi documentati: sostituire %_node con WITHOUT ROWID, oppure piantare BEFORE INSERT RAISE(IGNORE) sulla shadow table, poi riaprire in modo che l'ultimo rowid della connessione sia 0. Eseguire INSERT fino a uno split del nodo. Il sibling destro viene sottoposto a hash come nodo 0, rilasciato e ancora trovabile.
Il processo host utilizza quindi un RtreeNode liberato. Il lab è AddressSanitizer heap-use-after-free. Il controllo dell'instruction pointer non è in questo pacchetto. Il crash sì.
SQLITE_VTAB_INNOCUOUS è impostato, quindi rtree viene comunque eseguito con trusted_schema disattivato. La compilazione predefinita non imposta SQLITE_DEFAULT_DEFENSIVE. Un trigger pre-piantato in un file DB non attendibile viene comunque eseguito quando l'INSERT di pWriteNode di rtree viene eseguito.
Venti ricerche nel sorgente di https://github.com/sqlite/sqlite tag https://github.com/sqlite/sqlite/tree/version-3.53.4. L'albero canonico è https://sqlite.org/src. GitHub è un mirror Fossil. 3.54.0 è ancora una bozza. Questo residuo è tipico dell'extra -DSQLITE_ENABLE_RTREE (compilazione ufficiale raccomandata, OPTS di Windows nmake, fallback autoconf e fuzzcheck lo impostano tutti).
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 cerca nell'hash prima di rifiutare iNode<=0. Un hit sulla voce zero liberata è la UAF. Un miss su iNode<=0 è SQLITE_CORRUPT_VTAB, che è un percorso diverso.
Vicoli ciechi già registrati: trattare %_rowid last_insert_rowid 0 come questo bug (quello assegna cell rowid 0, non la hash UAF); aspettarsi SQLITE_CORRUPT_VTAB su questo pin (quella è la correzione successiva); riempire 50 celle in modo che non ci sia split; piantare il trigger e non riaprire; lookaside che nasconde la free ad ASan; chiamare il crash RCE.
cd lab
./run.sh
Amalgamation 3.53.4 con -DSQLITE_ENABLE_RTREE=1 e -fsanitize=address. Vendor rtreeI.test 2.0-2.2: riempire 51 righe (RTREE_MAXCELLS), BEFORE INSERT RAISE(IGNORE) su %_node, riaprire last_insert_rowid()==0, INSERT della 52ª riga (split), poi SELECT *. Lo split di controllo senza il trigger è OK. Bind 127.0.0.1:18890. L'harness è 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
Dopo sqlite3_last_insert_rowid(), se iNode è ancora 0, restituire SQLITE_CORRUPT_VTAB e non eseguire nodeHashInsert. branch-3.53 11ce330557 / Git 63b6d705 (2026-09-24) fa questo. Il test si aspetta "database disk image is malformed". Questa GA no.
b09c88c14082339b66c7b7158d609a771e64ca69)2026-07-24 19:02:57 bf7c7f30031888f4e796e429ab3978879485813aaca6f641c7b33e4e09459bccnodeWrite, nodeHashDelete, nodeRelease, nodeAcquire, SplitNode11ce330557 (2026-09-24, Claude / Ada Logics)