# Cách kiểm tra VPS Linux có bị hack không

> Kiểm tra VPS bị hack: đăng nhập, user, SSH key, tiến trình, kết nối mạng, cron, systemd, file hệ thống bị sửa và rootkit, kèm lệnh và dấu hiệu cần tìm.

Source: https://zopanel.net/vi/blog/check-if-linux-server-hacked  
Updated: 2026-10-21

## Ý chính

- Kiểm tra đăng nhập, tài khoản và SSH key trước: chúng cho thấy kẻ tấn công vào bằng cách nào và định quay lại ra sao.
- Tìm tiến trình chạy từ /tmp hoặc /dev/shm, file chạy đã bị xoá và kết nối ra ngoài bất thường.
- Cơ chế bám trụ thường nằm trong cron, timer systemd, authorized_keys và /etc/ld.so.preload.
- Kết quả sạch không chứng minh server sạch; nếu root đã bị chiếm, hãy dựng lại thay vì tin vào nó.

Để kiểm tra VPS bị hack hay không, hãy xem bảy chỗ: lịch sử đăng nhập gần đây, tài khoản user và SSH key, tiến trình đang chạy, kết nối mạng, tác vụ định kỳ và dịch vụ, file hệ thống bị thay đổi, và email gửi ra. Dấu hiệu bị xâm nhập gồm đăng nhập từ IP lạ, tài khoản UID 0 mới, SSH key bạn không thêm, tiến trình chạy từ `/tmp` hoặc `/dev/shm`, kết nối tới máy lạ và file hệ thống không khớp checksum của gói cài đặt. Các lệnh dưới đây chạy trên Ubuntu và Debian, mất khoảng 20 phút.

Một lưu ý trước khi bắt đầu: nếu kẻ tấn công đã có root, họ có thể làm các công cụ nói dối. Các bước này bắt được những kiểu xâm nhập phổ biến như trình đào coin, script gửi spam và webshell, vốn thường để lại nhiều dấu vết, nhưng kết quả sạch không phải bằng chứng. Nếu thấy dấu hiệu bị chiếm quyền root, hãy dừng điều tra trên hệ thống đang chạy và làm theo kế hoạch dựng lại, như bài [quy trình khôi phục khi server bị mã hoá ransomware](/vi/blog/web-server-ransomware-recovery).

## Dấu hiệu VPS có thể đã bị hack

| Triệu chứng | Có thể là gì | Kiểm tra đầu tiên |
| --- | --- | --- |
| CPU 100% dù không có truy cập | Trình đào coin | `ps aux --sort=-%cpu` |
| IP server nằm trong blacklist spam | Script spam hoặc website bị hack | Hàng đợi mail, `ss -tnp` ở cổng 25 |
| File lạ trong thư mục website | Webshell hoặc backdoor | Quét mã độc, `find` theo ngày |
| Nhà cung cấp gửi cảnh báo abuse vì quét cổng hoặc DDoS | Server bị dùng làm bot | `ss -tupan`, danh sách tiến trình |
| Mật khẩu bị đổi, không đăng nhập được | Bị chiếm tài khoản | `last`, log xác thực |
| Log bị mất hoặc bị cắt ngắn | Kẻ tấn công xoá dấu vết | `journalctl --list-boots`, kích thước log |
| Thiết lập tường lửa hoặc SSH bị đổi | Kẻ tấn công giữ đường vào | `ufw status`, `sshd -T` |

## Trước khi bắt đầu

- **Chụp snapshot** ở nhà cung cấp VPS nếu có thể. Nó giữ nguyên bằng chứng phòng khi cần sau này.
- **Làm việc bằng root qua SSH từ máy tính tin cậy**, và lưu kết quả ra file ngoài thư mục web, ví dụ `script /root/ir-$(date +%F).log`.
- **Chưa khởi động lại, chưa xoá gì cả.** Xoá file đáng ngờ trước khi biết nó từ đâu tới là xoá mất manh mối tốt nhất.

## Bước 1: ai đã đăng nhập, từ đâu?

```bash
last -F | head -30              # đăng nhập thành công, đủ ngày giờ
lastb -F | head -30             # đăng nhập thất bại (cần root)
who; w                          # ai đang đăng nhập lúc này
journalctl -u ssh --since "-7 days" | grep -E "Accepted|Invalid user" | tail -50
grep -E "Accepted (password|publickey)" /var/log/auth.log* 2>/dev/null | tail -50
```

