
ÒýÑÔ
ÔÚ±¾ÎÄÖУ¬ÎÒÃǽ«Ïò¶ÁÕß½éÉÜÔÚ¹¥»÷Internet ExplorerºÍEdgeä¯ÀÀÆ÷ʱ¿ÉÓÃÓÚÈÆ¹ýMicrosoftµÄ¿ØÖÆÁ÷·À»¤(CFG)µÄ·½·¨¡£ÎÒÃÇÒÔǰµÄ¸ÅÄîÑéÖ¤ÐÔÖʵÄ©¶´ÀûÓôúÂëÊÇͨ¹ý¸²¸Ç¶ÔÏóµÄº¯ÊýÖ¸ÕëÀ´ÊµÏֵġ£µ«ÊÇ£¬µ±Óöµ½CFGʱ£¬ÕâÖÖ·½·¨¾Í²»Ì«ºÃʹÁË¡£ÎÒÃǼÙÉè¹¥»÷ÕßÒѾ»ñµÃÁ˶ÁдÄÚ´æÈ¨ÏÞ¡£
±³¾°ÖªÊ¶
CFGÊÇ΢Èí½üÀ´ÎªWindowsϵͳÌí¼ÓÒ»ÖÖ°²È«·À»¤»úÖÆ¡£¸Ã»úÖÆÍ¨¹ý¼ä½Óµ÷ÓÃ/Ìø×ªÖ¸ÁîµÄÄ¿±êµØÖ·µÄ¸ßЧ¼ì²éÀ´Ìṩ±£»¤¡£Èç¹ûÄúÏ£Íû½øÒ»²½Á˽âCFGµÄ¸ü¶àÏêÇ飬¿ÉÒÔ²ÎÔIJο¼ÎÄÏ×[1][2][3]£¬ËùÒÔÎÒÃDz»×öÉîÈëϸÖµĽ²½â¡£

ËäÈ»¸Ã»º½â»úÖÆÔö¼ÓÁË¿ØÖÆÁ÷½Ù³ÖÐ͹¥»÷µÄÄѶȣ¬µ«ÊÇCFG±¾Éí²¢²»ÍêÃÀ¡£¸Ã¼¼ÊõµÄÉè¼ÆÄ¿±êÊDZ£»¤¼ä½Óµ÷ÓúÍÌø×ª£¬ËùÒÔ£¬Ã»ÓÐΪ¶ÑÕ»(¼´ROPÈÔÊÇ¿ÉÄܵÄ)Ìṩ±£»¤¡£´ËÍ⣬ֵµÃ×¢ÒâµÄÊÇ£¬ÕâÊÇÒ»¸ö±àÒëʱ²å×®¼¼Êõ£¬ÐèÒªÖØÐ±àÒëÔ´´úÂë¡£¾¡¹Ü΢ÈíÏÖÔÚµÄÐí¶à¶þ½øÖÆÎļþ¿ÉÒÔÊÜÒæÓÚCFG£¬µ«»¹ÓкܶàÆäËû³ÌÐò²»ÊÇÀûÓÃCFG±£»¤»úÖÆ±àÒëµÄ¡£
Chakra JIT
Chakra JIT¸ºÔðΪ¶à´Îµ÷Óõĺ¯ÊýºÍÑ»·Éú³ÉÓÅ»¯µÄJIT´úÂë¡£Õâ¸ö¹ý³Ì·ÖΪ¶à¸ö½×¶ÎÍê³É£¬ÆäÖÐFull
JIT CompilerºÍGarbage Collection½×¶ÎÊÇÔÚºǫ́Ïß³ÌÖнøÐеġ£Èç¹ûÄúÓÐÐËȤµÄ»°£¬¿ÉÒÔ´ÓMSDNÉÏÕÒµ½Ïà¹ØµÄ¹¤×÷Á÷³ÌºÍ¸÷ÖÖͼÊÍ¡£
JIT¹¤×÷Á÷³Ì

