Hướng dẫn bảo mật Gitea trên VPS: Vá lỗ hổng RCE (CVE-2026-60004) khẩn cấp và Hardening Git Server

Tác giả: Trần Thảo 02 tháng 09, 2026

Source code nội bộ bị rò rỉ, VPS ngừng phản hồi vì CPU bị quá tải bởi tiến trình đào coin lạ, hay hệ thống liên tục nhận những request kiểm tra API khả nghi. Đó là thực trạng mà nhiều Sysadmin và Developer đang phải đối mặt khi tự host Git Server nhưng bỏ qua các bước cấu hình an toàn.

Ngày 25/08/2026, Cơ quan An ninh mạng và Cơ sở hạ tầng Hoa Kỳ (CISA) đã chính thức bổ sung lỗ hổng CVE-2026-60004 vào danh mục KEV (Known Exploited Vulnerabilities) và yêu cầu các cơ quan hoàn thành việc vá lỗi khẩn cấp. Việc thiết lập bảo mật Gitea trên VPS lúc này là nhiệm vụ bắt buộc phải làm ngay để bảo vệ hệ thống khỏi các đợt tấn công tự động. Vậy làm thế nào để audit dấu hiệu bị xâm nhập, vá lỗi an toàn và khóa chặt các backdoor tiềm ẩn trước khi sự cố đáng tiếc xảy ra?

Nỗi đau hạ tầng: Khi Gitea mặc định mở cấu hình kết hợp cùng RCE (CVE-2026-60004)

Tự host các dịch vụ quản lý mã nguồn, tương tự như quá trình triển khai cài đặt Gitea trên VPS để xây dựng Git Server riêng với độ bảo mật và hiệu năng cao, mang lại sự chủ động, nhưng đi kèm với đó là trách nhiệm bảo vệ dữ liệu ở mức cao. Sự nguy hiểm của CVE-2026-60004 không chỉ nằm ở bản thân lỗi phần mềm, mà còn do các cấu hình mặc định lỏng lẻo của ứng dụng tạo đòn bẩy cho kẻ tấn công. Lỗ hổng này được đánh giá ở mức Nguy cấp (Critical) với điểm số CVSS v3.1 là 9.8/10.0. Điểm đánh giá này phản ánh khả năng khai thác từ xa qua mạng (AV:N), độ phức tạp thấp (AC:L), không yêu cầu đặc quyền (PR:N) và không cần tương tác người dùng (UI:N).

Bản chất lỗ hổng diffpatch và cách attacker lợi dụng Git Hook

Lỗ hổng CVE-2026-60004 hoạt động dựa trên sự kết hợp giữa lỗ hổng logic của API diffpatch và hành vi xử lý xung đột của Git trong một kho lưu trữ trống (bare repository). Cơ chế khai thác diễn ra qua các bước kỹ thuật sau:

  • Áp dụng bản vá trong thư mục bare: Khi người dùng có quyền ghi gửi yêu cầu tới endpoint POST /api/v1/repos/{owner}/{repo}/diffpatch, Gitea sẽ áp dụng bản vá này bên trong một bản sao tạm thời dạng bare. Việc này được thực hiện bằng lệnh git apply cùng các tham số --index, --recount, --cached, và --binary.
  • Kích hoạt cơ chế Fallback ba chiều (3-Way Fallback): Nếu máy chủ sử dụng Git phiên bản từ 2.32 trở lên, Gitea tự động bổ sung đối số -3 để kích hoạt fallback ba chiều khi áp dụng bản vá.
  • Tạo va chạm Add/Add Collision: Kẻ tấn công gửi cùng một bản vá hai lần liên tiếp để ép Git tạo ra lỗi va chạm xung đột. Cơ chế fallback ba chiều sẽ ghi trực tiếp tệp tin bị xung đột ra đĩa cứng, bỏ qua cờ --cached.
  • Ghi đè Git Hook: Trong thư mục bare, thư mục gốc chính là thư mục Git nội bộ ($GIT_DIR). Nếu kẻ tấn công đặt tên đường dẫn tệp trong bản vá là hooks/post-index-change, file này sẽ được ghi thẳng vào thư mục chứa Git Hook đang hoạt động.
  • Thực thi mã độc (RCE): Khi Git ghi lại vùng đệm index, nó tự động gọi và thực thi hook post-index-change vừa bị chèn vào. Đoạn mã độc sẽ được chạy dưới quyền của tài khoản hệ điều hành đang vận hành Gitea.
Sơ đồ minh họa cơ chế khai thác lỗ hổng RCE CVE-2026-60004 trên Gitea thông qua API diffpatch.

Cơ chế khai thác CVE-2026-60004 kết hợp cùng cấu hình Open Registration tạo ra rủi ro chiếm quyền điều khiển hệ thống.

Open Registration: Sai lầm cấu hình biến Git Server thành mỏ đào coin

Về lý thuyết, lỗ hổng RCE này yêu cầu kẻ tấn công phải có quyền ghi (write access) vào kho lưu trữ. Tuy nhiên, rào cản này hoàn toàn bị phá vỡ nếu Gitea được giữ nguyên cấu hình Open Registration (mở đăng ký tự do) mặc định:

  • Gitea mặc định cấu hình DISABLE_REGISTRATION = falseREGISTER_EMAIL_CONFIRM = false, cho phép bất kỳ ai tạo tài khoản mà không cần xác thực email, quản trị viên phê duyệt hay mã CAPTCHA.
  • Kẻ tấn công sử dụng công cụ tự động để đăng ký tài khoản mới và khởi tạo một kho lưu trữ riêng.
  • Ngay khi kho lưu trữ được tạo ra, tài khoản mới lập tức có toàn quyền ghi vào repo đó.
  • Từ đây, chúng gửi yêu cầu patch độc hại tới API diffpatch để chèn Git Hook và kích hoạt lệnh thực thi hệ thống. Toàn bộ chuỗi hành động kiểm tra, đăng ký tài khoản và kích hoạt RCE này có thể diễn ra tự động chỉ trong khoảng 11 giây.

CISA đánh giá mức độ khai thác của lỗ hổng này là Active (Đang bị khai thác ngoài thực tế) và khả năng tự động hóa là Yes. Sự cố này chứng minh rằng việc kết hợp một lỗ hổng API với cấu hình đăng ký lỏng lẻo sẽ biến máy chủ thành mục tiêu lý tưởng cho các chiến dịch phát tán mã độc.

Kịch bản Audit bảo mật Gitea trên VPS trước khi cập nhật (Threat Hunting)

Trước khi vội vàng chạy lệnh nâng cấp, Sysadmin cần thực hiện quy trình Forensics Triage (Phân tích Pháp y) để xác định xem hệ thống đã từng bị xâm nhập hay cài cắm backdoor từ trước đó hay chưa. Nếu chỉ vá lỗi phần mềm mà bỏ qua các shell ẩn hoặc backdoor, hệ thống của bạn vẫn nằm trong vòng nguy hiểm. Đây cũng là quy trình tiêu chuẩn khi áp dụng các biện pháp bảo mật VPS Linux toàn diện qua 5 lớp phòng thủ nhằm chặn đứng Brute-force và Ransomware.

Rà soát process lạ (Cryptominer) và kiểm tra file Git Hook bất thường

Các chiến dịch tấn công thực tế thường sử dụng lỗ hổng RCE để thả script dropper nhằm tải mã độc đào tiền ảo. Bạn cần rà soát các tiến trình trên máy chủ:

  • Sử dụng lệnh top hoặc htop để kiểm tra tải tài nguyên. Các cuộc tấn công được ghi nhận thường đẩy hiệu suất CPU lên mức trên 70% liên tục.
  • Theo dõi các hành vi đặc trưng của script dropper như xóa bỏ các biến môi trường LD_PRELOADLD_LIBRARY_PATH.
  • Kiểm tra xem dropper có tìm kiếm và tắt (kill) các tiến trình chiếm CPU cao khác trên VPS để độc chiếm tài nguyên hay không.
  • Giám sát tiến trình chạy dịch vụ Gitea xem có sinh ra các tiến trình con (child processes) lạ hoặc mở kết nối mạng không mong muốn ra ngoài Internet.
  • Kiểm tra sự tồn tại của các cron job lạ, systemd service mới, hoặc các khóa SSH bất thường được thêm vào file authorized_keys của hệ thống.
  • Đặc biệt, bạn cần duyệt qua các thư mục lưu trữ repo để tìm kiếm sự xuất hiện của tệp tin thực thi tại đường dẫn hooks/post-index-change. Bất kỳ thay đổi hook nào không nằm trong lịch sử commit nội bộ đều là dấu hiệu bị xâm nhập.

