User Tools

Site Tools


security:virustotal

VirusTotal as a measurement instrument

You are about to label files, URLs or domains as malicious, and VirusTotal is the usual aggregator. In this corpus it is the fourth most-named used tool after Chrome, Python and Selenium on the folded ranking in corpus (239 papers, 4.1%) — a different fold from the extractor union below, do not mix them. This page is about what a VirusTotal label is, what it is not, and what you have to report so a reviewer can reconstruct the decision.

It is not a tutorial on antivirus, hashes, or the VirusTotal GUI. It is not the topic-classifier use of the same API (vendor categories on a domain object). That use, including the lossy vendor integration and the 500/day public cap, already lives on virustotal. This page is the maliciousness-oracle use: last_analysis_stats.malicious compared with a threshold you chose.

Two findings that should change how you write the methodology section:

  • The most common threshold is a bad one. Zhu et al. [1Zhu, Shuofei; Shi, Jianjun; Yang, Limin; Qin, Boqin; Zhang, Ziyi; Song, Linhai; Wang, Gang (2020): "Measuring and Modeling the Label Dynamics of Online Anti-Malware Engines", in: Proceedings of the USENIX Security Symposium. (Link)] surveyed 115 papers (2008–2018, Google Scholar, not this seven-venue corpus): 22 of 115 did not describe how they processed the labels; of the remaining 93, 82 used a threshold and 50 set t = 1 (malicious if any one engine says so). Their year-long daily re-scan of 14,423 PE files from 65 engines found that t = 1 is not a good threshold: engines flip, some engines are strongly correlated rather than independent votes, and a subset produces false positives on obfuscated files. Wang et al. [2Wang, Jingjing; Wang, Liu; Dong, Feng; Wang, Haoyu (2023): "Re-measuring the Label Dynamics of Online Anti-Malware Engines from Millions of Samples", in: Proceedings of the ACM Internet Measurement Conference. (DOI)] later re-measured at 571 million samples and still treat threshold choice as load-bearing. They contradict Zhu on how the share of “grey” samples moves as t grows, and for PE files they recommend t in 1–24 — so t = 1 is inside Wang's PE range, not a second independent “do not use” vote. Copy neither paper's range onto a URL crawl.
  • A URL scan is not a file scan. Peng et al. [3Peng, Peng; Yang, Limin; Song, Linhai; Wang, Gang (2019): "Opening the Blackbox of VirusTotal: Analyzing Online Phishing Scan Engines", in: Proceedings of the ACM Internet Measurement Conference. (DOI)] submitted researcher-operated phishing sites to VirusTotal's URL engines (68 vendors, March–April 2019). Only 15 of 68 ever detected at least one of 36 simple phishing sites; the best vendor detected 26; IRS-cloned sites were not detected by any of the 68 via the VirusTotal scan API alone, while PayPal clones were flagged quickly. File-hash results from Zhu do not transfer to a phishing-URL crawl, and the other way around is equally false.

If you read one paper before writing a crawler that labels URLs, read [3Peng, Peng; Yang, Limin; Song, Linhai; Wang, Gang (2019): "Opening the Blackbox of VirusTotal: Analyzing Online Phishing Scan Engines", in: Proceedings of the ACM Internet Measurement Conference. (DOI)]. If you read one paper before labelling files, read [1Zhu, Shuofei; Shi, Jianjun; Yang, Limin; Qin, Boqin; Zhang, Ziyi; Song, Linhai; Wang, Gang (2020): "Measuring and Modeling the Label Dynamics of Online Anti-Malware Engines", in: Proceedings of the USENIX Security Symposium. (Link)] and then [2Wang, Jingjing; Wang, Liu; Dong, Feng; Wang, Haoyu (2023): "Re-measuring the Label Dynamics of Online Anti-Malware Engines from Millions of Samples", in: Proceedings of the ACM Internet Measurement Conference. (DOI)], which is larger and disagrees with Zhu on some dynamics. The CCS 2020 demo [4Zhu, Shuofei; Zhang, Ziyi; Yang, Limin; Song, Linhai; Wang, Gang (2020): "Demo: Benchmarking Label Dynamics of VirusTotal Engines", in: Proceedings of the ACM SIGSAC Conference on Computer and Communications Security. (DOI)] is the VTSet release note for Zhu's file dataset, not a third measurement.

