Здесь 3 задачи:
Во всех трёх нужно найти скрытый пароль, ввести его в приложении и получить флаг.
Как всегда первым делом устанавливаем приложение и открываем. Из основных элементов видим поле ввода пароля и кнопку подтверждения.
После ввода 123 и нажатия кнопки получаем следующее.
Далее, тоже как всегда, открываем приложение в jadx-gui. И сразу смотрим манифест.
<application android:theme="@style/AppTheme" android:label="@string/app_name" android:icon="@mipmap/launcher_ic" android:allowBackup="true" android:supportsRtl="true" android:roundIcon="@mipmap/launcher_ic">
<activity android:name="com.entebra.crackme0x01.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
В манифесте обнаруживаем только одну Активити - MainActivity. Далее заходим внутрь класса и в методе onCreate видим такие строки:
final EditText editText = (EditText) findViewById(R.id.password);
((Button) findViewById(R.id.submit)).setOnClickListener(new View.OnClickListener() {
Первая строка - это нахождение поля ввода. Вторая строка - это привязывание обработчика нажатия к кнопке. Нам это и нужно.
Обработчик нажатия выглядит примерно таким образом:
String flag = new FlagGuard().getFlag(editText.getText().toString());
if (flag != null) {
// сообщение об успехе и отображение флага
return;
}
// сообщение о неудаче
Как можно заметить, метод getFlag возвращает строку с флагом, но магия происходит внутри него, так как в этот метод передаётся введённый в поле ввода текст. Посмотрим на метод:
public String getFlag(String str) {
if (str.equals(new Data().getData())) {
return unscramble();
}
return null;
}
Видим просто сравнение введённой строки с результатом метода getData объекта класса Data. Флаг возвращается методом unscramle, который можно скопировать в тестовое приложение, после чего вызвать метод и получить флаг. Но пойдём другим путём.
Зайдём в метод getData.
public String getData() {
getClass();
return "s3cr37_p4ssw0rd_1337";
}
Как оказалось этот путь проще, здесь просто константой лежит пароль, который нам нужен. Введём его в приложении.
Первое задание решено. Полученный флаг: fl4g_f0r_cr4ckm3_0x01_hgf82f8bm
Устанавливаем приложение, запускаем и видим точной такой же экран с полем ввода и кнопкой подтверждения.
При вводе 123 и нажатии кнопки снова получаем диалоговое окно с текстом о неверном пароле.
Откроем apk файл в jadx-gui. В манифесте как и предыдущем приложении есть одна MainActivity.
В методе onCreate также ничего не изменилось, поменялись лишь аргументы метода getFlag, теперь туда передаётся Context и введённая строка. Внутри метода тоже почти ничего не поменялось, за исключением вызова метода getData с параметром Контекста.
public String getFlag(Context context, String str) {
if (str.equals(new Data().getData(context))) {
return unscramble();
}
return null;
}
Посмотрим на метод getData:
public String getData(Context context) {
this.secret = context.getString(R.string.secret);
return this.secret;
}
Видим получение строки secret из ресурсов по id R.string.secret. Значит наш пароль находится в строковых ресурсах приложения.
Чтобы найти значение строкового ресурса воспользуемся глобальным поиском (Ctrl+Shift+F), введём название secret, далее оставим только галочку на типе Resource.
Перейдём в ресурсы и найдём тем самым пароль.
<string name="secret">s0m3_0th3r_s3cr3t_passw0rd</string>
Пароль: s0m3_0th3r_s3cr3t_passw0rd
Введём пароль в приложении и получим флаг.
Второе задание решено. Полученный флаг: th1s_1s_fl4g_f0r_cr4ckm3_0x02_jh4bo
Устанавливаем приложение, запускаем и видим точной такой же экран с полем ввода и кнопкой подтверждения.
При вводе 123 и нажатии кнопки получаем диалоговое окно с текстом о том, что пароль слишком короткий.
При переборе более длинных комбинаций получаем другое сообщение при вводе текста 123456.
При вводе 1234567 получаем третье сообщение, что пароль слишком длинный.
Откроем apk файл в jadx-gui. В манифесте находится только одна MainActivity.
В методе onCreate ничего не изменилось по сравнению с первым заданием. Немного изменилась реализация метода getFlag.
public String getFlag(String str) {
if (new Data().isPasswordOk(str)) {
return generate();
}
return null;
}
Но по сути ничего не поменялось, просто названия методов отличаются. Посмотрим как реализован isPasswordOk.
public boolean isPasswordOk(String str) {
if (str.length() < this.password_length) {
lastError = "Password too SHORT";
return false;
} else if (str.length() > this.password_length) {
lastError = "Password too LONG";
return false;
} else if (str.length() != this.password_length) {
return false;
} else {
getClass();
if (!MD5Compare(str, "ac43bb53262e4edd82c0e82a93c84755")) {
lastError = "WRONG password entered";
return false;
}
getClass();
return MD5Compare(str, "ac43bb53262e4edd82c0e82a93c84755");
}
}
Можно увидеть знакомый текст из диалогового окна при вводе различных паролей. А также говорящий название метода: MD5Compare, в который передаётся строка str (вводится на экране приложения) и хэш, с которым будет проводиться сравнение.
Метод MD5Comapre
private boolean MD5Compare(String str, String str2) {
try {
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
messageDigest.update(str.getBytes());
byte[] digest = messageDigest.digest();
messageDigest.reset();
StringBuilder sb = new StringBuilder();
for (byte b : digest) {
String hexString = Integer.toHexString(b & 255);
while (hexString.length() < 2) {
hexString = "0" + hexString;
}
sb.append(hexString);
}
return sb.toString().contentEquals(str2);
} catch (Exception e) {
Log.e("Exception MD5 compare", e.getMessage());
return false;
}
}
Алгоритм хэширования MD5 уже давно имеет славу не самого лучшего алгоритма и существует большое число сервисов по подбору строки с таким же хэшем.
После попыток на нескольких сайтах нашёлся один, где выдало результат, им оказалась строка: 3#8H1J
Строка длиной 6 символов, что нам и требуется. Возможен случай, когда строка подберётся, но будет иметь другую длину, нам этот вариант не подобшёл бы. Пробуем ввести её и получаем флаг.
Третье задание решено. Полученный флаг: hERe_yOu_gO_tAkE_IT_