Having to remember which arguments to use and what to add can be quite a lot, especially with tools such as Impacket. Recently there’s this pull request which added autocompletion functionalities to NetExec (the successor to CrackMapExec), so I thought I’d enable autocompletion for Impacket, and other python command line tools which use argparse.

This guide will be aimed at the pipx installation of Impacket, the steps will be similar if your Impacket is installed with pip or just a local repository.

The process is quite straightforward, here’s an overview.

First, we need to ensure argcomplete is installed:

pip install argcomplete

Enable argcomplete automatically for all compatible scripts:

activate-global-python-argcomplete

Add the argcomplete package to the tool by using pipx inject <name> argcomplete.

For Impacket, we will do:

pipx inject impacket argcomplete

Then, we will need to modify the files a little:

  • Add import argcomplete after import argparse
  • argcomplete.autocomplete(parser) should be added before options = parser.parse_args() and if len(sys.argv)==1:

The modified files should look something like this:

import argparse
import argcomplete

# Rest of the code

    argcomplete.autocomplete(parser)

    if len(sys.argv) == 1:
        parser.print_help()
        sys.exit(1)

	options = parser.parse_args()

# More code

Finally, run eval "$(register-python-argcomplete <name>)" to enable autocompletion.

This would be easy if the tool has a single __main__.py or cli.py or entry.py used to parse all arguments, such as the NetExec example earlier or Certipy. But the Impacket examples are a number of different tools, all with their own argparse and arguments, so it can be quite a pain to do it manually.

I have written a script to simplify the process, I would highly recommend making a snapshot or backup of the files before running it.

#!/usr/bin/env python

import os
import sys
import re

def modify_file(file_path):
    with open(file_path, 'r') as file:
        lines = file.readlines()
    
    modified = False
    autocomplete_inserted = False
    
    # Find and insert `import argcomplete` after any import that includes `argparse`
    for i, line in enumerate(lines):
        if 'import' in line and 'argparse' in line:
            lines.insert(i + 1, 'import argcomplete\n')
            modified = True
            break
    
    # Find and insert `argcomplete.autocomplete(parser)` with the same indentation for `if len(sys.argv) == 1:`
    pattern_if = re.compile(r'^(\s*)if len\(sys\.argv\) ?== ?1:')
    pattern_parse_args = re.compile(r'^(\s*).*parser\.parse_args\(\)')
    
    for i, line in enumerate(lines):
        match_if = pattern_if.match(line)
        if match_if:
            indent = match_if.group(1)
            lines.insert(i, f'{indent}argcomplete.autocomplete(parser)\n')
            modified = True
            autocomplete_inserted = True
            break

    # If `argcomplete.autocomplete(parser)` was not inserted above, find `parser.parse_args()` and insert before it
    if not autocomplete_inserted:
        for i, line in enumerate(lines):
            match_parse_args = pattern_parse_args.match(line)
            if match_parse_args:
                indent = match_parse_args.group(1)
                lines.insert(i, f'{indent}argcomplete.autocomplete(parser)\n')
                modified = True
                break

    if modified:
        with open(file_path, 'w') as file:
            file.writelines(lines)
        print(f"Modified file: {file_path}")
    
    return modified

def main(directory):
    if not os.path.isdir(directory):
        print(f"Error: {directory} is not a valid directory.")
        return

    count = 0
    for filename in os.listdir(directory):
        if filename.endswith('.py'):
            file_path = os.path.join(directory, filename)
            if modify_file(file_path):
                count += 1
    
    print(f"{count} files modified.")

if __name__ == "__main__":
    if len(sys.argv) != 2:
        print("Usage: ./addAutocomplete.py <directory>")
        sys.exit(1)

    directory = sys.argv[1]
    main(directory)

The Impacket examples should be located at ~/.local/pipx/venvs/impacket/bin/ for pipx. Do not run it against ~/.local/bin/, it will remove the symlinks used by pipx. If you aren’t sure where the files are located, run which <any impacket scripts>, then run ls -l on the output, that should show you the symlinks.

For example, running which psexec.py returned ~/.local/bin/psexec.py, and running ls -l ~/.local/bin/psexec.py shows that it points to ~/.local/bin/smbclient.py -> ~/.local/pipx/venvs/impacket/bin/smbclient.py, I will run the python script against that directory: ./addAutocomplete.py ~/.local/pipx/venvs/impacket/bin/

Finally, activate argcomplete for the examples:

