当前位置:编程问答 > JAVA >

关于Java getSoundBank函数堆栈溢出漏洞

Inking的自留地

:: 漏洞原理

确切的说,错误函数为Java的Native方法Java.com.sun.media.sound.HeadspaceSoundbank.nOpenResource。该函数在拷贝文件路径时没有检查字符串大小而直接进行了拷贝,最终导致了栈溢出:

// $$kk: 04.11.99: we are never calling XFileClose!!
JNIEXPORT jlong JNICALL
    Java_com_sun_media_sound_HeadspaceSoundbank_nOpenResource(JNIEnv* e, jobject thisObj, jstring path)
{
    XFILE file = NULL;
    XFILENAME xfilename;

    const char *str = (*e)->GetStringUTFChars(e, path, 0);

    TRACE0("Java_com_sun_media_sound_HeadspaceSoundbank_nOpenResource. ");

    XConvertNativeFileToXFILENAME((void *)str, &xfilename);
    file = XFileOpenResource(&xfilename, TRUE);

    (*e)->ReleaseStringUTFChars(e, path, str);

    TRACE1("Java_com_sun_media_sound_HeadspaceSoundbank_nOpenResource completed, returning %lu. ", file);

    return (jlong) (INT_PTR) file;
}

// given a native file spec (FSSpec for MacOS, and C string for WinOS, fill in a XFILENAME
void XConvertNativeFileToXFILENAME(void *file, XFILENAME *xfile)
{
    if (xfile)
{
     XSetMemory(xfile, (INT32)sizeof(XFILENAME), 0);
}
    if (file)
{
#if USE_HAE_EXTERNAL_API == TRUE
     {
   void *dest;

   dest = &xfile->theFile;
   HAE_CopyFileNameNative(file, dest);
     }
#else
#if X_PLATFORM == X_MACINTOSH
     xfile->theFile = *((FSSpec *)file);
#endif
#if ( (X_PLATFORM == X_WINDOWS)     ||
    (X_PLATFORM == X_WIN_HARDWARE) ||
    (X_PLATFORM == X_BE)     ||
    (X_PLATFORM == X_SOLARIS)    ||
    (X_PLATFORM == X_LINUX)    ||
    (X_PLATFORM == X_NAVIO) )
     XStrCpy((char *)xfile->theFile, (char *)file);
#endif
#endif
}
}

    struct XFILENAME
    {
// public platform specific
#if X_PLATFORM == X_MACINTOSH
XFILE_HANDLE     fileReference;
FSSpec     theFile;
#endif
#if ( (X_PLATFORM == X_WINDOWS)     ||
   (X_PLATFORM == X_WIN_HARDWARE) ||
   (X_PLATFORM == X_WEBTV)    ||
   (X_PLATFORM == X_BE)     ||
   (X_PLATFORM == X_SOLARIS)    ||
   (X_PLATFORM == X_LINUX)    ||
   (X_PLATFORM == X_NAVIO) )
/* $$fb 2002-02-14: itanium port */
XFILE_HANDLE     fileReference;
char     theFile[FILE_NAME_LENGTH]; // "C" string name for path
#endif

// private variables. Zero out before calling functions
INT32     fileValidID;
XBOOL     resourceFile;

XPTR     pResourceData; // if file is memory based
INT32     resMemLength; // length of memory resource file
INT32     resMemOffset; // current offset of memory resource file
XBOOL     readOnly;   // TRUE then file is read only
XBOOL     allowMemCopy; // if TRUE, when a memory based resource is
// read, a copy will be created otherwise
// its just a pointer into the larger memory resource
// file
XFILE_CACHED_ITEM memoryCacheEntry;
XFILERESOURCECACHE *pCache;   // if file has been cached this will point to it
    };
    typedef struct XFILENAME XFILENAME;
    typedef void *     XFILE;

// standard strcpy
// Copies C string src into dest
char * XStrCpy(char *dest, char *src)
{
    char *sav;

    sav = dest;
    if (src == NULL)
{
     src = "";
}
    if (dest)
{
     while (*src)
   {
      *dest++ = *src++;
   }
     *dest = 0;
}
    return sav;
}

以上FILE_NAME_LENGTH在windows下为_MAX_PATH,也就是256,所以只要url->filename大于256就溢出了。还有一个关键点:

HeadspaceSoundbank(URL url) throws IOException {

if(Printer.trace) Printer.trace("HeadspaceSoundbank: constructor: url: " + url);

String protocol = url.getProtocol();
if ( ! (protocol.equals("file")) ) {

     InputStream stream = url.openStream();
     try {
     initialize(stream, false);
     } catch (IllegalArgumentException e) {
     stream.close();
     throw e;
     }
} else {
     String path = url.getFile();
     initialize(path); // 来到这里
}

if(Printer.trace) Printer.trace("HeadspaceSoundbank: constructor: url: " + url + " completed");
}

:: 漏洞利用

漏洞的原理虽然简单,但是利用却不是那么容易。首先,虽然漏洞是通过IE触发的,但是最终执行漏洞函数的进程为java.exe,所以无法通过简单的JavaScript堆喷射来进行利用。如果要做到比较通用,直接在java.exe内进行java堆喷射会比较好,但是事实上问题也挺多的,最麻烦的就是java堆的大小默认只有64M,而且如果进行gc的频率过高,jvm会直接抛出异常。所以经过几次尝试后,暂时放弃了这条路。还有一条路是常规的栈溢出利用。但是这个方法也有几个问题。比较庆幸的是我们有足够大的空间来写Shellcode,可以直接用JMP ESP来进行Shellcode的定位。最棘手的是字符编码的问题,在利用的过程中,跳转地址和Shellcode的字节大小都必须在0x80以内(java现学的,可能存在盲点),否则会因为字符集的问题而转的乱七八糟的。Shellcode在0x80内比较好解决,因为esp刚好指向Shellcode,用alpha2编码可以使得Shellcode最终全部为可见字符。JMP ESP(CALL ESP)就比较麻烦了,因为比较稳定的opcode地址很多都在kernel32和ntdll上,而这两者的加载基址又比较郁闷(比如kernel32.dll为0x7c800000),又不能去找类似于shell32.dll这种不是很稳定的动态链接库,所以最后还是锁定了java自身的几个动态链接库,希望能通过java代码得到版本号,然后按照不同版本号来计算不同的opcode地址。

:: PoC

PoC中的opcode随便找的,测试环境为xp sp2,jre 6.0.160.1

////////////////////////////////////////////////////////////////////////////////////////////////

import java.applet.Applet;
import java.awt.Graphics;
import java.io.CharArrayWriter;
import java.io.PrintStream;
import java.lang.reflect.Array;
import java.net.URL;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.Soundbank;
import javax.sound.midi.Synthesizer;

public class test extends Applet
{
pub

补充:软件开发 , Java ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,