leonwxqian 发布的文章

开源了我之前做的jsfuzzer

在文章http://www.nul.pw/2017/07/17/245.html中我提到了jsfuzzer,现在开源其中一个。

这个jsfuzzer主要是因为funfuzz改起来实在是太困难,然后动手写的一个。可以生成可扩展的js结构,具体的输出样例可以参考github页面的sample-out.html。

Github page:
https://github.com/blastxiang/lucky-js-fuzz

由于是一个周末写的,代码风格十分烂,欢迎提出改进并修改。

同一类浏览器漏洞,看谁修复速度快

目前姑且当打了个平手,因为上报时间是周末

Microsoft Edge信息泄露漏洞

2017-8-18 05:29 已经复现出通用攻击代码,转开发修复中。
2017-8-18 01:51 表示复现,但是不知道有啥危害。
2017-8-17 15:35 感觉微软的测试大哥环境配错了,回复了具体环境。
2017-8-17 05:07 微软表示仍然复现不了。
2017-8-16 10:25 回复微软表示3台电脑均能复现,但是Edge表现诡异,仅在部分URL下复现
2017-8-16 01:47 微软表示无法复现,差点以为撞洞
2017-8-6 00:16 微软MSRC建单
2017-8-5 14:07 漏洞报告微软
————————————————————————————————————

360安全浏览器&极速浏览器信息泄露漏洞

2017-8-24 11:24 360表示,只在se8上生效,升级到se9了就没这问题了。事实也是如此,好吧,迟了一步。
2017-8-18 05:29 360仍然在验证中
2017-8-17 23:59 360仍然在验证中,验证状态已经保持了11天。期间无任何反馈。
2017-8-6 13:01 漏洞报告360

使用ChakraCore来Fuzz NScript(3)——构建符合NScript的Funfuzz脚本

NScript在对脚本进行evaluation之前会做一次熵判断,现在常用的做法是用一段长而无用的代码插在要评估的代码之前,达到熵的限值。而我们既然要用其他程序生成Funfuzz的结果再抛给NScript,看Funfuzz的输出:

/*FRC-fuzzSeed-58694107*/count=25; tryItOut("mathy5 = (function(x, y) { \"use strict\"; return ( ! (((( ~ (( + ( - y)) | 0)) | 0) & ( + ( + ((-(2**53) != (-0 >>> 0)) >>> 0)))) | 0)); }); testMathyFunction(mathy5, [(new Number(-0)), -0, '', null, /0/, (new Number(0)), (new String('')), false, '\\0', ({valueOf:function(){return '0';}}), (new Boolean(false)), '0', [], undefined, '/0/', 0.1, NaN, [0], ({valueOf:function(){return 0;}}), 0, (new Boolean(true)), (function(){return 0;}), ({toString:function(){return '0';}}), objectEmulatingUndefined(), true, 1]); ");
Running threw: ReferenceError: 'objectEmulatingUndefined' is not defined

/*FRC-fuzzSeed-58694107*/count=26; tryItOut("mathy2 = (function(stdlib, foreign, heap){ \"\";   var ff = foreign.ff;\n  var Int32ArrayView = new stdlib.Int32Array(heap);\n  function f(i0, d1)\n  {\n    i0 = i0|0;\n    d1 = +d1;\n    var i2 = 0;\n    var i3 = 0;\n    return +(((Int32ArrayView[((0xa5daeffa)+(0xffffffff)) >> 2])));\n  }\n  return f; })(this, {ff: encodeURIComponent}, new ArrayBuffer(4096)); testMathyFunction(mathy2, /*MARR*/[(void 0), false, (void 0), false, false, false, (yield -21), false]); ");
Plain eval!

