In this post, we explore the VirusTotal API. We also look at how Tines and no-code automation can power up your usage of the VirusTotal API.
Public vs. Private APIs
VirusTotal provides two API versions: a Public API and a Private API. The main differences between the two are the volume of queries available and the depth of information provided. The public API allows four queries per minute and does not allow malware sample downloads. For the majority of uses, the public VirusTotal API will be sufficient and is what we’ll focus on in this post.
Creating a VirusTotal account
Access to the VirusTotal public API is free, to get started, you’ll need to obtain an API key. The API key allows you to make queries against the API. Click “Join our community” on the virustotal.com home page. Enter the required details and click “sign-up.”
Virustotal API sign up
Getting a VirusTotal API key
After you’ve created your account, click your username in the top right-hand corner of the page. Then, from the drop-down menu, select “My API key.”
On the next page, VirusTotal will display your API key. It will be a long, alpha-numeric string. As with all API keys, you should treat this key as a password, store it securely in a password manager, and don’t embed it in scripts.
VirusTotal API examples
Now that we have an API key, we can start making queries against the public API. The following examples use cURL, if you don’t have cURL installed, you may want to use our Postman collection. Additionally, in all the below examples, you should replace $your-api-key with the API key from your VirusTotal profile.
Further details, including sample requests in Python, are available from the VirusTotal Public API docs page.
Get a file scan report
If we have a suspicious file, we can check its status with the VirusTotal API. In the below example, replace $your-file-hash with the hash of the file you want to check.
curl -v --request POST \
--url 'https://www.virustotal.com/vtapi/v2/file/report' \
-d apikey=$your-api-key \
-d 'resource=$your-file-hash'
If the file existed in its database, VirusTotal will return the results in a response similar to the below:
{
'response_code': 1,
'verbose_msg': 'Scan finished, scan information embedded in this object',
'resource': '99017f6eebbac24f351415dd410d522d',
'scan_id': '52d3df0ed60c46f336c131bf2ca454f73bafdc4b04dfa2aea80746f5ba9e6d1c-1273894724',
'md5': '99017f6eebbac24f351415dd410d522d',
'sha1': '4d1740485713a2ab3a4f5822a01f645fe8387f92',
'sha256': '52d3df0ed60c46f336c131bf2ca454f73bafdc4b04dfa2aea80746f5ba9e6d1c',
'scan_date': '2010-05-15 03:38:44',
'positives': 40,
'total': 40,
'scans': {
'nProtect': {'detected': true, 'version': '2010-05-14.01', 'result': 'Trojan.Generic.3611249', 'update': '20100514'},
'CAT-QuickHeal': {'detected': true, 'version': '10.00', 'result': 'Trojan.VB.acgy', 'update': '20100514'},
'McAfee': {'detected': true, 'version': '5.400.0.1158', 'result': 'Generic.dx!rkx', 'update': '20100515'},
'TheHacker': {'detected': true, 'version': '6.5.2.0.280', 'result': 'Trojan/VB.gen', 'update': '20100514'},
.
.
.
'VirusBuster': {'detected': true, 'version': '5.0.27.0', 'result': 'Trojan.VB.JFDE', 'update': '20100514'},
'NOD32': {'detected': true, 'version': '5115', 'result': 'a variant of Win32/Qhost.NTY', 'update': '20100514'},
'F-Prot': {'detected': false, 'version': '4.5.1.85', 'result': null, 'update': '20100514'},
'Symantec': {'detected': true, 'version': '20101.1.0.89', 'result': 'Trojan.KillAV', 'update': '20100515'},
'Norman': {'detected': true, 'version': '6.04.12', 'result': 'W32/Smalltroj.YFHZ', 'update': '20100514'},
'TrendMicro-HouseCall': {'detected': true, 'version': '9.120.0.1004', 'result': 'TROJ_VB.JVJ', 'update': '20100515'},
'Avast': {'detected': true, 'version': '4.8.1351.0', 'result': 'Win32:Malware-gen', 'update': '20100514'},
'eSafe': {'detected': true, 'version': '7.0.17.0', 'result': 'Win32.TRVB.Acgy', 'update': '20100513'}
},
'permalink': 'https://www.virustotal.com/file/52d3df0ed60c46f336c131bf2ca454f73bafdc4b04dfa2aea80746f5ba9e6d1c/analysis/1273894724/'
}
Scan a file with VirusTotal API
The below request sends a file located at /your/file/path to VirusTotal for scanning.
curl -X POST \
https://www.virustotal.com/vtapi/v2/file/scan \
-F apikey=$your-api-key \
-F file=@/your/file/path
If the submission was successful, you will receive a response similar to the below. Notice that VirusTotal will include a field called “scan_id”. VirusTotal will not scan the file immediately, and will instead add it to a queue. As such, the “scan_id” field allows us to check the status of the scan.
Example response:
{
'permalink': 'https://www.virustotal.com/file/d140c...244ef892e5/analysis/1359112395/',
'resource': u'd140c244ef892e59c7f68bd0c6f74bb711032563e2a12fa9dda5b760daecd556',
'response_code': 1, 'scan_id': 'd140c244ef892e59c7f68bd0c6f74bb711032563e2a12fa9dda5b760daecd556-1359112395',
'verbose_msg': 'Scan request successfully queued, come back later for the report',
'sha256': 'd140c244ef892e59c7f68bd0c6f74bb711032563e2a12fa9dda5b760daecd556'
}
Rescan an already submitted file
If a scan report is out-of-date, or you want the most recent status, you can rescan the file by submitting its hash.
curl -X POST \
https://www.virustotal.com/vtapi/v2/file/rescan \
-F apikey=$your-api-key \
-F resource=7657fcb7d772448a6d8504e4b20168b8
Send and scan URL
VirusTotal mt1 mb0 provides the status of URLs in its database. To scan a URL, use a command similar to that shown below.
curl -X POST \
https://www.virustotal.com/vtapi/v2/url/scan \
-F apikey=$your-api-key \
-F url=https://tines.io
Retrieve URL scan report
Before submitting a URL for scanning, it’s best practice to check if the URL already exists in the database. Do this using a command similar to that shown below.
curl -X POST \
http://www.virustotal.com/vtapi/v2/url/report \
-F apikey=$your-api-key \
-F resource=https://tines.io
Retrieve URL scan report (scan if does not exist)
In the above command, if the URL doesn’t already exist in VirusTotal’s database, the response will be blank. By specifying “scan=1” in our request, we can tell VirusTotal to automatically scan the URL if it doesn’t already exist.
curl -X POST \
https://www.virustotal.com/vtapi/v2/url/report \
-F apikey=$your-api-key \
-F url=https://tines.io \
-F scan=1
Retrieve domain report
Most people are aware that VirusTotal contains the status of URLs and files across anti-virus engines. However, not everyone is aware that it also maintains an extensive database of passive DNS data. To get the status of a domain, including passive DNS information via the VirusTotal API, use a request similar to the below.
curl -X GET \
'http://www.virustotal.com/vtapi/v2/domain/report?domain=tines.i
Retrieve IP address report
To retrieve the status of an IP address, including passive DNS, submit a request similar to the below to the VirusTotal API.
curl -X GET \
'http://www.virustotal.com/vtapi/v2/ip-address/report?ip=165.227.135.98&apikey=$your-api-key'
Comment on file or URL
An extremely valuable, if slightly underused resource in VirusTotal, is the comments applied to files and URLs. Below, we’re indicating that the URL in question is a phishing page. As such, we’re providing valuable context to other VirusTotal users.
curl -X POST \
https://www.virustotal.com/vtapi/v2/comments/put \
-F apikey=$your-api-key \
-F resource=https://evil-phishing-site.com/secured/login \
-F 'comment=This is a phishing page'
VirusTotal API Postman Collection
When experimenting with APIs, an extremely useful tool is Postman. Postman provides a GUI that allows interaction with APIs. Additionally, it’s easy to create, troubleshoot and share API requests. At Tines, we’ve created a Postman collection with common VirusTotal API queries. You can download the collection, or clone the repository, from here and then import it into Postman.
Configuring Postman with the VirusTotal API collection
After you have imported the Postman collection, create a new environment variable called “apikey”. Next, store your VirusTotal API key in this variable (see below).
Postman with VirusTotal API key
The Postman collection contains several API calls that can be customized based on your requirements. For example, replace the URL you wish to scan, or the IP address for which you wish to perform a passive DNS lookup.
Using the VirusTotal API with Tines
In our automating phishing and abuse inbox management tutorial series, we used the VirusTotal API extensively to analyze suspicious URLs and files. So, you may want to start there to understand a real-world security automation application of the VirusTotal API.
Adding your VirusTotal API key to a Tines credential
The Tines credential widget allows storage of secret information so it can be safely included in Action bodies, without fear of disclosure. To add your VirusTotal API key to Tines, when signed in to a tenant, choose “Credentials” -> “New Credential”.
Tines supports a variety of credential types, for the VirusTotal API, choose “text”. Next, choose a name for the credential, then enter your API key under “Credential value”.
VirusTotal API key in Tines credential
After saving the credential, we can now include it in Action configurations with the credential widget: {{CREDENTIAL.Virustotal }}.
Submitting queries to the VirusTotal API using Tines
The Tines HTTP Request Action (HRA) is used to integrate with 3rd-party APIs. Configuring an HRA to talk to the VirusTotal API is easy. First, create a new HRA: from anywhere in your tenant choose “Actions -> New Action” from the main menu, then choose “HTTP Request Action” from the Action type drop-down.
After configuring the common config, edit the options block to reflect the call you want to make to VirusTotal. For example, if you want to comment on a file or URL you would use the following options block:
{
"url": "https://www.virustotal.com/vtapi/v2/comments/put",
"content_type": "form",
"method": "post",
"payload": {
"apikey": "{{ credential Virustotal }}",
"resource": "https://evil-phishing-site.com/secured/login",
"comment": "This is a phishing page"
}
}
If you wanted to retrieve an IP address report, you would use an options block similar to that shown below:
{
"url": "http://www.virustotal.com/vtapi/v2/ip-address/report",
"content_type": "form",
"method": "get",
"payload": {
"apikey": "{{ CREDENTIAL. Virustotal }}",
"ip": "165.227.135.98"
}
}
In this case, we’re using the GET method and calling the /ip-address/report endpoint. When the Action runs, it will emit the response from the VirusTotal API as a new event. See the sample emitted event below:
Sample emitted Tines event Virus Total API
Avoiding VirusTotal API rate limits
As mentioned above, the VirusTotal API rate limits requests to four per minute. As a result, when you exceed this quota, VirusTotal will respond with an empty body and an HTTP status of 429.
{
"get_ip_report_from_virustotal": ⊖{
"body": "",
"headers": {...},
"status": 429
}
Using Tines, we can account for event spikes and thus avoid exceeding our quota using Trigger Actions (TA) and an Event Transformation Actions (ETA) in delay mode.
In the diagram below, we use an HTTP Request Action to query the VirusTotal API, then check whether the response hit a rate limit. If it did, that is the status was 429, we wait 20 seconds and try again.