2015XCTF福州站的第一道Mobile题,同时也是RCTF的题目,100分
第一个坑,先判断这是个啥玩意,file没看出来,但是应该是有的
Copy wnagzihxa1n@wnagzihxa1n:~$ file 2e204fe0ec33b1689f1c47bd60a9770c
2e204fe0ec33b1689f1c47bd60a9770c: data
后来查看16进制格式,才发现这是个Android Backup文件
能搜到搞这玩意的文章确实是不多,这里根据外国网友提供的方法,我们来一发
Copy wnagzihxa1n@wnagzihxa1n:~$ dd if=2e204fe0ec33b1689f1c47bd60a9770c.ab bs=1 skip=24 of=compressed
记录了8977280+0 的读入
记录了8977280+0 的写出
8977280 bytes (9.0 MB, 8.6 MiB) copied, 96.922 s, 92.6 kB/s
wnagzihxa1n@wnagzihxa1n:~$ printf "\x1f\x8b\x08\x00\x00\x00\x00\x00" | cat - compressed | gunzip -c > decompressed.tar
gzip: stdin: unexpected end of file
wnagzihxa1n@wnagzihxa1n:~$ tar xf decompressed.tar
解压生成一个apps
文件夹,查看一下文件树,发现有一个BOOKS.db
,这一定是个关键文件
Copy wnagzihxa1n@wnagzihxa1n:~$ tree apps
apps
├── com.example.mybackup
│ ├── a
│ │ └── com.example.mybackup-1.apk
│ ├── db
│ │ └── BOOKS.db
│ ├── f
│ │ └── icu
│ │ └── icudt46l.dat
│ └── _manifest
└── com.example.zi
├── a
│ └── com.example.zi-2.apk
└── _manifest
7 directories, 6 files
再翻其余文件夹,发现有两个APK文件,在反编译查看代码后,确定com.example.mybackup
这个比较有用
在简单的分析过后,可以确定的是,这个BOOKS.db
用了sqlcipher加密了,秘钥的生成方式如下
Copy public BooksDB(Context context) {
super(context, DATABASE_NAME, null, 1);
this.k = Test.getSign(context);
}
getSign()
方法如下
Copy public static String getSign(Context context) {
for (PackageInfo packageinfo : context.getPackageManager().getInstalledPackages(64)) {
if (packageinfo.packageName.equals(context.getPackageName())) {
return SHA1(packageinfo.signatures[0].toCharsString());
}
}
return "";
}
然后SHA1加密
Copy public static String SHA1(String decript) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-1");
digest.update(decript.getBytes());
byte[] messageDigest = digest.digest();
StringBuffer hexString = new StringBuffer();
for (byte b : messageDigest) {
String shaHex = Integer.toHexString(b & MotionEventCompat.ACTION_MASK);
if (shaHex.length() < 2) {
hexString.append(0);
}
hexString.append(shaHex);
}
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return "";
}
}
那么我们只需要写个APP去获取即可,只需要把getSign()
方法中间的equals()
方法里的参数换成样本的包名即可
然后获取到加密秘钥
Copy 320b42d5771df37906eee0fff53c49059122eeaf
获取到秘钥,接下来写个APP去解密
首先将样本里的lib文件拷贝到工程的jniLibs文件夹里,再从下面的地址下载对应的jar包,解压后将三个jar包放到libs文件夹下
https://www.zetetic.net/sqlcipher/
各种搞定文件的结构应该是这样的
然后写代码
Copy package com.wnagzihxa1n.myapplication;
import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import net.sqlcipher.database.SQLiteDatabase;
import java.io.File;
public class MainActivity extends Activity {
private final String SDcardPath = "/mnt/sdcard/";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SQLiteDatabase.loadLibs(this);//引用SQLiteDatabase的方法之前必须先添加这句代码
decrypt("BOOKS.db", "decryptedtest.db", "320b42d5771df37906eee0fff53c49059122eeaf");
}
/**
* 解密数据库
* @param encryptedName 要解密的数据库名称
* @param decryptedName 解密后的数据库名称
* @param key 密码
*/
private void decrypt(String encryptedName, String decryptedName, String key) {
try {
File databaseFile = getDatabasePath(SDcardPath + encryptedName);
SQLiteDatabase database = SQLiteDatabase.openOrCreateDatabase(databaseFile, key, null);
if (database != null) {
database.close();
}
database = SQLiteDatabase.openDatabase(databaseFile.toString(), key, null, SQLiteDatabase.OPEN_READWRITE);
File decrypteddatabaseFile = getDatabasePath(SDcardPath + decryptedName);
//连接到解密后的数据库,并设置密码为空
database.rawExecSQL(String.format("ATTACH DATABASE '%s' as "+ decryptedName.split("\\.")[0] +" KEY '';", decrypteddatabaseFile.getAbsolutePath()));
database.rawExecSQL("SELECT sqlcipher_export('"+ decryptedName.split("\\.")[0] + "');");
database.rawExecSQL("DETACH DATABASE "+ decryptedName.split("\\.")[0] + ";");
SQLiteDatabase decrypteddatabase = SQLiteDatabase.openOrCreateDatabase(decrypteddatabaseFile, "", null);
decrypteddatabase.close();
database.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
添加权限
Copy <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
先把BOOKS.db
文件push到/sdcard/
路径下,然后运行APP,即可在/sdcard/
下看到解密后的数据库文件,最后pull到本地
Copy root@jflte:/sdcard # ls | grep ".db"
BOOKS.db
root@jflte:/sdcard # ls | grep ".db"
BOOKS.db
decryptedtest.db
root@jflte:/sdcard # exit
C:\Users\wangz>adb pull /sdcard/decryptedtest.db C:\Users\wangz\Desktop
[100%] /sdcard/decryptedtest.db
使用sqlcipher.exe可视化工具打开
再结合要求,flag:RCTF{backuuuuuP}
上面那么顺畅的解题过程问人间哪里有?
首先知道这是Android BackUp文件倒不是很难,因为十六进制看Magic Number一下子就看出来了
难的地方在哪里,如何解压是个问题,一开始我并不熟悉这个文件的格式,所以在Github上找了个工具来解压
Copy https://github.com/nelenkov/android-backup-extractor
然而这玩意并不能解压给的BOOKS.db
,问了某老铁才知道,因为只支持到v3,然而作者把这个字段改了一下
改成3,继续解压,依旧不行,后来才知道,Compressed那个字段要改成1,作者也改了
后来直接在Google上找了老外写的方法,一下子就搞出来了
也就是上面写的方法
解压出了文件之后,解密BOOKS.db
是一个大问题
作为一个开发者,第一想法是写个APP,用sqlcipher库去解密给的BOOKS.db
但是莫名不想写
于是想着,网上应该有对这种数据库加密的解密工具吧,于是找啊找
还真的找到了,凭借着神一般的记忆力,我给列一下
Github---某需要自行编译的工具
32位Ubuntu编译出错,一直以为是编译环境问题
两天就这么过去了
想了想,我还是回归开发者吧
东拼西凑,根据别人的代码写了个解密的APP
然而一直unable to open......
后来发现,权限没给
然后就搞出来了
References
How do you extract an App's data from a full backup made through “adb backup”?: http://android.stackexchange.com/questions/28481/how-do-you-extract-an-apps-data-from-a-full-backup-made-through-adb-backup
安卓hacking Part 15: 使用备份技术黑掉安卓应用: http://bobao.360.cn/learning/detail/169.html
使用SQLCipher对数据库加密: http://blog.csdn.net/small_lee/article/details/50971132
sqlcipher: https://github.com/sqlcipher/sqlcipher
利用SQLCipher加解密数据库(包括加解密已有的数据库): http://blog.csdn.net/wjk343977868/article/details/53410738