/*FRC-fuzzSeed-58694107*/count=27; tryItOut("mathy4 = (function(stdlib, foreign, heap){ \"\";   var ff = foreign.ff;\n  function f(i0, i1)\n  {\n    i0 = i0|0;\n    i1 = i1|0;\n    i1 = (x * b);\n    {\n      i0 = (0xa9b3c950);\n    }\n    i1 = (0xfa09be60);\n    i1 = (i0);\n    return (((i0)-(i0)))|0;\n  }\n  return f; })(this, {ff: this}, new ArrayBuffer(4096)); testMathyFunction(mathy4, [-(2**53+2), 0, -0x100000001, 1, -Number.MAX_SAFE_INTEGER, Math.PI, -0, 0.000000000000001, 0x100000000, -Number.MAX_VALUE, 0x080000000, Number.MAX_VALUE, 1/0, 0x0ffffffff, -Number.MIN_VALUE, 42, -0x080000000, 0/0, (2**53), (2**53)+2, 0x100000001, -(2**53-2), -0x07fffffff, Number.MIN_VALUE, -Number.MIN_SAFE_INTEGER, 0x080000001, Number.MIN_SAFE_INTEGER, -(2**53), (2**53)-2, Number.MAX_SAFE_INTEGER, -0x0ffffffff, -0x100000000, -1/0, -0x080000001, 0x07fffffff, 1.7976931348623157e308]); ");

可以看到有很多不一样的,比如tryItOut,这个实际上是Funfuzz自己定义的,一个复杂的函数,它的内容如下:

垃圾回收
DUPTRY自定义FUZZ关键字处理,我们暂时不管
JS STRICT MODE为代码自动加上'use strict',不管,因为NScript根本不支持:

input:a=4;
evaluation result: 4

input:'use strict';a=4;
evaluation result: 4

创建new Function,将代码传给Function,如果有compile error,抛出compile error。
输出字面代码
执行代码

其实我们就简化一下,直接把它包装成一个try catch即可。

testMathyFunction(a,b)是另一个test类型的函数,调用a(b),并输出结果,我们对结果其实并不是非常感兴趣,简单起见可以在里面直接调用a(b)。

mathy*属于动态生成的函数,这部分需要提前拿到,并丢给NScript。

  if (rnd(5)) {
    if (rnd(8)) {
      s += "mathy" + i + " = " + makeMathFunction(6, b, i) + "; ";
    } else {
      s += "mathy" + i + " = " + makeAsmJSFunction(6, b) + "; ";
    }
  }

这里makeMathFunction和makeAsmJSFunction均不好直接移植,同时你也可以看出来为什么我对funfuzz的代码反映这么大,全是d、b、i这样的参数,根本没法明白地看清楚函数在干什么。

好在我们可以忽略它在干啥,在上面s += ‘mathy’…… 的时候,我们就可以为这里加上一个特殊的注释,然后抛出去。ChakraCore在收到带有这种注释的输出时,直接通过socket将数据发送给NScript备用。 NScript同时减少为了增熵数据的量,填入这里有用的函数代码。

function makeMathFunction(d, b, i)
{
  if (rnd(TOTALLY_RANDOM) == 2) return totallyRandom(d, b);

  var ivars = ["x", "y"];
  if (rnd(10) == 0) {
    // Also use variables from the enclosing scope
    ivars = ivars.concat(b);
  }
  return "(function(x, y) { " + directivePrologue() + "return " + makeMathExpr(d, ivars, i) + "; })";
}

除去这些,我们可以再看一下NScript对基础数据类型的支持:

input:var x = new RegExp('d');x;
evaluation result: /d/

input:var x = new Array('d');x;
evaluation result: d

input:var x = new Function('g(){}');x;
evaluation result: [object Function]

input:var x = new Image('');x;
evaluation result: ReferenceError: 'Image' is undefined

input:var x = new Int32Array('');x;
evaluation result: ReferenceError: 'Int32Array' is undefined

input:var x = new DataView('');x;
evaluation result: ReferenceError: 'DataView' is undefined

input:var x = new HTMLDivElement('');x;
evaluation result: ReferenceError: 'HTMLDivElement' is undefined

input:function SomeObj(x){log(x);}; SomeObj.y = "1"; SomeObj.prototype.oops = function(){log(SomeObj.y)}; var xx = new SomeObj("2"); xx.oops();
evaluation result: 2
evaluation result: 1
evaluation result: undefined


input:Infinity
evaluation result: Infinity

input:Error
evaluation result: [object Function]

input:Date
evaluation result: [object Function]

input:JSON
evaluation result: ReferenceError: 'JSON' is undefined

input:Math
evaluation result: [object Math]

input:Map
evaluation result: ReferenceError: 'Map' is undefined

input:Proxy
evaluation result: ReferenceError: 'Proxy' is undefined

input:String
evaluation result: [object Function]

input:TypedArray
evaluation result: ReferenceError: 'TypedArray' is undefined

input:parseFloat
evaluation result: [object Function]

