Задание: Даниил Фукалов, SPbCTF Прохождение: Влад Росков, SPbCTF

К вам подходит старушка с кибернетической ногой и протягивает терминал: «Помоги бабушке! Купила вот умную избушку на курьих ножках, а она глупая! Говоришь ей вперед — она стоит как вкопанная. Ключ какой-то клянчит. Посмотри, что там в ней сломано?».

Провод терминала ведет к избушке: iZba.apk

Помогите бабушке переместить избушку на юго-восток леса.

Мобильное приложение

Запустим приложение на эмуляторе Андроида — можно выбрать официальный от гугла или, например, Bluestacks.

Untitled

Перед нами экран, требующий ввести валидный графический ключ. Никакие функции не работают, ключ неизвестен.

Всё, что остаётся — декомпилировать приложение и посмотреть его внутри. Хороший декомпилятор приложений под Андроид — JADX.

/* com.epriori.witch.control */
public class MainActivity extends AppCompatActivity {
    ImageButton down;
    ImageButton left;
    private LockView lockView;
    private PasswordChecker passwordChecker = new PasswordChecker();
    ImageButton right;
    private Animation shakeAnimation;
    private TextView textInputPassword;
}

/* com.epriori.witch.control.password */
public class PasswordChecker implements LockView.PasswordChecker {
    private double hashPassword = 0.4881882901949958d;
    private double eps = 1.0E-12d;

    @Override // com.epriori.witch.control.p004ui.lock.LockView.PasswordChecker
    public boolean isPasswordAccept(ArrayList<Integer> arrayList) {
        return !arrayList.isEmpty() && Math.abs(hashPassword(toNumber(arrayList)) - this.hashPassword) < this.eps;
    }

    private double hashPassword(double d) {
        double d2 = 0.0d;
        int i = 1;
        double d3 = 1.0d;
        double d4 = d;
        double d5 = 2.0d;
        while (d4 / d3 > this.eps) {
            d2 += (i * d4) / d3;
            i *= -1;
            d4 *= d * d;
            d3 *= (d5 * d5) + d5;
            d5 += 2.0d;
        }
        return d2;
    }

    private double toNumber(ArrayList<Integer> arrayList) {
        Iterator<Integer> it = arrayList.iterator();
        double d = 0.0d;
        double d2 = 1.0d;
        while (it.hasNext()) {
            d += it.next().intValue() * d2;
            d2 /= 10.0d;
        }
        return d;
    }
}

Решение 1. Ревёрсинг алгоритма проверки

Итак, судя по коду, проверка ключа заключается в следующем:

Способов определить, что делает функция hashPassword, несколько — увидеть глазами и вспомнить, поэкспериментировать с входными и выходными значениями, подобрать нужное входное значение бинарным поиском. Я спросил ChatGPT.

ChatGPT говорит про отличия от настоящего разложения, но это галлюцинации

ChatGPT говорит про отличия от настоящего разложения, но это галлюцинации

Получается, нам нужно дать проверке пароля на вход такое число, синус от которого будет равен 0.4881882901949958. Посчитаем арксинус:

In [1]: import math

In [2]: math.asin(0.4881882901949958)
Out[2]: 0.5100126535899954

Не похоже, чтобы мы могли ввести 0.510012… графическим ключом — много повторяющихся цифр. Вспомним, что синус — периодическая функция: сначал нарастает до 𝜋/2, потом убывает, и имеет период 2𝜋.

Нам подходят красные точки

Нам подходят красные точки