start to work on a pipx install

This commit is contained in:
n1nj4sec 2022-11-04 18:03:21 +01:00
parent c8ea691a86
commit ffe2df59f6
24 changed files with 120 additions and 320 deletions

View File

@ -11,7 +11,7 @@ from pupylib.PupyOutput import Info, Warn, Success, Error
from pupylib.utils.listener import get_listener_ip, get_listener_port
from pupylib.utils.listener import get_listener_ip_with_local
import pupygen
from pupylib.cli import pupygen
usage = 'Generate payload'
@ -20,12 +20,14 @@ def parser(server, handler, config):
def do(server, handler, config, args):
handler.display(Info("Raw user arguments given for generation: {0}".format(str(args.launcher_args))))
if not args.launcher:
handler.display(Info("Launcher/connection method not given. It is set to 'connect' now"))
args.launcher = 'connect'
#launcher method 'connect' or 'auto_proxy'
if args.launcher and args.launcher in ('connect', 'auto_proxy'):
transport = None #For saving the transport method (default or given by user)
transport = config.get("pupyd", "listen") #For saving the transport method (default or given by user)
transport_idx = None
host = None #Host for listening point (not for launcher args)
port = None #Port for listening point (not for launcher args)

View File

@ -1,22 +0,0 @@
export PATH=$PATH:/bin:/usr/sbin:~/.local/bin
alias pupysh=/opt/pupy/pupysh.py
alias pupygen=/opt/pupy/pupygen.py
alias gen=/opt/pupy/pupygen.py
project=default
if [ -f /home/pupy/.project ]; then
project=`cat /home/pupy/.project`
fi
case $- in *i*)
if [ -z "$TMUX" ] && [ ! -z "$SSH_CLIENT" ]; then
echo -ne "\033]0;[ PUPY:${project} ]\007"
( tmux -2 attach || tmux -2 new-session \
-c "/projects/${project}" \
-s pupy \
-n "${project}" /opt/pupy/pupysh.py )
[ $? -eq 0 ] && exit 0
fi
esac

View File

