Skip to content

j-hc/patching-istanbulkart

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 

Repository files navigation

Patching istanbulkart

istanbulkart app'inin telefonun sıfırlanması ve app'e tekrar giriş yapılması durumunda dijital ve fiziksel bütün kartlarınıza 'Cihazın eşleşmedi' hatası vererek 153'ü aramadan ulaşmanızı engelleyen bir mekanizması var.

App'i patchliyip bu kısıtlamayı kaldırmak istediğimde ilk varsaydığım şey device id kontrolünün muhtemelen server-sided olduğu idi ama şansıma durum böyle değilmiş. Bunu görebilmek için ilk denediğim şey mitm-proxy kullanarak API'ı incelemekti.

  • Muhtemelen istanbulkart'ın son zamanlarda bir bankacılık uygulamasına kayıyor olmasından SSL pinning gibi basic korumalar implement edilmiş. Bir-iki yıl önce baktığımda onun bile olmadığını hatırlıyorum.

  • istanbulkart'ın da kullanığı Android için HTTP client librarysi olan OkHttp'nin kendi içinde SSL pinning özelliği var ama pen testing yapmak isteyen çoğu kişinin bir noktada kırdığı bir şey olduğundan aşması çok kolay, kısa bir Google search ile keşfedilebiliyor.

API call'ları incelememden sonra cihaz eşleşmiyor olsa bile response içinde kart bilgisinin frontende ulaştığını görebildim.

Patchlemek için istanbulkart APK'sını indirdiğimizde bir base.apk ve diğer split APK'lardan bundle formatında olduğunu görebiliyoruz. İlgilendiğimiz kısım base.apk içinde olduğundan apktool ile disassemble ediyoruz:

apktool d -r base.apk

MainActivity.smali'yi incelediğimizde bu instruction'ı görüyoruz:

invoke-super {p0, p1}, Lcom/facebook/react/ReactActivity;->onCreate(Landroid/os/Bundle;)V

ve istanbulkart'ın bir React Native app'i olduğunu anlıyoruz. React Native app'leri katlanılamayacak bir yavaşlıkta çalışmasının yanı sıra:) reverse engineerlaması da çok daha kolay. apktool'un apk'tan extract ettiği dosyalar içinde assets/index.android.bundle'a rastlıyoruz. Bu Facebook'un React Native'ı hızlandırmak için geliştirdiği (ve gayet başarısız) JavaScript bytecode'u olan Hermes bundle'ı... ve bunu disassemble etmemiz gerekiyor.

Bunun için hbctool kullanıyoruz:

hbctool disasm index.android.bundle hasm

hasm directory'sinde instruction.hasm'ı açarak patchlemek istediğimiz şey ile ilgili tamamen tahmini olarak keywordler aratıyoruz. Örneğin, hata mesajında gördüğümüz 'Cihazın eşleşmedi' textinden yola çıkarak 'match' aratıyoruz:

	LoadFromEnvironment 	Reg8:27, Reg8:6, UInt8:1
	GetById             	Reg8:27, Reg8:27, UInt8:23, UInt16:31628
	; Oper[3]: String(31628) 'digitalcard_unmatched_popup_title'

Byte code'da hata mesajını görüntülenmek üzere loadlayan instruction'ı buluyoruz. Aynı fonksiyonu incelemeye devam etmemiz üzerine muhtemelen API response'undan gelen hata kodlarına göre control flowu farklı adreslere dispatch eden koda rastlıyoruz:

	GetByIdShort        	Reg8:24, Reg8:24, UInt8:17, UInt8:46
	; Oper[3]: String(46) 'StatusCodes'

	GetById             	Reg8:24, Reg8:24, UInt8:22, UInt16:30666
	; Oper[3]: String(30666) 'ACCOUNT_PAIRED_DIFFERENT_DEVICE'

	JStrictNotEqualLong     Addr32:132, Reg8:25, Reg8:24

Hermes byte code'u incelerseniz JStrictNotEqualLong instruction'ının Reg8:25 ve Reg8:24 registerlarındaki değerlerin strict olarak (JavaScript'teki === operatörü) eşit olmaması durumunda Addr32:132 offsetine long jump yaptığını görebilirsiniz. Bizim aldığımız hatanın ACCOUNT_PAIRED_DIFFERENT_DEVICE olduğunu varsayarak bu instruction'ı aynı offsete sahip bir unconditional long jump ile değiştiriyorum:

	JmpLong Addr32:132

Bunu ACCOUNT_PAIRED_DIFFERENT_DEVICE veya REGISTER_DEVICE_SUCCESS gördüğüm her yerde uyguluyorum. Duruma göre bazı instructionları REGISTER_DEVICE_SUCCESS'ten uzağa jump yapmaması için comment-out yapmam da gerekebiliyor.

	; Oper[3]: String(30067) 'REGISTER_DEVICE_SUCCESS'
	; JStrictNotEqual     Addr8:71, Reg8:25, Reg8:24

Hepsini tamamladığımı umarak hbctool ile assembly etmeyi deniyorum:

jhc@pop-os:~$ hbctool asm hasm index.android.bundle-patched
...
AssertionError: Overflowed instruction length is not supported yet.

hbctool bir fonksiyon içindeki instruction boyutları toplamının orijinal bundle'dakinden yüksek olmasını desteklemediğinden bu hatayı dönüyor. Neyse ki 132 offsetli unconditional long jumplar yaptığımızdan kırpabileceğimiz birçok instruction var. JmpLong girdiğim yerlerin altındaki kafama göre birkaç instruction'ı zaten hiçbir zaman ulaşılamayacağı için kaldırıyorum ve hbctool patchlediğim bundle'ı assemble ediyor:

[*] Assemble 'hasm' to 'index.android.bundle-patched' path
[*] Hermes Bytecode [ Source Hash: 4e928e8c1c844e7f47ff8bcd603b20d0ae21e1e4, HBC Version: 76 ]
[*] Done

APK'yı yeniden buildlemeden önce runtime'da native liblerin yüklenememesi hatasını giderebilmek için AndroidManifest'i düzenliyorum:

sed -i 's/android:extractNativeLibs="false"/android:extractNativeLibs="true"/' AndroidManifest.xml

ve bundle'ı apk'ya yerleştirerek apktool ile buildliyorum:

cp -f index.android.bundle-patched base/assets/index.android.bundle
apktool b base -o base-patched.apk

Patchlenmiş base.apk ve diğer split APK'larımın hepsinin aynı imzaya sahip olması için hepsini aynı keystore ile signlamayı da unutmuyorum:

apksigner sign --ks ks.keystore base-patched.apk
apksigner sign --ks ks.keystore split_config.*.apk

ve son olarak telefonuma adb ile kuruyorum:

adb install-multiple base-patched.apk split_config.arm64_v8a.apk split_config.tr.apk \
						split_config.xxhdpi.apk

ve artık cihazın eşleşmedi hatası almıyorum :)

Bu "açığın" bir noktada kapatılacağını bilmekle beraber, bu zamana kadar nasıl server-sided bir check ile kaldığı da bir şaşkınlık kaynağı.

About

No description or website provided.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published