00001 //--------------------------------------------------------------------------
00002 // Copyright (C) 2014-2017 Cisco and/or its affiliates. All rights reserved.
00003 // Copyright (C) 2004-2013 Sourcefire, Inc.
00004 // Copyright (C) 2001-2004 Jeff Nathan <jeff@snort.org>
00005 //
00006 // This program is free software; you can redistribute it and/or modify it
00007 // under the terms of the GNU General Public License Version 2 as published
00008 // by the Free Software Foundation. You may not use, modify or distribute
00009 // this program under any other version of the GNU General Public License.
00010 //
00011 // This program is distributed in the hope that it will be useful, but
00012 // WITHOUT ANY WARRANTY; without even the implied warranty of
00013 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00014 // General Public License for more details.
00015 //
00016 // You should have received a copy of the GNU General Public License along
00017 // with this program; if not, write to the Free Software Foundation, Inc.,
00018 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00019 //--------------------------------------------------------------------------
00020
00021 /* Snort ARPspoof Preprocessor Plugin
00022 * by Jeff Nathan <jeff@snort.org>
00023 * Version 0.1.4
00024 *
00025 * Purpose:
00026 *
00027 * This preprocessor looks for anomalies in ARP traffic and attempts to
00028 * maliciously overwrite ARP cache information on hosts.
00029 *
00030 * Arguments:
00031 *
00032 * To check for unicast ARP requests use:
00033 * arpspoof: -unicast
00034 *
00035 * WARNING: this can generate false positives as Linux systems send unicast
00036 * ARP requests repetitively for entries in their cache.
00037 *
00038 * This plugin also takes a list of IP addresses and MAC address in the form:
00039 * arpspoof_detect_host: 10.10.10.10 29:a2:9a:29:a2:9a
00040 * arpspoof_detect_host: 192.168.40.1 f0:0f:00:f0:0f:00
00041 * and so forth...
00042 *
00043 * Effect:
00044 * By comparing information in the Ethernet header to the ARP frame, obvious
00045 * anomalies are detected. Also, utilizing a user supplied list of IP
00046 * addresses and MAC addresses, ARP traffic appearing to have originated from
00047 * any IP in that list is carefully examined by comparing the source hardware
00048 * address to the user supplied hardware address. If there is a mismatch, an
00049 * alert is generated as either an ARP request or REPLY can be used to
00050 * overwrite cache information on a remote host. This should only be used for
00051 * hosts/devices on the **same layer 2 segment** !!
00052 *
00053 * Bugs:
00054 * This is a proof of concept ONLY. It is clearly not complete. Also, the
00055 * lookup function LookupIPMacEntryByIP is in need of optimization. The
00056 * arpspoof_detect_host functionality may false alarm in redundant environments.
00057 * Also, see the comment above pertaining to Linux systems.
00058 *
00059 * Thanks:
00060 *
00061 * First and foremost Patrick Mullen who sat beside me and helped every step of
00062 * the way. Andrew Baker for graciously supplying the tougher parts of this
00063 * code. W. Richard Stevens for readable documentation and finally
00064 * Marty for being a badass. All your packets are belong to Marty.
00065 *
00066 */
00067
00068 /* I N C L U D E S ************************************************/
00069
00070 #ifdef HAVE_CONFIG_H
00071 #include "config.h"
00072 #endif
00073
00074 #include "detection/detection_engine.h"
00075 #include "events/event_queue.h"
00076 #include "log/messages.h"
00077 #include "profiler/profiler.h"
00078 #include "protocols/arp.h"
00079 #include "protocols/eth.h"
00080 #include "protocols/packet.h"
00081 #include "sfip/sf_ip.h"
00082
00083 #include "arp_module.h"
00084
00085 static const uint8_t bcast[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
00086
00087 THREAD_LOCAL ProfileStats arpPerfStats;
00088
00089 //-------------------------------------------------------------------------
00090 // implementation stuff
00091 //-------------------------------------------------------------------------
00092
00093 static IPMacEntry* LookupIPMacEntryByIP(
00094 IPMacEntryList& ipmel, uint32_t ipv4_addr)
00095 {
00096 for ( auto& p : ipmel )
00097 {
00098 if (p.ipv4_addr == ipv4_addr)
00099 return &p;
00100 }
00101 return nullptr;
00102 }
00103
00104 #ifdef DEBUG_MSGS
00105 static void PrintIPMacEntryList(IPMacEntryList& ipmel)
00106 {
00107 if ( ipmel.empty() )
00108 return;
00109
00110 LogMessage("Arpspoof IPMacEntry List");
00111 LogMessage(" Size: %zu\n", ipmel.size());
00112
00113 for ( auto p : ipmel )
00114 {
00115 SfIp in;
00116 in.set(&p.ipv4_addr, AF_INET);
00117 LogMessage(" %s -> ", in.ntoa());
00118
00119 for (int i = 0; i < 6; i++)
00120 {
00121 LogMessage("%02x", p.mac_addr[i]);
00122 if (i != 5)
00123 LogMessage(":");
00124 }
00125 LogMessage("\n");
00126 }
00127 }
00128
00129 #endif
00130
00131 //-------------------------------------------------------------------------
00132 // class stuff
00133 //-------------------------------------------------------------------------
00134
00135 class ArpSpoof : public Inspector
00136 {
00137 public:
00138 ArpSpoof(ArpSpoofModule*);
00139 ~ArpSpoof() override;
00140
00141 void show(SnortConfig*) override;
00142 void eval(Packet*) override;
00143
00144 private:
00145 ArpSpoofConfig* config;
00146 };
00147
00148 ArpSpoof::ArpSpoof(ArpSpoofModule* mod)
00149 {
00150 config = mod->get_config();
00151 }
00152
00153 ArpSpoof::~ArpSpoof ()
00154 {
00155 delete config;
00156 }
00157
00158 void ArpSpoof::show(SnortConfig*)
00159 {
00160 LogMessage("arpspoof configured\n");
00161
00162 #ifdef DEBUG_MSGS
00163 if ( Debug::enabled(DEBUG_INSPECTOR) )
00164 PrintIPMacEntryList(config->ipmel);
00165 #endif
00166 }
00167
00168 void ArpSpoof::eval(Packet* p)
00169 {
00170 Profile profile(arpPerfStats);
00171
00172 // precondition - what we registered for
00173 assert(p->type() == PktType::ARP);
00174
00175
00176 new_invoked_inspector(2, p, 0);
00177
00178 // 802.11 not supported
00179 if ((p->proto_bits & PROTO_BIT__ETH) == 0)
00180 return;
00181
00182 const arp::EtherARP* ah = layer::get_arp_layer(p);
00183 const eth::EtherHdr* eh = layer::get_eth_layer(p);
00184
00185 /* is the ARP protocol type IP and the ARP hardware type Ethernet? */
00186 if ((ntohs(ah->ea_hdr.ar_hrd) != 0x0001) ||
00187 (ntohs(ah->ea_hdr.ar_pro) != ETHERNET_TYPE_IP))
00188 return;
00189
00190 ++asstats.total_packets;
00191
00192 switch (ntohs(ah->ea_hdr.ar_op))
00193 {
00194 case ARPOP_REQUEST:
00195 if (memcmp((const u_char*)eh->ether_dst, (const u_char*)bcast, 6) != 0)
00196 {
00197 DetectionEngine::queue_event(GID_ARP_SPOOF, ARPSPOOF_UNICAST_ARP_REQUEST);
00198 DebugMessage(DEBUG_INSPECTOR, "MODNAME: Unicast request\n");
00199 }
00200 else if (memcmp((const u_char*)eh->ether_src,
00201 (const u_char*)ah->arp_sha, 6) != 0)
00202 {
00203 DetectionEngine::queue_event(GID_ARP_SPOOF, ARPSPOOF_ETHERFRAME_ARP_MISMATCH_SRC);
00204 DebugMessage(DEBUG_INSPECTOR, "MODNAME: Ethernet/ARP mismatch request\n");
00205 }
00206 break;
00207 case ARPOP_REPLY:
00208 if (memcmp((const u_char*)eh->ether_src,
00209 (const u_char*)ah->arp_sha, 6) != 0)
00210 {
00211 DetectionEngine::queue_event(GID_ARP_SPOOF, ARPSPOOF_ETHERFRAME_ARP_MISMATCH_SRC);
00212 DebugMessage(DEBUG_INSPECTOR, "MODNAME: Ethernet/ARP mismatch reply src\n");
00213 }
00214 else if (memcmp((const u_char*)eh->ether_dst,
00215 (const u_char*)ah->arp_tha, 6) != 0)
00216 {
00217 DetectionEngine::queue_event(GID_ARP_SPOOF, ARPSPOOF_ETHERFRAME_ARP_MISMATCH_DST);
00218 DebugMessage(DEBUG_INSPECTOR, "MODNAME: Ethernet/ARP mismatch reply dst\n");
00219 }
00220 break;
00221 }
00222
00223 /* return if the overwrite list hasn't been initialized */
00224 if (!config->check_overwrite)
00225 return;
00226
00227 IPMacEntry* ipme = LookupIPMacEntryByIP(config->ipmel, ah->arp_spa32);
00228 if ( ipme )
00229 {
00230 DebugFormat(DEBUG_INSPECTOR,
00231 "MODNAME: LookupIPMacEntryByIP returned %p\n", (void*)ipme);
00232
00233 auto cmp_ether_src = memcmp(eh->ether_src, ipme->mac_addr, 6);
00234 auto cmp_arp_sha = memcmp(ah->arp_sha, ipme->mac_addr, 6);
00235
00236 // If the Ethernet source address or the ARP source hardware address
00237 // in p doesn't match the MAC address in ipme, then generate an alert
00238 if ( cmp_ether_src || cmp_arp_sha )
00239 {
00240 DetectionEngine::queue_event(GID_ARP_SPOOF, ARPSPOOF_ARP_CACHE_OVERWRITE_ATTACK);
00241 DebugMessage(DEBUG_INSPECTOR, "MODNAME: Attempted ARP cache overwrite attack\n");
00242 }
00243 }
00244 else
00245 {
00246 DebugMessage(DEBUG_INSPECTOR,
00247 "MODNAME: LookupIPMacEntryByIp returned NULL\n");
00248 }
00249 }
00250
00251 //-------------------------------------------------------------------------
00252 // api stuff
00253 //-------------------------------------------------------------------------
00254
00255 static Module* mod_ctor()
00256 { return new ArpSpoofModule; }
00257
00258 static void mod_dtor(Module* m)
00259 { delete m; }
00260
00261 static Inspector* as_ctor(Module* m)
00262 {
00263 return new ArpSpoof((ArpSpoofModule*)m);
00264 }
00265
00266 static void as_dtor(Inspector* p)
00267 { delete p; }
00268
00269 static const InspectApi as_api =
00270 {
00271 {
00272 PT_INSPECTOR,
00273 sizeof(InspectApi),
00274 INSAPI_VERSION,
00275 0,
00276 API_RESERVED,
00277 API_OPTIONS,
00278 MOD_NAME,
00279 MOD_HELP,
00280 mod_ctor,
00281 mod_dtor
00282 },
00283 IT_NETWORK,
00284 (uint16_t)PktType::ARP,
00285 nullptr, // buffers
00286 nullptr, // service
00287 nullptr, // pinit
00288 nullptr, // pterm
00289 nullptr, // tinit
00290 nullptr, // tterm
00291 as_ctor,
00292 as_dtor,
00293 nullptr, // ssn
00294 nullptr, // reset
00295 };
00296
00297 #ifdef BUILDING_SO
00298 SO_PUBLIC const BaseApi* snort_plugins[] =
00299 #else
00300 const BaseApi* nin_arp_spoof[] =
00301 #endif
00302 {
00303 &as_api.base,
00304 nullptr
00305 };
END OF CODE