Hacker News Nổi bật (buzzing.cc bản dịch tiếng Trung)
92

Tin ngành

Dùng GPT-5.6 tìm ra lỗ hổng WordPress RCE: Môi giới trả 500.000 USD, tác giả chỉ bán 25 USD

(giờ Việt Nam)

Tóm tắt AI

Một nhà nghiên cứu bảo mật đã dùng GPT-5.6 phát hiện lỗ hổng RCE trên WordPress, nhưng lại bán với giá rẻ bất ngờ là 25 USD, trong khi thị trường chợ đen định giá tới 500.000 USD. Sự việc làm dấy lên tranh luận về năng lực của AI trong bảo mật và sự chênh lệch giá trị thực tế.

Bản dịch AI

Exploit brokers pay $500,000 for a WordPress RCE. I found one with GPT5.6 Sol Ultra and $25 › Searchlight Cyber

Nếu bạn đang chạy WordPress và muốn kiểm tra xem phiên bản của mình có lỗ hổng hay không, bạn có thể sử dụng công cụ mà chúng tôi đã lưu trữ tại đây: https://wp2shell.com/.

Chúng tôi đã trì hoãn việc công bố vấn đề này để tạo cơ hội cho những người bảo vệ nâng cấp các phiên bản WordPress của họ trong suốt cuối tuần, nhưng trong thời gian đó, Calif và Hacktron đã có thể độc lập tái tạo toàn bộ chuỗi khai thác trước khi các PoC khác xuất hiện trên GitHub.

Câu chuyện về wp2shell

Giống như hầu hết các nhà nghiên cứu bảo mật, chúng tôi theo dõi rất sát sao các mô hình mới được phát hành tại Searchlight Cyber. Khi GPT5.6 Sol Ultra được ra mắt, chúng tôi đã rất hào hứng để thử nghiệm nó.

Tôi đã đọc được rằng Sol gần đây đã giải được một phỏng đoán toán học nổi tiếng có tên là phỏng đoán Cycle Double Cover. Với tư cách là một nhà nghiên cứu bảo mật, điều thú vị đối với tôi là OpenAI đã công khai câu lệnh (prompt) mà họ sử dụng, được đăng tại đây. Tôi chưa thấy bất kỳ ai trong cộng đồng bảo mật thảo luận hay đề cập đến nó, nhưng tôi nghĩ rằng nếu câu lệnh này đủ tốt để giải quyết các bài toán khó, thì nó có lẽ cũng đủ tốt cho nghiên cứu bảo mật. Tôi đã lấy câu lệnh đó, điều chỉnh lại, sau đó hướng nó vào WordPress và yêu cầu nó sử dụng 4 tác nhân (agents) trong ít nhất 6 giờ. Đây là câu lệnh chính xác mà tôi đã sử dụng:

Cấu trúc thư mục mà tôi đã sử dụng như sau:

Trước khi bắt đầu, tôi đã sao chép (clone) phiên bản WordPress ổn định mới nhất vào thư mục main/ và xóa thư mục.git. Tôi làm điều này vì tôi thường thấy rằng các LLM hay tìm kiếm lịch sử thay đổi hoặc internet để lấy gợi ý khi thực hiện nghiên cứu bảo mật, và đối với việc khám phá lỗ hổng mới, cá nhân tôi nghĩ đây là sự lãng phí token. Đây cũng là lý do tại sao tôi thêm dòng này:

Kinh nghiệm của tôi cũng cho thấy rằng các mô hình đôi khi sẽ ‘gian lận’ để đạt được những gì bạn yêu cầu, bằng cách chọn các tùy chọn cấu hình cực kỳ khó xảy ra hoặc bằng cách tạo ra các điều kiện tiên quyết không thể đạt được bởi một kẻ tấn công. Đây là lý do tại sao tôi rất rõ ràng rằng nó phải là RCE trước xác thực (pre-authentication) trong một môi trường triển khai thực tế điển hình với MySQL.

Cuối cùng, tôi nhận thấy rằng các mô hình không thực sự ‘đi sâu vào chi tiết’ với các thư viện nền tảng nếu cần thiết. Bản năng đầu tiên của chúng là tìm kiếm thứ gì đó về một API hoặc hàm PHP nếu chúng không biết. Nhưng các mô hình thực sự rất giỏi trong việc đọc mã nguồn, vì vậy tôi chỉ yêu cầu chúng đọc mã nguồn:

Phần còn lại của câu lệnh được lấy gần như toàn bộ từ câu lệnh CDC của OpenAI.

