为一名经验丰富的数据分析师,我深知采集文章所需的下载工具对于提高工作效率和数据准确性的重要性。在这篇文章中,我将分享我在采集文章过程中使用的一些下载工具,并提供一些实用的技巧和注意事项。
第一,选择合适的下载工具
在采集文章时,选择合适的下载工具是至关重要的。根据不同的需求,可以选择使用专业的网页抓取工具,如Scrapy和Beautiful Soup,或者使用浏览器插件来实现页面截取和保存。
第二,了解目标网站结构
在采集文章之前,先了解目标网站的结构是非常必要的。通过分析网站的HTML结构和URL规律,可以更好地确定采集规则,并避免因网站结构变化而导致的数据抓取失败。
第三,设置合理的采集频率
在进行大规模文章采集时,设置合理的采集频率非常重要。过快或过频繁的访问可能会给目标网站带来压力,并可能导致IP被封禁。因此,在进行大规模数据采集时,建议合理设置访问间隔和访问频率,以避免给目标网站造成困扰。
第四,处理反爬机制
许多网站为了防止数据被非法采集,会设置一些反爬机制。在采集文章时,我们需要了解并应对这些反爬机制。可以通过设置合理的请求头、使用代理IP等方式应对反爬措施,确保数据的正常采集。
第五,数据清洗和整理
采集到的文章数据可能存在一些噪音或冗余信息,因此在采集之后,我们需要进行数据清洗和整理。可以使用正则表达式、自然语言处理等技术来去除HTML标签、过滤无效内容,并对文章进行格式化和结构化处理。
第六,存储与备份
为了保证数据的安全性和可用性,在采集文章时需要做好存储与备份工作。可以选择将数据存储在数据库中,或者将其导出为CSV或Excel文件以备后续分析使用。同时,定期进行数据备份是非常必要的,以防止意外情况导致数据丢失。
第七,合规与道德
在进行文章采集时,我们要始终遵守相关法律法规,并尊重网站的使用规则和版权。不得采集敏感信息和未经授权的内容,同时要注意保护用户隐私和个人信息。
第八,持续学习与改进
文章采集是一个不断学习和改进的过程。随着技术的发展和网站的变化,我们需要不断更新自己的知识和技能,并关注新的数据采集工具和方法,以提高效率和准确性。
通过以上八点经验分享,我希望能够帮助到正在进行文章采集工作的各位同仁。采集文章需要下载,但更需要我们对技术和道德的认知与把握。只有在合规、持续学习与改进的基础上,我们才能更好地完成我们的工作。愿大家都能在文章采集中取得优异的成果!
么是css的调用方式?
简单来讲,就是解决把css代码写在哪里的问题,这块内容之前在课堂上演示过,这里再用文字说明一下,权当复习。
大体有以下四种方式:
1,外联式样式表
添加在HTML的头部信息标识符< head>里:
<head>
<link rel="stylesheet" href="style.css" type="text/css">
</head>
其中href是目标文档的URL, type则规定了目标URL的MIME类型,而media规定了文档将显示在什么设备上。
2,内嵌样式表
添加在HTML的头部信息标识符< head>里:
<head>
<style type="text/css">
<!-- 样式表的具体内容 -->
</style>
</head>
type=”text/css”表示样式表采用MIME类型,帮助不支持CSS的浏览器过滤掉CSS代码,避免在浏览器面前直接以源代码的方式显示我们设置的样式表。但为了保证上述情况一定不要发生,还是有必要在样式表里加上注释标识符“< !--注释内容-->”。
3,元素内定
语法:
<Tag style="properties">网页内容</tag>
举个例子:
<p style="color: blue; font-size: 10px">CSS实例</p>
上面例子的代码说明:
用蓝色显示字体大小为10px的“CSS实例”。尽管使用简单、显示直观,但是这种方法不怎么常用,因为这样添加无法完全发挥样式表的优势——即内容结构和格式控制分别保存。
4,导入样式表(高级用法,暂时先放下)
语法:
<style type="text/css">
<!-- @import url("css/base.css"); -->
</style>
其中外部引用CSS主要用到两种方式link和@import
本质上,这两种方式都是为了加载CSS文件,但还是存在着细微的差别。
link和@import存在如下差别:
差别1:老祖宗的差别。link属于HTML标签,而@import完全是CSS提供的一种方式。
link标签除了可以加载CSS外,还可以做很多其它的事情,比如定义RSS,定义rel连接属性等,@import就只能加载CSS了。
差别2:加载顺序的差别。当一个页面被加载的时候(就是被浏览者浏览的时候),link引用的CSS会同时被加载,而@import引用的CSS会等到页面全部被下载完再被加载。所以有时候浏览@import加载CSS的页面时开始会没有样式(就是闪烁),网速慢的时候还挺明显。
差别3:兼容性的差别。由于@import是CSS2.1提出的所以老的浏览器不支持,@import只有在IE5以上的才能识别,而link标签无此问题。
差别4:当使用javascript控制DOM去改变样式的时候,只能使用link标签,因为@import不是DOM可以控制的。
从上面的分析来看,还是使用link标签比较好。
课后练习:
步骤一:新建一个名为index.html的网页。
步骤二:在index.html网页文档里加入两个p标签,分别在p标签中加入内容(内容随意)
步骤三:尝试用上面提到的前三种方式,用css代码改变p标签内容的显示格式。
在文件上传的功能处,若服务端脚本语言未对上传的文件进行严格验证和过滤,导致恶意用户上传恶意的脚本文件时,就有可能获取执行服务端命令的能力,这就是文件上传漏洞。文件上传漏洞对Web应用来说是一种非常严重的漏洞。
一般情况下,Web应用都会允许用户上传一些文件,如头像、附件等信息,如果Web应该没有对用户上传的文件进行有效的检查过滤,那么恶意用户就会上传一句话木马等webshell,从而达到控制web网站的目的。
由于程序员在对用户文件上传部分的控制不足或者处理缺陷,而导致用户可以越过其本身权限向服务器上传可执行的动态脚本文件,并通过此脚本文件获得了执行服务端命令的能力。
高危出发点:
相册、头像上传视频、照片分享附件上传(论坛发帖、邮箱)文件管理器
1、代码执行
上传文件是web脚本语言,服务器的web容器解释并执行了用户上传的脚本,导致代码执行,甚至导致网站甚至整个服务器被控制
2、域控制
上传文件是flash的策略文件crossdomiain.xml,黑客用以控制flash在该域下的行为;
3、网站挂马
上传文件是病毒、木马,黑客用以诱骗用户或者管理员下载执行;
4、钓鱼
上传文件是钓鱼图片或为包含了脚本的图片,在某些版本的浏览器中会被作为脚本执行,被用于钓鱼和欺诈。
首先,上传的文件能够被web容器解释执行。所以文件上传后所在的目录要是web容器所覆盖到的路径。
其次,用户能够从web访问这个文件。如果文件上传了,但用户无法通过web访问,或者无法得到web容器解释这个脚本,那么也不能称之为漏洞。 最后,用户上传的文件若被安全检查、格式化、图片压缩等功能改变了内容,则也可能导致攻击不成功。
将上传文件与web服务隔离 白名单过滤、限制上传文件类型 ? 文件上传路径设置为不可执行权限 ? 检查文件上传路径 ? 自带函数检测 ? 自定义函数检测 ? 图片渲染 ? 对上传文件重命名 ? 对文件内容压缩,重新生成文件内容 ? 检查文件内容
详情看图片,后面有一些场景的题目配合学习
需要上传个webshell上去 但是限制了上传的文件类型
F12查看javascrip内容
代码审计
function checkFile() {
var file=document.getElementsByName('upload_file')[0].value;
if (file==null || file=="") {
alert("请选择要上传的文件!");
return false;
}
//定义允许上传的文件类型
var allow_ext=".jpg|.png|.gif"; //前端白名单,只允许这三个格式
//提取上传文件的类型
var ext_name=file.substring(file.lastIndexOf("."));
//判断上传文件类型是否允许上传
if (allow_ext.indexOf(ext_name)==-1) {
var errMsg="该文件不允许上传,请上传" + allow_ext + "类型的文件,当前文件类型为:" + ext_name;
alert(errMsg); //输出errMsg
return false;
}
}
F12点击查看器,将其中的js过滤代码(上面那段)删除掉,ctrl+s保存在本地桌面上,在桌面上右键文本方式打开,寻找action(目标),ctrl+f,没有找到,说明没有上传目标,手动添加,重新刷新网页正常上传正常图片,查看网络,可以看到一个post数据包,200ok,里面有一个地址
action="http://127.0.0.1/upload/Pass-01/index.php"
将这个代码添加在<from>标签
<div id="upload_panel">
<ol>
<li>
<h3>任务</h3>
<p>上传一个<code>webshell</code>到服务器。</p>
</li>
<li>
<h3>上传区</h3>
<form action="http://127.0.0.1/upload/Pass-01/index.php" enctype="multipart/form-data" method="post" onsubmit="return checkFile()">
<p>请选择要上传的图片:</p><p>
<input class="input_file" type="file" name="upload_file">
<input class="button" type="submit" name="submit" value="上传">
</p></form>
<div id="msg">
</div>
<div id="img">
</div>
</li>
</ol>
</div>
重新上传一个a.php文件,(<?php phpinfo(); ?>)
在网站根目录result可以查看到a.php文件,说明上传成功。
或者访问:http://127.0.0.1/upload/upload/a.php
浏览器172.21.172.56访问靶场,修改php文件后缀改为png文件,上传,bp抓包,修改数据包后缀,又改回php,发送数据包,再次访问http://172.21.172.56/upload/upload/b.php出现phpinfo信息说明成功
接着再地址栏输入about:config,点击回车键。
接着看到如下画面。去掉下次任显示此警告前面的方框里的钩,再点击我 保证会小心按钮
点击后看到如下画面,在搜索地址栏中输入javascript.enabled
改为flase
直接上传c.php就可以了
访问:http://172.21.172.56/upload/upload/c.php
出现信息,成功
js代码很明显是白名单,只允许三种通过,那么我们可以手动添加一个规则
直接上传php文件
右键网页源码,没有发现js过滤
网站根目录查看index.php源码
<?php
include '../config.php';
include '../head.php';
include '../menu.php';
$is_upload=false;
$msg=null;
if (isset($_POST['submit'])) { //isset — 检测变量是否已设置并且非 null
if (file_exists(UPLOAD_PATH)) { // file_exists 检查文件或目录是否存在 ,UPLOAD_PATH如果存在
if (($_FILES['upload_file']['type']=='image/jpeg') || ($_FILES['upload_file']['type']=='image/png') || ($_FILES['upload_file']['type']=='image/gif')) { //如果文件类型是image/jpeg image/png image/gif(白名单)
$temp_file=$_FILES['upload_file']['tmp_name'];
$img_path=UPLOAD_PATH . '/' . $_FILES['upload_file']['name'];
if (move_uploaded_file($temp_file, $img_path)) {
$is_upload=true;
} else {
$msg='上传出错!';
}
} else {
$msg='文件类型不正确,请重新上传!'; //非image/jpeg image/png image/gif,重新上传
}
} else {
$msg=UPLOAD_PATH.'文件夹不存在,请手工创建!';
}
}
?>
不是在js中,而是在服务器端进行了限制
MIME (Multipurpose Internet Mail Extensions) 是描述消息内容类型的因特网标准 MIME 消息能包含文本、图像、音频、视频以及其他应用程序专用的数据 在HTTP 协议中,使用Content-Type 字段表示文件的MIME 类型。
所以抓包修改Content-Type
200ok
仅仅判断content-type类型,因此上传a.php抓包修改content-type为图片类型:image/jpeg、image/png、image/gif 就可了
$is_upload=false;
$msg=null;
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) { //检查文件或目录是否存在
$deny_ext=array('.asp','.aspx','.php','.jsp'); //数组deny_exit相当于黑名单
$file_name=trim($_FILES['upload_file']['name']); //trim()去除字符串首尾处的空白字符(或者其他字符)
$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)) { //检查数组中是否存在某个值,如果不存在 执行下一步
$temp_file=$_FILES['upload_file']['tmp_name'];
$img_path=UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext; //date()重置时间,时间戳 //rand产生一个随机整数
if (move_uploaded_file($temp_file,$img_path)) { //将上传的文件移动到新位置
$is_upload=true;
} else {
$msg='上传出错!';
}
} else {
$msg='不允许上传.asp,.aspx,.php,.jsp后缀文件!';
}
} else {
$msg=UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
/*
move_uploaded_file(string `$filename`, string `$destination`)
filename
上传的文件的文件名。
destination
移动文件到这个位置。
*/
是个黑名单,不允许上传.asp,.aspx,.php,.jsp后缀的文件
那最简单的绕过办法,用特殊后缀名
.phtml
.phps
.php5
.pht
类似的 前提是apache的配置文件httpd.conf中有如下配置
AddType application/x-httpd-php .php .phtml .phps .php5 .pht
实质就是添加可以执行php的文件类型
回复告诉我们路径以及文件名了。图片上传后右键也能得到图片地址
$is_upload=false;
$msg=null;
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$deny_ext=array(".php",".php5",".php4",".php3",".php2",".php1",".html",".htm",".phtml",".pht",".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",".ini");
$file_name=trim($_FILES['upload_file']['name']);//trim()去除字符串首尾处的空白字符(或者其他字符)
$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)) {
$temp_file=$_FILES['upload_file']['tmp_name'];
$img_path=UPLOAD_PATH.'/'.$file_name;
if (move_uploaded_file($temp_file, $img_path)) {
$is_upload=true;
} else {
$msg='上传出错!';
}
} else {
$msg='此文件不允许上传!';
}
} else {
$msg=UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
黑名单拒绝了几乎所有有问题的后缀名,除了.htaccess 前提条件(1.mod_rewrite模块开启。2.AllowOverride All)
因此先上传一个.htaccess文件,内容如下:SetHandler application/x-httpd-php
这样所有文件都会当成php来解析
在上传一个 1.jpg,(内容写成 <?php phpinfo();?>)之后访问就会以php来执行
$is_upload=false;
$msg=null;
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$deny_ext=array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".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)) {
$temp_file=$_FILES['upload_file']['tmp_name'];
$img_path=UPLOAD_PATH.'/'.$file_name;
if (move_uploaded_file($temp_file, $img_path)) {
$is_upload=true;
} else {
$msg='上传出错!';
}
} else {
$msg='此文件类型不允许上传!';
}
} else {
$msg=UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
更完善的黑名单.htaccess文件也被禁了
但是.ini没禁 所以可以上传.user.ini文件 内容是 auto_prepend_file=1.jpg
让所有php文件都“自动”包含1.jpg文件
试了半天根本不行,介绍另一个方法
$is_upload=false;
$msg=null;
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$deny_ext=array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".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",".ini");
$file_name=trim($_FILES['upload_file']['name']);
$file_name=deldot($file_name);//删除文件名末尾的点
$file_ext=strrchr($file_name, '.');
$file_ext=str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
$file_ext=trim($file_ext); //首尾去空
?
if (!in_array($file_ext, $deny_ext)) {
$temp_file=$_FILES['upload_file']['tmp_name'];
$img_path=UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;
if (move_uploaded_file($temp_file, $img_path)) {
$is_upload=true;
} else {
$msg='上传出错!';
}
} else {
$msg='此文件类型不允许上传!';
}
} else {
$msg=UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
更完善的黑名单
但是大小写的函数去掉了
所以可以后缀名大写绕过
$file_ext=strtolower($file_ext); //转换为小写
这里没得
抓包看看,文件改成啥名字了
$is_upload=false;
$msg=null;
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$deny_ext=array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".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",".ini");
$file_name=$_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
if (!in_array($file_ext, $deny_ext)) {
$temp_file=$_FILES['upload_file']['tmp_name'];
$img_path=UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;
if (move_uploaded_file($temp_file,$img_path)) {
$is_upload=true;
} else {
$msg='上传出错!';
}
} else {
$msg='此文件不允许上传';
}
} else {
$msg=UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
对比前面的题 这题没有对后缀名进行去空 所以在后缀名里加个空格就能绕过
$file_ext=trim($file_ext); //收尾去空 $file_name=trim($_FILES['upload_file']['name']); //trim()去除字符串首尾处的空白字符(或者其他字符)
这里没有
$is_upload=false;$msg=null;if (isset($_POST['submit'])) { if (file_exists(UPLOAD_PATH)) { $deny_ext=array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".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",".ini"); $file_name=trim($_FILES['upload_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)) { $temp_file=$_FILES['upload_file']['tmp_name']; $img_path=UPLOAD_PATH.'/'.$file_name; if (move_uploaded_file($temp_file, $img_path)) { $is_upload=true; } else { $msg='上传出错!'; } } else { $msg='此文件类型不允许上传!'; } } else { $msg=UPLOAD_PATH . '文件夹不存在,请手工创建!'; }}
对比前面 没有对后缀名末尾的点进行处理
$file_name=deldot($file_name);//删除文件名末尾的点
没有这个
$is_upload=false;$msg=null;if (isset($_POST['submit'])) { if (file_exists(UPLOAD_PATH)) { $deny_ext=array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".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",".ini"); $file_name=trim($_FILES['upload_file']['name']); $file_name=deldot($file_name);//删除文件名末尾的点 $file_ext=strrchr($file_name, '.'); $file_ext=strtolower($file_ext); //转换为小写 $file_ext=trim($file_ext); //首尾去空 if (!in_array($file_ext, $deny_ext)) { $temp_file=$_FILES['upload_file']['tmp_name']; $img_path=UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext; if (move_uploaded_file($temp_file, $img_path)) { $is_upload=true; } else { $msg='上传出错!'; } } else { $msg='此文件类型不允许上传!'; } } else { $msg=UPLOAD_PATH . '文件夹不存在,请手工创建!'; }}
没有对后缀名中的’::$DATA’进行过滤 在php+windows的情况下文件名+"::$DATA"会把::$DATA之后的数据当成文件流处理,不会检测后缀名 且保持"::$DATA"之前的文件名
少了 $file_ext=str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
$is_upload=false;$msg=null;if (isset($_POST['submit'])) { if (file_exists(UPLOAD_PATH)) { $deny_ext=array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".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",".ini"); $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)) { $temp_file=$_FILES['upload_file']['tmp_name']; $img_path=UPLOAD_PATH.'/'.$file_name; if (move_uploaded_file($temp_file, $img_path)) { $is_upload=true; } else { $msg='上传出错!'; } } else { $msg='此文件类型不允许上传!'; } } else { $msg=UPLOAD_PATH . '文件夹不存在,请手工创建!'; }}
区别是路径里不是file_ext,而是file_name即保存文件的时候没有重命名而使用的原始的文件名 可以利用1.php. .(点+空格+点)来绕过
$is_upload=false;$msg=null;if (isset($_POST['submit'])) { 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","ini"); $file_name=trim($_FILES['upload_file']['name']); $file_name=str_ireplace($deny_ext,"", $file_name); $temp_file=$_FILES['upload_file']['tmp_name']; $img_path=UPLOAD_PATH.'/'.$file_name; if (move_uploaded_file($temp_file, $img_path)) { $is_upload=true; } else { $msg='上传出错!'; } } else { $msg=UPLOAD_PATH . '文件夹不存在,请手工创建!'; }}
用str_ireplace把黑名单里的后缀全替换为空 但是这个函数只进行一次替换 所以可以双写绕过
$is_upload=false;$msg=null;if(isset($_POST['submit'])){ $ext_arr=array('jpg','png','gif'); //数组 $file_ext=substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1); /*返回字符串的子串substr(string $string, int $start, int $length=?): string返回字符串 string 由 start 和 length 参数指定的子字符串。strrpos(string,find,start)函数查找字符串中最后一次出现的位置*/ 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; } else { $msg='上传出错!'; } } else{ $msg="只允许上传.jpg|.png|.gif类型文件!"; }}
定义一个白名单,只允许三种文件类型
substr()函数返回字符的一部分,strrpos(string,find,start)函数查找字符串中最后一次出现的位置,这里是将后缀名提取出来赋值给file_ext img_path是直接拼接
我们可以抓包修改get的参数,然后通过file_ext无效,这样就可以上传PHP文件
%00截断的概念和原理:
在URL中%00表示ASCII码中的0,而0作为特殊字符保留,表示字符结束,当url中出现%00时就会认为读取已结束,而忽略后面上传的文件或图片,只上传截断前的文件或图片
要求:php版本小于5.3.4,php的magic_quotes_gpc为OFF状态 将路径改为path=“upload/1.php%00”,那么拼接之后,文件上传时就变成了“upload/1.php%1.jpg”,这时上传便将1.php上传进去,而1.jpg则被截断
切换php版本
$is_upload=false;$msg=null;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=$_POST['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext; if(move_uploaded_file($temp_file,$img_path)){ $is_upload=true; } else { $msg="上传失败"; } } else { $msg="只允许上传.jpg|.png|.gif类型文件!"; }}
与上一题相比,区别是上传方式变成了POST。还是利用00截断
但是POST不会像GET对%00进行自动解码,需要在二进制中进行修改,将70 68 70后面的2b改为00
function getReailFileType($filename){ //得到真实的文件类型 $file=fopen($filename, "rb"); //打开文件 读 二进制 $bin=fread($file, 2); //只读2字节 也就是说只对文件头进行了检测 fclose($file);//关闭文件 $strInfo=@unpack("C2chars", $bin); //根据给定的格式将二进制字符串解压缩到数组中。 $typeCode=intval($strInfo['chars1'].$strInfo['chars2']); //获取变量的整数值 $fileType=''; switch($typeCode){ case 255216: $fileType='jpg'; break; case 13780: $fileType='png'; break; case 7173: $fileType='gif'; break; default: $fileType='unknown'; } return $fileType; //得到文件类型,返回的只有这四种}$is_upload=false;$msg=null;if(isset($_POST['submit'])){ //检测变量是否已设置并且非 null $temp_file=$_FILES['upload_file']['tmp_name']; $file_type=getReailFileType($temp_file); //跳转到上面的函数 if($file_type=='unknown'){ $msg="文件未知,上传失败!"; }else{ $img_path=UPLOAD_PATH."/".rand(10, 99).date("YmdHis").".".$file_type; if(move_uploaded_file($temp_file,$img_path)){ $is_upload=true; } else { $msg="上传出错!"; } }}- unpack(format,data)函数是规定在解包数据时所使用的格式,这里是文件头按照c格式解包- intval() 函数用于获取变量的整数值
rb是读取二进制文件
后面的fread函数只读两字节,也就是说只对文件头进行了检测
也就是说,文件头检测,如果你没有伪造文件头,即便你是jpg图片它也识别不出来,所以这里我们要抓包,伪造一个文件头
我们随便拿一张图片,文件内容最前面添加GIF89a 和
<?php $filename=$_GET['test']; include($filename); ?>
直接上传test.php文件,抓包拿到文件目录以及文件名
制作图片马 将一句话木马1.php和普通图片1.jpg合并 得到shell.jpg
这里Windows会报毒."无法成功完成操作,因为文件包含病毒或潜在的垃圾软件。"
要关掉防火墙里面的实时防护
dos命令: copy 1.jpg /b + 1.php /a shell.jpg
shell.jpg上传上去之后,拿到图片地址以及文件名
但直接访问图片并不能把图片当做PHP解析,还需要利用文件包含漏洞
在www/upload-labs/upload目录下建立一个php文件
<?php/*本页面存在文件包含漏洞,用于测试图片马是否能正常运行!*/header("Content-Type:text/html;charset=utf-8");$file=$_GET['file'];if(isset($file)){ include $file;}else{ show_source(__file__);}
function isImage($filename){ $types='.jpeg|.png|.gif'; //三种类型的后缀 if(file_exists($filename)){ //文件存在文件名 $info=getimagesize($filename); //getimagesize()取得图像大小 $ext=image_type_to_extension($info[2]); //image_type_to_extension — 取得图像类型的文件后缀 if(stripos($types,$ext)>=0){ //stripos — 查找字符串首次出现的位置(不区分大小写)在变量types找,返回变量ext return $ext; }else{ return false; } }else{ return false; }}$is_upload=false;$msg=null;if(isset($_POST['submit'])){ $temp_file=$_FILES['upload_file']['tmp_name']; $res=isImage($temp_file); if(!$res){ $msg="文件未知,上传失败!"; }else{ $img_path=UPLOAD_PATH."/".rand(10, 99).date("YmdHis").$res; if(move_uploaded_file($temp_file,$img_path)){ //move_uploaded_file — 将上传的文件移动到新位置 $is_upload=true; } else { $msg="上传出错!"; } }}
用getimagesize函数获取图像大小及相关信息,成功返回一个数组,失败则返回 FALSE 并产生一条 E_WARNING 级的错误信息
仍然可以图片马绕过
function isImage($filename){ // //需要开启php_exif模块 $image_type=exif_imagetype($filename); //exif_imagetype — 判断一个图像的类型 switch ($image_type) { //语句类似于具有同一个表达式的一系列 if 语句。很多场合下需要把同一个变量(或表达式)与很多不同的值比较,并根据它等于哪个值来执行不同的代码。 case IMAGETYPE_GIF: return "gif"; break; case IMAGETYPE_JPEG: return "jpg"; break; case IMAGETYPE_PNG: return "png"; break; default: return false; break; }}$is_upload=false;$msg=null;if(isset($_POST['submit'])){ //检测变量是否已设置并且非 null $temp_file=$_FILES['upload_file']['tmp_name']; $res=isImage($temp_file); //在这里跑上去查看这个文件类型,返回一个后缀名或者错误 if(!$res){ //如果res==flase $msg="文件未知,上传失败!"; }else{ $img_path=UPLOAD_PATH."/".rand(10, 99).date("YmdHis").".".$res; if(move_uploaded_file($temp_file,$img_path)){ $is_upload=true; } else { $msg="上传出错!"; } }}
用到php_exif模块来判断文件类型
仍然可以图片马绕过
$is_upload=false;$msg=null;if (isset($_POST['submit'])){ ////检测变量是否已设置并且非 null // 获得上传文件的基本信息,文件名,类型,大小,临时文件路径
$filename=$_FILES['upload_file']['name']; $filetype=$_FILES['upload_file']['type']; $tmpname=$_FILES['upload_file']['tmp_name']; $target_path=UPLOAD_PATH.'/'.basename($filename); //路径 // 获得上传文件的扩展名
$fileext=substr(strrchr($filename,"."),1); //strrchr — 查找指定字符在字符串中的最后一次出现 。substr — 返回字符串的子串。 //判断文件后缀与类型,合法才进行上传操作
if(($fileext=="jpg") && ($filetype=="image/jpeg")){ //如果文件类型是jpg 后缀也是jpg
if(move_uploaded_file($tmpname,$target_path)){ // 将上传的文件移动到新位置$target_path //使用上传的图片生成新的图片
$im=imagecreatefromjpeg($target_path); //imagecreatefromjpeg — 由文件或 URL 创建一个新图象
if($im==false){
$msg="该文件不是jpg格式的图片!";
@unlink($target_path); //unlink — 删除文件
}
else{ //给新图片指定文件名
srand(time()); //srand播下随机数发生器种子 time — 返回当前的 Unix 时间戳
$newfilename=strval(rand()).".jpg"; //strval — 获取变量的字符串值 //显示二次渲染后的图片(使用用户上传图片生成的新图片)
$img_path=UPLOAD_PATH.'/'.$newfilename;//路径
imagejpeg($im,$img_path); //imagejpeg — 输出图象到浏览器或文件。 @unlink($target_path);//删除 $is_upload=true; } } else { $msg="上传出错!"; }//这里只只展示了jpg的情况
if(($fileext=="jpg") && ($filetype=="image/jpeg"))
检测$fileext和$filetype是否为jpg格式.
if(move_uploaded_file($tmpname,$target_path)){
然后使用move_uploaded_file函数来做判断条件,如果成功将文件移动到$target_path,就会进入二次渲染的代码,反之上传失败.
在这里有一个问题,如果作者是想考察绕过二次渲染的话,在move_uploaded_file($tmpname,$target_path)返回true的时候,就已经成功将图片马上传到服务器了,所以下面的二次渲染并不会影响到图片马的上传.如果是想考察文件后缀和content-type的话,那么二次渲染的代码就很多余
由于在二次渲染时重新生成了文件名,所以可以根据上传后的文件名,来判断上传的图片是二次渲染后生成的图片还是直接由move_uploaded_file函数移动的图片.
在这里很明显,代码先将图片进行了上传,之后在进行判断格式是否正确,否则删除文件,所以这里存在一个逻辑漏洞。
我们可以用bp不断的发送.php格式的包,一直发,然后我们在网页上浏览打开这个php文件,只要我们速度在代码执行删除之前打开了php文件,那么代码就不能对php文件进行任何操作了。
https://xz.aliyun.com/t/2657
二次渲染了上传的图片
绕过方法:找到渲染前后没有变化的位置,然后将php代码写进去,就可以成功上传带有php代码的图片。
$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.php,只需要在它删除之前访问即可。
可以利用burp的intruder模块不断上传,然后我们不断的访问刷新该地址。
//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.phpclass 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" ); }.................. };
白名单,一步一步检查文件大小、文件是否存在等等。
move在rename之前,move操作进行了一次文件保存,rename进行了一次更改文件名。
因此可以通过不断上传图片马,由于条件竞争可能来不及重命名。
$is_upload=false;$msg=null;if (isset($_POST['submit'])) { 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)) { $temp_file=$_FILES['upload_file']['tmp_name']; $img_path=UPLOAD_PATH . '/' .$file_name; if (move_uploaded_file($temp_file, $img_path)) { $is_upload=true; }else{ $msg='上传出错!'; } }else{ $msg='禁止保存为该类型文件!'; } } else { $msg=UPLOAD_PATH . '文件夹不存在,请手工创建!'; }}
这关感觉像是个测试,对比前面可以发现有很多方法
点绕过或空格绕过:直接在后缀名后面加上/.
利用 apache 的解析漏洞,如给文件命名为test.php.aba
Post的00截断
$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="请选择要上传的文件!";}
in_array(value,array,type) 在数组中搜索给定的值
参数 | 描述 |
value | 必需。规定要在数组搜索的值 |
array | 必需。规定要搜索的数组。 |
type | 可选。如果设置该参数为true,则检查搜索的数据与数组的值的型昰否相同 |
explode(separator,string,limit) 把字符串打散为数组
参数 | 描述 |
separator | 必需。规定在哪里分割字符串。 |
string | 必需。要分割的字符串。 |
limit | 可选。规走所返回的数组元素的数目。 |
白名单限制
对文件上传的文件类型做了判断 将用户输入的文件名赋值给 $file is_array($file) 判断文件名是不是数组,如果不是,使用 explode() 以 . 为标记分割为数组 如果是数组,则定义后缀名白名单 $allow_suffix 对数组最后一个元素 $ext(正常情况是指后缀名)进行过滤 如果符合则取数组的第一个元素reset($file)作为文件名,倒数第二个元素$file[count($file)-1]作为后缀名。
绕过方法
$file[0]为smi1e.php/,也就是reset($file) $file[2]为白名单中的jpg,此时end($file)等于jpg,$file[count($file) - 1]为空 而$file_name=reset($file) . '.' . $file[count($file) - 1];,也就是smi1e.php/. 于是move_uploaded_file会忽略掉/.,最终上传smi1e.php
*请认真填写需求信息,我们会在24小时内与您取得联系。