input:parseInt
evaluation result: [object Function]

input:eval
evaluation result: [object Function]

input:typeof escape === typeof unescape
evaluation result: true

input:ArrayBuffer
evaluation result: ReferenceError: 'ArrayBuffer' is undefined

input:void 0
evaluation result: undefined

input:encodeURIComponent
evaluation result: [object Function]

可以发现

DOM相关的,大部分不支持
ES5+的不支持
JSON不支持

本文来自http://nul.pw,作者blast,转载请注明

然后,让我们看一些常见的对象:

input:for(var p in Math) log(p)
evaluation result: abs
evaluation result: acos
evaluation result: asin
evaluation result: atan
evaluation result: atan2
evaluation result: ceil
evaluation result: cos
evaluation result: exp
evaluation result: floor
evaluation result: log
evaluation result: max
evaluation result: min
evaluation result: pow
evaluation result: random
evaluation result: round
evaluation result: sin
evaluation result: sqrt
evaluation result: tan
evaluation result: E
evaluation result: LN10
evaluation result: LN2
evaluation result: LOG2E
evaluation result: LOG10E
evaluation result: PI
evaluation result: SQRT1_2
evaluation result: SQRT2
evaluation result: undefined

以及:

input:for(var p in Number) log(p)
evaluation result: prototype
evaluation result: constructor
evaluation result: MAX_VALUE
evaluation result: MIN_VALUE
evaluation result: NaN
evaluation result: POSITIVE_INFINITY
evaluation result: NEGATIVE_INFINITY
evaluation result: undefined

还有 String:

input:for(p in String) log(p)
evaluation result: fromCharCode
evaluation result: prototype
evaluation result: prototype
evaluation result: undefined

在这里我们看到了很多不支持的东西,因此只要攻击者能够去简单检测一下,就可以绕过这个evaluation……

input:for(p in window) log(p)
evaluation result: Date
evaluation result: Scripting
evaluation result: MSXML2
evaluation result: ADODB
evaluation result: WScript
evaluation result: Enumerator
evaluation result: WMI
evaluation result: WinHTTP
evaluation result: Shell
evaluation result: WSH
evaluation result: ActiveXObject
evaluation result: GetObject
evaluation result: d
evaluation result: ScriptEngineBuildVersion
evaluation result: deconcept
evaluation result: hex2bin
evaluation result: rc4
evaluation result: log
evaluation result: dump
evaluation result: p
evaluation result: undefined

input:for(p in Object) log(p)
evaluation result: prototype
evaluation result: undefined

input:for(p in Function) log(p)
evaluation result: prototype
evaluation result: undefined

input:for(p in Array) log(p)
evaluation result: prototype
evaluation result: undefined

以及arguments:

input:function p(){for(x in arguments) log(x)} p()
evaluation result: undefined

所以看起来要对Funfuzz再一次做一个大工程,把不兼容的内容全部换成兼容代码。

以子之矛,陷子之盾,何如?——使用ChakraCore来Fuzz NScript (2)

我在很早以前做了一个多进程的Fuzz程序,其实最终的实现很简单,就是类似一个使用socket互相通信的程序,好似QQ互相发送消息一样。Server和Client约定好一组消息,互相通信。

代码实现很简单,百度随便一搜就是一大堆,比如:http://blog.csdn.net/yaopeng_2005/article/details/6696105,可以直接拿来用。NScript处,因为我需要将待分析的代码传给rs函数,在我没有传递完成前,整个流程都可以停滞。因此NScript作为客户端,只需要一直等待Server传来的Fuzz代码,并从Buffer中取出代码,传递给rs即可。

nscript.png

callClient.cpp

// callClient.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <stdio.h>  
#include <Winsock2.h>  

#pragma comment(lib, "ws2_32.lib")   