Khi quay lại, tôi thấy trong kết quả chạy của nó rằng nó tuyên bố đã phát hiện ra một lỗ hổng SQL injection trước xác thực. Ban đầu tôi không tin lắm, vì WordPress là một trong những mục tiêu được bảo mật kỹ lưỡng nhất mọi thời đại, và nó cũng không có bất kỳ lỗ hổng pre-auth đáng kể nào trong thập kỷ này. Nhưng khi hiểu những gì nó đã làm, tôi nhận ra rằng nó thực sự đã phát hiện ra một lỗ hổng SQLi hoàn toàn trước xác thực. Vẫn chưa hoàn toàn tin tưởng, tôi đã cài đặt một phiên bản WordPress gốc trên một máy chủ từ xa và yêu cầu nó đánh cắp email của quản trị viên. Trong vòng vài phút, nó đã in ra địa chỉ email mà tôi đã dùng để thiết lập phiên bản đó.

Từ đó, tôi hỏi Sol liệu điều này có thể leo thang thành RCE hay không. Khoảng 4 giờ sau, Sol trả lời khẳng định: lỗ hổng SQLi chỉ đọc (read-only) trước xác thực có thể được sử dụng một cách đáng tin cậy để leo thang đặc quyền lên quản trị viên mà không cần phải bẻ khóa bất kỳ mật khẩu nào hay thực hiện bất kỳ tính toán ngoại tuyến nào.

Tổng mức sử dụng: 50% hạn mức hàng tuần. Chi phí tính theo tỷ lệ trên gói đăng ký 200 đô la: ~ 25 USD.

Tại thời điểm này, tôi chợt nhận ra rằng mình đang nắm giữ một mã khai thác trong cấu hình mặc định của một trong những phần mềm phổ biến nhất thế giới. Các ước tính có thể khác nhau, nhưng hầu hết đều đồng ý rằng có hơn 500 triệu phiên bản WordPress đang chạy trên toàn thế giới.

Tôi đã dành cả ngày hôm sau để gỡ rối những gì Sol đã làm và chuẩn bị một báo cáo để gửi cho WordPress. Trong khi lỗ hổng SQLi khá dễ hiểu, thì công việc hậu khai thác (post-exploitation) mà Sol thực hiện để leo thang lên RCE hoàn toàn vô lý. Có thể Sol chỉ mất 4 giờ để viết, nhưng tôi chắc chắn đã mất nhiều, rất nhiều thời gian hơn để hiểu nó. Sau đây là mô tả (của con người) về mã khai thác của tôi: lỗi ban đầu, SQLi và chuỗi hậu khai thác được sử dụng để leo thang lên RCE.

Lỗi (The Bug)

API batch của WordPress được giới thiệu trong WordPress 5.6 vào năm 2020 và cho phép người dùng thực hiện nhiều yêu cầu API ảo trong một yêu cầu duy nhất. Bạn có thể truy cập endpoint này bất kể bạn đã xác thực hay chưa, nhưng mỗi yêu cầu con đều được chuyển thông tin xác thực đó. Một ví dụ đơn giản về lý do tại sao bạn muốn sử dụng tính năng này là để cập nhật tiêu đề hoặc thẻ của nhiều bài đăng blog cùng một lúc; đây là một ví dụ đơn giản:

Nếu bạn truy cập trực tiếp vào một endpoint, chẳng hạn như POST /wp-json/wp/v2/posts, WordPress có một quy trình xác thực (validation pipeline) trông đại khái như sau:

Điều này có nghĩa là bất kỳ tham số nào truyền vào callback của endpoint đều đã được xác thực là đúng định dạng và kiểu dữ liệu. Bản thân các API endpoint dựa vào việc xác thực này ở một số nơi để đảm bảo rằng, ví dụ, ID bài đăng là một số nguyên, tiêu đề bài đăng là một chuỗi, v.v. Do đó, việc có thể vượt qua quy trình làm sạch tham số (parameter sanitization) là một vấn đề lớn.

API batch thực hiện mọi thứ hơi khác một chút. Thay vì chạy quy trình bốn bước ở trên theo trình tự như bạn mong đợi, nó gộp việc xác thực và thực thi thành hai vòng lặp, như sau:

Điều này được triển khai trong class-wp-rest-server.php bằng cách có hai mảng, một cho các kết quả khớp ($matches) và một cho xác thực ($validation). Ý định là mỗi chỉ số $i trong mảng matches tương ứng với cùng chỉ số đó trong mảng validation. Vì vậy, $validation[0] chứa xác thực cho $matches[0], $validation[1] chứa xác thực cho $matches[1], v.v. Quy trình xác thực hoạt động như sau:

