国产精品香蕉在线观看网,亚洲欧美精品综合在线观看,亚洲不卡av一区二区无码不卡,亚洲日本精品国产第一区二区

移動(dòng)安全 安全管理 應(yīng)用案例 網(wǎng)絡(luò)威脅 系統(tǒng)安全應(yīng)用安全 數(shù)據(jù)安全 云安全
當(dāng)前位置: 主頁(yè) > 信息安全 > 應(yīng)用安全 >

顫抖了嗎?九步逆向破解銀行安然令牌

時(shí)間:2014-03-05 16:30來(lái)源:TuZhiJiaMi企業(yè)信息安全專家 點(diǎn)擊:
作者歷經(jīng)千辛萬(wàn)苦,逆向了一個(gè)銀步履態(tài)口令的APP,過(guò)程艱辛盤曲。最后發(fā)現(xiàn),其實(shí)生成算法也不算很復(fù)雜,此中首要利用了 android_id 系統(tǒng)時(shí)候戳 作為生成變量。看起來(lái)高大年夜上的動(dòng)態(tài)口令
Tags應(yīng)用安全(1006)銀行安全(5)  

  作者歷經(jīng)千辛萬(wàn)苦,逆向了一個(gè)銀步履態(tài)口令的APP,過(guò)程艱辛盤曲。最后發(fā)現(xiàn),其實(shí)生成算法也不算很復(fù)雜,此中首要利用了 android_id 系統(tǒng)時(shí)候戳 作為生成變量??雌饋?lái)高大年夜上的動(dòng)態(tài)口令,在逆向工程師抽絲剝繭的闡發(fā)下,亦不外如斯哇~

  正文

  我此次要對(duì)全巴西最大年夜的銀行之一開(kāi)刀,我也常常會(huì)利用到這家銀行所供給的辦事。他利用包含用戶暗碼在內(nèi)的多種路子來(lái)驗(yàn)證用戶的身份。我逆向的動(dòng)態(tài)口令(OPT)也是此中一種,并且我把他移植到了Arduino-compatible 平臺(tái)上。

  免責(zé)聲明和更多的免責(zé)聲明

  在文章中我過(guò)去掉落了敏感的信息,以呵護(hù)無(wú)辜的人的好處。并且這項(xiàng)研究的成果也不足以讓我可以或許黑掉落他人的銀行賬戶。即便一個(gè)具有root權(quán)限的第三方歹意利用,在沒(méi)有足夠的賬戶信息的前提下,都不克不及摹擬動(dòng)態(tài)口令的生成。并且,此次研究并沒(méi)有發(fā)現(xiàn)任何代碼層面的縫隙,這家銀行的生成算法乃至比谷歌的認(rèn)證算法還要安然,乃至可以說(shuō)這篇文章是對(duì)該銀行APP安然性的一次嘉獎(jiǎng)。他的動(dòng)態(tài)生成算法,完全合適TOTP規(guī)范。把數(shù)據(jù)安然做到了極致。

  下面就是免責(zé)聲明 balabala一大年夜堆,請(qǐng)諒解小編就不翻譯了,想看的可以往原文地址不雅看

顫抖了嗎?九步逆向破解銀行安然令牌

  凡是對(duì)新用戶,他們會(huì)獲得一張密保卡,可是密??ㄟ@東西是極其不便利的。別的一個(gè)更好的編制就是利用android app生成一個(gè)動(dòng)態(tài)口令,大年夜膽的猜想,這個(gè)口令的生成可能把手機(jī)號(hào),或是pin值作為參數(shù)。在我每次刷機(jī),或改換手機(jī)的時(shí)辰,都要從頭綁定一遍這個(gè)安然辦事。當(dāng)然過(guò)程比較簡(jiǎn)單,但仍是令我不爽。所以我決定,逆向這個(gè)android APP做一個(gè)本身的動(dòng)態(tài)暗碼生成東西。

  Activating the application 激活利用

  在瀏覽源碼之前,我仍是喜好先把利用下載下來(lái)看看他長(zhǎng)啥樣,下面是三個(gè)階段的截圖。

顫抖了嗎?九步逆向破解銀行安然令牌

