Working PoC
This commit is contained in:
parent
1ae44a1f46
commit
feeeb7c421
|
@ -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}')
|
|
@ -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()
|
|
@ -7,82 +7,70 @@
|
||||||
# Distributed under terms of the MIT license.
|
# 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 os
|
||||||
|
import click
|
||||||
import requests
|
import requests
|
||||||
import pyqrcode
|
|
||||||
|
|
||||||
import socket
|
import SuperBeam
|
||||||
import struct
|
|
||||||
import base64
|
|
||||||
|
|
||||||
# from http.server import HTTPServer, BaseHTTPRequestHandler
|
|
||||||
import http.server
|
|
||||||
import socketserver
|
|
||||||
|
|
||||||
|
|
||||||
def get_primary_ip():
|
@click.group()
|
||||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
def cli():
|
||||||
try:
|
pass
|
||||||
# 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():
|
@cli.command()
|
||||||
primary_ip = get_primary_ip()
|
@click.option('--recursive', default=True, is_flag=True,
|
||||||
octets = [1]
|
help='Recurse into sub directories')
|
||||||
octets.extend([int(_) for _ in primary_ip.split('.')[::-1]])
|
@click.argument('filename', nargs=-1, type=click.Path(exists=True))
|
||||||
url = 'http://superbe.am/q?' + base64.b64encode(
|
def serve(recursive, filename):
|
||||||
struct.pack('BBBBB', *octets)
|
filelist = SuperBeam.build_filelist(filename, recursive)
|
||||||
).decode('utf-8')
|
SuperBeam.serve_forever(filelist)
|
||||||
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 serve_files():
|
@cli.command()
|
||||||
show_qrcode()
|
@click.argument('host', default='127.0.0.1')
|
||||||
Handler = http.server.SimpleHTTPRequestHandler
|
def superlist(host):
|
||||||
with socketserver.TCPServer(('', 8081), Handler) as httpd:
|
with requests.get(f'http://{host}:8080/superlist',
|
||||||
httpd.serve_forever()
|
|
||||||
|
|
||||||
|
|
||||||
def get_superlist():
|
|
||||||
with requests.get('http://192.168.178.50:8080/superlist',
|
|
||||||
headers={'User-Agent': 'sbeam'}) as response:
|
headers={'User-Agent': 'sbeam'}) as response:
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
return response.text.split('\n')
|
return response.text.split('\n')
|
||||||
|
|
||||||
|
|
||||||
def save_stream(filename):
|
@cli.command()
|
||||||
with requests.get('http://192.168.178.50:8080/getstream', stream=True,
|
@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:
|
headers={'User-Agent': 'sbeam'}) as response:
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
with open(filename, 'wb') as handle:
|
with open(filename, 'wb') as handle:
|
||||||
# length = response.headers.get('Content-length')
|
|
||||||
# print(length)
|
|
||||||
for chunk in response.iter_content(chunk_size=8196):
|
for chunk in response.iter_content(chunk_size=8196):
|
||||||
handle.write(chunk)
|
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:
|
with open(data, 'rb') as blob:
|
||||||
for item in superlist:
|
for item in superlist:
|
||||||
if not item:
|
if not item:
|
||||||
|
@ -96,7 +84,7 @@ def split_stream(data, directory, superlist):
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
serve_files()
|
cli()
|
||||||
# to_download = 0
|
# to_download = 0
|
||||||
# superlist = get_superlist()
|
# superlist = get_superlist()
|
||||||
# try:
|
# try:
|
||||||
|
|
Loading…
Reference in New Issue