NeoQuest 2017: Reverse Android applications in the task "Fix the leader!"



All good day, today, March 10, over online stage NeoQuest 2017. While the judges sum up the results and send out invitations to the finals, please see ratapan one job: Greenoid which according to the table rankings, it was possible to get to 85 points.

As usual, the job will be available for a while, who did not, can now quietly doreset, or to see.

the

Start


The downloaded file NeoQuest.apk and then decompiling the resulting listing:

MainActivity.java
package com.neobit.neoquest;

import android.app.Activity;
import android.content.Res. AssetManager;
import android.os.Bundle;
import android.telephony.TelephonyManager;
import android.util.Base64;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.TextView;
import dalvik.system.DexClassLoader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Arrays;

public class MainActivity extends Activity implements OnClickListener {
private Method f1373a;

static {
System.loadLibrary("neolib"); // Load external library so
}
// Declare functions from dynamically loaded libraries
public native byte[] decrypt(String str, byte[] bArr);

public native int nativeCRC32sum(byte[] bArr);

public void onClick(View View) {
int i = 0;
CharSequence charSequence = "";
try {
Open InputStream = getAssets().open("cred");
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] bArr = new byte[1024];
// Read the contents of the file cred
while (true) {
int read = open.read(bArr, 0, 1024);
if (read == -1) {
break;
}
byteArrayOutputStream.write(bArr, 0, read);
}
byteArrayOutputStream.flush();
byte[] toByteArray = byteArrayOutputStream.toByteArray();
byteArrayOutputStream.close();
open.close();
while (i < 1024 && bArr[i] != (byte) 10) {
i++;
}
// Break file contents into lines
String login = new String(toByteArray, 0, i - 1, "UTF-8");
int i2 = i + 1;
i = i2;
while (i < toByteArray.length && bArr[i] != (byte) 10) {
i++;
}
String key = new String(toByteArray, i2, (i - i2) - 1, "UTF-8");
String comment = Base64.encodeToString(Arrays.copyOfRange(toByteArray, i + 1, toByteArray.length), 2);
byteArrayOutputStream.close();
// Calculate CRC32 for the whole file content cred
String crc32 = Integer.of the tohexstring(nativeCRC32sum(toByteArray)).toUpperCase();
// Send the data to the server
charSequence = (String) this.f1373a.invoke(null, new Object[]{login, key, comment, crc32});
} catch (Exception e) {
}
((TextView) findViewById(2131492970)).setText(charSequence);
}

protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(2130968601);
AssetManager assets = getAssets();
try {
// Get current IMEI of the device
String deviceId = ((TelephonyManager) getSystemService("phone")).getDeviceId();
// Read contents of file 1.dex
InputStream open = assets.open("1.dex");
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] bArr = new byte[1024];
while (true) {
int read = open.read(bArr);
if (read != -1) {
byteArrayOutputStream.write(bArr, 0, read);
} else {
// Decipher 1.dex
byte[] decrypt = decrypt(deviceId, byteArrayOutputStream.toByteArray());
File File = new File(getCacheDir(), "1.dex");
file.delete();
FileOutputStream fileOutputStream = new FileOutputStream(file, false);
fileOutputStream.write(decrypt);
fileOutputStream.close();
// Ship the get method from the class com.neobit.neoquest.Server
this.f1373a = new DexClassLoader(file.getAbsolutePath(), getDir("outdex", 0).getAbsolutePath(), null, getClassLoader()).loadClass("com.neobit.neoquest.Server").getMethod("get", new Class[]{String.class, String.class, String.class, String.class});
findViewById(2131492969).setOnClickListener(this);
return;
}
}
} catch (Throwable th) {
// In case of error swear by IMEI
((TextView) findViewById(2131492970)).setText("Phone IMEI is not correct");
}
}
}


The code has been annotated, so to explain it I think not. Proceed to the next step.

the

Decrypted 1.dex


First you need to unpack a APK file:

the
$ apktool d NeoQuest.apk

We find that there are several libraries for different architecture. Open one of them in the IDA. Code responsible for decryption looks like this:



And Java wrapper for it:


As can be seen, there exists a faithful IMEI, then there are several options:
    the
  1. you Can patch myself apk file, replacing the relevant lines in smali file so that decrypt went the faithful IMEI;
  2. the
  3. Or rewrite it in another language and do everything manually.

The first option is more simple, but the second is more convenient, because then you will not have to rewrite the key from the phone screen, you can simply copy from the console. Python it will look like this:

