SGX_SQLite【源码分析】

SQLite3

请添加图片描述

SQLite3的架构分析可参考这里官网

sqlite3_open() → \rightarrow openDatabase() → \rightarrow sqlite3_initialize()

功能包含Mutex初始化、Malloc初始化(某些全局变量、静态变量的初始化)、Page Cache(SQLite中处于B树和OS硬盘等操作接口之间,用于加速B树到文件介质之间的读写)初始化、Extra额外初始化……

sqlite3_exec()

1.SQLITE_API int sqlite3_exec(  
2.  sqlite3 *db,                /* The database on which the SQL executes */  
3.  const char *zSql,           /* The SQL to be executed */  
4.  sqlite3_callback xCallback, /* Invoke this callback routine */  
5.  void *pArg,                 /* First argument to xCallback() */  
6.  char **pzErrMsg             /* Write error messages here */  
7.){  
8.  int rc = SQLITE_OK;         /* Return code */  
9.  const char *zLeftover;      /* Tail of unprocessed SQL */  
10.  sqlite3_stmt *pStmt = 0;    /* The current SQL statement */  
11.  char **azCols = 0;          /* Names of result columns */  
12.  int callbackIsInit;         /* True if callback data is initialized */  
13.  
14.  if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;  
15.  ...  
16.}  

可以看到具体执行SQL语言之前会调用sqlite3SafetyCheckOk(),代码如下

1./* 
2.** Check to make sure we have a valid db pointer.  This test is not 
3.** foolproof but it does provide some measure of protection against 
4.** misuse of the interface such as passing in db pointers that are 
5.** NULL or which have been previously closed.  If this routine returns 
6.** 1 it means that the db pointer is valid and 0 if it should not be 
7.** dereferenced for any reason.  The calling function should invoke 
8.** SQLITE_MISUSE immediately. 
9.** 
10.** sqlite3SafetyCheckOk() requires that the db pointer be valid for 
11.** use.  sqlite3SafetyCheckSickOrOk() allows a db pointer that failed to 
12.** open properly and is not fit for general use but which can be 
13.** used as an argument to sqlite3_errmsg() or sqlite3_close(). 
14.*/  
15.SQLITE_PRIVATE int sqlite3SafetyCheckOk(sqlite3 *db){  
16.  u32 magic;  
17.  if( db==0 ){  
18.    logBadConnection("NULL");  
19.    return 0;  
20.  }  
21.  magic = db->magic;  
22.  if( magic!=SQLITE_MAGIC_OPEN ){  
23.    if( sqlite3SafetyCheckSickOrOk(db) ){  
24.      testcase( sqlite3GlobalConfig.xLog!=0 );  
25.      logBadConnection("unopened");  
26.    }  
27.    return 0;  
28.  }else{  
29.    return 1;  
30.  }  
31.}  

从注释和代码可以看到,这个函数对db指针进行检查,会检测db指针是否为0或者通过Magic检测db指针是否曾被关掉。

也就是说db检查包括检查NULL、检查MAGIC是否OPEN。

sqlite3_close() → \rightarrow sqlite3Close()

再来看一下sqlite3Close的代码如下:

1.static int sqlite3Close(sqlite3 *db, int forceZombie){  
2.  ...  //一些清理工作
3.  
4.  /* Convert the connection into a zombie and then close it. 
5.  */  
6.  db->magic = SQLITE_MAGIC_ZOMBIE;  
7.  sqlite3LeaveMutexAndCloseZombie(db);  
8.  return SQLITE_OK;  
9.}  

发现他会调用sqlite3LeaveMutexAndCloseZombie()来将对db的连接变成zombie,然后关掉,代码如下

1.SQLITE_PRIVATE void sqlite3LeaveMutexAndCloseZombie(sqlite3 *db){  
2.  ... //数据结构的清理维护  
3.  
4.  /* The temp-database schema is allocated differently from the other schema 
5.  ** objects (using sqliteMalloc() directly, instead of sqlite3BtreeSchema()). 
6.  ** So it needs to be freed here. Todo: Why not roll the temp schema into 
7.  ** the same sqliteMalloc() as the one that allocates the database 
8.  ** structure? 
9.  */  
10.  sqlite3DbFree(db, db->aDb[1].pSchema);  
11.  sqlite3_mutex_leave(db->mutex);  
12.  db->magic = SQLITE_MAGIC_CLOSED;  
13.  sqlite3_mutex_free(db->mutex);  
14.  assert( db->lookaside.nOut==0 );  /* Fails on a lookaside memory leak */  
15.  if( db->lookaside.bMalloced ){  
16.    sqlite3_free(db->lookaside.pStart);  
17.  }  
18.  sqlite3_free(db);  
19.}  