eval "$(register-python-argcomplete addcomputer.py)"
eval "$(register-python-argcomplete atexec.py)"
eval "$(register-python-argcomplete changepasswd.py)"
eval "$(register-python-argcomplete dcomexec.py)"
eval "$(register-python-argcomplete describeTicket.py)"
eval "$(register-python-argcomplete dpapi.py)"
eval "$(register-python-argcomplete DumpNTLMInfo.py)"
eval "$(register-python-argcomplete esentutl.py)"
eval "$(register-python-argcomplete exchanger.py)"
eval "$(register-python-argcomplete findDelegation.py)"
eval "$(register-python-argcomplete GetADUsers.py)"
eval "$(register-python-argcomplete getArch.py)"
eval "$(register-python-argcomplete Get-GPPPassword.py)"
eval "$(register-python-argcomplete GetNPUsers.py)"
eval "$(register-python-argcomplete getPac.py)"
eval "$(register-python-argcomplete getST.py)"
eval "$(register-python-argcomplete getTGT.py)"
eval "$(register-python-argcomplete GetUserSPNs.py)"
eval "$(register-python-argcomplete goldenPac.py)"
eval "$(register-python-argcomplete karmaSMB.py)"
eval "$(register-python-argcomplete keylistattack.py)"
eval "$(register-python-argcomplete kintercept.py)"
eval "$(register-python-argcomplete lookupsid.py)"
eval "$(register-python-argcomplete machine_role.py)"
eval "$(register-python-argcomplete mimikatz.py)"
eval "$(register-python-argcomplete mqtt_check.py)"
eval "$(register-python-argcomplete mssqlclient.py)"
eval "$(register-python-argcomplete mssqlinstance.py)"
eval "$(register-python-argcomplete net.py)"
eval "$(register-python-argcomplete netview.py)"
eval "$(register-python-argcomplete ntfs-read.py)"
eval "$(register-python-argcomplete ntlmrelayx.py)"
eval "$(register-python-argcomplete ping6.py)"
eval "$(register-python-argcomplete ping.py)"
eval "$(register-python-argcomplete psexec.py)"
eval "$(register-python-argcomplete raiseChild.py)"
eval "$(register-python-argcomplete rbcd.py)"
eval "$(register-python-argcomplete rdp_check.py)"
eval "$(register-python-argcomplete registry-read.py)"
eval "$(register-python-argcomplete reg.py)"
eval "$(register-python-argcomplete rpcdump.py)"
eval "$(register-python-argcomplete rpcmap.py)"
eval "$(register-python-argcomplete sambaPipe.py)"
eval "$(register-python-argcomplete samrdump.py)"
eval "$(register-python-argcomplete secretsdump.py)"
eval "$(register-python-argcomplete services.py)"
eval "$(register-python-argcomplete smbclient.py)"
eval "$(register-python-argcomplete smbexec.py)"
eval "$(register-python-argcomplete smbrelayx.py)"
eval "$(register-python-argcomplete smbserver.py)"
eval "$(register-python-argcomplete ticketConverter.py)"
eval "$(register-python-argcomplete ticketer.py)"
eval "$(register-python-argcomplete tstool.py)"
eval "$(register-python-argcomplete wmiexec.py)"
eval "$(register-python-argcomplete wmipersist.py)"
eval "$(register-python-argcomplete wmiquery.py)"
eval "$(register-python-argcomplete dacledit.py)"

But wait, those are just a temporary activation, we will need to add that again every reboot or .*rc file is sourced.

For a more permanent solution run register-python-argcomplete psexec.py >> ~/.zshrc, which seems to be a lot faster than adding all those eval lines, it creates the following functions in the file, instead of running the commands one by one, I have added them all to the same functions, so just paste them in:

# Run something, muting output or redirecting it to the debug stream
# depending on the value of _ARC_DEBUG.
# If ARGCOMPLETE_USE_TEMPFILES is set, use tempfiles for IPC.
__python_argcomplete_run() {
    if [[ -z "${ARGCOMPLETE_USE_TEMPFILES-}" ]]; then
        __python_argcomplete_run_inner "$@"
        return
    fi
    local tmpfile="$(mktemp)"
    _ARGCOMPLETE_STDOUT_FILENAME="$tmpfile" __python_argcomplete_run_inner "$@"
    local code=$?
    cat "$tmpfile"
    rm "$tmpfile"
    return $code
}

__python_argcomplete_run_inner() {
    if [[ -z "${_ARC_DEBUG-}" ]]; then
        "$@" 8>&1 9>&2 1>/dev/null 2>&1
    else
        "$@" 8>&1 9>&2 1>&9 2>&1
    fi
}