Bạn có phát hiện ra lỗ hổng ở đây không? Nếu chúng ta chọn nhánh is_wp_error( $single_request), mảng $validation được cập nhật, nhưng vì có lệnh continue;, mảng matches không được cập nhật! Giả sử yêu cầu đầu tiên bị lỗi định dạng. Các yêu cầu ban đầu và kết quả xác thực của chúng vẫn khớp nhau, nhưng mọi mục trong $matches bị dịch chuyển lùi lại một vị trí. Vòng lặp thực thi thứ hai bỏ qua lỗi ở chỉ số 0. Tại chỉ số 1, nó sử dụng yêu cầu gốc ở chỉ số 1 và kết quả xác thực của yêu cầu đó, nhưng $matches[1] bây giờ lại chứa trình xử lý (handler) khớp với yêu cầu gốc ở chỉ số 2. $matches[0] không bao giờ được sử dụng. Điều này cho phép chúng ta xác thực một yêu cầu và sau đó thực thi nó bằng trình xử lý endpoint của yêu cầu tiếp theo.

Sử dụng điều này, chúng ta có thể vượt qua tất cả các bước làm sạch trên mọi endpoint hỗ trợ batch bằng cách khớp mọi endpoint với xác thực của một endpoint khác không làm sạch các tham số tương tự. Nhưng chúng ta có thể sử dụng điều này ở đâu?

Điểm đến (The Sink)

Tuyến đường GET /wp/v2/posts cho phép người dùng liệt kê các bài đăng đáp ứng các tiêu chí nhất định. API cung cấp chức năng loại trừ các ID tác giả khỏi kết quả:

Có một lỗi nghiêm trọng ở đây. Nếu đầu vào cho author__not_in là một mảng, nó sẽ lọc từng mục bằng absint, làm sạch nó thành một số nguyên. Nhưng nếu đầu vào là một giá trị vô hướng (scalar), nó sẽ để nguyên không thay đổi. Do đó, việc cung cấp một chuỗi vô hướng như "foobar" sẽ được nội suy trực tiếp vào truy vấn SQL thô mà không cần thoát ký tự (escaping).

Điều này thường không phải là vấn đề khi gọi tuyến đường này trực tiếp, vì tham số công khai author_exclude phải là một mảng các số nguyên. Bộ điều khiển bài đăng chỉ ánh xạ nó tới author__not_in sau khi xác thực. Tuy nhiên, thông qua API batch, chúng ta có sự lệch pha giữa xác thực/thực thi, cho phép chúng ta né tránh việc xác thực tham số một cách gọn gàng. Hãy thử xem:

Ở đây chúng ta áp dụng những gì đã có. Đầu tiên, chúng ta có một đường dẫn không hợp lệ trong một yêu cầu để làm lệch pha (desync) các đường dẫn và xác thực nội dung. Tham số author_exclude được xác thực dựa trên tuyến đường DELETE /wp/v2/posts/1, vốn không thực hiện xác thực trên nó (nó không nhận ra tham số này), nhưng sau đó, do sự lệch pha của chúng ta, author_exclude được áp dụng cho GET /wp/v2/posts thay thế. Chỉ có một vấn đề:

API batch không hỗ trợ các yêu cầu GET. Lỗ hổng SQLi mà chúng ta tìm thấy chỉ có thể truy cập được qua GET, vì vậy có vẻ như chúng ta đã đi vào ngõ cụt. Giải pháp của Sol cho vấn đề này rất thông minh và khá mang tính hướng dẫn. Mô hình nhận ra rằng việc xác thực các phương thức yêu cầu được triển khai như chính việc xác thực tham số. Chúng ta có cách nào để vượt qua xác thực tham số không? Có! Chúng ta có thể sử dụng lỗi lệch pha! Do đó, Sol xây dựng một payload trong đó nó gọi đệ quy endpoint batch. Trong lệnh gọi bên trong, do sự lệch pha, phương thức yêu cầu không được xác thực. Sau đó, chúng ta có thể thực hiện yêu cầu GET của mình. Payload cuối cùng cho một lỗ hổng SQLi trước xác thực chỉ đơn giản là:

Ở đây chúng ta khai thác lỗi xác thực của API batch hai lần, một cách đệ quy. Trong yêu cầu bên ngoài, chúng ta làm lệch pha để trường phương thức không được xác thực. Và sau đó trong yêu cầu bên trong, chúng ta lại làm lệch pha để trường author_exclude không được xác thực. Payload 0) OR 1=1 -- sẽ trả về tất cả các hàng bài đăng, xác nhận rằng việc tiêm mã (injection) hoạt động. Từ đây, một cuộc tấn công dựa trên UNION có thể làm rò rỉ các giá trị cơ sở dữ liệu tùy ý bằng cách trả về chúng trong một hàng có cấu trúc wp_posts đầy đủ.