发现代码最后有设置db->magic = SQLITE_MAGIC_CLOSED的操作,然后调用sqlite3_free()。

也就是说,MAGIC先设置为ZOMBIE,最后会设置为CLOSED,并且调用sqlite3_free()。

可能的UAF场景(硕士毕设开题前的杂记,可能有错)

我们已经可以发现db存在未被置NULL的问题,因此我们可以触发一些UAF问题,下面针对绕过MAGIC检查进行攻击。

Ring3的攻击

值得注意,db是处于Enclave内部的指针变量。

(1)Enclave线程A调用顺序乱序,但Enclave线程A、B不要求使用同一个db

Enclave线程A自己错误出现sql_close->sql_exec的调用场景,并且Enclave线程A、B不要求操控同一个db。

Enclave线程(也可以是进程,进程类似)A调用sql_close之后,magic设置为了MAGIC_CLOSED,但是db没有置成NULL。由于Enclave线程B的内存覆写(如对内存0x700地址(Enclave内)malloc并覆写,然后0x700刚好是MAGIC值)等问题导致db->magic虽然之前已经置成MAGIC_CLOSED,但是又被覆写置成了MAGIC_OPEN。导致线程A 执行sql_exec时,check里面的“db==0?”和“db->magic!=MAGIC_OPEN?”都被绕过。

(2)Enclave线程A调用顺序正确,但Enclave线程A、B使用同一个db

Enclave线程A调用sql_open然后正要sql_exec时,Enclave线程B对db进行了sql_close,并且还没来得及设置MAGIC值从MAGIC_OPEN变成MAGIC_ZOMBIE、MAGIC_CLOSED,此时部分的清理工作已经完成(虽然sqlite_free还没执行,sqlite_free晚于设置MAGIC_CLOSED,但是sql_close的很多清理工作已经做了),然后Enclave线程A里sql_exec的check就被绕过,然后往下执行,同时线程B继续完成db剩下的设置MAGIC_CLOSED和其他清理工作。

小结

上述两种本质都是magic状态被操纵导致绕过检查,第一种是通过间接的内存覆写(较难发生),第二种是对db、magic状态的直接改变。

Ring0的攻击

由于SGX的攻击者很可能是Ring0,这种条件下攻击者能力很强。

攻击者可以任意修改ECALL的调用顺序(通过修改不可信的EIP寄存器实现函数粒度)、任意暂停线程A的执行(AsyncShock的做法,清除Page PROT_READ、PROT_EXECUTE(mprotect)等构造异常使Enclave执行AEX从而被暂停,其实SGX-Step等利用异常或中断也提供了暂停原语),因此除了上述的Ring3攻击,Ring0攻击可以提供更多的UAF机会。

(修正:线程进入Enclave,会切换上下文寄存器,RIP寄存器也会切换,使用指定SSA里的RIP,因此无法通过修改不可信世界的RIP来进行代码粒度的控制流劫持,是否有方法篡改可信世界的SSA保存的RIP进行攻击尚不清楚。Mprotect和页表项Flag位和Execute Disable Bit关系猜想就后面)

(3)Enclave线程A、B不要求操控同一个db,攻击者可以恶意的通过不可信EIP篡改函数粒度控制流,攻击者不会任意暂停线程

攻击者让Enclave线程A执行sql_open->sql_close->sql_exec的过程(乱序)。在sql_close完成后,MAGIC变成MAGIC_CLOSED。但是由于Enclave线程B的malloc及内存覆写,将MAGIC_CLOSED变成了MAGIC_OPEN,使得Enclave线程A的sql_exec的check被绕过。

(4) Enclave线程A、B操控同一个db的场景下,攻击者不恶意通过不可信EIP篡改控制流,攻击者可以任意暂停线程

Enclave线程A执行sql_open、sql_exec,并且在线程A sql_exec的check之前暂停,然后启动线程B,使线程B sql_close完成部分清理工作时暂停线程B。此时MAGIC没法设置为MAGIC_CLOSED(虽然sql_free也还没执行,sql_free晚于MAGIC_CLOSED设置)。然后恢复线程A执行,这样就能绕过线程A 的sql_exec的check。然后再暂停线程A,恢复线程B完成MAGIC_CLOSED设置和sql_free工作。然后恢复线程A,就能出现问题。

