15 Kasım 2015

Android SSL Pinning (Certificate Pinning) Hakkında Herşey - Bölüm 1

Güvenlikçiler olarak SSL pinning bizi iki açıdan ilgilendiriyor; birincisi sızma testlerini yaparken işimizi güçleştiriyor, ikincisi ise araya girme (MITM) saldırılarına karşı uygulanması gerektiğine dair raporlarımızda yer alıyor. Makalemizin adı, Android SSL Pinning, çalışmamızın sınırlarını da çiziyor. Bu yazıda sadece Android ortamına değiniyoruz. iOS ortamına farklı bir makalede değineceğiz.


Bu makalede BTRisk olarak konuyu tüm açıları ile inceledik. Sertifika otoritesinin kurulmasından test edilecek bir uygulamada SSL pinning’in aşılması için hangi düzenlemeleri yapmamız gerektiğine kadar tüm noktalara değiniyoruz.

Öncelikle SSL pinning’in ne anlama geldiğini açıklayalım. Certificate Pinning olarak da geçen bu yöntem ile programcı uygulamanın üzerinde çalıştığı cihazda hali hazırda yüklü olan Trusted sertifikalara güvenmiyor. Kendisi program içinde yine uygulama paketiyle birlikte gelmiş ve uygulamanın anlayacağı formattaki sertifika veya sertifikaları programatik olarak hafızaya yükleyerek sadece bu sertifika otoritelerinin imzaladığı sertifikalara güveniyor.

Bunun amacı ilk bakışta sadece güvenliği hedefliyormuş gibi görünüyor, çünkü bunu yapmaz isek cihaza aslında güvenilmeyen bir sertifika otoritesinin sertifikası da kullanıcı (veya bir saldırgan) tarafından yüklenmiş olabilir ve bu sertifika ile araya girme amaçlı olarak üretilmiş olan sahte sertifikalara da güvenilebilir. Bizim Burp’den elde ettiğimiz CA sertifikasını yüklemek ile yaptığımız tam olarak bu. Böylece uygulama ve uygulamanın iletişim kurduğu HTTP sunucu arasına rahatlıkla girebiliyoruz. Ancak bu yöntem programcılar tarafından self signed sertifikaların kullanımı amacıyla da kullanılabiliyor. Yani aslında güvenilen bir sertifika otoritesi (CA) tarafından imzalanmamış (dolayısıyla para ödenmemiş) ve kendi ortamımızda imzaladığımız bir sertifikayı da bu şekilde kullanabiliyoruz. Aksi takdirde uygulamamızın üzerinde çalıştığı platform bu sertifikaya güvenmeyeceğinden uygulama bir sertifika doğrulama hatası fırlatarak uygulama durdurulacaktı. Programcının amacı ne olursa olsun SSL pinning biz güvenlik testi yapanlar için bir problem.

