└── README.md
/README.md:
--------------------------------------------------------------------------------
1 | ## Patching istanbulkart
2 |
3 |
4 |
5 |
6 |
7 | 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.
8 |
9 | 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](https://github.com/mitmproxy/mitmproxy) kullanarak API'ı incelemekti.
10 |
11 |
12 | - 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.
13 |
14 | - 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.
15 |
16 | API call'ları incelememden sonra cihaz eşleşmiyor olsa bile response içinde kart bilgisinin frontende ulaştığını görebildim.
17 |
18 | 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
19 | [apktool](https://github.com/iBotPeaches/Apktool/releases/tag/v2.9.3) ile disassemble ediyoruz:
20 | ```console
21 | apktool d -r base.apk
22 | ```
23 |
24 | MainActivity.smali'yi incelediğimizde bu instruction'ı görüyoruz:
25 | ```smali
26 | invoke-super {p0, p1}, Lcom/facebook/react/ReactActivity;->onCreate(Landroid/os/Bundle;)V
27 | ```
28 |
29 | 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.
30 |
31 | Bunun için [hbctool](https://github.com/bongtrop/hbctool) kullanıyoruz:
32 | ```console
33 | hbctool disasm index.android.bundle hasm
34 | ```
35 |
36 | 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:
37 | ```rust
38 | LoadFromEnvironment Reg8:27, Reg8:6, UInt8:1
39 | GetById Reg8:27, Reg8:27, UInt8:23, UInt16:31628
40 | ; Oper[3]: String(31628) 'digitalcard_unmatched_popup_title'
41 | ```
42 |
43 | 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:
44 | ```rust
45 | GetByIdShort Reg8:24, Reg8:24, UInt8:17, UInt8:46
46 | ; Oper[3]: String(46) 'StatusCodes'
47 |
48 | GetById Reg8:24, Reg8:24, UInt8:22, UInt16:30666
49 | ; Oper[3]: String(30666) 'ACCOUNT_PAIRED_DIFFERENT_DEVICE'
50 |
51 | JStrictNotEqualLong Addr32:132, Reg8:25, Reg8:24
52 | ```
53 | [Hermes byte code](https://p1sec.github.io/hermes-dec/opcodes_table.html)'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:
54 | ```rust
55 | JmpLong Addr32:132
56 | ```
57 |
58 | 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.
59 | ```rust
60 | ; Oper[3]: String(30067) 'REGISTER_DEVICE_SUCCESS'
61 | ; JStrictNotEqual Addr8:71, Reg8:25, Reg8:24
62 | ```
63 |
64 | Hepsini tamamladığımı umarak hbctool ile assembly etmeyi deniyorum:
65 | ```console
66 | jhc@pop-os:~$ hbctool asm hasm index.android.bundle-patched
67 | ...
68 | AssertionError: Overflowed instruction length is not supported yet.
69 | ```
70 | 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:
71 | ```console
72 | [*] Assemble 'hasm' to 'index.android.bundle-patched' path
73 | [*] Hermes Bytecode [ Source Hash: 4e928e8c1c844e7f47ff8bcd603b20d0ae21e1e4, HBC Version: 76 ]
74 | [*] Done
75 | ```
76 |
77 | APK'yı yeniden buildlemeden önce runtime'da native liblerin yüklenememesi hatasını giderebilmek için AndroidManifest'i düzenliyorum:
78 | ```console
79 | sed -i 's/android:extractNativeLibs="false"/android:extractNativeLibs="true"/' AndroidManifest.xml
80 | ```
81 |
82 | ve bundle'ı apk'ya yerleştirerek apktool ile buildliyorum:
83 | ```console
84 | cp -f index.android.bundle-patched base/assets/index.android.bundle
85 | apktool b base -o base-patched.apk
86 | ```
87 |
88 | 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:
89 | ```console
90 | apksigner sign --ks ks.keystore base-patched.apk
91 | apksigner sign --ks ks.keystore split_config.*.apk
92 | ```
93 |
94 | ve son olarak telefonuma adb ile kuruyorum:
95 | ```console
96 | adb install-multiple base-patched.apk split_config.arm64_v8a.apk split_config.tr.apk \
97 | split_config.xxhdpi.apk
98 | ```
99 |
100 | ve artık cihazın eşleşmedi hatası almıyorum :)
101 |
102 | 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ğı.
103 |
104 |
105 |
106 |
107 |
--------------------------------------------------------------------------------