I am new to Android and java in general, i wanted to inspect how a specific app works, so i begun by de-compiling the app using apktool and then used jadx to browse the source files, everything makes sense so far, so i added a mitmproxy to inspect the network traffic from the app.
I know that the request reply is a JSON payload, however "some of them" are encrypted possibly using mcrypt or openssl? so tracing methods leads me to this file.
import java.security.NoSuchAlgorithmException;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class MC {
private IvParameterSpec a;
private SecretKeySpec b;
private Cipher c;
private native int getint();
public native String I6MOYF();
static {
System.loadLibrary("native-lib");
}
public MC(String str) {
this.a = new IvParameterSpec(str.getBytes());
this.b = new SecretKeySpec((I6MOYF() + String.valueOf(getint())).getBytes(), "AES");
try {
this.c = Cipher.getInstance("AES/CBC/NoPadding");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e2) {
e2.printStackTrace();
}
}
public byte[] encrypt(String str) throws Exception {
if (str == null || str.length() == 0) {
throw new Exception("Empty string");
}
try {
this.c.init(1, this.b, this.a);
return this.c.doFinal(a(str).getBytes());
} catch (Exception e) {
throw new Exception("[encrypt] " + e.getMessage());
}
}
private static String a(String str) {
int length = 16 - (str.length() % 16);
for (int i = 0; i < length; i++) {
str = str + 0;
}
return str;
}
public byte[] des(String str) throws Exception {
if (str == null || str.length() == 0) {
throw new Exception("Empty string");
}
try {
this.c.init(2, this.b, this.a);
Object doFinal = this.c.doFinal(hexToBytes(str));
if (doFinal.length > 0) {
int i = 0;
for (int length = doFinal.length - 1; length >= 0; length--) {
if (doFinal[length] == (byte) 0) {
i++;
}
}
if (i > 0) {
Object obj = new byte[(doFinal.length - i)];
System.arraycopy(doFinal, 0, obj, 0, doFinal.length - i);
return obj;
}
}
return doFinal;
} catch (Exception e) {
throw new Exception("[decrypt] " + e.getMessage());
}
}
public static byte[] hexToBytes(String str) {
byte[] bArr = null;
if (str != null && str.length() >= 2) {
int length = str.length() / 2;
bArr = new byte[length];
for (int i = 0; i < length; i++) {
bArr[i] = (byte) Integer.parseInt(str.substring(i * 2, (i * 2) + 2), 16);
}
}
return bArr;
}
}
i understand that it uses OpenSSL "AES/CBC/NoPadding" mode for for decrypting the payload, however i am at loss how to take the payload and do it manually.
here is an example of the payload the server gives, the app send key header, however changing and dropping it does not change the payload, so i concluded it's not using the key for the actual encryption
AwFpdchYa7twLSEwN884uGQ/CNoLKrGBxtwIXGcL9OQTPPh96I1uhuh85HXLw3XUikVCmKaKgnssGorqYuvHQELce3nAhnaeHDcEsMFIykeitgDWLXeCed6f9UXHn+XF8nC3arHVbhMgIW8bUlWMq6KygRb4jeUufRHJzJ7LK0q6TvY+rF+utv3i//3NCuKfmbiiMlBvyDdMWPIL83YywkdjLujbBn0RNaeqUDjE0I7xqYypWPjwPXH1DZPbnGFYHemJgNS8QKtFnbtiRwEhpzx2sEoe/NBIgvcXsYkRSgrt+Q==
So the main question is, how would you use the provided code to manually decrypt the payload?
EDIT:
as suggested by Mr. #Robert, i tried to see how the native functions being called, so i installed frida-server on android emulator, and here is the interesting call, not sure what to make of it
/* TID 0x10b1 */
15066 ms open(pathname="/data/app/com.friga.gameapp-lPtwMqeZ36x47-Yo8YDzOg==/lib/x86/libnative-lib.so", flags=0x0)
i guess this supposed to be the key? -lPtwMqeZ36x47-Yo8YDzOg==
I managed to solve the problem using frida by following "Frida hooking android part 5: Bypassing AES encryption" post by 11x256 blog
Related
I've got a class MCrypt in Java (in an android App), it's working well, but i need to translate it in Swift 4.
Here my Class in Java
public class MCrypt {
private String SecretKey = "0pw3av67$979cdxf";
private Cipher cipher;
private String iv = "xe95bmad7x5432p8";
private IvParameterSpec ivspec = new IvParameterSpec(this.iv.getBytes());
private SecretKeySpec keyspec = new SecretKeySpec(this.SecretKey.getBytes(), "AES");
public MCrypt() {
try {
this.cipher = Cipher.getInstance("AES/CBC/NoPadding");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e2) {
e2.printStackTrace();
}
}
public byte[] encrypt(String str) throws Exception {
if (str != null) {
if (str.length() != 0) {
try {
this.cipher.init(1, this.keyspec, this.ivspec);
return this.cipher.doFinal(padString(str).getBytes());
} catch (Exception e) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("[encrypt] ");
stringBuilder.append(e.getMessage());
throw new Exception(stringBuilder.toString());
}
}
}
throw new Exception("Empty string");
}
public byte[] decrypt(String str) throws Exception {
if (str != null) {
if (str.length() != 0) {
try {
this.cipher.init(2, this.keyspec, this.ivspec);
return this.cipher.doFinal(hexToBytes(str));
} catch (Exception e) {
System.out.println(e);
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("[decrypt] ");
stringBuilder.append(e.getMessage());
throw new Exception(stringBuilder.toString());
}
}
}
throw new Exception("Empty string");
}
public static String bytesToHex(byte[] bArr) {
if (bArr == null) {
return null;
}
int length = bArr.length;
String str = "";
for (int i = 0; i < length; i++) {
StringBuilder stringBuilder;
if ((bArr[i] & 255) < 16) {
stringBuilder = new StringBuilder();
stringBuilder.append(str);
//stringBuilder.append(AppEventsConstants.EVENT_PARAM_VALUE_NO);
stringBuilder.append(Integer.toHexString(bArr[i] & 255));
str = stringBuilder.toString();
} else {
stringBuilder = new StringBuilder();
stringBuilder.append(str);
stringBuilder.append(Integer.toHexString(bArr[i] & 255));
str = stringBuilder.toString();
}
}
return str;
}
public static byte[] hexToBytes(String str) {
if (str == null || str.length() < 2) {
return null;
}
int length = str.length() / 2;
byte[] bArr = new byte[length];
for (int i = 0; i < length; i++) {
int i2 = i * 2;
bArr[i] = (byte) Integer.parseInt(str.substring(i2, i2 + 2), 16);
}
return bArr;
}
private static String padString(String str) {
int length = 16 - (str.length() % 16);
for (int i = 0; i < length; i++) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(str);
stringBuilder.append(' ');
str = stringBuilder.toString();
}
return str;
}}
You can use it like that :
String testst = "03e91bc1d6c14ad806832f98cf567830561460c565c9f1db426be13e86007c53d8171e1e7d7265c24ad84a0d3c4d346c59bd676ed3f2b2a6c4bde44b541c6a9697c63244c84483817b59e3dd3ac9bd76b9665b1495df06ab64939644221e58bfbf3624cc06af1679833ad8f7430a6314e3eb0a449c59a1f9ea093fc670bd7abab73f0f9ce7e1f74de6a34f4e4bbde6a8c4a44befffbdb020382730c2f99fdcc54319478168150d188d482306c6cc15252b21fc4c569d25cf5d2135b03612c73cd4f066b97b08a8bcd3f82f3158dcd692c33752d3fd0d85f22c841bf4b7ab8adeb9963bc16702baa38e041bbdab9bd7bc786fd5330d3a9d277dbe0ef331e1e5db6a4fc56b19210f694d1be6bc7939027da7fed1b8c412780daf93c033149bb2a90fcda46a8f54edaee3316622e7ec84d3ea831c724607614584aa2b7b882181d4291d0b1a2c73d34a41778125a7a44f7a";
String mastring = new String(new MCrypt().decrypt(teststring));
Result is
{"tempsnews":false,"imagenews":"","comments":"","relatednews":"","rnews":"","banned":"","banneduk":"","bannedus":"","bannedca":"","bannedau":"","bannedfr":"","bannedeg":"","bannedsa":"","bannedma":"","bannedsportfr":"","bannedbefr":"","bannedae":"","bannedflashbn":"","bannedglobal":"","bannedhmo":"","bannedng":"","bannednigeria":""}
For swift i use CryptoSwift (https://github.com/krzyzanowskim/CryptoSwift) and i start working on the decrypt function.
From the first step it looks easier than in Java but i can not make it works.
This is my method in Swift:
func aesDecrypt(encryptedString: String,key: String, iv: String) throws -> String {
let input: [UInt8] = Array(encryptedString.utf8)
let decrypted: Array<UInt8> = try AES(key: Array(key.utf8), blockMode: CBC(iv: Array(iv.utf8)), padding: .noPadding).decrypt(input)
let decryptedData = Data(decrypted)
return String(bytes: decryptedData.bytes, encoding: .utf8) ?? "Could not decrypt"
}
and you can call like htaht :
var strencrypted = "03e91bc1d6c14ad806832f98cf567830561460c565c9f1db426be13e86007c53d8171e1e7d7265c24ad84a0d3c4d346c59bd676ed3f2b2a6c4bde44b541c6a9697c63244c84483817b59e3dd3ac9bd76b9665b1495df06ab64939644221e58bfbf3624cc06af1679833ad8f7430a6314e3eb0a449c59a1f9ea093fc670bd7abab73f0f9ce7e1f74de6a34f4e4bbde6a8c4a44befffbdb020382730c2f99fdcc54319478168150d188d482306c6cc15252b21fc4c569d25cf5d2135b03612c73cd4f066b97b08a8bcd3f82f3158dcd692c33752d3fd0d85f22c841bf4b7ab8adeb9963bc16702baa38e041bbdab9bd7bc786fd5330d3a9d277dbe0ef331e1e5db6a4fc56b19210f694d1be6bc7939027da7fed1b8c412780daf93c033149bb2a90fcda46a8f54edaee3316622e7ec84d3ea831c724607614584aa2b7b882181d4291d0b1a2c73d34a41778125a7a44f7a"
var k = "0pw3av67$979cdxf" // 16 Bit Key
var iv = "xe95bmad7x5432p8" // 16 Bit Vector
do {
let deccc = try aesDecrypt(encryptedString: strencrypted, key: k, iv: iv)
print(deccc)
} catch {
print('error')
}
I missed something somewhere, but where ! Thanks in advance
So I am making an app that sends secure messages to the specified IP address. I am using AES to encrypt the message and that part works great. I am able to encrypt the message and also decrypt it before I send the message. However, when I try to decrypt a message that has been recieved from the server, I can not decrypt it. It gets displayed in it's encrypted form.
I get this error "java.lang.NumberFormatException: Invalid int: "ne"" and I think it may have to do with the character encoding or something? Is a string altered in any way when it is sent over a network?
Here are snippets that may be related to the issue.
public static String encrypt(String seed, String cleartext)
throws Exception {
byte[] rawKey = getRawKey(seed.getBytes());
byte[] result = encrypt(rawKey, cleartext.getBytes());
return toHex(result);
}
public static String decrypt(String seed, String encrypted)
throws Exception {
byte[] rawKey = getRawKey(seed.getBytes());
byte[] enc = toByte(encrypted);
byte[] result = decrypt(rawKey, enc);
return new String(result);
}
public static byte[] toByte(String hexString) {
int len = hexString.length() / 2;
byte[] result = new byte[len];
for (int i = 0; i < len; i++)
result[i] = Integer.valueOf(hexString.substring(2 * i, 2 * i + 2),
16).byteValue();
return result;
}
public static String toHex(byte[] buf) {
if (buf == null)
return "";
StringBuffer result = new StringBuffer(2 * buf.length);
for (int i = 0; i < buf.length; i++) {
appendHex(result, buf[i]);
}
return result.toString();
}
This is where I do the decryption for the message to be displayed.
while (!goOut) {
if (dataInputStream.available() > 0) {
incoming = dataInputStream.readUTF();
if(encrypt == true) {
try{
msgLog += AESHelper.decrypt(seed,incoming);
} catch (Exception e) {
e.printStackTrace();
}
} else msgLog += incoming;
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
chatMsg.setText(msgLog);
}
});
This is encrypting the message:
OnClickListener buttonEncryptOnClickListener = new OnClickListener() {
public void onClick(View v) {
if (chatClientThread == null) {
return;
}
if (editTextSay.getText().toString().equals("")) {
return;
}
if(!editTextSay.getText().toString().equals("")){
String message = editTextSay.getText().toString();
encrypt = true;
int secLvl = Integer.parseInt(editTextSecurity.getText().toString());
String encryptedMsg;
try {
encryptedMsg = AESHelper.encrypt(seed, message);
textEncryptedmsg.setText(encryptedMsg);
textEncryptedmsg.setVisibility(View.VISIBLE);
} catch (Exception e) {
e.printStackTrace();
}
}
}
};
This is sending the message:
OnClickListener buttonSendOnClickListener = new OnClickListener() {
#Override
public void onClick(View v) {
if (editTextSay.getText().toString().equals("")) {
return;
}
if(chatClientThread==null){
return;
}
if (encrypt == true){
chatClientThread.sendMsg(textEncryptedmsg.getText().toString() + "\n");
} else {
chatClientThread.sendMsg(editTextSay.getText().toString() + "\n");
}
editTextSay.setText("");
textEncryptedmsg.setText("");
textDecryptedmsg.setText("");
encrypt = false;
incomingmsg.setVisibility(View.VISIBLE);
}
};
I'll assume that you're sending the ciphertext in a hex encoded form. DataInputStream#readUTF() reads a single Unicode character from the stream. Since you're sending hex characters this will mean that a single ciphertext byte can be constructed from two of such Unicode characters.
The problem is that AES operates on blocks. Trying to decrypt every single "half"-byte separately won't work.
You will need to rewrite the decryption method to either
use streaming decryption or
read the whole ciphertext and decrypt in one go.
If you want to try streaming decryption, then you have basically two options:
update the internal buffer of a Cipher object using the offset-version of Cipher#update() or
use a CipherInputStream.
Here is an (pseudo-code) example of reading the whole thing before trying to decrypt it:
StringBuilder incoming = new StringBuilder();
while (!goOut && ) {
incoming.append(dataInputStream.readUTF());
}
if(encrypt == true) {
try{
msgLog += AESHelper.decrypt(seed, incoming.toString());
} catch (Exception e) {
e.printStackTrace();
}
} else msgLog += incoming;
I found a way around the issue without using CipherInputStream.
Whenever I encrypted a message and sent it, the encryption/decryption algorithm would not decrypt the message received from the server. Since the output encrypted message was identical to what I sent, I printed the incoming encrypted message into a TextView then I copied the TextView into a String and decrypted it and it works great now.
while (!goOut) {
if (dataInputStream.available() > 0) {
final String incoming = dataInputStream.readUTF();
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
incomingmsg.setText(incoming);
mustDecrypt = incomingmsg.getText().toString();
if (encrypt)
try {
mustDecrypt = AESHelper.decrypt(seed, mustDecrypt);
msgLog += mustDecrypt;
} catch (Exception e){
e.printStackTrace();
}
else msgLog += mustDecrypt;
chatMsg.setText(msgLog);
}
});
}
Could anyone tell me how I would decrypt data (using Java) that has been encrypted with this PHP function?
PHP Code
public function pad($data, $blocksize = 16) {
$pad = $blocksize - (strlen($data) % $blocksize);
return $data . str_repeat(chr($pad), $pad);
}
public function decryptECB($data) {
return mcrypt_decrypt(MCRYPT_RIJNDAEL_128, self::BLOB_ENCRYPTION_KEY, self::pad($data), MCRYPT_MODE_ECB);
}
public function encryptECB($data) {
return mcrypt_encrypt(MCRYPT_RIJNDAEL_128, self::BLOB_ENCRYPTION_KEY, self::pad($data), MCRYPT_MODE_ECB);
}
I have tried most of the things here but most of them are without padding and even when I add padding they don't work.
Edit 1:
(From PHP)
The input looks like this: http://pastebin.com/2cyig9nh
Key is this:
M02cnQ51Ji97vwT4
And output is this: http://pastebin.com/XcA50UGH
(The Java code)
public class Mcrypt {
private SecretKeySpec keyspec;
private Cipher cipher;
private String SecretKey = "M02cnQ51Ji97vwT4";
public Mcrypt() {
keyspec = new SecretKeySpec(SecretKey.getBytes(), "AES");
try {
cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
}
}
public String encrypt(String text) throws Exception {
if (text == null || text.length() == 0)
throw new Exception("Empty string");
byte[] encrypted = null;
try {
cipher.init(Cipher.ENCRYPT_MODE, keyspec );
encrypted = cipher.doFinal(padString(text).getBytes());
} catch (Exception e) {
throw new Exception("[encrypt] " + e.getMessage());
}
return Base64.encodeBase64String(encrypted);
}
public byte[] decrypt(String code) throws Exception {
if (code == null || code.length() == 0)
throw new Exception("Empty string");
byte[] decrypted = null;
try {
cipher.init(Cipher.DECRYPT_MODE, keyspec );
decrypted = cipher.doFinal(new Base64().decode(code.getBytes()));
} catch (Exception e) {
throw new Exception("[decrypt] " + e.getMessage());
}
return decrypted;
}
private static String padString(String source) {
char paddingChar = ' ';
int size = 16;
int x = source.length() % size;
int padLength = size - x;
for (int i = 0; i < padLength; i++) {
source += paddingChar;
}
return source;
}
}
You are encoding and decoding to Base64 in your Java code, but your PHP code does not seem to perform any encoding/decoding whatsoever. This seems to be confirmed by what you posted on Pastebin. If you want to use strings instead of bytes - bytes are the only input accepted by modern ciphers - then you should make sure that the (character) encoding is correct on both sides. If you just want to use bytes, don't decode the binary in Java - the input is already in bytes, not text.
I've been developing an Android App and in certain part of the app I need to calculate the MD5 of a certain string. I've been using the following code, but every now and then the output string if the byte it has to convert to String is lower than 10, then it will miss a 0 in the two byte representation:
MessageDigest di = java.security.MessageDigest.getInstance("MD5");
di.update(cadena.getBytes());
byte mdi[] = di.digest();
StringBuffer md5= new StringBuffer();
for (byte b : mdi) {
md5.append(Integer.toHexString(0xFF & b));
}
For example, if I pass the string 109370 the MD5 it will have to return is 932ff0696b0434d7a83e1ff84fe298c5 but instead it calculates the 932ff0696b434d7a83e1ff84fe298c5.
That's because the byte array has a 4 and Integer.toHexString() is returning only 1 char array instead of two.
Any thought about how can I handle this?
Thanks!
below is the code that i am using:
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class MD5Encode {
private static String convertedToHex(byte[] data) {
StringBuffer buf = new StringBuffer();
for (int i = 0; i < data.length; i++) {
int halfOfByte = (data[i] >>> 4) & 0x0F;
int twoHalfBytes = 0;
do {
if ((0 <= halfOfByte) && (halfOfByte <= 9)) {
buf.append((char) ('0' + halfOfByte));
} else {
buf.append((char) ('a' + (halfOfByte - 10)));
}
halfOfByte = data[i] & 0x0F;
} while (twoHalfBytes++ < 1);
}
return buf.toString();
}
public static String MD5(String text) throws NoSuchAlgorithmException,
UnsupportedEncodingException {
MessageDigest md;
md = MessageDigest.getInstance("MD5");
byte[] md5 = new byte[64];
md.update(text.getBytes("iso-8859-1"), 0, text.length());
md5 = md.digest();
return convertedToHex(md5);
}
}
and use it by this way:
MD5Encode.MD5("your string here")
hope this will help you :)
You can use a java.util.Formatter:
Formatter fmt = new Formatter(md5);
for (byte b : mdi) {
fmt.format("%02x", b&0xff);
}
fmt.close();
Use this:
public String calculateMD5(String string) {
StringBuilder result = new StringBuilder();
try {
MessageDigest m = MessageDigest.getInstance("MD5");
m.update(string.getBytes("UTF8"));
byte s[] = m.digest();
for (int i = 0; i < s.length; i++) {
result.append(Integer.toHexString((0x000000ff & s[i]) | 0xffffff00).substring(6));
}
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("Password hash is unsupported by device android implementation.", e);
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException("Password hash is unsupported by device android implementation.", e);
}
return result.toString();
}
I have a Java snippet here, I was wondering if it is possible to translate to VB.Net, as I have no found a snippet for VB.Net - only this:
private static byte[] SHA1(final String in)
throws NoSuchAlgorithmException, UnsupportedEncodingException {
MessageDigest md = MessageDigest.getInstance("SHA-1");
md.update(in.getBytes("iso-8859-1"), 0, in.length());
return md.digest();
}
public static String decryptSHA1(String key, final String start) {
final String delim = "a";
if (start == null)
return null;
byte[] hashedkey;
byte[] password;
int i;
try {
hashedkey = SHA1(key);
} catch (final NoSuchAlgorithmException e) {
e.printStackTrace();
return start;
} catch (final UnsupportedEncodingException e) {
e.printStackTrace();
return start;
}
final String[] temp = start.split(delim);
password = new byte[temp.length];
for (i = 0; i < hashedkey.length; i++) {
final int temp2 = Integer.parseInt(temp[i]);
if (hashedkey[i] == temp2) {
break;
} else {
password[i] = (byte) (temp2 - hashedkey[i]);
}
}
return new String(password, 0, i);
}
Thanks for any advice.
The hardest part here seems to be redoing the SHA1 method. You just need to find the equivalent .NET library classes/methods. Judging from the names, you probably want the System.Text.Encoding class and the System.Security.Cryptography.SHA1 class. Off hand, the algorithm probably ends up something like this
Private Shared Function SHA1(input As String) As Byte()
Dim iso8859 = System.Text.Encoding.GetEncoding("iso-8859-1")
Dim inBytes = ios8859.GetBytes(input)
' This is one implementation of the abstract class SHA1.'
Dim sha As New SHA1CryptoServiceProvider()
Return sha.ComputeHash(data)
End Function
From there you should be able to convert the rest of the decryptSHA1 function yourself as it is just basic byte manipulation. I will note that the GetEncoding function says it throws ArgumentException if you pass an invalid code page name and there does not seem to be any equivalent exception for NoSuchAlgorithmException to worry about catching.