Почему printf не распечатал строку в этом переполнении буфера? - PullRequest
1 голос
/ 14 апреля 2020

Я изучаю переполнение буфера. Я написал небольшую C программу:

#include<stdio.h>
#include<string.h>
void vuln();
void win();
void main(int argc, char *argv[]){
        vuln();

}
void win (){
        printf("Pwned!!");
}
void vuln(){
        char buffer[256];
        printf("Enter a string: ");
        scanf(" %s", buffer);
}

Разборка функции vuln:

gef➤  disas vuln
Dump of assembler code for function vuln:
   0x0000555555555179 <+0>:     push   rbp
   0x000055555555517a <+1>:     mov    rbp,rsp
   0x000055555555517d <+4>:     sub    rsp,0x100
   0x0000555555555184 <+11>:    lea    rdi,[rip+0xe81]        # 0x55555555600c
   0x000055555555518b <+18>:    mov    eax,0x0
   0x0000555555555190 <+23>:    call   0x555555555030 <printf@plt>
   0x0000555555555195 <+28>:    lea    rax,[rbp-0x100]
   0x000055555555519c <+35>:    mov    rsi,rax
   0x000055555555519f <+38>:    lea    rdi,[rip+0xe77]        # 0x55555555601d
   0x00005555555551a6 <+45>:    mov    eax,0x0
   0x00005555555551ab <+50>:    call   0x555555555040 <__isoc99_scanf@plt>
   0x00005555555551b0 <+55>:    nop
   0x00005555555551b1 <+56>:    leave  
   0x00005555555551b2 <+57>:    ret    
End of assembler dump.

Мне удалось перейти на win с помощью полезной нагрузки:

>>> payload = "A"264 + p64(0x0000555555555161)

Проблема в том, что она не распечатала строку в функции win. Все, что я получил, было ошибкой сегментации. Вот что я получаю:

Program received signal SIGSEGV, Segmentation fault.
0x00007fffffffe158 in ?? ()
[ Legend: Modified register | Code | Heap | Stack | String ]
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$rax   : 0x7               
$rbx   : 0x0               
$rcx   : 0x0               
$rdx   : 0x0               
$rsp   : 0x00007fffffffe068  →  0x0000000200000000
$rbp   : 0x4141414141414141 ("AAAAAAAA"?)
$rsi   : 0x656e7750        
$rdi   : 0x00005555555592a0  →  "Pwned!! string: "
$rip   : 0x00007fffffffe158  →  0x00007fffffffe436  →  "/media/sf_Code/asm/vuln"
$r8    : 0x00007ffff7fb9500  →  0x00007ffff7fb9500  →  [loop detected]
$r9    : 0x7               
$r10   : 0x0000555555556004  →  0x00212164656e7750 ("Pwned!!"?)
$r11   : 0x246             
$r12   : 0x0000555555555060  →  <_start+0> xor ebp, ebp
$r13   : 0x00007fffffffe150  →  0x0000000000000002
$r14   : 0x0               
$r15   : 0x0               
$eflags: [zero carry PARITY ADJUST sign trap INTERRUPT direction overflow RESUME virtualx86 identification]
$cs: 0x0033 $ss: 0x002b $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000 
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x00007fffffffe068│+0x0000: 0x0000000200000000   ← $rsp
0x00007fffffffe070│+0x0008: 0x00005555555551c0  →  <__libc_csu_init+0> push r15
0x00007fffffffe078│+0x0010: 0x00007ffff7e1ebbb  →  <__libc_start_main+235> mov edi, eax
0x00007fffffffe080│+0x0018: 0x0000000000000000
0x00007fffffffe088│+0x0020: 0x00007fffffffe158  →  0x00007fffffffe436  →  "/media/sf_Code/asm/vuln"
0x00007fffffffe090│+0x0028: 0x0000000200100000
0x00007fffffffe098│+0x0030: 0x0000555555555145  →  <main+0> push rbp
0x00007fffffffe0a0│+0x0038: 0x0000000000000000
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
   0x7fffffffe152                  add    BYTE PTR [rax], al
   0x7fffffffe154                  add    BYTE PTR [rax], al
   0x7fffffffe156                  add    BYTE PTR [rax], al
 → 0x7fffffffe158                  ss     in  al, 0xff
   0x7fffffffe15b                  (bad)  
   0x7fffffffe15c                  (bad)  
   0x7fffffffe15d                  jg     0x7fffffffe15f
   0x7fffffffe15f                  add    BYTE PTR [rsi-0x1c], cl
   0x7fffffffe162                  (bad)  
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "vuln", stopped 0x7fffffffe158 in ?? (), reason: SIGSEGV
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x7fffffffe158 → ss in  al, 0xff

Как это printf не распечатало строку?

1 Ответ

2 голосов
/ 14 апреля 2020

stdout по умолчанию буферизован строкой, и строка не заканчивается новой строкой. Если вы измените его на puts("Pwned!!");, то stdout будет сброшено до того, как puts вернется.

Но с printf данные просто сидят там в буфере stdio, пока что-то еще не напечатает новую строку, или до fflush(stdout). exit() или cleanly возврат из main вызовет fflu sh, но segfaulting убьет процесс, даже не сделав системный вызов для передачи данных ввода / вывода в ОС.

Это точно такая же проблема, как и Использование printf в сборке приводит к пустому выводу за исключением того, что в этом случае вместо segfaulting используется системный вызов _exit(2).


Если цель состоит в том, чтобы заставить вас вызывать win() без прерывания последующего выполнения, это еще один уровень сложности.

Но если win() должен представлять что-то вроде успешной атаки ROP, которая вызывает system() или execve с "/bin/sh", тогда win() написано плохо. execve произойдет на месте, а не позже.

...