blast 发布的文章

Linux ASLR的一些实验 (1)

最近在调试一个漏洞,在利用信息泄露的洞的时候发现了一个比较好玩或者说和我预期不太一样的现象,即在Android或者Linux中,即使程序开启ASLR,DSO(Dynamic Shared Objects)的相对偏移却是固定的。即任意两个DSO加载位置的delta永远是固定值。除非中间有其他什么模块的干扰。

这就导致了一个问题:假如一个程序能够泄露某个模块的加载地址,那么就能够推算出其他所有模块的地址。而我的预期则稍稍有点不一样,我最开始是以为所有模块的加载都是随机的。

首先是标记为DT_NEEDED的模块,用ldd看可以发现,vDSO先不管,以libdl.so.2为基准,libc.so.6永远在它-0x39f000的地方,而ld-linux-x86-64.so.2则永远在它的+0x407000的地方。当然,这个只是我随便写的一个printf的程序,可能这几个库除了ld就是libc看起来比较“特殊”,但是对于其他程序而言,情况也是这样的。

1.png

对于dlopen的加载也没什么区别,可以看到永远libcalculate.so都是在前一个模块的+0x39D000处。不管是LAZY还是RTLD_NOW,都是一样。

2.png

对它们进行检测,可以发现PIE都处于Enabled的状态。

3.png

所以比较迷茫的情况下看了下glibc的源代码(对,这一块是ld做的,ld的源代码事实上绝大部分情况下都在glibc,[或者其他什么库里面,如果开发者愿意修改的话]),似乎从中发现了一些线索。

在Visual Studio中快速展开复合宏的方式

对例如dlmalloc之类的函数代码宏套着宏的代码复杂,但是工程简单(只有少量文件)的项目来说,展开宏会让静态阅读看起来更爽(其实是老方法,但是最近发现不少不用Windows开发的朋友完全不知道..)。要展开也非常简单,三步:

1、 将工程导入Visual Studio
2、 查看工程属性,打开预编译到文件选项。
precompile.jpg
正如它提示的那样,预编译到文件意味着不会生成obj文件,也就是说,工程现在开始不能编译成目标文件。
3、 编译工程,去工程目录下寻找.i文件吧。

.i文件前面会包含一大堆乱七八糟的数据,那些是系统库的一些头文件,定位到你的代码前,把系统库的代码全部删掉,最后整理一下多行回车、在VS里面格式化一下代码,大功告成。

dlmalloc模式的内存layout

主要自己最近在调试这个,相比Windows的来说,Linux程序选择alloc的方式真是千奇百怪,alloc也是有一大堆,先看一个dlmalloc的实现。

A 简介

dlmalloc是目前一个十分流行的内存分配器,其由Doug
Lea1从1987年开始编写,到目前为止,最新版本为2.8.32,由于其高效率等特点被广泛的使用和研究(很多linux系统等用的就是dlmalloc或其变形,比如ptmalloc)。

B 内存布局

pdlmalloc.png
来源@https://cw.fel.cvut.cz/old/_media/courses/a4m33pal/04_dynamic_memory_v6.pdf

实际上的内存类似于:
dmp.png

P位的作用是:1 代表前一个thunk在使用,0 代表被释放。
参考@https://medium.com/@ktecv2000/heap-exploit-%E5%AD%B8%E7%BF%92%E7%AD%86%E8%A8%98-d724d0afa59b

C 有点太快了
源代码可以从ftp://gee.cs.oswego.edu/pub/misc/malloc.c获取到。
看一下malloc.h中的作者简介吧。写的比百度百科的词条要好很多。(机器翻译,我用上瘾了)

