checking in all work done so far because what if my SSD dies?

This commit is contained in:
brent s
2017-11-18 22:33:31 -05:00
parent b2109646f3
commit 9c528c4908
24 changed files with 820 additions and 114 deletions

49
net/addr/app/dnsinfo.py Normal file
View File

@@ -0,0 +1,49 @@
#!/usr/bin/env python3
# https://gist.github.com/akshaybabloo/2a1df455e7643926739e934e910cbf2e
import ipaddress
import dns # apacman -S python-dnspython
import ipwhois # apacman -S python-ipwhois
import whois # apacman -S python-ipwhois
class netTarget(object):
def __init__(self, target):
self.target = target
##!/usr/bin/env python3
#
#import pprint
#import dns
#import whois
#import ipwhois
#
#d = 'sysadministrivia.com' # A/AAAA
#d = 'autoconfig.sysadministrivia.com' # CNAME
#
#records = {'whois': None,
# 'ptr': None,
# 'allocation': None}
#
#def getWhois(domain):
# _w = whois.whois(d)
# records['whois'] = dict(_w)
# return()
#
#def getIps(domain):
# addrs = []
# for t in ('A', 'AAAA'):
# answers = dns.resolver.query(domain, t)
# for a in answers:
# try:
# addrs.append(a.address)
# except:
# pass
# return(addrs)
#
#def getPtr(addrs):
# for a in addrs:
# pass
#
#print(getIps(d))
##pprint.pprint()

210
net/dhcp/dhcpcdump.py Executable file
View File