void main()  
{  
    WORD wVersionRequested = MAKEWORD(1, 1);   
    WSADATA wsaData;  
    int err = WSAStartup(wVersionRequested, &wsaData);

    if (err != 0) 
    {  
        return;  
    }  

    if (LOBYTE(wsaData.wVersion ) != 1 || HIBYTE(wsaData.wVersion) != 1) 
    {   
        WSACleanup();  
        return;  
    }  

    for (int index = 0; ; index++)  
    {  
        SOCKET sockClient = socket(AF_INET,SOCK_DGRAM, 0);  

        int len = sizeof(SOCKADDR);  

        SOCKADDR_IN local;  
        local.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");   
        local.sin_family = AF_INET;   
        local.sin_port = htons(27015);   

        DWORD dwSize = MAX_PATH;
        char sendBuf[MAX_PATH] = { 0 };  
        sprintf(sendBuf, "%3d,", index); 

        //client(ChakraCore) shall send a file name to client. incase of buffer has a limit of 64kb.
        strcat(sendBuf, "c:\\test\\wow.js"); 
        sendto(sockClient, sendBuf, strlen(sendBuf) + 1, 0, (SOCKADDR*)&local, len);  

        /*char recvBuf[50];  
        recvfrom(sockClient,recvBuf,50,0,(SOCKADDR*)&local,&len);  
        printf("my reply is : %s\n",recvBuf);  
        printf("%s\n",inet_ntoa(local.sin_addr));  
        */

        closesocket(sockClient);  
        Sleep(2000);       
        WSACleanup();  
    }  
}  

callServer.cpp

// callServer.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <stdio.h>  
#include <Winsock2.h>  

#pragma comment(lib, "ws2_32.lib")   

void main()  
{  
    WORD wVersionRequested = MAKEWORD(1, 1);
    WSADATA wsaData;  
    int err = WSAStartup(wVersionRequested, &wsaData);  

    if (err != 0) {  
        return;  
    }  

    if ( LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) {   
        WSACleanup();  
        return;  
    }  

    SOCKET sockSrv = socket(AF_INET,SOCK_DGRAM, 0);  

    int len = sizeof(SOCKADDR);  

    SOCKADDR_IN from;     
    SOCKADDR_IN local;   
    local.sin_addr.S_un.S_addr = htonl(INADDR_ANY);   
    local.sin_family = AF_INET;   
    local.sin_port = htons(27015);   

    int bindResult = bind(sockSrv, (SOCKADDR*)&local, len);  
    while(1)  
    {  
        DWORD dwSize = MAX_PATH;
        char recvBuf[MAX_PATH];  

        //server(NScript) will receive a buffer contains filename of generated fuzzer.

        recvfrom(sockSrv, recvBuf, dwSize, 0, (SOCKADDR*)&from, &len);

        printf("%s\n", recvBuf);  
        printf("%s\n", inet_ntoa(local.sin_addr));  

        /*char sendBuf[50];  
        sprintf(sendBuf, "Welcome %s to here!", inet_ntoa(from.sin_addr));    
        sendto(sockSrv, sendBuf, strlen(sendBuf) + 1, 0, (SOCKADDR*)&from, len);  
        */

        Sleep(2000);  
    }  
    closesocket(sockSrv);  

    WSACleanup();  
}  

而Client端如果发生异常,处理起来就更容易了,使用SetUnhandledExceptionFilter可以设置TopLevelExceptionFilter。这是一个定义为

LONG WINAPI UnhandledExceptionFilter(__in struct _EXCEPTION_POINTERS* pExceptionInfo)

的函数,可以在pExceptionInfo中取到异常信息并记录。

所以,这里的实现比较容易,
1、在NScript侧,WinMain进入时,调用
SetUnhandledExceptionFilter()

int WinMain(...)
{
    ...
    LPTOP_LEVEL_EXCEPTION_FILTER originalFilter = SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);
    ...
}

2、MyUnhandledExceptionFilter里面可以收集异常信息并告警

LONG WINAPI MyUnhandledExceptionFilter(__in struct _EXCEPTION_POINTERS* pExcepInfo)
{
    DWORD dwCrashPid = GetCurrentProcessId();
    DWORD dwCrashTid = GetCurrentThreadId();

    pExcepInfo->ExceptionRecord.ExceptionCode;
    pExcepInfo->ExceptionRecord.ExceptionAddress;

    alert!
    ...

}

3、NScript部分,循环等待,并在接受到数据时调用__rs

4、ChakraCore部分

JsValueRef __stdcall WScriptJsrt::EchoCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState)
{
    for (unsigned int i = 1; i < argumentCount; i++)
    {
        ……
                if (i > 1)
                {
                    wprintf(_u(" "));
                }

                ////////////////////////////////////////////////////////
                SEND CODE HERE
                ////////////////////////////////////////////////////////

                wprintf(_u("%ls"), str.GetWideString());
            }
        }

        ……
    }
……