NScript loader也一起开源了
https://github.com/blastxiang/windows-defender-nscript-loader
之前截图中用来调试NScript的工程。
https://github.com/blastxiang/windows-defender-nscript-loader
之前截图中用来调试NScript的工程。
在文章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
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再一次做一个大工程,把不兼容的内容全部换成兼容代码。
我在很早以前做了一个多进程的Fuzz程序,其实最终的实现很简单,就是类似一个使用socket互相通信的程序,好似QQ互相发送消息一样。Server和Client约定好一组消息,互相通信。
代码实现很简单,百度随便一搜就是一大堆,比如:http://blog.csdn.net/yaopeng_2005/article/details/6696105,可以直接拿来用。NScript处,因为我需要将待分析的代码传给rs
函数,在我没有传递完成前,整个流程都可以停滞。因此NScript作为客户端,只需要一直等待Server传来的Fuzz代码,并从Buffer中取出代码,传递给rs
即可。
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());
}
}
……
}
……