从代码层面理解java的00截断漏洞深入篇
4个月前写了一篇文章叫《从代码层面理解java的00截断漏洞》,由于当时出差新疆没时间深入,便在文末立了个有空继续深入的flag。今天我们通过跟踪jdk代码, 彻底搞清楚java中00截断的原理,以及它之后版本是如何修复的?
一、漏洞测试代码改进
看了一些java web系统文件上传代码,基本都是使用FileOutputStream
来实现对上传内容的保存。于是将上篇文章的测试代码修改如下,简单模拟java的文件上传。
import java.io.*; public class T2 { public static void main(String[] args) { String filepath = "c://shell.jsp" + (char)0 + ".txt"; String content = "Test by c0ny1"; System.out.println(filepath); try { FileOutputStream fos = new FileOutputStream(filepath); fos.write(content.getBytes()); fos.close(); } catch (FileNotFoundException e) { e.printStackTrace(); }catch (IOException e) { e.printStackTrace(); } } }
通过在漏洞版本和非漏洞版本运行以上代码,可知如果00截断成功,则会在系统的c盘根目录新建一个内容为Test by c0ny1
的shell.jsp
,如果没有截断成功,则抛出Invalid file path
异常。
二、漏洞是如何产生的?
我选择使用JDK1.7.0
(JDK1.7第一个版本),来跟踪漏洞测试代码从运行到触发。
第一个构造函数
将传进来的name参数作为路径,新建了File对象,再次传入到FileOutputStream
对象新的构造函数。根据传入的两个参数的类型,我们可以确定会进入到以下这个构造函数。
第二个构造函数
FileOutputStream对象的构造方法又调用了open函数,打开了name参数传进来的文件路径,我们继续跟进open函数。
open方法的声明
发现open函数是一个native method。它的实现体是由非java语言(c语言)实现的。只能去OpenJDK官网下载jdk源码来查看它的实现。无奈没有找到jdk7u1的源码,只找到了jdk7u75的源码。其实在小版本上源码应该区别不大。
在\openjdk\jdk\src\windows\native\java\io\FileOutputStream_md.c
中找到了FileOutputStream
类的open
方法的JNI实现。open方法又调用了fileOpen
方法,继续跟进fileOpen方法。
open方法的定义
在io_util_md.c
中找到了fileOpen
方法的定义。
fileOpen方法的定义
fileOpen方法调用了winFileHandleOpen
函数,继续跟进。由于winFileHandleOpen函数代码比较多,这里精简出了关键代码。
jlong winFileHandleOpen(JNIEnv *env, jstring path, int flags) { ...... if (onNT) { //如果在Windows NT/Windows 2000操作系统下 WCHAR *pathbuf = pathToNTPath(env, path, JNI_TRUE); if (pathbuf == NULL) { /* Exception already pending */ return -1; } h = CreateFileW( pathbuf, /* Wide char path name */ access, /* Read and/or write permission */ sharing, /* File sharing flags */ NULL, /* Security attributes */ disposition, /* creation disposition */ flagsAndAttributes, /* flags and attributes */ NULL); free(pathbuf);//创建文件 } else { WITH_PLATFORM_STRING(env, path, _ps) { h = CreateFile(_ps, access, sharing, NULL, disposition,flagsAndAttributes, NULL);//创建文件 } END_PLATFORM_STRING(env, _ps); } ...... return (jlong)h; }
通过阅读以上代码,可知如果在Windows NT/Windows 2000平台下会调用pathToNTPath
函数将原始文件路径转化为Windows NT系统合法路径。然而通过阅读该方法源码,发现它并没有对\00字符串进行过滤。如果在其他Window操作系统版本下,则直接使用原始文件路径。
按照winFileHandleOpen
方法的逻辑,无论如何最终都是调用了CreateFileW
这个Windows API函数来创建文件。由于这个过程中均未对\00
字符串进行过滤,如果传入的文件路径带有\00字符,则CreateFileW
函数在创建文件时,路径会被截断。这没什么好说的。
这里我们没法继续跟进CreateFileW函数,毕竟Windows不开源。为了文章的严谨性,这里我用C语言写一个demo,来证明该函数可以截断。
//test.c #include "windows.h" int main() { HANDLE fileHandle = CreateFileW(L"C:\\shell.jsp\0test.txt", GENERIC_WRITE, FILE_SHARE_WRITE, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); char *data = "Test by c0ny1"; DWORD a = strlen(data); unsigned long b; WriteFile(fileHandle, data, a, &b, NULL); CloseHandle(fileHandle); return 0; }
代码运行演示如下:
CreateFileW函数00截断演示
三、漏洞是如何修复的?
这里选择使用jdk1.7.0_80
(JDK1.7最新版本),来观察漏洞如果被修复的。
我们继续按照原来漏洞触发的调用链重新跟踪一遍,跟踪到第二构造函数时,发现多了一个针对文件路径的检查,若检查结果为非法,则抛出异常Invalid file path
.
构造函数中检查文件路径
继续跟进,来到java.io.File
类的isInvalid
方法,发现该检查函数判断了路径中是否包含00字符串。(注意:java默认编码为Unicode,00字符串的Unicode编码为\u0000)。
文件路径检查函数
四、漏洞影响的版本范围
我们知道jdk1.7版本是部分版本存在漏洞的。但这里我们需要确定是哪个版本修复了这个漏洞。翻阅了JDK1.7多个版本代码,发现在JDK1.7.0_40(7u40)开始加上了对文件名是否存在\00字符的检查。也就是说 JDK1.7.0_40之前java是存在00截断的,而之后的版本就不存在了!
后面在官网的JDK 7u40的更新日志中也找到了关于00截断问题Bug ID,分别为JDK-8003992
和JDK-8011539
,具体链接放在了文末的参考文章里了。其实这两个是同一个Bug,官网也说明了它们重复了。
oracle官方更新日志
五、参考文章
JAVA /00文件路径截断漏洞与分析for windows并对.NET比较
JDK-8003992 : File and other classes in java.io do not handle embedded nulls properly
JDK-8011539 : File APIs Should Not Allow Null Bytes
[超站]友情链接:
四季很好,只要有你,文娱排行榜:https://www.yaopaiming.com/
关注数据与安全,洞悉企业级服务市场:https://www.ijiandao.com/
随时掌握互联网精彩
- 1 中秘关于深化全面战略伙伴关系的声明 7955897
- 2 外卖员吐槽尽量不要点黄焖鸡 7978048
- 3 教师指认猥亵现场坠亡 民警免予刑罚 7852370
- 4 秘鲁总统:已备美酒 欢迎光临! 7788659
- 5 俄罗斯喀山机场实施临时航空管制措施 7653806
- 6 俄媒:苏-57比中国歼-35更强 7559421
- 7 专家:本轮房价拐点全方位到来 7496734
- 8 俄罗斯开出停战先决条件 7388784
- 9 上海一路面的“操”字成网红打卡点 7295739
- 10 10月份主要经济指标回升明显 7127115