pendora-box/pendora-box.py

348 lines
10 KiB
Python

import json
import pathlib
import hashlib
import requests
import base64
import sys
from os import geteuid
import subprocess
from io import BytesIO
import zipfile
import rpmfile
import gzip
def compute_file_hash(filepath):
with open(filepath, 'rb') as file_for_hash:
data = file_for_hash.read()
filesize = len(data)
# Github sha value is computed as such:
return hashlib.sha1(b"blob " + bytes(str(filesize), 'utf-8') + b"\0" + data).hexdigest()
def get_master_info(repo, filepath, credz):
url = f"https://api.github.com/repos/{repo}/contents/{filepath}"
r = requests.get(url, auth=credz)
sha = r.json()['sha']
content = r.json()['content']
return sha, content
def get_last_release_info(repo, credz):
url = f"https://api.github.com/repos/{repo}/releases"
r = requests.get(url, auth=credz)
for release in r.json():
if not release['draft'] and not release['prerelease']:
return release['tag_name']
def extract_bin(archtype, binpath, destpath, content):
ioobj = BytesIO(content) # Get a File object from bytes ;)
if archtype == 'zip':
with zipfile.ZipFile(ioobj, "r") as zf:
filenames = zf.namelist()
for fn in filenames:
if binpath in fn:
with open(destpath, 'wb') as f:
f.write(zf.read(fn))
break
elif archtype == 'rpm':
with rpmfile.RPMFile(fileobj=ioobj) as rpm:
# Extract a fileobject from the archive
fd = rpm.extractfile(binpath)
with open(destpath, 'wb') as f:
f.write(fd.read())
elif archtype == 'gz':
with open(destpath, 'wb') as f:
f.write(gzip.decompress(content))
ioobj.close()
def githubmastersync(reponame, filepaths, credz):
for filepath in filepaths:
localfile = pathlib.Path('files').joinpath(pathlib.Path(filepath).name)
print(f" * {localfile} ", end='')
lastsha, content = get_master_info(reponame, filepath, credz)
if not localfile.exists():
content = base64.b64decode(content)
with open(localfile, 'wb') as f:
f.write(content)
print('-> Installed! ;)')
else:
sha = compute_file_hash(localfile)
if sha == lastsha:
print('-> Up-to-date.')
else:
content = base64.b64decode(content)
with open(localfile, 'wb') as f:
f.write(content)
print('-> Updated!')
def githubreleasesync(reponame, repoinfo, credz):
local_version = repoinfo['local_version']
last_version = get_last_release_info(reponame, credz)
short_version = last_version.replace('v', '')
filenames = repoinfo['files']
for filename in filenames:
if isinstance(filename, dict):
binpath = filename['binpath']
filename = filename['filename']
filename = filename.replace('{last_version}', last_version).replace('{short_version}', short_version)
localfile = pathlib.Path('files').joinpath(pathlib.Path(binpath).name)
if filename.endswith('.gz'):
is_gz = True
print(f" * {localfile} ", end='')
else:
filename = filename.replace('{last_version}', last_version).replace('{short_version}', short_version)
localfile = pathlib.Path('files').joinpath(pathlib.Path(filename).name)
print(f" * {localfile} ", end='')
urldl = f'https://github.com/{reponame}/releases/download/{last_version}/{filename}'
if not localfile.exists():
content = requests.get(urldl, auth=credz).content
if is_gz:
extract_bin('gz', binpath, localfile, content)
else:
with open(localfile, 'wb') as f:
f.write(content)
print('-> Installed! ;)')
else:
if local_version == last_version:
print('-> Up-to-date.')
else:
content = requests.get(urldl, auth=credz).content
if is_gz:
extract_bin('gz', binpath, localfile, content)
else:
with open(localfile, 'wb') as f:
f.write(content)
print('-> Updated!')
with open("config.json", "r") as jsonfile:
data = json.load(jsonfile)
data['githubreleasesync'][reponame]['local_version'] = last_version
with open("config.json", "w") as jsonfile:
json.dump(data, jsonfile, indent=4)
def ncatsync(conf):
r = requests.get('https://nmap.org/dist/')
last_version = r.text.split('The latest Nmap release is version ')[1].split('.\n')[0]
local_version = conf['local_version']
for filename in conf['files']:
localfile = pathlib.Path('files').joinpath(pathlib.Path(filename).name)
print(f" * {localfile} ", end='')
if filename == "ncat.exe":
archtype = 'zip'
binpath = 'ncat.exe'
destpath = 'files/ncat.exe'
urldl = f'https://nmap.org/dist/nmap-{last_version}-win32.zip'
elif filename == "ncat":
archtype = 'rpm'
binpath = './usr/bin/ncat'
destpath = 'files/ncat'
urldl = f'https://nmap.org/dist/ncat-{last_version}-1.x86_64.rpm'
if not localfile.exists():
content = requests.get(urldl).content
extract_bin(archtype, binpath, destpath, content)
print('-> Installed! ;)')
else:
if local_version == last_version:
print('-> Up-to-date.')
else:
content = requests.get(urldl).content
extract_bin(archtype, binpath, destpath, content)
with open("config.json", "r") as jsonfile:
data = json.load(jsonfile)
data['ncat']['local_version'] = last_version
with open("config.json", "w") as jsonfile:
json.dump(data, jsonfile, indent=4)
print('-> Updated!')
with open("config.json", "r") as jsonfile:
data = json.load(jsonfile)
data['ncat']['local_version'] = last_version
with open("config.json", "w") as jsonfile:
json.dump(data, jsonfile, indent=4)
def update(config):
print("Updating...")
with open("credz.json", "r") as jsonfile:
credz = json.load(jsonfile)
credz = (credz['username'], credz['token'])
for reponame, filepaths in config['githubmastersync'].items():
githubmastersync(reponame, filepaths, credz)
for reponame, repoinfo in config['githubreleasesync'].items():
githubreleasesync(reponame, repoinfo, credz)
ncatsync(config['ncat'])
make_executable()
def make_executable():
path = pathlib.Path('files')
for fpath in path.glob("*"):
if fpath.name != '.gitkeep':
fpath.chmod(0o777)
def print_menu(menu_options):
for key in menu_options.keys():
print(key, '->', menu_options[key])
def is_sudo():
return True if geteuid() == 0 else False
def ask_port(default_port):
while True:
try:
choice = input(f'What port would you like to listen on ? [{default_port}]: ')
if choice == '':
port = default_port
else:
port = int(choice)
break
except ValueError:
print('Wrong input. Please enter a number ...')
return port
def listen_http(files_dir):
port = ask_port(80)
ips = get_ips()
print('The HTTP server is about to start.\nA good start ;) :')
for iname in ips:
if port == 80:
print(f' -> {iname}: http://{ips[iname]}/linpeas.sh')
else:
print(f' -> {iname}: http://{ips[iname]}:{port}/linpeas.sh')
cmd = ['python', '-m', 'http.server', '-d', files_dir, str(port)]
if port < 1024 and not is_sudo():
print('Listening on any port under 1024 requires root permissions.')
cmd.insert(0, 'sudo')
subprocess.call(cmd)
def listen_smb(files_dir, version):
port = ask_port(445)
ips = get_ips()
print('The HTTP server is about to start.\nA good start ;) :')
for iname in ips:
if port == 445:
print(f' -> {iname}: \\\\{ips[iname]}\\share\\winPEASany.exe')
else:
print(f' -> {iname}: \\\\{ips[iname]}:{port}\\share\\winPEASany.exe # This syntax (:port) is not supported on Windows ?')
if version == 1:
cmd = ['smbserver.py', '-port', str(port), 'share', files_dir]
elif version == 2:
cmd = ['smbserver.py', '-smb2support', '-port', str(port), 'share', files_dir]
else:
sys.exit('Wrong SMB version')
if port < 1024 and not is_sudo():
print('Listening on any port under 1024 requires root permissions.')
cmd.insert(0, 'sudo')
subprocess.call(cmd)
def get_public_ip():
return requests.get('https://api.ipify.org').text
def get_ips():
ips = dict()
try:
ips['public'] = get_public_ip()
except requests.exceptions.ConnectionError:
pass
cmd = ['ip', 'a']
out = subprocess.run(cmd, capture_output=True).stdout.decode('utf-8')
lines = out.split('\n')
while("" in lines):
lines.remove("")
for line in lines:
if line[0].isdigit():
iname = line.split(' ')[1].split(':')[0]
if 'inet ' in line:
ip = line.split(' ')[5].split('/')[0]
if ip != '127.0.0.1':
ips[iname] = ip
return ips
def menu_choice(menu_options):
while(True):
print_menu(menu_options)
option = ''
try:
option = int(input('Enter your choice: '))
except ValueError:
print('Wrong input. Please enter a number ...')
files_dir = pathlib.Path.cwd().joinpath('files')
if option == 1:
listen_http(files_dir)
elif option == 2:
listen_smb(files_dir, 1)
elif option == 3:
listen_smb(files_dir, 2)
elif option == 0:
sys.exit('Quitting')
else:
print('Invalid option. Please enter a valid number.')
if __name__ == '__main__':
with open("config.json", "r") as jsonfile:
config = json.load(jsonfile)
update(config)
print('Choose a service to start a listener:')
menu_options = {
1: 'HTTP',
2: 'SMB1',
3: 'SMB2',
0: 'Exit',
}
menu_choice(menu_options)