Impersonation - MadWizardDE/ARPergefactor GitHub Wiki

What's the problem?

Suppose ARPergefactor runs on an always-on device "pie" and therefore isn't the source or destination of a particular connection attempt made by your laptop to the host "morpheus". Nevertheless you only want "morpheus" to be woken, when someone tries to connect to the SSH server. Furthermore you also don't want ping requests to wake "morpheus" at any rate. Consider then the following configuration:

<Network interface="eth0">

  <PingFilterRule type="MustNot" />

  <WatchHost name="morpheus" MAC="00:1A:2B:3C:4D:5E" IPv4="192.168.178.10">
    <ServiceFilterRule name="SSH" port="22" />
  </WatchHost>

</Network>

The problem here is: Running on an unparticipating network node, ARPergefactor would never see the actual TCP packet which initiates a connection to port 22 or the ICMP echo request, because this packet would be sent directly to the requested host "morpheus" – which is a good thing, because else everybody could read what sensitive data "MacBookPro" and "morpheus" would be exchanging (apart from the fact, that SSH is fully encrypted by nature, so that it can operate over unsecure links – but that's beside the point). So what we need, is to make us somehow to be the man (or woman) in the middle to receive any unicast packets directed to "morpheus", but without to establish a permanent proxy relationship between the server and the client, which would also create a single point of failure in our architecture. That is not what we aim for.

The (quite practical) solution

The technique to gather more information about the connection attempts, beside the source MAC and IP, isn't that secret sauce. In a more shady context, what we do, you would call "ARP/NDP spoofing". But since we do it with the best intentions and aren't even interested in the payload data of these connections, let's call it "ARP borrowing" instead. Yeah, that sounds better right away.

When you configure any filter rule that need to inspect unicast IP traffic (ServiceFilterRule and PingFilterRule are one of them) and ARPergefactor cannot rule out the request by MAC or IP alone, it starts to impersonate the requested IP address, but only for brief (and configurable) moment. After it gathered enough information to meet the filter requirements, it stops responding to ARP or NDP requests on behalf of the sleeping device and also makes sure, that all hosts with a now tampered IP cache are restored to the correct mappings again, before sending the actual Magic Packet to the host waking it from it's slumber.

You can configure the time for how long to wait until we stop the impersonation and consider the WakeRequest as cancelled, as usual for the whole network or host wise:

<Network interface="eth0" poseTimeout="5s">

  <WatchHost name="morpheus" MAC="00:1A:2B:3C:4D:5E" IPv4="192.168.178.10" poseTimeout="10s">
    <ServiceFilterRule name="SSH" port="22" />
  </WatchHost>

</Network>

This tells ARPergefactor to wait up to 5 seconds to receive unicast traffic from the client after the impersonation started, but also overwrite that value for requests to "morpheus" to 10 seconds. If you don't configure anything the default value will be 5 seconds.

Do try this at home only

It should be obvious that you should never do this in a big corporate network, unless you like to answer uncomfortable questions to your superior. But having the complete control over your network, the moral implications are negligible. In my tests nothing bad happened in spite of using this black magic and no application either on Windows, Linux or MacOS threw up on this. Having this witchcraft in place however feels so comfortable, that I don't really want to miss it anymore.

Static address mappings

When you run ARPergefactor with privileged rights in either host or network mode, it will also apply static IP to MAC address mappings for all your configured hosts locally, as long as it runs. It is necessary to do this in order to monitor outgoing traffic to these hosts, because your system can't send out a single TCP or UDP packet, unless it knows the MAC address of it's target IP. The way remote hosts can be coerced into sending these packets to us (via ARP/NDP spoofing) cannot be reliably done for the local host. But having static address mappings in place, your computer won't do any address resolution in the first place, which allows us to inspect the outgoing traffic most easily.

The static mappings will be removed, when you stop ARPergefactor gracefully. If you just kill the program, the mappings will still linger on your system, until the next reboot. You can remedy this immediately by restarting ARPergefactor and then stop it by hitting Ctrl+C. But technically this shouldn't be a problem at any rate, because these mappings already point to the right host anyway.

Are you ready for the next step?

Since version 2 of ARPergefactor, the software also offers an experimental mode, in which it will not wait until it receives an address resolution request to impersonate the host, but will rather eagerly check if the host is still reachable. As soon as we detect that the host isn't available anymore, ARPergefactor will immediately begin with the impersonation. This may improve the responsiveness of a sleeping host in some situations, especially when you try to connect soon after the host went to sleep and your client machine still have lingering mappings in it's cache. Until these will reach the end of their lifetime (usually some minutes) or you clear them manually, your machine won't do another address resolution for the target host, rendering ARPergefactor powerless to step in and wake the target host for you.

Having ARPergefactor to pose eagerly for the recently suspended host, we can overcome the timeframe in which no connection attempt will be possible. You can activate this behaviour on a per host basis or for the entire configured network. Either way, your config file will look something like this:

<Network interface="eth0" poseLatency="1min">

  <WatchHost name="morpheus" MAC="00:1A:2B:3C:4D:5E" IPv4="192.168.178.10" poseLatency="30s" pingTimeout="100ms">
    <ServiceFilterRule name="SSH" port="22" />
  </WatchHost>

</Network>

The keyword here is poseLatency, which means the maximum duration of time, in which the host will not be impersonated, after it ceases to respond. This effectively sets a timer at which interval it will be checked if the corresponding IP is reachable, either via ARP or NDP. If the host doesn't respond after pingTimeout, ARPergefactor will consider it OK to take ownership of the IP addresses, which means actively advertising them as it's own and responding to the respective ARP and NDP requests. This also reduces the delay until the next WakeRequest can be executed, because we don't have to check the availability of the host first and can reduce the needed amount of handshakes to a minimum.

Of course ARPergefactor won't respond to ARP or NDP requests originating from the impersonated host, to avoid triggering the Duplicate Address Detection (DAD). In fact, on receiving any packet from that host, it will rather stop the impersonation immediately.

This configuration is best suited for situations where you need to satisfy a requirement for low latency access while also reducing the actual up-time of the machine to a minimum.

The "Unmagic Packet"

To reduce unnecessary broadcast traffic, you can set the poseLatency to a rather high value (say 10 minutes or more) and make use of a rather unorthodox use of a Magic Packet sequence. If ARPergefactor captures a Magic Packet with the MAC address of a soon going to be impersonated host sent by this very host, it will treat this as a hint to skip the timer and check the availability of said host immediately after poseTimeout. The configured value of poseLatency will be in place as a fallback, in case the Unmagic Packet was missed or haven't been sent entirely for some reason, to start the impersonation eventually even without it. But with a cooperating host you can, in most cases, effectively reduce the ping pong traffic on your network to a minimum, without losing the high availability behavior.

<WatchHost name="morpheus" MAC="00:1A:2B:3C:4D:5E" IPv4="192.168.178.10"
  poseLatency="10min"
  poseTimeout="5s">

  <ServiceFilterRule name="SSH" port="22" />
</WatchHost>

There is an implementation of this behaviour already present in my other project called "Insomnia", which is a cross-platform (but currently only supporting Windows) sleep manager. It gives you fine-grained control over when a (preferably headless) machine should suspend. Go check it out.

⚠️ **GitHub.com Fallback** ⚠️