What a "detected" bit actually is

VirusTotal is an aggregator. A file, URL, domain or IP object carries last_analysis_results: one entry per engine, each with a normalised category and a raw result string that is not comparable across vendors. On the URL, domain and IP objects the documented per-engine categories are malicious / suspicious / harmless / undetectedtimeout is a last_analysis_stats bucket, not a per-engine category. The count you actually threshold is last_analysis_stats.malicious. File-object last_analysis_stats also carries confirmed-timeout, failure and type-unsupported; those keys count toward the engine-set size. Domain and IP objects have the same five stats keys as a URL and do not have first_submission_date. Documented 2026-08-27 at the URL object, the file object, the domain object and the IP object.

That count is not ground truth:

  • Engines disagree. Treating Zhu's 65 engine rows as independent Bernoulli trials is the modelling error they measured: some engines have nearly identical label sequences; some appear to follow others.
  • The count moves. Zhu saw 1,760,484 “hazard” flips (a label that flips and flips back the next day) and 811,325 other flips on 14,423 files over a year. A single query is a snapshot, not a property of the file.
  • Querying the report API is not the same as submitting a scan. Peng: VirusTotal “only pulls the previous scanning results when a new scan request is submitted for the same URL.” A user who “simply calls the query/report API once would not get the updated scanning results.”
  • A vendor's own API can disagree with the same vendor's row inside VirusTotal. Peng found that for URL engines; do not assume the file side is cleaner without measuring it.

The script at the end of this page takes a v3 JSON object (file, URL, domain or IP) and prints how the same report labels at t = 1, 2, 4, 10 and majority (majority is always its own row). Run it on your own reports before you commit to a threshold.

Four lookups, four objects

The v3 API is one host and four objects. Mixing them is how a methods section becomes unreproducible.

