Working PoC

This commit is contained in:
jpk 2019-08-11 16:25:40 +02:00
parent 1ae44a1f46
commit feeeb7c421
3 changed files with 284 additions and 53 deletions

View File

@ -0,0 +1,133 @@
import os
import json
import uuid
class BaseTextHandler(object):
mimetype = 'text/mime'
@staticmethod
def handle(httpd):
raise Exception(f'Not implemented: Unable to handle {httpd} '
'({BaseTextHandler.mimetype}')
class OctetStreamHandler(object):
mimetype = 'application/octet-stream'
def handle(httpd):
raise Exception(f'Not implemented: Unable to handle {httpd} '
'({StreamHandler.mimetype}')
class ChunkTemplateHandler(BaseTextHandler):
pass
class ThumbHandler(object):
mimetype = 'image/png'
def handle(httpd):
raise Exception(f'Not implemented: Unable to handle {httpd} '
'({ThumbHandler.mimetype}')
class LegacyListHandler(BaseTextHandler):
def handle(httpd):
# files = filess['files']
raise Exception(f'Not implemented: Unable to handle {httpd} '
'({LegacyListHandler.mimetype}')
class JsonListHandler(object):
mimetype = 'application/json'
def _make_jsonlist(files):
filestats = []
totalsize = 0
for file_ in files:
statinfo = os.stat(file_)
filename = os.path.basename(file_)
filepath = os.path.dirname(file_)
filestats.append({
'created': str(int(statinfo.st_ctime) * 1000),
'modified': str(int(statinfo.st_mtime) * 1000),
'name': filename,
'path': filepath,
'size': str(statinfo.st_size),
'type': "Image" # TODO dynamic
})
totalsize += statinfo.st_size
jsonlist = {
'device': 'ANYONMOUS',
'empty_dirs': [],
'files': filestats,
'uuid': str(uuid.uuid4()),
'version': 4130
}
return json.dumps(jsonlist), totalsize
def handle(httpd):
jsonlist, totalsize = JsonListHandler._make_jsonlist(httpd.files)
httpd.send_response(200)
httpd.send_header('Content-Type', 'application/json')
httpd.end_headers()
response = bytes(jsonlist, 'utf-8')
httpd.wfile.write(response)
httpd.total_file_size = totalsize
class SingleFileHandler(OctetStreamHandler):
pass
class ZipFileHandler(object):
mimetype = 'application/zip'
def handle(httpd):
raise Exception('ZipFileHandler not implemeted.')
class SuperStreamHandler(OctetStreamHandler):
@staticmethod
def _get_streamsize(files):
totalsize = 0
for file_ in files:
statinfo = os.stat(file_)
totalsize += statinfo.st_size
return totalsize
@staticmethod
def _stream_file_content(files):
for file_ in files:
with open(file_, 'rb') as f_handle:
yield f_handle.read()
@staticmethod
def handle(httpd):
httpd.send_response(200)
httpd.send_header('ylfrettub', '1')
httpd.send_header('Content-Type', 'application/octet-stream')
httpd.send_header('Content-Length',
str(SuperStreamHandler._get_streamsize(httpd.files)))
httpd.send_header('Content-Disposition', 'attachment; '
'filename="RedHotChillyStream"')
httpd.end_headers()
for chunk in SuperStreamHandler._stream_file_content(httpd.files):
httpd.wfile.write(chunk)
class AssetHandler(OctetStreamHandler):
pass
class ApkRequestHandler(object):
mimetype = 'application/vnd.android.package-archive'
def handle(httpd):
raise Exception(f'Not implemented: Unable to handle {httpd} '
'({ApkRequestHandler.mimetype}')

110
SuperBeam/__init__.py Normal file
View File

@ -0,0 +1,110 @@
import os
from http.server import BaseHTTPRequestHandler
import socketserver
import threading
import pyqrcode
import socket
import struct
import base64
from SuperBeam.RequestHandlers import (
ChunkTemplateHandler, ThumbHandler, ApkRequestHandler, LegacyListHandler,
SingleFileHandler, ZipFileHandler, SuperStreamHandler, AssetHandler,
JsonListHandler
)
class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
pass
class SuperBeamServer(BaseHTTPRequestHandler):
def do_GET(self):
# TODO Add globbing
print(threading.current_thread())
paths = {
'index': ChunkTemplateHandler,
'/': ChunkTemplateHandler,
'/index.htm': ChunkTemplateHandler, # TODO handler2
'/getthumb': ThumbHandler,
'/light': ChunkTemplateHandler, # TODO handler2
'/superlist': LegacyListHandler,
'/jsonlist': JsonListHandler,
'/get/': SingleFileHandler,
'/getapk': ApkRequestHandler,
'/getzip': ZipFileHandler,
'/getstream': SuperStreamHandler,
'*': AssetHandler,
}
if self.path in paths: # TODO startswith key check
self.respond(paths[self.path])
def respond(self, handler):
print('='*30 + ' DEBUG ' + '='*30)
print('CONNECTION:', self.connection)
print('HEADERS :', self.headers)
print('REQUEST :', self.request)
print('REQUEST* :', self.requestline)
print('PATH :', self.path)
print('COMMAND :', self.command)
print('HANDLER :', handler)
print('='*30 + ' DEBUG ' + '='*30)
handler.handle(self)
def get_primary_ip():
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
# doesn't even have to be reachable
s.connect(('10.255.255.255', 1))
IP = s.getsockname()[0]
except Exception as exc:
print(exc)
IP = '127.0.0.1'
finally:
s.close()
return IP
def show_qrcode():
primary_ip = get_primary_ip()
octets = [1]
octets.extend([int(_) for _ in primary_ip.split('.')[::-1]])
url = 'http://superbe.am/q?' + base64.b64encode(
struct.pack('BBBBB', *octets)
).decode('utf-8')
qrcode = pyqrcode.create(url)
print('\033[1;37;37m████████' + '██'*(len(qrcode.code[0])))
for row in qrcode.code:
print('\033[1;37;37m████', end='')
for block in row:
if block == 1:
print('\033[1;30;30m ', end='')
else:
print('\033[1;37;37m██', end='')
print('\033[1;37;37m████')
print('\033[1;37;37m████████' + '██'*(len(qrcode.code[0])))
def build_filelist(paths, recursive=True):
filelist = []
for path in paths:
if os.path.isdir(path) and recursive:
for root, dirs, files in os.walk(path):
for file_ in files:
filelist.append(os.path.join(root, file_))
else:
filelist.append(path)
return filelist
def serve_forever(files):
Handler = SuperBeamServer
Handler.files = files
with socketserver.ThreadingTCPServer(('', 8080), Handler) as httpd:
show_qrcode()
httpd.serve_forever()