小结

有了暂停功能可以让UAF的实现非常轻松。以上是以绕过check为目的UAF攻击,想要实现其他控制流劫持应该类似。

以上都是考虑受害者代码中没加设置db=NULL的过程。如果加上db=NULL,在Ring0攻击下,由于攻击者可以任意暂停线程,那么攻击者可以在线程B sql_free(sql_close的最后步骤)和db=NULL之间暂停线程B,然后让线程A sql_exec绕过db==NULL的检查(AsynShock做法,类似于前面讲的在sql_close完成前期的清理工作和MAGIC_CLOSED设置之间进行暂停)。因此,既然Ring0攻击可以任意暂停线程,那么加了db=NULL也不管用。

SGX_SQLite未对数据库文件密封(备忘)

数据库文件打开操作调用栈如下

ocall_open64(const char * filename, int flags, mode_t mode) (\home\leone\文档\SGX_SQLite\App\ocalls.c:42)
Enclave_ocall_open64(void * pms) (\home\leone\文档\SGX_SQLite\App\Enclave_u.c:248)
libsgx_urts.so!do_ocall(const bridge_fn_t bridge, void * ms) (\home\leone\文档\linux-sgx\psw\urts\linux\sig_handler.cpp:251)
libsgx_urts.so!CEnclave::ocall(CEnclave * const this, const unsigned int proc, const sgx_ocall_table_t * ocall_table, void * ms) (\home\leone\文档\linux-sgx\psw\urts\enclave.cpp:440)
libsgx_urts.so!sgx_ocall(const unsigned int proc, const sgx_ocall_table_t * ocall_table, void * ms, CTrustThread * trust_thread) (\home\leone\文档\linux-sgx\psw\urts\routine.cpp:83)
libsgx_urts.so!stack_sticker() (\home\leone\文档\linux-sgx\psw\urts\linux\enter_enclave.S:244)
__morestack() (\home\leone\文档\linux-sgx\sdk\trts\linux\trts_pic.S:435)
__morestack() (\home\leone\文档\linux-sgx\sdk\trts\linux\trts_pic.S:435)
sgx_ocall(const unsigned int index, void * ms) (\home\leone\文档\linux-sgx\sdk\trts\trts_ocall.cpp:64)
ocall_open64(int * retval, const char * filename, int flags, mode_t mode) (\home\leone\文档\SGX_SQLite\Enclave\Enclave_t.c:909)
open64(const char * filename, int flags) (\home\leone\文档\SGX_SQLite\Enclave\ocall_interface.c:36)
posixOpen(const char * zFile, int flags, int mode) (\home\leone\文档\SGX_SQLite\Enclave\sqlite3.c:30223)
robust_open(const char * z, int f, mode_t m) (\home\leone\文档\SGX_SQLite\Enclave\sqlite3.c:30522)
unixOpen(sqlite3_vfs * pVfs, const char * zPath, sqlite3_file * pFile, int flags, int * pOutFlags) (\home\leone\文档\SGX_SQLite\Enclave\sqlite3.c:35686)
sqlite3OsOpen(sqlite3_vfs * pVfs, const char * zPath, sqlite3_file * pFile, int flags, int * pFlagsOut) (\home\leone\文档\SGX_SQLite\Enclave\sqlite3.c:20291)
sqlite3PagerOpen(sqlite3_vfs * pVfs, Pager ** ppPager, const char * zFilename, int nExtra, int flags, int vfsFlags, void (*)(DbPage *) xReinit) (\home\leone\文档\SGX_SQLite\Enclave\sqlite3.c:51710)
sqlite3BtreeOpen(sqlite3_vfs * pVfs, const char * zFilename, sqlite3 * db, Btree ** ppBtree, int flags, int vfsFlags) (\home\leone\文档\SGX_SQLite\Enclave\sqlite3.c:61376)
openDatabase(const char * zFilename, sqlite3 ** ppDb, unsigned int flags, const char * zVfs) (\home\leone\文档\SGX_SQLite\Enclave\sqlite3.c:143430)
sqlite3_open(const char * zFilename, sqlite3 ** ppDb) (\home\leone\文档\SGX_SQLite\Enclave\sqlite3.c:143598)
ecall_opendb(const char * dbname) (\home\leone\文档\SGX_SQLite\Enclave\Enclave.cpp:21)
sgx_ecall_opendb(void * pms) (\home\leone\文档\SGX_SQLite\Enclave\Enclave_t.c:223)
trts_ecall(uint32_t ordinal, void * ms) (\home\leone\文档\linux-sgx\sdk\trts\trts_ecall.cpp:304)
_random_stack_noinline_wrapper<_status_t, unsigned int, void*, int&, void*&>(_status_t (*)(unsigned int, void *) f) (\home\leone\文档\linux-sgx\common\inc\sgx_random_buffers.h:83)
random_stack_advance<2048u, _status_t, unsigned int, void*, int&, void*&>(_status_t (*)(unsigned int, void *) f) (\home\leone\文档\linux-sgx\common\inc\sgx_random_buffers.h:102)
do_ecall(int index, void * ms, void * tcs) (\home\leone\文档\linux-sgx\sdk\trts\trts_ecall.cpp:438)
enter_enclave(int index, void * ms, void * tcs, int cssa) (\home\leone\文档\linux-sgx\sdk\trts\trts_nsp.cpp:96)
enclave_entry() (\home\leone\文档\linux-sgx\sdk\trts\linux\trts_pic.S:181)
libsgx_urts.so!__morestack() (\home\leone\文档\linux-sgx\psw\urts\linux\enter_enclave.S:77)
libsgx_urts.so!do_ecall(const int fn, const void * ocall_table, const void * ms, CTrustThread * trust_thread) (\home\leone\文档\linux-sgx\psw\urts\linux\sig_handler.cpp:242)
libsgx_urts.so!CEnclave::ecall(CEnclave * const this, const int proc, const void * ocall_table, void * ms, const bool is_switchless) (\home\leone\文档\linux-sgx\psw\urts\enclave.cpp:387)
libsgx_urts.so!_sgx_ecall(const sgx_enclave_id_t enclave_id, const int proc, const void * ocall_table, void * ms, const bool is_switchless) (\home\leone\文档\linux-sgx\psw\urts\routine.cpp:55)
libsgx_urts.so!sgx_ecall(const sgx_enclave_id_t enclave_id, const int proc, const void * ocall_table, void * ms) (\home\leone\文档\linux-sgx\psw\urts\routine.cpp:68)
ecall_opendb(sgx_enclave_id_t eid, const char * dbname) (\home\leone\文档\SGX_SQLite\App\Enclave_u.c:387)
main(int argc, char ** argv) (\home\leone\文档\SGX_SQLite\App\App.cpp:50)