重要情报: 支持的指针/size_t表示:4或8个字节。size_t必须是与指针宽度相同的无符号类型。 (如果您使用的是一个将size_t 声明为有符号类型的古老系统,或者需要它与指针的宽度不同,则可以使用此malloc的以前版本(例如2.7.2)来支持)
对齐:8个字节(最少)。这对几乎所有当前机器和C编译器来说都足够了。但是,如果需要的话,您可以将MALLOC_ALIGNMENT定义为更宽的范围(最多128字节),代价是使用更多的空间。
每个分配块的最小开销:4或8个字节(如果是4字节大小,原文如此,指32bit)、8或16个字节(如果是8字节大小,指64bit),每个malloc的块都有一个隐藏字,其中包含大小和状态信息,如果定义了FOOTER,则还有其他交叉检查字。
最小分配大小:4字节指针:16字节(包括开销),8字节指针:32字节(包括开销),即使是对零字节(即malloc(0))的请求也会返回一个指向最小可分配大小的指针。
最大开销浪费(即分配的额外字节数比malloc中请求的字节数)小于或等于最小大小,但通过mmap()服务的请求>=mmap_阈值除外,其中最坏的浪费大约是32个字节加上系统页面(最小mmap单元)的剩余部分;通常是4096或8192字节。

安全性:静态安全;可选地,malloc的“安全性”或多或少是指恶意代码在调用malloc的代码中强调错误影响的能力(例如,释放当前未被恶意占用的空间或覆盖过块的末端)。此malloc保证即使存在使用错误,也不会修改堆以下的任何内存位置(即静态变量)。例程还检测到大多数不适当的free和realloc。只要malloc本身的静态记录不被其他方法破坏,所有这些保护都是有效的。

这只是安全性的一个方面--这些检查也不能检测到所有可能的编程错误。如果FOOTER被定义为非零,则每个分配的块都带有一个额外的检查字,以验证它是从其空间中malloc的。
这些检查字在使用malloc的程序的每次执行中是相同的,但如果再次执行则不同,因此无法free由恶意代码构造的假内存块。
这可以通过拒绝可能损坏堆内存的free/realloc程序来提高安全性,此外还可以防止对始终处于打开状态的static写入进行检查。
这可能会以牺牲时间和空间开销为代价进一步提高安全性。(请注意,FOOTER可能也值得与MSPACES一起使用)。
默认情况下,检测到的错误会导致程序中止(调用“abort()”)。您可以重写它,通过定义PROCEED_ON_ERROR来处理错误。

在这种情况下,恶意的free不会有任何影响,遇到用户覆盖造成的坏地址的malloc将通过将指针和索引删除所有已知内存而忽略恶意地址。
这可能适用于在遇到编程错误时应尽可能继续运行的程序,尽管它们可能会耗尽内存,因为这样的话,程序将永远不会回收掉那些恶意内存。
如果您不喜欢这两种选项中的任何一种,则可以定义CORRUPTION_ERROR_ACTION 和 USAGE_ERROR_ACTION
来执行其他任何操作。

如果您确信您使用malloc的程序没有错误或漏洞,您可以将不安全定义为1,这可能(也可能不会)提供一个小的性能改进。还可以使用malloc_set_footprint_limit来限制最大总可分配空间。这本身并不是一个安全特性(对设置限制的调用没有屏蔽或特权),但是作为安全实现的一个方面可能很有用。

关于文章更新……

最近一直忙于一个项目,本来预计的是说一个月一篇的,不过现在看来时间不是很多啊...

不过相比于麻烦的原创文章,我也在更新一个翻译(大部分是机器翻译+人工润色)的博客:http://tem.pw

虽然说是机器翻译,不过现在机器翻译质量明显上升了,我只需要手动修改一点点就变得很通顺了,于是采用了这么个快捷简单的方式来更新文章……

也许得到下次搞出大新闻的时候nul.pw这边才有空更新吧 [/捂脸]

HTTPS访问异常的问题

站点因为历史原因,一直托管在一个虚拟主机上,HTTPS访问配置异常,请暂时使用HTTP来访问本站。

近期将会迁移到我自己的主机上,之后我会给本站开启正确的HTTPS的证书。


服务器抽风,HTTP也异常了,好在点了一下就好了,之前404的页面现在又可以访问了。