Android: SSL Unpinning
apk-mitm
JS-программа, которая перепатчивает апк, в том числе и для обхода пиннинга и доверие сертам пользовательским https://github.com/shroudedcode/apk-mitm#apk-mitm
Objection
android sslpinnning disableКак на новых android
В Android, начиная с 7 версии, сертификаты пользователя не подходят для перехвата трафика. Надо их добавлять в хранилище доверенных сертификатов (как и в Apple, только делается это нетривиально слегка): https://blog.ropnop.com/configuring-burp-suite-with-android-nougat/
При добавлении сертификата первым способом, может возникнуть проблема, что файловая система находится в режиме read-only. В этом случае:
mount -o rw,remount /system
добавляем сертификат
mount -o ro,remount /systemНастройка на эмуляторе
Выбираем образ с поддержкой Google API (но не Google Play) и далее по инструкции, что выше, или, если так будет понятнее, то вот красивая статья на medium: https://secabit.medium.com/how-to-configure-burp-proxy-with-an-android-emulator-31b483237053
Кратко (серт уже подготовлен):
$ adb root
$ adb remount
$ adb push 9a5ba575.0 /system/etc/security/cacerts
$ adb shell "chmod 664 /system/etc/security/cacerts/9a5ba575.0"
$ adb rebootВ “Settings -> Security -> Trusted Credentials” должен появится серт PortSwigger.
Далее, делаем снапшот эмулятора и пользуемся на здоровье!
Создание сертификатов, совместимых с системными сертификатами Android
Имя серта:
openssl x509 -in <some.cer> -subject_hash_old
Составные части:
Серт:
openssl x509 -in <some.cer>
Серт в текстовом виде:
openssl x509 -in <some.cer> -noout -text
Fingerprint:
openssl x509 -in <some.cer> -noout -fingerprint -sha1
Или все разом:
openssl x509 -in FiddlerRoot.cer -inform DER -text -subject_hash_old -sha1 -fingerprint
Главное, потом расположиить в правильном порядкеСистемные сертификаты на Android хранятся здесь: /system/etc/security/cacerts
Пользовательские сертификаты хранятся здесь: /data/misc/user/0/cacerts-added 
На более старых устройствах их можно найти здесь: /data/misc/keychain/cacerts-added
- Экспортируем сертификат из перехватываюжего прокси (Burp Suite; CER|DER) 
- openssl x509 -inform DER -in cacert.der -out cacert.pem
- openssl x509 -inform PEM -subject_hash_old -in cacert.pem |head -1
- mv cacert.pem *.0
- mv /sdcard/*.0 /system/etc/security/cacerts/- chmod 644 /system/etc/security/cacerts/*.0
- *chgrp - на root если вдруг группа не root стоит: - chgrp root <file>
- * - chown root <file>- если вдруг пользователь-владелец не root
- adb reboot- В trusted creds появляется наш сертификат 
Или все вместе одним скриптом — zcc.py (возможно, он немного недоотлажен).
Перехват через цепочку: android -> mitmproxy -> burp
Запускаем burp: localhost:<port_burp>
Запускаем mitmproxy: mitmproxy -p <port_mitmproxy> --mode upstream:localhost:<port_burp> --ssl-insecure
На телефоне: set proxy with <port_mitmproxy>
Из-за чего такие финтеля: не получается поставить серта бурпа на телефоне в системные. Серт mitmproxy становится норм!
Патчинг на примере Uber
1 Инструменты: JDK, Android SDK (Tool: zipalign)
Добавить их в PATH:
C:\path\to\jdk\bin
%USERPROFILE%\AppData\Local\Android\sdk\build-tools\23.0.2
2 Ставим Burp на прослушивание, в WiFi настройках телефона ставим наш прокси. Запускаем приложение - на аутентификации виснет.
3 Что случилось: Дело в том, что Burp Suite при перехвате HTTPs-соединений (а мы помним, что все соединения устройства проксируются через него) подменяет сертификат веб-сервера на свой, который, естественно, не входит в список доверенных. Круто! -> прокидываем наш сертификат на устройство в список доверенных сертификатов.
4 Опять пытаемся войти в свой аккаунт. Сейчас приложение Uber сообщит нам только о неудачной попытке аутентификации – значит прогресс есть, осталось только обойти certificate pinning.
5 Откроем Uber.apk как ZIP-архив. В /res/raw лежит ssl_pinning_certs_bk146.bks. По его расширению можно понять, что Uber использует хранилище ключей в формате BouncyCastle (BKS). Из-за этого нельзя просто заменить сертификат в приложении. Сначала нужно сгенерировать BKS-хранилище. Для этого качаем JAR (https://www.bouncycastle.org/latest_releases.html; bcprov-ext-jdk.jar) для работы с BKS.
Пример команды:
keytool -import -v -trustcacerts -alias mybks -file FiddlerRoot.cer -keystore ssl_pinning_certs_bk146.bks -storetype BKS -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath bcprov-ext-jdk15on-160.jar -storepass spassword
Копируем полученное хранилище в архив *.apk
6 Теперь надо подписать приложение
Удаляем из apk папку META-INF со старой подписью приложения и приступаем к генерации новой
Создаем хранилище ключей и генерируем в нем ключ для подписи apk: 
keytool -genkey -keystore mykeys.keystore -storepass spassword -alias mykey1 -keypass kpassword1 -dname "CN=ololo O=HackAndroid C=RU" -validity 10000 -sigalg MD5withRSA -keyalg RSA -keysize 1024
Подписываем только что сгенерированным ключом наш APK: 
jarsigner -sigalg MD5withRSA -digestalg SHA1 -keystore mykeys.keystore -storepass spassword -keypass kpassword1 Uber.apk mykey1
Теперь осталось выровнять данные в архиве по четырехбайтной границе: 
zipalign -f 4 Uber.apk Uber.apk_zipal.apk
Сконвертировать сертификат в системный для андроида (Python)
import os
import argparse
from binascii import hexlify
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
import OpenSSL.crypto
"""
script create cert: <hash>.0
android> # mount -o rw,remount /system
android> # mv <hash>.0 /system/etc/security/cacerts/
android> # chmod 644 /system/etc/security/cacerts/<hash>.0
android> # mount -o ro,remount /system
"""
def arg_parse():
    parser = argparse.ArgumentParser(description='Convert certificate -> Android System cert')
    parser.add_argument('-c', '--cert', help='input cert', default='', required=True)
    parser.add_argument('-o', '--output', help='output directory', default='.', required=False)
    args = parser.parse_args()
    return args
def md5(data):
    digest = hashes.Hash(hashes.MD5(), backend=default_backend())
    digest.update(data)
    return digest.finalize()
class CertConverter:
    def __init__(self, cert_path, output_dir='.'):
        self.cert_path = cert_path
        self.output_dir = output_dir
    def convert(self):
        with open(self.cert_path, 'rb') as cert_file:
            cert_data_pem = cert_file.read()
        if cert_data_pem[:2] == b'\x30\x82':
            file_type = OpenSSL.crypto.FILETYPE_ASN1
        else:
            file_type = OpenSSL.crypto.FILETYPE_PEM
        cert = OpenSSL.crypto.load_certificate(
            file_type,
            cert_data_pem
        )
        if file_type == OpenSSL.crypto.FILETYPE_ASN1:
            cert_data_pem = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert).decode()  # DER->PEM
        else:
            cert_data_pem = cert_data_pem.decode()
        cert_text = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_TEXT, cert).decode()
        cert_fingerprint_sha1 = cert.digest("sha1").decode()
        cert_subject_der = cert.get_subject().der()
        cert_subject_hash_old = hexlify(md5(cert_subject_der)[:4][::-1]).decode()
        out_cert_name = f'{self.output_dir}/{cert_subject_hash_old}.0'
        with open(out_cert_name, 'w') as android_system_cert:
            android_system_cert.write(f'{cert_data_pem}{cert_text}SHA1 Fingerprint={cert_fingerprint_sha1}')
        return self
if __name__ == "__main__":
    args = arg_parse()
    if not os.path.isfile(args.cert):
        print(f'{args.cert}: File not found')
        exit(-1)
    if not os.path.isdir(args.output):
        print(f'{args.output}: Directory not found')
        exit(-2)
    converter = CertConverter(args.cert, output_dir=args.output).convert()Last updated
Was this helpful?