Compare commits
16 Commits
36c46920ec
...
master
Author | SHA1 | Date | |
---|---|---|---|
91b5ce8f28 | |||
eefc1e7aa1 | |||
6a5e28dc1c | |||
b9790c7d9f | |||
89450cd2fe | |||
926676eb1b | |||
18eaa61dda | |||
5144bf6c79 | |||
bd5d7a2647 | |||
e8eb9803d8 | |||
3c92425126 | |||
0e3d6c171e | |||
9c090991c0 | |||
46b68a17b8 | |||
e235f7a72b | |||
78bebf19d5 |
25
README.md
25
README.md
@ -6,15 +6,20 @@ This too has several functions:
|
||||
|
||||
## To-Do
|
||||
|
||||
* Add chisel x64
|
||||
* Integrate msfvenom
|
||||
* Add Webshells sync
|
||||
* Ability to add comments or description
|
||||
* Prettier code
|
||||
* Only update once per day, except if `-u` `--update`
|
||||
* Adding more services to listen to ?
|
||||
|
||||
## Dependencies
|
||||
## Pre-requisites
|
||||
|
||||
* requests python module
|
||||
* rpmfile python module
|
||||
* impacket smbserver.py must be in PATH
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
[Create a Github personal access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token), and create the file `credz.json`:
|
||||
@ -26,9 +31,23 @@ This too has several functions:
|
||||
}
|
||||
```
|
||||
|
||||
`python pendora-box.py`
|
||||
```
|
||||
user@wow$ python pendora-box.py -h
|
||||
usage: pendora-box.py [-h] [-u]
|
||||
|
||||
Sync your files and starts a listener on HTTP, SMB or SMB2.
|
||||
|
||||
options:
|
||||
-h, --help show this help message and exit
|
||||
-u, --update update your files (described in config.json)
|
||||
```
|
||||
|
||||
## Adding a file to track
|
||||
|
||||
Simply add the informations to [config.json](./config.json), for a release set "local_version" to a random value and run the script.
|
||||
The file is gonna be automatically downloaded.
|
||||
|
||||
## Share a file temporarly
|
||||
|
||||
You can add a file you wish to share once in `files/tmp`.
|
||||
Every time you start the script, you will be asked if you want to clear the folder (if not empty).
|
113
config.json
113
config.json
@ -14,16 +14,111 @@
|
||||
"churrasco.exe"
|
||||
],
|
||||
"SecWiki/windows-kernel-exploits": [
|
||||
"MS11-046/ms11-046.exe"
|
||||
"MS11-046/ms11-046.exe",
|
||||
"MS11-046/MS11_46_k8.exe",
|
||||
"MS10-059/MS10-059.exe"
|
||||
],
|
||||
"flozz/p0wny-shell": [
|
||||
"shell.php"
|
||||
],
|
||||
"int0x33/nc.exe": [
|
||||
"nc64.exe"
|
||||
],
|
||||
"BloodHoundAD/BloodHound": [
|
||||
"Collectors/SharpHound.exe"
|
||||
],
|
||||
"PowerShellMafia/PowerSploit": [
|
||||
"Recon/PowerView.ps1"
|
||||
],
|
||||
"Flangvik/SharpCollection": [
|
||||
"NetFramework_4.7_Any/Rubeus.exe"
|
||||
],
|
||||
"calebstewart/CVE-2021-1675": [
|
||||
"CVE-2021-1675.ps1"
|
||||
],
|
||||
"besimorhino/powercat": [
|
||||
"powercat.ps1"
|
||||
],
|
||||
"r3motecontrol/Ghostpack-CompiledBinaries": [
|
||||
"Seatbelt.exe"
|
||||
]
|
||||
},
|
||||
"githubreleasesync": {
|
||||
"carlospolop/PEASS-ng": {
|
||||
"local_version": "20220417",
|
||||
"local_version": "20230419-b6aac9cb",
|
||||
"files": [
|
||||
"linpeas.sh",
|
||||
"winPEAS.bat",
|
||||
"winPEASany.exe"
|
||||
"winPEASany.exe",
|
||||
"winPEASany_ofs.exe"
|
||||
]
|
||||
},
|
||||
"AlessandroZ/LaZagne": {
|
||||
"local_version": "v2.4.5",
|
||||
"files": [
|
||||
"lazagne.exe"
|
||||
]
|
||||
},
|
||||
"DominicBreuker/pspy": {
|
||||
"local_version": "v1.2.1",
|
||||
"files": [
|
||||
"pspy32",
|
||||
"pspy64"
|
||||
]
|
||||
},
|
||||
"itm4n/PrintSpoofer": {
|
||||
"local_version": "v1.0",
|
||||
"files": [
|
||||
"PrintSpoofer32.exe",
|
||||
"PrintSpoofer64.exe"
|
||||
]
|
||||
},
|
||||
"PowerShell/Win32-OpenSSH": {
|
||||
"local_version": "v9.2.2.0p1-Beta",
|
||||
"files": [
|
||||
"OpenSSH-Win32-{nobeta_version}.msi",
|
||||
"OpenSSH-Win64-{nobeta_version}.msi"
|
||||
]
|
||||
},
|
||||
"elddy/NimScan": {
|
||||
"local_version": "1.0.8",
|
||||
"files": [
|
||||
"NimScan.exe"
|
||||
]
|
||||
},
|
||||
"jpillora/chisel": {
|
||||
"local_version": "v1.8.1",
|
||||
"files": [
|
||||
{
|
||||
"filename": "chisel_{short_version}_windows_386.gz",
|
||||
"inpath": "chisel.exe",
|
||||
"outpath": "chisel.exe"
|
||||
},
|
||||
{
|
||||
"filename": "chisel_{short_version}_linux_386.gz",
|
||||
"inpath": "chisel",
|
||||
"outpath": "chisel"
|
||||
},
|
||||
{
|
||||
"filename": "chisel_{short_version}_linux_amd64.gz",
|
||||
"inpath": "chisel64",
|
||||
"outpath": "chisel64"
|
||||
}
|
||||
]
|
||||
},
|
||||
"gentilkiwi/mimikatz": {
|
||||
"local_version": "2.2.0-20220919",
|
||||
"files": [
|
||||
{
|
||||
"filename": "mimikatz_trunk.zip",
|
||||
"inpath": "Win32/mimikatz.exe",
|
||||
"outpath": "mimikatz32.exe"
|
||||
},
|
||||
{
|
||||
"filename": "mimikatz_trunk.zip",
|
||||
"inpath": "x64/mimikatz.exe",
|
||||
"outpath": "mimikatz64.exe"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
@ -33,5 +128,17 @@
|
||||
"ncat.exe",
|
||||
"ncat"
|
||||
]
|
||||
},
|
||||
"netcat": {
|
||||
"local_version": "1.12",
|
||||
"files": [
|
||||
"nc.exe"
|
||||
]
|
||||
},
|
||||
"plink": {
|
||||
"local_version": "0.78",
|
||||
"files": [
|
||||
"w32/plink.exe"
|
||||
]
|
||||
}
|
||||
}
|
232
pendora-box.py
Normal file → Executable file
232
pendora-box.py
Normal file → Executable file
@ -1,14 +1,19 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import pathlib
|
||||
from pathlib import Path
|
||||
import hashlib
|
||||
import requests
|
||||
import base64
|
||||
import sys
|
||||
from os import geteuid
|
||||
from os import geteuid, chdir
|
||||
import subprocess
|
||||
from io import BytesIO
|
||||
import zipfile
|
||||
import rpmfile
|
||||
import gzip
|
||||
from bs4 import BeautifulSoup
|
||||
import re
|
||||
|
||||
|
||||
def compute_file_hash(filepath):
|
||||
@ -23,8 +28,8 @@ 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
|
||||
dlurl = r.json()['download_url']
|
||||
return sha, dlurl
|
||||
|
||||
|
||||
def get_last_release_info(repo, credz):
|
||||
@ -53,19 +58,22 @@ def extract_bin(archtype, binpath, destpath, content):
|
||||
|
||||
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)
|
||||
localfile = Path('files').joinpath(Path(filepath).name)
|
||||
print(f" * {localfile} ", end='')
|
||||
lastsha, content = get_master_info(reponame, filepath, credz)
|
||||
lastsha, dlurl = get_master_info(reponame, filepath, credz)
|
||||
|
||||
if not localfile.exists():
|
||||
content = base64.b64decode(content)
|
||||
r = requests.get(dlurl)
|
||||
with open(localfile, 'wb') as f:
|
||||
f.write(content)
|
||||
f.write(r.content)
|
||||
print('-> Installed! ;)')
|
||||
|
||||
else:
|
||||
@ -74,9 +82,9 @@ def githubmastersync(reponame, filepaths, credz):
|
||||
if sha == lastsha:
|
||||
print('-> Up-to-date.')
|
||||
else:
|
||||
content = base64.b64decode(content)
|
||||
r = requests.get(dlurl)
|
||||
with open(localfile, 'wb') as f:
|
||||
f.write(content)
|
||||
f.write(r.content)
|
||||
print('-> Updated!')
|
||||
|
||||
|
||||
@ -84,15 +92,41 @@ def githubreleasesync(reponame, repoinfo, credz):
|
||||
local_version = repoinfo['local_version']
|
||||
last_version = get_last_release_info(reponame, credz)
|
||||
|
||||
short_version = last_version.replace('v', '')
|
||||
nobeta_version = last_version.replace('p1-Beta', '') # See https://github.com/PowerShell/Win32-OpenSSH/releases
|
||||
|
||||
filenames = repoinfo['files']
|
||||
|
||||
for filename in filenames:
|
||||
localfile = pathlib.Path('files').joinpath(pathlib.Path(filename).name)
|
||||
urldl = f'https://github.com/{reponame}/releases/download/{last_version}/{filename}'
|
||||
if isinstance(filename, dict):
|
||||
inpath = filename['inpath']
|
||||
outpath = filename['outpath']
|
||||
filename = filename['filename']
|
||||
filename = filename.replace('{last_version}', last_version).replace('{short_version}', short_version).replace('{nobeta_version}', nobeta_version)
|
||||
localfile = Path('files').joinpath(outpath)
|
||||
print(f" * {localfile} ", end='')
|
||||
|
||||
else:
|
||||
filename = filename.replace('{last_version}', last_version).replace('{short_version}', short_version).replace('{nobeta_version}', nobeta_version)
|
||||
localfile = Path('files').joinpath(Path(filename).name)
|
||||
print(f" * {localfile} ", end='')
|
||||
|
||||
if filename.endswith('.gz'):
|
||||
is_gz, is_zip = True, False
|
||||
elif filename.endswith('.zip'):
|
||||
is_gz, is_zip = False, True
|
||||
else:
|
||||
is_gz, is_zip = False, False
|
||||
|
||||
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', inpath, localfile, content)
|
||||
elif is_zip:
|
||||
extract_bin('zip', inpath, localfile, content)
|
||||
else:
|
||||
with open(localfile, 'wb') as f:
|
||||
f.write(content)
|
||||
|
||||
@ -103,6 +137,11 @@ def githubreleasesync(reponame, repoinfo, credz):
|
||||
|
||||
else:
|
||||
content = requests.get(urldl, auth=credz).content
|
||||
if is_gz:
|
||||
extract_bin('gz', inpath, localfile, content)
|
||||
elif is_zip:
|
||||
extract_bin('zip', inpath, localfile, content)
|
||||
else:
|
||||
with open(localfile, 'wb') as f:
|
||||
f.write(content)
|
||||
print('-> Updated!')
|
||||
@ -122,7 +161,7 @@ def ncatsync(conf):
|
||||
local_version = conf['local_version']
|
||||
|
||||
for filename in conf['files']:
|
||||
localfile = pathlib.Path('files').joinpath(pathlib.Path(filename).name)
|
||||
localfile = Path('files').joinpath(Path(filename).name)
|
||||
print(f" * {localfile} ", end='')
|
||||
|
||||
if filename == "ncat.exe":
|
||||
@ -168,6 +207,103 @@ def ncatsync(conf):
|
||||
json.dump(data, jsonfile, indent=4)
|
||||
|
||||
|
||||
def netcatsync(conf):
|
||||
# https://eternallybored.org/misc/netcat/ seems to be the most stable from all tested. (04/2022)
|
||||
r = requests.get('https://eternallybored.org/misc/netcat/')
|
||||
soup = BeautifulSoup(r.content, 'lxml')
|
||||
versions = []
|
||||
for link in soup.find_all('a', {'href': re.compile('netcat.+win.+')}):
|
||||
version = link['href'].split('-')[-1].split('.zip')[0]
|
||||
versions.append(version)
|
||||
|
||||
last_version = max(versions)
|
||||
local_version = conf['local_version']
|
||||
|
||||
for filename in conf['files']:
|
||||
# nc64.exe is also available in the archive feel free to add it.
|
||||
localfile = Path('files').joinpath(Path(filename).name)
|
||||
print(f" * {localfile} ", end='')
|
||||
|
||||
archtype = 'zip'
|
||||
binpath = filename
|
||||
destpath = f'files/{filename}'
|
||||
urldl = f'https://eternallybored.org/misc/netcat/netcat-win32-{last_version}.zip'
|
||||
|
||||
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['netcat']['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['netcat']['local_version'] = last_version
|
||||
|
||||
with open("config.json", "w") as jsonfile:
|
||||
json.dump(data, jsonfile, indent=4)
|
||||
|
||||
|
||||
def plinksync(conf):
|
||||
r = requests.get('https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html')
|
||||
last_version = r.text.split('PuTTY.\nCurrently this is ')[1].split(', ')[0]
|
||||
local_version = conf['local_version']
|
||||
|
||||
for filename in conf['files']:
|
||||
localfile = Path('files').joinpath(Path(filename).name)
|
||||
print(f" * {localfile} ", end='')
|
||||
|
||||
urldl = 'https://the.earth.li/~sgtatham/putty/latest/w32/plink.exe'
|
||||
|
||||
if not localfile.exists():
|
||||
content = requests.get(urldl).content
|
||||
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).content
|
||||
with open(localfile, 'wb') as f:
|
||||
f.write(content)
|
||||
|
||||
with open("config.json", "r") as jsonfile:
|
||||
data = json.load(jsonfile)
|
||||
|
||||
data['plink']['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['plink']['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:
|
||||
@ -180,17 +316,59 @@ def update(config):
|
||||
for reponame, repoinfo in config['githubreleasesync'].items():
|
||||
githubreleasesync(reponame, repoinfo, credz)
|
||||
|
||||
ncatsync(config['ncat'])
|
||||
# ncatsync(config['ncat'])
|
||||
netcatsync(config['netcat'])
|
||||
plinksync(config['plink'])
|
||||
make_executable()
|
||||
|
||||
|
||||
def make_executable():
|
||||
path = pathlib.Path('files')
|
||||
path = Path('files')
|
||||
for fpath in path.glob("*"):
|
||||
if fpath.name != '.gitkeep':
|
||||
fpath.chmod(0o777)
|
||||
|
||||
|
||||
def yes_or_no(question, default="yes"):
|
||||
"""Ask a yes/no question via input() and return their answer.
|
||||
|
||||
"question" is a string that is presented to the user.
|
||||
"default" is the presumed answer if the user just hits <Enter>.
|
||||
It must be "yes" (the default), "no" or None (meaning
|
||||
an answer is required of the user).
|
||||
|
||||
The "answer" return value is True for "yes" or False for "no".
|
||||
"""
|
||||
valid = {"yes": True, "y": True, "ye": True, "no": False, "n": False}
|
||||
if default is None:
|
||||
prompt = " [y/n] "
|
||||
elif default == "yes":
|
||||
prompt = " [Y/n] "
|
||||
elif default == "no":
|
||||
prompt = " [y/N] "
|
||||
else:
|
||||
raise ValueError("invalid default answer: '%s'" % default)
|
||||
|
||||
while True:
|
||||
print(question + prompt)
|
||||
choice = input().lower()
|
||||
if default is not None and choice == "":
|
||||
return valid[default]
|
||||
elif choice in valid:
|
||||
return valid[choice]
|
||||
else:
|
||||
print("Please respond with 'yes' or 'no' " "(or 'y' or 'n').\n")
|
||||
|
||||
|
||||
def rmtree(root):
|
||||
for p in root.iterdir():
|
||||
if p.is_dir():
|
||||
rmtree(p)
|
||||
else:
|
||||
p.unlink()
|
||||
root.rmdir()
|
||||
|
||||
|
||||
def print_menu(menu_options):
|
||||
for key in menu_options.keys():
|
||||
print(key, '->', menu_options[key])
|
||||
@ -294,7 +472,7 @@ def menu_choice(menu_options):
|
||||
except ValueError:
|
||||
print('Wrong input. Please enter a number ...')
|
||||
|
||||
files_dir = pathlib.Path.cwd().joinpath('files')
|
||||
files_dir = Path.cwd().joinpath('files')
|
||||
|
||||
if option == 1:
|
||||
listen_http(files_dir)
|
||||
@ -303,16 +481,34 @@ def menu_choice(menu_options):
|
||||
elif option == 3:
|
||||
listen_smb(files_dir, 2)
|
||||
elif option == 0:
|
||||
sys.exit('Quitting')
|
||||
print('Quitting')
|
||||
sys.exit()
|
||||
else:
|
||||
print('Invalid option. Please enter a valid number.')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
chdir(Path(__file__).resolve().parent) # Change current dir to source location
|
||||
|
||||
parser = argparse.ArgumentParser(description='Sync your files and starts a listener on HTTP, SMB or SMB2.')
|
||||
parser.add_argument('-u', '--update', action='store_true', help='update your files (described in config.json)')
|
||||
args = parser.parse_args()
|
||||
|
||||
with open("config.json", "r") as jsonfile:
|
||||
config = json.load(jsonfile)
|
||||
|
||||
if args.update:
|
||||
update(config)
|
||||
tmp = Path('files/tmp')
|
||||
is_empty = not any(tmp.iterdir())
|
||||
if not is_empty:
|
||||
if yes_or_no("The folder 'files/tmp' is not empty. Would you like to remove the content ?", default="no"):
|
||||
for p in tmp.iterdir():
|
||||
if p.is_dir():
|
||||
rmtree(p)
|
||||
else:
|
||||
p.unlink()
|
||||
|
||||
print('Choose a service to start a listener:')
|
||||
menu_options = {
|
||||
|
Reference in New Issue
Block a user