最近想把高危漏洞都总结一下,虽然都利用过,但没有好好整理,用的时候总会手忙脚乱。先从xxe开始
漏洞测试环境用的是java11+最新版webgoat,其中有专门的xxe部分。先简单介绍下xml的结构,比如有一个xml
1 |
|
其中第一行是元数据,带上就行。person是根元素,一个xml只有一个,里面的name和age是子元素,可以有多个。
xml标签中不能有特殊符号,比如尖括号等,因为会混淆格式。这时可以用实体(entites)解决。实体相当于变量,可以声明后多处引用。实体在DTD(document tyoe definition)部分被定义。加入DTD的xml比如
1 |
|
DOCTYPE标识符代表DTD,其中定义了name变量。这种就是普通的引用实体。还有种实体是参数实体,后面再介绍。
xxe的全称是xml external entites也就是外部实体(注入),外部实体也就是从外部,比如本地文件或网络获取内容,自然可能会产生文件读取或ssrf之类的漏洞。比如
1 |
|
SYSTEM关键字就是外部实体的标志,会访问后面的url,这样输出就会是passwd文件的内容。
这也就是最简单的xxe利用,直接回显(in-band)的xxe。和sql注入类似,除此之外还有报错(error-based)xxe和盲(blind)xxe也就是带外(out-of-band)/OOB)xxe。利用方法也更复杂一些。
###blind xxe
盲xxe也是最常见的类型,需要带外传输数据。那么很自然想到用http请求传递文件内容。这里就需要使用前面说过的参数实体。参数实体只能在DTD中引用,可以用一个实体给另一个实体赋值。但注意下面这种嵌套的用法(%name)只能在外部DTD中使用(文档是这么说明的,但实际实现不同语言和解析器会有差异),也就是需要单独创建个DTD文件放在服务器上。
1 | <!--name.dtd--> |
那么带外传输数据可以这样,比方写个1.txt内容是test xxe,运行后可以在监听端收到GET /test%20xxe的请求
1 | <!--evil.dtd--> |
也可以用参数实体,这时DTD中嵌套实体部分需要编码
1 | <!--evil.dtd--> |
这样就相当于直接引入实体send,请求ip加passwd信息。看上去很完美。
但实际上这样还是会出现问题,比如文件中包含特殊字符(主要是尖括号)和换行时都会导致xml或url的解析问题,这就需要新的解决方法。
解决尖括号的方法是利用CDATA
1 | <![CDATA[ <text> ]]> |
其中的标签不会被解析,那么就可以用CDATA把读取的文件内容包起来,在外部DTD中拼接,比如1.txt中内容改为
1 | asd<button onmouseover=alert(1)> |
1 |
|
然后在xml中调用all实体就可以获取带有尖括号的字符串了,某些情况下还可以形成存储xss。
本地环境我试了下,回显时会受到尖括号影响,带外时不需要CDATA也可以正常获取带尖括号的数据,但是无法正常获取带#号的数据,因为按url处理掉了。
但是带外的过程还会有个问题,就是出现换行符时,url解析会报错。php环境的话很简单,用filter来base64一下就可以了。java中需要用ftp协议,并且只有低版本(leadroyal给出 <7u141 和 <8u162)可以,高版本也对换行做了限制。并且ftp协议中?、#等特殊符号也会被截断。可以利用工具xxe-ftp-server
所以高版本java读取多行文件就只能依赖服务器的报错了,比如DTD内容为
1 |
|
这样服务器就会报一个no protocol: 文件内容,并且其中所有内容包括多行内容及特殊字符都会打印。一次性解决上面的问题。这就属于报错xxe。
前面说的都是利用外部DTD,那不允许请求外网服务器DTD怎么办?还有种攻击手法是利用本地DTD。这种手法也可以算一种更复杂的OOB的方法。首先需要利用本地存在的DTD文件,每个系统都有一些内置的,比如ubuntu系统自带的/usr/share/yelp/dtd/docbookx.dtd,这里存在许多参数实体,随便选一个比如ISOamsa,然后重写它,最开始的版本是这么写的
1 |
|
其实和前面的方法相比,就是多了一步重写参数实体,但是前面不是说了内部DTD不允许嵌套实体吗?这里用的是外部DTD在内部重写,并且用了三层嵌套。分析下是哪一点起了作用,首先试试外部DTD重写,两层嵌套
1 |
失败了,和之前直接在内部DTD引用参数实体一样。
那么改成内部DTD,三层嵌套呢?
1 |
还是失败了,网上有的帖子这样是可以的,但他们的环境都是php的,可能是解析器不同。
试试四层?
1 |
还是不行,引入外部DTD的话可以。所以java11环境下应该是只有外部DTD+三层以上嵌套调用才能解析,否则是不行的。也可以改成报错的版本,把send那里的url改了就行,这也是不出网情况下唯一的解决方案了。
###利用方式
有回显–>回显xxe,不需要引入实体
有报错–>外网DTD报错、本地DTD报错
无报错–>外网DTD盲xxe、本地DTD盲xxe
###漏洞点
首先如果请求里有明显xml标签就可能有xxe。
如果请求是json,转化成xml格式可能存在xxe,这是后端restful写法导致的。
再就是其他格式文档,docx、xslx、pptx、pdf、svg等等,如果被解析了的话都有可能存在xxe。
###一些差异
前面自己的实验和网上的帖子有很多矛盾的地方,怀疑是解析器的区别。所以又启动了一个phpstudy的php7.3.4+xml2.9.9的服务测试下,服务端代码
1 |
|
网上都用的这个代码,很奇怪我没有开启allow_url_include但也可以用php://input。好像是file_get_contents可以,include不行,跟网上说的又不一样。
和文档的说明不同,这段php代码可以在内部DTD中嵌套实体
1 |
|
读文件
1 |
|
三层都不需要,两层就可以了,还要啥自行车。
不过这是回显条件下的。带外时可以用三层,同样不需要外部DTD
1 |
还可以利用filter实现双层嵌套带外,顺便绕过某些关键字过滤,php的各种花样都可以用了。
1 |
|
最好的语言,最好的语言(拱手
参考链接
https://www.youtube.com/watch?v=gjm6VHZa_8s
https://www.freebuf.com/vuls/207639.html
https://www.leadroyal.cn/?p=914
https://cloud.tencent.com/developer/article/1500898
https://github.com/LandGrey/xxe-ftp-server