Skip to content

ProxyChains does not intercept Nmap connect() when the Nmap binary has Linux file capabilities #179

Description

@kibmkw

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 Host

The 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 ... OK

Expected 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>:445

Since -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/nmap

returns:

/usr/bin/nmap: a sh script, ASCII text executable

The 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 "$@"
fi

Therefore, the actual Nmap executable is:

/usr/lib/nmap/nmap

File capabilities

Checking the actual executable:

getcap /usr/lib/nmap/nmap

returns:

/usr/lib/nmap/nmap cap_net_bind_service,cap_net_admin,cap_net_raw=eip

The binary is not setuid:

ls -l /usr/lib/nmap/nmap

returns:

-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:1080

Nmap attempted to connect directly to:

<INTERNAL_IP>:445

This 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-test

I verified that the copied executable did not have file capabilities:

getcap /tmp/nmap-test

No 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 ... OK

The 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 connection

Capability-free copy

/tmp/nmap-test

No file capabilities

        ↓

ProxyChains

        ↓

connect(127.0.0.1:1080)

        ↓

SOCKS5 / Chisel

        ↓

<INTERNAL_IP>:445

Possible 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_raw

the 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.py work through the same ProxyChains configuration.
  • Nmap does not produce any ProxyChains Strict chain connection messages.
  • strace shows 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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions