SQLCipher之攻與防

tiny丶發表於2019-03-22

SQLCipher之攻與防

0x00 SQLCipher

在移動端,不管是iOS還是Android,開發人員用的最多的本地資料庫非SQlite莫屬了。SQLite是一個輕量的、跨平臺的、開源的資料庫引擎,它的在讀寫效率、消耗總量、延遲時間和整體簡單性上具有的優越性,使其成為移動平臺資料庫的最佳解決方案。

但是,用SQLite儲存一些不是很敏感的資訊還可以接受,儲存敏感資訊就值得商榷了,免費版的SQLite中的資訊是明文存放的,你甚至直接用文字編輯器開啟都可以看到敏感內容,比如下面這個:

enter description hereenter description here

其實SQLite是提供了加密功能的,即SQLite Encryption Extension (SEE),但是,要收費!還不便宜,2000刀。Coder們窮啊,於是大家尋找免費的解決方案,最終SQLCipher成了比較理想的選擇,因為它免費、開源,而其它家的SQLiteEncrypt、SQLiteCrypt等還是要收費。

SQLCipher也有收費版本,但收費版本只是在整合、依賴等易用性方面有優勢,功能和免費差不多,因此免費版的SQLCipher成了Coder們的選擇物件。

SQLCipher採用的是資料庫檔案整體加密的策略,使用256-bit AES加密,從演算法角度來看是相當強悍了。於是,加密後的檔案看起來及時這樣了:

enter description hereenter description here

完全是天書嘛,ok,可以洗洗睡了。但真的就安全了嗎?呵呵。

0x01 謀攻篇

密碼學中有一句比較經典的話:一切祕密寓於金鑰之中。只要我們拿到了金鑰,剩下的問題都不是問題了。下面我們就來分析一下SQLCipher的金鑰和加密過程。
根據官方的描述,SQLCipher是整體加密方案:

Transparent – An application doesn’t require any special knowledge of the underlying database security. Applications use the standard SQLite API to manipulate tables using SQL. Behind the scenes the library silently manages the security.

On-the-fly – SQLCipher encrypts and decrypts in chunks called pages, as needed, so it doesn’t operate on the database all at one time. This means SQLCipher
starts up and closes down quickly
performs very well even with very large databases
works with SQLite indexing (i.e. retrieving a single record using an indexed search can incur as little as 5% overhead above a standard SQLite database)

它並不是對錶或者列進行加密,也就是說他的資料庫金鑰只有一個,拿下這一個金鑰就成。

SQLCipher在iOS上是這樣用的:

1)在專案中加入sqlite3.h和sqlite3.m;

2)使用下面程式碼連線資料庫:

enter description hereenter description here

其中的key就是我們需要的金鑰!然後通過sqlite3_key函式將該key與資料庫關聯起來,接下來就可以執行sql語句操作了。程式設計師肯定覺得好用啊,就增加了兩行語句就完成了加密功能,直接拷貝官方的程式碼到自己的工程中,把“StrongPassword”換成自己的金鑰就成。真實容易啊,那麼我攻擊也很easy啦。

一般來說,對付這種程度的加密,只需要使用靜態分析即可。對於iOS程式,將程式拖到ida中,找到sqlite3_key函式相關引用地址,順藤摸瓜,基本就搞定了。下面就是某應用中定位該函式在setKey方法中,該方法是在它的資料open方法中呼叫的,可以看到已經明文硬編碼了資料庫的密碼(圖中抹紅部分)。

enter description hereenter description here

拿到密碼後有個偷懶的方法可以直接開啟加密資料庫:使用SQLiteManager,它支援SQLCipher加密的資料庫,會提示輸入資料庫密碼,不需要自己寫程式了,而且瀏覽資料也很方便。

enter description hereenter description here

對於Android版的程式,如果沒加殼的話,也是很好分析的。Android呼叫SQLCipher使用下面的方式:

enter description hereenter description here
openOrCreateDatabase方法的第二個引數就是金鑰了。在jeb反編譯出來的程式碼中尋找相關的方法就可以摸到相應的資料庫金鑰。

到這兒程式設計師可能要想了,我不硬編碼看你咋整,我在程式碼中經過加減乘除、異或後在算出key,或者更狠一點用使用者口令等進行動態解密變換,金鑰就不會在程式中出現了。

但攻擊者還有n多招數還沒上場了,比如動態除錯,在輸入key的方法入口打斷點,對key的明文進行攔截。
還有就是hook技術,對於Android上的應用,我們可以使用CydiaSubstrate,使用MS.hookClassLoad 方法將openOrCreateDatabase進行hook處理:


 

 

這段程式碼可以將openOrCreateDatabase方法鉤住並將第二個引數key在日誌中列印出來,無視你前期經過了多少隱藏、變換。至於Substrate的用法這裡就不細講了,各位看官可以參考網上的資料。當然,你也可以嘗試其它hook框架,如Xposed。

程式設計師可能不服氣,我還可以上加殼,在iOS端我也可以上混淆、lua動態網上載入等手段啊?不過這貌似超出了SQLCipher攻防的地盤了,是程式本地保護的大範疇,有機會再研究。

0x02 防?

寫到這兒,其實對本地資料庫加密是感到沮喪的,這完全就是把門鎖和鑰匙放在一起的做法嘛,不管你在本地上任何軟的手段(硬體另說,如果你的金鑰、演算法都放在硬體中,比如USBKey中,安全性應該是有本質上的提升),不管是簡單變換還是混淆、加殼、反除錯、反hook,都難保不被攻擊者破解掉,不過是或多或少延緩找到鑰匙的時間罷了。

因此,建議除了以下情況,不要使用SQLCipher之類的本地資料庫加密:

  1. 非敏感資料,愛咋用咋用
  2. 使用者自己的資料,可以採用使用者口令、指紋等方式直接或間接保護,因為這種情況下key其實是隨使用者走的,可以做到key和演算法、資料的分離

其它情況推薦資料放在APP的服務端,將客戶端做瘦,將安全的防護重點放在通訊和服務端的防護上。

相關文章