929 words
5 minutes
OWASP MAS CrackMe writeup
題目連結:OWASP MAS crackmes
對我很爛只解幾題, 看到這篇文的兄弟直接敲我DC催我打題目
Android
Level 1
先使用jadx定位到MainActivity
MainActivity:
public class MainActivity extends Activity {
private void a(String str) {
AlertDialog create = new AlertDialog.Builder(this).create();
create.setTitle(str);
create.setMessage("This is unacceptable. The app is now going to exit.");
create.setButton(-3, "OK", new DialogInterface.OnClickListener() { // from class: sg.vantagepoint.uncrackable1.MainActivity.1
@Override // android.content.DialogInterface.OnClickListener
public void onClick(DialogInterface dialogInterface, int i) {
System.exit(0);
}
});
create.setCancelable(false);
create.show();
}
@Override // android.app.Activity
protected void onCreate(Bundle bundle) {
if (c.a() || c.b() || c.c()) {
a("Root detected!");
}
if (b.a(getApplicationContext())) {
a("App is debuggable!");
}
super.onCreate(bundle);
setContentView(R.layout.activity_main);
}
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();
}
}
重點看到verify()
裡面的
if (a.a(obj)) {
create.setTitle("Success!");
str = "This is the correct secret.";
} else {
create.setTitle("Nope...");
str = "That's not it. Try again.";
}
顯然這邊在驗證我們輸入的是否為期望的flag
找到a
public class a {
public static boolean a(String str) {
byte[] bArr;
byte[] bArr2 = new byte[0];
try {
bArr = sg.vantagepoint.a.a.a(b("8d127684cbc37c17616d806cf50473cc"), Base64.decode("5UJiFctbmgbDoLXmpL12mkno8HT4Lv8dlat8FxR2GOc=", 0));
} catch (Exception e) {
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;
}
}
顯然這是在做AES decrypt
再找到sg.vantagepoint.a.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);
}
}
可以確定為ECB + PKCS7模式
這樣就可以寫個解密程式啦
from Crypto.Cipher import AES
import base64
key = bytes.fromhex("8d127684cbc37c17616d806cf50473cc")
ciphertext = base64.b64decode("5UJiFctbmgbDoLXmpL12mkno8HT4Lv8dlat8FxR2GOc=")
cipher = AES.new(key, AES.MODE_ECB)
plaintext = cipher.decrypt(ciphertext)
print(plaintext.decode("utf-8"))
執行結果:I want to believe
Level 2
跟上題一樣丟到jadx,定位到MainActivity,裡面也有verify()
public class MainActivity extends c {
private CodeCheck m;
static {
System.loadLibrary("foo");
}
/* JADX INFO: Access modifiers changed from: private */
public void a(String str) {
AlertDialog create = new AlertDialog.Builder(this).create();
create.setTitle(str);
create.setMessage("This is unacceptable. The app is now going to exit.");
create.setButton(-3, "OK", new DialogInterface.OnClickListener() { // from class: sg.vantagepoint.uncrackable2.MainActivity.1
@Override // android.content.DialogInterface.OnClickListener
public void onClick(DialogInterface dialogInterface, int i) {
System.exit(0);
}
});
create.setCancelable(false);
create.show();
}
private native void init();
/* JADX WARN: Type inference failed for: r0v4, types: [sg.vantagepoint.uncrackable2.MainActivity$2] */
@Override // android.support.v7.app.c, android.support.v4.app.h, android.support.v4.app.z, android.app.Activity
protected void onCreate(Bundle bundle) {
init();
if (b.a() || b.b() || b.c()) {
a("Root detected!");
}
if (a.a(getApplicationContext())) {
a("App is debuggable!");
}
new AsyncTask<Void, String, String>() { // from class: sg.vantagepoint.uncrackable2.MainActivity.2
/* JADX INFO: Access modifiers changed from: protected */
@Override // android.os.AsyncTask
/* renamed from: a, reason: merged with bridge method [inline-methods] */
public String doInBackground(Void... voidArr) {
while (!Debug.isDebuggerConnected()) {
SystemClock.sleep(100L);
}
return null;
}
/* JADX INFO: Access modifiers changed from: protected */
@Override // android.os.AsyncTask
/* renamed from: a, reason: merged with bridge method [inline-methods] */
public void onPostExecute(String str) {
MainActivity.this.a("Debugger detected!");
}
}.execute(null, null, null);
this.m = new CodeCheck();
super.onCreate(bundle);
setContentView(R.layout.activity_main);
}
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 (this.m.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.uncrackable2.MainActivity.3
@Override // android.content.DialogInterface.OnClickListener
public void onClick(DialogInterface dialogInterface, int i) {
dialogInterface.dismiss();
}
});
create.show();
}
}
最前面有一個private CodeCheck m;
所以找到CodeCheck
public class CodeCheck {
private native boolean bar(byte[] bArr);
public boolean a(String str) {
return bar(str.getBytes());
}
}
裡面使用到native
,代表是用其他語言寫的,他們用Java Native Interface跟java互動
可以在lib底下找到libfoo.so檔
接著拖到Ghidra分析
根據JNI命名規則,定位到Java_sg_vantagepoint_uncrackable2_CodeCheck_bar()
undefined8
Java_sg_vantagepoint_uncrackable2_CodeCheck_bar(long *param_1,undefined8 param_2,undefined8 param_3)
{
int iVar1;
char *__s1;
undefined4 extraout_var;
undefined8 uVar2;
long in_FS_OFFSET;
char local_38 [24];
long local_20;
local_20 = *(long *)(in_FS_OFFSET + 0x28);
if (DAT_0010400c == '\x01') {
builtin_strncpy(local_38,"Thanks for all the fish",0x18);
__s1 = (char *)(**(code **)(*param_1 + 0x5c0))(param_1,param_3,0);
iVar1 = (**(code **)(*param_1 + 0x558))(param_1,param_3);
if (iVar1 == 0x17) {
iVar1 = strncmp(__s1,local_38,0x17);
if (iVar1 == 0) {
uVar2 = CONCAT71((int7)(CONCAT44(extraout_var,iVar1) >> 8),1);
goto LAB_0010119c;
}
}
}
uVar2 = 0;
LAB_0010119c:
if (*(long *)(in_FS_OFFSET + 0x28) == local_20) {
return uVar2;
}
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
稍微可以猜出邏輯,把Thanks for all the fish
丟到local_38
裡面,iVar1
作string compare,相等的話就作其他事情