顫抖了嗎?九步逆向破解銀行安然令牌

顫抖了嗎?九步逆向破解銀行安然令牌

  第一張是安裝時(shí)的截圖,看一下他所需要的權(quán)限,這可不是開(kāi)辟人員為何好玩才加上往的。他們此中有些乃至有可能影響到動(dòng)態(tài)口令的生成。第二張是激活界面,需要填四個(gè)數(shù)字,這四個(gè)數(shù)字只能經(jīng)由過(guò)程給銀行打德律風(fēng)被奉告。第三幅圖就是,成功激活后生成的動(dòng)態(tài)口令。

  The toolset 東西相干

  逆向android利用需要用到的幾個(gè)小東西,鄙人面列出來(lái)了,其實(shí)就是網(wǎng)上常常利用的幾種東西。

  Android SDK

  供給adb這個(gè)強(qiáng)大年夜的號(hào)令行東西,提取apk文件,和獲得手機(jī)信息端賴它。

  dex2jar

  這個(gè)東西可以吧dex轉(zhuǎn)換成jar包的情勢(shì)

  JD, JD-GUI

  這個(gè)就是··java反編譯東西,直接出源碼

  Eclipse

  這個(gè)就不多說(shuō)了,地球人都知道。

  Getting the APK file from the phone 第一步:獲得APK文件

  這個(gè)相當(dāng)簡(jiǎn)單的嘛~??梢灾苯釉趐lay里面下,也可利用ADB在手機(jī)中把他抓出來(lái)。

  查找包名

  $ ./adb shell pm list packages | grep mybank

  package:com.mybank

  肯定路徑

  $ ./adb shell pm list packages | grep mybank

  package:com.mybank

  下載

  $ ./adb pull /data/app/com.mybank-1.apk

  2950 KB/s (15613144 bytes in 5.168s)

  第二步:解緊縮APK文件

  APK直接可以被解緊縮,其實(shí)他就是一個(gè)緊縮包文件,只是后綴不合。解緊縮今后的classes.dex文件中包含了java源代碼信息。

  解緊縮

  $ unzip com.mybank-1.apk

  (file list omitted for brevity)

  把dex轉(zhuǎn)換成jar文件

  $ mv classes.dex com.mybank-1.dex

  $ ./d2j-dex2jar.sh com.mybank-1.dex

  dex2jar com.mybank-1.dex -> com.mybank-1-dex2jar.jar

  第三步:看代碼

  把jar包放進(jìn)JD—GUI里面,便可以看到源代碼了。

