Skip to content

feat(cli): add --exclude and --skip-self target filters (#674)#1227

Closed
ChrisJr404 wants to merge 1 commit intoPennyw0rth:mainfrom
ChrisJr404:exclude-and-skip-self
Closed

feat(cli): add --exclude and --skip-self target filters (#674)#1227
ChrisJr404 wants to merge 1 commit intoPennyw0rth:mainfrom
ChrisJr404:exclude-and-skip-self

Conversation

@ChrisJr404
Copy link
Copy Markdown

Closes #674.

Background

Right now nxc can't remove specific hosts or networks from a resolved target list. On engagements that scan a /24 containing the attacker host or sensitive infrastructure, the only workarounds are (a) break the scope into smaller CIDRs and feed each separately, or (b) edit the target file by hand — both error-prone, and #674 calls out that "funny things happen" when the attacker host gets caught in its own scan.

Change

Add two related flags on the standard parser (std_parser in nxc/cli.py):

Flag Behaviour
--exclude HOST [HOST ...] IP / range / CIDR / hostname entries to remove from the resolved target list. Accepts the same forms target accepts, including ranges (10.0.0.1-50) and CIDRs.
--skip-self Convenience flag — detects the attacker host's primary outbound IPv4 via a UDP-connect to a non-routable address (no traffic actually leaves the host; the kernel just populates the local end of the socket with whichever interface IP would be used) and adds it to the exclusion set. Mirrors the snippet @hugo-syn included in #674.

Both run after target resolution in nxc/netexec.py, so a user can exclude a single IP from a CIDR scope without having to fragment the CIDR into smaller pieces.

excluded = set()
if hasattr(args, "exclude") and args.exclude:
    for entry in args.exclude:
        try:
            excluded.update(parse_targets(entry))
        except Exception as e:
            nxc_logger.fail(f"Failed to parse --exclude entry '{entry}': {e}")
if hasattr(args, "skip_self") and args.skip_self:
    local_ip = _detect_local_ip()
    if local_ip:
        excluded.add(local_ip)
        nxc_logger.debug(f"--skip-self: excluding local IP {local_ip}")
if excluded:
    before = len(targets)
    targets = [t for t in targets if t not in excluded]
    removed = before - len(targets)
    if removed:
        nxc_logger.info(f"Excluded {removed} host(s) from the target list")

Verification

$ nxc smb --help | grep -A1 exclude
  --exclude HOST [HOST ...]
                        IP(s), range(s), CIDR(s), or hostname(s) to remove ...

$ nxc smb --help | grep skip-self
  --skip-self           exclude the attacker host's own primary IPv4 ...

python3 -m py_compile nxc/cli.py nxc/netexec.py clean.

Notes

  • Diff is +40 / 0 lines across two files.
  • The exclusion set uses set-based membership for O(1) filter, so a /16 scan with --exclude 192.0.2.0/24 is still O(n) in the resolved target count.
  • --skip-self only handles IPv4. Adding IPv6 detection would mean a second UDP-connect probe to an unreachable v6 destination — happy to follow up if you'd like that included.
  • Hostnames in --exclude aren't resolved before set-membership comparison (matches the way parse_targets handles them today). If a future PR wants resolved-hostname matching, the natural place to add it is in parse_targets itself.

Currently nxc has no way to remove specific hosts or networks from a
resolved target list, which means scanning a /24 during an engagement
sometimes hits the attacker host itself (or sensitive infrastructure
inside the same range that shouldn't be touched). The user fix to
that today is "split your scope into smaller CIDRs and run the tool
multiple times" — error-prone.

Add two related flags on the standard parser:

  --exclude HOST [HOST ...]   IP / range / CIDR / hostname entries to
                              remove from the resolved target list.
                              Accepts the same forms the positional
                              `target` accepts, including ranges
                              (`10.0.0.1-50`) and CIDRs.

  --skip-self                 Convenience flag: detect the attacker
                              host's primary outbound IPv4 (UDP
                              connect to a non-routable address; no
                              traffic actually leaves the host) and
                              add it to the exclusion set.

The exclusion set is built after target resolution, so a user can
exclude a single IP from a CIDR scope without having to break the
range up. Closes Pennyw0rth#674.
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 3, 2026

It looks like the PR template may not have been filled out. The following sections appear to be missing:

  • Description

  • Type of change

  • Setup guide for the review

  • Checklist

Please edit your PR description to include them. The template helps reviewers understand and test your changes. Thanks!

@ChrisJr404
Copy link
Copy Markdown
Author

Closing this myself — re-reading NetExec's AI_POLICY.md after the template-check bot fired, two of the rules apply here:

  • This PR was prepared with AI assistance (Claude Code / Opus 4.7), and the policy is clear that AI PRs can only target accepted issues.
  • Issue Skip-self or exclude single IP #674 has no maintainer engagement yet, so it doesn't fit that bar — happy to revisit if a maintainer flags it as accepted, otherwise I shouldn't push through.

Apologies for the noise on the issue tracker. Closing rather than refilling the template feels like the right call for the project's contributor culture.

@ChrisJr404 ChrisJr404 closed this May 3, 2026
@NeffIsBack
Copy link
Copy Markdown
Member

Thanks for the PR :)

This PR was prepared with AI assistance (Claude Code / Opus 4.7), and the policy is clear that AI PRs can only target accepted issues.

Actually this is an accepted issue, but closing this PR is probably still the right call as we already have people that are working on this, see #732, #1204. Therefore, this PR would have been duplicate.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Skip-self or exclude single IP

2 participants