Kiểm tra log Nginx/Gitea tìm dấu hiệu rà soát API và user khả nghi

Lịch sử truy cập (Access Logs) là nguồn dữ liệu quý giá để truy vết dấu hiệu tấn công:

  • API diffpatch: Kiểm tra log Gitea và Nginx để tìm các request bất thường đến POST /api/v1/repos/{owner}/{repo}/diffpatch. Chú ý đến request từ IP lạ, User-Agent không quen thuộc, gửi dồn dập (bursts) hoặc vào khung giờ khuya.
  • Endpoint Markup (CVE-2026-59774): Kiểm tra các yêu cầu POST không xác thực đến /{owner}/{repo}/markup, đặc biệt các request chọn chế độ render Org-mode chứa tham số đường dẫn tuyệt đối hoặc chỉ thị #+INCLUDE đáng ngờ.
  • Giả mạo quyền (CVE-2026-20896): Kiểm tra log Reverse Proxy tìm tiêu đề HTTP X-WEBAUTH-USER gửi từ IP không thuộc danh sách proxy tin tưởng.
  • Hành vi quản trị: Rà soát gitea.log tìm hành động của tài khoản admin xuất phát từ IP lạ, kiểm tra lịch sử Webhook xem có request hướng về mạng nội bộ (như 169.254.x.x, 10.x.x.x) hay không.
  • Đối soát tài khoản: Tìm mối tương quan giữa các request API đáng ngờ với thời điểm các tài khoản mới tự đăng ký hoặc các token truy cập cá nhân (PAT) mới được khởi tạo.
Quy trình audit bảo mật Gitea trên VPS để phát hiện tiến trình đào coin và dấu hiệu xâm nhập.

3 trọng điểm cần rà soát để đảm bảo hiệu quả bảo mật Gitea trên VPS trước khi tiến hành nâng cấp.

4 bước vá lỗi và siết chặt bảo mật Gitea trên VPS Linux

Để chặn đứng khai thác, bản cập nhật hiện hành đã sửa đổi cơ chế clone tạm thời từ dạng bare sang non-bare, ngăn chặn hoàn toàn việc tệp tin xung đột bị checkout ghi đè vào thư mục chứa hook ($GIT_DIR/hooks). Dưới đây là quy trình 4 bước nâng cấp an toàn.

Bước 1: Backup toàn diện bằng công cụ tích hợp và bảo vệ Database

Bản cập nhật Gitea thường đi kèm các thao tác di chuyển cơ sở dữ liệu (database migrations) tự động và không thể đảo ngược. Gitea hiện tại cung cấp sẵn cơ chế backup rút gọn tiện lợi.

Đối với Gitea chạy bằng file Binary:

Bạn chỉ cần thực thi lệnh sau, hệ thống sẽ tự động đóng gói toàn bộ mã nguồn, cấu hình, cơ sở dữ liệu (nếu dùng SQLite) và tệp đính kèm.

Dừng dịch vụ để đảm bảo dữ liệu tĩnh:

sudo systemctl stop gitea

Chạy lệnh dump (Lưu ý: Nếu dùng PostgreSQL/MySQL ngoài, hãy chạy thêm lệnh pg_dump hoặc mysqldump tương ứng để lưu file .sql):

gitea dump --file /backups/gitea-backup-$(date +%Y%m%d).zip

Đối với Gitea chạy bằng Docker:

Tạm dừng container ứng dụng nhưng giữ container database hoạt động.

Dừng Gitea:

docker compose stop server

Dump PostgreSQL:

docker compose exec -T db pg_dump -U gitea gitea | gzip > backups/gitea-db-$(date +%F).sql.gz

Backup volume dữ liệu:

docker run --rm -v gitea_data:/source:ro -v "$PWD/backups:/backup" alpine sh -c 'tar -czf /backup/gitea-data.tar.gz -C /source .'