顫抖了嗎?九步逆向破解銀行安然令牌

  很等閑的便可以發(fā)現(xiàn)幾個(gè)比較特別的包,br.com.mybank.integrador.token, br.com.othercompany.token , com.mybank.varejo.token毫無(wú)疑問(wèn)核心代碼就在里面,只不外代碼應(yīng)當(dāng)被混合了。

  第四步:經(jīng)由過(guò)程異常字符串反混合

  代碼中常常的良多字符串名都被混合了(其實(shí)是加密了,不外加密的秘鑰在代碼中能找到~,作者說(shuō)是混合就是混合把~),這比較蛋疼。要知道,代碼中的字符串會(huì)對(duì)逆向起到很大年夜的幫忙。

  public void trocaPINcomLogin(int paramInt, boolean paramBoolean, Perfil paramPerfil)

  {

  if (paramPerfil == null)

  throw new IllegalArgumentException(a.a("1p5/eEf/sl3kbeUcP509qg=="));

  if (!this.jdField_a_of_type_U.jdField_a_of_type_JavaUtilHashtable.contains(paramPerfil))

  throw new RuntimeException(a.a("86jcmKgr/ZshQu9aGVbuGscy2nHW4UEWqudRoUXhImQ=") + a.a("7u8KqqwqUD3a7FM339fp6pRrxUtQrHDMyqvZ6A2MurQ="));

  if ((this.jdField_a_of_type_BrComOtherCompanyTokenParamsGerenciador.isPinObrigatorio()) &&(!paramBoolean))

  throw new RuntimeException(a.a("aMsL/5kjkXKD4K1SvpTuuJZUS0U0fL19UT2GxjJ/QzQ="));

  Configuracao localConfiguracao = paramPerfil.getConfiguracao();

  if ((localConfiguracao.a().a()) &&(paramPerfil != this.jdField_a_of_type_BrComOtherCompanyTokenPerfil))

  throw new RuntimeException(a.a("ASszutKFJW3iqDb7X/+vqAcYxTLXN2SJOIs0ne596Pu3ZoRxjiiscwhV6fT70efX"));

  localConfiguracao.a().a(paramInt);

  localConfiguracao.a().a(paramBoolean);

  this.jdField_a_of_type_U.a(paramPerfil);

  if (!paramPerfil.equals(this.jdField_a_of_type_BrComOtherCompanyTokenPerfil))

  a(paramPerfil);

  }

  不外榮幸的是,在拋出異常的語(yǔ)句中,我們可以找到一些蛛絲馬跡,我們經(jīng)由過(guò)程不雅察可以發(fā)現(xiàn),混合字符串的函數(shù)是a.a。按照這些信息的提示,我們可以猜想a.a是一個(gè)解密有關(guān)的類。瓜熟蒂落,我們直接往a函數(shù)中闡發(fā)解密所利用的代碼。

  這是闡發(fā)完a類以后的一些額外收成

  p類是一個(gè)base64解密的類。

  b類,實(shí)現(xiàn)了AES的功能。搜刮這個(gè)類當(dāng)中的一些字符串,我發(fā)現(xiàn)它是收集上的一個(gè)開(kāi)源實(shí)現(xiàn) Paulo Barreto's JAES中的內(nèi)容。

  a類中的private static byte[]是混合所利用的秘鑰,可以經(jīng)由過(guò)程一個(gè)簡(jiǎn)短的法度來(lái)反混合。

  不外不幸的是,a.a不單單是JAES的AES加密的包裝,此中也包含著本身實(shí)現(xiàn)的一些加密。

  不外這都不是事兒,我仍是把a(bǔ).a中的解密函數(shù)用python實(shí)現(xiàn)了。

  def decodeExceptionString(str):

  aesKey =

  xorKey =

  blockSize = 16

  aes = AES(aesKey)

  stringBytes = Base64.decode(str)

  outputString = ""

  for blockStart in xrange(0, len(stringBytes), blockSize):

  encryptedBlock = stringBytes[blockStart:blockStart+blockSize]

  plaintextBlock = aes.decrypt(encryptedBlock)

  outputString += plaintextBlock ^ xorKey

  xorKey = encryptedBlock

  return outputString

  簡(jiǎn)而言之,除AES和混合秘鑰,這個(gè)類還實(shí)現(xiàn)了CBC(暗碼段鏈接)。

  實(shí)驗(yàn)一下上述代碼的功能

  $ ./decode "ASszutKFJW3iqDb7X/+vqAcYxTLXN2SJOIs0ne596Pu3ZoRxjiiscwhV6fT70efX"

  N?o possvel alterar PIN sem estar logado.

  這段葡萄牙語(yǔ)的意思是,it is not possible to change PIN without being logged in??磥?lái)代碼運(yùn)行的還不錯(cuò)。

  第五步:逆向核心代碼–隨奧秘碼生成過(guò)程

  解決了,字符串混合的標(biāo)題問(wèn)題,接下來(lái)個(gè)就要弄清晰隨奧秘碼的生成過(guò)程了~找啊找啊找啊找~~~~找了好久,我終究發(fā)現(xiàn)了一個(gè)切進(jìn)點(diǎn),br.com.othercompany.token.dispositivo.OTP這個(gè)類。下面是它拋出的一些異常,反混合以后我們可以看到原文。

  public String calculate() throws TokenException {

  int i = (int)Math.max(Math.min((this.a.getConfiguracao().getAjusteTemporal() + Calendar.getInstance().getTime().getTime() - 1175385600000L) / 36000L, 2147483647L), -2147483648L);

  a();

  if (i <0)

  throw new TokenException("Janela negativa"), i);

  int j = (0x3 &this.a.getConfiguracao().getAlgoritmos().a) >>0;

  switch (j)

  {

  default:

  throw new TokenException("Algoritmo inválido:" + j, i);

  case 0:

  return a(i);

  case 1:

  }

  return o.a(this.a.getConfiguracao().getChave().a(20), i);

  }

  很等閑讀懂,變量i是一個(gè)時(shí)候戳,從2007年4月11日到此刻的秒數(shù)除以36,36就是每個(gè)動(dòng)態(tài)口令的存活時(shí)候。

  至于為甚么是2007年4月11日,我就不知道了,大年夜概是法度員他老婆的生日 : )

  他還引進(jìn)了一個(gè)批改函數(shù)getAjusteTemporal(),為體味決各地區(qū)的時(shí)差標(biāo)題問(wèn)題。上文代碼中的o.a函數(shù)是用于天活潑態(tài)暗碼,他的兩個(gè)參數(shù)一個(gè)是剛才說(shuō)到的時(shí)候戳,還有一個(gè)是遺傳byte數(shù)組(應(yīng)當(dāng)是一個(gè)密鑰)。

  第六步:尋覓密鑰

  看一下,生成語(yǔ)句的這段調(diào)用,this.a.getConfiguracao().getChave().a(20) this.a 是一個(gè)Perfil (profile) 對(duì)象,getConfiguracao() 返回一個(gè)Configuracao (settings) 對(duì)象getChave()返回一個(gè)z類,a(int)返回一個(gè)byte數(shù)組,這個(gè)數(shù)組就是key。

  z類中的字符串,也顛末端混合,可是比較簡(jiǎn)單,反混合過(guò)程就不提了.查看一下c類中的a(int)函數(shù),是返回一個(gè)byte數(shù)組,長(zhǎng)度截取到參數(shù)值。Perfil (profile) 對(duì)象反而是由PersistenciaDB類成立的,這個(gè)類中也包含了良多被混合的字符串。

  a = a.a("DwYyIlrWxIS9ruNMCKH/PQ==");

  b = a.a("SceoTjidi0XqlgRUo9hcDw==");

  c = a.a("yrYBlcp8nEfVKUT9WSqTqA==");

  d = a.a("jUTzBfsP/AO/Kx/1+VQ3CQ==");

  e = a.a("Y56SnU/pIKROPCLHu7oFuw==") + b + a.a("38oyp4eW3xqT3TaMfWZ5RA==") + "_id" + a.a("3Q+FCEVH2PxZ31ms4WHHwNB40EbmtWzHPhwoaB1nM7lGr+9zZzuVpx5iZ4YR+KUw") + c + a.a("bYYIl6LtqthcUCCFFb7JCRSC8zr5hKIFXe5JHFCCkZA=") + d + a.a("ENCtPBu4RtFta2XI1GsQag==") + a.a("ImPhDy43f+Nr4G5ofkZz+g==");

  好在a.a的機(jī)制我們前面研究過(guò),翻譯出的原文以下。

  a = "token.db";

  b = "perfis";

  c = "nome";

  d = "data";

  e = "create table perfis (_id integer primary key autoincrement, nome text not null, data blob not null);";

  竟然是一條,SQL語(yǔ)句,真是很有趣,本來(lái)它利用數(shù)據(jù)庫(kù)來(lái)存儲(chǔ)建設(shè)信息。此中還有一個(gè)文件名token.db,很多是一個(gè)SQLite數(shù)據(jù)庫(kù)。緊接著經(jīng)由過(guò)程研究PersistenciaDB類中的 carregar(load)函數(shù),我們可以肯定這一設(shè)法,他經(jīng)由過(guò)程SQLiteDatabase類來(lái)拜候這個(gè)數(shù)據(jù)庫(kù)。

  不外我接下來(lái)發(fā)現(xiàn)數(shù)據(jù)blob(binary large object)在carragar函數(shù)中被aa.a(和上文中的a.a不是一碼事)這個(gè)函數(shù)加密了,這個(gè)函數(shù)領(lǐng)受blob數(shù)據(jù),和一串16個(gè)字符的密鑰。

  在研究aa.a這個(gè)函數(shù)之前。我們先研究一下解密Blob的密鑰,他作為carregar的一個(gè)參數(shù)傳遞進(jìn)來(lái)。由PersistenciaUtils這個(gè)類產(chǎn)生。下面是這個(gè)類的進(jìn)口。

  public class PersistenciaUtils {

  public static byte[] getChave(Context paramContext, byte[] paramArrayOfByte) {

  try {

  byte[] arrayOfByte = MessageDigest.getInstance("SHA-1").digest(getId(paramContext).getBytes());

  return arrayOfByte;

  } catch (NoSuchAlgorithmException localNoSuchAlgorithmException) {

  }

  return new byte[20];

  }

  public static String getId(Context paramContext) {

  String str = Settings.System.getString(paramContext.getContentResolver(), "android_id");

  if (str == null)

  str = "";

  return str;

  }

  }

  從代碼可以看出,先獲得android_id的SHA-1摘要,假定獲得掉敗就是用一個(gè)默許的16進(jìn)制hash字符串。

  利用adb看看我的android_id

  $ ./adb shell

  shell@hammerhead:/$ content query --uri content://settings/secure --projection name:value --where "name='android_id'"

  Row: 0 name=android_id, value=0123456789abcdef

  shell@hammerhead:/$ exit

  a.aa把blob分成良多段,96位的頭,16位的隨機(jī)數(shù),16位的標(biāo)簽和為加密數(shù)據(jù)預(yù)留的空間。

  進(jìn)一步研究aa這個(gè)類,我們的到了,以下的信息。

  類a實(shí)現(xiàn)了 EAX AEAD(Authenticated Encryption with Associated Data)

  類f實(shí)現(xiàn)了 CMAC (Cipher-based Message Authentication Code)

  類h實(shí)現(xiàn)了 CTR (counter) mode 告白計(jì)費(fèi)的功能

  以上三個(gè)類的算法實(shí)現(xiàn)都取自JAES庫(kù)

  類l實(shí)現(xiàn)了 SHA-1 哈希算法 ,有趣的是類PersistenciaUtil并沒(méi)有益用它,而是利用了MessageDigest這個(gè)函數(shù)來(lái)替代它。

  類m實(shí)現(xiàn)了 HMAC (keyed-Hash Message Authentication Code) 算法

  類n,包裝了l和m,供給了HMAC-SHA1接口

  最首要的我發(fā)現(xiàn),aa.a函數(shù)是經(jīng)由過(guò)程CMAC標(biāo)簽進(jìn)行加密的,寫出python的解密代碼。

  def decodeBlob(datablob, android_id):

  header = datablob[:96]

  nonce = datablob[96:112]

  tag = datablob[112:128]

  cryptotext = datablob[128:]

  key1 = SHA1(android_id)[:16]

  aes = AES(key1)

  cmac = CMAC(aes)

  cmac.update(header)

  key2 = cmac.getTag()

  eax = EAX(key2, aes)

  (validTag, plaintext) = eax.checkAndDecrypt(cryptotext, tag)

  if validTag:

  return plaintext

  假定EAX驗(yàn)證成功,aa.a返回解密的內(nèi)容給PersistenciaDB利用。

  再來(lái)看PersistenciaDB這個(gè)類,此中的a編制可以解析明文數(shù)據(jù)并把它變成一個(gè)perfil對(duì)象,并反序列化之,把其變成一個(gè)包含著bool,short,byte的數(shù)組。

  此中以下3個(gè),是關(guān)頭的數(shù)據(jù),按照偏移量反推出偏移。

  pin = int(blob[82:86])

  key = blob[38:70]

  timeOffset = long(blob[90:98])

  這就是密鑰所需要的三個(gè)數(shù)據(jù),只要三個(gè)都準(zhǔn)確,法度便可以正常運(yùn)行。

  第七步:深度理解代碼

  上文中獲得的秘鑰在OPT 類中被截取到20個(gè)字節(jié),并和時(shí)候戳一路傳送到o.a編制中,這個(gè)別例援引了上文提到的良多類,所以,比較輕松的就寫出了python代碼~

  def generateToken(key, timestamp):

  message = [0] * 8

  for i in xrange(7, 0, -1):

  message[i] = timestamp &0xFF

  timestamp >>= 8

  hmacSha1 = HMAC_SHA1(key)

  hmacSha1.update(message)

  hash = hmacSha1.getHash()

  k = 0xF &hash[-1]

  m = ((0x7F &hash[k]) <<24 | (0xFF &hash[(k + 1)]) <<16 | (0xFF &hash[(k + 2)]) <<8 | 0xFF &hash[(k + 3)]) % 1000000;

  return "%06d" % m

  根基時(shí)候戳是一個(gè)占8字節(jié)的長(zhǎng)整形,手動(dòng)把它轉(zhuǎn)換成大年夜端的byte數(shù)組,接著利用HMAC-SHA1,獲得hash最后四位作為整數(shù)讀取的索引。利用這個(gè)整形,mod 1000000,就是我們的隨奧秘碼了,簡(jiǎn)單的超乎我的想象~~

  頓時(shí)我在谷歌的TOTP認(rèn)證的實(shí)現(xiàn)代碼中,發(fā)現(xiàn)了很類似的一段。

  public String generateResponseCode(byte[] challenge)

  throws GeneralSecurityException {

  byte[] hash = signer.sign(challenge);

  // Dynamically truncate the hash

  // OffsetBits are the low order bits of the last byte of the hash

  int offset = hash[hash.length - 1] &0xF;

  // Grab a positive integer value starting at the given offset.

  int truncatedHash = hashToInt(hash, offset) &0x7FFFFFFF;

  int pinValue = truncatedHash % (int) Math.pow(10, codeLength);

  return padOutput(pinValue);

  }

  其實(shí)他們利用了基底蘊(yùn)同的算法。

  第九步:克隆

  此刻一個(gè)德州出品的Stellaris LaunchPad正躺在我面前,我預(yù)備了以下的庫(kù)~

  Cryptosuite

  Arduino的加密算法庫(kù) (包含 SHA and HMAC-SHA)

  RTClib

  JeeNodes and Arduinos所利用的輕量級(jí)時(shí)候日期庫(kù).

  2x16LCD_library

  A library for 2x16 LCD (like JDH162A or HD44780) written for Energia and Stellaris Launchpad (LM4F).

  RTC有一部門需要改進(jìn)。因?yàn)镾tellaris LaunchPad沒(méi)有板載實(shí)不時(shí)鐘,內(nèi)部時(shí)鐘需要在每次啟動(dòng)時(shí)設(shè)置,并且需要一臺(tái)電腦來(lái)輔助,這是很麻煩的工作。

  完全代碼以下。

  #include

  #include

  #include

  RTC_Millis RTC;

  void setup() {

  RTC.begin(DateTime(__DATE__, __TIME__));

  LCD.init(PE_3, PE_2, PE_1, PD_3, PD_2, PD_1);

  LCD.print("Token");

  LCD.print("valverde.me", 2, 1);

  delay(1000);

  LCD.clear();

  }

  char token[6];

  uint8_t message[8];

  long timestamp = 0;

  long i = 0;

  uint8_t key[] = {};

  void showToken() {

  long now = RTC.now().get() - 228700800 + 7200;

  i = now / 36;

  int timeLeft = now % 36;

  for(int j = 7; j >= 0; j--) {

  message[j] = ((byte)(i &0xFF));

  i >>= 8;

  }

  Sha1.initHmac(key, 20);

  Sha1.writebytes(message, 8);

  uint8_t * hash = Sha1.resultHmac();

  int k = 0xF &hash[19];

  int m = ((0x7F &hash[k]) <<24 | (0xFF &hash[(k + 1)]) <<16 | (0xFF &hash[(k + 2)]) <<8 | 0xFF &hash[(k + 3)]) % 1000000;

  LCD.print(m, 2, 1);

  LCD.print(36 - timeLeft, 2, 15);

  }

  void loop() {

  LCD.clear();

  LCD.print("Current token:");

  showToken();

  delay(1000);

  }

  最后作者還分享了一個(gè),解決Arduino時(shí)候標(biāo)題問(wèn)題標(biāo)小技能~這里省略啦~感歡愉愛(ài)好的同窗可以往原文看。

------分隔線----------------------------
  • 上一篇:沒(méi)有了
  • 下一篇:沒(méi)有了

推薦內(nèi)容