@@ -0,0 +1,210 @@
#!/usr/bin/env python3
# See RFC 2131, Figure 1 and Table 1 (section 2)
# Much thanks to https://github.com/igordcard/dhcplease for digging into dhcpcd
# source for the actual file structure (and providing inspiration).
import argparse
import collections
import os
import re
import struct
from io import BytesIO
## DEFINE SOME PRETTY STUFF ##
class color(object):
PURPLE = '\033[95m'
CYAN = '\033[96m'
DARKCYAN = '\033[36m'
BLUE = '\033[94m'
GREEN = '\033[92m'
YELLOW = '\033[93m'
RED = '\033[91m'
BOLD = '\033[1m'
UNDERLINE = '\033[4m'
END = '\033[0m'
class packetParser(object):
def __init__(self, data):
## Set the segment labels and struct formats
self.fmt = collections.OrderedDict()
# In the below, 'cnt' is how large (in octets) the field is.
# 'fmt' is a struct format string (https://docs.python.org/3/library/struct.html#format-characters)
# "op" through "hops" (incl.) may actually be '8B' instead of '8c'.
self.fmt['op'] = {'cnt': 8, 'fmt': '8c'} # this will always be \x02
self.fmt['htype'] = {'cnt': 8, 'fmt': '8c'} # this will always be \x01
self.fmt['hlen'] = {'cnt': 8, 'fmt': '8c'}
self.fmt['hops'] = {'cnt': 8, 'fmt': '8c'}
self.fmt['xid'] = {'cnt': 32, 'fmt': '8I'}
self.fmt['secs'] = {'cnt': 16, 'fmt': '8H'}
self.fmt['flags'] = {'cnt': 16, 'fmt': '8H'}
# "ciaddr" through "giaddr" (incl.) may actually be '4c' instead of '4B'.
self.fmt['ciaddr'] = {'cnt': 4, 'fmt': '4B'}
self.fmt['yiaddr'] = {'cnt': 4, 'fmt': '4B'}
self.fmt['siaddr'] = {'cnt': 4, 'fmt': '4B'}
self.fmt['giaddr'] = {'cnt': 4, 'fmt': '4B'}
# "chaddr" through "file" (incl.) may actually be <#>c instead of <#>B.
self.fmt['chaddr'] = {'cnt': 16, 'fmt': '16B'} # first 6 bytes used for MAC addr of client
self.fmt['sname'] = {'cnt': 64, 'fmt': '64B'} # server host name (via BOOTP)
self.fmt['file'] = {'cnt': 128, 'fmt': '128B'} # the boot filename (for BOOTP)
# OPTIONS - RFC 2132
# Starting at octet 320 (so, f.seek(319, 0)) to the end of the message are
# DHCP options. It's a variable-length field so it makes things tricky
# for us. But it's at *least* 312 octets long per the RFC?
# It probably starts with a magic.
#self.dhcp_opts = {'idx': 324, 'cnt': 4, 'fmt': '4c'}
#self.dhcp_opts = {'idx': 324, 'cnt': 4, 'fmt': None}
self.opts = {'magic': b'\x63\x82\x53\x63',
'struct': {'idx': 324, 'cnt': 4, 'fmt': '4B'},
'size': 0,
'bytes': b'\00'}
## Convert the data into a bytes object because struct.unpack() wants a stream
self.buf = BytesIO(data)
def getStd(self):
self.reconstructed_segments = collections.OrderedDict()
_idx = 0 # add to this with the 'cnt' value for each iteration.
for k in self.fmt.keys():
print('Segment: ' + k) # TODO: remove, this stuff goes in the printer
pkt = struct.Struct(self.fmt[k]['fmt'])
self.buf.seek(_idx, 0)
try:
self.reconstructed_segments[k] = pkt.unpack(self.buf.read(self.fmt[k]['cnt']))
except struct.error as e:
# Some DHCP implementations are... broken.
# I've noticed it mostly in Verizon Fi-OS gateways/WAPs/routers.
print('Warning({0}): {1}'.format(k, e))
self.buf.seek(_idx, 0)
_truesize = len(self.buf.read(self.fmt[k]['cnt']))
print('Length of bytes read: {0}'.format(_truesize))
# But sometimes it's... kind of fixable?
if k == 'file' and _truesize < self.fmt[k]['cnt']:
self.buf.seek(_idx, 0)
self.fmt[k] = {'cnt': _truesize, 'fmt': '{0}B'.format(_truesize)}
pkt = struct.Struct(self.fmt[k]['fmt'])
print('Struct format size automatically adjusted.')
try:
self.reconstructed_segments[k] = pkt.unpack(self.buf.read(self.fmt[k]['cnt']))
except struct.error as e2:
# yolo.
print('We still couldn\'t populate {0}; filling with a nullbyte.'.format(k))
print('Error (try #2): {0}'.format(e2))
print('We read {0} bytes.'.format(_truesize))
print('fmt: {0}'.format(self.fmt[k]['fmt']))
self.reconstructed_segments[k] = b'\00'
_idx += self.fmt[k]['cnt']
self.buf.seek(_idx, 0)
# Finally, check for opts. If they exist, populate.
_optbytes = len(self.buf.read())
if _optbytes >= 1:
self.opts['size'] = _optbytes
self.buf.seek(_idx, 0)
self.opts['bytes'] = self.buf.read() # read to the end
return()
def getOpts(self):
pass
def close(self):
self.buf.close()
def parseArgs():
args = argparse.ArgumentParser()
_deflease = '/var/lib/dhcpcd/'
args.add_argument('-l', '--lease',
metavar = '/path/to/lease/dir/or_file.lease',
default = _deflease,
dest = 'leasepath',
help = ('The path to the directory of lease files or specific lease file. ' +
'If a directory is provided, all lease files found within will be ' +
'parsed. Default: {0}{1}{2}').format(color.BOLD,
_deflease,
color.END))
args.add_argument('-n', '--no-color',
action = 'store_false',
dest = 'color',
help = ('If specified, suppress color formatting in output.'))
args.add_argument('-d', '--dump',
metavar = '/path/to/dumpdir',
default = False,
dest = 'dump',
help = ('If provided, dump the parsed leases to this directory (in ' +
'addition to printing). It will dump with the same filename ' +
'and overwrite any existing file with the same filename, so ' +
'do NOT use the same directory as your dhcpcd lease files! ' +
'({0}-l/--lease{1}). The directory will be created if it does ' +
'not exist').format(color.BOLD,
color.END))
args.add_argument('-p', '--pretty',
action = 'store_true',
dest = 'prettyprint',
help = ('If specified, include color formatting {0}in the dump ' +
'file(s){1}').format(color.BOLD, color.END))
return(args)
def getLeaseData(fpath):
if not os.path.isfile(fpath):
raise FileNotFoundError('{0} does not exist'.format(fpath))
with open(fpath, 'rb') as f:
_data = f.read()
return(_data)
def iterLease(args):
# If the lease path is a file, just operate on that.
# If it's a directory, iterate (recursively) through it.
leases = {}
if not os.path.lexists(args['leasepath']):
raise FileNotFoundError('{0} does not exist'.format(args['leasepath']))
if os.path.isfile(args['leasepath']):
_pp = packetParser(getLeaseData(args['leasepath']))
# TODO: convert the hex vals to their actual vals... maybe?
_keyname = re.sub('^(dhcpcd-)?(.*)\.lease$',
'\g<2>',
os.path.basename(args['leasepath']))
leases[_keyname] = leaseParse(_pp, args)
else:
# walk() instead of listdir() because whotf knows when some distro like
# *coughcoughUbuntucoughcough* will do some breaking change like creating
# subdirs based on iface name or something.
for _, _, files in os.walk(args['leasepath']):
if not files:
continue
files = [i for i in files if i.endswith('.lease')] # only get .lease files
for i in files:
_args = args.copy()
_fpath = os.path.join(args['leasepath'], i)
_keyname = re.sub('^(dhcpcd-)?(.*)\.lease$', '\g<2>', os.path.basename(_fpath))
_dupeid = 0
# JUST in case there are multiple levels of dirs in the future
# that have files of the sama name
while _keyname in leases.keys():
# TODO: convert the hex vals to their actual vals... maybe?
_keyname = re.sub('^$',
'\g<1>.{0}'.format(_dupeid),
_keyname)
_dupeid += 1
_pp = packetParser(getLeaseData(_fpath))
leases[_keyname] = leaseParse(_pp, _args, fname = _fpath)
return(leases)
def leaseParse(pp, args, fname = False):
# Essentially just a wrapper function.
# Debugging output...
if fname:
print(fname)
pp.getStd()
pp.getOpts()
if args['dump']:
pass # TODO: write to files, creating dump dir if needed, etc.
pp.close()
# do pretty-printing (color-coded segments, etc.) here
return(pp.reconstructed_segments)
if __name__ == '__main__':
args = vars(parseArgs().parse_args())
args['leasepath'] = os.path.abspath(os.path.expanduser(args['leasepath']))
if not os.path.lexists(args['leasepath']):
exit('{0} does not exist!'.format(args['leasepath']))
leases = iterLease(args)
# just print for now until we write the parser/prettyprinter
print(list(leases.keys()))