ÎÒÃǹØ×¢µÄÖØµãÊÇFull JIT Compiler½×¶Î£¬Ëü¸ºÔð»ñÈ¡×Ö½ÚÂëºÍÊä³ö±¾µØ´úÂë¡£Õë¶Ôµ¥¸öº¯Êý»òÑ»·µÄ¸ß¼¶´¦ÀíÊÇÔÚFunc::Codegen()ÖнøÐеġ£Ê×ÏÈ£¬Ëü»áÉú³É×Ö½ÚÂëµÄÖмä±íʾ(IR)¡£È»ºó£¬ÕâЩIR½«±»×ª»»Èô¸É´Î£ºÓÅ»¯¡¢¼Ä´æÆ÷·ÖÅä¡¢prologºÍepilogµÈ¡£Ò»µ©IR×¼±¸¾ÍÐ÷£¬¾Í»á±»Encoder::Encode()±àÂëΪ±¾µØ´úÂë¡£
// https://github.com/Microsoft/ ChakraCore/blob/master/lib/Backend/Encoder.cpp#L15 void Encoder::Encode() { NoRecoverMemoryArenaAllocator localAlloc(_u ("BE-Encoder"), m_func->m_alloc-> GetPageAllocator(), Js::Throw::OutOfMemory); m_tempAlloc = &localAlloc; ... m_encodeBuffer = AnewArray(m_tempAlloc, BYTE, m_encodeBufferSize); ... } |
ʵ¼ÊÉÏ£¬ÕæÕýÉú³Éʵ¼Ê±¾µØ´úÂëµÄÈÎÎñÊÇÓÉEncoderÍê³ÉµÄ¡£Ê×ÏÈ£¬Ëü»á·ÖÅäm_encodeBufferÀ´ÁÙʱ´æ·Å±¾µØ´úÂë¡£µ±ËùÓб¾µØÖ¸Áî±»·¢Ë͵½m_encodeBufferÖ®ºó£¬Encoder½«¶Ô¸Ã»º³åÇø½øÐÐÖØÐ¶¨Î»£¬½«Æä¸´ÖƵ½read-only-executeÄڴ棬²¢°´ÕÕCFGµÄÒªÇó´¦Àíµ÷ÓÃÄ¿±ê¡£´Ëʱ£¬¸ÃÁÙʱ»º³åÇø¾Í²»ÔÙʹÓã¬ËùÒÔ¿ÉÒÔÊÍ·ÅÁË¡£
// https://github.com/Microsoft/ ChakraCore/blob/master/lib/Backend/Encoder.cpp#L294 ... m_encoderMD.ApplyRelocs((size_t) workItem->GetCodeAddress()); workItem->RecordNativeCode(m_func, m_encodeBuffer); m_func->GetScriptContext()->GetThreadContext()-> SetValidCallTargetForCFG((PVOID) workItem->GetCodeAddress()); ... |
×¢Ò⣬һµ©´úÂë±»¸´ÖƵ½¿ÉÖ´ÐÐÄÚ´æºó£¬¾ÍºÜÄÑÐÞ¸ÄÁË¡£µ«ÊÇ£¬µ±EncoderÔÚÕâ¸öÁÙʱ»º³åÆ÷ÖÐÉú³É±¾µØ´úÂëʱ£¬ÊÇÎÞ·¨·ÀÖ¹¹¥»÷ÕßÀûÓÃдÈëÄÚ´æÈ¨ÏÞÀ´¸ü¸ÄÁÙʱ»º³åÆ÷ÖеĴúÂëµÄ¡£ÓÉÓÚJIT½ø³ÌλÓÚºǫ́Ïß³ÌÖУ¬ËùÒÔJavaScriptÏß³ÌÈÔÈ»¿ÉÒÔÕý³£ÔËÐС£¹¥»÷ÕßµÄÄѵãÊÇÕÒµ½¸ÃÁÙʱ»º³åÇø£¬²¢ÔÚEncoderÔËÐеļ«¶Ìʱ¼äÄÚÍê³ÉÏàÓ¦µÄÐÞ¸ÄÈÎÎñ¡£
ÈÆ¹ýCFG·À»¤
¼ÈÈ»ÒѾ֪µÀÁËÐÞ¸ÄJIT´úÂëµÄ»ù±¾·½·¨£¬ÏÂÃæ¾ÍÈÃÎÒÃǸ¶ÖîÐж¯£¬ÒÔ±ãÉè·¨ÈÆ¹ýCFG¡£
ÎÒÃǵĹý³Ì·ÖΪÈý²½£º
´¥·¢JIT¡£
²éÕÒÁÙʱµÄ±¾µØ´úÂ뻺³åÇø¡£
Ð޸Ļº³åÇøµÄÄÚÈÝ¡£
µ±È»£¬ÕâÀïÒþº¬µÄ×îºóÒ»²½ÊÇÖ´ÐÐJIT´¦Àí¹ýµÄ´úÂë¡£
´¥·¢JIT
µÚÒ»²½£¬Ò²ÊÇ×î¼òµ¥µÄÒ»²½£¬¾ÍÊÇ´¥·¢JIT£¬ÈÃËü¿ªÊ¼¶ÔÒ»¸öº¯Êý½øÐбàÂ롣ΪÁËʹµÚ¶þ²½±äµÃ¸üÈÝÒ×һЩ£¬ÎÒÃÇÏ£Íûº¯ÊýµÄ´úÂë¶àһЩ£¬ÒÔ±ãÎÒÃÇÓÐ×ã¹»µÄʱ¼äÔÚÄÚ´æÖÐѰÕÒ¸ÃÁÙʱ»º³åÇø¡£µ±È»£¬º¯ÊýÖеľßÌåÖ¸ÁîÊÇÎ޹ؽôÒªµÄ¡£
var code = "var i = 10; var j = 1; "; for (var i = 0; i < 6000; i++) { code += "i *= i + j.toString();"; } code += "return i.toString();" f = Function(code); for (var i = 0; i < 1000; i++) { // trigger jit f.call(); } |
²éÕÒ±¾»ú´úÂ뻺³åÇø
Ò»µ©ºǫ́Ï߳̽øÈëEncoder::Encode()£¬ÎÒÃÇÐèÒª¿ìËÙÕÒµ½ÁÙʱ±¾µØ´úÂ뻺³åÇø¡£·¢ÏÖ»º³åÇøµÄÒ»ÖÖ·½·¨ÊÇ£¬ÕÒµ½¸ø¸Ã»º³åÇø·ÖÅäÄÚ´æµÄÒ³·ÖÅäÆ÷£¬È»ºóÖð¸ö²é¿´Ëü·ÖÅäµÄÄÚ´æ¶Î¡£ÎÒÃÇ×¢Òâµ½£¬¿ÉÒÔÏÈÕÒµ½ThreadContext£¬È»ºóÕÒµ½¸Ãºǫ́Ï̵߳ÄBackgroundJobProcessor£¬ÕâÑù¾Í¿ÉÒÔÕÒµ½¸ÃÒ³Ãæ·ÖÅäÆ÷µÄÒýÓÃÁË¡£
// find the ThreadContext using ThreadContext::globalListLast var tctx = readN(jscript9Base + 0x00349034, 4); // BackgroundJobProcessor var bgjob = readN(tctx + 0x3b0, 4); // PageAllocator var pgalloc = bgjob + 0x1c; |
PageAllocator¾ßÓÐÈô¸ÉÒÑ·ÖÅä¶ÎµÄÁÐ±í¡£ÓÉÓÚ¾JIT´¦Àí¹ýµÄº¯Êý»á±ä´ó£¬ËùÒÔ¸ÃÁÙʱ±¾µØ´úÂ뻺³åÆ÷Ò²½«ºÜ´ó¡£ËùÒÔ£¬Í¨¹ý¼ì²élargeSegmentsÁÐ±í£¬ÎÒÃǾͿÉÒÔÇáËÉÕÒµ½¸ÃÄÚ´æ¶ÎÁË¡£ÎÒÃÇ¿ÉÒÔʹÓÃÒ»¸öwhileÑ»·£¬ÕâÑùÒ»Ö±µÈµ½Õâ¸ölargeSegmentsÁбí±äΪ·Ç¿Õ£¬È»ºó½øÈë×îºóÒ»²½¡£
while (true) { // read largeSegments list var largeseg = readN(pgalloc + 0x24, 4); // check if the list was empty if (largeseg == pgalloc + 0x24) continue; // get the address of the actual data var page = readN(largeseg + 8 + 8, 4); if (page == 0) continue; break; } |
Ð޸IJ¢ÔËÐÐ
ÏÖÔÚ£¬¼ÈÈ»ÒѾ֪µÀÁËÁÙʱ±¾µØ´úÂ뻺³åÇøµÄλÖã¬ÄÇô½ÓÏÂÀ´¾Í¿ÉÒÔÐÞ¸ÄÆäÄÚÈÝÀ´×¢ÈëshellcodeÁË¡£µ±È»£¬°´Àí˵ֻҪʹÓÃÎÒÃǵÄshellcode¸²¸Ç»º³åÇøµÄÄÚÈݾÍÐÐÁË£¬µ«ÊÇʵ¼ÊÉÏÒª±ÈÕâ¸ö¹ý³ÌÒª¸´ÔӵĶ࣬ÒòΪÎÒÃDZØÐë±ÜÃ⸲¸ÇδÀ´ÔÚÖØ¶¨Î»²½ÖèÖн«ÒªÐ޸ĵÄÈκÎÄÚÈÝ¡£ÒòΪÓÃÓÚ´¥·¢JITµÄº¯ÊýÐèÒª¶à´Îµ÷ÓÃtoString()£¬Í¬Ê±»¹Òª±ÜÃâÖØ¶¨Î»µÄÓ°Ï죬ËùÒÔ£¬Êµ¼ÊÉÏ¿ÉÓÃÓÚshellcodeµÄ¿Õ¼ä²¢²»³äÔ£¡£
ËäÈ»×î¼Ñ֮ѡÊÇÐÞ¸ÄÒª½øÐÐJIT´¦ÀíµÄº¯Êý£¬µ«ÕâÀïÑ¡ÔñʹÓÃfirst-stage shellcode£¬ËüÖ»ÊǼòµ¥µ÷ÓÃVirtualProtect£¬È»ºóÌø×ªµ½ÎÒÃǵÄsecond-stage
shellcode¡£Õâ¸öfirst-stage shellcodeͨ³£ÊǷdz£Ð¡(Ö»ÓÐ20¸ö×Ö½Ú)µÄ¡£ËùÒÔ
£¬ÎÒÃÇ¿ÉÒÔ°Ñfirst-stage shellcode·Åµ½¾àÕâ¸ö»º³åÇø±È½Ï½üµÄµØ·½£¬È»ºóÔÚÕâ¸ö»º³åÇøµÄÆðʼλÖ÷ÅÉÏÒ»¸ö½ü×ªÒÆÖ¸Á´Ó¶øÌø×ªÖÁ¸Ã´úÂë¡£
ÕâÑùµÄ»°£¬ÎÒÃǵÄsecond-stage shellcode¿ÉÒÔÊÇÈκγ¤¶È£¬ËùÒÔÔÚÎÒÃǵÄ©¶´ÀûÓôúÂëÖУ¬Ê¹ÓÃÁËÒ»¸ömetasploitÉú³ÉshellcodeÀ´Ö´ÐÐnotepad.exe¡£Êµ¼ÊÉÏ£¬Õâ¸ösecond-stage
shellcode»¹¿ÉÒÔÈÆ¹ý±£»¤Ä£Ê½(ɳÏä)¡£
ÔÚÐ޸ĺÃÁÙʱ»º³åÇøÖ®ºó£¬ÎÒÃǽ«½øÈë×îºóÒ»²½£¬¾ÍÊǽøÐеȴý£¬Ö±µ½JIT´¦ÀíÍê³É²¢Ö´ÐÐÐ޸ĺóµÄJIT´úÂ롣Ϊ´Ë£¬Äã¿ÉÒÔ²»¶Ïµ÷ÓÃÄ¿±êº¯Êý£¬Ö±µ½ÄãµÄshellcodeµÃµ½Ö´ÐÐΪֹ¡£
for (var i = 0; i < 1000; i++) { // call overwritten jit block f.call(); } |
©¶´ÀûÓÃ
ΪÁËÑÝʾÕâÖÖÈÆ¹ý¼¼Êõ£¬ÎÒÃÇ½è¼øÁË´ËǰÓÃÓÚWindows 10ÉϵÄInternet Explorer 11µÄÀûÓôúÂ룬²¢½øÐÐÁËÏàÓ¦µÄÐ޸ġ£ÆäÖУ¬»ñÈ¡¶ÁдÄÚ´æÈ¨Ï޵ĴúÂëûÓиı䣬µ«ÊÇÔÚÖ´ÐÐÎÒÃǵÄshellcodeµÄʱºò£¬Ê¹ÓõÄÊÇJIT´úÂ븲¸Ç¼¼Êõ£¬¶ø²»ÊÇÈ¥¸²¸Ç´¥·¢CFGµÄº¯ÊýÖ¸Õë¡£

Äã¿ÉÒÔÔÚhttps://github.com/theori-io/jscript9-typedarray-cfgÒ³ÃæÖÐÕÒµ½×îÖյĸÅÄîÑé֤©¶´ÀûÓôúÂë¡£
°²È«Ó°Ïì
ÓÉÓÚ¸ÃCFGÈÆ¹ý©¶´µÄÓ°Ïì½öÏÞÓÚ¹¥»÷ÕßÒѾ»ñµÃÁ˶ÁдÄÚ´æÈ¨ÏÞµÄÇé¿ö£¬Òò´ËÆäʵÓÃÐÔÔÚÏÖʵÖпÉÄÜ»áÊܵ½Ò»Ð©ÏÞÖÆ¡£ÐèÒªÒýÆð¾¯¾õµÄÊÇ£¬Õâ¸öÈÆ¹ý·½·¨¾ßÓÐÄÚÖõ½Chakra
JIT¼Ü¹¹ÄÚ²¿µÄÓÅÊÆ£¬ÕâÒâζ×ÅËü¿ÉÄܺÜÄÑÐÞ²¹£¬²¢ÇÒ²»»áÊܵ½ÏñÓ¢ÌØ¶ûµÄCETÕâÑùµÄδÀ´»º½â´ëÊ©µÄÓ°Ïì¡£
²¹¾È´ëÊ©
΢ÈíÒѾ³Ðŵ¶ÔChakraCore½øÐÐÏàÓ¦µÄÐ޸ģ¬ÒÔ»º½âÎÒÃÇ·¢ÏÖµÄCFGÈÆ¹ý(ÒÔ¼°ÆäËûCVE²¹¶¡)©¶´ËùÔì³ÉµÄÍþв¡£ËûÃǵĻù±¾Ë¼ÏëÊÇ£¬ÔÚ±àÂëÆ÷±àÂëÖ¸Áîʱ¼ÆËãУÑéºÍ£¬È»ºóÔÚ½«Õû¸ö»º³åÇø¸´ÖƵ½×îÖÕµÄRX(read/execute-only)»º³åÇøÖ®ºó¶ÔУÑéºÍ½øÐÐÑéÖ¤¡£²¢ÇÒ£¬Ö»ÓÐͨ¹ýÑéÖ¤ºó£¬JIT´¦ÀíºóµÄ´úÂëµÄÈë¿Úµã²ÅÄÜ×÷ΪÓÐЧµÄCFGÄ¿±ê¡£ÕâÀïµÄÑ¡ÔñµÄУÑéºÍËã·¨ÊÇCRC32¡£ |