Как взломать банк? (или разбор Payment Village на StandOff 11)
Егор Филатов, младший аналитик отдела анализа защищенности Angara Security, подготовил разбор двух недопустимых событий в банковской сфере, которые могли бы нанести серьезный урон бизнесу. Но на практике, это моделирование кибератаки через мобильное приложение условного «банка», которое получили все участники майского StandОff 11.
Итак, подготовка к учениям:
До начала соревнований командам, которые успели связаться с организаторами, были высланы две смарт-карты: по легенде одна из этих карт ваша, вторая — украденная. В справочной информации по данному заданию организаторы написали: «Это практически те же карты, как Visa или MasterCard, с такими же уязвимостями». Забегая вперед, для реализации первых двух недопустимых событий эксплуатировать «уязвимости карты» не потребовалось. Кратко покажу, что же было записано на картах на примере одной из них:
{
"applications": [
{
"aid": [-96, 0, 0, 0, 3, 16, 16],
"amount": -1.0,
"applicationLabel": "Paymentvillage.com Challenge Card",
"leftPinTry": 20,
"listTransactions": [],
"priority": 1,
"readingStep": "READ",
"transactionCounter": 6
}
],
"at": "",
"atrDescription": [],
"state": "ACTIVE",
"track2": {
"cardNumber": "1234563175481004",
"expireDate": "Jan 1, 2024 12:00:00 AM",
"raw": [18, 52, 86, 49, 117, 72, 16, 4, -46, 64, 18, 1, -111, 79],
"service": {
"serviceCode1": "INTERNATIONNAL_ICC",
"serviceCode2": "NORMAL",
"serviceCode3": "NO_RESTRICTION"
}
},
"type": "VISA"
}
Из интересного и того, что сразу же бросается в глаза:
amount — количество денег на карте, которое мы не можем точно узнать;
leftPinTry — количество некорректных попыток, которые можно сделать до блокировки карты; transactionCounter — количество транзакций, совершенных с помощью данной карты; cardNumber и expireDate — данные карты, которые помогут нам далее в реализации одного из недопустимых событий.
Уже на соревнованиях (в 10:33 первого дня) участникам выслали ссылку на Android-приложение, которое представляло собой SoftPOS, то есть фактически являлось мобильным POS-терминалом.
На главном экране приложения есть два поля для ввода данных: сумма перевода и id приобретаемого товара.
Если ввести сумму больше 100 и нажать на верхнюю кнопку, приложение выдает ошибку: «Price cannot be more than $10 as we do not accept PIN (yet)».
При попытке перевести сумму в 100 центов или меньше, приложение начнет ругаться, что оно запущено на устройстве с рутом.
Здесь нам требуется обойти проверку на рут и отключить SSL Pinning. Это можно сделать, воспользовавшись базовым функционалом objection (тулкит для динамического анализа мобильных приложений, использующий frida), а именно, двумя командами.
После этих действий можно было нажать на нижнюю кнопку, приложить карту и перевести деньги. Теперь можем приступать к поиску уязвимостей.
Недопустимое событие №1: вывод денег с украденных банковских карт
В данном НС требовалось списать деньги с украденной смарт-карты в размере $100000 и перевести их на свой счет. Нужно было обойти ввод пин-кода, так как при попытке просто перевести данную сумму сервер возвращал следующую ошибку:
Для этого посмотрим, какой запрос POS-терминал отправляет на сервер.
GET
/auth_host.php? amount=100&trans_type=EMV&9f15=5000&9f34=1f0302&9f36=00B5&9f26=EBD9D9BB0A99BBF1&82=0040&5a=1234563175481012&9f37=5C5543CA&5f2a=0840&9a=230602&9f02=000000000100&merchant_id=1&type=purchase HTTP/1.1
User-Agent: Dalvik/2.1.0 (Linux; U; Android 12; Pixel 6 Build/SQ3A.220705.004)
Host: www.paymentvillageprocessing.com
Connection: close
Accept-Encoding: gzip, deflate
По тегу trans_type можно понять, что тип транзакции — EMV (Europay + MasterCard + VISA).
Отсюда вопрос: какие еще типы транзакций бывают.
Ответ: CNP.
В данном НС нам потребуется только тип транзакции EMV, на CNP мы заострим внимание во втором недопустимом событии.
Краткий обзор тегов, передающихся на сервер:
Номер тега | Название | Значение |
9f15 | Merchant Category Code | 5000 |
9f34 | Cardholder Verification Method (CVM) Results | 1f0302 |
9f36 | Application Transaction Counter (ATC) | 00B5 |
9f26 | Application Cryptogram | EBD9D9BB0A99BBF1 |
82 | Application Interchange Profile | 0040 |
5a | Application Primary Account Number (PAN) | 1234563175481012 |
9f37 | Unpredictable Number | 5C5543CA |
5f2a | Transaction Currency Code | 0840 |
9a | Transaction Date | 230602 |
9f02 | Amount, Authorised (Numeric) | 000000000100 |
Из всех этих тегов интерес для нас представляет тег под номером 9f34, который отвечает за проверку принадлежности карты и того, кто делает транзакцию. Если проще: в этом теге посылается информация о том, была ли транзакция подтверждена и каким способом.
Когда осуществляется оплата больше легитимной суммы, POS-терминалы требуют ввод пин-кода, и в этом случае у них есть две возможности. Либо подтверждать транзакцию самому и ждать ответ от сервера, либо запросить у владельца карты пин-код и проверить его в оффлайн-режиме. Соответственно все, что требуется, − это поменять значение 9f34 на значение, которое означало бы, что пин-код проверен в оффлайн-режиме и был введен корректно.
Для этого нам потребуется декодер данного тега.
Если ввести в него стандартное значение, отправленное нашим приложением, получаем следующую расшифровку:
Далее берем в руки побитовый декод данного тега и пытаемся понять, что нам требуется послать. Рассмотрим генерацию первого байта:
Можем заметить, что есть возможность передать значение «Enciphered PIN verification performed by ICC». Это нам и нужно, так как данное значение можно перевести как: «Зашифрованная проверка пин-кода выполнена картой».
Далее скажу честно: был легкий брутфорс b7 значения с помощью рук и редактирования запроса. В итоге получаем нужное нам значение тега 9f34 — 440002, что декодируется как:
То есть пин-код был введен и проверен картой в оффлайн режиме.
Отправляем запрос, при котором сервер должен у нас требовать ввод пин-кода (то есть сумму больше $10), но с измененным значением тега 9f34:
Основное сделано, мы перевели $100000 без ввода пин-кода, осталось закинуть их на свою карту. Напоминаю, что сейчас мы «купили» товар с id 1208 и нам нужно вернуть за него деньги. Если посмотреть внимательно, то можно заметить тег type, который равен purchase. При попытке подставить в данный тег что-то не особо осмысленное получим следующую ошибку:
Соответственно, есть type refund, который вернет нам деньги за покупку. Здесь все оказалось просто: делаем запрос на сервер с просьбой вернуть нам деньги, но вместо возврата на карту, с которой был выполнен платеж, вписываем свою:
На этом этапе первое недопустимое событие было успешно реализовано и можно переходить ко второму.
Недопустимое событие №2: мошенничество с карточным процессингом
У нас была задача совершить мошенническую операцию с картой, которой у команды не было физически. Тут небольшая ремарка, если НС1 был засчитан, то НС2 нет: почему он был отклонен, объясню позже.
Помните про тег trans_type, который равнялся EMV? Тогда заглянем в приложение (все же таск на мобилки, да?) и посмотрим на генерацию запроса на сервер.
Из интересного стоит обратить внимание на следующие строчки:
Из этих строк можно понять, что существует два типа транзакций: CNP и EMV. EMV мы уже проэксплуатировали, попробуем послать CNP-запрос:
Ошибка: сервер хочет получить от нас cvc, exp_year, exp_month. Так как у нас есть своя карта, попробуем указать ее данные и послать запрос:
Транзакция выполнена успешно, теперь нам доступен новый формат общения с сервером. Так как у нас было две карты, мы можем понять, что у StandOff карт exp_year и exp_month одинаковы и равны 24 и 01 соответственно. Следовательно, остается забрутить cvc-код одной из карт и перевести с нее деньги. Для этого переходим в Burp Intruder и пытаемся набрутать любую карту.
Но тут есть одна оговорка, брутать cvc-код мы можем только с минимальной задержкой 8 секунд. Долго? Да, но больше на тот момент ничего не оставалось.
Запускаем, ждем и надеемся:
Супер, нам удалось успешно найти правильные cvc и теперь мы можем списывать деньги с карты, которой нет в физическом доступе.
На этом этапе мы сдали отчет и соревнования подошли к концу, но данный НС организаторы нам не засчитали. Полагаю, что им важно было видеть, что мошенническая транзакция была сделана не с одной карты, а минимум с трех. Возможно, это условие ввели, чтобы отсеивать команды, которые проводили транзакции с двух смарт-карт, которые были в физическом доступе.
Надеюсь тем, кто участвовал в StandОff 11 было интересно прочитать про реализацию двух недопустимых событий и сравнить со своей тактикой. А тем, кто не принимал участие, — узнать, какие уязвимости были заложены и как их можно было обойти.