@ -1,49 +0,0 @@
FROM debian:stretch-slim
LABEL maintainer "alxchk@gmail.com"
ENV DEBIAN_FRONTEND noninteractive
RUN echo 'deb http://ftp.debian.org/debian stretch-backports main' >>/etc/apt/sources.list && \
apt-get update && \
mkdir -p /usr/share/man/man1/ && \
apt-get install -t stretch-backports --no-install-recommends -y build-essential libssl1.0-dev libffi-dev \
python-dev python-pip openssh-server tmux sslh libcap2-bin \
john vim-tiny less osslsigncode nmap net-tools libmagic1 swig cython \
autoconf automake unzip libtool locales ncurses-term bash tcpdump libpam-cap netbase \
git fuse && \
apt-get clean && \
rm -rf /var/lib/apt/lists/* /usr/share/doc* /usr/share/man/* /usr/share/info/*
RUN echo 'en_US.UTF-8 UTF-8' >/etc/locale.gen; locale-gen; echo 'LC_ALL=en_US.UTF-8' >/etc/default/locale
RUN useradd -m -d /home/pupy -s /bin/bash pupy
RUN mkdir -p /var/run/sshd /home/pupy/.config/pupy /home/pupy/.ssh /projects
RUN ln -sf /projects/keys/authorized_keys /home/pupy/.ssh/authorized_keys
COPY conf/pupy.conf.docker /home/pupy/.config/pupy/pupy.conf
COPY conf/.bashrc /home/pupy/.bashrc.pupy
COPY conf/capability.conf /etc/security/capability.conf
RUN chmod +s /usr/sbin/tcpdump
RUN chown pupy:pupy -R /home/pupy; chmod 700 /home/pupy/.ssh
RUN echo 'source /home/pupy/.bashrc.pupy' >> /home/pupy/.bashrc
RUN python -m pip install --upgrade pip six setuptools wheel
COPY . /opt/pupy
RUN cd /opt/pupy && pip install --upgrade -r requirements.txt
ADD https://github.com/gentilkiwi/mimikatz/releases/download/2.2.0-20200519/mimikatz_trunk.zip \
/opt/mimikatz/mimikatz.zip
RUN cd /opt/mimikatz && unzip mimikatz.zip && rm -f mimikatz.zip
RUN mkdir /opt/uacme
RUN apt-get remove -y autoconf automake python-dev libtool build-essential && \
apt-get -y autoremove && rm -rf /root/.cache/pip && \
rm -f /etc/ssh/ssh_host_*; rm -f /tmp/requirements.txt
ENV LANG en_US.UTF-8
ENV LC_ALL en_US.UTF-8
EXPOSE 22 1080 5454 5454/udp 8080
VOLUME [ "/projects" ]
ENTRYPOINT [ "/opt/pupy/conf/pupyenv.sh" ]
CMD [ "default" ]

View File

@ -1,46 +0,0 @@
FROM debian:buster-slim
LABEL maintainer "alxchk@gmail.com"
ENV DEBIAN_FRONTEND noninteractive
RUN \
echo 'deb http://ftp.debian.org/debian buster-backports main' >> \
/etc/apt/sources.list && \
apt-get update --fix-missing && \
mkdir -p /usr/share/man/man1/ && \
apt-get install -t buster-backports --no-install-recommends -y \
libssl-dev libffi-dev python-dev python-pip swig \
unzip libtool locales ncurses-term tcpdump \
netbase fuse build-essential cython && apt-get clean && \
rm -rf /var/lib/apt/lists/* /usr/share/doc* /usr/share/man/* /usr/share/info/* && \
echo 'en_US.UTF-8 UTF-8' >/etc/locale.gen && \
locale-gen && echo 'LC_ALL=en_US.UTF-8' >/etc/default/locale
RUN \
mkdir -p /project && \
mkdir -p /pupy && \
mkdir -p /build
COPY requirements.txt /build/
COPY external/pykcp /build/external/pykcp
RUN \
python -m pip install --no-cache-dir --upgrade pip six setuptools wheel && \
cd /build && \
python -m pip install --no-cache-dir --upgrade -r requirements.txt
RUN \
apt-get remove -y \
autoconf automake libssl1.0-dev libffi-dev python-dev \
libtool build-essential && apt-get -y autoremove && \
rm -rf /root/.cache /build
ADD \
https://github.com/gentilkiwi/mimikatz/releases/download/2.2.0-20200519/mimikatz_trunk.zip \
/opt/mimikatz
ENV LANG en_US.UTF-8
ENV LC_ALL en_US.UTF-8
ENTRYPOINT [ "python", "-OB", "/pupy/pupysh.py", "--workdir", "/project" ]

View File

@ -1,2 +0,0 @@
cap_net_broadcast,cap_net_raw,cap_net_bind_service,cap_net_admin pupy
none *

View File

@ -1,67 +0,0 @@
[pupyd]
transport = ec4
port = 8080
ipv6 = false
igd = false
httpd = false
webserver = true
dnscnc = localhost:5454
use_gnome_keyring = false
allow_requests_to_external_services = false
[cmdline]
display_banner = yes
colors = yes
[httpd]
secret = true
[paths]
prefer_workdir = yes
downloads = data/downloads/%c
memstrings = data/memstrings/%c
searches = data/searches/%c
screenshots = data/screenshots/%c/%t
pcaps = data/pcaps/%c/%t.pcap
logs = data/logs/%c/%t-%M
creds = data/creds
crypto = crypto
wwwroot = data/wwwroot
webstatic = webstatic
records = data/%c
keystrokes = data/keylogger/%c/%t.log
mouseshots = data/mouselogger/%c/%w-%t.png
payload_output = output
plugins = data/plugindata
ad = data/ad/%c/%r/%n.json
[on_connect]
#run_module = gather/keylogger start
[default_viewers]
image_viewer = eog
sound_player = totem
[mimikatz]
exe_Win32=/opt/mimikatz/Win32/mimikatz.exe
exe_x64=/opt/mimikatz/x64/mimikatz.exe
[aliases]
info = get_info
pyexec = pyexec
exec = shell_exec
shell = interactive_shell
kill = process_kill
mount = drives
[listeners]
ssl = 443=8443
obfs3 = 9090
rsa = 9091
ec4 = 80=1234
kc4 = 123=1234
tcp_cleartext = 80=1234
udp_cleartext = 123=1234
websocket = 80=8081
http = 80=8080
ecm = 1235

View File

@ -1,67 +0,0 @@
#!/bin/sh
mkdir -p /projects/keys
mkdir -p /projects/hostkeys
chown root /projects/hostkeys
chmod 700 /projects/hostkeys
chown pupy /projects/keys
chmod 700 /projects/keys
if [ ! -f /projects/hostkeys/ssh_host_rsa_key ]; then
ssh-keygen -f /projects/hostkeys/ssh_host_rsa_key -N '' -t rsa
fi
if [ ! -f /projects/hostkeys/ssh_host_dsa_key ]; then
ssh-keygen -f /projects/hostkeys/ssh_host_dsa_key -N '' -t dsa
fi
if [ ! -f /projects/hostkeys/ssh_host_ecdsa_key ]; then
ssh-keygen -f /projects/hostkeys/ssh_host_ecdsa_key -N '' -t ecdsa
fi
if [ ! -f /projects/hostkeys/ssh_host_ed25519_key ]; then
ssh-keygen -f /projects/hostkeys/ssh_host_ed25519_key -N '' -t ed25519
fi
for k in /projects/hostkeys/*; do
cp -af $k /etc/ssh/
done
if [ ! -d "/projects/$1" ]; then
mkdir -p "/projects/$1"
chown pupy "/projects/$1"
fi
/sbin/setcap cap_net_broadcast,cap_net_raw,cap_net_bind_service+eip /usr/bin/python2.7
/usr/bin/python2.7 --version >/dev/null 2>/dev/null
if [ ! $? -eq 0 ]; then
echo "[!] Xattrs not supported"
echo "[!] You can start container with --priviliged option"
/sbin/setcap -r /usr/bin/python2.7
fi
echo "$1" >/home/pupy/.project
cd /opt/pupy
find -type f -exec md5sum {} ';' >/projects/integrity.txt
echo 'Copy your authorized_keys here!' >/projects/keys/README
cat >>/projects/README <<__EOF__
SSH user: pupy
Port: 22
cp ~/.ssh/authorized_keys /projects/keys/authorized_keys
Example:
mkdir /tmp/projects/keys
cp ~/.ssh/authorized_keys /projects/keys/authorized_keys
docker run -D -p 2022:22 -p 9999:9999 -v /tmp/projects:/projects pupy:latest
ssh -p 2022 pupy@127.0.0.1
__EOF__
/usr/sbin/sshd -D

View File

@ -1,17 +0,0 @@
#!/bin/sh
if [ ! -f /project/config/pupy.conf ]; then
echo "[+] Copy default configuration to config/pupy.conf"
mkdir -p /project/config/
cp -f /opt/pupy/conf/pupy.conf.docker /project/config/pupy.conf
fi
for dir in data crypto output; do
if [ ! -d /project/$dir ]; then
mkdir /project/$dir
fi
done
cd /project
exec /opt/pupy/pupysh.py

2
pupy/external/pykcp vendored

@ -1 +1 @@
Subproject commit fdca1d59f7a31540f30ca851b3d19156d29eef1c
Subproject commit ce9dff065b9e0c0eb942b1809c4fb48dbcc6f29c

View File

@ -12,7 +12,7 @@ from pupylib.PupyModule import config, PupyModule, PupyArgumentParser
from modules.lib.windows.memory_exec import exec_pe
from modules.lib.linux.exec_elf import mexec
import pupygen
from pupylib.cli import pupygen
__class_name__ = 'MemoryDuplicate'

View File

@ -3,7 +3,7 @@ from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
import pupygen
from pupylib.cli import pupygen
import time
import gzip

View File

@ -8,7 +8,7 @@ from __future__ import unicode_literals
import sys
import time
import pupygen
from pupylib.cli import pupygen
from pupylib.payloads.dependencies import Target

View File

@ -13,7 +13,7 @@ from io import open
from os import unlink
from threading import Event
from pupygen import generate_binary_from_template
from pupylib.cli.pupygen import generate_binary_from_template
from pupylib.payloads.dotnet import DotNetPayload
DEFAULT_TIMEOUT = 90

View File

@ -26,7 +26,7 @@ from __future__ import print_function
from __future__ import unicode_literals
from pupylib.PupyModule import config, PupyModule, PupyArgumentParser
from pupylib.PupyCompleter import remote_path_completer
import pupygen
from pupylib.cli import pupygen
__class_name__ = "Persistence"

View File

@ -68,7 +68,7 @@ allow_by_default = true
# Default ports and args
# Ensure ports are different for all enabled listeners
# Or remove listener(s) from pupyd.listen parameter
ssl = 443
ssl = 443=8443
obfs3 = 9090
rsa = 9091
ec4 = 80=1234

View File

@ -29,12 +29,15 @@ import random
import string
import datetime
import errno
import shutil
import os
from network.lib.convcompat import (
as_unicode_string, as_native_string
)
from .PupyLogger import getLogger
from pupylib import ROOT
logger = getLogger('config')
if sys.version_info.major > 2:
@ -100,16 +103,17 @@ class Tags(object):
class PupyConfig(RawConfigParser):
def __init__(self, config='pupy.conf'):
self.root = path.abspath(path.join(path.dirname(__file__), '..'))
self.root = path.abspath(ROOT)
self.user_root = path.expanduser(path.join('~', '.config', 'pupy'))
self.project_path = path.join('config', config)
self.default_file = path.join(self.root, config+'.default')
self.user_path = path.join(self.user_root, config)
if not os.path.exists(self.user_path):
shutil.copyfile(self.default_file, self.user_path)
logger.info("No default pupy config file, creating one in {}".format(self.user_path))
self.files = [
path.join(self.root, config+'.default'),
path.join(self.root, config),
self.user_path,
self.project_path,
config
self.default_file,
self.user_path
]
self.randoms = {}
self.command_line = {}
@ -121,7 +125,7 @@ class PupyConfig(RawConfigParser):
self.read_file(open(file, 'r'))
logger.info(
'Loaded config data from %s', self.files
'Loaded config data from %s', file
)
except (IOError, OSError) as e:
if e.errno == errno.EEXIST:

View File

@ -1234,6 +1234,7 @@ class PupyServer(object):
if error:
del self.listeners[name]
self.handler.display_error(error)
self.display(message, error, motd)

View File

@ -11,12 +11,39 @@ __all__ = [
import os
import sys
import platform
import re
ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
HOST_SYSTEM = platform.system()
HOST_CPU_ARCH = platform.architecture()[0]
HOST_OS_ARCH = platform.machine()
USE_PIPX = False
# hotpatch for pipx installs. TODO: do this a cleaner way
if "/pipx/venv" in ROOT:
res = re.findall("(.*/pipx/venvs/[^/]+)", ROOT)
if len(res) == 1:
ROOT = os.path.join(res[0], "data")
USE_PIPX = True
if USE_PIPX:
LIBDIR = os.path.join(ROOT, "pupy")
else:
LIBDIR = ROOT
DEPS = [
os.path.abspath(os.path.join(LIBDIR, 'library_patches_py3')),
os.path.abspath(os.path.join(LIBDIR, 'packages', 'all')),
]
for dep in DEPS:
if not os.path.exists(dep):
raise Exception("Dependency path not found : {}".format(dep))
if "library_patches" in dep:
sys.path.insert(0, dep)
else:
sys.path.append(dep)
# dirty, TODO: refactor PupyCompiler to be able to call it standalone
if not getattr(sys, '__from_build_library_zip_compiler__', False):
from .PupyLogger import getLogger

