Issue 433: RCE thông qua gii generator
Cài đặt và debug Yii
Quá trình cài đặt và debug Yii framework khá đơn giản. Có thể tải source trên github với basic app để bắt đầu chạy thử. Công cụ mình sử dụng để debug là VsCode. Các bước cấu hình có thể thao khảo ở những bài viết khác.
Có thể tham khảo docx để bắt đầu làm quen với framework.
Mô tả của một bug RCE
Một bug rce được submit thông qua issue của github.
Có thể thấy lỗi do không kiểm soát đầu vào người dùng trong function generateString. Bắt đầu quá trình tìm kiếm thì thấy function được sử dụng trong chức năng generator của gii. Về cơ bản thì chức năng được sử dụng trong môi trường develop nhằm tăng hiệu suất khi developer không cần tạo từng class hay CRUD bằng tay. Trên môi trường product thì tính năng này cần được disable đi vì nó ảnh hưởng đến code đang chạy. Tuy nhiên do sơ suất hoặc lý do nào đó, web được build lên mà vẫn mở tính năng này thì có thể dẫn đến RCE tuy nhiên cần một số điều kiện để có thể exploit thành công.
Sau khi cài đặt xong thì thử sử dụng qua một vài chức năng như theo hướng dẫn guideline. Sau đó thử sử dụng chức năng generate model của gii. Có thể tham khảo tại đây. Theo 1 bài viết trên mạng thì chúng ta để RCE cần tạo model. Vậy web cần có kết nối đến database ngoài ra cần biết table name trong Database. Tuy nhiên gii đã hỗ trợ việc này bằng cách tự gợi ý tên bảng.
Tiếp theo là trong chức năng tạo model cần chọn chức năng enable I18N.
Sơ qua về chức I18N thì sử dụng chức năng này để làm cho trang web đa dạng về ngôn ngữ. Khi đó thì thay vì fix cứng tên biến thì gii khi sinh code cho chúng ta sẽ sử dụng Yii.t()
để translate. Để có thể khai thác được lỗi này cần phải cấu hình để web có tính năng translate. Hay nói cách khác là bật tính năng này lên.
Tiếp tục với công cuộc cấu hình I18n. Sau một hồi vật lộn thì mình đã mở tính năng này lên. Mọi người có thể tham khảo cách cấu hình theo bài viết. Đây cũng là một điểm khá đặc biệt vì nếu không mở tính năng này thì code sẽ bị lỗi và không thể RCE được.
Ngoài ra việc chèn được code tuy nhiên phần thừa ra thì khó có thể làm cho nó thành code để chạy. Tuy nhiên php lại có function __halt_compiler
để dừng quá trình compile ở sau. Từ đây có thể xóa toàn bộ đoạn code thừa đằng sau.
Payload hoàn chỉnh có dạng như sau
app', 'something'),];}}system('curl http://localhost:12134');__halt_compiler();
Sau khi generate code chúng ta có Model có dạng như sau:
Tiếp theo cần chỗ để có thể trigger được lỗi này thì gii cung cấp luôn cho chúng ta chức năng generate CRUD để trigger lỗi.
Sau khi config CRUD như hình dưới thì khi ta ấn preview thì lỗi đã được trigger.
Kết quả là RCE thành công trên server.
Code flow chức năng gây nên lỗi
Bắt đầu với hàm generate trong class Generator.php
,
với mỗi bảng truyền lên hàm sẽ gọi tạo file Model dựa trên một template có sẵn là model.php
$files[] = new CodeFile( Yii::getAlias('@' . str_replace('\\', '/', $this->ns)) . '/' . $modelClassName . '.php', $this->render('model.php', $params)
);
Trong phần template, có thể thấy trong hàm attributeLabels
có gọi đến $generator->generateString($label)
với mỗi thuộc tính của Model.
Hàm generateString
gọi đến của lớp Generator
thì valid thông tin khá sơ sài chỉ kiểm tra messageCategory có null hay không.
Điều này dẫn đến việc RCE thông qua Model.
$str = "Yii::t('" . $this->messageCategory . "', '" . $string . "'" . $ph . ")";
Vá lỗi
Hàm validateMessageCategory
đã được chọn để vá lỗi. Với việc kiểm tra các đầu vào chỉ là các ký tự thì có lẽ đã phòng chống được lỗi.
Kết bài
Điều kiện để có thể RCE:
- yii có bật tính năng develop gii.
- gii version <= 2.2.1
- yii có bật tính năng translate I18n
- Có kết nối đến database.
POC:
app', 'something'),];}}system('curl http://localhost:12134');__halt_compiler();