SSL pinning’i teknik olarak tüm açılardan açıklamak amacıyla aşağıdaki adımları izleyeceğiz:

  • Bir sertifika otoritesi özel anahtarı oluşturacağız.
  • Sertifika otoritesi özel anahtarı ile yine sertifika otoritesine ait olan açık anahtarı imzalayacağız (bunun için elbette önce özel anahtarın çifti olan açık anahtarı oluşturacağız). Sertifika otoritesine güvenmenin tüm mantığı da burada yatıyor, sertifika otoritesi kendi sertifikasını imzaladığı için güvenebileceğimiz başka bir taraf yok.
  • Sunucumuz için bir özel anahtar oluşturacağız.
  • Sunucumuz için bu özel anahtarın çifti olacak açık anahtarı oluşturacağız.
  • Sunucumuzun açık anahtarı için sertifika otoritemize yönelik olarak bir imzalama isteği oluşturacağız. Bu isteği oluştururken sunucumuza bir ad vereceğiz. IP adresi verdiğimizde maalesef Android emülatör platformunda problem oluşuyor. Vereceğimiz bu adın IP adresine dönüştürülebilmesi için hem Android platformundaki hem de araya girmek için kullanacağımız Burp aracımızın üzerinde çalıştığı PC’deki “hosts” dosyasına bir kayıt girmemiz lazım, aksi takdirde iletişimi sağlayamayız.
  • Sertifika otoritemizin özel anahtarı ile sunucumuzun açık anahtarını imzalayacağız ve sunucu sertifikasını oluşturacağız.
  • Bir Apache web sunucu oluşturacağız ve HTTPS servisinin çalışması için gerekli ayarları yapacağız.
  • Sunucu özel anahtarını ve sertifikasını HTTPS servisimizde kullanabilmek için gerekli Apache ayarlarını yaptıktan sonra bu dosyaları doğru dizinlere yerleştireceğiz. Böylece SSL üzerinden hizmet veren bir HTTP servis altyapımız oluşmuş olacak.
  • PHP ile basit bir web servisi yazacağız. Bu servis JSON formatında bir POST isteğini yine JSON formatında yanıtlayacak.
  • Basit bir Android uygulaması yazacağız. Uygulamamız sadece bir aktivite barındıracak ve tek işlevi HTTPS servisimize bir istekte bulunmak ve dönen yanıtı bir text alanda görüntülemek olacak.
  • Android uygulamamızın içinde en başta oluşturduğumuz sertifika otoritesinin sertifikasına güvenilmesi için özel bir düzenleme yapacağız. Bu düzenleme sayesinde uygulamamızın kuracağı HTTPS bağlantısı cihaz üzerinde tanımlı güvenilen sertifikalara güvenmek yerine bizim tanımladığımız farklı bir CA sertifikasına güvenilecek. Bunu yapabilmek için özel bir işlem yapmamız gerekiyor. CA sertifikamızı Java ortamında güvenilen sertifikaların tutulduğu bir formattaki dosyanın içine yerleştireceğiz. Bunun için JDK içinde gelen “keytool” aracını kullanacağız.
  • “keytool” aracı ile oluşturduğumuz yeni KeyStore dosyasını Android projemizin “res/raw” dizini altına yerleştireceğiz. Bu kısım SSL pinning’i aşabilmek için de son derece önemli. Buraya kadar dikkatini dağıtmadan okuyabilmiş arkadaşların aklına gerebileceği gibi aslında bu keystore’un içine başka bir CA sertifikasını koyduğumuzda (örneğin Burp’ün kullandığı CA sertifikası gibi) bu sertifikaya da uygulama tarafından güvenilmeye başlanacak.
  • Android uygulamamız içinde uygulama paketine dahil ettiğimiz bu dosyayı okuyarak keystore olarak kullanacağız. Böylece uygulamanın SSL kontrollerini uyguladığı noktada bizim sertifikamız doğrulama amacıyla kullanılacak. Bu işlemin tam olarak nerede olduğunu, araya girme işlemi ile uygulamaya hata aldırarak, dinamik analiz ve kaynak kod inceleyerek tespit edeceğiz. Bu aşamada Java uygulamaları debug etmek için kullanılan JDB aracı ile de biraz haşır neşir olacağız ve biz güvenlikçilerin debugger’ları sıklıkla kullanmasına rağmen her durumda işimize yaramayacaklarını da bir örnek üzerinden açıklamış olacağız.
  • SSL pinning kontrolünü aşmak için çok bilinen ve kullanılan Android SSL Killer aracından ve bu araçla ilgili sıkıntıdan bahsedeceğiz.
  • Son olarak SSL pinning kontrolünü atlatmanın iki yolunu uygulayacağız. Bu yollardan birincisi uygulama paketini disassemble ederek smali koduna ulaşmak, bu kodu özel keystore’u kullanmayacak biçimde düzenlemek ve tekrar assemble etmek olacak. Diğeri ise yukarıda da bahsettiğim şekilde keystore dosyasına kendi araya girme aracımızın kullandığı CA sertifikasını yerleştirme yöntemi olacak.

Böylece Android ortamında SSL pinning ile ilgili aklınıza gelebilecek tüm soruları yanıtlamayı ve SSL pinning uygulayan bir uygulamaya karşı sizleri rahatlatmayı hedefliyoruz.

İlk işlem olarak sertifika otoritesinin sertifikasını ve sunucu sertifikasını oluşturacağız demiştik. Şimdi sertifika otoritemiz için bir özel anahtar oluşturalım:

# openssl genrsa -out btriskCA.key 2048


Şimdi kendi CA açık anahtarını oluşturarak kendi kendimize CA sertifikasını oluşturacağız (yani kendi özel anahtarımız ile self signed bir sertifika oluşturacağız).