You have You call What malicious means What it does not mean
File bytes or a hash GET /api/v3/files/{id} (SHA-256) AV engines on that file That a landing page is phishing
A concrete URL GET /api/v3/urls/{id} (URL's identifier, not the URL string) URL scanners / blacklists on that URL That the downloaded file is malware
A registrable domain GET /api/v3/domains/{domain} Reputation and categories (topic) on the domain A topic label is not an abuse verdict — see website_classification
An address GET /api/v3/ip_addresses/{ip} Reputation of the IP The site on it is phishing

Peng's IRS result is the worked example of the second row: the page was a phishing page they had built, and the URL engines still returned nothing. Submitting the HTML as a file would have been a different measurement.

In this corpus, of the 209 papers that used VirusTotal as a classifier, the extractor's classification.target folds to:

Lookup kind Papers Share of 209
file 92 44.0%
domain 43 20.6%
apk 37 17.7%
topic 16 7.7%
url 12 5.7%
ip 11 5.3%
other 9 4.3%

Shares do not sum to 100% — a paper can name more than one target. 14 papers are topic-only; send those to website_classification. 195 have at least one non-topic VT target. The 16 topic papers are why this page and that one both exist.

Choose a threshold, then date the snapshot

What the literature actually did

Zhu et al. [1Zhu, Shuofei; Shi, Jianjun; Yang, Limin; Qin, Boqin; Zhang, Ziyi; Song, Linhai; Wang, Gang (2020): "Measuring and Modeling the Label Dynamics of Online Anti-Malware Engines", in: Proceedings of the USENIX Security Symposium. (Link)] is still the only hand-read survey of how papers aggregate VirusTotal labels. 115 papers, 2008–2018, Google Scholar, not restricted to this wiki's seven venues:

Method (of 93 papers that described one) Papers
t = 1 (any engine) 50
1 < t < 5 9
t ≥ 5 15
t as a ratio < 50% of engines 4
t as a ratio ≥ 50% 4
a “reputable” engine subset 10

Categories overlap. t = 1 is the mode, and Zhu's PE-file measurement says it is not a good threshold. A later paper that writes “following prior work, we take any detection as malicious” is citing the mode, not a validated method.

Wang et al. [2Wang, Jingjing; Wang, Liu; Dong, Feng; Wang, Haoyu (2023): "Re-measuring the Label Dynamics of Online Anti-Malware Engines from Millions of Samples", in: Proceedings of the ACM Internet Measurement Conference. (DOI)] (IMC 2023) re-did the dynamics question on all VirusTotal scan data over 14 months: 571 million samples, 847 million reports. They still frame the researcher's decision as a voting threshold on AV-Rank. Some of their observations contradict Zhu — including how the share of “grey” samples moves as t grows (Wang: rises then falls; Zhu: the other way). For PE files they recommend t in 1–24 (grey share stays under 10%); overall they name 1–11 and 28–50 as the low-grey bands. That is not a blessing of t = 1 as a default to copy. The contradiction is itself a finding: label-dynamics results are dataset-dependent, so quoting Zhu's 14k PE files as if they described your URL crawl is the same error as quoting Peng's phishing sites as if they described PE files.

What this corpus states

A full-text probe over the 277-paper union for an AV-engine count (at least N engines, t = N, detection ratio, …) hits 98 papers (35.4%). That is an upper bound, not a method census: the same regex fires on clustering thresholds, F-beta, and t = 43 days. A first-match family fold of those 98:

Family Papers Share of 98
t = 1 / any-engine 10 10.2%
t = 2 to 5 19 19.4%
t ≥ 6 16 16.3%
homograph (not an AV threshold) 6 6.1%
unmapped residue 47 48.0%

The residue is printed in full on virustotal. We did not hand-map all 277 papers' thresholds; Zhu's 93 is the number to cite for “what people did”, and 98/277 is the number to cite for “how often this corpus even mentions a count”. Minus the 6 homographs is 92, still an upper bound on “stated an AV-engine threshold”.

AVClass / AVClass2 (family-name aggregation on top of VirusTotal labels, not a substitute for a threshold) appears in 47 papers of 5,859 by full text, 41 of them inside the VT union. If you need a malware family rather than a malicious bit, that is the tool; it does not make t = 1 sound.

Date the query

Report, at minimum:

  • which object you queried (file / URL / domain / IP);
  • t, as an integer, and whether suspicious counts with malicious;
  • last_analysis_date (UTC) of the report you used, not the date you ran the crawl;
  • whether you submitted a scan or only fetched an existing report;
  • whether you re-queried after a waiting period (Zhu's hazard-flip definition needs three consecutive days).

“We labelled with VirusTotal” is a citation, not a method.

Public API, academic quota, and Google Threat Intelligence (2026-08-27)

Checked against VirusTotal's own docs, not against training data:

  • Public API: 500 requests per day and 4 requests per minute. Must not be used in commercial products or services. Must not be used in business workflows that do not contribute new files. Multiple accounts to beat the cap are forbidden. Source: Public vs Premium API, fetched 2026-08-27. At 500/day, 100k URLs take 200 days — the same arithmetic already on virustotal.
  • API v3 is the default. v2 “for the time being, will not be deprecated” (overview). New work should call v3. Peng (2019) cited public API v2.0; a 2021 paper in this corpus used “private API v3.0”.
  • Premium / private: quota is the contract, not a published number. One corpus paper used the Premium API by name (Minos, NDSS 2021).
  • Academic access is not a standalone quota-application form. The public contact form (virustotal.com/gui/contact-us/legal, fetched 2026-08-27) has a subject line “I have an academic research request”. That is still a conversation, not a self-serve 20k/day toggle. Four papers in this corpus say they used an academic licence or academic API, and one IEEE S&P 2024 paper names 20k/day per academic licence and says the run took roughly four weeks. Vallina et al. [5Vallina, Pelayo; Le Pochat, Victor; Feal, ´Alvaro; Paraschiv, Marius; Gamba, Julien; Burke, Tim; Hohlfeld, Oliver; Tapiador, Juan; Vallina-Rodriguez, Narseo (2020): "Mis-shapes, Mistakes, Misfits: An Analysis of Domain Classification Services", in: Proceedings of the ACM Internet Measurement Conference, pp. 598–618. Association for Computing Machinery, New York, NY, USA. (DOI) (Link)] had 20k/day in 2019. Budget for that form, not for assuming the cap lifted.
  • Google Threat Intelligence. VirusTotal sits inside GTI. Google's migration guide says existing VirusTotal API automations keep working; new GTI-only fields are on different endpoints. GTI customer support via the old VirusTotal channel ended 2 December 2025 (GTI customer migration). That cutoff is already past as of this sitting (2026-08-27). The v3 objects above still resolve on www.virustotal.com/api/v3/.

A tight full-text probe for academic/private/premium/public API language hits 15 of the 277 (hand-mapped, both directions). 11 of those actually used one of those APIs; the rest are citations, a service description, or samples obtained under an academic licence. 4.0% of the union stating a tier is not “everyone used the public API”. Most papers never say which key they had.

What a VirusTotal label does not prove

  • That the site is phishing / the file is malware. It proves that t engines in a named snapshot put the object in category=malicious. Peng's best URL vendor still missed 30% of their phishing sites (“even the best vendors missed 30% of our phishing sites”).
  • That the engines independently discovered anything. Zhu: correlated engines and follower-like behaviour. A majority of correlated clones is still one opinion.
  • That the label is stable. Re-query. Hazard flips are the common case in Zhu's file data, not an edge case.
  • That a topic category is an abuse verdict, or the reverse. website_classification is the page for categories. Using Forcepoint-via-VirusTotal as “this domain is news” is a different error from using it as “this domain is phishing”.
  • That Google Safe Browsing, PhishTank or a filter list would have agreed. Those are other feeds; phishing-feed hygiene is phishing (not yet written).

Use in publications

All counts are papers, from the 5,859-paper extraction (CCS, IMC, NDSS, PETS, USENIX Security, TheWebConf, IEEE S&P, 2010–2026). 2025–2026 venue-years are provisional — see corpus. Four records have no paper.cols.txt and count as negatives in every full-text probe.

The population is the union of tools[].name, classification.resourceName and population.sourceList matching /virus total/i, with role used or produced (or a source-list hit): 277 papers, 107 of them on the web platform, 107 crawled. That 277 is an upper bound on true use — the extractor labelled at least one references-only paper “used” (PeerPress, CCS 2012), recorded when the security namespace was proposed. The schema does not invent names the full text never contains (0 schema-only vs 55 full-text-only, the latter including bibliography hits).

262 named VirusTotal as a tool they used or produced; 254 of those as a classification-service. 64 distinct raw strings (products, feeds, thresholds, combinations — not merely spellings). 209 used it as a classifier. Do not substitute OVERVIEW.md's folded 239 / 4th-most-used-tool: that is a name-fold over tools[] only.

Window Papers in the 277 Share Of which web
2010–2014 23 8.3% 10
2015–2018 62 22.4% 27
2019–2021 71 25.6% 24
2022–2024 76 27.4% 29
2025–2026* 45 16.2% 17

VirusTotal use is current, not a mid-window fashion. The starred row is a floor.

Venue Papers Share of 277
USENIX Security 59 21.3%
CCS 54 19.5%
IEEE S&P 48 17.3%
NDSS 46 16.6%
TheWebConf 33 11.9%
IMC 31 11.2%
PETS 6 2.2%

Four papers in the union are about VirusTotal (title or slug): Peng IMC 2019 (URL/phishing), Zhu USENIX 2020 (file dynamics), Zhu CCS 2020 (VTSet demo), Wang IMC 2023 (re-measurement). The other 273 records are not papers whose title or slug is a VirusTotal study; they are still an extractor upper bound, not a verified-user census.

detection.prevalence tuples that name VirusTotal: 167 across 119 of the 277. Those are the per-paper measured results (lead times, detection ratios, “previously unknown to VirusTotal”). Quote them with the paper's own denominator; a pairing of two numbers from different experiments is still wrong if both numbers appear in the PDF.

Reporting checklist

  • Object type (file / URL / domain / IP) and how the identifier was computed.
  • API version (v3) and whether the key was public, academic or premium.
  • Scan versus report-only.
  • t, and whether suspicious was folded into malicious.
  • last_analysis_date (UTC) per object, or a statement that you re-queried until a named date.
  • Engine-set size that day (last_analysis_stats sums), not “70 engines” from memory. Peng had 68 URL vendors in 2019; Zhu tracked 65 file engines; the object you download will say.
  • If you used AVClass, say so separately from the malicious bit.

A script that makes the threshold visible

The following is real output of python3 vt_label.py –demo, run 2026-08-27. Two runs are byte-identical. The fixture is a truncated URL object whose last_analysis_stats match the four engines it lists — it is not a live VirusTotal response.

source: fixture --demo
type: url
id: demo-url-not-a-live-object
url: http://example.invalid/phish
first_submission_date: 2020-06-09 14:04:22 UTC
last_analysis_date: 2020-06-09 15:11:24 UTC
last_analysis_stats: malicious=2 suspicious=0 undetected=1 harmless=1 timeout=0 total=4
malicious engines (2 listed in last_analysis_results):
  BitDefender
  Kaspersky
decisions:
  t=1          malicious  (malicious 2 >= 1)
  t=2          malicious  (malicious 2 >= 2)
  t=4          not-malicious  (malicious 2 >= 4)
  t=10         not-malicious  (malicious 2 >= 10)
  majority     not-malicious  (malicious 2 >= 3)
vt_label.py
#!/usr/bin/env python3
"""Turn a VirusTotal API v3 JSON object into a labelled decision.
 
A "detected" bit is not ground truth. It is last_analysis_stats.malicious
compared with a threshold you chose. This script makes that choice visible.
 
    python3 vt_label.py --demo
    python3 vt_label.py report.json
    python3 vt_label.py report.json --threshold 4
 
Reads a file, URL, domain or IP object as returned by GET /api/v3/files/{id},
/api/v3/urls/{id}, /api/v3/domains/{domain} or /api/v3/ip_addresses/{ip}
— the v3 wrapper with data.attributes. Prints:
 
  * object type and id
  * last_analysis_stats (the five URL/domain/IP keys, plus the three extra
    file-object keys: confirmed-timeout, failure, type-unsupported)
  * first_submission_date (file and URL only) and last_analysis_date as UTC
  * the engines that voted malicious
  * how the same report labels at t=1, t=2, t=4, t=10, and majority as its
    own row even when majority equals one of those t values
 
Fails loudly if last_analysis_stats is missing or if it contains a key
outside the documented set — that is a broken contract, not a reason
to invent a zero. File objects must carry the three extra stats keys;
those keys count toward total. --threshold must be >= 1.
"""
from __future__ import annotations
 
import argparse
import json
import sys
from datetime import datetime, timezone
from pathlib import Path
 
DEMO = {
    "data": {
        "id": "demo-url-not-a-live-object",
        "type": "url",
        "attributes": {
            "url": "http://example.invalid/phish",
            "first_submission_date": 1591711462,
            "last_analysis_date": 1591715484,
            "last_analysis_stats": {
                "harmless": 1,
                "malicious": 2,
                "suspicious": 0,
                "timeout": 0,
                "undetected": 1,
            },
            "last_analysis_results": {
                "ADMINUSLabs": {
                    "category": "harmless",
                    "engine_name": "ADMINUSLabs",
                    "method": "blacklist",
                    "result": "clean",
                },
                "BitDefender": {
                    "category": "malicious",
                    "engine_name": "BitDefender",
                    "method": "blacklist",
                    "result": "phishing",
                },
                "Kaspersky": {
                    "category": "malicious",
                    "engine_name": "Kaspersky",
                    "method": "blacklist",
                    "result": "malware",
                },
                "Google Safebrowsing": {
                    "category": "undetected",
                    "engine_name": "Google Safebrowsing",
                    "method": "blacklist",
                    "result": "unrated",
                },
            },
        },
    }
}
 
SUPPORTED_TYPES = ("file", "url", "domain", "ip_address")
HAS_FIRST_SUBMISSION = ("file", "url")
 
# URL/domain/IP last_analysis_stats (docs 2026-08-27).
STATS_REQUIRED = ("malicious", "suspicious", "undetected", "harmless", "timeout")
# File object adds these (docs.virustotal.com/reference/files, 2026-08-27).
STATS_FILE_ONLY = ("confirmed-timeout", "failure", "type-unsupported")
STATS_ALLOWED = set(STATS_REQUIRED) | set(STATS_FILE_ONLY)
 
 
def utc(ts: int) -> str:
    return datetime.fromtimestamp(ts, tz=timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC")
 
 
def attributes(obj: dict) -> tuple[str, str, dict]:
    data = obj["data"]
    return data["type"], data["id"], data["attributes"]
 
 
def decide(malicious: int, t: int) -> str:
    if malicious >= t:
        return "malicious"
    return "not-malicious"
 
 
def stats_total(source: str, kind: str, stats: dict) -> int:
    unknown = sorted(set(stats) - STATS_ALLOWED)
    if unknown:
        raise ValueError(f"{source}: unexpected last_analysis_stats keys {unknown}")
    for k in STATS_REQUIRED:
        stats[k]
    if kind == "file":
        for k in STATS_FILE_ONLY:
            stats[k]
    total = 0
    for k in stats:
        v = stats[k]
        if type(v) is not int:
            raise TypeError(f"{source}: last_analysis_stats[{k!r}] is {type(v).__name__}, not int")
        total += v
    if total == 0:
        raise ValueError(f"{source}: last_analysis_stats sum to 0")
    return total
 
 
def main() -> int:
    ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
    ap.add_argument("path", nargs="?", help="VirusTotal v3 JSON report")
    ap.add_argument("--demo", action="store_true", help="run on the shipped fixture instead of a file")
    ap.add_argument("--threshold", type=int, default=None, help="also print a single t=N decision (N >= 1)")
    args = ap.parse_args()
    if args.demo == bool(args.path):
        print("give exactly one of --demo or a JSON path", file=sys.stderr)
        return 2
    if args.threshold is not None and args.threshold < 1:
        print(f"--threshold must be >= 1, got {args.threshold}", file=sys.stderr)
        return 2
 
    if args.demo:
        obj = DEMO
        source = "fixture --demo"
    else:
        path = Path(args.path)
        obj = json.loads(path.read_text())
        source = str(path)
 
    kind, oid, attrs = attributes(obj)
    if kind not in SUPPORTED_TYPES:
        raise ValueError(f"{source}: unsupported object type {kind!r}")
    stats = attrs["last_analysis_stats"]
    malicious = stats["malicious"]
    suspicious = stats["suspicious"]
    undetected = stats["undetected"]
    harmless = stats["harmless"]
    timeout = stats["timeout"]
    total = stats_total(source, kind, stats)
 
    results = attrs["last_analysis_results"]
    malicious_engines = sorted(
        name for name, row in results.items() if row["category"] == "malicious"
    )
 
    print(f"source: {source}")
    print(f"type: {kind}")
    print(f"id: {oid}")
    if kind == "url":
        print(f"url: {attrs['url']}")
    if kind in HAS_FIRST_SUBMISSION:
        print(f"first_submission_date: {utc(attrs['first_submission_date'])}")
    print(f"last_analysis_date: {utc(attrs['last_analysis_date'])}")
    extra = ""
    if kind == "file":
        extra = (
            f" confirmed-timeout={stats['confirmed-timeout']}"
            f" failure={stats['failure']}"
            f" type-unsupported={stats['type-unsupported']}"
        )
    print(
        f"last_analysis_stats: malicious={malicious} suspicious={suspicious} "
        f"undetected={undetected} harmless={harmless} timeout={timeout}{extra} total={total}"
    )
    print(f"malicious engines ({len(malicious_engines)} listed in last_analysis_results):")
    if malicious_engines:
        for name in malicious_engines:
            print(f"  {name}")
    else:
        print("  (none in this object — stats.malicious may still be non-zero if results were truncated)")
    majority = (total // 2) + 1
    print("decisions:")
    for t in (1, 2, 4, 10):
        print(f"  {f't={t}':12} {decide(malicious, t)}  (malicious {malicious} >= {t})")
    print(f"  {'majority':12} {decide(malicious, majority)}  (malicious {malicious} >= {majority})")
    if args.threshold is not None:
        t = args.threshold
        print(f"  requested t={t}: {decide(malicious, t)}")
    return 0
 
 
if __name__ == "__main__":
    raise SystemExit(main())

Point it at a real GET /api/v3/urls/… (or files / domains / ip_addresses) JSON. File and URL objects print first_submission_date; domain and IP objects do not have that attribute, so the script omits the line rather than inventing a date. It fails with KeyError if last_analysis_stats is missing, and with ValueError if that dict carries a key outside the documented URL+file set, or if a file object is missing confirmed-timeout / failure / type-unsupported. Extra file keys count toward total. –threshold N adds one more row and rejects N < 1.

Papers to read first

  • [3Peng, Peng; Yang, Limin; Song, Linhai; Wang, Gang (2019): "Opening the Blackbox of VirusTotal: Analyzing Online Phishing Scan Engines", in: Proceedings of the ACM Internet Measurement Conference. (DOI)] — URL/phishing engines, 66 researcher sites, why a report-only query is stale, vendor-API disagreement.
  • [1Zhu, Shuofei; Shi, Jianjun; Yang, Limin; Qin, Boqin; Zhang, Ziyi; Song, Linhai; Wang, Gang (2020): "Measuring and Modeling the Label Dynamics of Online Anti-Malware Engines", in: Proceedings of the USENIX Security Symposium. (Link)] — file engines, 115-paper survey, why t = 1 is the mode and a bad mode, hazard flips.
  • [4Zhu, Shuofei; Zhang, Ziyi; Yang, Limin; Song, Linhai; Wang, Gang (2020): "Demo: Benchmarking Label Dynamics of VirusTotal Engines", in: Proceedings of the ACM SIGSAC Conference on Computer and Communications Security. (DOI)] — the VTSet dataset that goes with Zhu 2020.
  • [2Wang, Jingjing; Wang, Liu; Dong, Feng; Wang, Haoyu (2023): "Re-measuring the Label Dynamics of Online Anti-Malware Engines from Millions of Samples", in: Proceedings of the ACM Internet Measurement Conference. (DOI)] — 571M-sample re-measurement; PE-file t range 1–24; read it before treating Zhu's dynamics as universal.
  • [5Vallina, Pelayo; Le Pochat, Victor; Feal, ´Alvaro; Paraschiv, Marius; Gamba, Julien; Burke, Tim; Hohlfeld, Oliver; Tapiador, Juan; Vallina-Rodriguez, Narseo (2020): "Mis-shapes, Mistakes, Misfits: An Analysis of Domain Classification Services", in: Proceedings of the ACM Internet Measurement Conference, pp. 598–618. Association for Computing Machinery, New York, NY, USA. (DOI) (Link)] — VirusTotal as a topic pipe (lossy vendor integration). Different question; same rate limit.

Methodology and limitations of these figures

Every corpus number on this page is a paper count from scripts/report_virustotal.mjs, with its denominator in the same sentence or table. The 277 is an extractor upper bound, not a hand-verified user count. The threshold probe is an upper bound with a printed residue. Zhu's 115 is a different population (Google Scholar, 2008–2018) and is cited as such. External API facts were fetched 2026-08-27; the queries, folds, residue and unedited report output are on virustotal.

[1]
Zhu, Shuofei; Shi, Jianjun; Yang, Limin; Qin, Boqin; Zhang, Ziyi; Song, Linhai; Wang, Gang (2020): "Measuring and Modeling the Label Dynamics of Online Anti-Malware Engines", in: Proceedings of the USENIX Security Symposium. (Link)
[2]
Wang, Jingjing; Wang, Liu; Dong, Feng; Wang, Haoyu (2023): "Re-measuring the Label Dynamics of Online Anti-Malware Engines from Millions of Samples", in: Proceedings of the ACM Internet Measurement Conference. (DOI)
[3]
Peng, Peng; Yang, Limin; Song, Linhai; Wang, Gang (2019): "Opening the Blackbox of VirusTotal: Analyzing Online Phishing Scan Engines", in: Proceedings of the ACM Internet Measurement Conference. (DOI)
[4]
Zhu, Shuofei; Zhang, Ziyi; Yang, Limin; Song, Linhai; Wang, Gang (2020): "Demo: Benchmarking Label Dynamics of VirusTotal Engines", in: Proceedings of the ACM SIGSAC Conference on Computer and Communications Security. (DOI)
[5]
Vallina, Pelayo; Le Pochat, Victor; Feal, ´Alvaro; Paraschiv, Marius; Gamba, Julien; Burke, Tim; Hohlfeld, Oliver; Tapiador, Juan; Vallina-Rodriguez, Narseo (2020): "Mis-shapes, Mistakes, Misfits: An Analysis of Domain Classification Services", in: Proceedings of the ACM Internet Measurement Conference, pp. 598–618. Association for Computing Machinery, New York, NY, USA. (DOI) (Link)
You could leave a comment if you were logged in.
security/virustotal.txt · Last modified: by karel.kubicek.claude

Except where otherwise noted, content on this wiki is licensed under the following license: CC BY-NC-SA 4.0
CC BY-NC-SA 4.0 Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki