Как решить задачу по взлому кредитных карт DEF CON

Man

Professional
Messages
2,965
Reaction score
488
Points
83


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

Это испытание было скорее открытым вопросом, «что может пойти не так» для участников. Я подробно опишу некоторые потенциальные решения для задач по взлому карт, которые мы представили на DEF CON в этом году.

Кроме того, что-то всегда идет не так, когда вы проходите испытания в первый раз. Поэтому я расскажу и об этом.

Повторюсь, в этом году мы сделали SoftPOS — приложение для Android, которое принимает платежи NFC с карт. И мы выпускаем наши бесконтактные кредитные карты. Это практически те же карты, что и Visa или MasterCard, но другого бренда — карты PaymentVillage.Org. Поэтому вы не можете платить этими картами нигде, кроме как через наше POS-приложение. В то же время наше приложение не может использовать карты из любой другой платежной системы, например Visa или MasterCard. Мы даже использовали те же теги EMV, поэтому вы можете ссылаться на разные базы знаний тегов EMV, например https://emvlab.org/emvtags/, чтобы найти описание каждого тега.


Поскольку в этом году мы не смогли приехать в Вегас, мы передали 40 карт организаторам Retail Hacking Village, которые любезно предложили свою помощь.

Для конференции OFFZONE, которая прошла через пару недель после DEF CON, мы не смогли вовремя отправить наши карты с двойным интерфейсом — они застряли на венгерской таможне. Поэтому мне пришлось придумать обходной путь — мы взяли карты, которые уже были у организаторов с Java cards challenge 2019, и загрузили на них наш код. Единственная проблема здесь — они не бесконтактные. Поэтому нам нужно было перенести вызов на контактную настройку. Для этой цели я быстро набросал код Python, который копирует код на нашем SoftPOS:

POSSim.py​

Python:
from smartcard.System import readers
from smartcard.util import toHexString
import random
from datetime import date
import requests

r_list=readers()
i = 0
for r in r_list:
    print str(i)+": "+str(r)
    i = i+1

id = input("Select reader:")

connection = r_list[int(id)].createConnection()
connection.connect()

amount = input("Enter transaction amount without (,) E.g. for $10.01 enter 1001:")
if (amount>1000):
    exit("Price cannot be more than $10 as we do not accept PIN (yet).")
def APDU(cmd):
    res = ""
    for i in cmd:
        res = res + "%0.2X" % i
    print ">>"+res

    data, sw1, sw2 = connection.transmit( cmd )
    res = ""
    #print "%x %x" % (sw1, sw2)
    for i in data:
        res = res + "%0.2X" % i
    res = res + "%0.2X" % sw1+ "%0.2X" %sw2

    print "<<"+res

    return res

SELECT = [0x00, 0xA4, 0x04, 0x00, 0x0E, 0x32, 0x50, 0x41, 0x59, 0x2E, 0x53, 0x59, 0x53, 0x2E, 0x44, 0x44, 0x46, 0x30, 0x31, 0x00]
APDU(SELECT)

SELECT = [0x00, 0xA4, 0x04, 0x00, 0x07, 0xA0, 0x00, 0x00, 0x00, 0x04, 0x10, 0x10, 0x00]
APDU(SELECT)

#80 A8 00 00 11 83 0F 70 55 81 F9 00 00 00 00 01 00 22 08 03 08 40 00
#9F 37 04 -- Unpredictable Number
#9F 02 06 -- Amount, Authorised (Numeric)
#9A 03 -- Transaction Date
#5F 2A 02 -- Transaction Currency Code

today = date.today()

UN = []
UN_str = ""
for i in range (0,4):
    rand = random.randrange(0,255)
    UN.append(rand)
    UN_str = UN_str + "%0.2X" % rand

amount_full = str(amount).rjust(12,'0')
amount_list = []
for i in range(0,6):
    amount_list.append(int(amount_full[i*2:(i*2+2)],16))

GENERATE_AC = [0x80, 0xA8, 0x00, 0x00, 0x11, 0x83, 0x0F] + UN + amount_list
GENERATE_AC.append(int(today.strftime("%y"),16))
GENERATE_AC.append(int(today.strftime("%m"),16))
GENERATE_AC.append(int(today.strftime("%d"),16))
GENERATE_AC = GENERATE_AC + [0x08, 0x40] # Currency - USD
GENERATE_AC = GENERATE_AC + [0x00] # end byte