# openssl req -x509 -new -nodes -key btriskCA.key -days 1024 -out btriskCA.pem



Sertifika otoritemizin anahtar çiftini ve sertifikasını oluşturduktan sonra web sunucumuzda kullanacağımız özel anahtarı oluşturalım.

# openssl genrsa -out btriskWWW.key 2048


Web sunucumuzda kullanacağımız sertifika için bir imzalama isteği oluşturalım (csr – Certificate Sign Request)

# openssl req -new -key btriskWWW.key -out btriskWWW.csr


Sertifika imzalama isteğimizde sunucu adımızı “btrmobile” olarak belirttik. Bu önemli olacak. BTRMobile bizim web uygulama ve mobil uygulama güvenlik denetim eğitimlerimizde kullandığımız ve akla gelebilecek hemen hemen tüm açıklıkları barındıran web, Android ve iOS uygulamalarının adı. Bizim Damn Vulnerable uygulamalarımız yani. Bu uygulamalar bizim sözde mobil operatörümüzün Online İşlem Merkezi (OIM) uygulamalarını simüle ediyor. “btrmobile” adını sunucu adı olarak kullanacağız. IP adresi ile gitmeye çalışmanın problem oluşturduğunu daha önce belirtmiştim.

Şimdi web sunucumuzun sertifikasını sertifika otoritemize imzalattırarak oluşturalım. Bunun için bir önceki adımda oluşturduğumuz imzalama isteğini ve ondan önce oluşturduğumuz CA özel anahtarını kullanacağız. Normalde bu işlemin karanlık odalarda çok güvenli bir ortamda oluşturulduğunu düşünün.

# openssl x509 -req -in btriskWWW.csr -CA btriskCA.pem -CAkey btriskCA.key -CAcreateserial -out btriskWWW.crt -days 500


CRT uzantılı sunucu sertifikamız da PEM uzantılı CA sertifikamızda olduğu gibi cleartext formatta. Aslında her iki uzantı da aynı tür sertifikaya işaret ediyor.


Sunucu sertifikamız hazır. Artık web sunucu platformundaki gerekli hazırlıklara başlayabiliriz. Apache web sunucusu için bir XAMPP paketini kullanacağız. Aşağıda XAMPP ile birlikte gelen Apache sunucusunun HTTP servis ayarlarının bulunduğu dosyayı görebilirsiniz.


Bu dosya bizi HTTPS servis konfigürasyonu için farklı bir dosyaya yönlendiriyor.


SSL konfigürasyon dosyasının bulunduğu yere ve dosyadaki ayarlara göz atalım.


Aşağıda ssl konfigürasyonu açısından önemli satırları bulabilirsiniz:

SSLEngine on
SSLCertificateFile "conf/ssl.crt/btriskWWW.crt"
SSLCertificateKeyFile "conf/ssl.key/btriskWWW.key"


Şimdi özel anahtar dosyamızı ve sertifikamızı uygun dizinlere yerleştirelim.



Artık Apache servisimizi başlatabiliriz. HTTPS servisimiz başladığında tarayıcımız ile localhost’a bağlanarak sertifikamızın aktif olup olmadığını inceleyebiliriz.


Yeterince büyük görünmüyor ama tarayıcımız aşağıda iki konudan şikayetçi; birincisi sertifikayı imzalayan tarafın tanınmıyor olması, ikincisi de sertifikanın btrmobile sunucu adına imzalanmış olması. Biz isteğimizi “localhost” sunucusuna yönelik yaptığımız için sunucu adında da sıkıntı oluştu. Tarayıcının bir mobil uygulamadan farkı kullanıcıya bu problemlere rağmen bir seçim hakkı sunması. Bu nedenle biz web uygulama denetimlerinde araya girdiğimizde Burp’ün CA sertifikasını tarayıcıya yüklemek zorunda dahi kalmadan testimizi gerçekleştirebiliyoruz.


Sertifikayı incelediğimizde imzalayan sertifika otoritesinin adının BTRisk CA olduğunu görüyoruz. Bu bilgiyi hatırlarsanız kendi sertifikamızı imzalarken tanımlamıştık.


