abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · sqlite-rtree-rowid0
类别: UAF 可达性: 本地
SQLite 3.53.4(b09c88c14082339b66c7b7158d609a771e64ca69,SOURCE_ID 2026-07-24 19:02:57 bf7c7f30031888f4e796e429ab3978879485813aaca6f641c7b33e4e09459bcc)- Hwaci
当向 %_node 执行 INSERT 后 sqlite3_last_insert_rowid() 仍为 0,但该 INSERT 仍报告 OK 时,nodeWrite 会以 iNode==0 对新 rtree 节点进行哈希。nodeHashDelete 会跳过 0。SplitNode 之后的 nodeRelease 释放了 RtreeNode,而哈希表仍持有它。之后的 nodeHashLookup 就是一次堆上的 use-after-free。同样的 nodeWrite 也存在于 geopoly 路径上。
未演示 RCE。ASan 在 nodeHashLookup 中报 READ。后续 branch-3.53 的修复是:当分配的节点号仍为 0 时返回 SQLITE_CORRUPT_VTAB。该检查不在本 GA 版本上。
| ID | 暂无 CVE |
| 类别 | UAF(RtreeNode 的堆 use-after-free;未演示 RCE) |
| 可达性 | 本地(在 rtree/geopoly 表上执行不可信 SQL 或使用不可信 .db) |
| CWE | CWE-416 |
| CVSS | High: 8.1 CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H |
| 产品 | SQLite rtree / geopoly(SQLITE_ENABLE_RTREE) |
| 受影响版本 | 3.53.4(b09c88c) |
| 认证 | 攻击者 SQL 或预置的 shadow-table 触发器 / WITHOUT ROWID %_node |
| 许可证 | GNU Affero GPL v3.0 |
| 实验环境 | 仅 127.0.0.1:18890。ASan 见证。无利用载荷。 |
在 rtree 或 geopoly 表上,使节点 INSERT 报告 OK,同时 last_insert_rowid() 保持为 0。已记录的方法:将 %_node 替换为 WITHOUT ROWID,或在 shadow 表上预置 BEFORE INSERT RAISE(IGNORE),然后重新打开,使连接的 last rowid 为 0。持续 INSERT 直到发生节点分裂。右兄弟节点被哈希为节点 0,被释放,且仍可被找到。
宿主进程随后使用一个已释放的 RtreeNode。实验环境为 AddressSanitizer heap-use-after-free。本包中不包含指令指针控制。崩溃是存在的。
SQLITE_VTAB_INNOCUOUS 已设置,因此 rtree 在 trusted_schema 关闭时仍会运行。默认编译不会设置 SQLITE_DEFAULT_DEFENSIVE。当 rtree 自身的 pWriteNode INSERT 执行时,不可信 DB 文件中预置的触发器仍会运行。
对 https://github.com/sqlite/sqlite 标签 https://github.com/sqlite/sqlite/tree/version-3.53.4 进行了二十次源码搜寻。规范树是 https://sqlite.org/src。GitHub 是 Fossil 镜像。3.54.0 仍是草稿。这个遗留问题是典型的额外 -DSQLITE_ENABLE_RTREE(官方推荐编译、Windows nmake OPTS、autoconf 回退以及 fuzzcheck 都会设置它)。
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 在拒绝 iNode<=0 之前先查找哈希表。命中已释放的零条目就是 UAF。未命中 iNode<=0 则是 SQLITE_CORRUPT_VTAB,那是另一条路径。
已记录的弯路:将 %_rowid 的 last_insert_rowid 为 0 当作此 bug(那会分配 cell rowid 0,而不是哈希 UAF);期望在此 pin 上出现 SQLITE_CORRUPT_VTAB(那是后续修复);填充 50 个 cell 使其不发生分裂;预置触发器后未重新打开;lookaside 使 ASan 看不到释放;将崩溃称为 RCE。
cd lab
./run.sh
Amalgamation 3.53.4,带 -DSQLITE_ENABLE_RTREE=1 和 -fsanitize=address。Vendor rtreeI.test 2.0-2.2:填充 51 行(RTREE_MAXCELLS),在 %_node 上设置 BEFORE INSERT RAISE(IGNORE),重新打开使 last_insert_rowid()==0,INSERT 第 52 行(分裂),然后 SELECT *。无触发器的对照分裂为 OK。绑定 127.0.0.1:18890。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
在 sqlite3_last_insert_rowid() 之后,如果 iNode 仍为 0,则返回 SQLITE_CORRUPT_VTAB,并且不执行 nodeHashInsert。branch-3.53 11ce330557 / Git 63b6d705(2026-09-24)就是这样做的。测试期望 "database disk image is malformed"。本 GA 版本没有。
2026-07-24 19:02:57 bf7c7f30031888f4e796e429ab3978879485813aaca6f641c7b33e4e09459bccnodeWrite、nodeHashDelete、nodeRelease、nodeAcquire、SplitNode11ce330557(2026-09-24,Claude / Ada Logics)