malvbox 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,4 @@
1
+ .buildenv/
2
+ dist/
3
+ *.egg-info/
4
+ __pycache__/
malvbox-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,53 @@
1
+ Metadata-Version: 2.5
2
+ Name: malvbox
3
+ Version: 1.0.0
4
+ Summary: Exegol-like offensive container CLI wired to the malveillance platform VPN
5
+ License-Expression: MIT
6
+ Keywords: docker,exegol,offensive-security,pentest,wireguard
7
+ Requires-Python: >=3.8
8
+ Description-Content-Type: text/markdown
9
+
10
+ # malvbox (wrapper installable)
11
+
12
+ Distribue la commande `malvbox` **comme Exegol** : via `pipx`, sans que
13
+ l'utilisateur ne manipule un fichier script.
14
+
15
+ ## Pour l'utilisateur final
16
+
17
+ ```bash
18
+ pipx install malvbox # une fois publié sur PyPI
19
+ malvbox start redteam --vpn ~/malveillance.conf -s ~/partage
20
+ ```
21
+
22
+ Prérequis : `docker`, `bash` (déjà présents sous Linux/WSL/macOS), et le module
23
+ noyau `wireguard`. Le paquet n'embarque que le lanceur ; l'arsenal est dans
24
+ l'image Docker `ghcr.io/loic051n7/malvbox:default`, tirée automatiquement.
25
+
26
+ ## Pour toi (mainteneur)
27
+
28
+ Le paquet embarque le script bash canonique (`../malvbox`) et l'exécute. Pour
29
+ (re)construire après une modif du script :
30
+
31
+ ```bash
32
+ cd wrapper
33
+ ./build.sh # resync + build dans dist/
34
+ ./build.sh --publish # + upload PyPI (twine)
35
+ ```
36
+
37
+ Test local avant publication :
38
+
39
+ ```bash
40
+ pipx install ./wrapper
41
+ malvbox version
42
+ ```
43
+
44
+ ### Diffusion sans exposer le code source
45
+
46
+ - **PyPI public** (comme Exegol) : `pipx install malvbox`. Le code reste
47
+ téléchargeable (open source), mais l'utilisateur ne gère aucun fichier.
48
+ - **Index privé** (`--repository-url`) ou dépôt Git privé
49
+ (`pipx install git+https://…`) : diffusion restreinte si tu ne veux pas
50
+ rendre la source publique.
51
+
52
+ Dans tous les cas, l'image Docker (l'arsenal) reste sur GHCR et se pull toute
53
+ seule — rien d'autre à télécharger.
@@ -0,0 +1,44 @@
1
+ # malvbox (wrapper installable)
2
+
3
+ Distribue la commande `malvbox` **comme Exegol** : via `pipx`, sans que
4
+ l'utilisateur ne manipule un fichier script.
5
+
6
+ ## Pour l'utilisateur final
7
+
8
+ ```bash
9
+ pipx install malvbox # une fois publié sur PyPI
10
+ malvbox start redteam --vpn ~/malveillance.conf -s ~/partage
11
+ ```
12
+
13
+ Prérequis : `docker`, `bash` (déjà présents sous Linux/WSL/macOS), et le module
14
+ noyau `wireguard`. Le paquet n'embarque que le lanceur ; l'arsenal est dans
15
+ l'image Docker `ghcr.io/loic051n7/malvbox:default`, tirée automatiquement.
16
+
17
+ ## Pour toi (mainteneur)
18
+
19
+ Le paquet embarque le script bash canonique (`../malvbox`) et l'exécute. Pour
20
+ (re)construire après une modif du script :
21
+
22
+ ```bash
23
+ cd wrapper
24
+ ./build.sh # resync + build dans dist/
25
+ ./build.sh --publish # + upload PyPI (twine)
26
+ ```
27
+
28
+ Test local avant publication :
29
+
30
+ ```bash
31
+ pipx install ./wrapper
32
+ malvbox version
33
+ ```
34
+
35
+ ### Diffusion sans exposer le code source
36
+
37
+ - **PyPI public** (comme Exegol) : `pipx install malvbox`. Le code reste
38
+ téléchargeable (open source), mais l'utilisateur ne gère aucun fichier.
39
+ - **Index privé** (`--repository-url`) ou dépôt Git privé
40
+ (`pipx install git+https://…`) : diffusion restreinte si tu ne veux pas
41
+ rendre la source publique.
42
+
43
+ Dans tous les cas, l'image Docker (l'arsenal) reste sur GHCR et se pull toute
44
+ seule — rien d'autre à télécharger.
malvbox-1.0.0/build.sh ADDED
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env bash
2
+ # Resynchronise le script bash canonique puis construit le paquet malvbox.
3
+ # ./build.sh -> copie ../malvbox et build le wheel dans dist/
4
+ # ./build.sh --publish -> build + upload sur PyPI (twine)
5
+ # Utilise un venv local (.buildenv) pour éviter PEP 668 (externally-managed).
6
+ set -euo pipefail
7
+ cd "$(dirname "$0")"
8
+
9
+ cp ../malvbox src/malvbox/malvbox.sh
10
+ echo "✓ script synchronisé ($(wc -l < src/malvbox/malvbox.sh) lignes)"
11
+
12
+ VENV=.buildenv
13
+ [ -d "$VENV" ] || python3 -m venv "$VENV"
14
+ "$VENV/bin/pip" install --quiet --upgrade pip build twine
15
+
16
+ rm -rf dist
17
+ "$VENV/bin/python" -m build
18
+ echo "✓ paquet construit dans dist/"
19
+
20
+ if [ "${1:-}" = "--publish" ]; then
21
+ "$VENV/bin/twine" upload dist/*
22
+ echo "✓ publié"
23
+ fi
@@ -0,0 +1,22 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "malvbox"
7
+ version = "1.0.0"
8
+ description = "Exegol-like offensive container CLI wired to the malveillance platform VPN"
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ license = "MIT"
12
+ keywords = ["pentest", "docker", "exegol", "wireguard", "offensive-security"]
13
+
14
+ [project.scripts]
15
+ malvbox = "malvbox.cli:main"
16
+
17
+ [tool.hatch.build.targets.wheel]
18
+ packages = ["src/malvbox"]
19
+
20
+ # Embarque le script bash comme donnée de paquet (le point d'entrée l'exécute).
21
+ [tool.hatch.build.targets.wheel.force-include]
22
+ "src/malvbox/malvbox.sh" = "malvbox/malvbox.sh"
@@ -0,0 +1 @@
1
+ __version__ = "1.0.0"
@@ -0,0 +1,20 @@
1
+ """malvbox — thin launcher.
2
+
3
+ The whole CLI lives in the bundled ``malvbox.sh``; this entry point just execs
4
+ it with bash, forwarding all arguments. Packaging it this way lets users install
5
+ the command with ``pipx install malvbox`` (like Exegol) without ever handling a
6
+ raw script file.
7
+ """
8
+ import os
9
+ import shutil
10
+ import sys
11
+ from pathlib import Path
12
+
13
+
14
+ def main() -> None:
15
+ script = Path(__file__).with_name("malvbox.sh")
16
+ bash = shutil.which("bash")
17
+ if bash is None:
18
+ sys.exit("malvbox: 'bash' is required but was not found in PATH "
19
+ "(on Windows, run malvbox from WSL).")
20
+ os.execv(bash, [bash, str(script), *sys.argv[1:]])
@@ -0,0 +1,234 @@
1
+ #!/usr/bin/env bash
2
+ # ═════════════════════════════════════════════════════════════════════════════
3
+ # malvbox — Exegol-like CLI for the malveillance platform.
4
+ # One image (Kali + Exegol-Free arsenal + wordlists + resources) that wires
5
+ # straight onto the platform WireGuard VPN.
6
+ #
7
+ # malvbox start redteam --vpn ~/malveillance.conf -s ~/share
8
+ # malvbox list | stop <name> | rm <name> [--purge]
9
+ # malvbox pull | build
10
+ # ═════════════════════════════════════════════════════════════════════════════
11
+ set -euo pipefail
12
+
13
+ MALVBOX_VERSION="1.0"
14
+ IMAGE_REPO="${MALVBOX_IMAGE_REPO:-ghcr.io/loic051n7/malvbox}"
15
+ image_ref() { [ -n "${MALVBOX_IMAGE:-}" ] && echo "$MALVBOX_IMAGE" || echo "$IMAGE_REPO:default"; }
16
+ PREFIX="malvbox-"
17
+ SELF="$(readlink -f "${BASH_SOURCE[0]}" 2>/dev/null || echo "${BASH_SOURCE[0]}")"
18
+ HERE="$(cd "$(dirname "$SELF")" && pwd)"
19
+
20
+ c() { printf '\033[%sm%s\033[0m' "$1" "$2"; }
21
+ info() { echo "$(c '1;36' '›') $*"; }
22
+ err() { echo "$(c '1;31' '✗') $*" >&2; }
23
+ die() { err "$*"; exit 1; }
24
+ cname() { echo "${PREFIX}$1"; }
25
+
26
+ # ── Encadré "container summary" façon Exegol ─────────────────────────────────
27
+ BOX_L=16; BOX_V=64
28
+ _dl() { local s; s=$(printf '%*s' "$1" ''); printf '%s' "${s// /─}"; }
29
+ tbord() { printf '%s%s%s%s%s\n' "$1" "$(_dl 18)" "$2" "$(_dl $((BOX_V+2)))" "$3"; }
30
+ trow() { # label value [ansi-code]
31
+ local val="$2" n=${#2} disp
32
+ [ "$n" -gt "$BOX_V" ] && { val="${2:0:$((BOX_V-1))}…"; n=$BOX_V; }
33
+ disp="$val"; [ -n "${3:-}" ] && disp="$(c "$3" "$val")"
34
+ printf '│ %*s │ %s%*s │\n' "$BOX_L" "$1" "$disp" "$((BOX_V-n))" ''
35
+ }
36
+
37
+ usage() {
38
+ cat <<EOF
39
+ $(c '1;35' malvbox) — offensive box + VPN
40
+
41
+ Usage: malvbox <command> [options]
42
+
43
+ start <name> [--vpn <conf|url>] [-s <dir>]
44
+ create the box and open a shell — or, if it exists, start it and open
45
+ a shell. Run it again anytime to get back in. Graphical tools
46
+ (Firefox…) display on your screen automatically.
47
+ exec <name> <cmd…> run a command in the box (no interactive shell)
48
+ restart <name> restart the box and open a shell
49
+ stop <name> stop a box (workspace kept)
50
+ update <name> [start opts] pull the latest image and recreate the box
51
+ info <name> show a box's status and mounts
52
+ list list boxes and their state
53
+ rm <name> [--purge] remove a box (--purge: drop its workspace too)
54
+ pull pull the latest image
55
+ build build the image locally
56
+ version print malvbox and image version
57
+
58
+ start options
59
+ -s, --share share a host dir into the box (repeatable):
60
+ ~/loot -> /root/work/loot
61
+ ~/loot:/opt/x -> explicit mountpoint
62
+ ~/loot:ro -> read-only
63
+ --vpn WireGuard .conf path OR URL (/api/user/vpn/config)
64
+ --cookie session cookie if --vpn is a protected URL
65
+ --full-tunnel route all traffic through the VPN
66
+ --no-pull use the local image, skip pull
67
+ EOF
68
+ }
69
+
70
+ need_docker() { command -v docker >/dev/null || die "docker not found in PATH"; }
71
+
72
+ resolve_conf() { # <src> <cookie> -> local .conf path on stdout
73
+ local src="$1" cookie="${2:-}" tmp
74
+ if [[ "$src" =~ ^https?:// ]]; then
75
+ tmp="$(mktemp --suffix=.conf)"
76
+ if [ -n "$cookie" ]; then curl -fsSL --cookie "$cookie" "$src" -o "$tmp" || die "VPN download failed (bad cookie?)"
77
+ else curl -fsSL "$src" -o "$tmp" || die "VPN download failed (private config: pass --cookie)"; fi
78
+ grep -q '\[Interface\]' "$tmp" || die "downloaded file is not a WireGuard config"
79
+ echo "$tmp"
80
+ else
81
+ [ -f "$src" ] || die "VPN config not found: $src"
82
+ echo "$src"
83
+ fi
84
+ }
85
+
86
+ cmd_start() {
87
+ need_docker
88
+ local name="" vpn="" cookie="" full=0 nopull=0
89
+ local -a run_cmd=() shares=()
90
+ while [ $# -gt 0 ]; do case "$1" in
91
+ -n|--name) name="$2"; shift 2 ;;
92
+ --vpn) vpn="$2"; shift 2 ;;
93
+ --cookie) cookie="$2"; shift 2 ;;
94
+ --full-tunnel) full=1; shift ;;
95
+ -s|--share) shares+=("$2"); shift 2 ;;
96
+ --image) MALVBOX_IMAGE="$2"; shift 2 ;;
97
+ --no-pull) nopull=1; shift ;;
98
+ --) shift; run_cmd=("$@"); break ;;
99
+ -*) die "unknown option: $1" ;;
100
+ *) [ -z "$name" ] && { name="$1"; shift; } || die "unexpected argument: $1" ;;
101
+ esac; done
102
+ [ -n "$name" ] || { usage; die "a name is required: malvbox start <name>"; }
103
+ # Display forwarding is always on when the host exposes one (WSLg / X11), so
104
+ # graphical tools (Firefox…) can open their window on your screen.
105
+ local gui=0
106
+ { [ -d /mnt/wslg ] || [ -e /tmp/.X11-unix ] || [ -n "${DISPLAY:-}" ]; } && gui=1
107
+ local image cn; image="$(image_ref)"; cn="$(cname "$name")"
108
+
109
+ # Already running -> open another shell. Stopped -> start it, then shell.
110
+ if docker ps --format '{{.Names}}' | grep -qx "$cn"; then
111
+ exec docker exec -it "$cn" zsh
112
+ fi
113
+ if docker ps -a --format '{{.Names}}' | grep -qx "$cn"; then
114
+ docker start "$cn" >/dev/null; exec docker exec -it "$cn" zsh
115
+ fi
116
+
117
+ local -a args=(run -it --name "$cn" --hostname "$name"
118
+ -v "${PREFIX}${name}-work:/root/work"
119
+ --cap-add NET_ADMIN --device /dev/net/tun
120
+ --sysctl net.ipv4.conf.all.src_valid_mark=1)
121
+
122
+ # Shared dirs (host<->box, rw, live). Default mountpoint in the home (~), kept
123
+ # separate from ~/work where you actually work.
124
+ local sh hostp contp ro; local -a share_disp=()
125
+ for sh in "${shares[@]:-}"; do
126
+ [ -z "$sh" ] && continue
127
+ ro=""; case "$sh" in *:ro) ro=":ro"; sh="${sh%:ro}" ;; esac
128
+ if [[ "$sh" == *:/* ]]; then hostp="${sh%%:*}"; contp="${sh#*:}"
129
+ else hostp="$sh"; contp="/root/$(basename "$sh")"; fi
130
+ hostp="$(cd "$hostp" 2>/dev/null && pwd || echo "$hostp")"
131
+ [ -e "$hostp" ] || die "share not found: $hostp"
132
+ args+=(-v "$hostp:$contp$ro")
133
+ share_disp+=("$hostp -> $contp (${ro:+ro}${ro:-rw})")
134
+ done
135
+
136
+ # GUI: X11 forwarding + a root-owned runtime dir so Firefox runs as root.
137
+ if [ "$gui" = 1 ]; then
138
+ args+=(-e "DISPLAY=${DISPLAY:-:0}" -v /tmp/.X11-unix:/tmp/.X11-unix
139
+ -e XDG_RUNTIME_DIR=/run/user/0 -e MOZ_ENABLE_WAYLAND=0)
140
+ fi
141
+
142
+ # VPN: mount the .conf; the entrypoint brings the tunnel up.
143
+ local vpn_disp="none" conf=""
144
+ if [ -n "$vpn" ]; then
145
+ conf="$(resolve_conf "$vpn" "$cookie")"
146
+ args+=(-v "$conf:/vpn/wg.conf:ro")
147
+ vpn_disp="$conf"
148
+ if [ "$full" = 1 ]; then args+=(-e VPN_FULL_TUNNEL=1); vpn_disp="$conf (full-tunnel)"; fi
149
+ fi
150
+
151
+ if [ "$nopull" = 0 ]; then
152
+ docker pull "$image" >/dev/null 2>&1 || info "pull failed (offline?) — using local image"
153
+ fi
154
+
155
+ echo
156
+ echo "$(c '1;33' ★) $(c '1;37' 'Container summary')"
157
+ tbord ┌ ┬ ┐
158
+ trow Name "$name" '1;37'
159
+ trow Image "$image"
160
+ tbord ├ ┼ ┤
161
+ if [ "$gui" = 1 ]; then trow "Console GUI" "on ✓ (X11)" '1;32'; else trow "Console GUI" "off" '2'; fi
162
+ if [ "$vpn_disp" = none ]; then trow VPN "off" '2'; else trow VPN "$vpn_disp" '1;32'; fi
163
+ trow Network "bridge"
164
+ trow Resources "/opt/resources"
165
+ if [ ${#share_disp[@]} -gt 0 ]; then
166
+ trow Shares "${share_disp[0]}"
167
+ for ((i=1;i<${#share_disp[@]};i++)); do trow "" "${share_disp[i]}"; done
168
+ else
169
+ trow Shares "none" '2'
170
+ fi
171
+ trow Workspace "/root/work (${PREFIX}${name}-work)"
172
+ trow Privileged "off ✓ (cap NET_ADMIN)"
173
+ tbord └ ┴ ┘
174
+ echo
175
+
176
+ exec docker "${args[@]}" "$image" "${run_cmd[@]:-zsh}"
177
+ }
178
+
179
+ cmd_exec() { need_docker; [ $# -ge 2 ] || die "usage: malvbox exec <name> <cmd...>"
180
+ local name="$1"; shift
181
+ exec docker exec -it "$(cname "$name")" "$@"; }
182
+
183
+ cmd_restart() { need_docker; [ $# -ge 1 ] || die "usage: malvbox restart <name>"
184
+ local cn; cn="$(cname "$1")"; docker restart "$cn" >/dev/null || die "no such box: $1"
185
+ exec docker exec -it "$cn" zsh; }
186
+
187
+ cmd_update() { need_docker; [ $# -ge 1 ] || die "usage: malvbox update <name> [start options]"
188
+ local name="$1"; shift
189
+ info "pulling latest image…"; docker pull "$(image_ref)" || info "pull failed — keeping current image"
190
+ docker rm -f "$(cname "$name")" >/dev/null 2>&1 || true # workspace volume is kept
191
+ info "recreating '$name' on the latest image…"
192
+ cmd_start "$name" --no-pull "$@"; }
193
+
194
+ cmd_info() { need_docker; [ $# -ge 1 ] || die "usage: malvbox info <name>"
195
+ local cn; cn="$(cname "$1")"
196
+ docker ps -a --filter "name=^/${cn}$" --format 'BOX {{.Names}} · {{.Status}} · {{.Image}}' | sed "s/${PREFIX}//"
197
+ docker inspect "$cn" --format 'mounts:{{range .Mounts}}
198
+ {{if .Name}}{{.Name}}{{else}}{{.Source}}{{end}} -> {{.Destination}}{{end}}' 2>/dev/null || die "no such box: $1"; }
199
+
200
+ cmd_stop() { need_docker; [ $# -ge 1 ] || die "usage: malvbox stop <name>"
201
+ docker stop "$(cname "$1")" >/dev/null && info "stopped $1"; }
202
+
203
+ cmd_rm() { need_docker; [ $# -ge 1 ] || die "usage: malvbox rm <name> [--purge]"
204
+ local name="$1" purge=0; [ "${2:-}" = "--purge" ] && purge=1
205
+ docker rm -f "$(cname "$name")" >/dev/null 2>&1 || true
206
+ if [ "$purge" = 1 ]; then docker volume rm "${PREFIX}${name}-work" >/dev/null 2>&1 || true
207
+ info "removed $name (+ workspace)"
208
+ else info "removed $name (workspace kept)"; fi; }
209
+
210
+ cmd_list() { need_docker
211
+ docker ps -a --filter "name=^/${PREFIX}" \
212
+ --format 'table {{.Names}}\t{{.Status}}\t{{.Image}}' | sed "1s/NAMES/BOX/;s/${PREFIX}//"; }
213
+
214
+ cmd_pull() { need_docker; exec docker pull "$(image_ref)"; }
215
+ cmd_build() { need_docker
216
+ [ -f "$HERE/Dockerfile" ] || die "no Dockerfile next to this script — 'build' needs the source repo. To just use malvbox, run 'malvbox pull'."
217
+ # --progress=plain streams the per-repo clone counter ([ 42%] 71/168 …) live.
218
+ exec docker build --progress=plain -t "$(image_ref)" "$HERE"; }
219
+
220
+ case "${1:-}" in
221
+ start) shift; cmd_start "$@" ;;
222
+ exec) shift; cmd_exec "$@" ;;
223
+ restart) shift; cmd_restart "$@" ;;
224
+ stop) shift; cmd_stop "$@" ;;
225
+ update) shift; cmd_update "$@" ;;
226
+ info) shift; cmd_info "$@" ;;
227
+ list|ls) shift; cmd_list "$@" ;;
228
+ rm|remove) shift; cmd_rm "$@" ;;
229
+ pull) shift; cmd_pull "$@" ;;
230
+ build) shift; cmd_build "$@" ;;
231
+ version|--version|-v) echo "malvbox $MALVBOX_VERSION · image $(image_ref)" ;;
232
+ ""|-h|--help|help) usage ;;
233
+ *) err "unknown command: $1"; usage; exit 1 ;;
234
+ esac