Как взломать банк? (или разбор 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-терминалом.

36130e7387ab6422ea360d66556e5d95.png231a89c5fecc4eee5581a2c153f1862f.png

На главном экране приложения есть два поля для ввода данных: сумма перевода и id приобретаемого товара.

Если ввести сумму больше 100 и нажать на верхнюю кнопку, приложение выдает ошибку: «Price cannot be more than $10 as we do not accept PIN (yet)».

a48249dbfde3ffff7dce2cf5c64c7254.png

При попытке перевести сумму в 100 центов или меньше, приложение начнет ругаться, что оно запущено на устройстве с рутом.

9b96d2b13f4fb0132a8c0a60bf0b94fa.png

Здесь нам требуется обойти проверку на рут и отключить SSL Pinning. Это можно сделать, воспользовавшись базовым функционалом objection (тулкит для динамического анализа мобильных приложений, использующий frida), а именно, двумя командами.

789aed3639bca068bbdfe788b4895cfb.jpeg

После этих действий можно было нажать на нижнюю кнопку, приложить карту и перевести деньги. Теперь можем приступать к поиску уязвимостей.

Недопустимое событие №1: вывод денег с украденных банковских карт

В данном НС требовалось списать деньги с украденной смарт-карты в размере $100000 и перевести их на свой счет. Нужно было обойти ввод пин-кода, так как при попытке просто перевести данную сумму сервер возвращал следующую ошибку:

1e45d6062e3b208dfe46f2118f2aba4c.png

Для этого посмотрим, какой запрос 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 на значение, которое означало бы, что пин-код проверен в оффлайн-режиме и был введен корректно.

Для этого нам потребуется декодер данного тега.

Если ввести в него стандартное значение, отправленное нашим приложением, получаем следующую расшифровку:

84fea883508d6ed9993ea677a6449ff2.png

Далее берем в руки побитовый декод данного тега и пытаемся понять, что нам требуется послать. Рассмотрим генерацию первого байта:

b50bc06c943bb6bc2ef379896a781b59.png

Можем заметить, что есть возможность передать значение «Enciphered PIN verification performed by ICC». Это нам и нужно, так как данное значение можно перевести как: «Зашифрованная проверка пин-кода выполнена картой».

Далее скажу честно: был легкий брутфорс b7 значения с помощью рук и редактирования запроса. В итоге получаем нужное нам значение тега 9f34 — 440002, что декодируется как:

06df0fe8983f3ef2a5c9a191fc802ffb.png

То есть пин-код был введен и проверен картой в оффлайн режиме.

Отправляем запрос, при котором сервер должен у нас требовать ввод пин-кода (то есть сумму больше $10), но с измененным значением тега 9f34:

6dff89a2754c72ef9b87d0874a475027.png

Основное сделано, мы перевели $100000 без ввода пин-кода, осталось закинуть их на свою карту. Напоминаю, что сейчас мы «купили» товар с id 1208 и нам нужно вернуть за него деньги. Если посмотреть внимательно, то можно заметить тег type, который равен purchase. При попытке подставить в данный тег что-то не особо осмысленное получим следующую ошибку:

421d231218e89fe3392bb7ef0eaa3047.png

Соответственно, есть type refund, который вернет нам деньги за покупку. Здесь все оказалось просто: делаем запрос на сервер с просьбой вернуть нам деньги, но вместо возврата на карту, с которой был выполнен платеж, вписываем свою:

7b5b3b57e46c12de11cf4f4bbe46e9c1.png

На этом этапе первое недопустимое событие было успешно реализовано и можно переходить ко второму.

Недопустимое событие №2: мошенничество с карточным процессингом

У нас была задача совершить мошенническую операцию с картой, которой у команды не было физически. Тут небольшая ремарка, если НС1 был засчитан, то НС2 нет: почему он был отклонен, объясню позже.

Помните про тег trans_type, который равнялся EMV? Тогда заглянем в приложение (все же таск на мобилки, да?) и посмотрим на генерацию запроса на сервер.

6874cb8af5a3b5395dd3074b0ba14c88.png

Из интересного стоит обратить внимание на следующие строчки:

2e98b40cdafef78791412216f505e2af.png

Из этих строк можно понять, что существует два типа транзакций: CNP и EMV. EMV мы уже проэксплуатировали, попробуем послать CNP-запрос:

6a0b357a31d39477d24d144bb56af279.png

Ошибка: сервер хочет получить от нас cvc, exp_year, exp_month. Так как у нас есть своя карта, попробуем указать ее данные и послать запрос:

96d48ae5ed53ea8c80fc6e8f1167664d.png

Транзакция выполнена успешно, теперь нам доступен новый формат общения с сервером. Так как у нас было две карты, мы можем понять, что у StandOff карт exp_year и exp_month одинаковы и равны 24 и 01 соответственно. Следовательно, остается забрутить cvc-код одной из карт и перевести с нее деньги. Для этого переходим в Burp Intruder и пытаемся набрутать любую карту.

Но тут есть одна оговорка, брутать cvc-код мы можем только с минимальной задержкой 8 секунд. Долго? Да, но больше на тот момент ничего не оставалось.

Запускаем, ждем и надеемся:

776085ce95a4e747db321007ad228f9e.png

Супер, нам удалось успешно найти правильные cvc и теперь мы можем списывать деньги с карты, которой нет в физическом доступе.

На этом этапе мы сдали отчет и соревнования подошли к концу, но данный НС организаторы нам не засчитали. Полагаю, что им важно было видеть, что мошенническая транзакция была сделана не с одной карты, а минимум с трех. Возможно, это условие ввели, чтобы отсеивать команды, которые проводили транзакции с двух смарт-карт, которые были в физическом доступе.

Надеюсь тем, кто участвовал в StandОff 11 было интересно прочитать про реализацию двух недопустимых событий и сравнить со своей тактикой. А тем, кто не принимал участие, — узнать, какие уязвимости были заложены и как их можно было обойти.

© Habrahabr.ru