the
def getLbits(number):
bits = '%08x' % number
return int(bits[-2:], 16)

def setLbits(dst, src):
bits = '%08x' % src
bits = int(bits[-2:], 16)
dst = '%08x' % dst
return int('%s%02x' % (dst[:-2], bits), 16)

def decrypt(data, data_len, key, key_len):
prekey = {}
prekey2 = {}
for i in range(0x100):
prekey[i] = i
prekey2[i] = ord(key[i % key_len])
y = 0x0
for i in range(0x100):
rdi = prekey[i]
key_len = setLbits(key_len, prekey[i] + prekey2[i] + y)
y = key_len
prekey[i] = getLbits(getLbits(prekey[key_len]) & 0xFF)
prekey[key_len] = getLbits(rdi)
result = []
if data_len != 0x0:
i = 0x0
y = 0x0
k = 0x0
while i < data_len:
k = (k + 0x1) & 0xFF
rax = getLbits(prekey[k])
y = (y + rax) & 0xFF
prekey[k] = getLbits(prekey[y])
prekey[y] = rax
rax += prekey[k]
result.append(data[i] ^ getLbits(prekey[getLbits(rax)]))
i += 0x1
return result

dex = open('1.dex', 'rb').read()
imei = '352612062282062'
result = decrypt(dex, len(dex), imei, len(imei))
outdex = open('out.dex', 'wb')
outdex.write(bytes(result))
outdex.close()

PS the Code is not perfect and it can be optimized, but this option is in my opinion more intuitive.

After running, the resulting decrypted file out.dex, which in decompilers in the following code:

Server.java
package com.neobit.neoquest;

import android.os.AsyncTask;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.An executionexception;

