mirror of
https://github.com/calebstewart/pwncat.git
synced 2024-11-27 19:04:15 +01:00
Merge branch 'master' of https://github.com/calebstewart/pwncat
This commit is contained in:
commit
1ffbc0e8e7
36
.github/pull_request_template.md
vendored
Normal file
36
.github/pull_request_template.md
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
## Description of Changes
|
||||||
|
|
||||||
|
Fixes #XXX.
|
||||||
|
|
||||||
|
**Please note any `noqa:` comments needed to appease flake8.**
|
||||||
|
|
||||||
|
## Major Changes Implemented:
|
||||||
|
-
|
||||||
|
-
|
||||||
|
-
|
||||||
|
|
||||||
|
## Pre-Merge Tasks
|
||||||
|
- [ ] Formatted all modified files w/ `python-black`
|
||||||
|
- [ ] Sorted imports for modified files w/ `isort`
|
||||||
|
- [ ] Ran `flake8` on repo, and fixed any new problems w/ modified files
|
||||||
|
- [ ] Ran `pytest` test cases
|
||||||
|
- [ ] Added brief summary of updates to CHANGELOG (under `[Unreleased]`)
|
||||||
|
|
||||||
|
**For issues with pre-merge tasks, see CONTRIBUTING.md**
|
||||||
|
|
||||||
|
<!--
|
||||||
|
If you are submitting a pull request for a new module, the following
|
||||||
|
information is also helpful:
|
||||||
|
|
||||||
|
## Platform and Environment Restrictions
|
||||||
|
(e.g. "Windows targets without HOTFIX XXXXXX")
|
||||||
|
|
||||||
|
## Full Qualified Module Name
|
||||||
|
(e.g. "linux.enumerate.system.network")
|
||||||
|
|
||||||
|
## Artifacts Generated
|
||||||
|
(e.g. "creates file at /etc/something.ini tracked w/ a tamper")
|
||||||
|
|
||||||
|
## Tested Targets
|
||||||
|
(e.g. "Tested on Windows 10 1604")
|
||||||
|
-->
|
10
.github/workflows/publish.yml
vendored
10
.github/workflows/publish.yml
vendored
@ -29,9 +29,19 @@ jobs:
|
|||||||
# They are stored in ~/.local/share/pwncat by default
|
# They are stored in ~/.local/share/pwncat by default
|
||||||
tar czvf pwncat-plugins.tar.gz --transform='s|.*pwncat/||' ~/.local/share/pwncat/*
|
tar czvf pwncat-plugins.tar.gz --transform='s|.*pwncat/||' ~/.local/share/pwncat/*
|
||||||
|
|
||||||
|
- name: Extract Release Body
|
||||||
|
run: |
|
||||||
|
# Grab tag name without the `v`
|
||||||
|
version=$(git describe --tags --abbrev=0 | sed 's/v//')
|
||||||
|
|
||||||
|
# build a changelog with just logs from this release
|
||||||
|
echo "## Changelog" >> this_version_changelog.md
|
||||||
|
cat CHANGELOG.md | sed -n '/^## \['"$version"'\]/,/^## /p' | head -n -1 | tail -n +2 >> this_version_changelog.md
|
||||||
|
echo "[Full Changelog](https://github.com/calebstewart/pwncat/blob/v$version/CHANGELOG.md)" >> this_version_changelog.md
|
||||||
- name: Publish Plugins
|
- name: Publish Plugins
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v1
|
||||||
with:
|
with:
|
||||||
files: "pwncat-plugins.tar.gz"
|
files: "pwncat-plugins.tar.gz"
|
||||||
|
body_path: this_version_changelog.md
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
36
CHANGELOG.md
Normal file
36
CHANGELOG.md
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
# Changelog
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
The Changelog starts with v0.4.1, because we did not keep one before that,
|
||||||
|
and simply didn't have the time to go back and retroactively create one.
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [0.4.2] - 2021-06-15
|
||||||
|
Quick patch release due to corrected bug in `ChannelFile` which caused command
|
||||||
|
output to be empty in some situations.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Fixed `linux.enumerate.system.network` to work with old and new style `ip`.
|
||||||
|
- Fixed `ChannelFile.recvinto` which will no longer raise `BlockingIOError` ([#126](https://github.com/calebstewart/pwncat/issues/126), [#131](https://github.com/calebstewart/pwncat/issues/131))
|
||||||
|
- Fixed sessions command with invalid session ID ([#130](https://github.com/calebstewart/pwncat/issues/130))
|
||||||
|
- Fixed zsh shell prompt color syntax ([#130](https://github.com/calebstewart/pwncat/issues/130))
|
||||||
|
### Added
|
||||||
|
- Added Pull Request template
|
||||||
|
- Added CONTRIBUTING.md
|
||||||
|
- Added `--version` option to entrypoint to retrieve pwncat version
|
||||||
|
- Added `latest` tag to documented install command to prevent dev installs
|
||||||
|
|
||||||
|
## [0.4.1] - 2021-06-14
|
||||||
|
### Added
|
||||||
|
- Differentiate prompt syntax for standard bash, zsh and sh ([#126](https://github.com/calebstewart/pwncat/issues/126))
|
||||||
|
- Added `-c=never` to `ip` command in `linux.enumerate.system.network`
|
||||||
|
([#126](https://github.com/calebstewart/pwncat/issues/126))
|
||||||
|
- Updated Dockerfile to properly build post-v0.4.0 releases ([#125](https://github.com/calebstewart/pwncat/issues/125))
|
||||||
|
- Added check for `nologin` shell to stop pwncat from accidentally
|
||||||
|
closing the session ([#116](https://github.com/calebstewart/pwncat/issues/116))
|
||||||
|
- Resolved all flake8 errors ([#123](https://github.com/calebstewart/pwncat/issues/123))
|
||||||
|
- Improved EOF handling for Linux file-writes ([#117](https://github.com/calebstewart/pwncat/issues/117))
|
124
CONTRIBUTING.md
Normal file
124
CONTRIBUTING.md
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
# Contributing
|
||||||
|
|
||||||
|
If you have an idea for a new feature or just want to help out with a bug
|
||||||
|
fix, please refer to this guide and follow the rules before submitting a
|
||||||
|
pull request.
|
||||||
|
|
||||||
|
## Submitting Issues
|
||||||
|
|
||||||
|
If you aren't a programmer or don't have the time to contribute code to the
|
||||||
|
project, we would still appreciate bug reports and feature requests. Please
|
||||||
|
use the appropriate issue type in the GitHub issue system to report either
|
||||||
|
bugs or feature requests.
|
||||||
|
|
||||||
|
When reporting bugs, ensure you include the current version pwncat you have
|
||||||
|
installed, what type of target/victim you are using, what payload you used
|
||||||
|
on the target to gain a shell, any relevant tracebacks, and of course
|
||||||
|
screenshots if they add context to your problem. In general, the more
|
||||||
|
information we have, the more chance there is we can fix the problem.
|
||||||
|
|
||||||
|
For feature requests, please be very specific on what you would like pwncat
|
||||||
|
to do. We can't read your mind, and English isn't perfect. If you are
|
||||||
|
interested in or willing to help implement your new feature, please explicitly
|
||||||
|
let us know. This will help in prioritizing the issue.
|
||||||
|
|
||||||
|
## Submitting Pull Requests
|
||||||
|
|
||||||
|
When submitting a pull request, ensure you have read through and comply with
|
||||||
|
these contributing rules. The pull request template should guide you through
|
||||||
|
the things that need done before merging code.
|
||||||
|
|
||||||
|
For help with running pre-merge tools, see the styling and formatting section
|
||||||
|
below. For running pytest test cases, see the testing section.
|
||||||
|
|
||||||
|
Before submitting your changes in a pull request, please add a brief one-line
|
||||||
|
summary to the `CHANGELOG.md` file under the `[Unreleased]` heading. This makes
|
||||||
|
releases more straightforward and bug fixes and features are added along the way.
|
||||||
|
For information on the format of the changelog, see
|
||||||
|
[Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
|
If you are submitting a bug fix, annotate this with `Fixes #XXX` replacing the
|
||||||
|
`XXX` with the issue number. This ensures that the issue will be closed once
|
||||||
|
the bug fix is merged. If your bug fix does not **completely** fix the issue,
|
||||||
|
do not use the `Fixes` keyword. Instead, mention the issue by number in your
|
||||||
|
pull request to ensure the link between the issue and pull request is clear.
|
||||||
|
|
||||||
|
## Versioning
|
||||||
|
|
||||||
|
pwncat follows Semantic Versioning. You can learn about the basics of semver
|
||||||
|
[here](https://semver.org). pwncat does not currently have any release
|
||||||
|
schedule, but in general the following rules apply:
|
||||||
|
|
||||||
|
- `PATCH` fixes are released whenever there is either significant aggregate of
|
||||||
|
bug fixes or when a significantly agregious bug is fixed. The decision for
|
||||||
|
what "significant" means will be decided by a project owner.
|
||||||
|
- `MINOR` releases are for added functionality. The pwncat API is relatively
|
||||||
|
stable, but still has not attained `v1.0.0` status, and therefore minor
|
||||||
|
releases could make breaking API changes. However, a concerted effort
|
||||||
|
should be made to make all changes backwards compatible.
|
||||||
|
|
||||||
|
As mentioned above, pwncat has not reached `v1.0.0` yet. As such, I don't have
|
||||||
|
rules yet for `MAJOR` version bumps. I will update this file as the situation
|
||||||
|
develops.
|
||||||
|
|
||||||
|
## Making Changes
|
||||||
|
|
||||||
|
In general, when contributing to a project on GitHub, you should work from a
|
||||||
|
branch. This helps organize your changes within the project. There are two
|
||||||
|
main branches which pwncat uses to organize contributions: `master` and the
|
||||||
|
next release branch (named like `release-vX.Y.Z`).
|
||||||
|
|
||||||
|
- Any bug fixes which do not add new features should be made targeting `master`.
|
||||||
|
- Any new features should be made targeting the latest `release-vX.Y.Z`.
|
||||||
|
|
||||||
|
When forking the repository to make contributions, you can work directly out
|
||||||
|
of your fork's `master` or `release` branches or fork them. When creating a
|
||||||
|
pull request, you must target the appropriate branch based on the intent of
|
||||||
|
your work.
|
||||||
|
|
||||||
|
Pull requests targeting the wrong branch will be retargeted, which could
|
||||||
|
cause issues while merging.
|
||||||
|
|
||||||
|
## Styling and Format
|
||||||
|
|
||||||
|
The majority of pwncat is written in Python. We use `python-black` to format
|
||||||
|
code in a consistent and readable manner. We recommend you install a Black
|
||||||
|
plugin for your editor or IDE to ensure all code is formatted prior to
|
||||||
|
opening a pull request.
|
||||||
|
|
||||||
|
Beyond Black, you should also run `isort` and `flake8` within your branch
|
||||||
|
prior to opening a pull request. `isort` will sort your imports to ensure
|
||||||
|
they are easy to read. `flake8` will notify you of some common Python
|
||||||
|
errors. pwncat has `flake8` and `isort` configurations, so the process is
|
||||||
|
as simple as running the associated tool.
|
||||||
|
|
||||||
|
Prior to creating a pull request, please run the following from the repository
|
||||||
|
root to ensure formatting is in order:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Automatically fixes imports
|
||||||
|
isort ./pwncat
|
||||||
|
# Automatically fixes formatting
|
||||||
|
black ./pwncat
|
||||||
|
# Warns of errors or other syntax problems
|
||||||
|
flake8
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing Your Changes
|
||||||
|
|
||||||
|
Testing pwncat is difficult. There are some unit tests implemented in `tests/`.
|
||||||
|
These tests can be executed with `pytest`, but you must provide suitable targets
|
||||||
|
for the testing framework. The `run-tests.sh` script uses `podman` to start two
|
||||||
|
containers to act as targets, and then runs all tests. One container is a Ubuntu
|
||||||
|
machine with a bind shell and the other is a CentOS container with a bind shell.
|
||||||
|
|
||||||
|
If you are creating Windows features, you can run the Windows tests as well by
|
||||||
|
manually providing a Windows bind shell target:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
WINDOWS_HOST=10.10.10.10 WINDOWS_BIND_PORT=4444 ./run-tests.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
The included unit tests are not great. They do not have a lot of coverage, but
|
||||||
|
they at least ensure that the basic automated functionality of pwncat is not
|
||||||
|
broken across some common target types.
|
@ -13,7 +13,8 @@ Once you have a working ``pip`` installation, you can install pwncat with the pr
|
|||||||
# A virtual environment is recommended
|
# A virtual environment is recommended
|
||||||
python -m venv /opt/pwncat
|
python -m venv /opt/pwncat
|
||||||
# Install pwncat within the virtual environment
|
# Install pwncat within the virtual environment
|
||||||
/opt/pwncat/bin/pip install git+https://github.com/calebstewart/pwncat
|
# Replace `latest` with a versioned tag if needed (e.g. `v0.4.0`)
|
||||||
|
/opt/pwncat/bin/pip install 'git+https://github.com/calebstewart/pwncat@latest#egg=pwncat'
|
||||||
# This allows you to use pwncat outside of the virtual environment
|
# This allows you to use pwncat outside of the virtual environment
|
||||||
ln -s /opt/pwncat/bin/pwncat /usr/local/bin
|
ln -s /opt/pwncat/bin/pwncat /usr/local/bin
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
import sys
|
import sys
|
||||||
import logging
|
import logging
|
||||||
import argparse
|
import argparse
|
||||||
|
import importlib.metadata
|
||||||
|
|
||||||
from rich import box
|
from rich import box
|
||||||
from rich.table import Table
|
from rich.table import Table
|
||||||
@ -23,6 +24,9 @@ def main():
|
|||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description="""Start interactive pwncat session and optionally connect to existing victim via a known platform and channel type. This entrypoint can also be used to list known implants on previous targets."""
|
description="""Start interactive pwncat session and optionally connect to existing victim via a known platform and channel type. This entrypoint can also be used to list known implants on previous targets."""
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--version", "-v", action="store_true", help="Show version number and exit"
|
||||||
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--download-plugins",
|
"--download-plugins",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
@ -78,6 +82,11 @@ def main():
|
|||||||
)
|
)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# Print the version number and exit.
|
||||||
|
if args.version:
|
||||||
|
print(importlib.metadata.version("pwncat"))
|
||||||
|
return
|
||||||
|
|
||||||
# Create the session manager
|
# Create the session manager
|
||||||
with pwncat.manager.Manager(args.config) as manager:
|
with pwncat.manager.Manager(args.config) as manager:
|
||||||
|
|
||||||
|
@ -14,11 +14,7 @@ from rich.progress import (
|
|||||||
import pwncat
|
import pwncat
|
||||||
from pwncat import util
|
from pwncat import util
|
||||||
from pwncat.util import console
|
from pwncat.util import console
|
||||||
from pwncat.commands import (
|
from pwncat.commands import Complete, Parameter, CommandDefinition
|
||||||
Complete,
|
|
||||||
Parameter,
|
|
||||||
CommandDefinition,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class Command(CommandDefinition):
|
class Command(CommandDefinition):
|
||||||
|
@ -6,7 +6,7 @@ from rich.table import Table, Column
|
|||||||
|
|
||||||
import pwncat
|
import pwncat
|
||||||
from pwncat.util import console
|
from pwncat.util import console
|
||||||
from pwncat.commands import CommandDefinition, Complete, Parameter
|
from pwncat.commands import Complete, Parameter, CommandDefinition
|
||||||
|
|
||||||
|
|
||||||
class Command(CommandDefinition):
|
class Command(CommandDefinition):
|
||||||
|
@ -12,12 +12,7 @@ from rich.progress import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
import pwncat
|
import pwncat
|
||||||
from pwncat.util import (
|
from pwncat.util import console, copyfileobj, human_readable_size, human_readable_delta
|
||||||
console,
|
|
||||||
copyfileobj,
|
|
||||||
human_readable_size,
|
|
||||||
human_readable_delta,
|
|
||||||
)
|
|
||||||
from pwncat.commands import Complete, Parameter, CommandDefinition
|
from pwncat.commands import Complete, Parameter, CommandDefinition
|
||||||
|
|
||||||
|
|
||||||
|
@ -5,9 +5,7 @@ import rich.markup
|
|||||||
|
|
||||||
import pwncat
|
import pwncat
|
||||||
from pwncat.db import Fact
|
from pwncat.db import Fact
|
||||||
from pwncat.facts.ability import (
|
from pwncat.facts.ability import build_gtfo_ability
|
||||||
build_gtfo_ability,
|
|
||||||
)
|
|
||||||
from pwncat.platform.linux import Linux
|
from pwncat.platform.linux import Linux
|
||||||
from pwncat.modules.enumerate import Schedule, EnumerateModule
|
from pwncat.modules.enumerate import Schedule, EnumerateModule
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
|
||||||
from pwncat.db import Fact
|
from pwncat.db import Fact
|
||||||
from pwncat.modules import Status
|
from pwncat.modules import Status
|
||||||
from pwncat.subprocess import CalledProcessError
|
from pwncat.subprocess import CalledProcessError
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
import rich.markup
|
import rich.markup
|
||||||
|
|
||||||
from pwncat.db import Fact
|
from pwncat.db import Fact
|
||||||
|
from pwncat.subprocess import CalledProcessError
|
||||||
from pwncat.platform.linux import Linux
|
from pwncat.platform.linux import Linux
|
||||||
from pwncat.modules.enumerate import Schedule, EnumerateModule
|
from pwncat.modules.enumerate import Schedule, EnumerateModule
|
||||||
|
|
||||||
@ -32,32 +33,36 @@ class Module(EnumerateModule):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
output = session.platform.run(
|
output = session.platform.run(
|
||||||
["ip", "-c=never", "addr"], capture_output=True, text=True
|
["ip", "-c=never", "addr"], capture_output=True, text=True, check=True
|
||||||
)
|
)
|
||||||
if output.stdout:
|
except CalledProcessError:
|
||||||
output = (
|
try:
|
||||||
line
|
output = session.platform.run(
|
||||||
for line in output.stdout.replace("\r\n", "\n").split("\n")
|
["ip", "addr"], capture_output=True, text=True, check=True
|
||||||
if line
|
|
||||||
)
|
)
|
||||||
|
except CalledProcessError:
|
||||||
interface = None
|
return
|
||||||
|
|
||||||
for line in output:
|
|
||||||
if not line.startswith(" "):
|
|
||||||
interface = line.split(":")[1].strip()
|
|
||||||
continue
|
|
||||||
|
|
||||||
if interface is None:
|
|
||||||
# This shouldn't happen. The first line should be an interface
|
|
||||||
# definition, but just in case
|
|
||||||
continue
|
|
||||||
|
|
||||||
line = line.strip()
|
|
||||||
if line.startswith("inet"):
|
|
||||||
address = line.split(" ")[1]
|
|
||||||
yield InterfaceData(self.name, interface, address)
|
|
||||||
|
|
||||||
return
|
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
pass
|
return
|
||||||
|
|
||||||
|
if output.stdout:
|
||||||
|
output = (
|
||||||
|
line for line in output.stdout.replace("\r\n", "\n").split("\n") if line
|
||||||
|
)
|
||||||
|
|
||||||
|
interface = None
|
||||||
|
|
||||||
|
for line in output:
|
||||||
|
if not line.startswith(" "):
|
||||||
|
interface = line.split(":")[1].strip()
|
||||||
|
continue
|
||||||
|
|
||||||
|
if interface is None:
|
||||||
|
# This shouldn't happen. The first line should be an interface
|
||||||
|
# definition, but just in case
|
||||||
|
continue
|
||||||
|
|
||||||
|
line = line.strip()
|
||||||
|
if line.startswith("inet"):
|
||||||
|
address = line.split(" ")[1]
|
||||||
|
yield InterfaceData(self.name, interface, address)
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
import shlex
|
import shlex
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
|
|
||||||
from pwncat.db import Fact
|
from pwncat.db import Fact
|
||||||
from pwncat.platform.linux import Linux
|
from pwncat.platform.linux import Linux
|
||||||
from pwncat.modules.enumerate import Schedule, EnumerateModule
|
from pwncat.modules.enumerate import Schedule, EnumerateModule
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
|
|
||||||
from pwncat.db import Fact
|
from pwncat.db import Fact
|
||||||
from pwncat.util import Init
|
from pwncat.util import Init
|
||||||
from pwncat.platform.linux import Linux
|
from pwncat.platform.linux import Linux
|
||||||
|
@ -1041,8 +1041,6 @@ class Linux(Platform):
|
|||||||
command += f" 2>{stderr}"
|
command += f" 2>{stderr}"
|
||||||
elif stderr == pwncat.subprocess.DEVNULL:
|
elif stderr == pwncat.subprocess.DEVNULL:
|
||||||
command += " 2>/dev/null"
|
command += " 2>/dev/null"
|
||||||
elif stderr == pwncat.subprocess.PIPE:
|
|
||||||
command += " 2>&1"
|
|
||||||
|
|
||||||
if isinstance(stdin, str):
|
if isinstance(stdin, str):
|
||||||
command += f" 0<{stdin}"
|
command += f" 0<{stdin}"
|
||||||
|
2
setup.py
2
setup.py
@ -20,7 +20,7 @@ dependency_links = []
|
|||||||
# Setup
|
# Setup
|
||||||
setup(
|
setup(
|
||||||
name="pwncat",
|
name="pwncat",
|
||||||
version="0.4.0a1",
|
version="0.4.2",
|
||||||
python_requires=">=3.8",
|
python_requires=">=3.8",
|
||||||
description="A fancy reverse and bind shell handler",
|
description="A fancy reverse and bind shell handler",
|
||||||
author="Caleb Stewart",
|
author="Caleb Stewart",
|
||||||
|
Loading…
Reference in New Issue
Block a user