Reading a Netmask Without Guessing
Last week I was stuck in a pivoting exercise much longer than I want to admit, and the reason had nothing to do with pivoting. It was a netmask that I guessed instead of calculated.
The setup
The scenario is a standard one for this kind of lab: two internal machines and I can only reach one of them directly from my attack box. I get a foothold on the reachable one, and from there I need to route traffic to the second, hidden machine — pivoting through the first host like through a router.
The compromised host sees both networks. My attack box sees only the first one. So I add a route through my session on that host, pointing to whatever subnet the second machine is in, and everything after that — scans, exploits — goes through it automatically.
This “whatever subnet” is where it went wrong.
The mistake: guessing instead of reading
I ran ipconfig on the compromised host and got back an IP address and a
subnet mask. I looked at the IP, assumed where the network boundary is, and
routed like that. The route came up, some hosts answered, some did not, and I
burned real time being convinced that pivoting itself is broken.
It was not. My subnet was wrong. I routed a slice of the network that did not contain the host I was looking for — smaller than the real subnet — so traffic for everything outside of my guessed range simply had nowhere to go.
The fix is not a pivoting technique. It is math that I skipped.
Netmask to CIDR, done properly
A subnet mask tells you per octet which bits are “network” and which are “host”. Convert the mask to CIDR notation and you get the real size of the network you are standing in:
| Netmask | CIDR | Addresses | Example range |
|---|---|---|---|
255.255.255.0 | /24 | 256 | x.x.x.0 – x.x.x.255 |
255.255.240.0 | /20 | 4096 | x.x.0.0 – x.x.15.255 |
255.255.0.0 | /16 | 65536 | x.x.0.0 – x.x.255.255 |
The row that got me is the /20 one, because it is not aligned to a clean
octet boundary like /24 is. 255.255.240.0 means only the top four bits
of the third octet are network bits. To find the real network address you AND
the host’s own address with the mask. You do not round to the next
nice looking number.
Example: a host with address 10.2.23.x and mask 255.255.240.0. 240 in
binary is 11110000. 23 in binary is 00010111. AND them together and you
get 00010000, decimal 16. So the network is not 10.2.23.0 (a guess based
on the octet you can see), it is 10.2.16.0/20, which covers everything from
10.2.16.0 to 10.2.31.255. My guessed range was completely inside that
block but did not cover all of it — exactly narrow enough that some traffic
routes fine and other traffic dies silently.
The second trap: a target that was never “behind” anything
The machine I wanted to reach was in arp -a on the compromised host, but not
as a separate entry in ipconfig. That difference matters: ARP only resolves
inside the same broadcast domain. When a host only shows up via ARP, it is not
hiding behind another hop at all — it is a direct neighbour in the same
subnet as the box you are already standing on. After I had the /20 routed
correctly, that “hidden” second machine was already reachable. No second pivot
needed, just the first subnet done right.
What I would tell myself one week ago
Read the mask directly from the compromised host, do not derive it from the IP
alone. Convert it to CIDR with the real AND operation, not with rounding in
your head. And check arp -a before you assume a target needs a deeper route,
maybe it is already a neighbour.
Nothing here is advanced. It is the kind of thing that is obvious afterwards and completely invisible while you are three failed scans deep and doubting a technique that worked the whole time.