public class Server {
private static final String address = "http://213.170.100.214/neoquest.php";

/* renamed from: com.neobit.neoquest.Server.1 */
static final class C00001 extends AsyncTask<Void, Void, String> {
final /* synthetic */ String val$comment;
final /* synthetic */ String val$crc32;
final /* synthetic */ String val$keyWorld;
final /* synthetic */ String val$login;

C00001(String str, String str2, String str3, String str4) {
this.val$login = str;
this.val$keyWorld = str2;
this.val$comment = str3;
this.val$crc32 = str4;
}

protected String doInBackground(Void... voidArr) {
try {
HttpURLConnection httpURLConnection = (HttpURLConnection) new URL(Server.address).openConnection();
httpURLConnection.setRequestMethod("POST");
httpURLConnection.addRequestProperty("Content-Type", "application/json");
DataOutputStream dataOutputStream = new DataOutputStream(httpURLConnection.getOutputStream());
dataOutputStream.writeBytes(String.format("{\"login\":\"%s\",\"key_word\":\"%s\",\"comment\":\"%s\",\"crc32\":\"%s\"}"new Object[]{this.val$login, this.val$keyWorld, this.val$comment, this.val$crc32}));
dataOutputStream.flush();
dataOutputStream.close();
InputStream inputStream = httpURLConnection.getInputStream();
String access$000 = Server.isToString(inputStream);
inputStream.close();
httpURLConnection.disconnect();
return access$000;
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
}

public static String get(String str, String str2, String str3, String str4) throws an executionexception, InterruptedException {
return (String) new C00001(str, str2, str3, str4).execute(new Void[0]).get();
}

private static String isToString(InputStream inputStream) throws IOException {
BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
for (int read = bufferedInputStream.read(); read != -1; read = bufferedInputStream.read()) {
byteArrayOutputStream.write((byte) read);
}
return byteArrayOutputStream.toString();
}
}


Okay! You can proceed to the last part of the task.

the

Sending data to the server


Below are the contents of the file cred:

cred
Admin
26892263f3d18dfabb665e2d2a680899b2577f0f4daa77287fadb3e4ae581ec1
NeoQuestNeoQuestNeoQuestNeoQuestneoquestneoquestneoquestneoquestneoquest


First comes the username, then key and comment.

If you send it, you will get a message that the login is already in use.

If you change the username, the server swears is not true CRC32 text.

If you send the original signature and the modified data, the server reports that the signature does not match.

So looks like the IDA algorithm the checksum:



Based on the foregoing, it follows that the need to send such data, which will correspond to the original signature, but with the correct login. As the unit is in CRC32 is only 4 bytes, and the signature is calculated on the basis of the entire contents of the file cred then you just need sproutit these 4 bytes:
the
#!/usr/bin/python3
from struct import pack

crc_tab = [
0, 0x2BDDD04F, 0x57BBA09E, 0x7C6670D1, 0x0AF77413C, 0x84AA9173,
0x0F8CCE1A2, 0x0D31131ED, 0x0F6DD1A53, 0x0DD00CA1C, 0x0A166BACD,
0x8ABB6A82, 0x59AA5B6F, 0x72778B20, 0x0E11FBF1, 0x25CC2BBE, 0x4589AC8D,
0x6E547CC2, 0x12320C13, 0x39EFDC5C, 0x0EAFEEDB1, 0x0C1233DFE, 0x0BD454D2F,
0x96989D60, 0x0B354B6DE, 0x98896691, 0x0E4EF1640, 0x0CF32C60F, 0x1C23F7E2,
0x37FE27AD, 0x4B98577C, 0x60458733, 0x8B13591A, 0x0A0CE8955, 0x0DCA8F984,
0x0F77529CB, 0x24641826, 0x0FB9C869, 0x73DFB8B8, 0x580268F7, 0x7DCE4349,
0x56139306, 0x2A75E3D7, 0x1A83398, 0x0D2B90275, 0x0F964D23A, 0x8502A2EB,
0x0AEDF72A4, 0x0CE9AF597, 0x0E54725D8, 0x99215509, 0x0B2FC8546, 0x61EDB4AB,
0x4A3064E4, 0x36561435, 0x1D8BC47A, 0x3847EFC4, 0x139A3F8B, 0x6FFC4F5A,
0x44219F15, 0x9730AEF8, 0x0BCED7EB7, 0x0C08B0E66, 0x0EB56DE29, 0x0BE152A1F,
0x95C8FA50, 0x0E9AE8A81, 0x0C2735ACE, 0x11626B23, 0x3ABFBB6C, 0x46D9CBBD,
0x6D041BF2, 0x48C8304C, 0x6315E003, 0x1F7390D2, 0x34AE409D, 0x0E7BF7170,
0x0CC62A13F, 0x0B004D1EE, 0x9BD901A1, 0x0FB9C8692, 0x0D04156DD, 0x0AC27260C,
0x87FAF643, 0x54EBC7AE, 0x7F3617E1, 0x3506730, 0x288DB77F, 0x0D419CC1,
0x269C4C8E, 0x5AFA3C5F, 0x7127EC10, 0x0A236DDFD, 0x89EB0DB2, 0x0F58D7D63,
0x0DE50AD2C, 0x35067305, 0x1EDBA34A, 0x62BDD39B, 0x496003D4, 0x9A713239,
0x0B1ACE276, 0x0CDCA92A7, 0x0E61742E8, 0x0C3DB6956, 0x0E806B919, 0x9460C9C8,
0x0BFBD1987, 0x6CAC286A, 0x4771F825, 0x3B1788F4, 0x10CA58BB, 0x708FDF88,
0x5B520FC7, 0x27347F16, 0x0CE9AF59, 0x0DFF89EB4, 0x0F4254EFB, 0x88433E2A,
0x0A39EEE65, 0x8652C5DB, 0x0AD8F1594, 0x0D1E96545, 0x0FA34B50A, 0x292584E7,
0x2F854A8, 0x7E9E2479, 0x5543F436, 0x0D419CC15, 0x0FFC41C5A, 0x83A26C8B,
0x0A87FBCC4, 0x7B6E8D29, 0x50B35D66, 0x2CD52DB7, 0x708FDF8, 0x22C4D646,
0x9190609, 0x757F76D8, 0x5EA2A697, 0x8DB3977A, 0x0A66E4735, 0x0DA0837E4,
0x0F1D5E7AB, 0x91906098, 0x0BA4DB0D7, 0x0C62BC006, 0x0EDF61049, 0x3EE721A4,
0x153AF1EB, 0x695C813A, 0x42815175, 0x674D7ACB, 0x4C90AA84, 0x30F6DA55,
0x1B2B0A1A, 0x0C83A3BF7, 0x0E3E7EBB8, 0x9F819B69, 0x0B45C4B26, 0x5F0A950F,
0x74D74540, 0x8B13591, 0x236CE5DE, 0x0F07DD433, 0x0DBA0047C, 0x0A7C674AD,
0x8C1BA4E2, 0x0A9D78F5C, 0x820A5F13, 0x0FE6C2FC2, 0x0D5B1FF8D, 0x6A0CE60,
0x2D7D1E2F, 0x511B6EFE, 0x7AC6BEB1, 0x1A833982, 0x315EE9CD, 0x4D38991C,
0x66E54953, 0x0B5F478BE, 0x9E29A8F1, 0x0E24FD820, 0x0C992086F, 0x0EC5E23D1,
0x0C783F39E, 0x0BBE5834F, 0x90385300, 0x432962ED, 0x68F4B2A2, 0x1492C273,
0x3F4F123C, 0x6A0CE60A, 0x41D13645, 0x3DB74694, 0x166A96DB, 0x0C57BA736,
0x0EEA67779, 0x92C007A8, 0x0B91DD7E7, 0x9CD1FC59, 0x0B70C2C16, 0x0CB6A5CC7,
0x0E0B78C88, 0x33A6BD65, 0x187B6D2A, 0x641D1DFB, 0x4FC0CDB4, 0x2F854A87,
0x4589AC8, 0x783EEA19, 0x53E33A56, 0x80F20BBB, 0x0AB2FDBF4, 0x0D749AB25,
0x0FC947B6A, 0x0D95850D4, 0x0F285809B, 0x8EE3F04A, 0x0A53E2005, 0x762F11E8,
0x5DF2C1A7, 0x2194B176, 0x0A496139, 0x0E11FBF10, 0x0CAC26F5F, 0x0B6A41F8E,
0x9D79CFC1, 0x4E68FE2C, 0x65B52E63, 0x19D35EB2, 0x320E8EFD, 0x17C2A543,
0x3C1F750C, 0x407905DD, 0x6BA4D592, 0x0B8B5E47F, 0x93683430, 0x0EF0E44E1,
0x0C4D394AE, 0x0A496139D, 0x8F4BC3D2, 0x0F32DB303, 0x0D8F0634C, 0x0BE152A1,
0x203C82EE, 0x5C5AF23F, 0x77872270, 0x524B09CE, 0x7996D981, 0x5F0A950,
0x2E2D791F, 0x0FD3C48F2, 0x0D6E198BD, 0x0AA87E86C, 0x815A3823
]

def crc32(array, array_len):
v3 = 2910424328 # pre-Calculated value to the penultimate step
for v4 in array[-4:]:
v3 = (v3 >> 8) ^ crc_tab[getLbits(v3 ^ v4)]
return NOT(v3)

def checkCRC(item):
if crc32(item, len(item)) == 0x3E9A75C2:
print('CRC Found: %s' % item)

creds = b'AdminAdmin\r\n26892263f3d18dfabb665e2d2a680899b2577f0f4daa77287fadb3e4ae581ec1\r\nNeoQuestNeoQuestNeoQuestNeoQuestneoquestneoquestneoquestneoquestneo'
x1 = 0xFFFFFFFF
while x1 > 0:
checkCRC(creds + pack ('>I', x1))
x1 -= 1

In order to calculate the signature over the whole message it is possible to calculate in advance for the chosen site, and then just count the remaining 4 bytes. Run, and after a while we get the answer:

the
$ ./libneo.py
CRC Found: b'AdminAdmin\r\n26892263f3d18dfabb665e2d2a680899b2577f0f4daa77287fadb3e4ae581ec1\r\nNeoQuestNeoQuestNeoQuestNeoQuestneoquestneoquestneoquestneoquestneo\xfe\xa3\x0f#'

Now let's send it to the server and take the flag:

the
#!/usr/bin/python3
import requests
import base64
import json

def connect():
url = 'http://213.170.100.214/neoquest.php'
header = {'Content-Type': 'application/json'}
data = {"comment": "", "login": "AdminAdmin", "crc32": "3E9A75C2", "key_word": "26892263f3d18dfabb665e2d2a680899b2577f0f4daa77287fadb3e4ae581ec1"}
data['comment'] = base64.b64encode(b'NeoQuestNeoQuestNeoQuestNeoQuestneoquestneoquestneoquestneoquestneo\xfe\xa3\x0f#').decode()
data = json.dumps(data)
req = requests.post(url, data, header).text
if 'wrong' not in req and 'not your checksum!' not in req:
print(req)

connect()

After sending the data received the response:
login OK
key_word — OK
CRC32 — OK

ce91ecbefd83b69a88055e151800f4ebec7cda1a93b94cb0b420251a169e5abf

That's it!
Article based on information from habrahabr.ru

Популярные сообщения из этого блога

Approval of WSUS updates: import, export, copy

Kaspersky Security Center — the fight for automation

The Hilbert curve vs. Z-order