View File

@@ -0,0 +1,7 @@
from flask import Flask
app = Flask(__name__, instance_relative_config=True)
from app import views
app.config.from_object('config')

View File

@@ -0,0 +1,41 @@
#!/usr/bin/env python3
import argparse
import sys
import os
# This is ugly as fuck. TODO: can we do this more cleanly?
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
import config
class DBmgr(object):
def __init__(self, args = None):
self.DB = config.DB
self.args = args
def keyChk(self):
# Is it a pubkey file?
if os.path.isfile(os.path.abspath(os.path.expanduser(self.args['key']))):
with open(os.path.abspath(os.path.expanduser(self.args['key'])), 'r') as f:
self.args['key'] = f.read()
self.args['key'] = self.args['key'].strip()
def add(self, key, host, role):
pass
def argParse():
args = argparse.ArgumentParser()
args.add_argument('-k',
'--key',
dest = 'key',
default = None,
type = 'str',
return(args)
def main():
args -
d = DBmgr(args)
if __name__ == '__main__':
main()

View File

View File

@@ -0,0 +1,4 @@
{% extends "base.html" %}{% block title %}r00t^2 SSH Key Repository || About{% endblock %}{% block body %}<div class="jumbotron">
<h1>About</h1></div>
<p>This is a tool to deliver SSH public keys (or, optionally, host keys) to SSH's authentication system in a safe and secure manner.</p>
{% endblock %}

View File

@@ -0,0 +1,35 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>{% block title %}{% endblock %}</title>
<!-- Bootstrap core CSS -->
<!-- Thanks, https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-xii-facelift and
https://scotch.io/tutorials/getting-started-with-flask-a-python-microframework -->
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<!--<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">-->
<!-- Custom styles for this template -->
<link href="https://getbootstrap.com/examples/jumbotron-narrow/jumbotron-narrow.css" rel="stylesheet">
<!--<link href="https://getbootstrap.com/docs/4.0/examples/offcanvas/offcanvas.css" rel="stylesheet">-->
</head>
<body>
<div class="container">
<div class="header clearfix">
<nav>
<ul class="nav nav-pills pull-right">
<li role="presentation"><a href="/">Home</a></li>
<li role="presentation"><a href="/about">About</a></li>
<li role="presentation"><a href="/usage">Usage</a></li>
<!-- the following opens in a new tab/window/whatever. the line after opens in the same tab/window/etc. -->
<!-- <li role="presentation"><a href="https://square-r00t.net/" target="_blank">r00t^2</a></li> -->
<li role="presentation"><a href="https://square-r00t.net/">r00t^2</a></li>
</ul>
</nav>
</div>
{% block body %}{% endblock %}
<footer class="footer">
<p><sub>The code for this page is released under the <a href="https://www.gnu.org/licenses/gpl-3.0.en.html#content">GPL 3.0 License</a>. It can be found <a href="https://git.square-r00t.net/OpTools/tree/net">here</a>.</sub></p>
</footer>
</div>
<!-- /container -->
</body>
</html>

View File

@@ -0,0 +1,38 @@
<h2>Client/Browser Information</h2>
<p>This is information that your browser sends with its connection.</p>
<p>
<ul>
<li><b>Client IP:</b> <a href="https://ipinfo.io/{{ visitor['ip'] }}">{{ visitor['ip'] }}</a></li>
<li><b>Browser:</b> {{ '<a href="{0}">{1}</a>'.format(browsers[visitor['client']['browser']][0],
browsers[visitor['client']['browser']][1])|safe
if visitor['client']['browser'] in browsers.keys()
else visitor['client']['browser'].title()
if visitor['client']['browser'] is not none
else '(N/A)' }}</li>
<li><b>Language/Locale:</b> {{ visitor['client']['language'] or '(N/A)' }}</li>
{%- set alt_os = alts[visitor['client']['os']] if visitor['client']['os'] in alts.keys() else '' %}
<li><b>Operating System:</b> {{ '<a href="{0}">{1}</a>{2}'.format(os[visitor['client']['os']][0],
os[visitor['client']['os']][1],
alt_os)|safe
if visitor['client']['os'] in os.keys()
else visitor['client']['os'].title()
if visitor['client']['os'] is not none
else '(N/A)' }}</li>
<li><b>User Agent:</b> {{ visitor['client']['str'] }}</li>
<li><b>Version:</b> {{ visitor['client']['version'] or '(N/A)' }}</li>
</ul>
</p>
<h2>Request Headers</h2>
<p>These are headers sent along with the request your browser sends for the page's content.</p>
<p>
<table>
<tr>
<th>Field</th>
<th>Value</th>
</tr>{% for k in visitor['headers'].keys()|sort(case_sensitive = True) %}
<tr>
<td>{{ k }}</td>
<td>{{ visitor['headers'][k] if visitor['headers'][k] != '' else '(N/A)' }}</td>
</tr>{% endfor %}
</table>
</p>

View File

@@ -0,0 +1,6 @@
{% extends "base.html" %}{% block title %}r00t^2 Client Info Revealer{% endblock %}{% block body %}<div class="jumbotron">
<h1>Client Info Revealer</h1>
<p class="lead">A tool to reveal client-identifying data sent to webservers</p>
</div>
{% include 'html.html' if not params['json'] else 'json.html' %}
{% endblock %}

View File

@@ -0,0 +1 @@
<pre>{{ json }}</pre>

View File

@@ -0,0 +1,51 @@
{% extends "base.html" %}{% block title %}r00t^2 Client Info Revealer || Usage{% endblock %}{% block body %}<div class="jumbotron">
<h1>Usage</h1></div>
<h2>Parameters</h2>
<p>You can control how this page displays/renders. By default it will try to "guess" what you want; e.g. if you access it in Chrome, it will display this page but if you fetch via Curl, you'll get raw JSON. The following parameters control this behavior.</p>
<p><i><b>Note:</b> "Enabled" parameter values can be one of <b>y</b>, <b>yes</b>, <b>1</b>, or <b>true</b>. "Disabled" parameter values can be one of <b>n</b>, <b>no</b>, <b>0</b>, or <b>false</b>. The parameter names are case-sensitive but the values are not.</i></p>
<p><ul>
<li><b>json:</b> Force rendering in JSON format
<ul>
<li>It will display it nicely if you're in a browser, otherwise it will return raw/plaintext JSON.</li>
<li>Use <b>raw</b> if you want to force raw plaintext JSON output.</li>
</ul></li>
<li><b>html:</b> Force rendering in HTML
<ul>
<li>It will render HTML in clients that would normally render as JSON (e.g. curl, wget).</li>
</ul></li>
<li><b>raw:</b> Force output into a raw JSON string
<ul>
<li>Pure JSON instead of HTML or formatted JSON. This is suitable for API usages if your client is detected wrongly (or you just want to get the raw JSON).</li>
<li>Overrides all other tags.</li>
<li>Has no effect for clients that would normally render as JSON (curl, wget, etc.).</li>
</ul></li>
<li><b>tabs:</b> Indentation for JSON output
<ul>
<li>Accepts a positive integer.</li>
<li>Default is 4 for "desktop" browsers (if <b>json</b> is enabled), and no indentation otherwise.</li>
</ul></li>
</ul></p>
<h2>Examples</h2>{% set scheme = 'https' if request.is_secure else 'http'%}
<p><table>
<tr>
<th>URL</th>
<th>Behavior</th>
</tr>
<tr>
<td><a href="{{ scheme }}://{{ request.headers['host'] }}/">{{ scheme }}://{{ request.headers['host'] }}/</a></td>
<td>Displays HTML and "Human" formatting if in a graphical browser, otherwise returns a raw, unformatted JSON string.</td>
</tr>
<tr>
<td><a href="{{ scheme }}://{{ request.headers['host'] }}/?raw=1">{{ scheme }}://{{ request.headers['host'] }}/?raw=1</a></td>
<td>Renders a raw, unformatted JSON string if in a graphical browser, otherwise no effect. All other parameters ignored (if in a graphical browser).</td>
</tr>
<tr>
<td><a href="{{ scheme }}://{{ request.headers['host'] }}/?html=1">{{ scheme }}://{{ request.headers['host'] }}/?html=1</a></td>
<td>Forces HTML rendering on non-graphical clients.</td>
</tr>
<tr>
<td><a href="{{ scheme }}://{{ request.headers['host'] }}/?json=1&tabs=4">{{ scheme }}://{{ request.headers['host'] }}/?json=1&tabs=4</a></td>
<td>Returns JSON indented by 4 spaces for each level (you can leave "json=1" off if it's in a non-graphical browser, unless you specified "html=1").</td>
</tr>
</table></p>
{% endblock %}

View File

@@ -0,0 +1,57 @@
import json
import re
from flask import render_template, make_response, request
from app import app
@app.route('/', methods = ['GET']) #@app.route('/')
def index():
hostkeys = None # TODO: hostkeys go here. dict?
# First we define interactive browsers
_intbrowsers = ['camino', 'chrome', 'firefox', 'galeon',
'kmeleon', 'konqueror', 'links', 'lynx']
# Then we set some parameter options for less typing later on.
_yes = ('y', 'yes', 'true', '1', True)
_no = ('y', 'no', 'false', '0', False, 'none')
# http://werkzeug.pocoo.org/docs/0.12/utils/#module-werkzeug.useragents
# We have to convert these to strings so we can do tuple comparisons on lower()s.
params = {'json': str(request.args.get('json')).lower(),
'html': str(request.args.get('html')).lower(),
'raw': str(request.args.get('raw')).lower()}
if request.user_agent.browser in _intbrowsers:
if params['html'] == 'none':
params['html'] = True
if params['json'] == 'none':
params['json'] = False
elif params['json'] in _yes:
params['json'] = True
for k in params.keys():
if params[k] in _no:
params[k] = False
else:
params[k] = True
# Set the tabs for JSON
try:
params['tabs'] = int(request.args.get('tabs'))
except (ValueError, TypeError):
if request.user_agent.browser in _intbrowsers or params['html']:
params['tabs'] = 4
else:
params['tabs'] = None
j = json.dumps(hostkeys, indent = params['tabs'])
if (request.user_agent.browser in _intbrowsers and params['html'] and not params['raw']) or \
(request.user_agent.browser not in _intbrowsers and params['html']):
return(render_template('index.html', hostkeys = hostkeys))
else:
if visitor['client']['browser'] in _intbrowsers.keys() and not params['raw']:
return(render_template('json.html',
json = j,
params = params))
return(j)
@app.route('/about', methods = ['GET'])
def about():
return(render_template('about.html'))
@app.route('/usage', methods = ['GET'])
def usage():
return(render_template('usage.html'))

View File

@@ -0,0 +1,8 @@
# config.py
# Flask debugging - DISABLE FOR PRODUCTION ENVIRONMENTS
#DEBUG = True
DEBUG = False
# Path to your Sqlite3 DB
DB = '/var/local/db/optools/ssh_keys.sqlite3'

View File

@@ -0,0 +1,4 @@
from app import app
if __name__ == '__main__':
app.run()

View File

@@ -0,0 +1,18 @@
[uwsgi]
plugin = python
py-autoreload = 1
#uid = http
#gid = http
socket = /run/uwsgi/netinfo.sock
chown-socket = http:http
processes = 4
master = 1
base = /usr/local/lib/optools/net/ssh
chdir = %(base)
#mount = /=%(base)/run.py
wsgi-file = %(base)/run.py
chmod-socket = 660
callable = app
cgi-helper =.py=python
logto = /var/log/uwsgi/%n.log
vacuum