Cần tìm: dòng `Accepted password` trong khi bạn chỉ dùng key, đăng nhập từ quốc gia hoặc nhà mạng bạn chưa bao giờ dùng, đăng nhập vào giờ bất thường, và các tài khoản lẽ ra không bao giờ đăng nhập như `www-data` hay một tài khoản hosting. Hàng nghìn lần đăng nhập thất bại là chuyện bình thường với mọi server công khai; một lần thành công từ IP lạ thì không. (Trên một số hệ thống, unit SSH tên là `sshd` thay vì `ssh`.)

## Bước 2: tài khoản, quyền sudo và SSH key

```bash
awk -F: '$3 == 0 {print $1}' /etc/passwd          # ngoài root ra đều đáng ngờ
awk -F: '$3 >= 1000 {print $1, $6, $7}' /etc/passwd
awk -F: '($2 == "") {print $1}' /etc/shadow       # tài khoản mật khẩu rỗng
getent group sudo; ls -la /etc/sudoers.d/
find / -xdev -name authorized_keys -exec ls -la {} \; -exec cat {} \; 2>/dev/null
```

Tìm tài khoản UID 0 thứ hai, user bạn không tạo, file `sudoers.d` mới sửa gần đây và SSH key có ghi chú lạ. Thêm key vào `authorized_keys` là kỹ thuật bám trụ tiêu chuẩn, được MITRE ATT&CK ghi nhận là [T1098.004](https://attack.mitre.org/techniques/T1098/004/). Kiểm tra key của root và của mọi user, không chỉ của bạn.

## Bước 3: tiến trình

```bash
ps auxf --sort=-%cpu | head -30                   # tiến trình tốn tài nguyên nhất, dạng cây
ls -l /proc/*/exe 2>/dev/null | grep -E "deleted|/tmp/|/dev/shm/|/var/tmp/"
ls -la /tmp /var/tmp /dev/shm
```

Dấu hiệu đáng lo:

- tiến trình chạy một file đã bị **xoá** khỏi ổ đĩa, mẹo ẩn mình kinh điển;
- file chạy từ `/tmp`, `/var/tmp` hoặc `/dev/shm`, những nơi ai cũng ghi được;
- tên giả dạng luồng kernel (kiểu `[kworker]`) nhưng lại có đường dẫn file chạy;
- user web server hoặc PHP (`www-data`, tài khoản hosting) chạy `bash`, `perl`, `python`, `nc` hay `curl` lặp đi lặp lại.

Với PID đáng ngờ, `ls -l /proc/<PID>/cwd`, `cat /proc/<PID>/cmdline | tr '\0' ' '` và `ls -l /proc/<PID>/fd` cho biết nó chạy từ đâu, được khởi động thế nào và đang giữ file, socket nào.

## Bước 4: kết nối mạng

```bash
ss -tulpn                       # cổng nào đang lắng nghe, tiến trình nào
ss -tupn state established      # kết nối đang mở kèm tên tiến trình
```

Mỗi cổng lắng nghe phải ứng với một dịch vụ bạn biết: SSH, web server, mail, panel, database trên `127.0.0.1`. Hãy điều tra cổng bạn không giải thích được, tiến trình lắng nghe chạy bằng user web, hoặc kết nối ra ngoài từ PHP hay tài khoản hosting tới cổng IRC, pool đào coin hoặc tới rất nhiều máy ở cổng 22 (server của bạn đang đi quét máy khác).

## Bước 5: cron job, unit systemd và cơ chế bám trụ khác

Kẻ tấn công muốn quay lại sau khi reboot hoặc sau khi bị dọn. Kiểm tra mọi bộ lập lịch, không chỉ crontab của root:

```bash
for u in $(cut -d: -f1 /etc/passwd); do crontab -l -u "$u" 2>/dev/null | sed "s/^/$u: /"; done
ls -la /etc/cron.d /etc/cron.hourly /etc/cron.daily /var/spool/cron/crontabs 2>/dev/null
systemctl list-timers --all
find /etc/systemd /lib/systemd /usr/lib/systemd ~/.config/systemd -name "*.service" -mtime -14 2>/dev/null
cat /etc/ld.so.preload 2>/dev/null                # bình thường file này không tồn tại
ls -la /etc/rc.local /etc/profile.d/ 2>/dev/null
```

Cron ([T1053.003](https://attack.mitre.org/techniques/T1053/003/)) và dịch vụ systemd ([T1543.002](https://attack.mitre.org/techniques/T1543/002/)) là chỗ quen thuộc. Để ý những dòng tải về rồi chạy (`curl ... | sh`, `wget -q -O-`), chuỗi base64 dài và dịch vụ có tên ngẫu nhiên. `/etc/ld.so.preload` ép một thư viện vào mọi chương trình; hiếm khi được dùng hợp lệ, và sửa nó là kỹ thuật chiếm luồng thực thi đã biết ([T1574.006](https://attack.mitre.org/techniques/T1574/006/)) có thể che file và tiến trình khỏi các công cụ khác.

## Bước 6: file hệ thống bị thay đổi

Trên Debian và Ubuntu, trình quản lý gói biết checksum của mọi file nó đã cài:

```bash
dpkg --verify 2>/dev/null | grep -v " c /"       # file bị đổi, bỏ qua file cấu hình
sudo apt install debsums && sudo debsums -s      # cách khác: chỉ in khi có file khác
find /usr/bin /usr/sbin /bin /sbin -newer /etc/hostname -type f 2>/dev/null | head
```

Trong [kết quả của dpkg](https://manpages.debian.org/bookworm/dpkg/dpkg.1.en.html), ký tự `5` ở cột thứ ba nghĩa là checksum của file không còn khớp với gói. Điều đó bình thường với file cấu hình bạn đã sửa, nên lệnh lọc bỏ các dòng đánh dấu `c`. Nó không bình thường với `/usr/bin/ls`, `/usr/sbin/sshd` hay `ps`. Lệnh `-newer` dựa vào một file mốc không bị đụng tới từ lúc cài; hãy chọn file phù hợp với server của bạn.

Sau đó xem các website, nơi phần lớn vụ xâm nhập bắt đầu:

```bash
find /var/www /home -name "*.php" -mtime -7 -type f 2>/dev/null | head -50
find /var/www /home -path "*uploads*" -name "*.php" -type f 2>/dev/null
grep -rlE "eval\(base64_decode|gzinflate\(base64_decode|assert\(\\\$_(POST|GET|REQUEST)" /var/www /home --include="*.php" 2>/dev/null | head
```

File PHP trong thư mục upload và file PHP mới sửa mà bạn không deploy đều cần xem kỹ. Với việc dọn WordPress, xem bài [dọn website WordPress bị hack](/vi/blog/wordpress-malware-cleanup).

## Bước 7: log, email và công cụ quét rootkit

```bash
journalctl --list-boots | tail                    # có reboot bất thường không?
ls -la /var/log/ | head -40                       # log rỗng hoặc quá nhỏ?
cat /root/.bash_history | tail -50; ls -la /root/.bash_history
postqueue -p 2>/dev/null | tail -5              # hàng đợi mail (Postfix)
```

File history trỏ về `/dev/null`, log dừng đột ngột hay hàng đợi mail có hàng nghìn thư đều là dấu hiệu cần lần theo.

Cuối cùng, chạy công cụ quét rootkit như [chkrootkit](https://www.chkrootkit.org/) hoặc [rkhunter](https://sourceforge.net/projects/rkhunter/). Cả hai đều có trong kho gói của Ubuntu và Debian. Coi kết quả là manh mối, không phải phán quyết: cả hai có thể báo nhầm trên hệ thống hiện đại, và không công cụ nào thấy được rootkit kernel được làm kỹ.

## Nếu phát hiện điều bất thường thì làm gì?

1. **Đừng đánh động kẻ tấn công** bằng cách xoá từng file khi họ vẫn đang kết nối. Chặn mạng ở nhà cung cấp trước, chỉ chừa IP của bạn.
2. **Tìm lối vào** từ các mốc thời gian đã thu thập: lần đăng nhập lạ đầu tiên, webshell đầu tiên, file hệ thống bị sửa đầu tiên.
3. **Xác định phạm vi.** Một webshell chạy bằng một tài khoản hosting, không có dấu hiệu chiếm root, thường dọn được bên trong tài khoản đó. Dấu hiệu bị chiếm root (file hệ thống bị sửa, user UID 0 mới, key hoặc crontab của root bị đổi, có `ld.so.preload`) nghĩa là bạn dựng lại server và khôi phục dữ liệu từ backup trước thời điểm bị xâm nhập.
4. **Đổi mọi thông tin đăng nhập** từng nằm trên server, và vá chỗ đã cho họ vào. Bài [chống brute force SSH](/vi/blog/ssh-brute-force-protection) nói về một lối vào phổ biến của server vẫn cho đăng nhập bằng mật khẩu.

## Kiểm tra trên server ZoPanel

ZoPanel tự động hoá một phần công việc này. [Quét mã độc](/vi/docs/malware-scan) kiểm tra file website của mọi tài khoản hosting vào 03:00 mỗi Chủ nhật và khi bạn bấm quét, tìm webshell, backdoor, PHP bị làm rối, JavaScript bị chèn và file PHP trong thư mục upload, rồi cho bạn cách ly (quarantine) file tìm thấy vào một thư mục chỉ root đọc được. **Security Center**, mô tả trong [tài liệu bảo mật](/vi/docs/security), kiểm tra tài khoản UID 0 thừa, mật khẩu rỗng, dịch vụ mở cổng công khai, thiết lập SSH, bản cập nhật đang chờ và nhật ký hoạt động có chuỗi hash còn nguyên vẹn không. Cảnh báo có thể báo cho bạn khi đăng nhập panel thất bại, có đăng nhập từ IP mới và khi phát hiện mã độc, qua email hoặc Telegram.

Những gì nó không làm: trình quét mã độc chỉ đọc file trong thư mục `domains/` của mỗi tài khoản, tối đa 4 MB, với các đuôi file script đã biết, nên mã PHP giấu trong file `.jpg` hoặc nằm ngoài `domains/` sẽ bị bỏ sót. Nó không phải công cụ phát hiện rootkit và không kiểm tra file hệ thống. Hãy chạy thêm các lệnh ở trên, nhất là sau mỗi cảnh báo. Cách ly tài khoản giúp website bị hack không lây sang chỗ khác; chúng tôi giải thích các lớp trong bài [cách ly tài khoản hosting](/vi/blog/shared-hosting-account-isolation).

## Kết luận

Để kiểm tra VPS bị hack, hãy đi lần lượt qua đăng nhập, tài khoản và key, tiến trình, mạng, cơ chế bám trụ, file bị sửa và log, đồng thời ghi lại những gì tìm thấy kèm thời gian. Các vụ xâm nhập phổ biến thường để lại dấu vết ở ít nhất một trong những chỗ đó. Nếu dấu vết chỉ tới quyền root, hãy dừng dọn dẹp và dựng lại.

Nếu bạn muốn có sẵn quét mã độc hằng tuần, các kiểm tra của Security Center và cảnh báo, hãy thử [gói Free của ZoPanel](/vi/pricing): 3 tài khoản hosting, 10 website, 10 database, không giới hạn thời gian. [Trang tính năng bảo mật](/vi/features/security) liệt kê chi tiết các lớp bảo vệ.

## Câu hỏi thường gặp

### Làm sao biết có người đã truy cập VPS Linux của mình?

Xem `last -F` và log SSH (`journalctl -u ssh` hoặc `/var/log/auth.log`) để tìm lần đăng nhập thành công, rồi so IP và thời gian với của bạn. Kiểm tra cả `authorized_keys` của mọi user, vì kẻ tấn công đã thêm key có thể đăng nhập mà không cần mật khẩu.

### Server bị hack có thể trông hoàn toàn bình thường không?

Có. Kẻ tấn công cẩn thận có root có thể che tiến trình, file và kết nối khỏi các công cụ thông thường, ví dụ bằng rootkit hoặc `/etc/ld.so.preload`. Vì vậy kết quả quét sạch không phải bằng chứng, và server có dấu hiệu bị chiếm root nên được dựng lại.

### Công cụ nào quét mã độc cho server Linux?

Ở cấp server, chkrootkit và rkhunter tìm rootkit đã biết và thay đổi đáng ngờ, còn `dpkg --verify` hoặc `debsums` so file đã cài với checksum của gói. Với file website, hãy dùng trình quét hiểu webshell và PHP bị làm rối, như trình quét có sẵn trong control panel.

### VPS bị hack thì việc đầu tiên cần làm là gì?

Chụp snapshot ở nhà cung cấp, giới hạn truy cập mạng chỉ còn IP của bạn, chưa reboot và chưa xoá file. Sau đó tìm lối vào, xác định root có bị chiếm không, rồi hoặc dọn tài khoản bị ảnh hưởng, hoặc dựng lại server từ backup sạch.

### Vì sao CPU server lên 100% dù không có ai truy cập?

Trên server bị xâm nhập, nguyên nhân hay gặp là trình đào coin, thường chạy từ `/tmp` hoặc `/dev/shm` và được cron job khởi động lại. Chạy `ps aux --sort=-%cpu`, xem tiến trình đứng đầu chạy từ đâu bằng `ls -l /proc/<PID>/exe`, và kiểm tra crontab của mọi user.
