Một file PHP xuất hiện trong wp-content/uploads trên server dùng aaPanel là finding cần điều tra ngay, nhưng chưa đủ để kết luận file đã được thực thi hay aaPanel là nguyên nhân. Cần tách bốn câu hỏi: file đến từ đâu, ai có quyền ghi, web server có route file đó sang PHP-FPM không, và runtime user có thể tác động tới đâu.
WordPress mặc định thường dùng WP_CONTENT_DIR/uploads làm upload directory khi không có cấu hình override, nhưng đường dẫn có thể thay đổi qua option hoặc constant. Vì vậy trước khi hunting, cần lấy upload path thực tế thay vì hard-code giả định. WordPress wp_upload_dir().
PHP trong uploads có đồng nghĩa website đã bị RCE không?
Không nhất thiết. Một file .php tồn tại trên disk chứng minh rằng một actor hoặc process đã ghi được file vào vị trí đó. Để chuyển thành PHP execution qua HTTP, web server còn phải định tuyến request tương ứng tới PHP/FastCGI hoặc một handler khác có thể thực thi file.
Với Nginx, PHP thường được chuyển tới FastCGI bằng một location match và fastcgi_pass; tham số SCRIPT_FILENAME xác định script PHP được đưa cho backend. Vì vậy câu hỏi forensic quan trọng là: request tới file trong uploads có match PHP location và tới PHP-FPM hay bị deny/serve như static/404? Nginx FastCGI module.
Vì sao cần phân biệt “file tồn tại” với “file thực thi được”?
Hai trạng thái dẫn tới severity khác nhau:
| Quan sát | Điều chứng minh được | Điều chưa chứng minh |
|---|---|---|
| File PHP nằm trong uploads | Có write path tới uploads | Chưa chứng minh HTTP execution |
| Access log có request tới file PHP | File đã được request | Chưa chắc PHP handler đã chạy |
| PHP/FPM log hoặc response chứng minh script chạy | Execution path tồn tại | Chưa tự chứng minh initial entry point |
| Artifact persistence khác xuất hiện sau đó | Scope có thể đã mở rộng | Chưa chứng minh cùng root cause nếu thiếu timeline |
aaPanel thay đổi bài toán ở điểm nào?
aaPanel là control plane giúp quản lý website, Nginx/Apache, PHP và file. Nó không làm thay đổi nguyên lý của web server: file chỉ thực thi khi configuration và runtime cho phép.
Tài liệu aaPanel về File Manager ghi website files mặc định thường dùng permission 755/644 và owner mặc định là www. Đây là lý do investigator phải kiểm tra ownership và runtime identity thực tế trên server, đặc biệt khi nhiều site cùng tồn tại. aaPanel Files documentation.
Không nên suy luận rằng mọi aaPanel server đều có cấu hình giống nhau. Administrator có thể thay đổi owner, PHP mode, web server, per-site config và permission.
5 con đường khiến PHP có thể xuất hiện trong uploads
1. Vulnerable upload endpoint
Plugin/theme/custom code có thể thiếu validation extension, MIME, filename hoặc authorization, cho phép attacker ghi file ngoài chính sách. Nếu nghi entry point đến từ plugin, đối chiếu thêm các lỗ hổng File Upload WordPress đã bị khai thác với version thực tế và access log.
2. Attacker đã có code execution từ vector khác
Nếu RCE đã xảy ra qua plugin, stolen admin, compromised deployment hoặc server account, attacker có thể chọn uploads vì đây thường là vùng writable. Trong trường hợp này file trong uploads là post-exploitation artifact, không phải entry point.
3. Credential hoặc control panel bị compromise
aaPanel/File Manager, FTP/SFTP, deployment key hoặc hosting credential bị chiếm có thể tạo file trực tiếp mà không đi qua WordPress upload API. Vì vậy không nên chỉ rà WordPress logs.
4. Restore/migration mang file cũ trở lại
Backup hoặc migration archive có thể khôi phục một artifact đã tồn tại trước đó. Timestamp trên filesystem cũng có thể bị ảnh hưởng bởi archive metadata, nên cần correlate với backup provenance.
5. File hợp lệ nhưng đặt sai vị trí
Một developer hoặc plugin tùy biến có thể viết PHP vào khu vực không phù hợp. Đây vẫn là configuration/security smell nhưng không tự động là malware. Cần xác minh provenance và behavior.
Cách kiểm tra uploads có bị route sang PHP-FPM hay không
Trên Nginx, hãy đọc site-specific configuration, không chỉ global nginx.conf. Tìm:
locationmatch file PHP;fastcgi_passvà backend/socket tương ứng;SCRIPT_FILENAMEmapping;- rule deny riêng cho
/wp-content/uploads/; - include file có thể bổ sung hoặc override behavior;
- location ordering/regex khiến rule deny không thực sự thắng.
Trong forum hỗ trợ aaPanel, staff hướng dẫn đặt rule chặn PHP trong uploads vào configuration của đúng website/domain, cho thấy control này cần nằm ở vhost phù hợp chứ không chỉ thêm tùy ý ở nơi khác. aaPanel support discussion.
Không nên test bằng webshell hoặc payload nguy hiểm
Nếu cần xác minh execution boundary, hãy dùng staging hoặc một benign validation method trong phạm vi được phép. Mục tiêu là chứng minh request có/không được chuyển tới PHP handler, không phải mô phỏng malware trên production. Sau validation phải xóa artifact test và lưu kết quả trong change record.
Forensic workflow khi phát hiện file PHP lạ trong uploads
- Preserve evidence: ghi path, size, owner/group, permission, timestamps và hash trước khi sửa/xóa.
- Snapshot nội dung an toàn: lưu bản evidence phù hợp với quy trình IR; không execute file để “xem nó làm gì”.
- Map URL: xác định file có public URL tương ứng hay không.
- Correlate access logs: tìm request tới path, IP, user-agent, status, response size và thời điểm.
- Check PHP/FPM logs: tìm evidence request thực sự tới runtime.
- Find creation path: đối chiếu upload endpoint, admin activity, FTP/SFTP, panel logs, restore job và deployment timeline.
- Expand scope: rà sibling artifacts, admin users, cron, MU-plugin, drop-in và file changes sau thời điểm nghi compromise.
- Fix root cause: patch vulnerable component/credential/configuration trước khi chỉ xóa file.
- Verify execution boundary: xác nhận uploads không còn được route tới PHP khi business requirement không cần.
Ownership trên aaPanel cần đọc như thế nào?
aaPanel documentation cho biết website files thường có owner www theo mặc định. Nếu nhiều website thực tế cùng được process bởi identity có quyền ghi chéo, compromise của một site có thể mở rộng blast radius. Tuy nhiên không thể kết luận chỉ từ documentation; cần kiểm tra UID/GID và permission thật.
PHP-FPM cho phép các pool chạy với Unix user/group khác nhau. Tài liệu PHP ghi rõ user là Unix user của FPM process và mỗi pool có thể có cấu hình riêng. Xem thêm hướng dẫn PHP-FPM Security: cô lập và giới hạn PHP trên Web Server. PHP-FPM pool configuration.
Nếu nhiều site dùng cùng identity, xem thêm Một site bị hack có thể lây sang site khác cùng Unix user không?.
Chặn PHP trong uploads có đủ không?
Không. Đây là một defense-in-depth control rất hữu ích nhưng chỉ giải quyết execution boundary ở một vị trí. Nó không sửa:
- vulnerability cho phép arbitrary upload;
- credential bị lộ;
- RCE ở plugin khác;
- persistence nằm ngoài uploads;
- cross-site write permission;
- control panel compromise.
Nếu attacker vẫn ghi được file nhưng file không chạy qua HTTP, impact có thể giảm mạnh; tuy nhiên write primitive vẫn cần remediation. Với phần hardening upload tổng quát, xem Giới hạn upload file để giảm nguy cơ webshell và mã độc.
Một Nginx deny rule nên được đánh giá thế nào?
Concept đúng là có một location rõ ràng cho uploads và từ chối request PHP ở đó trước khi chúng tới FastCGI. Nhưng không nên copy một snippet vào production rồi coi là xong. Cần:
- backup site config;
- kiểm tra syntax;
- reload có kiểm soát;
- validate đúng hostname/vhost;
- kiểm tra location precedence;
- test các chức năng upload/media hợp lệ;
- xác nhận PHP trong uploads không được thực thi.
Rule bảo vệ phải được đặt đúng site configuration. Nếu nhiều vhost/domain dùng cấu hình khác nhau, validation cần thực hiện theo từng scope.
File PHP có timestamp cũ thì có thể bỏ qua không?
Không. Filesystem timestamp là một artifact, không phải bằng chứng tuyệt đối về thời điểm compromise. Restore/archive có thể giữ hoặc thay đổi thời gian; attacker có quyền đủ cao cũng có thể tác động metadata. Hãy correlate với backup, access logs, file hash, deployment history và account activity.
Khi nào cần mở Incident Response thay vì chỉ hardening?
Chuyển sang incident workflow nếu có một hoặc nhiều dấu hiệu:
- file không có provenance hợp lệ;
- access/PHP logs cho thấy file đã được request/thực thi;
- Administrator, plugin, MU-plugin hoặc cron lạ xuất hiện cùng thời gian;
- nhiều site trên server có artifact tương tự;
- credential/panel login đáng ngờ;
- file tiếp tục tái xuất hiện sau khi xóa.
Trong trường hợp đó, xem Root Cause Analysis sau Security Incident và Webshell qua File Upload và PHP Execution.
Kết luận
Khi phát hiện PHP trong wp-content/uploads trên aaPanel, đừng dừng ở câu hỏi “xóa file nào?”. Hãy xác minh bốn lớp: provenance của file → write path → Nginx/FastCGI execution mapping → PHP-FPM/runtime permission. File tồn tại và file thực thi được là hai finding khác nhau; chỉ khi dựng được timeline và correlation mới nên kết luận entry point, persistence và blast radius.
