seggewiss

Learning IT security in public — eJPT, CPTS, OSCP, and what actually happens along the way.

Reading a Netmask Without Guessing

I spent longer than I’d like to admit stuck on a pivoting exercise last week, and the root cause had nothing to do with pivoting. It was a netmask I guessed instead of calculated.

The setup

The scenario is a standard one for this kind of lab: two internal machines, only one of which I can reach directly from my attack box. I land 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 a router.

The compromised host can see both networks. My attack box can only see the first. So I add a route through my session on that host, pointed at whatever subnet the second machine lives on, and everything downstream — scans, exploits — gets tunneled through it automatically.

That “whatever subnet” is where I 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, made an assumption about where the network boundary was, and routed accordingly. The route came up, some hosts responded, some didn’t, and I burned real time convinced pivoting itself was broken.

It wasn’t. My subnet was wrong. I’d routed a slice of the network that didn’t actually contain the host I was after — narrower than the real subnet — so traffic for anything outside my guessed range just had nowhere to go.

The fix isn’t a pivoting technique. It’s arithmetic I was skipping.

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 actual size of the network you’re standing on:

NetmaskCIDRAddressesExample range
255.255.255.0/24256x.x.x.0x.x.x.255
255.255.240.0/204096x.x.0.0x.x.15.255
255.255.0.0/1665536x.x.0.0x.x.255.255

The part that actually bit me is the /20 row, because it’s not aligned to a clean octet boundary the way /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 against the mask — you don’t round to the nearest tidy-looking number.

Worked 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 isn’t 10.2.23.0 (a guess anchored on the octet you can see) — it’s 10.2.16.0/20, covering everything from 10.2.16.0 through 10.2.31.255. My guessed range fell entirely inside that block but didn’t cover all of it, which is exactly narrow enough to make some traffic route fine and other traffic silently die.

The second trap: a target that was never “behind” anything

The machine I was trying to reach turned out to show up in arp -a on the compromised host, but not as a separate entry in ipconfig. That distinction matters: ARP only resolves within the same broadcast domain. If a host only shows up via ARP, it isn’t hiding behind another hop at all — it’s a direct neighbor on the same subnet as the box you’re already standing on. Once I had the /20 routed correctly, that “hidden” second machine turned out to already be reachable — no second pivot needed, just the first subnet done right.

What I’d tell myself a week ago

Read the mask directly off the compromised host — don’t infer it from the IP alone. Convert it to CIDR with the actual AND operation, not a mental rounding. And check arp -a before assuming a target needs a deeper route; it might already be a neighbor.

None of this is advanced. It’s the kind of thing that’s obvious in hindsight and completely invisible while you’re three failed scans deep, second-guessing a technique that was working fine the whole time.