└── 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 | --------------------------------------------------------------------------------