Чтобы пользоваться программными интерфейсами VirusTotal без ограничений, нужно получить ключ, который обходится в серьезную сумму — цены начинаются с 700 евро в месяц. Причем частному лицу даже при готовности платить ключ не дадут.
Однако отчаиваться не стоит, поскольку основные функции сервис предоставляет бесплатно и ограничивает нас лишь по числу запросов — не более двух в минуту. Что ж, придется с этим мириться.
Итак, первым делом нам нужна регистрация на сайте. Тут проблем никаких — я уверен, что ты справишься. После регистрации берем ключ доступа, перейдя в пункт меню API key.
Сейчас актуальная версия API имеет номер 2. Но при этом уже существует и новый вариант — номер 3. Эта версия API пока еще находится в стадии беты, но ее уже вполне можно использовать, тем более что возможности, которые она предоставляет, гораздо шире.
Разработчики пока что рекомендуют применять третью версию только для экспериментов либо для некритичных проектов. Мы же разберем обе версии. Ключ доступа для них одинаков.
Как и в случае с другими популярными веб-сервисами, работа с API заключается в пересылке запросов по HTTP и получении ответов.
API второй версии позволяет:
scan_id
из ответа, полученного после отправки файла);scan_id
из ответа, полученного после отправки URL на сервер;Если запрос был правильно обработан и ошибок не возникло, будет возвращен код 200 (OK).
Если же произошла ошибка, то могут быть такие варианты:
При правильном формировании запроса (код состояния HTTP — 200) ответ будет представлять собой объект JSON, в теле которого присутствуют как минимум два поля:
-2
; если запрашиваемый объект отсутствует в базе VirusTotal — равно нулю;response_code
(например, Scan finished, information embedded после отправки файла на сканирование).Остальная информация, содержащаяся в ответном объекте JSON, зависит от того, какая функция API была использована.
Для отправки файла на сканирование необходимо сформировать POST-запрос на адрес https://www.virustotal.com/vtapi/v2
, при этом в запросе нужно указать ключ доступа к API и передать сам файл (здесь есть ограничение на размер файла — не более 32 Мбайт). Это может выглядеть следующим образом (используем Python):
import json
import requests
...
api_url = 'https://www.virustotal.com/vtapi/v2/file/scan'
params = dict(apikey='<ключ доступа>')
with open('<путь к файлу>', 'rb') as file:
files = dict(file=('<путь к файлу>', file))
response = requests.post(api_url, files=files, params=params)
if response.status_code == 200:
result=response.json()
print(json.dumps(result, sort_keys=False, indent=4))
...
Здесь вместо строки <ключ доступа>
необходимо вставить свой ключ доступа к API, а вместо <путь к файлу>
— путь к файлу, который ты будешь отправлять в VirusTotal. Если у тебя нет библиотеки requests, то поставь ее командой pip install requests
.
В ответ, если все прошло успешно и код состояния HTTP равен 200, мы получим примерно вот такую картину:
{
"response_code": 1,
"verbose_msg": "Scan request successfully queued, come back later for the report",
"scan_id": "275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f-1577043276",
"resource": "275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f",
"sha1": "3395856ce81f2b7382dee72602f798b642f14140",
"md5": "44d88612fea8a8f36de82e1278abb02f",
"sha256": "275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f",
"permalink": "https://www.virustotal.com/file/275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f/analysis/1577043276/"
}
Здесь мы видим значения response_code
и verbose_msg
, а также хеши файла SHA-256, SHA-1 и MD5, ссылку на результаты сканирования файла на сайте permalink
и идентификатор файла scan_id
.
В приведенных в статье примерах кода опущена обработка ошибок. Помни, что в ходе открытия файла или отправки запросов на сервер могут возникать исключения:
FileNotFoundError
, если файла нет,requests.ConnectionError
,requests.Timeout
при ошибках соединения и так далее.
Используя какой-либо из хешей или значение scan_id
из ответа, можно получить отчет по последнему сканированию файла (если файл уже загружался на VirusTotal). Для этого нужно сформировать GET-запрос и в запросе указать ключ доступа и идентификатор файла. Например, если у нас есть scan_id
из предыдущего примера, то запрос будет выглядеть так:
import json
import requests
...
api_url = 'https://www.virustotal.com/vtapi/v2/file/report'
params = dict(apikey='<ключ доступа>', resource='275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f-1577043276')
response = requests.get(api_url, params=params)
if response.status_code == 200:
result=response.json()
print(json.dumps(result, sort_keys=False, indent=4))
...
В случае успеха в ответ мы увидим следующее:
{
"response_code": 1,
"verbose_msg": "Scan finished, information embedded",
"resource": "275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f",
"sha1": "3395856ce81f2b7382dee72602f798b642f14140",
"md5": "44d88612fea8a8f36de82e1278abb02f",
"sha256": "275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f",
"scan_date": "2019-11-27 08:06:03",
"permalink": "https://www.virustotal.com/file/275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f/analysis/1577043276/",
"positives": 59,
"total": 69,
"scans": {
"Bkav": {
"detected": true,
"version": "1.3.0.9899",
"result": "DOS.EiracA.Trojan",
"update": "20191220"
},
"DrWeb": {
"detected": true,
"version": "7.0.42.9300",
"result": "EICAR Test File (NOT a Virus!)",
"update": "20191222"
},
"MicroWorld-eScan": {
"detected": true,
"version": "14.0.297.0",
"result": "EICAR-Test-File",
"update": "20191222"
},
...
...
"Panda": {
"detected": true,
"version": "4.6.4.2",
"result": "EICAR-AV-TEST-FILE",
"update": "20191222"
},
"Qihoo-360": {
"detected": true,
"version": "1.0.0.1120",
"result": "qex.eicar.gen.gen",
"update": "20191222"
}
}
Здесь, как и в первом примере, получаем значения хешей файла, scan_id
, permalink
, значения response_code
и verbose_msg
. Также видим результаты сканирования файла антивирусами и общие результаты оценки total
— сколько всего антивирусных движков было задействовано в проверке и positives
— сколько антивирусов дали положительный вердикт.
Чтобы вывести результаты сканирования всеми антивирусами в удобоваримом виде, можно, например, написать что-то в таком роде:
import requests
...
api_url = 'https://www.virustotal.com/vtapi/v2/file/report'
params = dict(apikey='<ключ доступа>', resource='275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f-1577043276')
response = requests.get(api_url, params=params)
if response.status_code == 200:
result=response.json()
for key in result['scans']:
print(key)
print(' Detected: ', result['scans'][key]['detected'])
print(' Version: ', result['scans'][key]['version'])
print(' Update: ', result['scans'][key]['update'])
print(' Result: ', 'result['scans'][key]['result'])
...
Чтобы отправить URL для сканирования, нам необходимо сформировать и послать POST-запрос, содержащий ключ доступа и сам URL:
import json
import requests
...
api_url = 'https://www.virustotal.com/vtapi/v2/url/scan'
params = dict(apikey='<ключ доступа>', url='https://xakep.ru/author/drobotun/')
response = requests.post(api_url, data=params)
if response.status_code == 200:
result=response.json()
print(json.dumps(result, sort_keys=False, indent=4))
...
В ответ мы получим примерно то же, что и при отправке файла, за исключением значений хеша. Содержимое поля scan_id
можно использовать для получения отчета о сканировании данного URL.
Сформируем GET-запрос с ключом доступа и укажем либо непосредственно сам URL в виде строки, либо значение scan_id
, полученное с помощью предыдущей функции. Это будет выглядеть следующим образом:
import json
import requests
...
api_url = 'https://www.virustotal.com/vtapi/v2/url/report'
params = dict(apikey='<ключ доступа>', resource='https://xakep.ru/author/drobotun/', scan=0)
response = requests.get(api_url, params=params)
if response.status_code == 200:
result=response.json()
print(json.dumps(result, sort_keys=False, indent=4))
...
Помимо ключа доступа и строки с URL, здесь присутствует опциональный параметр scan
— по умолчанию он равен нулю. Если же его значение равно единице, то, когда информации о запрашиваемом URL в базе VirusTotal нет (URL ранее не проверялся), этот URL будет автоматически отправлен на сервер для проверки, после чего в ответ мы получим ту же информацию, что и при отправке URL на сервер. Если этот параметр равен нулю (или не задавался), мы получим отчет об этом URL либо (если информация о нем в базе VirusTotal отсутствует) ответ такого вида:
{
"response_code": 0,
"resource": "<запрашиваемый URL>",
"verbose_msg": "Resource does not exist in the dataset"
}
Чтобы проверить IP-адреса и домены, нужно сформировать и отправить GET-запрос с ключом, именем проверяемого домена либо IP в виде строки. Для проверки домена это выглядит так:
...
api_url = 'https://www.virustotal.com/vtapi/v2/domain/report'
params = dict(apikey='<ключ доступа>', domain=<'имя домена'>)
response = requests.get(api_url, params=params)
...
Для проверки IP-адреса:
...
api_url = 'https://www.virustotal.com/vtapi/v2/ip-address/report'
params = dict(apikey='<ключ доступа>', ip=<'IP-адрес'>)
response = requests.get(api_url, params=params)
...
Ответы на такие запросы объемны и содержат много информации. Например, для IP 178.248.232.27
(это IP «Хакера») начало отчета, полученного с сервера VirusTotal, выглядит так:
{
"country": "RU",
"response_code": 1,
"as_owner": "HLL LLC",
"verbose_msg": "IP address in dataset",
"continent": "EU",
"detected_urls": [
{
"url": "https://xakep.ru/author/drobotun/",
"positives": 1,
"total": 72,
"scan_date": "2019-12-18 19:45:02"
},
{
"url": "https://xakep.ru/2019/12/18/linux-backup/",
"positives": 1,
"total": 72,
"scan_date": "2019-12-18 16:35:25"
},
...
]
}
В третьей версии API намного больше возможностей по сравнению со второй — даже с использованием бесплатного ключа. Более того, при экспериментах с третьей версией я не заметил, чтобы ограничивалось число загружаемых объектов (файлов или адресов) на сервер в течение минуты. Похоже, ограничения в бете пока вообще не действуют.
Функции третьей версии API спроектированы с использованием принципов REST и просты для понимания. Ключ доступа здесь передается в заголовке запроса.
В третьей версии API список ошибок (и, соответственно, кодов состояния HTTP) расширился. Были добавлены:
В случае ошибки, помимо кода состояния, сервер возвращает дополнительную информацию в форме JSON. Правда, как выяснилось, не для всех кодов состояния HTTP: к примеру, для ошибки 404 дополнительная информация представляет собой обычную строку.
Формат JSON для ошибки следующий:
{
"error": {
"code": "<код состояния HTTP>",
"message": "<сообщение с описанием ошибки>"
}
}
Третья версия API позволяет:
Для загрузки файла на сервер нужно его отправить через POST-запрос. Это можно сделать так:
...
api_url = 'https://www.virustotal.com/api/v3/files'
headers = {'x-apikey' : '<ключ доступа к API>'}
with open('<путь к файлу>', 'rb') as file:
files = {'file': ('<путь к файлу>', file)}
response = requests.post(api_url, headers=headers, files=files)
...
В ответ мы получим следующее:
{
"data": {
"id": "ZTRiNjgxZmJmZmRkZTNlM2YyODlkMzk5MTZhZjYwNDI6MTU3NzIxOTQ1Mg==",
"type": "analysis"
}
}
Здесь мы видим значение id
, которое служит идентификатором файла. Этот идентификатор нужно использовать для получения информации об анализе файла в GET-запросах типа /analyses
(об этом мы поговорим чуть позже).
Чтобы получить URL для загрузки большого файла (более 32 Мбайт), нужно отправить GET-запрос, в котором в качестве URL указывается https://www.virustotal.com/api/v3/files/upload_url
. В заголовок вставляем ключ доступа:
...
api_url = 'https://www.virustotal.com/api/v3/files/upload_url'
headers = {'x-apikey' : '<ключ доступа к API>'}
response = requests.get(api_url, headers=headers)
...
В ответ получим JSON с адресом, по которому следует загрузить файл для анализа. Полученный URL при этом можно использовать только один раз.
Чтобы получить информацию о файле, который сервис уже анализировал, нужно сделать GET-запрос с идентификатором файла в URL (им может быть хеш SHA-256, SHA-1 или MD5). Так же как и в предыдущих случаях, указываем в заголовке ключ доступа:
...
api_url = 'https://www.virustotal.com/api/v3/files/<значение идентификатора файла>'
headers = {'x-apikey' : '<ключ доступа к API>'}
response = requests.get(api_url, headers=headers)
...
В ответ мы получим отчет о проверке файла, где, помимо результатов сканирования всеми антивирусами VirusTotal, будет много дополнительной информации, состав которой зависит от типа проверенного файла. Например, для исполняемых файлов можно увидеть информацию о таких атрибутах:
{
"attributes": {
"authentihash": "8fcc2f670a166ea78ca239375ed312055c74efdc1f47e79d69966461dd1b2fb6",
"creation_date": 1270596357,
"exiftool": {
"CharacterSet": "Unicode",
"CodeSize": 20480,
"CompanyName": "TYV",
"EntryPoint": "0x109c",
"FileFlagsMask": "0x0000",
"FileOS": "Win32",
"FileSubtype": 0,
"FileType": "Win32 EXE",
"FileTypeExtension": "exe",
"FileVersion": 1.0,
"FileVersionNumber": "1.0.0.0",
"ImageFileCharacteristics": "No relocs, Executable, No line numbers, No symbols, 32-bit",
...
...
"SubsystemVersion": 4.0,
"TimeStamp": "2010:04:07 00:25:57+01:00",
"UninitializedDataSize": 0
},
...
}
}
Или, например, информацию о секциях исполняемого файла:
{
"sections": [
{
"entropy": 3.94,
"md5": "681b80f1ee0eb1531df11c6ae115d711",
"name": ".text",
"raw_size": 20480,
"virtual_address": 4096,
"virtual_size": 16588
},
{
"entropy": 0.0,
"md5": "d41d8cd98f00b204e9800998ecf8427e",
"name": ".data",
"raw_size": 0,
"virtual_address": 24576,
"virtual_size": 2640
},
...
}
}
Если файл ранее не загружался на сервер и еще не анализировался, то в ответ мы получим ошибку типа Not Found Error с HTTP-кодом состояния, равным 404:
{
"error": {
"code": "NotFoundError",
"message": "File \"<идентификатор файла>" not found"
}
}
Чтобы повторно проанализировать файл, нужно также отправить на сервер GET-запрос, в котором в URL помещаем идентификатор файла, а в конце добавляем /analyse
:
...
api_url = 'https://www.virustotal.com/api/v3/files/<значение идентификатора файла>/analyse'
headers = {'x-apikey' : '<ключ доступа к API>'}
response = requests.get(api_url, headers=headers)
...
Ответ будет включать в себя такой же дескриптор файла, как и в первом случае — при загрузке файла на сервер. И так же, как и в первом случае, идентификатор из дескриптора можно использовать для получения информации об анализе файла через GET-запрос типа /analyses
.
Просмотреть комментарии пользователей сервиса, а также результаты голосования по файлу можно, отправив на сервер соответствующий GET-запрос. Для получения комментариев:
...
api_url = 'https://www.virustotal.com/api/v3/files/<значение идентификатора файла>/comments'
headers = {'x-apikey' : '<ключ доступа к API>'}
response = requests.get(api_url, headers=headers)
...
Для получения результатов голосования:
...
api_url = 'https://www.virustotal.com/api/v3/files/<значение идентификатора файла>/votes'
headers = {'x-apikey' : '<ключ доступа к API>'}
response = requests.get(api_url, headers=headers)
...
В обоих случаях можно использовать дополнительный параметр limit
, определяющий максимальное количество комментариев или голосов в ответе на запрос. Использовать этот параметр можно, например, так:
...
limit = {'limit': str(<число голосов в ответе>)}
api_url = 'https://www.virustotal.com/api/v3/files/<значение идентификатора файла>/votes'
headers = {'x-apikey' : '<ключ доступа к API>'}
response = requests.get(api_url, headers=headers, params=limit)
...
Чтобы разместить свой комментарий или проголосовать за файл, создаем POST-запрос, а комментарий или голос передаем как объект JSON:
...
## Для отправки результатов голосования
votes = {'data': {'type': 'vote', 'attributes': {'verdict': <'malicious' или 'harmless'>}}}
api_url = 'https://www.virustotal.com/api/v3/files/<значение идентификатора файла>/votes'
headers = {'x-apikey' : '<ключ доступа к API>'}
response = requests.post(api_url, headers=headers, json=votes)
...
## Для отправки комментария
comments = {'data': {'type': 'vote', 'attributes': {'text': <текст комментария>}}}
headers = {'x-apikey' : '<ключ доступа к API>'}
api_url = 'https://www.virustotal.com/api/v3/files/<значение идентификатора файла>/comments'
response = requests.post(api_url, headers=headers, json=comments)
...
Чтобы получить дополнительную информацию о файле, можно запросить подробности о связанных с ним объектах. В данном случае объекты могут характеризовать, например, поведение файла (объект behaviours
) или URL, IP-адреса, доменные имена (объекты contacted_urls
, contacted_ips
, contacted_domains
).
Интереснее всего объект behaviours
. К примеру, для исполняемых файлов он будет включать в себя информацию о загружаемых модулях, создаваемых и запускаемых процессах, операциях с файловой системой и реестром, сетевых операциях.
Чтобы получить эту информацию, отправляем GET-запрос:
api_url = 'https://www.virustotal.com/api/v3/files/<значение идентификатора файла>/behaviours'
headers = {'x-apikey' : '<ключ доступа к API>'}
response = requests.get(api_url, headers=headers)
В ответе будет объект JSON с информацией о поведении файла:
{
"data": [
{
"attributes": {
"analysis_date": 1548112224,
"command_executions": [
"C:\\WINDOWS\\system32\\ntvdm.exe -f -i1",
"/bin/bash /private/tmp/eicar.com.sh"
],
"has_html_report": false,
"has_pcap": false,
"last_modification_date": 1577880343,
"modules_loaded": [
"c:\\windows\\system32\\user32.dll",
"c:\\windows\\system32\\imm32.dll",
"c:\\windows\\system32\\ntdll.dll"
]
},
...
}
]
}
В список возможных операций с URL входят:
Большая часть указанных операций (за исключением последней) выполняется аналогично таким же операциям с файлами. При этом в качестве идентификатора URL могут выступать либо строка с URL, закодированная в Base64 без добавочных знаков «равно», либо хеш SHA-256 от URL. Реализовать это можно так:
## Для Base64
import base64
...
id_url = base64.urlsafe_b64encode(url.encode('utf-8')).decode('utf-8').rstrip('=')
...
## Для SHA-256
import hashlib
...
id_url = hashlib.sha256(url.encode()).hexdigest()
Чтобы отправить URL для анализа, нужно использовать POST-запрос:
data = {'url': '<строка с именем URL>'}
api_url = 'https://www.virustotal.com/api/v3/urls'
headers = {'x-apikey' : '<ключ доступа к API>'}
response = requests.post(api_url, headers=headers, data=data)
В ответ мы увидим дескриптор URL (по аналогии с дескриптором файла):
{
"data": {
"id": "u-1a565d28f8412c3e4b65ec8267ff8e77eb00a2c76367e653be774169ca9d09a6-1577904977",
"type": "analysis"
}
}
Идентификатор id
из этого дескриптора используем для получения информации об анализе файла через GET-запрос типа /analyses
(об этом запросе ближе к концу статьи).
Получить информацию о доменах или IP-адресах, связанных с каким-либо URL, можно, применив GET-запрос типа /network_location
(здесь используем Base64 или SHA-256 идентификатор URL):
api_url = 'https://www.virustotal.com/api/v3/urls/<идентификатор URL (Base64 или SHA-256)>/network_location'
headers = {'x-apikey' : '<ключ доступа к API>'}
response = requests.post(api_url, headers=headers)
Остальные операции с URL выполняются так же, как и аналогичные операции работы с файлами.
Этот список функций включает в себя:
Все эти операции реализуются аналогично таким же операциям с файлами либо с URL. Отличие в том, что здесь используются непосредственно имена доменов или значения IP-адресов, а не их идентификаторы.
Например, получить информацию о домене www.xakep.ru
можно таким образом:
api_url = 'https://www.virustotal.com/api/v3/domains/www.xakep.ru'
headers = {'x-apikey' : '<ключ доступа к API>'}
response = requests.get(api_url, headers=headers)
А, к примеру, посмотреть комментарии по IP-адресу 178.248.232.27 — вот так:
api_url = 'https://www.virustotal.com/api/v3/ip_addresses/178.248.232.27/comments'
headers = {'x-apikey' : '<ключ доступа к API>'}
response = requests.get(api_url, headers=headers)
Такой запрос позволяет получить информацию о результатах анализа файлов или URL после их загрузки на сервер или после повторного анализа. При этом необходимо использовать идентификатор, содержащийся в поле id
дескриптора файла, или URL, полученные в результате отправки запросов на загрузку файла или URL на сервер либо в результате повторного анализа файла или URL.
Например, сформировать подобный запрос для файла можно вот так:
TEST_FILE_ID = 'ZTRiNjgxZmJmZmRkZTNlM2YyODlkMzk5MTZhZjYwNDI6MTU3NjYwMTE1Ng=='
...
api_url = 'https://www.virustotal.com/api/v3//analyses/' + TEST_FILE_ID
headers = {'x-apikey' : '<ключ доступа к API>'}
response = requests.get(api_url, headers=headers)
И вариант для URL:
TEST_URL_ID = 'u-dce9e8fbe86b145e18f9dcd4aba6bba9959fdff55447a8f9914eb9c4fc1931f9-1576610003'
...
api_url = 'https://www.virustotal.com/api/v3//analyses/' + TEST_URL_ID
headers = {'x-apikey' : '<ключ доступа к API>'}
response = requests.get(api_url, headers=headers)
Мы прошлись по всем основным функциям API сервиса VirusTotal. Ты можешь позаимствовать приведенный код для своих проектов. Если используешь вторую версию, понадобится следить за тем, чтобы не отправлять запросы слишком часто, но в третьей версии такого ограничения пока что нет. Рекомендую выбрать именно ее, поскольку и возможности здесь тоже намного шире. К тому же рано или поздно она станет основной.
Чтобы взломать сеть Wi-Fi с помощью Kali Linux, вам нужна беспроводная карта, поддерживающая режим мониторинга…
Работа с консолью считается более эффективной, чем работа с графическим интерфейсом по нескольким причинам.Во-первых, ввод…
Конечно, вы также можете приобрести подписку на соответствующую услугу, но наличие SSH-доступа к компьютеру с…
С тех пор как ChatGPT вышел на арену, возросла потребность в поддержке чата на базе…
Если вы когда-нибудь окажетесь в ситуации, когда вам нужно взглянуть на спектр беспроводной связи, будь…
Elastic Security стремится превзойти противников в инновациях и обеспечить защиту от новейших технологий злоумышленников. В…