Lưu ý bảo mật Database (Docker): Tuyệt đối không publish port của PostgreSQL (ví dụ 5432) ra ngoài host VPS. Container database chỉ được phép giao tiếp kín với container Gitea thông qua mạng nội bộ (networks) của Docker. Việc map cổng database ra ngoài host sẽ tạo ra rủi ro bị kiểm tra mật khẩu từ Internet. Khuyến nghị đẩy file backup ra ngoài máy chủ VPS (như lưu trữ đám mây S3) để đề phòng trường hợp xấu.

Bước 2: Nâng cấp Gitea lên phiên bản an toàn

Tiến hành nâng cấp lên phiên bản Gitea 1.27.2 (hoặc các bản cập nhật kế tiếp).

Môi trường Docker Compose:
Chỉnh sửa file compose.yml, cập nhật thẻ image Gitea thành phiên bản an toàn (ví dụ: gitea/gitea:1.27.2).

Kéo image mới:

docker compose pull

Khởi tạo lại container:

docker compose up -d

Môi trường Binary trực tiếp:
Tải file binary phiên bản an toàn từ trang chủ và cấp quyền thực thi:

wget -O gitea-new https://dl.gitea.io/gitea/1.27.2/gitea-1.27.2-linux-amd64
chmod +x gitea-new

Hoán đổi file bằng cách dừng dịch vụ, đổi tên file cũ, đưa file mới vào, chown lại quyền và khởi động lại:

sudo systemctl stop gitea
sudo mv /usr/local/bin/gitea /usr/local/bin/gitea-old
sudo mv gitea-new /usr/local/bin/gitea
sudo chown git:git /usr/local/bin/gitea
sudo systemctl start gitea

Bước 3: Ép Gitea listen tại Localhost và cấu hình ROOT_URL

Việc cấu hình Gitea lắng nghe trực tiếp trên cổng mặc định 3000 ra ngoài Internet bằng HTTP plaintext sẽ gây nguy cơ bị nghe lén (sniffing), tấn công trung gian (MitM), và vô hiệu hóa các lớp phòng thủ bổ sung như WAF, GeoIP hay ACLs. Botnet liên tục rà soát cổng 3000 mở để khai thác RCE tự động.

Giải pháp: Ép Gitea chỉ giao tiếp nội bộ và định tuyến URL chuẩn xác.

  • Chạy Binary: Mở file app.ini, trong block [server], tiến hành đổi tham số HTTP_ADDR = 127.0.0.1. Đồng thời, bổ sung tham số ROOT_URL = https://git.yourdomain.com/. Nếu thiếu ROOT_URL, Gitea sẽ tạo sai các đường dẫn Git clone hiển thị trên giao diện và làm hỏng luồng hoạt động của webhook cũng như callback URL khi chạy sau Nginx proxy. Sau đó restart dịch vụ.
  • Chạy Docker: Giữ nguyên cấu hình listen 0.0.0.0 bên trong container, nhưng sửa file docker-compose.yml để chỉ ánh xạ cổng ra loopback của VPS: ports: - "127.0.0.1:3000:3000". Khai báo biến môi trường tương đương cho ROOT_URL trong file env.

Kiểm tra trạng thái bằng lệnh sau, nếu hiển thị 127.0.0.1:3000 là quá trình khóa port đã hoàn tất:

sudo ss -tlnp | grep 3000

Bước 4: Thiết lập Firewall chặn truy cập port trực tiếp

Hệ thống cần chặn toàn bộ kết nối trực tiếp vào cổng nội bộ. Bạn có thể tham khảo thêm tài liệu hướng dẫn cấu hình UFW trên Ubuntu nhằm làm chủ tường lửa VPS để nắm rõ các lệnh quản trị firewall cơ bản.

Cấu hình UFW (Ubuntu/Debian):

Thiết lập chính sách mặc định từ chối chiều vào, chỉ mở cổng 22 (SSH), 80 (HTTP), 443 (HTTPS) và cổng SSH tùy chỉnh của Gitea (ví dụ 222).

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 222/tcp
sudo ufw enable

Cấu hình Nginx Reverse Proxy làm điểm chặn tĩnh:

Để chặn kẻ tấn công truy cập bằng IP trực tiếp nhằm dò tìm cấu hình server, hãy cấu hình 2 server block Nginx độc lập.

