91 lines
5.8 KiB
ArmAsm
91 lines
5.8 KiB
ArmAsm
#---------------------------------------------------------------------------------------------------------------#
|
|
# description: handwritten program running gets to a buffer of [8] to exemplify a quick and dirty #
|
|
# buffer overflow exploit. #
|
|
# #
|
|
# objectives: SY0-601 1.3, 2.3, 3.2 #
|
|
# #
|
|
# intended environment: DOCKER - kalilinux/kali-last-release:latest #
|
|
# #
|
|
# author: bfu #
|
|
# file: bof.s #
|
|
# binary: bof.elf #
|
|
# #
|
|
# assembler: GNU Assembler (as or GAS) #
|
|
# assemble: as --gstabs+ bof.s -o bof.o #
|
|
# link: gcc -no-pie -nostartfiles -z execstack -ggdb -fno-stack-protector bof.o -o bof.elf #
|
|
#---------------------------------------------------------------#---------------------------------------------- #
|
|
.code64 # not required, but specifying we're 64-bit :) #
|
|
#---------------------------------------------------------------#-----------------------------------------------#
|
|
# ~ read only data ~ #
|
|
.section .rodata #-----------------------------------------------#
|
|
money_str: .string "woohoo!! free money\n" # this is the string we're going to print on #
|
|
# successful exploitation #
|
|
#---------------------------------------------------------------#-----------------------------------------------#
|
|
.section .text # ~ text section ~ #
|
|
#-----------------------------------------------#
|
|
.globl _start # make it known that `_start` is global #
|
|
#-----------------------------------------------#
|
|
.extern printf # (FROM LIBC) #
|
|
.extern gets # printf(char*,...), gets(char*) #
|
|
#---------------------------------------------------------------#-----------------------------------------------#
|
|
_get_input: # #
|
|
push %rbp # void _get_input(void) { #
|
|
mov %rsp, %rbp # char ptr[8]; #
|
|
sub $0x10, %rsp # gets(ptr); #
|
|
lea -0x8(%rbp),%rax # return; #
|
|
mov %rax, %rdi # } #
|
|
call gets@plt # #
|
|
#---------------------------------------------------------------#-----------------------------------------------#
|
|
# Knowing that the buffer size is 8 and that there are no protections on this binary, we can overflow the #
|
|
# buffer to call a function such as `_get_rich_fast`. This is because the stack also contains a saved base #
|
|
# pointer (1) to know where to jump back to at the of the function. After inputting the correct amount of #
|
|
# any data, for example, the character 'a' to fill the buffer, the stack looks like this: #
|
|
# #
|
|
# _______________________________________________________________________ #
|
|
# | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | a | b | c | d | e | f | #
|
|
# |---|---|---|---|---|---|---|---|----|----|----|----|----|----|----|----| #
|
|
# | / | / | / | / | / | / | / | / | 61 | 61 | 61 | 61 | 61 | 61 | 61 | 61 | #
|
|
# |---|---|---|---|---|---|---|---|----|----|----|----|----|----|----|----| #
|
|
# | x | x | x | x | x | x | x | x | 63 | 10 | 40 | 00 | <-- saved bp (1) | #
|
|
# |___|___|___|___|___|___|___|___|____|____|____|____|___________________| #
|
|
# #
|
|
# Since we don't really care what is in the memory where the x's are, we can put anything there. We want #
|
|
# to modify the base pointer, so we know that we want to replace the stored address 0x40116a. However, #
|
|
# since the binary is little endian (LSB), when we want to overwrite the memory, our input will have to #
|
|
# follow that format. Instead of writing {0x40, 0x10, 0x47} to stdin, we will write {0x47, 0x10, 0x40} to #
|
|
# be able to write 0x401047 (the address we want to jump to). #
|
|
# #
|
|
# This allows us to craft the final payload: "aaaaaaaaaaaaaaaa\x47\x10\x40". #
|
|
#---------------------------------------------------------------------------------------------------------------#
|
|
# Execution: bash -c 'printf "aaaaaaaaaaaaaaaa\x47\x10\x40" | ./bof.elf' #
|
|
#---------------------------------------------------------------#-----------------------------------------------#
|
|
nop # #
|
|
leave # END OF FUNCTION #
|
|
ret # #
|
|
#---------------------------------------------------------------#-----------------------------------------------#
|
|
_get_rich_fast: # #
|
|
push %rbp # void _get_rich_fast(void) { #
|
|
mov %rsp, %rbp # printf(money_str); // .rodata #
|
|
lea money_str, %rdi # } #
|
|
mov %rdi, %rax # #
|
|
call printf@plt #-----------------------------------------------#
|
|
# Since this function is not called in the #
|
|
#---------------------------------------------------------------# program, the goal is to jump to this #
|
|
jmp _bye # function (_get_rich_fast) via overflow. #
|
|
#---------------------------------------------------------------#-----------------------------------------------#
|
|
_start: # #
|
|
push %rbp # push the frame pointer #
|
|
call _get_input # call our input retrieving function #
|
|
mov $0, %rax # clean up rax #
|
|
pop %rbp # cleanup, jump to our exit routine #
|
|
jmp _bye # #
|
|
#---------------------------------------------------------------#-----------------------------------------------#
|
|
_bye: # exit(0) #
|
|
mov $60, %al #-----------------------------------------------#
|
|
xor %rdi, %rdi # sys_exit = 60 (dec) #
|
|
syscall # exit code 0 #
|
|
retq # bye bye #
|
|
#---------------------------------------------------------------#-----------------------------------------------#
|
|
# #
|
|
#---------------------------------------------------------------------------------------------------------------#
|