Böylece HTTPS sunucumuzu hazırlamış olduk. Şimdi sıra bu sunucu üzerinden hizmet verecek olan web servis uygulamasını geliştirmekte. Yukarıda da belirttiğim gibi bu servisi PHP ile geliştireceğiz ve son derece basit bir işlem yapacak. Web servisi JSON formatında bir istek bekleyecek ve bu istekte bulunan parametre değerlerini bir başka JSON verisi olarak istemciye döndürecek.


1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
// Json istek formatı: {"prm1":"deneme", "prm2":"deneme2"}
 
// Json isteğini oku
$json_istek_verisi = json_decode(file_get_contents('php://input'));
 
// Json isteğini değişkene ata
$istek_prm1 = $json_istek_verisi->{"prm1"};
$istek_prm2 = $json_istek_verisi->{"prm2"};
 
// Json yanıtını oluştur
$yanit[] = array("istek_prm1" => $istek_prm1, "istek_prm2" => $istek_prm2);

// Json yanıt formatı:
// [{"istek_prm1":"deneme","istek_prm2":"deneme2"},{"istek_prm1":"aaa","istek_prm2":"bbb"},{"istek_prm1":"ccc","istek_prm2":"ddd"}]
 
// İçerik tipi başlığını json olarak belirle
header("Content-type: application/json");
 
// Yanıtı gönder
echo json_encode($yanit);
?>


Web servisimizi Burp ile test edelim ve çalıştığından emin olalım.

Yapacağımız istek aşağıdaki gibi (aşağıdaki istek mobil uygulamamızın yapacağı HTTP isteğinin bir benzeri):


1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
POST /servis.php HTTP/1.1
Host: btrmobile
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:29.0) Gecko/20100101 Firefox/29.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Content-Length: 35

{"prm1":"deneme", "prm2":"deneme2"}


Web servisimizin çalıştığından böylece emin olmuş olduk.

Şimdi SSL pinning işleminin özüne geliyoruz. Öncelikle mobil uygulamamızın güvenmesini istediğimiz ve sadece ona güvenmesini istediğimiz sertifikamızı Android uygulama tarafından anlaşılabilir bir formata çevireceğiz. Burada oluşturacağımız keystore dosyası tek bir sertifika içerecek ancak birden fazla sertifika içermesine engel olacak herhangi bir durum yok.

Bu işlem için kullanacağımız araç “keytool” ve JDK ile birlikte gelen bir araç bu. Aracı herhangi bir dizinden komut satırında kullanmak istiyorsak elbette keytool aracının da içinde bulunduğu “bin” dizinini PATH çevresel değişkenimize eklememiz lazım. Benim bilgisayarımda bu dizin şu şekilde: “C:\Program Files\Java\jdk1.8.0_60\bin”.


Daha sonra kullanacağımız “JDB” debugger uygulaması da bu dizinin içinde yer alıyor.


Şimdi keytool aracımızla bir keystore oluşturmadan önce bir başka java paketine daha ihtiyacımız olacak. Bu paketi indirebileceğimiz link aşağıdaki gibi:

http://repo2.maven.org/maven2/org/bouncycastle/bcprov-ext-jdk15on/1.46/bcprov-ext-jdk15on-1.46.jar

Keystore oluşturma komutumuz aşağıdaki gibi (aşağıdaki komutta keystore dosya isminin tüm harfleri küçük harf olmalı, Android Studio’nun kısıtı bu şekilde):

# keytool -importcert -v -trustcacerts -file "btriskCA.pem" -alias ca -keystore "btrmobiletruststore.bks" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "bcprov-ext-jdk15on-1.46.jar" -storetype BKS -storepass btrisk



Keystore dosyamızı oluşturduktan sonra dizinimizdeki dosyalara bir göz atalım.


Dosyamızın içeriği aşağıdaki gibi görünüyor:


Keystore’umuz da hazır olduğuna göre Android kodumuza geçebiliriz. Bu keystore dosyasını daha sonra Android projemize dahil edeceğiz.

Android SSL pinning makalemizin ikinci bölümünde Android uygulamamızı geliştirmeye başlayacağız.

Not: BTRisk tarafından geliştirilen makalelerin kısmen veya tamamen kopyalanarak yayınlanması durumunda intihal suçu duyurusu yapılacak ve yasal süreç başlatılacaktır.