OWASP 实战分析 level 1
crackme
程序(owasp uncrackable)的几种方法。题目链接
链接:https://pan.baidu.com/s/1VJ7Y3psWoSi5NnlOpaohEw
提取码:1234
owasp uncrackable 安全机制
在
owasp uncrackable
能够找到的安全机制:
1.
Java
反调试2.
Java
完整性校验(CRC
)3.
java Root
检测4.
Native
层的反调试
使用到的工具
Java层的反编译工具(Dalvik bytecode
):
◆Jadx-gui.
◆JEB.
So层
反编译程序:
◆IDA Pro
动态二进制检测框架:
◆Frida.
编译工具
◆vscode
owasp level1
使用Jadx
反编译工具打开apk
如下所示。
来到MainActivity
入口:
猜测会有root
权限的检测工具,顺着这个猜测,我们可以有两种思路解决这个root
检测。
1.动态调试
smail
代码,修改代码绕过root
检测2.使用
frida
hook
绕过
继续往下看:
public void verify(View view) {
String str;
//获取输入框的字符串
String obj = ((EditText) findViewById(R.id.edit_text)).getText().toString();
AlertDialog create = new AlertDialog.Builder(this).create();
//我们需要进入的地方
if (a.a(obj)) {
create.setTitle("Success!");
str = "This is the correct secret.";
} else {
create.setTitle("Nope...");
str = "That's not it. Try again.";
}
create.setMessage(str);
// 设置对话框的按钮
create.setButton(-3, "OK", new DialogInterface.OnClickListener() { // from class: sg.vantagepoint.uncrackable1.MainActivity.2
@Override // android.content.DialogInterface.OnClickListener
public void onClick(DialogInterface dialogInterface, int i) {
dialogInterface.dismiss();
}
});
// 显示对话框
create.show();
}
我们进入a
类:
进入后,内容如下:
public class a {
// 判断字符串是否与加密后的字符串相等
public static boolean a(String str) {
byte[] bArr;
byte[] bArr2 = new byte[0];
try {
// 对字符串进行AES解密
bArr = sg.vantagepoint.a.a.a(b("8d127684cbc37c17616d806cf50473cc"), Base64.decode("5UJiFctbmgbDoLXmpL12mkno8HT4Lv8dlat8FxR2GOc=", 0));
} catch (Exception e) {
// 打印AES解密错误信息
Log.d("CodeCheck", "AES error:" + e.getMessage());
bArr = bArr2;
}
// 判断解密后的字符串与给定字符串是否相等
return str.equals(new String(bArr));
}
// 将字符串转换为字节数组
public static byte[] b(String str) {
int length = str.length();
byte[] bArr = new byte[length / 2];
for (int i = 0; i < length; i += 2) {
// 将字符串中的每两个字符转换为对应的字节
bArr[i / 2] = (byte) ((Character.digit(str.charAt(i), 16) << 4) + Character.digit(str.charAt(i + 1), 16));
}
return bArr;
}
}
经过观察,发现如果成功返回equals
(true
),就能suceeful
,由于没有涉及到so
层(没看到native
),所以我们直接在Java
层进行代码分析。
1.Base64
解密字节数组
手写一下伪代码:
java
public static byte[] getdecode(String word){
byte[] base64decodedBytes = Base64.getDecoder().decode(word);
byte[] newbase64decodedBytes =Base64.getDecoder().decode(word);
String strcode = new String(newbase64decodedBytes);
System.out.println(strcode);
System.out.println(newbase64decodedBytes);
return base64decodedBytes;
}
js实现上述java
函数。
getBase64decode = function (){
var encode = "5UJiFctbmgbDoLXmpL12mkno8HT4Lv8dlat8FxR2GOc=";
let decrypted = atob(encode);
//转成字节数组
let base64decodedBytes = new Uint8Array(atob(encode).split('').map((char) => char.charCodeAt(0)));
return base64decodedBytes;
}
2.对字符串"8d127684cbc37c17616d806cf50473cc
" 使用sg.vantagepoint.a.a.a
进行处理
java
public static byte[] b(String str) {
int length = str.length();
byte[] bArr = new byte[length / 2];
for (int i = 0; i < length; i += 2) {
bArr[i / 2] = (byte) ((Character.digit(str.charAt(i), 16) << 4) + Character.digit(str.charAt(i + 1), 16));
}
return bArr;
}
使用js
完成java
的功能。
const byte = (x) => ((((x | 0) & 0xff) + 128) % 256) - 128;
//判断是否大小写
Judgment_capitalization = function (input){
if ( 'A'<=input &&input <='Z'){
return true;
}
else
return false;
}
//判断是否小写
Determine_whether_to_lower = function (input){
if ( 'a'<=input &&input <='z'){
return true;
}
else
return false;
}
digit = function (input,radis) {
try {
if (!isNaN(parseInt(input))) {
if (parseInt(input) < 10 && parseInt(input) > -1) {
return parseInt(input) < parseInt(radis) ? parseInt(input) : -1;
}
}
} catch (err) {
}
try {
if (Judgment_capitalization(input)) {
return input.charCodeAt(0) < radis + 'A'.charCodeAt(0) - 10 ? input.charCodeAt(0) - 'A'.charCodeAt(0) + 10 : -1;
}
} catch (err) {
}
try {
if (Determine_whether_to_lower(input)) {
return input.charCodeAt(0) < radis + 'a'.charCodeAt(0) - 10 ? input.charCodeAt(0) - 'a'.charCodeAt(0) + 10 : -1;
}
} catch (err) {
}
}
b = function (paramString){
var nlength = paramString.length;
//直接使用intarrybuffer
var arrofByte = new Int8Array(nlength/2);
for(var i=0;i<nlength;i+=2){
arrofByte[i/2] = (byte)(((digit(paramString.charAt(i),16)<<4)+digit(paramString.charAt(i+1),16)));
}
return (arrofByte);
}
3.查看外层函数直接sg.vantagepoint.a
,点击进入
类如下:
public class a {
public static byte[] a(byte[] bArr, byte[] bArr2) {
SecretKeySpec secretKeySpec = new SecretKeySpec(bArr, "AES/ECB/PKCS7Padding");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(2, secretKeySpec);
return cipher.doFinal(bArr2);
}
}
这个时候,我们综合之前的分析,可以写出java
脚本。
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.Security;
import java.util.Base64;
public class AES128ECBwithPKCS7 {
private static final String SECRET = "AES";
private static final String CIPHER_ALGORITHM = "AES/ECB/PKCS7Padding";
// Decrypts the given byte array using AES 128 ECB with PKCS7 padding
public static String newaes256ECBPkcs7PaddingDecrypt(byte[] bArr, byte[] bArr2) throws Exception {
String strbrr = new String(bArr);
String strbrr2 = new String(bArr2);
// Create a new instance of the Cipher class with the specified algorithm
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
// Initialize the cipher in decryption mode with the secret key
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(bArr, SECRET));
// Perform the decryption operation
byte[] doFinal = cipher.doFinal(bArr2);
// Convert the decrypted byte array to a string and return it
return new String(doFinal);
}
// Converts a hexadecimal string to a byte array
public static byte[] b(String str) {
int length = str.length();
byte[] bArr = new byte[length / 2];
// Iterate over the hexadecimal string and convert each pair of characters to a byte
for (int i = 0; i < length; i += 2) {
bArr[i / 2] = (byte) ((Character.digit(str.charAt(i), 16) << 4) + Character.digit(str.charAt(i + 1), 16));
}
// Print the byte array
System.out.println(bArr);
return bArr;
}
// Decodes a Base64-encoded string and returns the decoded byte array
public static byte[] getdecode(String word) {
byte[] base64decodedBytes = Base64.getDecoder().decode(word);
byte[] newbase64decodedBytes = Base64.getDecoder().decode(word);
String strcode = new String(newbase64decodedBytes);
// Print the decoded string and byte array
System.out.println(strcode);
System.out.println(newbase64decodedBytes);
return base64decodedBytes;
}
public static void main(String[] args) throws Exception {
// Decrypt the given text using AES 128 ECB with PKCS7 padding
String text = newaes256ECBPkcs7PaddingDecrypt(b("8d127684cbc37c17616d806cf50473cc"), getdecode("5UJiFctbmgbDoLXmpL12mkno8HT4Lv8dlat8FxR2GOc="));
// Print the decrypted text
System.out.println(text);
}
}
js
脚本
const CryptoJS = require('crypto-js')
//转化为byte
const base = require("./Crypted/Base64");
const byte = (x) => ((((x | 0) & 0xff) + 128) % 256) - 128;
//判断是否大小写
Judgment_capitalization = function (input){
if ( 'A'<=input &&input <='Z'){
return true;
}
else
return false;
}
//判断是否小写
Determine_whether_to_lower = function (input){
if ( 'a'<=input &&input <='z'){
return true;
}
else
return false;
}
digit = function (input,radis) {
try {
if (!isNaN(parseInt(input))) {
if (parseInt(input) < 10 && parseInt(input) > -1) {
return parseInt(input) < parseInt(radis) ? parseInt(input) : -1;
}
}
} catch (err) {
}
try {
if (Judgment_capitalization(input)) {
return input.charCodeAt(0) < radis + 'A'.charCodeAt(0) - 10 ? input.charCodeAt(0) - 'A'.charCodeAt(0) + 10 : -1;
}
} catch (err) {
}
try {
if (Determine_whether_to_lower(input)) {
return input.charCodeAt(0) < radis + 'a'.charCodeAt(0) - 10 ? input.charCodeAt(0) - 'a'.charCodeAt(0) + 10 : -1;
}
} catch (err) {
}
}
b = function (paramString){
var nlength = paramString.length;
//直接使用intarrybuffer
var arrofByte = new Int8Array(nlength/2);
for(var i=0;i<nlength;i+=2){
arrofByte[i/2] = (byte)(((digit(paramString.charAt(i),16)<<4)+digit(paramString.charAt(i+1),16)));
}
return (arrofByte);
}
/**
*@description:将string转为UTF-8格式signed char字节数组
*
*/
function byteToString(arr) {
if(typeof arr === 'string') {
return arr;
}
var str = '',
_arr = arr;
for(var i = 0; i < _arr.length; i++) {
var one = _arr[i].toString(2),
v = one.match(/^1+?(?=0)/);
if(v && one.length == 8) {
var bytesLength = v[0].length;
var store = _arr[i].toString(2).slice(7 - bytesLength);
for(var st = 1; st < bytesLength; st++) {
store += _arr[st + i].toString(2).slice(2);
}
str += String.fromCharCode(parseInt(store, 2));
i += bytesLength - 1;
} else {
str += String.fromCharCode(_arr[i]);
}
}
return str;
}
//网络传输
function stringToByte(str) {
var len, c;
len = str.length;
var bytes = [];
for(var i = 0; i < len; i++) {
c = str.charCodeAt(i);
if(c >= 0x010000 && c <= 0x10FFFF) {
bytes.push(((c >> 18) & 0x07) | 0xF0);
bytes.push(((c >> 12) & 0x3F) | 0x80);
bytes.push(((c >> 6) & 0x3F) | 0x80);
bytes.push((c & 0x3F) | 0x80);
} else if(c >= 0x000800 && c <= 0x00FFFF) {
bytes.push(((c >> 12) & 0x0F) | 0xE0);
bytes.push(((c >> 6) & 0x3F) | 0x80);
bytes.push((c & 0x3F) | 0x80);
} else if(c >= 0x000080 && c <= 0x0007FF) {
bytes.push(((c >> 6) & 0x1F) | 0xC0);
bytes.push((c & 0x3F) | 0x80);
} else {
bytes.push(c & 0xFF);
}
}
return new Int8Array(bytes);
}
getBase64decode = function (){
var encode = "5UJiFctbmgbDoLXmpL12mkno8HT4Lv8dlat8FxR2GOc=";
let decrypted = atob(encode);
let base64decodedBytes = new Int8Array(atob(encode).split('').map((char) => char.charCodeAt(0)));
// var bytedecode = new Int8Array(100)
// bytedecode =stringToByte(decrypted);
const strbrr = String.fromCharCode.apply(null,base64decodedBytes);
console.log(strbrr);
return base64decodedBytes;
}
//AES_ECB模式解密
deCryptedByAes_ECB = function (data,AESkey){
var strdata = byteToString(data);
var strAESKey = byteToString(AESkey)
var key = CryptoJS.enc.Utf8.parse(AESkey);
var decrypt = CryptoJS.AES.decrypt(data, key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7,
});
return CryptoJS.enc.Utf8.stringify(decrypt).toString();
把加密的内容和秘钥转化为wordarry对象
// const encryptedwordarry = CryptoJS.enc.Hex.parse(data.join(''));
// const keywordArry = CryptoJS.enc.Utf8.parse(AESkey.join(''));
// //解密
// const decrypted = CryptoJS.AES.decrypt(
// {ciphertext:encryptedwordarry},
// keywordArry,{mode:CryptoJS.mode.ECB,padding:CryptoJS.pad.Pkcs7}
// )
// return decrypted.toString(CryptoJS.enc.Utf8);
}
const str = byteToString(b("8d127684cbc37c17616d806cf50473cc"))
gettext = function () {
var text = deCryptedByAes_ECB(b("8d127684cbc37c17616d806cf50473cc"),getBase64decode("5UJiFctbmgbDoLXmpL12mkno8HT4Lv8dlat8FxR2GOc="))
console.log(text);
}
console.log(gettext())
跑出的结果是:
就是我们想要的flag。
注意,此时并没有绕过root
检测,就已经完成了分析过程,但为了尊重开发者,我们按照之前的两个思路进行解题。
1.1. 使用AS动态修改smail代码进行绕过
1.使用Apk killer
进行反编译
把smali文件丢到其他目录下,方便我们接下来使用AS导入。
丢到H
盘,把smail
修改该名字为src
在AS
(Android Studio
下面统称AS)导入smalidea-0.06.zip。
导入后会提示重启AS。
重启AS
,然后开始进行下面修改。
file->setting->editor->filetype `选择 `smail Files`,然后点击右上方的+号,输入`*.smail
如果没有找到smail Files
说明在第三步(AS(Android Studio
下面统称AS)导入smalidea-0.06.zip
)出错,错误原因可能是当前AS的版本太低(低于4.0
)应该使用smalidea-0.05.zip
或者是当前AS
版本(高于4.0
)较高,但导入了低版本的smalidea-0.05.zip。
新建项目,导入文件smail
文件。
在Oncreate
函数下断点。
1.输入命令开启调试adb shell am start -D -n owasp.mstg.uncrackable1/sg.vantagepoint.uncrackable1.MainActivity
这是一条用于在Android设备上启动应用程序的ADB命令。具体含义如下:
● `adb shell`:通过adb连接到设备的shell终端。
● `am start`:启动ActivityManager服务,用于启动应用程序。 ● -D:在启动应用程序时,将应用程序置于调试模式。
● `-n wasp.mstg.uncrackable1/sg.vantagepoint.uncrackable1.MainActivity`:指定要启动的应用程序的包名和主Activity的类名。 总体来说,这条命令的作用是以调试模式启动包名为`owasp.mstg.uncrackable1`的应用程序,并打开其`MainActivity`。
运行如下
<img src="https://bbs.kanxue.com/upload/attach/202310/940967_RFDNEHHANPPKYHD.png" alt="image-20231024110212864" style="zoom:50%;" />
1.找到包名对应的pidadb shell ps | grep owasp.mstg.uncrackable1
这里是8980
使用adb
命令 转发本地端口。
adb forward tcp:8800 jdwp:8980
设置远程调试
点击右上方AddConfiguration ...
点击Remote JVM Debug
设置端口8800
和名字app1。
选择设备,并开始调试。
由于root
检测,远程调试无法进入OnCreated
界面,所以选择直接看smaile
源码。
思路是:直接在smail
层绕过Root
和debug
检测。
来到smail
代码层需要修改绕过(Root
函数代码)代码如下:
修改完后使用killer
重新编译一下。
安装apk
到手机,显示已经没有root
检测窗口了。
这个时候可以开启动态调试了,如图:
按照这个思路,我们也可以直接跳转到登录成功的界面,修改的smail
代码如下:
重新编译,无论输入什么都能成功登录。
1.2. 使用Frida进行hook java层
没什么好说的,直接hook
一把梭。
setImmediate(function (){ // 在下一个事件循环中执行以下代码
Java.perform(function() {
// 重写MainActivity类中的a方法
Java.use("sg.vantagepoint.uncrackable1.MainActivity").a.implementation = function (x) {
// 注释:这里是a方法的重写实现
// this.a(x);
}
// 获取sg.vantagepoint.a.a类的实例
var a = Java.use("sg.vantagepoint.a.a");
// 重写a类中的a方法
a["a"].implementation = function (bArr, bArr2) {
// 注释:这里是a方法的重写实现
console.log('a is called' + ', ' + 'bArr: ' + bArr + ', ' + 'bArr2: ' + bArr2);
// 调用原始的a方法并获取返回值
var ret = this.a(bArr, bArr2);
// 打印返回值
console.log('a ret value is ' + ret);
// 将返回值转换为字符串并打印
console.log(Java.use("java.lang.String").$new(ret));
// 返回原始的返回值
return ret;
};
})
})
看雪ID:4Chan
https://bbs.kanxue.com/user-home-940967.htm
# 往期推荐
球分享
球点赞
球在看