0%

CVE-2020-13630分析

CVE-2020-13630分析文档

原理:

  • 在sqlite3的fts3扩展中,fts3EvalNextRow中存在一个Use-After-Free漏洞,与Snippet函数有关。

fts3扩展是sqlite为了支持快速全文查询而加入的一个扩展,其官方文档里面列出了使用常规查询方法和fts查询方法的耗时对比,如下图:

image-20201031032907904

而Snippet函数是用来支持fts3扩展全文查询的一个辅助函数,返回的是用MATCH关键字匹配的内容的代码片段(ftsSegment),另外还有两个类似的函数offsets,matchinfo,这三个辅助函数只在使用FTS表的全文索引的SELECT语句中有用。如果在使用“rowid查询”或“线性扫描”策略的SELECT中使用,那么代码段和偏移量都返回一个空字符串,而matchinfo函数返回一个blob值,大小为零字节。所以使用Snippet函数的一个例子可以这样:

1
2
3
create virtual table t0 use fts3();
insert into t0 values ("abcd");
select snippet(t0) from t0 where t0 MATCH 'cd';

而在漏洞发现者给出的poc中也看到了相似的查询:

1
2
3
4
5
6
7
8
9
DROP TABLE IF EXISTS t0_content;
DROP TABLE IF EXISTS t0_messageize;
DROP TABLE IF EXISTS t0_segdir;
DROP TABLE IF EXISTS t0_segments;
DROP TABLE IF EXISTS t0_stat;
DROP TABLE IF EXISTS t0;
CREATE VIRTUAL TABLE t0 USING fts3(col0 INTEGER PRIMARY KEY,col1 VARCHAR(8),col2 BINARY,col3 BINARY);
INSERT INTO t0 VALUES (1, '1234','aaaa','bbbb');
SELECT snippet(t0 ) FROM t0 WHERE t0 MATCH x'0a4d4d4d4d320a4f52d70a310a310a4e4541520a0a31f6ce0a4f520a0a310a310a310a4f520a75fc2a242424' ;

POC中16进制匹配的值的ASCII表示如下(将换行符0x0a替换成了空格0x20):

image-20201031034434034

这里虽然不知道是怎么个原理。但漏洞触发的调用过程可以分析如下:

在执行具有snippet函数的fts全文查询的时候,会调用sqlite3Fts3SegReaderNew函数来为将要匹配到的内容分配一个空间来存储,这个对象叫做Fts3SegReader:

image-20201031074958842

然后sqlite会调用fts3BestSnippet来寻找最合适匹配的Snippet片段,在fts3BestSnippet调用了fts3SnippetFindPositions,然后又调用了sqlite3Fts3EvalPhrasePoslist来寻找具体的位置,在sqlite3Fts3EvalPhrasePoslist中就调用了漏洞函数fts3EvalNextRow:

image-20201031080108462

fts3EvalNextRow中会根据传入参数的Fts3Expr.eType(pNear)来进行不同的处理,在处理eType为FTSQUERY_AND的时候,如果当前pNear的左右子节点都被推迟解析了(通过bDeferred判断)的话,则会判断当前是否已经到了结束的地方(bEof),如果当前节点的左右子节点有一个结束且当前节点为FTSQOERY_NEAR的话,则将其左右子节点的文档列表(doclist)内容置为0,这代表了这里就是结束的地方了,所以应该确保其左右子节点的bEof都置为1了,但是在SQLite的版本(0d69f76f0865f962)之前,没有做此处理,这就引发了后续的UAF漏洞:

image-20201031082052006

查找到匹配的内容后就会调用fts3SegReaderCursorFreeFree掉之前分配的Fts3SegReader对象。上面的搜寻过程会将近似匹配片段存到一个SnippetIter中,之后需要从这一个候选的片段中找到一个最佳的,会调用fts3SnippetNextCandidate进行迭代,在该函数里面会调用fts3SnippetAdvance继续遍历下一个Snippet:

image-20201031085050339

然后在之前可能没有正确设置结束的地方的时候,fts3SnippetAdvance遍历会出现访问之前已经释放的地址的内容的情况,造成UAF漏洞:

image-20201031090735722

复现过程:

编译的SQLite版本:3.31.1

configure命令:

1
TCC+="-DSQLITE_ENABLE_VFSTRACE -DSQLITE_ENABLE_STAT3 -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_BATCH_ATOMIC_WRITE -DHAVE_READLINE -DSQLITE_ENABLE_FTS3_PARENTHESIS -DTHREADSAFE=0 -DSQLITE_ENABLE_DESERIALIZE -DSQLITE_ENABLE_DBSTAT_VTAB"  ../SQLite-3bfa9cc9/configure

修改Makefile加上ASAN参数:-fsanitize=address -fno-omit-frame-pointer

根据上面描述的,在sqlite3.c:176720(Malloc的地方), sqlite3:178133(Free的地方), sqlite3:181223(Use again的地方)处下断点,执行poc进行调试:

1. Malloc

首先会在Malloc处申请多个Fts3SegReader,每个大小为0x180:

image-20201031124107520

image-20201031124255316

image-20201031124423282

image-20201031125125861

一共有9个Fts3SegReader.

2. Free

分配,进行匹配完之后,一开始会Free掉最后分配的Fts3SegReader对象,然后把匹配到的值打印出来,最后再Free掉剩下的Fts3SegReader对象:

image-20201031125414512

3. Use

93ce是之前分配的Fts3SegReader里面的内容,属于0x6120000094c0这个对象里的内容:

image-20201031125830659

查看该对象的内容:

image-20201031140547018

而这一部分属于Fts3SegReader的aDoclist。然后继续执行,发现这个地方的内容在不同的Fts3SegReader对象中的内容都一样:

image-20201031134531348

image-20201031134551909

都是“\001\003\004\003\006\003\003”

继续执行,返回匹配到的值:

image-20201031140233123

最后Free剩下的Fts3SegReader:

image-20201031140952530

image-20201031141100982

一直执行,没有发现ASAN检测出的错误。

image-20201031141323083

确认的问题:

1. Chrome是否有独立的sqlite3文件?

单从安装在Windows上的Chrome版本来说,其安装目录下并没有独立的sqlite3的独立文件,甚至连和有关sqlite3的相关文件都没有。

2. FTS3扩展自行创建的信息表内容:

这些表叫做“shadow table”,是真实的表,用于存储底层数据。

  • t0_content: 会根据创建的虚表的创建而声明,就是虚表的内容。

image-20201031142923543

image-20201031142949495

  • t0_segdir, t0_segments: 这两张表是用于存储全文索引的。从概念上讲,这个索引是一个查找表,它将每个词(word)映射到一组docid值,这些docid值对应于% content表中包含一个或多个词的记录。为了检索包含指定术语的所有文档,FTS模块查询该索引以确定包含该术语的记录的docid值集,然后从% content表检索所需的文档。

image-20201031143457857

  • t0_stat, t0_docsize: 这两个表是使用FTS4创建虚表的时候才创建的表。

image-20201031143942190

  • t0_messageize: 这个表也是没有查到,而且官方文档里面并没有说有这个表。

image-20201031144057813

总结:

整个PoC的调用流程弄清楚了,但是具体的PoC里的那段MATCH的值所代表的的内容是不清楚的,我把那部分加入到PoC中的INSERT语句中才会走到Use(sqlite3.c:178133)的地方。如果是原始的PoC的话,则不会走到这个地方。编译的时候也测试了sqlite3是加入了ASAN编译选项的,但实际执行PoC后确实没有反应。

参考链接:

注:特意输错命令,sqlite3检测到memory leak错误。

image-20201031145922259