ProxyChains does not intercept Nmap connect() when the Nmap binary has Linux file capabilities
ProxyChains does not intercept Nmap connect() when the Nmap binary has Linux file capabilities
Description
I encountered an issue while using ProxyChains with Nmap through a Chisel SOCKS proxy during a penetration testing lab.
Nmap bypasses ProxyChains and attempts to connect directly to the internal target, even when explicitly using a TCP Connect scan (-sT) with host discovery disabled (-Pn).
After investigating with strace, I found that the behavior appears to be related to Linux file capabilities assigned to the Nmap executable.
Removing the file capabilities from the equation by using a capability-free copy of the same Nmap binary causes ProxyChains to work correctly.
Setup
The network path is:
Nmap
↓
ProxyChains
↓
SOCKS5 (127.0.0.1:1080)
↓
Chisel
↓
Internal HostThe SOCKS proxy itself is working correctly.
For example, Impacket's wmiexec.py successfully connects through the same ProxyChains configuration:
[proxychains] Strict chain ... 127.0.0.1:1080 ... <INTERNAL_IP>:445 ... OKExpected Behavior
Running:
proxychains nmap -n -Pn -sT -p 445 <INTERNAL_IP>should cause Nmap's TCP connect() calls to be intercepted by ProxyChains and forwarded through the SOCKS proxy:
Nmap
↓
ProxyChains
↓
127.0.0.1:1080
↓
<INTERNAL_IP>:445Since -sT explicitly selects a TCP Connect scan, I expected the connection to go through ProxyChains.
Actual Behavior
When running Nmap through ProxyChains:
proxychains nmap -n -Pn -sT -p 445 <INTERNAL_IP>no Strict chain connection messages appear.
Nmap also fails to use the SOCKS proxy.
Investigation
On this system, /usr/bin/nmap is a shell wrapper rather than the actual Nmap ELF executable.
file /usr/bin/nmapreturns:
/usr/bin/nmap: a sh script, ASCII text executableThe wrapper contains:
#!/usr/bin/env sh
set -e
if [ "$(id -u)" -eq 0 ] || [ "$1" = "--resume" ]; then
exec /usr/lib/nmap/nmap "$@"
else
exec /usr/lib/nmap/nmap --privileged "$@"
fiTherefore, the actual Nmap executable is:
/usr/lib/nmap/nmapFile capabilities
Checking the actual executable:
getcap /usr/lib/nmap/nmapreturns:
/usr/lib/nmap/nmap cap_net_bind_service,cap_net_admin,cap_net_raw=eipThe binary is not setuid:
ls -l /usr/lib/nmap/nmapreturns:
-rwxr-xr-x ...strace Result
I then traced the connect() system calls while running the actual Nmap executable through ProxyChains:
strace -f -e trace=connect \
proxychains /usr/lib/nmap/nmap \
--unprivileged -n -Pn -sT -p 445 <INTERNAL_IP>Instead of connecting to the SOCKS proxy at:
127.0.0.1:1080Nmap attempted to connect directly to:
<INTERNAL_IP>:445This indicates that the connect() call was not being intercepted by ProxyChains.
Workaround / A-B Test
To test whether the file capabilities were involved, I copied the exact same Nmap executable:
cp /usr/lib/nmap/nmap /tmp/nmap-testI verified that the copied executable did not have file capabilities:
getcap /tmp/nmap-testNo capabilities were returned.
I then ran the copied binary through ProxyChains:
proxychains /tmp/nmap-test \
--unprivileged -n -Pn -sT -p 445 <INTERNAL_IP>ProxyChains immediately worked as expected:
[proxychains] Strict chain ... 127.0.0.1:1080 ... <INTERNAL_IP>:445 ... OKThe traffic was successfully routed through the Chisel SOCKS proxy.
Comparison
Original Nmap executable
/usr/lib/nmap/nmap
File capabilities:
cap_net_bind_service,cap_net_admin,cap_net_raw=eip
↓
ProxyChains
↓
connect(<INTERNAL_IP>:445)
↓
Direct connectionCapability-free copy
/tmp/nmap-test
No file capabilities
↓
ProxyChains
↓
connect(127.0.0.1:1080)
↓
SOCKS5 / Chisel
↓
<INTERNAL_IP>:445Possible Cause
My current understanding is that this may be related to the interaction between Linux file capabilities, the dynamic linker's secure-execution mode, and ProxyChains' use of LD_PRELOAD.
ProxyChains relies on LD_PRELOAD to load its library and intercept networking functions such as connect().
When executing a binary with elevated file capabilities such as:
cap_net_admin
cap_net_rawthe dynamic linker may treat the executable as running in secure-execution mode and restrict or ignore LD_PRELOAD.
If that is what is happening here, the ProxyChains library would not be loaded into the Nmap process, explaining why Nmap's connect() calls go directly to the internal host.
This also appears consistent with the observations that:
- Other applications such as
wmiexec.pywork through the same ProxyChains configuration. - Nmap does not produce any ProxyChains
Strict chainconnection messages. straceshows Nmap connecting directly to the internal target.- The exact same Nmap binary works through ProxyChains after being copied without its file capabilities.
Questions
Is this an expected limitation of ProxyChains when the target executable has Linux file capabilities?
If this behavior is expected due to LD_PRELOAD restrictions, would it be useful to document it?
This may be particularly confusing when using Nmap for network pivoting because nmap -sT -Pn would normally be expected to work through ProxyChains, while other TCP applications using the same SOCKS proxy work correctly.
Source: haad/proxychains