View File

View File

@ -21,12 +21,6 @@ import tempfile
import shutil
import subprocess
if __name__ == '__main__':
sys.path.insert(0, os.path.join(
os.path.abspath(os.path.join(os.path.dirname(__file__))),
'library_patches'
))
import marshal
import base64
import os
@ -62,7 +56,7 @@ from pupylib.PupyCredentials import Credentials, EncryptionError
logger = getLogger('gen')
HARDCODED_CONF_SIZE = 500000
ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__)))
from pupylib import ROOT
if sys.version_info.major > 2:
unicode = str
@ -777,6 +771,7 @@ def pupygen(args, config, pupsrv, display):
scriptlets = load_scriptlets(args.os, args.arch)
if args.list:
print("ok")
display(MultiPart([
Table(
[{

View File

@ -45,12 +45,7 @@ import argparse
args = None
if __name__ == '__main__':
ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__)))
sys.path.insert(0, os.path.join(ROOT, 'library_patches_py3'))
sys.path.append(os.path.join(ROOT, 'packages', 'all'))
def parse_args():
parser = argparse.ArgumentParser(prog='pupysh', description="Pupy console")
parser.add_argument(
'--loglevel', '-d',
@ -72,8 +67,21 @@ if __name__ == '__main__':
help='Do not encrypt configuration', action='store_true')
parser.add_argument('--sound', dest='sounds',
help='Play a sound when a session connects', action='store_true')
args = parser.parse_args()
return parser
try:
import pupylib.PupySignalHandler
assert pupylib.PupySignalHandler
except ImportError:
pass
from pupylib import (
PupyServer, PupyCmdLoop, PupyCredentials, PupyConfig
)
def main():
parser = parse_args()
args = parser.parse_args()
if args.workdir:
os.chdir(args.workdir)
@ -99,18 +107,6 @@ if __name__ == '__main__':
root_logger.addHandler(logging_stream)
root_logger.setLevel(args.loglevel)
try:
import pupylib.PupySignalHandler
assert pupylib.PupySignalHandler
except ImportError:
pass
from pupylib import (
PupyServer, PupyCmdLoop, PupyCredentials, PupyConfig
)
if __name__ == "__main__":
PupyCredentials.DEFAULT_ROLE = 'CONTROL'
if args.not_encrypt:
PupyCredentials.ENCRYPTOR = None
@ -143,3 +139,5 @@ if __name__ == "__main__":
pupyServer.stop()
pupyServer.finished.wait()
if __name__ == "__main__":
main()

View File

@ -9,7 +9,7 @@ from base64 import b64encode
from time import sleep
import sys
import pupygen
from pupylib.cli import pupygen
import socket
import errno

View File

@ -5,7 +5,7 @@ rsa
netaddr
ecdsa==0.13
paramiko==2.0.2
https://github.com/alxchk/tinyec/archive/master.zip
tinyec @ https://github.com/alxchk/tinyec/archive/master.zip
psutil
netifaces
pylzma
@ -19,8 +19,8 @@ http-parser
cerberus
logutils
secretstorage==2.3.1
https://github.com/AlessandroZ/pypykatz/archive/master.zip
https://github.com/warner/python-ed25519/archive/master.zip
pypykatz @ https://github.com/AlessandroZ/pypykatz/archive/master.zip
ed25519==1.5
pygments
requests
tornado
@ -40,10 +40,10 @@ xattr
dukpy
pyaes
chardet
https://github.com/alxchk/urllib-auth/archive/master.zip
urllib-auth @ https://github.com/alxchk/urllib-auth/archive/master.zip
http_request
-e external/pykcp
kcp @ git+https://github.com/n1nj4sec/pykcp
flake8
flake8-per-file-ignores
ushlex; python_version<'3'
git+https://github.com/n1nj4sec/pyuv@fix-building-against-python311
pyuv @ git+https://github.com/n1nj4sec/pyuv@fix-building-against-python311

43
setup.py Normal file
View File

@ -0,0 +1,43 @@
#!/usr/bin/env python3
# -*- coding: UTF8 -*-
from setuptools import setup, find_packages
import os
import sys
requirements = [x.strip() for x in open("requirements.txt", "r").readlines()]
#requirements = [f"{line.split('#egg=')[-1]} @ {line}" if "#egg=" in line else line for line in requirements]
def generate_data_files():
data_files = [("data", ["pupy/pupy.conf.default"])]
data_dirs = ('pupy/library_patches_py3', 'pupy/library_patches_py2', 'pupy/packages')
for data_dir in data_dirs:
for path, dirs, files in os.walk(data_dir):
if "__pycache__" in path:
continue
install_dir = os.path.join("data", path)
list_entry = (install_dir, [os.path.join(path, f) for f in files if not f.startswith('.')])
data_files.append(list_entry)
return data_files
setup(
name='pupy',
version='3.0.0',
packages=find_packages(where='pupy', include=['pupy*', 'pupylib*', 'network*', 'commands*', 'modules*', 'scriptlets*', 'triggers*']),
package_dir={"": "pupy"},
data_files=generate_data_files(),
license_files = ('LICENSE'),
author='n1nj4sec',
author_email='contact@n1nj4.eu',
description='Pupy C2 is an opensource, cross-platform (Windows, Linux, OSX, Android) remote administration and post-exploitation tool in python',
#long_description='Pupy C2 Framework',
#long_description_content_type='text/x-rst',
url='https://github.com/n1nj4sec/pupy',
keywords=["python", "pentest", "cybersecurity", "redteam", "C2", "command and control", "post-exploitation"],
entry_points={
'console_scripts': [
'pupysh = pupylib.cli.pupysh:main'
]
},
install_requires=requirements
)