# Phân tích lổ hổng PHP Upload file Vulnerable. (Timf hieu ve apache cofig.) ## 1-Tổng quan :::info ### Sơ lượt về khái niệm: File upload vulnerability - Lỗ hỏng tải file lên, xảy ra trong việc xác thực khi tải file lên hệ thống, tạo điều kiện để các hacker thâm nhập vào bên trong hệ thống và thực hiện các cuộc tấn công kiểm soát máy chủ,... ::: Thường thì lỗ hỏng này được khai thác bằng cách sử dụng các ngôn ngữ phía server([Node.js](https://viblo.asia/p/securing-file-uploads-in-nodejs-express-a-comprehensive-guide-aAY4qvkyJPw) , [ASP . NET](https://www.linkedin.com/pulse/exploring-common-security-vulnerabilities-unpacking-file-singh/),...). Mục đích là thực thi mã độc cho phép thâm nhập vào bên trong server, tạo thành một webshell *(một hình thức của backdoor, ở đây là một chương trình được sử dụng để xâm nhập và khi thành công, hacker sẽ dựa vào đó để có được quyền điều khiển hệ thống website.)*, tự do khai thác thông qua lỗ hỏng này. Ví dụ về PHP "rất" dễ bị phá ```typescript:php <!DOCTYPE html> <html> <head> <title>Tải file lên</title> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/water.css@2/out/water.css"> </head> <body> <form action="dangbai.php" method="post" enctype="multipart/form-data"> <input type="file" name="taifilelen" value=""> <button type="submit" name="xacnhan">Xác nhận</button> </form> <?php $target_dir = "luufile/"; $target_file = $target_dir . $_FILES["taifilelen"]["name"]; if (move_uploaded_file($_FILES["taifilelen"]["tmp_name"], $target_file)) { die("tải lên thành công"); } else { die("Tải lên không thành công"); } ?> </body> </html> ``` ![image](https://hackmd.io/_uploads/SJS16fgN0.png) Có thể dễ dàng thấy ở trong đoạn code trên,ta đã không thêm vào bất kì một bước xác thực về định dạng file cho phép up lên, giới hạn dữ liệu,... dẫn tới việc Hacker tấn công dễ dàng ### Nguyên Nhân + Hệ quả File upload vulnerability - Xảy ra chủ yếu là do sự sơ sài trong cách xây dựng hệ thống xác thực của website khi cho phép đăng file lên hệ thống web, hoặc do thiếu sự nhất quán trong xây dựng cấu trúc thư mục, hoặc do sự cao tay trong cách xâm nhập vào hệ thống của các hacker. Hệ quả là khi hacker đã xây dựng thành công backdoor thì rất khó để khắc phục hậu quả này. ### Cách phòng thủ Để đảm bảo nhất, ta nên thường xuyên kiểm tra thông tin, đặt biệt lầ các file upload. Sử dựng whitelist, hoặc tăng cường trong xác thực file upload để hạn chế bypass của các hacker.... ## 2-Sử dụng RCE - Remote Code Execution để tấn công.(Challenges file upload+file upload workshop) ::: info ### A-Một số thông tin cần nắm: * 1 - Trong PHP, có một công cụ có sẵn trong hệ thống là PHP shell, cung cấp cho ta một môi trường thực thi các lệnh hệ thống (Tương tự như SSH hoặc Tenet). Và trong ngôn ngữ PHP, cũng có các hàm dùng để thực thi các lệnh hệ thống đó. Ngoài PHP thì các ngôn ngữ khác như python, ruby,... cũng có các lệnh/hàm có chức năng tương tự. Thường được dùng để thực hiện các truy cập hệ thống từ xa qua trình duyệt web. ![image](https://hackmd.io/_uploads/Sko0YTtEA.png) ![image](https://hackmd.io/_uploads/HkWRt6KER.png) * 2 - Khi ta đăng tài các tệp `.php`, máy chủ xác định và gửi qua trình thông dịch PHP (PHP interpreter), ở đó các file mang phần mở rộng được dịch mã và thực thi. ::: ### B-BYPASS để tấn công bằng RCE. ::: info Kỹ thuật thực thi mã từ xa, bằng cách tải lên hệ thống một đoạn mã scrpit hoặc shell thông qua lỗ hỏng, ở đây là PHP Upload file Vulnerable. Bằng cách này, kẻ xấu có thể chiếm lấy/phá hoại hệ thống,... ::: ::: warning #### 1 - File upload - Double extensions ::: Trong bài này, gợi ý ban đầu là "double extension", tức là file chứa hai phần mở rộng (là kết hợp của hai đuôi như php.png, docx.zip,... nhưng định dạng theo đuôi sau). Thường thì chỉ có các kẻ xấu mới đặt tên như vậy để bypass qua hệ thống kiểm tra trước khi upload file. ![image](https://hackmd.io/_uploads/H1lByCFNR.png) ![image](https://hackmd.io/_uploads/rkuhJAtNR.png) Gợi ý thứ hai bằng cách upload file `.php`, hãy truy cập vào file có tên là `.passwd` Sử dụng hàm `exec();` để thực thi lệnh `cat` đọc mật mã trong đó, và `echo` để in ra, ta thu được mật khẩu ở level này. ![image](https://hackmd.io/_uploads/By7_zCtN0.png) ![image](https://hackmd.io/_uploads/H1mxmRF4R.png) PASS : `Gg9LRz-hWSxqqUKd77-_q-6G8` :::success Ta để ý thấy rằng dù định dạng file ở bài trên là đuôi `.png` nhưng kết quả hiện ra là kết quả sau khi thực hiện lệnh của `.php` ta nhúng. Đây là do máy chủ có thể có một số công cụ hiểu thực thi phần mở rộng `.php` của file chứa nó. ![image](https://hackmd.io/_uploads/rk1dzgq4R.png) Thường được sử dụng "file inclusion attack",xảy ra do trong file code có hàm "include, require,..., dẫn tới trong đoạn mã URL xuất hiện `tên_file.php?page=tên_file_#`. Khi đã biết được địa chỉ file cần tìm, ta chỉ cần dùng biến tham chiếu`../` để đi vào các thư mục chứa tệp cần tìm, dùng các phương pháp tương tự như double extension để bypass. ::: ::: warning #### 2 - File upload - MIME type (Multipurpose Internet Mail Extensions) ::: :::info *Lưu ý nhỏ* MIME type, là một tiêu chuẩn để phân loại tập tin trên không gian mạng, được dùng để xử lý chính xác loại dữ liệu được gửi tới. Cấu tạo của nó gồm hai phần là type chính và type phụ, ngăn cách bởi một dấu gạch chéo. Ví dụ như : ![image](https://hackmd.io/_uploads/ryvkvnq40.png) ::: Dựa vào gợi ý thì ta biết đề lần này giống để làm trước, làm theo cách tương tự, nhưng kết quả không hề thực thi code `.php` nhúng. Lý do là vì do MIME type mà file được xử lý duy nhất theo type `image/png`, ngăn cho file `.PHP` được nhúng không được thực thi, nhưng vẫn đọc được. ![image](https://hackmd.io/_uploads/rk7gu25EA.png) Thử mở burp và nhìn thấy, web vẫn đọc được file `.php` nhúng bên trong, nhưng không chạy được do content-type: được xác định và định dạng theo file ảnh và xử lý theo file ảnh: ![image](https://hackmd.io/_uploads/B14qdhcN0.png) Lúc này ta chỉ cần đổi xóa đuôi `.png` và chạy, thành công upload file `.php` để đọc pass. Vì ta đã qua bước xác thực ban đầu và đang ở bước upload file nên lúc này khi ta đổi tên file thì sẽ không bị xác thực lại lần hai nữa, nên ta có thể xóa `.png` để gửi lên file thuần `php` và file sẽ đc thực thi. ![image](https://hackmd.io/_uploads/Hk6bth9VR.png) PASS : a7n4nizpgQgnPERy89uanf6T4 :::warning ### 3 - File upload - Null byte ::: Ở đề bài lần này, ta cần tìm cách gửi file `.php` lên. Đi thử sài cách ở bài trước, kết quả trả về là lỗi hệ thống 404 và dòng cảnh báo sai tên file: ![image](https://hackmd.io/_uploads/BkVvmp9VR.png) Có thể giải thích là vì file xác thực được code `.php` ẩn trong file `.png`. Tìm hiểu về từ khóa, ta biết được sử dụng null byte trong URl`00%` khi encode thì nó sẽ tượng trưng cho một khoảng trống(NUL). Khi ta đặt tên như vậy thì web sẽ thực thi file `.php` và qua bước xác thực, gửi lên và ta không thấy báo lỗi nữa. ![image](https://hackmd.io/_uploads/rkfL2TqN0.png) Nhưng bấm vào link thì nó hiện lỗi 400, vì mục đích của ta ban đầu là gửi file `.php`, nên ta sẽ xóa trên thanh địa chỉ, chừa lại đuôi `.php` và ta thành công vào được bên trong. ![image](https://hackmd.io/_uploads/Bkxn3p9EA.png) PASS : YPNchi2NmTwygr2dgCCF :::success Ngay từ lúc ta upload ảnh, file đã được xử lý, tức là nó sẽ nhận diện luôn cả file `.php` nhúng bên trong. Trong URL-encode sang ASCII,`%20`(dấu cách) và `%00` , *dùng trong là Null byte injection*, là một khoảng trống. Khác với dấu cách, khoảng trống tức là không tồn tại bất cứ ký tự nào bên trong, còn dấu cách được xem như một ký tự tàng hình. Dẫn đến nếu ở bài trên ta sài `%20` thì tên file được dịch thành `bypass.php .png`, không khác lắm với `bypass.php.png` và sẽ không thể mở được. ![image](https://hackmd.io/_uploads/Sy-jRT9NA.png) Còn ta dùng `%00`, thì lúc này hệ thống vẫn cho tải lên vì có đuôi `.png`, nhưng khi đọc file thì hệ thống chỉ đọc tới php, khi đó file sẽ xử lý file .php thay vì .php.png, và đạt được yêu cầu. ::: ::: warning #### 4 - File upload - ZIP ::: Đọc vào để, ta có hai gợi ý cần phải làm rõ là đọc file index.php và mục đích sử dụng lệnh zip trong linux. Bằng cách thử upload các file, ta phát hiện ra: 1 - File nhận là file có đuôi là .zip 2 - File sẽ được giải nén ngay lập tức khi ta gửi qua. 3 - Tất cả file giải nén có đuôi `.php` đều không thể mở được vì bị chặn (403 error). Vậy mục đích của ta là cần đọc file `index.php` bằng một đường dẫn, hoặc tệp dẫn tới `index.php`. tìm tòi một chút thì ta trong bài này ta sẽ dùng sympolic link(liên kết tượng trưng), là đường dẫn đến vị trí file trong hệ thống. Tức là khi ta biết tên file cần tìm là `'index.php'`, sử dụng symbolic link để thì ra sẽ trực tiếp truy cập vào nó và thực thi theo định dạng của tệp liên kết mà không cần quan tâm đến vị trí của nó. đầu tiên tạo một file `test` trong ổ C để ta có thể truy cập vào bên trong và tự do thao tác bằng Ubuntu, sau đó, tạo một symlink với tệp `index.php` là tệp có đuôi `.txt` để đọc nội dung. Sau đó nén lại bằng lệnh zip (em thử truy cập để nén trực tiếp nhưng không thành vì file 0 byte được coi như không tồn tại,nên phải dùng `--symlinks` để lệnh zip tự động thêm liên kết bên trong.) ![image](https://hackmd.io/_uploads/HJyPORcEA.png) Upload file và ta nhận thấy bên trong file đã tăng thêm byte, click vào và ta ra mật khẩu cần tìm. ![image](https://hackmd.io/_uploads/rJ_RY0qE0.png) Pass : N3v3r_7rU5T_u5Er_1npU7 ::: success khi mở thử tệp được nén bên trong thì ta nhận thấy có dòng `../../../index.php`. Điều này xảy ra là do cơ chế nén `zip` của linux. Như đã nói ở ban đầu khi file`.txt` được dùng làm liên kết tượng trưng không chứa nội dung gì bên trong, tức là không có byte, và ta không thể nén một thứ không có gì cả. ![image](https://hackmd.io/_uploads/rkxF4Uo4R.png) Vì vậy ta dùng `--symplink`, lúc này tệp zip đã bao gồm thông tin về liên kết tượng trưng, đồng thời chương trình nén sẽ hiều file `.txt` không data đó là một liên kết tượng nên sẽ hiển thị đường dẫn đó, dẫn đến hiện tượng trên ::: #### 5 - File_upload_workshop_level_1 Khi vào trang web em để ý thấy chỉ có một chổ để upload file, và không có cụ thể về loại file upload, và để cũng bảo ta RCE web này, nên em thử dùng hàm exec() để xem trong này có file gì khôg, kết quả là trang hiển thị tên file. Thử dùng shell_exec('ls -a') thì thứ hiển thị lại là danh sách tệp đã được tải lên. ![image](https://hackmd.io/_uploads/HyIuNXjVA.png) Vậy để tìm bypass, tức là tìm cách để ta truy cập vào thư mục gốc để xem có gợi ý nào về nơi chứa file tiếp theo không. Gửi lên `dánhach.php` để đọc danh sách file ở thư mục gốc. ```typescript=php <?php $rootfile = '/'; // Gán biến với đường dẫn tới thư mục gốc bằng giá trị '/' $files = scandir($rootfile); // dùng hàm 'scandir() để lấy danh sách trong thư mục, gán vào biến files foreach ($files as $file) { // dùng vòng lặp vỡi mỗi giá trị mới, dừng khi hết phần tử trong mảng. echo $file . "<br>"; // In ra danh sách các tệp và thư mục, } ?> ``` ![image](https://hackmd.io/_uploads/BkA-aQs40.png) Ta thấy có một tệp secret.txt, thử đọc và bằng đoạn code sau trong `secretcandoc.php` ```typescript=php <?php $rootfile = '/secret.txt'; //Lưu địa chỉ file vào biến if (file_exists($rootfile)) { $file_content = file_get_contents($rootfile); //đọc nội dung file đích và lưu nó vào biến khác echo $file_content; } else { echo "Lỗi"; } ?> ``` ![image](https://hackmd.io/_uploads/r13ggEsN0.png) #### 6 - File_upload_workshop_level_2 Vì giao diện giống ở level đầu nên ta thử đưa php đọc danh sách vào thì báo lỗi ![image](https://hackmd.io/_uploads/SynYlNoNC.png) Thử gửi các file khác thì em nhận ra nó chỉ báo lỗi file `.php`, vậy bypass bằng cách dùng thêm một đuôi mở rộng như ở trong các bài root me trước và vẫn không thành công, nên em thử vào file` .php` của nó để đọc ![image](https://hackmd.io/_uploads/BJDbEEjN0.png) giải phẩu một chút, ta hiểu ra rằng khi dùng hàm `explode()` thì nó sẽ chuyển string thành array dựa trên kí tự đánh dấu để phân cách, với kí tự được đánh dấu là ".", và phân tách thành tên và phần mở rộng. Vì được lưu thành array nên nó sẽ có dạng là Array (tên_file[0] -> php[1]), khi này phần tử thứ hai , tức là `php` được lấy và lưu vào biến, khi này kiểm tra sẽ báo lỗi. Từ đó ta chỉ cần đưa trước file php một cái đuôi khác, không nhất thiết phải là một phần mở rộng, và ta bypass thành công. Kết quả là file cần đọc flag giống bài trước. PASS : CBJS{FAKE_FLAG_FAKE_FLAG} #### 7 - File_upload_workshop_level_3 Vì nó vẫn là một giao diện cũ, nhưng để cho chắc thì em sẽ đọc file trước xem nó có khác gì ở file lần trước không ![image](https://hackmd.io/_uploads/B1-FdEoNA.png) Ở đây em để ý nó có phần end, thử tìm hiểu và em phát hiện ra rằng nó sẽ cho ra output là phần tử cuối của 1 array. Không thể áp dụng cách giống `MIME type root me` được, bằng cách tra mạng thì ta biết được ngoài đuôi mở rộng `.php` còn có các đuôi khác như sau ![image](https://hackmd.io/_uploads/S16OoEjEC.png) Thử lần lượt và có `.phtml` là hiệu quả ![image](https://hackmd.io/_uploads/SJIEnEjEA.png) ![image](https://hackmd.io/_uploads/HyxD24iNA.png) #### 8 - File_upload_workshop_level_4 ::: info #### Tổng quan về Apache Apache là phần mềm web server miễn phí mã nguồn mở, họa động như một cầu nối giữa server và client. còn .htaccess là công cụ dùng để thực hiện cấu hình cho mỗi thư mục trong hệ thống. Apache(2) thường dùng .htaccess để áp dụng cấu hình chi thư mục cụ thể. Chú ý là phần ![image](https://hackmd.io/_uploads/BJEZcUj4A.png) Với * Options: dùng để theo dõi thực thi một số tính năng của Apache sẽ áp dụng trong hệ thống khi đối tượng thực thi hành động kích hoạt nào đó * AllowOverride: cho phép tệp htaccess được ghe đè trong tệp cấu hình chính hay không. * Require: Kiểm soát các yêu cầu từ người dùng nội bộ/ngoài khi thực thi vào một thư mục ::: Ở level này thì ta thấy có một link apache nhưng không mở được, truy cập vào tệp tin thì ta mở được file `apache2`, cái mà không xuất hiện ở các level trước. Có thể thấy ở trong file này có thể thấy ta được phép truy cập hoặc ghi đè `AllowOverride All` vào file .htaccess. ![image](https://hackmd.io/_uploads/HJa7NrjNC.png) Nhưng vì ta không thể upload file `.php` nên ta sẽ thử tìm [cách ghi đè lên .htaccess lệnh sao cho nó cho phép ta gửi file .php lên thành công](https://stackoverflow.com/questions/12292754/how-to-execute-php-files-with-html-extension) Dùng `AddType application/x-httpd-php .html .htm`, bằng cách này thì các file đuôi `.html` sẽ được sử lý như `.php`, và ta thành công mở được thư mục gốc ![image](https://hackmd.io/_uploads/rk3yuSoVR.png) ![image](https://hackmd.io/_uploads/B1VQdSjNR.png) #### 9 - File_upload_workshop_level_5 Ở bài này, gợi ý đưa ra là kiểm tra xem file gửi qua là file ảnh hay không. Kiểm tra trong code thì thấy bên trong dùng Mime type để xác định, nhưng chỉ kiểm tra xem có chứa phần mở rộng của ảnh. Ta chỉ cần vượt qua bước kiểm tra này và đưa file về lại định dạng `.php` khi đang được gửi lên, giống trong bài `2 - File upload - MIME type`, thực hiện tương tự ![image](https://hackmd.io/_uploads/Sk85KSoNA.png) ![image](https://hackmd.io/_uploads/SkWRKHoNA.png) ![image](https://hackmd.io/_uploads/HkCXqSjNA.png) #### 10 - File_upload_workshop_level_6 ![image](https://hackmd.io/_uploads/rJvMp8sVA.png) Có thể thấy bài 6 có khác ở hai điểm khác cần chú ý là hàm finfo_open(FILEINFO_MIME_TYPE), hàm dùng để mô tả thông tin file, kể cả file .php nhúng bên trong. Và khi này ta đã truyền vào trong hàm `FILEINFO_MIME_TYPE` lúc này khi ta tải tệp lên thì tệp đã xác định theo một dạng MIME nhất định, nên dùng double extension sẽ bị phát hiện ngay. Nếu ta áp dụng cách ở level 5 thì hệ thống sẽ biết tệp không nằm trong list MIMEs của biến `$whitelist`, và ngay lập tức dừng và trả về `die("hack detected");` *Bổ sung vào ngày 04/06/2024 --- Vậy muốn bypass, ta cần tìm cách sao cho file ảnh mang định dạng ảnh, nhưng đồng thời chính bản thân file ảnh đó chứa mã độc `.php` mà không được thực thi hoặc bị đọc, nhưng vẫn gọi được. Đó là ta sử dụng [**Polyglot file**](https://hackmd.io/ANXIg60hQfq8lZt3zZFnAw)