Block mặc định bắt lưu lượng truy cập bằng IP và ngắt kết nối:

server {
    listen 80 default_server;
    listen 443 default_server;
    server_name _;
    ssl_reject_handshake on;
    return 444;
}

Block chính thức định tuyến traffic HTTPS an toàn vào loopback http://127.0.0.1:3000 và cấu hình Header chặt chẽ. Kết quả là các trình duyệt quét IP sẽ bị trả về lỗi ERR_CONNECTION_CLOSED ngay lập tức.

Sơ đồ kiến trúc mạng kết hợp Nginx Reverse Proxy và tường lửa UFW để khóa cổng nội bộ.

Đưa dịch vụ về Localhost và sử dụng Nginx Reverse Proxy là phương pháp phòng thủ giới hạn bề mặt tấn công.

Hardening file app.ini: Chốt chặn an toàn cho Git Server

Ngay cả khi đã có Firewall bảo vệ vòng ngoài, file cấu hình app.ini của ứng dụng vẫn là phòng tuyến quản trị then chốt để khóa các lỗ hổng logic.

Khóa đăng ký tự do và cấu hình Reverse Proxy an toàn

  • DISABLE_REGISTRATION = true: Vô hiệu hóa tính năng tự đăng ký tài khoản mới của người dùng ngoài Internet. Điều này triệt tiêu hoàn toàn con đường tự tạo tài khoản để chiếm quyền ghi khai thác RCE.
  • REQUIRE_SIGNIN_VIEW = true: Bắt buộc người dùng phải đăng nhập mới được xem mã nguồn. Cấu hình này loại bỏ rủi ro bị khai thác lỗi đọc file hệ thống unauthenticated (CVE-2026-59774).
  • INSTALL_LOCK = true: Khóa hoàn toàn giao diện cài đặt web (/install), ngăn không cho bất kỳ ai chạy lại trình cài đặt để thay đổi thông tin kết nối DB.
  • DISABLE_GIT_HOOKS: Gitea đã thiết lập mặc định cấu hình này là true từ phiên bản 1.13.0, qua đó ngăn chặn mọi user (kể cả admin) tự tạo custom Git Hook qua Web GUI. Bạn có thể kiểm tra lại tệp app.ini để đảm bảo tham số này chưa bị sửa đổi thủ công thành false.
  • REVERSE_PROXY_TRUSTED_PROXIES: Kể từ bản cập nhật 1.26.3, tính năng xác thực qua proxy đã chuyển sang cơ chế opt-in (phải chủ động bật mới hoạt động) và loại bỏ hoàn toàn wildcard * mặc định khỏi Docker image. Việc nâng cấp lên các bản cập nhật 1.27.2 trở lên tự động giúp hệ thống miễn nhiễm với lỗi giả mạo admin (CVE-2026-20896). Tuy nhiên, nếu bạn thực sự cần dùng tính năng proxy login, hãy khai báo IP nguồn proxy rõ ràng (ví dụ 127.0.0.1).
  • COOKIE_SECURE = true: Ép session cookie chỉ truyền tải qua kết nối HTTPS.

Xử lý sự cố hậu RCE: Rotate Secret keys, Token và rà soát SSH Keys

Nếu VPS của bạn từng bị khai thác, kẻ tấn công có thể đã đọc được file app.ini thông qua các lỗ hổng như CVE-2026-59774. Việc này đồng nghĩa với việc chúng đã thu thập trọn bộ INTERNAL_TOKEN, các secret của OAuth và JWT signing material lưu trong máy chủ. Lúc này, nâng cấp version mà không xoay vòng (rotate) khóa là vô tác dụng. Hãy tiến hành làm sạch triệt để:

Rotate khóa mã hóa và JWT bằng cách sử dụng các lệnh CLI sau để thay thế các khóa mã hóa trong app.ini bằng chuỗi ngẫu nhiên mới:

gitea generate secret SECRET_KEY
gitea generate secret INTERNAL_TOKEN

Việc đổi SECRET_KEY sẽ vô hiệu hóa toàn bộ cookie hiện tại. Bắt buộc xóa file jwt/private.pem cũ để hệ thống tự sinh key 4096-bit mới, ngăn tin tặc dùng khóa cũ để ký JWT giả mạo.

