Hacker'ın Olmazsa Olmazı Assembly #5 Nerede Bu Devlet

Crack konusunu bitireceğimiz bu yazıda,debugger tricks veya anti-debugging tricks olarak bilinen ayıklayıcı tuzaklarını kısaca inceleyip,serinin asıl maksadı olan Hack konusuna giriş için altyapımızı hazırlıyoruz. Serinin başından beri verdiğimiz bilgiler bundan sonraki yazılarda anlatılan teknik olayları kavramamızı kolaylaştırmak amacını taşıyordu.Altyapıyı tamamlamak için yazımızın başlıklarına bir bakalım:

Bedava mı Sandın

Konuların anlaşılması için verdiğimiz basit örnekleri bir kenara koyarsak,yazılım dünyasında güvenlik söz konusu olunca tam bir kedi-fare oyunu yaşanmaktadır.Yazılım üreticileri izinsiz kopyalamaya karşı birçok önlemler geliştirirken, art niyetli cracker arkadaşlar ün, para, ego tatmini gibi nedenlerle bu önlemleri atlamayı kendilerine vazife edinmiş durumdalar.

Günlerden birgün,yazılımcı arkadaşlardan biri [laz olma ihtimali yüksek ;)] çıkıp şunu demiş:
"Yahu doğrulama için matematiksel işlemleri ne kadar zorlaştırsak da adamlar allem ediyo,kallem ediyo yazılımlarımızı kırıp kafalarına göre kullanıp,satıyorlar.Madem bu işi yapmak için kullandıkları programları biliyoruz,yazılımın içine bunları engelleyen koruma mekanizmaları ekleyelim,debugger felan kullanamasınlar."

Kısa süren şaşkınlık halleri ve hakkateen yaa efektlerinden sonra denemeler yapmışlar ve karşınızda, konumuz olan ayıklayıcı tuzakları.Bu konuyu da örneklendirerek nasıl oluyomuş birlikte görelim.Yine crackmes.de adresinden eğitim,eğlence amaçlı hazırlanmış Xrockmr by synamics programını kullanıyoruz.Siteye giriş yaptıktan sonra xrockmr diye aratırsanız, direk geliyor zaten.

Öğrendiğimiz komutlarla programı incelemeye başlayalım,bakalım bizi ne tür tuzaklar bekliyor:

ka@ka-vm ~/debug $ unzip rockmr.zip
Archive: rockmr.zip
inflating: crackme_x64 [64 bit çalıştıramayız]
inflating: crackme_x86 <--
ka@ka-vm ~/debug $ file crackme_x86
crackme_x86: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=0xbfd53565129185217ec15a973ecbca9d7091b7b3, not stripped
ka@ka-vm ~/debug $ ldd crackme_x86
linux-gate.so.1 => (0xb7781000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75bf000)
/lib/ld-linux.so.2 (0xb7782000)
ka@ka-vm ~/debug $ ./crackme_x86
What is the password?
1234
ka@ka-vm ~/debug $ echo $?
0
ka@ka-vm ~/debug $ strings ./crackme_x86
...
you
rock
...
You WIN, congratulations...
killall -q gdb
killall -q strace
Sem cheat, fedepe...
What is the password?

Elimizdeki bu detaylarla anlıyoruz ki ne tür bir parola girmemiz gerektiği söylenmiyor.Yanlış parola girmemize rağmen çıkış kodu yine de sıfır,yani kabakuvvet uygulamak pek mantıklı gözükmüyor.Doğru parola girilince alacağımız "You WIN..." mesajını kovalamak için gdb'ye başvurabiliriz ama bu sefer de Linuxte çalışan bir programın işlemini sonlandırmak için kullanılan killall komutlarının kullanıldığını görüyoruz.Biz yine de denemelerimizi yapalım.