Tại thời điểm này, chúng tôi đã có một lỗ hổng SQLi pre-auth trong WordPress, vốn đã là một vấn đề rất lớn. Tuy nhiên, cảm thấy được tiếp sức bởi sức mạnh của LLM, tôi đã hỏi nó liệu nó có thể leo thang điều này thành một cuộc tấn công RCE toàn diện hay không.

Bản năng đầu tiên của tôi là làm rò rỉ những thứ như mật khẩu, mã đặt lại hoặc khóa API để leo thang đặc quyền. Tuy nhiên, WordPress có một mô hình bảo mật khá mạnh mẽ và tất cả những thứ này đều được băm (hashed) trong cơ sở dữ liệu. Trừ khi mật khẩu của quản trị viên đặc biệt yếu và có thể bẻ khóa vì lý do nào đó, việc làm rò rỉ cơ sở dữ liệu là không đủ để chiếm quyền kiểm soát tài khoản quản trị viên. Tuy nhiên, Sol đã nhanh chóng xác định một hướng đi đầy hứa hẹn khác:

Bộ nhớ đệm (The Cache)

WordPress có hiệu suất rất cao. Là một phần của điều này, WordPress duy trì một bộ nhớ đệm (in-memory cache) các đối tượng WP_Post được nhìn thấy trong suốt vòng đời của yêu cầu. Điều này không được lưu trữ ở bất kỳ đâu; khi yêu cầu kết thúc, các đối tượng được lưu trong bộ nhớ đệm này sẽ bị loại bỏ. Tuy nhiên, nếu cùng một bài đăng được tham chiếu nhiều lần trong suốt vòng đời yêu cầu, WordPress sẽ sử dụng bài đăng trong bộ nhớ đệm nếu nó tồn tại sau khi tìm nạp từ cơ sở dữ liệu lần đầu tiên. Điều này tránh được nhiều lần truy vấn cơ sở dữ liệu nếu cùng một bài đăng được sử dụng nhiều lần trong cùng một yêu cầu. Ví dụ, một người dùng có thể truy cập một bài đăng có ID 10, nhưng đồng thời, một widget thanh bên liệt kê 5 bài đăng gần đây nhất, bao gồm cả bài đăng ID 10, v.v.

Vì chúng ta có một lỗ hổng SQLi trong endpoint bài đăng, chúng ta có thể sử dụng một cuộc tấn công dựa trên UNION để ‘làm giả’ các bài đăng được trả về. Vì các bài đăng này sẽ được lưu vào bộ nhớ đệm, chúng ta có quyền kiểm soát phi thường đối với dữ liệu được lưu trong bộ nhớ đệm về bài đăng đó. Ngoài ra, WordPress thực hiện xử lý hậu kỳ văn bản bài viết trước khi hiển thị nó, ngay cả thông qua API, và chúng ta kiểm soát toàn bộ văn bản bài viết đang được trả về.

Mặc dù chúng ta có thể làm nhiễm độc bộ nhớ đệm yêu cầu, nhưng không rõ ràng lắm chúng ta có thể làm gì với nguyên thủy (primitive) này. Rốt cuộc, các bài đăng giả mà chúng ta trả về không phải là các bài đăng thực được hỗ trợ bởi cơ sở dữ liệu, điều này hạn chế tác động giữa các yêu cầu. Có vẻ như chúng ta không thể biến những bài đăng giả này thành các hàng cơ sở dữ liệu thực – ngoại trừ…

Nhúng (The Embed)

WordPress có một tính năng gọi là embeds. Bằng cách bao gồm một cấu trúc như [embed]https://example.com[/embed] trong bài viết của bạn, với điều kiện nội dung của trang từ xa ở định dạng mà WordPress hỗ trợ, nội dung đó sẽ được nhúng vào bài viết của bạn.

Để tránh việc thực hiện yêu cầu HTTP mỗi khi bài viết được tải, WordPress lưu trữ các nội dung nhúng này không chỉ trong bộ nhớ mà còn ở cấp độ cơ sở dữ liệu. Điều này có dạng một bài đăng thuộc loại oembed_cache được lưu trữ trong bảng wp_posts của cơ sở dữ liệu.

AIBảo mậtWordPressGPT-5.6Lỗ hổng
Đọc bài gốc

Bài viết được AI dịch và tổng hợp tự động từ Hacker News Nổi bật (buzzing.cc bản dịch tiếng Trung). Liên kết bài gốc ở phía trên. Dữ liệu đồng bộ qua API công khai được ghi nguồn tại AI HOT (canonical) ↗. AIHOT.vn luôn dẫn nguồn đầy đủ — nếu bạn thấy điểm cần chỉnh sửa, hãy gửi ý kiến tại trang phản hồi.