Week1--漏洞学习
来源于天问之路知识星球的每日安全推送。
ASAN检测不到的bugs
文章链接: https://nandynarwhals.org/bugs-asan-doesnt-detect/
这篇文章是关于三个能够绕过ASAN检测的bugs,之前我没有接触过ASAN,借此了解一下。
在gcc/g++下使用ASAN只需要在编译的时候加上-fsanitize=address,在此选项下编译简单的存在double free的C文件,运行时可以看到检测到double free:
//TODO
在源文章代码的基础上稍加修改在Linux Ubuntu16.04的环境下可以得到预期结果的源代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
struct sample {
char buf[8];
uint32_t val;
};
void choice1() {
puts("1. Overflowing a buffer within a struct.");
struct sample sample_struct;
printf("sample_struct.val = 0x%x\n", sample_struct.val);
strncpy(sample_struct.buf, "ccccccccdddd", 12);
printf("sample_struct.val = 0x%x\n", sample_struct.val);
}
/*
void choice2() {
puts("2. Arbitrary write with integer overflows skipping over shadow mem.");
char canary[10] = "IAMCANARY";
char buf[10];
printf("Canary = \"%s\"\n", canary);
int position = 2147483616;
position = position * 2;
printf("position = %d\n", position);
for (int i = 0; i < 9; i++) {
buf[position + i] = 'X';
printf("buf[%d] = 'X'\n", position + i);
}
printf("Canary = \"%s\"\n", canary);
}
*/
void choice2() {
puts("2. Skipping over shadow memory.");
char canary[10] = "IAMCANARY";
char buf[10];
printf("Canary = \"%s\"\n", canary);
int position = -64;
printf("position = %d\n", position);
for (int i = 0; i < 9; i++) {
buf[position + i] = 'X';
printf("buf[%d] = 'X'\n", position + i);
}
printf("Canary = \"%s\"\n", canary);
}
void f1(int ** p) {
int dangle = 0x41414141;
*p = &dangle;
}
void f2() {
int override1 = 0x42424242;
long long int a=1;
long long int b=2;
long long int c=3;
long long int d=4;
long long int e=5;
long long int f=6;
long long int g=7;
long long int h=8;
long long int i=9;
int j=10;
}
void choice3() {
puts("3. Use of stack variable after returning from a function.");
int * p = 0;
printf("p = %p\n", p);
printf("*p = undefined\n");
f1(&p);
printf("p = %p\n", p);
printf("*p = 0x%x\n", *p);
f2();
printf("p = %p\n", p);
printf("*p = 0x%x\n", *p);
}
int main(int argc, char ** argv) {
choice1();
choice2();
choice3();
return 0;
}
结果如下:
//TODO
其中choice1对应第一种漏洞:结构体中的缓冲区溢出。首先打印出了栈上未初始化的垃圾数据,其次利用结构体上的变量连续存储,第一个变量溢出,覆盖后一个本不该被赋值的变量,此缓冲区溢出不会被ASAN检测到。
choice2对应第二种漏洞:跳过shadow memory。(由于我在查了一些资料后,对ASAN中的shadow memory仍然不是非常理解,这一部分先翻译原文章的内容。)此漏洞利用在二进制文件上启用了ASAN来执行相同的缓冲区上溢/下溢,通过计算一些偏移量来跳过保护缓冲区的shadow字节。
choice3对应第三种漏洞:从函数返回后使用栈变量。在函数调用返回后,栈空间被销毁,下一次再次调用其他函数时不应该影响上一次调用的结果。但是由于在主函数中连续两次调用函数,会开拓同一段栈空间,因此可能会出现stack-use-after-return的bugs。choice3展示了该bug的一种情况,可以看到ASAN不会检测到使用指向已销毁栈帧的指针的情况。
跟我一起写Makefile
文章链接: https://seisman.github.io/how-to-write-makefile/index.html
Makefile可以实现“自动化编译”,使用make命令就可以实现整个工程完全自动编译。
规则
Makefile的规则包含两个部分:依赖关系、生成目标的方法
target ... : prerequisites ...
#或者 target : prerequisites ; command
command
...
target: 目标文件/可执行文件/label
prerequisites:生成该target所依赖的文件或target
command:必须以Tab键开头,说明该target所要执行的命令(任意的shell命令)
==> target依赖于prerequisites中的文件,其生成规则定义在command中。
prerequisites中如果有一个以上的文件比target文件要新,就会执行command定义的命令。
一般来说,make命令会以UNIX的标准Shell,/bin/sh
执行命令
书写规则
Makefile中只应该有一个最终目标,第一条规则中的第一个目标会成为最终的目标,也就是make命令所完成的目标。
如果某一行过长,可以使用反斜杠\
作为换行符。
make支持三个通配符:*
,?
,~
Makefile包含的五个部分
-
显示规则
说明了如何生成一个或多个目标文件,包括要生成的文件、文件的依赖文件和生成的命令
-
隐晦规则
GNU的make可以自动推导文件以及文件依赖关系后面的命令,make看到一个.o文件,就会自动的把.c文件加在依赖关系中,并且推导出
cc -c xxx.c
命令 -
变量的定义
makefile的变量也就是一个字符串:定义字符串
xxx=...
,则可以通过$(xxx)
来使用这个变量 -
文件指示
通过
inlcude <filename>
可以在一个Makefile中引用另一个Makefile文件;make也会引用环境变量MAKEFILES值所代表的所有Makefile,但引入Makefile的“目标”不起作用。 -
注释
Makefile的文件名
默认情况下:make命令会在当前目录下按顺序寻找文件名为"GNUmakefile","makefile","Makefile",其中"GNUmakefile"只能由GNU的make识别。
GNU make的工作方式
- 读入所有的Makefile
- 读入被include的其它Makefile
- 初始化文件中的变量
- 推导隐晦规则,并分析所有规则
- 为所有的目标文件创建依赖关系链
- 根据依赖关系,决定哪些目标要重新生成
- 执行生成命令
当变量出现在依赖关系的规则中,只有当这条依赖被决定要使用了,那么变量才会在其内部展开。
//UNDONE