haslang
1 | GNU C Library (Ubuntu GLIBC 2.27-3ubuntu1.6) stable release version 2.27 |
1 | haslang: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=5935040ca40a99cafbb83e41a98a1828bc3abec1, stripped |
- 64位,dynamically,Partial RELRO,NX
逆向分析
比赛时测试出来是 Lisp 的语法:
1 | >>> (define (concat list) (apply append list)) |
1 | >>> (equal?) |
1 | (define (flag "flag")) |
在逆向分析时发现本程序有很多加密的数据,导致交叉应用 X 失效,用 IDA 跟踪调试程序时发现根本找不到程序处理 Lisp 代码的核心逻辑
另外在 04EBFA0
处找到了一个表:
1 | .rodata:00000000004EBFA0 3E 3E 3E 20 00 asc_4EBFA0 db '>>> ',0 ; DATA XREF: sub_40BA98+32↑o |
- 值得注意的字符串就是
editChunk
和showChunk
Lisp 语法
LISP 是一种通用高级计算机程序语言,长期以来垄断人工智能领域的应用
LISP 作为应用人工智能而设计的语言,是第一个声明式系内函数式程序设计语言,有别于命令式系内过程式的 C,Fortran 和面向对象的 Java,Python 等结构化程序设计语言
LISP 只有两种数据结构:
- 原子 atom:原子为标识符形式的符号或数字的字面值
- 表 list:表则是由零个或多个表达式组成的序列(不需要使用一般表处理所必需的任意插入及删除操作)
LISP 的语法是简洁的典型,程序代码与数据的形式完全相同
以圆括号为边界的表:(A B C D)
- 数据:有 A,B,C,D 这4个元素的表
- 函数:将名为 A 的函数作用于 B,C 和 D 这3个参数
嵌套表结构亦是以圆括号来表示:(A(B C)D(E(F G)))
入侵思路
比赛时先用 IDA 搜索了一下字符串 “version”,想找一下有没有源码
1 | void __fastcall __noreturn sub_4C5F20(char *format, __gnuc_va_list arg) |
然后在如下网站中下载 9.0.2 版本的 GHC
接下来先编译源码然后 bindiff 一下,看看有没有什么改动:
发现不管是对比源码还是恢复符号,最终的效果都不是很好,猜测作者对源代码进行了扩展,添加了一些自己实现的功能进去(例如 editChunk showChunk
)
所以接下来的思路就是要尝试调用 editChunk
或者 showChunk
,看看能不能对堆进行泄露或者修改,比赛时也是最终卡在了这里,因为 IDA 的交叉引用失效,我很难找出来报错的原因:
1 | >>> (showChunk 1) |
1 | >>> (define var 1) |
- 其实比赛时没有想到
showChunk editChunk
要传入一个指针 - 因为对 Lisp 不熟悉,当时根本没有想到 Lisp 还有指针类型
正确的语法如下:
1 | (define ptr1 (alloc 64)) |
使用 patchelf 时有一个小细节需要注意:
1 | ➜ haslang patchelf ./haslang --set-interpreter ./ld-2.27.so --replace-needed libc.so.6 ./libc-2.27.so --replace-needed libpthread.so.0 ./libpthread-2.27.so --output haslang1 |
- 单独使用这两条命令中的一条,程序都运行不起来
- 同时使用这两条命令,程序就可以跑起来了
测试案例如下:
1 | def add(who, size): |
- 其实思路就是通过 free unsortedbin 和
showChunk
来泄露 libc_base - 先用
editChunk
写入一些数据,然后直接 search 并计算偏移 - 然后新开一个 GDB 来进行泄露:
- 由于
showChunk
不能泄露不可见字符,因此需要进行爆破 - 需要用
editChunk
来填充最后一字节不可见字符(PIE 最后3位固定)
- 由于
然后的思路就很简单了,通过 editChunk
修改 tcache chunk->FD 接修改 free_hook 即可
完整 exp 如下:
1 | # -*- coding:utf-8 -*- |
小结:
本题目只要知道如何调用 alloc editChunk showChunk
就可以做出来,但可惜比赛时没有掌握 alloc
函数的用法
比赛是初见 Lisp 这种函数式编程的语言(其实 python 和 java 中的 Lambda 也是用了函数式编程的思想,以前没有太注意)
- 当我第一次遇见 Lisp 的语法时就感觉就很奇怪,首先是简洁无比的语法,然后是不能赋值的特点
- 这个不能赋值的特点在比赛时有些干扰我的思考(没法赋值就没法覆盖数据),导致我以为程序提供了某个类似于 open 的语法,于是我想直接 open flag 然后读出来,但我跟踪分析了几乎每一个
fopen
后发现行不通,浪费了不少时间 - 后来发现了 “editChunk showChunk” 这两个字符串,感觉像是作者自己实现的内置函数,于是才想着尝试去调用它们