ka@ka-vm ~/debug $ strace ./crackme_x86
execve("./crackme_x86", ["./crackme_x86"], [/* 32 vars */]) = 0
...
clone(child_stack=0, flags=CLONE_PARENT_SETTID|SIGCHLD, parent_tidptr=0xbf9e0c1c) = 2914
waitpid(2914,
ka@ka-vm ~/debug $ gdb -q crackme_x86
Reading symbols from /home/ka/debug/crackme_x86...(no debugging symbols found)...done.
(gdb) run
Starting program: /home/ka/debug/crackme_x86
[Takılıp kalıyor]

Strace ve gdb olmadan neler yapabiliriz diye düşünürsek objdump ile disassemble yapıp dinamik olmasa da statik kod analizi yapabiliriz. Kulağa hiç hoş gelmeyen bu yolu izlemektense kedi fare oyununu başlatıp önümüze çıkan engelleri aşmanın bir yolunu bulmamız gerekiyor.Bu yöntemlere bir sonraki başlığımızda anlatacağımız için şimdilik engelleri incelemeye devam edelim.

ka@ka-vm ~/debug $ readelf -s crackme_x86
Symbol table '.dynsym' contains 13 entries:
Num: Value Size Type Bind Vis Ndx Name
...
7: 00000000 0 FUNC GLOBAL DEFAULT UND srand@GLIBC_2.0 (2)
8: 00000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.0 (2)
9: 00000000 0 FUNC GLOBAL DEFAULT UND rand@GLIBC_2.0 (2)
10: 00000000 0 FUNC GLOBAL DEFAULT UND ptrace@GLIBC_2.0 (2)
...

rand ve srand fonksiyonları rastgele değerler üreten,başınıza belalar açabilecek fonksiyonlardır. Programın tamamını incelediğimizde göz korkutma amaçlı kullanılan bu fonksiyonları da gözardı ederek, diğer bir tuzağımıza geçelim.

C programlama dili kullanılarak yazılan programlarda asıl mevzu main fonksiyonu içinde döner demiştik. Bu örneğimizde de göreceğimiz üzere, dananın kuyruğu main fonksiyonu içerisinde değil,öncesinde ve sonrasında kopmaktadır.Detayları yazı için hazırlanan videoda görebilirsiniz.Tuzaklarımızı belirlediğimize göre şimdi de bunlardan nasıl kurtuluyoruz,bir sonraki başlığımızda görelim.

Açın Gençlerin Önünü

Tuzaklarla yolumuzu kesmeye çalışan gaddar yazılımcılara inat, karşı karşıya olduğumuz bütün engelleri tek tek geçelim.Öncelikle elimizi kolumuzu bağlayan killall engelinden kurtulmamız lazım.Dikkatlice incelediğimizde komut direk çağrılıyor, herhangi bir yol (PATH) belirtilmediğine göre, env komutuyla inceleyebildiğimiz PATH tanımlamalarından kurtulursak,bu komutlardan da kurtulmuş oluruz.Hemen deneyelim:

[Değişiklik yapmadan önce]
ka@ka-vm ~ $ env
...
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/us
ka@ka-vm ~/debug $ strace ./crackme_x86
execve("./crackme_x86", ["./crackme_x86"], [/* 32 vars */]) = 0
...
pid(4206,
[Path tanımlamalarını sıfırlayalım]
ka@ka-vm ~/debug $ export PATH=
[Değişiklik yaptıktan sonra]
ka@ka-vm ~/debug $ env
bash: env: No such file or directory
ka@ka-vm ~/debug $ /usr/bin/strace ./crackme_x86
execve("./crackme_x86", ["./crackme_x86"], [/* 32 vars */]) = 0
...
write(1, "What is the password?\n", 22What is the password?) = 22
read(0, [Parola girmemizi bekliyor]

Gördüğümüz gibi ilk engelimizi geçmeyi başardık.Artık strace ve gdb araçlarını kullanmamızı engelleyemiyor.Şimdi programımızı gdb'de inceleyip neymiş bu parola öğrenelim.Main fonksiyonu içerisinde dikkatimizi çeken noktaları inceliyoruz.

(gdb) b main
Breakpoint 1 at 0x8048620
(gdb) run
Starting program: /home/ka/debug/crackme_x86
sh: 1: killall: not found
sh: 1: killall: not found
Sem cheat, fedepe... [Hile yapıyormuşuz, yok canımmm :)]
Breakpoint 1, 0x08048620 in main ()
(gdb) disas
Dump of assembler code for function main:
=> 0x08048626 <+9>: mov DWORD PTR ds:0x804a03c,0x20756f79 [ uoy (you )-> password]
0x08048630 <+19>: mov DWORD PTR ds:0x804a040,0x6b636f72 [kcor (rock)-> password+4]
0x0804863a <+29>: mov DWORD PTR [esp],0x0
0x08048641 <+36>: call 0x8048400
0x08048646 <+41>: mov DWORD PTR [esp],eax
0x08048649 <+44>: call 0x8048450
0x0804864e <+49>: call 0x8048470
ka@ka-vm ~/debug $ ./crackme_x86
What is the password?
you rock

Daha ilk satırlarda strings komutuyla gördüğümüz you rock kelimelerinin password değişkenine atandığını görüyoruz.Bu kadar basit olamaz deyip denesek de doğru parolayı henüz bulamadığımızı görüyoruz.İlerleyen satırlarda time fonksiyonu ile sistem saati alınıp srand ve rand fonksiyonlarıyla rastgele değerler üretiliyor.you rock kelimesi bu rastgele değerlerle bir işleme tabi tutulsa bile bizim için önemli olan karşılaştırılmanın yapıldığı nokta,öyle ya bir şekilde doğruyu yanlışı kontrol etmesi lazım. İşin garip tarafı main fonksiyonu içerisinde bir sürü matematiksel işlem yapılmasına rağmen, doğrulamanın yapıldığı tek bir yer var.Hemen inceleyelim:

=> 0x080487b9 <+412>: mov DWORD PTR [esp],0x804a03c [Password değişkenin adresi]
0x080487c0 <+419>: call 0x80483f0 [fgets ile değer iste]
0x080487c5 <+424>: movzx eax,BYTE PTR ds:0x804a040 [5. karakteri eax'a gönder]
0x080487cc <+431>: cmp al,0x6f [0x6f mi diye kontrol et]
0x080487ce <+433>: je 0x80487d7 [Eğer öyleyse 442ye atla]
0x080487d0 <+435>: mov BYTE PTR ds:0x804a03d,0x61[Yok değilse 0x61 yap]
0x080487d7 <+442>: mov eax,0x0
0x080487dc <+447>: leave
0x080487dd <+448>: ret

Main fonksiyonu içerisinde yapılan tek kontrolun girdiğimiz değerinin 5. karakterinin o harfi olup olmadığını kontrol ettiğini, öyle değilse 2. karakteri a harfine çevirdiğini görüyoruz.Çok ilginç bir kontrol olsa da asıl merak edilen genel kontrol ve doğru parolayı girince "You WIN.." muhabbetinin nerede yapıldığını bulmak.Araştırmaya bir sonraki başlığımızda devam ediyoruz.

Nerede Bu Devlet

Haber sunan tombul bir abimizden miras kalan bu feryadı, içinden çıkamadığımız programıza haykırıyor ve derinlere inmeye başlıyoruz. İlk yazılarımızda _start fonksiyonunu inceliyorduk,sonra C ile yazılan programlara başlayınca main fonksiyonunu incelemeye başladık.Peki main fonksiyonunun öncesinde ve sonrasında neler oluyor kısaca değinelim.

Başlı başına bir makale olabilecek bu konuyu açıklayabilmek için bulabildiğim en iyi grafiği sizlerle paylaşmak istedim.Detaylı bilgi almak için resmin altındaki adresi ziyaret edebilirsiniz.Karşınızda mainden öncesi ve sonrası:

Resimde dikkat etmemiz gereken bölümler constructor yani yapıcı ve destructor yani yıkıcı işlevi gören bölümlerdir.Bunlar main fonksiyonunun öncesini ve sonrasını düzenlemeye yaradığına göre önümüze çıkan engellerin kaynağı buralar olabilir diyor ve incelemeye devam ediyoruz.

Komut satırından çalıştırdığınız bir programı, C programınızın içinde kullanmak isterseniz,başvuracağınız bazı C komutları vardır. Örnek vermek gerekirse execve, system gibi.Objdump ile incelediğimizde hangisinin kullanıldığını öğrenebiliriz.

ka@ka-vm ~/debug $ readelf -s crackme_x86
Symbol table '.dynsym' contains 13 entries:
...
5: 00000000 0 FUNC GLOBAL DEFAULT UND system@GLIBC_2.0 (2)
(gdb) b system
Breakpoint 1 at 0x8048430
(gdb) run
Starting program: /home/ka/debug/crackme_x86
Breakpoint 1, 0xb7e5b430 in system () from /lib/i386-linux-gnu/libc.so.6
(gdb) backtrace
#0 0xb7e5b430 in system () from /lib/i386-linux-gnu/libc.so.6
#1 0x08048596 in _init_ ()
#2 0x08048832 in __libc_csu_init ()
#3 0xb7e3546a in __libc_start_main () from /lib/i386-linux-gnu/libc.so.6
#4 0x080484b1 in _start ()
(gdb) disas _init_
Dump of assembler code for function _init_:
0x08048584 <+0>: push ebp
0x08048585 <+1>: mov ebp,esp
0x08048587 <+3>: sub esp,0x18
0x0804858a <+6>: mov DWORD PTR [esp],0x80488cc <--
0x08048591 <+13>: call 0x8048430
0x08048596 <+18>: mov DWORD PTR [esp],0x80488db <--
0x0804859d <+25>: call 0x8048430
(gdb) x/s 0x080488cc
0x80488cc: "killall -q gdb"
(gdb) x/s 0x080488db
0x80488db: "killall -q strace"

Yaptığımız sorgulardan anlıyoruz ki program main fonksiyonuna gelmeden constructor bölümüne ait _init_ fonksiyonu kısmında gdb ve strace kullanımını engellemek için tuzaklar hazırlanmış.Basit bir yöntemle bu engeli aştığımız için fazla takılmıyor, şimdi de asıl kontrolun yapıldığı yeri bulmak için incelemeye devam ediyoruz.

(gdb) b _finit_
Breakpoint 1 at 0x804854a
(gdb) disas
Dump of assembler code for function _finit_:
0x08048544 <+0>: push ebp
0x08048545 <+1>: mov ebp,esp
0x08048547 <+3>: sub esp,0x18
=> 0x0804854a <+6>: movzx eax,BYTE PTR ds:0x804a03c
0x08048551 <+13>: cmp al,0x68 [1.harf h ?]
0x08048553 <+15>: jne 0x8048582 <_finit_+62>
0x08048555 <+17>: movzx eax,BYTE PTR ds:0x804a03d
0x0804855c <+24>: cmp al,0x65 [2.harf e ?]
0x0804855e <+26>: jne 0x8048582 <_finit_+62>
0x08048560 <+28>: movzx eax,BYTE PTR ds:0x804a03e
0x08048567 <+35>: cmp al,0x6c [3. harf l ?]
0x08048569 <+37>: jne 0x8048582 <_finit_+62>
0x0804856b <+39>: movzx eax,BYTE PTR ds:0x804a03f
0x08048572 <+46>: cmp al,0x6c [4. harf l ?]
0x08048574 <+48>: jne 0x8048582 <_finit_+62>
0x08048576 <+50>: mov DWORD PTR [esp],0x80488b0
0x0804857d <+57>: call 0x8048420
0x08048582 <+62>: leave
0x08048583 <+63>: ret

Buldummmm diye sevinmedim önce kontrolumuzu yapıyoruz ve "hell" parolasının da doğru olmadığını görüyoruz.Yapılan kontroller bunlar işte desek de main fonksiyonunda yapılan 5. karakter kontrolunu unutmuyoruz.Eğer 5. harf o değilse,2. harfi a yaptığı için siz hell yazsanız da kontrol anı geldiğine parola hall oluveriyor.Unutmadık değil mi destructorlar main fonksiyonundan sonra çalıştırılıyordu.Hemen parolaları kontrol edelim:

ka@ka-vm ~/debug $ ./crackme_x86
What is the password?
hell
ka@ka-vm ~/debug $ ./crackme_x86
What is the password?
hello
You WIN, congratulations...

Ciddi bütçelerle hazırlanan yazılımların yanında bebek oyuncağı olan programımızı çözmüş olmanın verdiği mutlulukla hedef konumuz olan hack işlemlerimiz için yapmamız gereken hazırlığı bir sonraki başlıkta anlatıp yazımızı bitirelim.

Peda Takviyesi

Takviye olarak kullanacağımız PEDA yani Python Exploit Development Assistance, hacker arkadaşların Python programlama dilini kullanarak, zaafiyetleri sömürmek için hazırlanan kodlara ki bunlara exploit deniyor, hazırlanmasında tekrarlanan işlemleri hızlandırmak ve kolaylaştırmak için yazılmış (ücretsiz) bir GDB eklentisidir.

Seriyi takip eden ve öğrenmek isteyen arkadaşlara kurmalarını tavsiye ediyorum.Nasıl kuruyoruz hemen tarif edelim:

ka@ka-vm ~ $ git clone https://github.com/longld/peda.git
Cloning into 'peda'...
remote: Counting objects: 93, done.
remote: Compressing objects: 100% (60/60), done.
remote: Total 93 (delta 58), reused 58 (delta 33)
Unpacking objects: 100% (93/93), done.
ka@ka-vm ~ $ echo "source ~/peda/peda.py" >>.gdbinit
ka@ka-vm ~ $ gdb -q
gdb-peda$

Mevcut kullanıcının ana dizininde git ile kendi sistemimizi klonlama yaptıktan sonra serimizin başında oluşturduğumuz .gdbinit dosyasına tek satır bir eklenti yapmamız yeterli oluyor.Kurarken sorun yaşayan arkadaşlar yorum bırakarak ya da twitterdan yardım alabilirler.

Peda kullanarak neler yapabiliyoruz,işimizi nasıl kolaylaştırdığını yazı için hazırlanan videoda izleyebilirsiniz.Hazırlıkları da tamamladığımıza göre bir sonraki yazıda görüşmek üzere diyorum.Videoyu izlemeyi unutmadan..

Oynatalım Uğurcum

Yazı için hazırlanan videoyu YouTube'dan izleyebilirsiniz.

Peda hakkında detaylı bilgi almak isteyenler bu adresi ziyaret edebilir.