Response = APDU(GENERATE_AC)
ATC = Response[Response.find("9F3602", 0)+6:Response.find("9F3602", 0)+10]
ARQC = Response[Response.find("9F2608", 0)+6:Response.find("9F2608", 0)+22]
PAN = Response[Response.find("570E", 0)+4:Response.find("570E", 0)+20]
AIP = Response[Response.find("8202", 0)+4:Response.find("8202", 0)+8]

AuthString = "amount="+str(amount)+"&trans_type=EMV&9f36="+ATC+"&9f26="+ARQC+"&82="+AIP+"&5a="+PAN+"&9f37="+UN_str+"&5f2a=0840"+"&9a="+today.strftime("%y%m%d")+"&9f02="+amount_full

#print AuthString
#print "https://www.paymentvillageprocessing.com/auth_host.php?"+AuthString
response = requests.get("https://www.paymentvillageprocessing.com/auth_host.php?"+AuthString, verify=True)
print "======================================="

print response.content.replace("<br>", "\r\n")

Этот код будет работать с любой из наших кредитных карт Paymentvillage.Org, если у вас есть подходящие считыватели, например SC3310.

Статистика
- 16 человек совершили хотя бы одну транзакцию с помощью карты
- первую задачу решили четыре человека
- один человек частично решил вторую задачу

Задача 0. Взлом SoftPOS APK.

Стандарт PCI для SoftPOS - CPoC, определяет, насколько важен контроль среды выполнения. Практические шаги для этого требования включают в себя антиотладочные функции, такие как запутывание кода и аттестация среды и приложений. Аттестация может быть выполнена с использованием различных функций и решений Android. SafetyNet - одна из лучших функций контроля целостности, явно упомянутых в CPoC. Клянусь, однажды мы реализуем SafetyNet или подобную структуру в нашем SoftPOS, но в этом году наша ленивая команда DevOps использовала простой код для обнаружения root:

Root detection​