94
sbeam.py Normal file → Executable file
View File

@ -7,82 +7,70 @@
# Distributed under terms of the MIT license.
"""
> GET /getstream HTTP/1.1
> Host: 192.168.178.50:8080
> User-Agent: sbeam
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< ylfrettub: 1
< Content-Disposition: attachment; filename="RedHotChillyStream"
< Date: Fri, 09 Aug 2019 12:57:20 GMT
< Content-Length: 8137967
< Content-Type: application/octet-stream
Server QR Code = http://superbe.am/q?ATKyqMA=
Base64 = 01 32 b2 a8 c0 = 01 50 178 168 192
"""
import os
import click
import requests
import pyqrcode
import socket
import struct
import base64
# from http.server import HTTPServer, BaseHTTPRequestHandler
import http.server
import socketserver
import SuperBeam
def get_primary_ip():
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
# doesn't even have to be reachable
s.connect(('10.255.255.255', 1))
IP = s.getsockname()[0]
except Exception as exc:
print(exc)
IP = '127.0.0.1'
finally:
s.close()
return IP
@click.group()
def cli():
pass
def show_qrcode():
primary_ip = get_primary_ip()
octets = [1]
octets.extend([int(_) for _ in primary_ip.split('.')[::-1]])
url = 'http://superbe.am/q?' + base64.b64encode(
struct.pack('BBBBB', *octets)
).decode('utf-8')
qrcode = pyqrcode.create(url)
print('\033[1;37;37m████████' + '██'*(len(qrcode.code[0])))
for row in qrcode.code:
print('\033[1;37;37m████', end='')
for block in row:
if block == 1:
print('\033[1;30;30m ', end='')
else:
print('\033[1;37;37m██', end='')
print('\033[1;37;37m████')
print('\033[1;37;37m████████' + '██'*(len(qrcode.code[0])))
@cli.command()
@click.option('--recursive', default=True, is_flag=True,
help='Recurse into sub directories')
@click.argument('filename', nargs=-1, type=click.Path(exists=True))
def serve(recursive, filename):
filelist = SuperBeam.build_filelist(filename, recursive)
SuperBeam.serve_forever(filelist)
def serve_files():
show_qrcode()
Handler = http.server.SimpleHTTPRequestHandler
with socketserver.TCPServer(('', 8081), Handler) as httpd:
httpd.serve_forever()
def get_superlist():
with requests.get('http://192.168.178.50:8080/superlist',
@cli.command()
@click.argument('host', default='127.0.0.1')
def superlist(host):
with requests.get(f'http://{host}:8080/superlist',
headers={'User-Agent': 'sbeam'}) as response:
assert response.status_code == 200
return response.text.split('\n')
def save_stream(filename):
with requests.get('http://192.168.178.50:8080/getstream', stream=True,
@cli.command()
@click.argument('host', default='127.0.0.1')
@click.argument('filename', default='received.bin', type=click.File('w'))
def receive(host, filename):
with requests.get(f'http://{host}:8080/getstream', stream=True,
headers={'User-Agent': 'sbeam'}) as response:
assert response.status_code == 200
with open(filename, 'wb') as handle:
# length = response.headers.get('Content-length')
# print(length)
for chunk in response.iter_content(chunk_size=8196):
handle.write(chunk)
def split_stream(data, directory, superlist):
@cli.command()
@click.argument('filename', default='received.bin', type=click.File('r'))
@click.argument('superlist', default='superlist', type=click.File('r'))
@click.argument('directory', default='received', type=click.File('w'))
def split_stream(data, superlist, directory):
with open(data, 'rb') as blob:
for item in superlist:
if not item:
@ -96,7 +84,7 @@ def split_stream(data, directory, superlist):
if __name__ == '__main__':
serve_files()
cli()
# to_download = 0
# superlist = get_superlist()
# try: