Đánh cắp token GitHub chỉ với 1 cú nhấp thông qua lỗi VSCode
(blog.ammaraskar.com)- github.dev nhận OAuth token được chuyển từ github.com để thực hiện xem tệp, PR và commit trong VSCode trên trình duyệt; token này không bị giới hạn vào một kho lưu trữ cụ thể nên có thể đọc và ghi trên toàn bộ các kho mà người dùng có quyền truy cập
- VSCode webview được cô lập bằng iframe
vscode-webview://..., nhưng để hỗ trợ UX phím tắt,keydowntrong webview được chuyển tới cửa sổ chính qua thông điệpdid-keydown, khiến script không đáng tin cậy có thể gửi sự kiện như thể là thao tác gõ phím của người dùng - Không thể nhập văn bản tùy ý do HTML
<input>, nhưng có thể kết hợp phím tắt mặc địnhCtrl+Shift+A, thông báo cài extension được đề xuất, local workspace extensions và keybinding tùy chỉnh để thực thi lệnh cài extension - PoC chạy JavaScript trong ô markdown của Jupyter notebook để chấp nhận cài extension được đề xuất, sau đó cài extension đã chọn bằng keybinding mới, rồi hiển thị token GitHub API và danh sách kho riêng tư
- VSCode desktop cũng có cùng lỗ hổng, nhưng kẻ tấn công phải dụ nạn nhân clone kho và mở notebook; người dùng github.dev nên xóa dữ liệu trang để hộp thoại xác nhận ban đầu xuất hiện lại như một biện pháp phòng vệ
Tổng quan lỗ hổng
- github.dev mở một VSCode nhẹ chạy trong trình duyệt khi đổi URL kho GitHub có thể truy cập từ
github.comsanggithub.devhoặc nhấp vào mục menu - VSCode trên trình duyệt này có thể xem tệp trong kho, mở cả kho riêng tư, đồng thời gửi PR và tạo commit
- github.com POST OAuth token sang github.dev để tương tác với GitHub thay mặt người dùng, và token này không bị giới hạn trong kho cụ thể mà người dùng đang thao tác
- Kẻ tấn công có thể đánh cắp GitHub token có quyền đọc/ghi chỉ bằng một cú nhấp vào liên kết, bao gồm cả quyền với các kho riêng tư
Cô lập Webview và vấn đề chuyển tiếp phím bấm
- VSCode webviews cô lập việc thực thi JavaScript bằng cách dùng
<iframe>với origin khác với cửa sổ VSCode chính - Đầu ra của Jupyter notebook được render trong
<iframe>có originvscode-webview://..., còn cửa sổ Electron chính dùng originvscode-file://... - Nhờ cơ chế cô lập này, ngay cả khi notebook dùng hiển thị HTML hoặc widget tương tác dựa trên JavaScript, nó vẫn không thể gọi Node.js API của Electron hay VSCode API từ bên trong iframe
- Các tính năng cần cửa sổ chính và webview phối hợp, như Markdown preview, sẽ trao đổi thông điệp qua Window.postMessage() API
- VSCode chuyển tiếp sự kiện
did-keydowntừ webview sang cửa sổ chính để các phím tắt nhưCtrl+Shift+Pvẫn hoạt động ngay cả khi đang focus trong webview - Script không đáng tin cậy bên trong webview có thể tự phát sinh sự kiện
keydownvà giả mạo như người dùng đang nhấn phím
Chuỗi tấn công
- Có thể mở command palette bằng
Ctrl+Shift+P, nhưng vì command palette dùng HTML<input>, cách nhập chuỗi tùy ý sẽ không hiệu quả - Tuy vậy, vẫn có thể tận dụng các thao tác được xử lý qua
keydownnhư phím mũi tên vàEnter, cùng với bộ phím tắt mặc định của VSCode Ctrl+Shift+Alà keybinding mặc định cho “Notifications: Accept Notification Primary Action”, tức bấm nút chính của thông báo VSCode gần nhất- Nếu thêm extension được đề xuất vào
.vscode/extensions.json, VSCode sẽ hiện thông báo cài đặt; tuy nhiên từ VSCode 1.97, hệ thống publisher trust sẽ hiện thêm hộp thoại tin cậy khi cài extension từ publisher mới - Có thể dùng
Tabđể di chuyển giữa các nút, nhưng xử lýEntercủa nút “Trust Publisher & Install” lại gắn vớikeydowncủa chính nút đó, nên rất khó hoàn tất cài đặt chỉ bằng đường này - local workspace extensions cho phép cài trực tiếp extension nằm trong
.vscode/extensionsbên trong workspace đã được tin cậy, và github.dev/web workspace luôn ở trạng thái trusted - Nếu cố chạy trực tiếp local workspace extension, extension worker sẽ mong đợi extension đến từ
vscode-cdn.net, dẫn tới lỗi CSP - Thay vào đó, có thể thêm keybinding tùy chỉnh vào
package.jsoncủa local workspace extension, rồi dùng keybinding đó để gọiworkbench.extensions.installExtensionvới ngữ cảnhskipPublisherTrust
Hoạt động của PoC và tác động
- Cấu hình cần thiết là một kho có Jupyter notebook và local workspace extension
- Ô markdown trong notebook có thể chạy JavaScript thông qua thuộc tính
onerrorcủa ảnh - Payload chờ đến khi VSCode hiện thông báo cài extension được đề xuất, rồi gửi sự kiện
Ctrl+Shift+Ađể chấp nhận hành động chính của thông báo - Sau đó, nó tiếp tục chờ đến khi extension được cài và kích hoạt, đồng thời keybinding tùy chỉnh sẵn sàng, rồi gửi sự kiện
Ctrl+F1để kích hoạt việc cài extension đã chọn - Extension được cài trong PoC sẽ lấy GitHub API token, truy vấn
https://api.github.com/user/reposđể lấy các kho riêng tư mà người dùng có thể truy cập, rồi hiển thị token và danh sách kho trong một hộp thông tin - Sau khi chạy PoC, cần xóa dữ liệu github.dev hoặc gỡ extension PoC; nếu không gỡ, nó sẽ theo người dùng trên mọi trang github.dev
- VSCode desktop cũng bị cùng lỗ hổng, nhưng kẻ tấn công phải dụ nạn nhân clone kho và mở notebook có chứa payload script trong webview
- Nếu webview mà nạn nhân mở còn có một XSS khác, trên desktop điều này thực tế có thể dẫn tới thực thi mã từ xa toàn phần
Phòng vệ và các yếu tố giảm thiểu
- Nếu trước đây chưa từng dùng github.dev, khi vào trang sẽ có một hộp thoại cần nhấp xác nhận, tạo cơ hội thoát khỏi trang tấn công
- Xóa cookie và dữ liệu trang cục bộ của github.dev có thể khiến hộp thoại ban đầu này xuất hiện lại
- Trên Chrome, có thể nhấn biểu tượng ở thanh URL rồi vào Cookies and site data > Manage on-device site data để xóa dữ liệu của các domain liên quan
- Nếu đã vượt qua hộp thoại github.dev trước đó và chưa xóa local storage của trình duyệt, github.dev không có cơ chế bảo vệ như CSRF token, nên bất kỳ liên kết nào trên Internet cũng có thể chuyển hướng sang trang tấn công
- VSCode không chỉ dựa vào cô lập iframe mà còn dùng Content Security Policy nghiêm ngặt và DOMPurify
- Vì Markdown preview trong trang extension dùng
script-src 'none'để chặn thực thi JavaScript tùy ý, nên tác động lớn hơn là RCE 1-click trên desktop chỉ từ một liên kết extension đã bị ngăn chặn
Bối cảnh công bố và mốc thời gian
- MSRC trước đây từng âm thầm sửa các báo cáo lỗi VSCode mà không ghi công và đánh dấu là không có tác động bảo mật
- Báo cáo gần đây về lỗi VSCode XSS của Starlabs cũng bị đánh dấu là không đủ điều kiện và mức độ nghiêm trọng thấp
- Có thể đội VSCode cần thêm thời gian để cân bằng giữa UI/UX và bảo mật, nhưng tác giả chọn công bố toàn bộ vì không nên xem thời gian và công sức của nhà nghiên cứu bảo mật là điều hiển nhiên
- Một giờ trước khi đăng bài vào ngày 2 tháng 6 năm 2026, kế hoạch công bố đã được gửi tới đầu mối liên hệ sẵn có bên bảo mật GitHub
- Lỗ hổng được công khai trong cùng ngày và cũng đã được đăng trên VSCode issue tracker
1 bình luận
Ý kiến trên Hacker News
Đây là một bài tổng hợp hay, và nhìn rộng ra thì điều đáng tiếc là chính việc trình soạn thảo VSCode nhúng trên web lại đăng nhập sẵn vào GitHub
Bỏ qua chuyện có phòng thủ nhiều lớp hay không, chính nguyên tội đó đã tạo ra bề mặt tấn công rất lớn. Nó gần giống như để sẵn một GitHub API token có toàn quyền ở dạng văn bản thuần trên máy trạm để một gói NPM độc hại có thể tìm thấy
Lý tưởng nhất là IDE trên trình duyệt nên chạy bằng phạm vi quyền tạm thời theo từng kho lưu trữ hoặc token chỉ cho phép pull/push với đúng kho đó, và hoàn toàn không có phiên web github.com. Nếu cần toàn bộ giao diện web GitHub thì quay lại github.com, còn github.dev nên là một dịch vụ dành cho một kho lưu trữ duy nhất thì có vẻ hợp lý hơn
Tuy vậy, điều đó sẽ gây bất tiện cho người dùng, khó triển khai, và có lẽ còn vướng những giả định đã ăn sâu trong toàn bộ hệ công cụ github.dev từ lâu
github.dev cũng nên nghiêm túc cân nhắc cách này
[1] https://orca.security/resources/blog/hacking-github-codespac...
Tệ hơn nữa là có vẻ ngay cả các lập trình viên cũng chẳng mấy bận tâm
keydownnàoTrên desktop, có lẽ tốt hơn là để Electron tự chặn trực tiếp và loại bỏ tính năng này; còn trên web thì nên tắt theo mặc định
Tôi cũng không rõ các dịch vụ host Git khác có tính năng tương tự hay không
Thành thật mà nói, các tác nhân LLM cũng nên làm như vậy. Để LLM tự push trực tiếp có vẻ rất liều lĩnh
Lý do cuộc tấn công này đặc biệt rắc rối là vì extension của VSCode chạy với cùng mức độ tin cậy như chính trình soạn thảo, trong khi phần lớn lập trình viên cài hàng chục extension mà không xem xét quyền hạn
Nếu một extension độc hại hoặc bị chiếm quyền âm thầm rò rỉ GitHub token thì rất khó phát hiện nếu không có giám sát mạng, và đó là lý do nên chạy extension trong các profile bị cô lập
Cách tốt nhất là rời khỏi GitHub, chuyển sang GitLab/Forgejo nội bộ tự host và chặn GitHub hoàn toàn
Gần đây tôi cũng gặp chuyện tương tự. GitHub token và Cloudflare token của tôi đã bị đánh cắp
Tôi nghĩ dù có nghiêm túc với bảo mật đến đâu thì nếu đủ thời gian, cuối cùng bạn vẫn sẽ bị dính đòn. Điều tốt nhất là tách biệt mọi thứ và kiểm soát phạm vi thiệt hại
Đừng tin ai, đừng tin thứ gì, hãy dùng OrbStack, và luôn làm việc với giả định rằng token rồi cũng sẽ bị lộ vào một ngày nào đó
Luồng công việc của tôi bị gián đoạn hoàn toàn, nhưng may là bên lấy token có vẻ gần giống bot spam hơn. Chúng tạo ra hàng loạt trang spam giả và cố đào tiền mã hóa
Cảm giác đọng lại lớn nhất là thấy mình bị xâm phạm. Mong mọi người cẩn thận
Đoạn nói rằng khi báo cáo lỗi VSCode cho MSRC thì họ âm thầm sửa luôn là một trải nghiệm tồi tệ đúng kiểu MSRC. Có vẻ họ đã nhận ra rằng các nhà nghiên cứu dù sao cũng sẽ báo miễn phí, nên không thấy lý do gì phải thay đổi
Tôi không biết chi tiết cụ thể của trường hợp này, nhưng trước đây tôi từng vận hành chương trình bug bounty qua Bountysource và HackerOne. Đôi khi báo cáo bị chuyển tới đội phát triển trước cả khi đội bảo mật đánh giá xong
Ở thời điểm đó, lập trình viên có thể âm thầm sửa nó luôn
Đôi khi là vì lo ngại, dù hợp lý hay không, rằng nếu bị gắn với lỗi bảo mật thì sẽ trông không hay cho bản thân hoặc ảnh hưởng tới cơ hội thăng tiến. Kết quả là đến khi đội bảo mật thử tái hiện lại thì lỗ hổng đã biến mất
Từ phía MSRC, họ chỉ thấy rằng các bước tái hiện được cung cấp không còn hoạt động nữa. Họ không nhìn thấy lịch sử lỗi nội bộ hay việc ai đó đã vá trước rồi. Vì vậy báo cáo sẽ bị đóng là không hợp lệ, ngay cả khi phát hiện ban đầu là hoàn toàn chính đáng
Cảm ơn vì đã gần như bỏ công vô điều kiện để cho thấy VS Code cần cải thiện cách phản hồi bảo mật. Bạn hoàn toàn có thể bỏ cuộc, nhưng vẫn tiếp tục giúp đỡ
Tôi không thật sự hiểu vì sao nhiều lập trình viên hơn lại không thử Neovim
Có thể là do sở thích, nhưng tôi thích một cấu hình nhỏ gọn, nơi có thể biết được cái gì đang được cài và cái gì đang chạy. Khi trộn lẫn VSCode, IDE trên trình duyệt, extension, đồng bộ, token và plugin ngẫu nhiên, sẽ rất khó biết thứ gì đang truy cập vào thứ gì
Đây là tính năng của extension Python chính thức từ Microsoft, và ở nhiều mặt khác thì đó gần như là extension dùng được duy nhất, nhưng nó lại cài định nghĩa kiểu cho phiên bản thư viện khác với phiên bản mà dự án của tôi sử dụng. Nó khiến tôi rất bất an vì trông như đang thản nhiên chạy mã bên thứ ba chưa được kiểm chứng, và dường như cũng không thể tắt bằng cấu hình
Tôi muốn nói rằng “từ đó tôi không ngoái đầu nhìn lại”, nhưng thành thật mà nói, trong 1~2 năm qua Neovim bắt đầu thường xuyên làm hỏng cấu hình của tôi gần như sau mỗi lần nâng cấp. Tôi cũng đã linh cảm rằng chuyện này sớm muộn gì sẽ xảy ra. Nói cho chính xác thì đã 10 năm trôi qua nhưng nvim vẫn chưa phát hành bản ổn định đầu tiên, nên không thể trách sự bất ổn đó, nhưng đây là điều đáng ghi nhớ
Tôi đang cân nhắc quay lại Vim thuần. Tôi sẽ mất khá nhiều tính năng tiện lợi, nhưng ít nhất mong là sẽ bớt phải debug các chức năng bị hỏng ngay giữa lúc làm việc
Không cần cài cả đống plugin hay dùng thứ như SpaceVim. Bạn có thể thử xem, biết đâu sẽ thích
Cần thời gian để quen với nvim, nhưng khi đã quen thì nhanh hơn. Dù vậy, điều đó cũng giải thích vì sao nhiều người vẫn ở trong vùng an toàn của mình
Việc công khai tiết lộ là làm đúng. Có quá nhiều người bất mãn với MSRC, và giờ chuyện đó bắt đầu tràn ly giống như vụ Nightmare Eclipse
Nếu những vụ công khai như thế này tích tụ đủ nhiều, có lẽ MSRC sẽ tự nhìn lại và nhận ra chính họ là vấn đề. Có vẻ khả năng đó thấp, nhưng vẫn có thể hy vọng
Dù vậy, tôi nghĩ ít nhất vẫn nên thử gửi, hoặc thông báo cho họ trước vài ngày rồi mới công khai. Không ai biết trước chuyện gì sẽ xảy ra mà
Bài viết rất hay, nhưng phần cuối làm tôi hơi bối rối. Tôi muốn kiểm tra xem mình hiểu đúng không
Tác giả nói rằng vì hệ thống tin cậy nhà phát hành mới, không thể trực tiếp cài extension độc hại chỉ bằng mẹo dùng phím tắt; có thể lách bằng extension workspace cục bộ không có kiểm tra nhà phát hành, nhưng CSP lại chặn điều đó
Có vẻ cách giải quyết là cài một extension workspace cục bộ có gán phím tắt “cài extension mà không cần xác minh nhà phát hành”
Vì vậy tôi muốn hỏi 1) có phải cần hai extension không. Cái đầu tiên là extension cục bộ chỉ để gán phím, còn cái thứ hai là extension độc hại thực sự, thứ mà do CSP nên không cần là local và trên thực tế cũng không thể là local, và 2) CSP chỉ chặn JS của extension local, chứ không chặn
package.jsonhay khả năng thêm phím tắt của nó, đúng khôngĐể thực thi trực tiếp nhất thì bạn có thể thử đưa
my-extension/extension.jsvào, nhưng CSP sẽ chặn. Tuy nhiên đây làscript-srcCSP nên nó chỉ chặn script, còn việc nạppackage.jsonthì vẫn được phép. Vì vậy người ta lợi dụng điều đó để thêm key bindingTình hình MSRC thật sự khó tin
Có thể có tài liệu tốt hơn, nhưng tôi nghĩ video này của The Primeagen là tài liệu nhập môn khá ổn
https://www.youtube.com/watch?v=9kxx5xp5nTQ
Tôi có một góp ý nhỏ với đoạn “Cách duy nhất để cho phép hành vi này là để hai trang web khác nguồn phối hợp với nhau qua API
Window.postMessage()”Cũng có thể giao tiếp bằng cách để iframe hoặc cửa sổ cha thay đổi thuộc tính
location.anchor