Code:
if ((!(new File("/system/bin/su")).exists()) &&
                    (!(new File("/system/xbin/su")).exists()) &&
                    (!(new File("/sbin/su")).exists()) &&
                    (!(new File("/system/su")).exists()) &&
                            (!(new File("/system/bin/.ext/.su")).exists()) &&
                                    (!(new File("/system/usr/we-need-root/su-backup")).exists()) &&
                                            (!(new File("/system/xbin/mu")).exists()) )
            {intent = new Intent(this, NFCCardReading.class);
                        ***

А также закрепление SSL-сертификата:
Code:
<pin-set>
   <pin digest="SHA-256">yBHZ4rCAO4LBhxV03oWwrZeURO+gM4Mrcny3bkqHx+U=</pin>
</pin-set>

Это дало бы нам некоторый поверхностный уровень безопасности: вы не можете запустить приложение на рутированных устройствах, где инструменты вроде Xposed SSLUnpinning помогут перехватывать трафик, и вы не сможете легко реализовать атаки MiTM на нерутированных устройствах. Это вызовет такие ошибки:
Code:
08-04 16:02:38.787 21908 21994 E AndroidRuntime: Caused by: javax.net.ssl.SSLHandshakeException: Pin verification failed

Я могу предложить три самых простых способа обойти эти проверки:
- декомпилируйте APK в SMALI, измените if-nez на if-eqz в разделе проверки root файла NfcHome.smali и используйте SSLUnpinning (https://repo.xposed.info/module/mobi.acpm.sslunpinning) на устройстве с root-правами;
- перепишите файлы манифеста Android, добавив свой сертификат в тег pin-set и организуйте атаку MiTM, используя нерутированное устройство;
- отладить URL-адрес для реконструкции запросов аутентификации карты, чтобы не было необходимости в исходном APK.

Давайте сделаем последний трюк — добавим отладочную строку в EmvParser$1.smali и запустим приложение:
Code:
85:       const-string v4, "log-tag"
86:       invoke-static {v4, v2}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
87:        invoke-static {v1}, Lorg/apache/commons/io/IOUtils;->toByteArray(Ljava/net/URL;)[B

Когда мы сможем отладить процесс, будет полезно записать все шаги коммуникации между картой и POS, а затем POS и сервером авторизации. Варианты здесь:
- Proxmark3 пассивно прослушивает все пакеты ISO14443 коммуникации.
- Два считывателя PN532, работающие как мост - мой любимый метод. Если вы хотите попробовать сами, используйте наш проект GitHub (https://github.com/a66at/NFCMiTM)
- Два мобильных телефона NFC и веб-сервер. Я использовал приложения Android с открытым исходным кодом для создания моста для некоторых наших видео (https://www.forbes.com/video/6064363471001/). Это незаметно, но когда вам приходится использовать два телефона с NFC, одну карту и еще один телефон для приложения SoftPOS, это становится слишком хлопотным.
- Отладка запросов и ответов на SoftPOS. Самый простой способ!

К счастью, наши "ленивые разработчики SoftPOS" оставили отладку в приложении. Так что вы можете увидеть все этапы коммуникации с помощью ADB:

ADB debug​

Code:
08-03 19:42:40.416 31959 32203 D com.peerbits.creditCardNfcReader.utils.Provider: Terminal: 80 A8 00 00 11 83 0F 70 55 81 F9 00 00 00 00 01 00 22 08 03 08 40 00

08-03 19:42:40.418 31959 32203 D com.peerbits.creditCardNfcReader.utils.Provider: Response: 77 24 9F 36 02 00 41 9F 26 08 A2 DC 5F 69 F9 87 B0 86 82 02 00 40 57 0E 12 34 80 86 80 00 67 25 D2 40 42 01 12 3F 90 00

08-03 19:42:40.419 31959 32203 D com.peerbits.creditCardNfcReader.utils.Provider: resp:

08-03 19:42:40.419 31959 32203 D com.peerbits.creditCardNfcReader.utils.Provider: 77 24 -- Response Message Template Format 2

08-03 19:42:40.419 31959 32203 D com.peerbits.creditCardNfcReader.utils.Provider:       9F 36 02 -- Application Transaction Counter (ATC)

08-03 19:42:40.419 31959 32203 D com.peerbits.creditCardNfcReader.utils.Provider:                00 41 (BINARY)

08-03 19:42:40.419 31959 32203 D com.peerbits.creditCardNfcReader.utils.Provider:       9F 26 08 -- Application Cryptogram

08-03 19:42:40.419 31959 32203 D com.peerbits.creditCardNfcReader.utils.Provider:                A2 DC 5F 69 F9 87 B0 86 (BINARY)

08-03 19:42:40.419 31959 32203 D com.peerbits.creditCardNfcReader.utils.Provider:       82 02 -- Application Interchange Profile

08-03 19:42:40.419 31959 32203 D com.peerbits.creditCardNfcReader.utils.Provider:             00 40 (BINARY)

08-03 19:42:40.419 31959 32203 D com.peerbits.creditCardNfcReader.utils.Provider:       57 0E -- Track 2 Equivalent Data08-03 19:42:40.419 31959 32203 D com.peerbits.creditCardNfcReader.utils.Provider:             12 34 80 86 80 00 67 25 D2 40 42 01 12 3F (BINARY)

08-03 19:42:40.419 31959 32203 D com.peerbits.creditCardNfcReader.utils.Provider: 90 00 -- Command successfully executed (OK)

08-03 19:42:40.419 31959 32203 D com.peerbits.creditCardNfcReader.utils.Provider: resp: Command successfully executed (OK)

08-03 19:42:40.421 31959 32203 D com.peerbits.creditCardNfcReader.utils.ResponseUtils: 2022-08-03 19:42:40.421 org.slf4j.impl.AndroidLoggerAdapter#log:55

08-03 19:42:40.421 31959 32203 D com.peerbits.creditCardNfcReader.utils.ResponseUtils:  Response Status <9000> : Command successfully executed (OK)

08-03 19:42:40.427 31959 32228 D log-tag : https://www.paymentvillageprocessing.com/auth_host.php?amount=100&trans_type=EMV&9f36=0041&9f26=A2DC5F69F987B086&82=0040&5a=1234808680006725&57=1234808680006725D2404201123F&9f37=705581F9&5f2a=0840&9a=220803&9f02=000000000100

Если вы хотите понять каждый этап оплаты, рекомендуем посмотреть видео Стивена Мердока о тегах EMV.

А также прочитайте наш технический документ, в котором мы описываем этапы транзакций для бесконтактных карт Visa.

Также видно, что разработчики не особо заморачивались с безопасностью. Все параметры передаются в GET, API-аутентификации нет, а SoftPOS отправляет данные карты открытым текстом во время запроса на авторизацию. Хотя наличие SSL и Certificate Pinning может быть достаточным даже для прохождения PCI-аттестации.

Для карточек контактов шаги коммуникации также отображались в выходных данных по умолчанию. Но самый простой способ найти URL был найден durcm. Вам нужно будет отключить подключение к Интернету, и полный URL будет раскрыт в исключении:
image.png


Теперь мы готовы решать реальные задачи по взлому карт, моделируя мошенничество с потоком транзакций!

Задача 1. Обойти ограничение в 10 долларов для бесконтактных платежей.

Лимит бесконтактной оплаты устанавливается в приложении:

image2.jpg


SMALI code​

Code:
if-le v0, v1, :cond_2
    invoke-virtual {p0, v2}, Lcom/peerbits/nfccardread/NfcHome;->findViewById(I)Landroid/view/View;
    move-result-object v0
    check-cast v0, Landroid/widget/TextView;
    const-string v1, "100"
    invoke-virtual {p1, v1}, Landroid/widget/EditText;->setText(Ljava/lang/CharSequence;)V
    const/16 p1, 0x64
    iput p1, p0, Lcom/peerbits/nfccardread/NfcHome;->amount:I
    const-string p1, "Price cannot be more than $10 as we do not accept PIN (yet)."
    invoke-virtual {v0, p1}, Landroid/widget/TextView;->setText(Ljava/lang/CharSequence;)V
    goto :goto_0

Перестройка приложения и изменение лимитов было бы самым простым решением. Но у нас также есть прямой доступ к потоку транзакций, поэтому давайте попробуем его использовать. Как выглядит запрос на авторизацию транзакции для платежа в размере 1$?

image3.jpg


Code:
GET /auth_host.php?amount=100&trans_type=EMV&9f36=0041&9f26=A2DC5F69F987B086&82=0040&5a=1234808680006725&57=1234808680006725D2404201123F&9f37=705581F9&5f2a=0840&9a=220803&9f02=000000000100 HTTP/1.1
Host: www.paymentvillageprocessing.com

Сразу бросается в глаза тот факт, что сумма транзакции появляется в запросе дважды: первый — параметр суммы, указывающий, сколько продавец взимает с покупателя. Второй — поле суммы EMV (9f02), используемое для генерации криптограммы:

image4.PNG


Для успешной генерации криптограммы нашей карте требуются сумма и валюта от SoftPOS.

В прошлом году мы показали, что такие платежные гиганты, как Visa, MasterCard и AMEX, позволяют авторизовать транзакции, в которых поля сумм различаются.

Например, я могу запросить криптограмму на сумму 1,01$, но я буду использовать ее для списания с карты суммы в 50$:

image5.PNG


Code:
https://www.paymentvillageprocessing.com/auth_host.php?amount=5000&trans_type=EMV&9f36=0043&9f26=EA423B5F9415B7F9&82=0040&5a=1234808680006725&57=1234808680006725D2404201123F&9f37=92D667DA&5f2a=0840&9a=220803&9f02=000000000101

Почему это возможно? Короткий ответ — «наследие». EMV не существовало пятьдесят лет назад, и запрос на авторизацию транзакции с магнитной полосой выглядел примерно так:

image6.png


Позже, когда поток EMV был интегрирован в существующие платежные схемы, Visa и MasterCard создали "Поле 55" - поле, в котором будут храниться все теги EMV. В нашей платежной схеме Поле 55 было заменено девятью параметрами EMV, отправленными отдельно:

9f36 (Счетчик транзакций приложения), 9f26 (Криптограмма транзакции), 82 (AIP), 5a (PAN), 57 (Эквивалент Track2), 9f37 (UN), 5f2a (Валюта транзакции), 9a (Дата), 9f02 (Сумма). Таким образом, в запросе авторизации у нас получилось два поля цены.

Правильным решением здесь было бы проверить согласованность между двумя полями «Сумма», если происходят операции EMV/NFC, но этого никто не делает.

Пять лет назад мы обнаружили ту же уязвимость в реализации Apple Pay, когда авторизованная сумма могла отличаться от списанной суммы (https://www.blackhat.com/docs/us-17...Future-Of-Applepwn-How-To-Save-Your-Money.pdf)

Из-за неоднозначности поля «Сумма» в спецификации платежа существует возможность злоупотребления транспортными или транзитными схемами Apple, Google и Samsung Pay и совершения мошенничества.

В итоге наша уязвимая платежная система, такая как Visa или MasterCard, будет взимать с вас цену, указанную в поле суммы, даже если представленная криптограмма разрешает совершенно другую цену.

Эту задачу решили четыре человека.

Несколько участников (Boringdreams, durcm, Endycoffeeman) пошли дальше. Они ввели отрицательное значение в поле суммы, например -50000, и этот запрос сделал счет в USD равным 500$. Это дало им все 500$, необходимые для решения следующей и последней задачи. Мне пришлось срочно исправить код и опубликовал подсказку, что это не правильное направление для задачи.

P.S.: Маловероятно, что подобная атака может быть обнаружена в реальном мире - 99% транзакций проходят через Visa-Net, MasterCard-Net или аналогичные, где операции с отрицательными значениями будут отклонены.

Задание 2. Используя одну карту, совершите платежи на общую сумму, эквивалентную 500$.

Шаг 1.
После снятия первых 100$ обработка ответа с полезной ошибкой "Карта **** Недостаточно денег!". Преступники могут использовать это для атак по проверке баланса.

Шаг 2. На счете USD больше нет денег, но наша платежная система поддерживает несколько валют; в противном случае не было бы необходимости в поле 5f2a EMV (валюта транзакции). Помимо EMV, мы используем коды валют ISO-4217. А что если мы поместим туда случайную валюту:

image7.jpg


Вот ценная информация - платежная система поддерживает три валюты:

Доллары США - 0840
Евро - 0978
MXN - 0484

А если мы изменим валюту с USD на EUR, транзакция будет авторизована!

image8.png


Подождите, как это возможно? Должна ли валюта быть частью криптограммы? Поскольку карта запрашивает поле валюты в списке PDOL, это не значит, что это поле является одним из входных данных для криптограммы. Мы обнаружили это в 2017 году, когда взломали самую популярную версию бесконтактной криптограммы Visa, CVN17. Оказалось, что многие поля, запрашиваемые картами Visa, используются только для этапов управления рисками на карте и не будут проверяться во время авторизации транзакции.

Но если мы попытаемся сгенерировать криптограмму для другого платежа в EUR, мы получим ошибку «Извините! Эта карта не поддерживает более одной транзакции в месяц!». Что с этим делать? Давайте попробуем печально известную атаку с повторным воспроизведением криптограммы. Напомню, что криптограмма всегда имеет две переменные, изменяющиеся при каждой транзакции: ATC — последовательный счетчик транзакций и UN — случайные 4 байта, генерируемые терминалом. Но что, если ATC, UN и все остальные поля одинаковы? Это означает, что криптограмма действительна! Это означает, что точно такая же криптограмма может быть использована для авторизации более чем одной транзакции. Многие банки по-прежнему не проверяют ATC на предмет неисправностей и допускают атаки с повторным воспроизведением криптограммы, но мы видим доказательства того, что некоторые банки извлекли урок и изменили свои правила, чтобы противодействовать атакам с повторным воспроизведением.

С помощью атаки с повторным воспроизведением криптограммы удалось слить с карты еще 200 евро.

Boringdreams удалось украсть 100 со счета EUR, используя оригинальную криптограмму USD.

Шаг 3. При попытке совершить транзакцию в MXN вы получите еще одну информацию:

Извините! Эти карты не поддерживают EMV. Тип транзакции должен быть magstripe и параметры: track2, amount, currency.

Итак, это своего рода наследие — транзакции в мексиканских песо разрешены только с помощью карт с магнитной полосой. Транзакции с магнитной полосой не имеют функций безопасности, таких как криптограммы, поэтому вся информация, которую отправляет терминал, — это данные Track2. Но как мы можем получить данные track2, если на карте есть только EMV/NFC? Если вы прочтете наше последнее исследование о злоупотреблении Track2 Equivalent, станет очевидно, что некоторые банки будут принимать коды безопасности из данных EMV/NFC в транзакциях с магнитной полосой. Давайте прочитаем Track2 Equivalent с карты NFC и составим правильный запрос:
Code:
08-03 19:42:40.419 31959 32203 D com.peerbits.creditCardNfcReader.utils.Provider:       57 0E -- Track 2 Equivalent Data08-03 19:42:40.419 31959 32203 D com.peerbits.creditCardNfcReader.utils.Provider:             12 34 80 86 80 00 67 25 D2 40 42 01 12 3F (BINARY)
https://www.paymentvillageprocessing.com/auth_host.php?amount=10000&trans_type=magstripe&track2=1234808680006725D2404201123F&currency=0484

Повторив этот запрос 41 раз, мы сможем списать еще 4100 мексиканских песо (~200$), что в общей сложности составит 500$, снятых с карты!

Пользователь Boringdreams попытался применить эту атаку, но безуспешно. Хотя это не помешало ему занять первое место в нашем испытании.

Источник
 
Last edited:
Top