数据库文件写

ocall_write(int fd, const void * buf, size_t count) (\home\leone\文档\SGX_SQLite\App\ocalls.c:56)
Enclave_ocall_write(void * pms) (\home\leone\文档\SGX_SQLite\App\Enclave_u.c:280)
libsgx_urts.so!do_ocall(const bridge_fn_t bridge, void * ms) (\home\leone\文档\linux-sgx\psw\urts\linux\sig_handler.cpp:251)
libsgx_urts.so!CEnclave::ocall(CEnclave * const this, const unsigned int proc, const sgx_ocall_table_t * ocall_table, void * ms) (\home\leone\文档\linux-sgx\psw\urts\enclave.cpp:440)
libsgx_urts.so!sgx_ocall(const unsigned int proc, const sgx_ocall_table_t * ocall_table, void * ms, CTrustThread * trust_thread) (\home\leone\文档\linux-sgx\psw\urts\routine.cpp:83)
libsgx_urts.so!stack_sticker() (\home\leone\文档\linux-sgx\psw\urts\linux\enter_enclave.S:244)
__morestack() (\home\leone\文档\linux-sgx\sdk\trts\linux\trts_pic.S:435)
__morestack() (\home\leone\文档\linux-sgx\sdk\trts\linux\trts_pic.S:435)
sgx_ocall(const unsigned int index, void * ms) (\home\leone\文档\linux-sgx\sdk\trts\trts_ocall.cpp:64)
ocall_write(int * retval, int fd, const void * buf, size_t count) (\home\leone\文档\SGX_SQLite\Enclave\Enclave_t.c:1068)
write(int fd, const void * buf, size_t count) (\home\leone\文档\SGX_SQLite\Enclave\ocall_interface.c:252)
seekAndWriteFd(int fd, i64 iOff, const void * pBuf, int nBuf, int * piErrno) (\home\leone\文档\SGX_SQLite\Enclave\sqlite3.c:33147)
seekAndWrite(unixFile * id, i64 offset, const void * pBuf, int cnt) (\home\leone\文档\SGX_SQLite\Enclave\sqlite3.c:33167)
unixWrite(sqlite3_file * id, const void * pBuf, int amt, sqlite3_int64 offset) (\home\leone\文档\SGX_SQLite\Enclave\sqlite3.c:33234)
sqlite3OsWrite(sqlite3_file * id, const void * pBuf, int amt, i64 offset) (\home\leone\文档\SGX_SQLite\Enclave\sqlite3.c:20172)
writeJournalHdr(Pager * pPager) (\home\leone\文档\SGX_SQLite\Enclave\sqlite3.c:48434)
pager_open_journal(Pager * pPager) (\home\leone\文档\SGX_SQLite\Enclave\sqlite3.c:52649)
pager_write(PgHdr * pPg) (\home\leone\文档\SGX_SQLite\Enclave\sqlite3.c:52834)
sqlite3PagerWrite(PgHdr * pPg) (\home\leone\文档\SGX_SQLite\Enclave\sqlite3.c:53007)
newDatabase(BtShared * pBt) (\home\leone\文档\SGX_SQLite\Enclave\sqlite3.c:62172)
sqlite3BtreeBeginTrans(Btree * p, int wrflag) (\home\leone\文档\SGX_SQLite\Enclave\sqlite3.c:62322)
sqlite3VdbeExec(Vdbe * p) (\home\leone\文档\SGX_SQLite\Enclave\sqlite3.c:81529)
sqlite3Step(Vdbe * p) (\home\leone\文档\SGX_SQLite\Enclave\sqlite3.c:76705)
sqlite3_step(sqlite3_stmt * pStmt) (\home\leone\文档\SGX_SQLite\Enclave\sqlite3.c:76766)
sqlite3_exec(sqlite3 * db, const char * zSql, sqlite3_callback xCallback, void * pArg, char ** pzErrMsg) (\home\leone\文档\SGX_SQLite\Enclave\sqlite3.c:111173)
ecall_execute_sql(const char * sql) (\home\leone\文档\SGX_SQLite\Enclave\Enclave.cpp:34)
sgx_ecall_execute_sql(void * pms) (\home\leone\文档\SGX_SQLite\Enclave\Enclave_t.c:270)
trts_ecall(uint32_t ordinal, void * ms) (\home\leone\文档\linux-sgx\sdk\trts\trts_ecall.cpp:304)
_random_stack_noinline_wrapper<_status_t, unsigned int, void*, int&, void*&>(_status_t (*)(unsigned int, void *) f) (\home\leone\文档\linux-sgx\common\inc\sgx_random_buffers.h:83)
random_stack_advance<2048u, _status_t, unsigned int, void*, int&, void*&>(_status_t (*)(unsigned int, void *) f) (\home\leone\文档\linux-sgx\common\inc\sgx_random_buffers.h:102)
do_ecall(int index, void * ms, void * tcs) (\home\leone\文档\linux-sgx\sdk\trts\trts_ecall.cpp:438)
enter_enclave(int index, void * ms, void * tcs, int cssa) (\home\leone\文档\linux-sgx\sdk\trts\trts_nsp.cpp:96)
enclave_entry() (\home\leone\文档\linux-sgx\sdk\trts\linux\trts_pic.S:181)
libsgx_urts.so!__morestack() (\home\leone\文档\linux-sgx\psw\urts\linux\enter_enclave.S:77)
libsgx_urts.so!do_ecall(const int fn, const void * ocall_table, const void * ms, CTrustThread * trust_thread) (\home\leone\文档\linux-sgx\psw\urts\linux\sig_handler.cpp:242)
libsgx_urts.so!CEnclave::ecall(CEnclave * const this, const int proc, const void * ocall_table, void * ms, const bool is_switchless) (\home\leone\文档\linux-sgx\psw\urts\enclave.cpp:387)
libsgx_urts.so!_sgx_ecall(const sgx_enclave_id_t enclave_id, const int proc, const void * ocall_table, void * ms, const bool is_switchless) (\home\leone\文档\linux-sgx\psw\urts\routine.cpp:55)
libsgx_urts.so!sgx_ecall(const sgx_enclave_id_t enclave_id, const int proc, const void * ocall_table, void * ms) (\home\leone\文档\linux-sgx\psw\urts\routine.cpp:68)
ecall_execute_sql(sgx_enclave_id_t eid, const char * sql) (\home\leone\文档\SGX_SQLite\App\Enclave_u.c:397)
main(int argc, char ** argv) (\home\leone\文档\SGX_SQLite\App\App.cpp:64)
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 12
    评论
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值