AUTHOR:kj021320

JAVA WEB文件上传 是取用户提交的文件名字而不是从random取随机器数字 就值得注意了

跟以前ASP+ADO.STREAM的上传漏洞同理

因为 WINDOWS平台不支持 带有/00字符的文件目录或者文件名字

所以 JAVA虽然与平台无关 但是他底层操作的时候必定是调用平台的API 所以就会难免有些与平台关联的

安全隐患或者漏洞令 JAVA语言开发者不能一一顾及周全 OK 下面是一个简单的代码例子

String filepath="c://kj021320.jsp"+(char)0+".txt";

System.out.println(filepath);

FileOutputStream fos=new FileOutputStream(filepath);

fos.write("hello".getBytes());

fos.close();

byte [] b=new byte[100];

int i=0;

FileInputStream fis=new FileInputStream(filepath);

i=fis.read(b);

out.println(new String(b,0,i));

fis.close();

然后看看有什么输出?

OK 以上都做了代码的测试 下面我们来分析JVM最终调用流程

来从源到内部核心实现

首先 分析FileOutputStream 类构造函数

//这个构造方法调用了 另一个构造函数

public FileOutputStream(String name) throws FileNotFoundException {

this(name != null ? new File(name) : null, false);

}

//以下就是被调用另外构造函数的具体实现

public FileOutputStream(File file, boolean append)throws FileNotFoundException

{

String name = (file != null ? file.getPath() : null);

SecurityManager security = System.getSecurityManager();

if (security != null) {

    security.checkWrite(name);//做安全检查 JAVA沙箱里面是否有权限写

}

if (name == null) {

    throw new NullPointerException();

}

fd = new FileDescriptor();

this.append = append;

if (append) {

    openAppend(name);//调用 openAppend

} else {

    open(name); //调用open

}

}

分析完了上面 FileOutputStream类里面的2个构造方法 我们可以继续跟踪进去

看看 open openAppend的实现

private native void open(String name) throws FileNotFoundException;

private native void openAppend(String name) throws FileNotFoundException;

不过很让我们遗憾!两个方法都是本地代码实现~ 到这里就结束了吗?不~  JDK6以前的源代码SUN公司是没有公布的!但是我们可以去下载

OPENJDK ​​http://openjdk.java.net/​​​ 里面的全部源代码估计都 大同小异的!

打开 openjdk源代码 openjdk/j2se/src/windows/native/java/io/FileOutputStream_md.c 目录下面会有

FileOutputStream 这个类的JNI实现

Java_java_io_FileOutputStream_open(JNIEnv *env, jobject this, jstring path) {

    fileOpen(env, this, path, fos_fd, O_WRONLY | O_CREAT | O_TRUNC);

}

调用了 fileOpen 继续跟踪进去,这个函数是在io_util_md.c 这个文件里面实现的

void fileOpen(JNIEnv *env, jobject this, jstring path, jfieldID fid, int flags){

    jlong h = winFileHandleOpen(env, path, flags);

    if (h >= 0) {

        SET_FD(this, h, fid);

    }

}

看来又调用了 winFileHandleOpen 这个函数 估计里面肯定也是调用WIN32API来创建文件 不用说了,继续

jlong winFileHandleOpen(JNIEnv *env, jstring path, int flags){

    const DWORD access =

 (flags & O_RDWR)   ? (GENERIC_WRITE | GENERIC_READ) :

 (flags & O_WRONLY) ?  GENERIC_WRITE :

 GENERIC_READ;

    const DWORD sharing =

 FILE_SHARE_READ | FILE_SHARE_WRITE;

    const DWORD disposition =

 /* Note: O_TRUNC overrides O_CREAT */

 (flags & O_TRUNC) ? CREATE_ALWAYS :

 (flags & O_CREAT) ? OPEN_ALWAYS   :

 OPEN_EXISTING;

    const DWORD  maybeWriteThrough =

 (flags & (O_SYNC | O_DSYNC)) ?

 FILE_FLAG_WRITE_THROUGH :

 FILE_ATTRIBUTE_NORMAL;

    const DWORD maybeDeleteOnClose =

        (flags & O_TEMPORARY) ?

        FILE_FLAG_DELETE_ON_CLOSE :

        FILE_ATTRIBUTE_NORMAL;

    const DWORD flagsAndAttributes = maybeWriteThrough | maybeDeleteOnClose;

    HANDLE h = NULL;

    if (onNT) {

        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);

    }

    if (h == INVALID_HANDLE_VALUE) {

        int error = GetLastError();

        if (error == ERROR_TOO_MANY_OPEN_FILES) {

            JNU_ThrowByName(env, JNU_JAVAIOPKG "IOException",

                            "Too many open files");

            return -1;

        }

        throwFileNotFoundException(env, path);

        return -1;

    }

    return (jlong) h;

}

//简单看一下以上的代码 最终都是调用 CreateFileW 来创建一个文件

而他采用 pathToNTPath 这个函数来对路径进行转换里面也没有对/00字符过滤导致在windows平台下面这个漏洞

OK 那现在我们来看看.NET的表现! 首先分析他的文件操作类

打开一个 Reflector.exe 反编译器 FileStream的构造函数太多我就不贴出来了 他最终会调用Path类的方法进行验证

internal static void CheckInvalidPathChars(string path)

{

    if (-1 != path.IndexOfAny(InternalInvalidPathChars))

    {

        throw new ArgumentException(Environment.GetResourceString("Argument_InvalidPathChars"));

    }

}

以上代码 如果包含了非法字符!会抛出一个异常~

看看 InternalInvalidPathChars 是怎么定义的

InternalInvalidPathChars = new char[] { '"', '<', '>', '|', '/0', '/b', '/x0010', '/x0011', '/x0012', '/x0014', '/x0015', '/x0016', '/x0017', '/x0018', '/x0019' };

呵呵 看来 .NET 在 MS平台上面做得更充分

呵呵! YY一下

WINDOWS:"555~~JAVA你不懂我的心!"

JAVA:"汗~~~......"

.NET:"我的地盘我做主...嘎嘎"