Skip to content

Baseten lộ token admin GitHub 3 năm trong Docker build history

Karify98 & Amy 🌸·
Cover Image for Baseten lộ token admin GitHub 3 năm trong Docker build history

Một agent bảo mật tự động mất 25 phút để lấy được quyền admin trên các repo GitHub nội bộ của Baseten — công ty inference vừa được định giá 13 tỷ USD. Không cần credential, không cần source code, chỉ cần domain công khai.

Chuyện gì đã xảy ra

Strix, công ty làm agent pentest tự động, đang cân nhắc dùng Baseten làm nhà cung cấp inference cho sản phẩm của họ. Trước khi gửi dữ liệu và mô hình cho bên thứ ba, họ có thói quen tự quét hạ tầng đối tác trước — và lần này họ chạy công cụ của chính mình nhắm vào *.baseten.co, không có credential hay quyền truy cập source.

Agent tìm ra một registry Harbor tại một subdomain ít ai để ý, với một project để public. Từ đó nó pull được image baseten/baseten-app mà không cần đăng nhập. Bên trong image có một cặp AWS key — nhưng key đó đã chết. Agent tiếp tục chạy TruffleHog quét các layer, rồi soi thẳng vào config của image thay vì chỉ file hệ thống, và tìm thấy một GitHub personal access token nằm trong trường history[].created_by — phần ghi lại lệnh build, không phải file thật trong container.

Token đó thuộc về tài khoản basetenbot, có scope repo đầy đủ, và thuộc tổ chức basetenlabs. Nó có quyền admin/push trên repo sản phẩm chính, trên repo GitOps điều khiển cluster production, trên Homebrew tap phân phối CLI, và quyền đọc/ghi trên nhiều repo private khác — bao gồm cả các thư mục đặt tên theo từng khách hàng cụ thể. Image chứa token này được build từ tháng 3/2023. Khi Strix tìm thấy vào tháng 7/2026, token vẫn còn hoạt động — hơn ba năm sau.

Vì sao lỗi này khó thấy bằng mắt thường

Nguyên nhân gốc là một pattern Dockerfile quen thuộc: build cần fetch dependency private từ GitHub, nên ai đó truyền token qua build argument rồi dùng nó để cấu hình git config --global cho phiên build đó.

# conceptual example — not a real tool
ARG GITHUB_TOKEN
RUN GITHUB_TOKEN=${GITHUB_TOKEN} bash -c '\
  if [[ "${GITHUB_TOKEN}" != "" ]]; then \
    git config --global --add \
    url."https://${GITHUB_TOKEN}@github.com/".insteadOf "git@github.com:"; \
  fi'

Vấn đề không nằm ở filesystem cuối cùng của image — dọn sạch file cấu hình sau khi build không giải quyết được gì. Docker ghi lại toàn bộ lệnh RUN vào history của image config, và giá trị build argument bị expand thẳng vào đó. Config này được tải về cùng lúc với image, tách biệt hoàn toàn khỏi các layer filesystem thông thường — nên một lượt scan chỉ soi file trong container sẽ bỏ sót nó hoàn toàn. Docker đã cảnh báo về hành vi này từ lâu, nhưng pattern trên vẫn phổ biến vì nó đơn giản và chạy được ngay.

Điều cần biết

Baseten xử lý sự cố nhanh: xác nhận mức độ nghiêm trọng "critical" trong vòng chưa đầy một ngày, khóa registry và thu hồi token ngay buổi chiều hôm sau. Nhưng bài học không nằm ở tốc độ phản ứng, mà ở việc lỗ hổng tồn tại được ba năm rưỡi mà không ai phát hiện.

  • Build history là bề mặt rò rỉ riêng, tách biệt khỏi filesystem image. Chạy docker history --no-trunc hoặc đọc trực tiếp trường history[].created_by trong config blob — không chỉ scan layer file như thông thường.
  • Fix đúng là dùng BuildKit secret mount, không phải build argument. Secret mount không ghi giá trị vào layer hay history, và không tồn tại sau khi bước build kết thúc.
  • Token cần scope tối thiểu và có hạn dùng. Một token chỉ cần quyền đọc dependency lại được cấp quyền admin/push trên repo sản phẩm và GitOps — đây là lỗi phân quyền, không chỉ lỗi rò rỉ.
  • Xóa Dockerfile không xóa được image đã public. Ai đã pull image cũ vẫn giữ được token trong đó — nên bước bắt buộc sau khi phát hiện là thu hồi token, không phải chỉ sửa Dockerfile cho lần build tiếp theo.
  • Registry riêng cần audit quyền public theo project, định kỳ. Project Harbor bị lộ không phải do cấu hình sai một lần — nó là một góc hạ tầng bị quên, đúng kiểu subdomain "không ai nhớ tới" mà mọi recon tool đều nhắm vào đầu tiên.

Baseten dùng AI security tooling và có đội bảo mật phản ứng nhanh, nhưng token từ 2023 vẫn nằm đó chờ được tìm ra. Câu hỏi đáng đặt ra cho mọi team đang build container image: image cũ nhất trong registry của bạn còn giữ gì trong build history?


Bài viết được hỗ trợ bởi AI (Amy 🌸). Nội dung đã được kiểm duyệt bởi tác giả.

Related Posts