_python_argcomplete() {
    local IFS=$'\013'
    if [[ -n "${ZSH_VERSION-}" ]]; then
        local completions
        completions=($(IFS="$IFS" \
            COMP_LINE="$BUFFER" \
            COMP_POINT="$CURSOR" \
            _ARGCOMPLETE=1 \
            _ARGCOMPLETE_SHELL="zsh" \
            _ARGCOMPLETE_SUPPRESS_SPACE=1 \
            __python_argcomplete_run "${words[1]}") )
        _describe "${words[1]}" completions -o nosort
    else
        local SUPPRESS_SPACE=0
        if compopt +o nospace 2> /dev/null; then
            SUPPRESS_SPACE=1
        fi
        COMPREPLY=($(IFS="$IFS" \
            COMP_LINE="$COMP_LINE" \
            COMP_POINT="$COMP_POINT" \
            COMP_TYPE="$COMP_TYPE" \
            _ARGCOMPLETE_COMP_WORDBREAKS="$COMP_WORDBREAKS" \
            _ARGCOMPLETE=1 \
            _ARGCOMPLETE_SHELL="bash" \
            _ARGCOMPLETE_SUPPRESS_SPACE=$SUPPRESS_SPACE \
            __python_argcomplete_run "$1"))
        if [[ $? != 0 ]]; then
            unset COMPREPLY
        elif [[ $SUPPRESS_SPACE == 1 ]] && [[ "${COMPREPLY-}" =~ [=/:]$ ]]; then
            compopt -o nospace
        fi
    fi
}
if [[ -z "${ZSH_VERSION-}" ]]; then
    complete -o nospace -o default -o bashdefault -F _python_argcomplete nxc
    complete -o nospace -o default -o bashdefault -F _python_argcomplete certipy
    complete -o nospace -o default -o bashdefault -F _python_argcomplete addcomputer.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete atexec.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete changepasswd.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete dcomexec.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete describeTicket.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete dpapi.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete DumpNTLMInfo.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete esentutl.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete exchanger.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete findDelegation.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete GetADUsers.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete getArch.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete Get-GPPPassword.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete GetNPUsers.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete getPac.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete getST.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete getTGT.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete GetUserSPNs.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete goldenPac.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete karmaSMB.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete keylistattack.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete kintercept.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete lookupsid.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete machine_role.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete mimikatz.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete mqtt_check.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete mssqlclient.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete mssqlinstance.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete net.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete netview.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete ntfs-read.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete ntlmrelayx.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete ping6.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete ping.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete psexec.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete raiseChild.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete rbcd.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete rdp_check.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete registry-read.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete reg.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete rpcdump.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete rpcmap.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete sambaPipe.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete samrdump.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete secretsdump.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete services.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete smbclient.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete smbexec.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete smbrelayx.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete smbserver.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete ticketConverter.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete ticketer.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete tstool.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete wmiexec.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete wmipersist.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete wmiquery.py
    complete -o nospace -o default -o bashdefault -F _python_argcomplete dacledit.py
else
    compdef _python_argcomplete nxc
    compdef _python_argcomplete certipy
    compdef _python_argcomplete addcomputer.py
    compdef _python_argcomplete atexec.py
    compdef _python_argcomplete changepasswd.py
    compdef _python_argcomplete dcomexec.py
    compdef _python_argcomplete describeTicket.py
    compdef _python_argcomplete dpapi.py
    compdef _python_argcomplete DumpNTLMInfo.py
    compdef _python_argcomplete esentutl.py
    compdef _python_argcomplete exchanger.py
    compdef _python_argcomplete findDelegation.py
    compdef _python_argcomplete GetADUsers.py
    compdef _python_argcomplete getArch.py
    compdef _python_argcomplete Get-GPPPassword.py
    compdef _python_argcomplete GetNPUsers.py
    compdef _python_argcomplete getPac.py
    compdef _python_argcomplete getST.py
    compdef _python_argcomplete getTGT.py
    compdef _python_argcomplete GetUserSPNs.py
    compdef _python_argcomplete goldenPac.py
    compdef _python_argcomplete karmaSMB.py
    compdef _python_argcomplete keylistattack.py
    compdef _python_argcomplete kintercept.py
    compdef _python_argcomplete lookupsid.py
    compdef _python_argcomplete machine_role.py
    compdef _python_argcomplete mimikatz.py
    compdef _python_argcomplete mqtt_check.py
    compdef _python_argcomplete mssqlclient.py
    compdef _python_argcomplete mssqlinstance.py
    compdef _python_argcomplete net.py
    compdef _python_argcomplete netview.py
    compdef _python_argcomplete ntfs-read.py
    compdef _python_argcomplete ntlmrelayx.py
    compdef _python_argcomplete ping6.py
    compdef _python_argcomplete ping.py
    compdef _python_argcomplete psexec.py
    compdef _python_argcomplete raiseChild.py
    compdef _python_argcomplete rbcd.py
    compdef _python_argcomplete rdp_check.py
    compdef _python_argcomplete registry-read.py
    compdef _python_argcomplete reg.py
    compdef _python_argcomplete rpcdump.py
    compdef _python_argcomplete rpcmap.py
    compdef _python_argcomplete sambaPipe.py
    compdef _python_argcomplete samrdump.py
    compdef _python_argcomplete secretsdump.py
    compdef _python_argcomplete services.py
    compdef _python_argcomplete smbclient.py
    compdef _python_argcomplete smbexec.py
    compdef _python_argcomplete smbrelayx.py
    compdef _python_argcomplete smbserver.py
    compdef _python_argcomplete ticketConverter.py
    compdef _python_argcomplete ticketer.py
    compdef _python_argcomplete tstool.py
    compdef _python_argcomplete wmiexec.py
    compdef _python_argcomplete wmipersist.py
    compdef _python_argcomplete wmiquery.py
    compdef _python_argcomplete dacledit.py
fi

Here it is in action:

argcomplete-demo.gif argcomplete-demo.png