Đổi mật khẩu DB: Thay đổi mật khẩu kết nối trên PostgreSQL/MySQL (ALTER USER gitea...) và cập nhật lại tham số PASSWD trong section [database].

Thu hồi Token và Deploy Keys: Truy cập SQL và chạy lệnh sau để hủy toàn bộ Personal Access Tokens (PATs):

DELETE FROM access_token;

Quét các bảng public_keydeploy_key tìm khóa SSH lạ, sau đó dùng công cụ Admin Panel để đồng bộ lại file .ssh/authorized_keys.

Rotate External Integrations: Reset Client Secret của các ứng dụng OAuth2, thay mật khẩu SMTP Mailer và đổi khóa bí mật của Webhook.

Reset 2FA: Đổi mật khẩu tài khoản quản trị và yêu cầu người dùng hủy liên kết 2FA cũ để đăng ký lại TOTP mới, vì secret cũ đã bị lộ.

Các bước xoay vòng khóa mã hóa và cấu hình thông số an toàn trong file app.ini của Gitea.

Làm sạch các khóa mã hóa cũ và thu hồi Token là quy trình bắt buộc sau khi hệ thống gặp sự cố rò rỉ dữ liệu.

Đưa Git Server vào vùng an toàn (Mạng nội bộ / VPN)

Mở port Gitea ra Internet dù đã cấu hình Reverse Proxy cẩn thận vẫn tiềm ẩn rủi ro zero-day trong tương lai. Kiến trúc an toàn cho mã nguồn doanh nghiệp là hạn chế lộ diện public hệ thống.

Thay vì public Internet, hãy dùng Tailscale hoặc WireGuard

Nếu Git Server của bạn chỉ phục vụ cho team nội bộ, đừng để tên miền Gitea truy cập được từ bất kỳ đâu trên Internet. Hãy giới hạn truy cập thông qua các giải pháp mạng riêng ảo (VPN) như WireGuard hoặc các nền tảng Zero-Trust network như Tailscale. Bằng cách thiết lập UFW chỉ cho phép lưu lượng HTTPS đến từ dải IP của mạng VPN nội bộ, bạn sẽ biến Git Server của mình thành tàng hình trước các botnet rà soát trên Internet, đảm bảo tính toàn vẹn lâu dài cho tài sản mã nguồn của công ty.

Câu hỏi thường gặp (FAQ)

1. Lỗ hổng CVE-2026-60004 trên Gitea nguy hiểm ở mức độ nào?

Là lỗi RCE điểm CVSS 9.8. Kẻ tấn công có thể lợi dụng tính năng mở đăng ký tự do để tự cấp quyền, chèn Git Hook và thực thi mã độc chiếm quyền điều khiển VPS mà không cần tài khoản nội bộ từ trước.

2. Làm sao để kiểm tra phiên bản Gitea đang chạy trên VPS?

Xem thông tin dưới chân trang web (footer) Gitea, vào Admin Panel > System Status, hoặc chạy lệnh gitea --version (nếu cài binary) và docker exec -it <container_name> gitea --version (nếu dùng Docker).

3. Phiên bản Gitea nào đã khắc phục lỗ hổng RCE này?

Cần nâng cấp lên phiên bản 1.27.1 hoặc cao hơn (khuyến nghị dùng bản 1.27.2 trở lên) để xử lý triệt để cơ chế xử lý Git Hook gây ra lỗi rò rỉ.

4. Tại sao phải tắt Open Registration trên Git server nội bộ?

Để chặn các công cụ tự động (bot) tự tạo tài khoản. Việc này trực tiếp ngăn chặn tin tặc lấy quyền ghi (write access), điều kiện bắt buộc để kích hoạt các kịch bản khai thác mã nguồn.

5. Đổi port Gitea từ 3000 sang port khác có đảm bảo an toàn không?

Không. Đổi port chỉ là biện pháp che giấu bề mặt. Cấu hình bảo vệ thực sự là ép Gitea lắng nghe nội bộ ở 127.0.0.1:3000 và sử dụng Nginx Reverse Proxy kết hợp tường lửa UFW để chặn mọi truy cập IP trực tiếp.

Tài liệu tham khảo