Skip to content

Commit 2f6b925

Browse files
committed
docs: rework README framing, fix factual drift, expand security notes
- Minimal header: drop shields, keep the two-line tagline - Reframe origins: inspired by p0wny-shell's concept and Weevely's transport/module ideas, not a 'fork' - Weevely section: fix stale claims (3 themes -> 11 + poly + none, modules list), drop the 'use X when' split, state full parity as the project goal and point the gaps to TODO.md - Standardize on the exact 275-name pool size everywhere - Security notes: document the env-var bcrypt build, and the honest caveats (rc4 = obfuscation, --seed determinism, --no-auth, hex fallback) - CI/CD: condense, fix shell count (10), mention seed reproducibility check - Legal disclaimer: standard authorized-use wording with jurisdictional refs - TODO: add CLI client, SQL console, proxy/pivot as Weevely-parity gaps
1 parent 53a263c commit 2f6b925

2 files changed

Lines changed: 46 additions & 56 deletions

File tree

README.md

Lines changed: 40 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,9 @@
11
<div id="top" align="center">
22

3-
[![License][license-shield]](LICENSE)
4-
[![Python][python-shield]](https://www.python.org/)
5-
[![PHP][php-shield]](https://www.php.net/)
6-
[![Release][release-shield]](https://github.com/franckferman/p0wnyShellX/releases)
7-
[![CI][ci-shield]](https://github.com/franckferman/p0wnyShellX/actions)
3+
# p0wnyShellX
84

5+
**Polymorphic single-file PHP webshell generator.**
96

10-
11-
**Polymorphic PHP webshell generator for authorized red team operations.**
127
*A unique shell on every run. No two deployments share the same signature.*
138

149
</div>
@@ -17,16 +12,16 @@
1712

1813
## What is p0wnyShellX
1914

20-
p0wnyShellX is a **polymorphic generator** for post-exploitation PHP webshells, forked and heavily extended from [p0wny-shell](https://github.com/flozz/p0wny-shell).
15+
p0wnyShellX is a **polymorphic generator** for post-exploitation PHP webshells. The project started from the [p0wny-shell](https://github.com/flozz/p0wny-shell) concept — a single self-contained PHP file that gives you a full terminal in the browser, with nothing to install on the operator machine. That idea is excellent; its limitations showed quickly in real engagement conditions: every deployment is byte-for-byte identical, there is no authentication layer, and the routing endpoints are hardcoded — all of it trivially signatureable. The transport obfuscation and the compile-time optional modules were inspired by [Weevely](https://github.com/epinna/weevely3) (per-session obfuscated request encoding, features baked in only when requested), rethought for a browser-first workflow.
2116

22-
The original p0wny-shell and most derivatives ship a **static file** — every deployment is byte-for-byte identical, making YARA/AV/SIEM detection trivial. p0wnyShellX solves this: instead of a static webshell, you run a Python generator that produces a **unique PHP file every time**, with randomized function names, routing tokens, junk code, bcrypt-hashed credentials, CSS themes, and a configurable AJAX transport layer.
17+
The result: instead of shipping a static webshell, you run a Python generator that produces a **unique PHP file on every run** randomized function names, routing tokens, junk code, bcrypt-hashed credentials, CSS camouflage themes, and a configurable AJAX transport layer. What reaches the target is a file that has never existed before and will never exist again.
2318

2419
```mermaid
2520
flowchart LR
2621
A["python3 p0wnyShellX.py<br/>-p 'MyPass!' -o shell.php"]
2722
2823
subgraph engine ["Polymorphic Engine"]
29-
B1["Random function names<br/>PHP + JS — 250+ pool"]
24+
B1["Random function names<br/>PHP + JS — 275-name pool"]
3025
B2["Random routing tokens<br/>?x4r9tz=k2m8jvn"]
3126
B3["Bcrypt hash — cost=12<br/>plaintext never stored"]
3227
B4["Junk functions<br/>20–80 decoys per run"]
@@ -55,48 +50,48 @@ flowchart LR
5550
| Password storage || bcrypt cost=12 (via `password_verify`) |
5651
| Timing-safe auth || `hash_equals` on username + `password_verify` |
5752
| Static signature | Yes — every deploy identical | **No** — every deploy unique |
58-
| Function names | Fixed (`featureShell`, etc.) | Random from 250+ business name pool |
53+
| Function names | Fixed (`featureShell`, etc.) | Random draw from a 275-name business pool |
5954
| JS variable names | Fixed | Random |
6055
| HTML element IDs | Fixed | Random tokens |
6156
| Routing endpoints | Fixed `?feature=shell` | Random tokens (e.g. `?x4r9tz=k2m8jvn`) |
6257
| Junk code | None | 20–80 dynamically generated decoy functions |
6358
| Exec fallback order | Fixed | Weighted shuffle per run + random method drop |
59+
| AJAX transport | Cleartext, fixed parameter names | `plain` / `mimic` / `rc4` — per-build keys and parameter names |
60+
| Optional modules || `revshell`, `clearlog`, `portscan`, `pingsweep` — compiled in only when requested |
6461
| CSS camouflage | Transparent webshell | 11 camouflage themes + `poly` (random palette per build) + `none` (no CSS) |
6562
| Password in file || bcrypt hash only — plaintext never stored |
66-
| Reproductible builds | Yes (static) | Via `--seed` flag |
63+
| Reproducible builds | Yes (static) | Via `--seed` flag |
6764

6865
---
6966

7067
## Compared to Weevely
7168

72-
[Weevely](https://github.com/epinna/weevely3) is the reference CLI webshell for red teamers. The two tools solve different problems — they can complement each other.
69+
[Weevely](https://github.com/epinna/weevely3) is the reference CLI webshell framework. p0wnyShellX borrows two of its ideas — obfuscated transport and features compiled in on demand — and drops the rest of its model: no Python client on the operator machine, no per-target agent protocol, just a browser tab.
7370

7471
| Feature | Weevely | p0wnyShellX |
7572
|---|---|---|
76-
| Authentication | MD5(password) as XOR key | bcrypt cost=12 + `password_verify` |
73+
| Authentication | MD5(password) used as the XOR transport key | bcrypt cost=12 + `password_verify`, decoupled from transport |
7774
| Polymorphism | Variable shuffling + random string chunks | Business names, routing tokens, junk functions, bcrypt salt, exec order |
78-
| Communication | XOR+gzip+base64 in POST body, obfuscated header/footer | 3 modes: `plain` (cleartext), `mimic` (base64 + random param names), `rc4` (RC4 + per-build shuffled base64 alphabet) |
75+
| Communication | XOR+gzip+base64 in POST body, obfuscated header/footer | 3 modes, both directions: `plain` (cleartext), `mimic` (base64 + random param names), `rc4` (RC4 + per-build shuffled base64 alphabet) |
7976
| Interface | Python CLI client | Browser terminal — no tooling on operator machine |
80-
| Camouflage | Bare PHP snippet | Fake monitoring dashboard (3 themes) |
81-
| Modules | 30+ (reverse shell, SQL, net scan, proxy…) | Shell, upload, download, tab-complete, reverse shell, log clearing, port scan |
77+
| Camouflage | Bare PHP snippet | 11 themed fake dashboards + `poly` (random palette per build) + `none` |
78+
| Modules | ~30 (SQL console, proxy, audit, bruteforce…) | Core: shell, upload, download, tab-complete. Opt-in at build time: `revshell`, `clearlog`, `portscan`, `pingsweep` |
8279
| Exec methods | 9 — `exec`, `shell_exec`, `system`, `passthru`, `popen`, `proc_open`, `pcntl_fork`, `python_eval`, `perl_system` — shuffled | 4–6 per build — `exec`, `shell_exec`, `system` always present; `passthru`, `popen`, `proc_open` randomly dropped (~30% each); weighted order (reliable methods tend first) |
83-
| `disable_functions` bypass | Yes — mod_cgi + `.htaccess` (Apache only, requires `AllowOverride` + write access) | No — not planned as a priority; the technique requires Apache + mod_cgi + AllowOverride + web-writable directory, which are rarely all met in prod |
80+
| `disable_functions` bypass | Yes — mod_cgi + `.htaccess` (Apache only, requires `AllowOverride` + write access) | No — not planned as a priority; the technique requires Apache + mod_cgi + AllowOverride + a web-writable directory, conditions rarely all met in production |
8481
| Reverse shell | Yes | Yes — `revshell <IP> <PORT>` (bash → python3 → perl → php, first available) |
8582
| Log clearing | Yes | Yes — `clearlog <file> <pattern>` strips matching lines in-place |
86-
| Port scan | Yes | Yes — `portscan <ip[-range]> <ports>` via fsockopen from the target host |
87-
| SQL console | Yes | No |
88-
89-
**Use Weevely when**: you need CLI automation, module ecosystem (SQL, reverse shell, scan), or obfuscated HTTP transport matters more than visual camouflage.
83+
| Port scan / host discovery | Yes (`:net_scan`) | Yes — `portscan` and `pingsweep`, via fsockopen from the target host |
84+
| SQL console | Yes (`:sql_console`) | Not yet — see TODO |
9085

91-
**Use p0wnyShellX when**: browser access is your only option, per-deploy unique signatures are the priority, or themed camouflage helps the shell survive visual inspection.
86+
**Where Weevely still leads, and the plan to close it.** The remaining gaps are a non-interactive CLI client for scripting and automation (p0wnyShellX is browser-only by design today), a SQL console module, proxy/pivoting, and XOR+gzip+base64 transport. They are tracked in [TODO.md](TODO.md)the stated goal of the project is full parity, so that "Weevely *or* a browser shell" stops being a choice you have to make.
9287

9388
---
9489

9590
## How the polymorphism works
9691

9792
### 1. Function name randomization
9893

99-
Every PHP and JS function is assigned a name drawn at random from a pool of 250+ plausible business names (`archiveReplicationLog`, `fetchComplianceStatus`, `validateSchemaCompatibility`…). A new mapping is generated on each run.
94+
Every PHP and JS function is assigned a name drawn at random from a pool of 275 plausible business names (`archiveReplicationLog`, `fetchComplianceStatus`, `validateSchemaCompatibility`…). A new mapping is generated on each run.
10095

10196
```
10297
# Run 1 # Run 2
@@ -233,7 +228,7 @@ A YARA rule targeting exec methods needs to commit to a specific set: "file cont
233228

234229
### 5. Dynamic junk code
235230

236-
Between 20 and 80 decoy PHP functions are generated per run, drawn from 20 body templates × 250+ name combinations, with randomized return values, loop counts, and string literals. They are scattered around the functional core to increase noise ratio.
231+
Between 20 and 80 decoy PHP functions are generated per run, drawn from 20 body templates × 275 name combinations, with randomized return values, loop counts, and string literals. They are scattered around the functional core to increase noise ratio.
237232

238233
### 6. AJAX transport modes
239234

@@ -492,36 +487,29 @@ Omit `--theme` to pick at random from the named themes below (`poly` and `none`
492487

493488
---
494489

495-
---
496-
497490
## CI/CD
498491

499-
Every push to a version tag (`v*.*.*`) triggers a GitHub Actions workflow that:
500-
501-
1. Installs Python 3.11 and PHP 8.3
502-
2. Generates an example shell with default credentials
503-
3. Runs `php -l` syntax validation
504-
4. Verifies absence of static signatures
505-
5. Publishes a GitHub Release with `p0wnyShellX.py` and the example shell as assets
492+
On every push and PR, a GitHub Actions suite generates 10 shells across the option matrix (named themes, `poly`, `none`, junk levels, transport modes, optional modules) and asserts: `php -l` passes on all of them, no static signature survives, two consecutive runs never produce the same file while two runs with the same `--seed` produce byte-identical ones, mimic mode randomizes parameter names, rc4 mode injects `tEnc`/`tDec` with no plaintext parameter left, and the exec fallback chain stays intact.
506493

507-
On every push/PR, the CI also runs a polymorphism validation suite:
508-
- Generates 9 shells (named themes, `--theme poly`, `--theme none`, junk levels, and transport modes)
509-
- Checks PHP syntax on all
510-
- Confirms no static signatures remain
511-
- Confirms two consecutive runs produce different output
512-
- Verifies mimic transport uses randomized param names
513-
- Verifies rc4 transport injects `tEnc`/`tDec` and no plain param names
514-
- Verifies exec fallback chain integrity on all non-transport shells
494+
On a version tag (`v*.*.*`), a second workflow additionally publishes a GitHub Release with `p0wnyShellX.py` and a freshly generated example shell as assets.
515495

516496
---
517497

518498
## Security notes
519499

520-
- The bcrypt hash in the generated file is irreversible without brute-force
521-
- `hash_equals` on username prevents timing oracle attacks
522-
- `password_verify` is constant-time for the password comparison
523-
- Session uses `cookie_httponly`, `use_strict_mode`, `cookie_samesite: Lax`
524-
- Wrong password triggers a random 400–700ms delay (anti-bruteforce)
500+
**On the generated file**
501+
502+
- The password exists nowhere in plaintext. The bcrypt hash (cost=12) is computed at build time by the local PHP CLI; the password is handed to the PHP process through an environment variable, never as a command-line argument — it does not leak through `ps`.
503+
- Username comparison goes through `hash_equals`, password through `password_verify`: both comparisons are constant-time, no timing oracle.
504+
- Sessions are started with `cookie_httponly`, `use_strict_mode` and `cookie_samesite=Lax`.
505+
- A failed login sleeps a random 400–700 ms before answering — enough to make online brute-force impractical without penalizing legitimate use.
506+
507+
**Caveats worth knowing before you deploy**
508+
509+
- The `rc4` transport is **obfuscation, not encryption**. The key and the shuffled alphabet are embedded in the file: anyone holding the shell can decode the traffic. It defeats regex-based WAF inspection, not an analyst.
510+
- `--seed` makes a build fully deterministic — **including the bcrypt salt**. Ideal for CI and testing; never reuse a seeded build on a real target.
511+
- `--no-auth` removes the login form, the session and the hash: anyone who reaches the URL gets a shell. Reserve it for throwaway lab use, and pair it with an unguessable filename.
512+
- If PHP is absent from the operator machine at build time, the generator falls back to a reversible hex encoding of the password and prints a loud warning — do not deploy a shell built that way.
525513

526514
---
527515

@@ -533,7 +521,11 @@ On every push/PR, the CI also runs a polymorphism validation suite:
533521

534522
## Legal disclaimer
535523

536-
This tool is intended for **authorized penetration testing, red team engagements, and security research only**. Use it only on systems you own or have explicit written permission to test. Unauthorized use against systems you do not own is illegal. The author assumes no liability for misuse.
524+
This software is provided for **authorized penetration testing, red team engagements, and security research only**. You may use it exclusively on systems you own, or on systems for which you hold explicit, prior, written authorization from the owner.
525+
526+
Unauthorized access to — or interference with — computer systems is a criminal offense in most jurisdictions (among others: the Computer Fraud and Abuse Act in the United States, articles 323-1 et seq. of the French Penal Code, the Computer Misuse Act in the United Kingdom, and the equivalent provisions transposing EU Directive 2013/40/EU).
527+
528+
This tool is provided *as is*, without warranty of any kind, express or implied. The author assumes no liability for any damage, data loss, or legal consequence arising from the use or misuse of this software. By using it, you accept sole responsibility for your actions and for complying with all applicable laws and regulations.
537529

538530
---
539531

@@ -542,10 +534,3 @@ This tool is intended for **authorized penetration testing, red team engagements
542534
GNU Affero General Public License v3.0 — see [LICENSE](LICENSE).
543535

544536
---
545-
546-
<!-- SHIELDS -->
547-
[license-shield]: https://img.shields.io/github/license/franckferman/p0wnyShellX.svg?style=for-the-badge
548-
[python-shield]: https://img.shields.io/badge/Python-3.8+-3776AB?style=for-the-badge&logo=python&logoColor=white
549-
[php-shield]: https://img.shields.io/badge/PHP-8.x-777BB4?style=for-the-badge&logo=php&logoColor=white
550-
[release-shield]: https://img.shields.io/github/v/release/franckferman/p0wnyShellX?style=for-the-badge
551-
[ci-shield]: https://img.shields.io/github/actions/workflow/status/franckferman/p0wnyShellX/ci.yml?style=for-the-badge&label=CI

TODO.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
# TODO / Roadmap
22

3-
## Planned
3+
**Project goal: full Weevely parity** — one generator that covers both the browser-terminal and the CLI-framework use cases, so the two tools no longer have to be combined.
4+
5+
## Planned (Weevely parity gaps)
46

57
- [ ] Obfuscated HTTP protocol — XOR+gzip+base64 for request and response payloads (à la Weevely), making POST body opaque to WAF inspection
8+
- [ ] Non-interactive CLI client — scripted, pipeable command execution against a deployed shell (automation, à la Weevely client); the browser stays the primary interface, the CLI is for tooling chains
9+
- [ ] SQL console module — interactive SQL queries against databases reachable from the target (à la `:sql_console`)
10+
- [ ] Proxy/pivot module — route operator traffic through the target host (à la `:net_proxy`)
611

712
## Low priority / won't implement soon
813

0 commit comments

Comments
 (0)