Untitled
unknown
python
2 years ago
9.5 kB
12
Indexable
#!/usr/bin/python3
import sys,re,os
import struct,random,math,itertools
import html
import requests
from datetime import date
import time
from bottle import route, run, get, post, request, response, abort, auth_basic
# an ugly spell to get unbuffer stdout on python3
class Unbuffered(object):
def __init__(self, stream):
self.stream = stream
def write(self, data):
self.stream.write(data)
self.stream.flush()
def writelines(self, datas):
self.stream.writelines(datas)
self.stream.flush()
def __getattr__(self, attr):
return getattr(self.stream, attr)
sys.stdout = Unbuffered(sys.stdout)
if len(sys.argv)<2:
print("BookHook web server")
print("Syntax: %s <port> [group]" % sys.argv[0])
sys.exit(1)
port = int(sys.argv[1])
group = None
if len(sys.argv)>=3:
group = int(sys.argv[2])
logfile = "log.txt"
target_url_file = "target_url.dat"
if group:
target_url_file = f"target_url{group}.dat"
def get_target_url():
try:
with open(target_url_file) as f:
r = f.read().strip()
except FileNotFoundError:
set_target_url("")
return ""
return r
def set_target_url(url):
with open(target_url_file,"w") as f:
f.write(url.strip())
failure_mode = None
failure_password = "dogs"
general_user = "admin"
general_pass = "ECE$%*"
# Define a function to authenticate users
def check_auth(username, password):
# Replace this with your own authentication logic
if username == general_user and password == general_pass:
return True
return False
def iff(c,a,b):
if c: return a
else: return b
def get_background():
return f"./static/bg{iff(group,group,0)}.gif"
def get_title():
r = "Hypothetical Books: BookHook Manager"
if group:
r += f" - GROUP {group}"
return r
@get('/fail')
@post('/fail')
@auth_basic(check_auth)
def fail():
global failure_mode
r = f"<html><body style='background: url({get_background()});'>\n"
r += f"<h1>{get_title()} - FAILURE SIMULATOR</h1>\n"
if request.method == 'POST':
new_failure_mode = request.forms.get('f')
given_password = request.forms.get('p')
if new_failure_mode=="None": new_failure_mode = None
if given_password == failure_password:
failure_mode = new_failure_mode
else:
r += "<div style='color:red; background-color: yellow;'>DON'T HACK MY WEBS!!!!!</div>\n"
else:
given_password = ''
r += f"failure_mode = {failure_mode}<p>"
r += f"""
<form action='/fail' method=post>
Password: <input type=password name=p value='{given_password}'><p>
<input type=submit name=f value="None">
<input type=submit name=f value="changedxml">
<input type=submit name=f value="wrongclosetag">
<input type=submit name=f value="nodate">
<input type=submit name=f value="badxml">
<input type=submit name=f value="blank">
<input type=submit name=f value="get">
<input type=submit name=f value="chargen">
<input type=submit name=f value="slow10sec">
<input type=submit name=f value="slow10min">
<p>
</form>
<a href="/">Front page</a>
"""
r+= "</body></html>"
return r
@get('/')
@get('/fancy')
@auth_basic(check_auth)
def root():
is_fancy = 'fancy' in request.url
r = """<html><body>
<style>
#map {
width: 600px;
height: 400px;
border: 1px #000 solid;
}
body {
"""
r += f"background-image: url({get_background()});"
r += """
}
</style>
"""
r += f"<h1>{get_title()}</h1>\n"
r += """
Connects to our POS systems over RS-232 and turns sales into XML hooks! By Ronnie Hypothetical, copyright 2002
<p>"""
r += f"""
<H2>Update target URL</h2>
<form method=post action=/settarget>
Target URL: <input type=text style="width:80%;" name=target value="{get_target_url()}"><br>
<input type=submit>
</form>
"""
r += """
<H2>Simulate purchase</h2>
This will send an XML post to the target URL describing the purchase below for the purposes of testing.
<p><i>Meta-note: In our case, this will be the ONLY posts sent to your system, since we don't actually have cash registers selling books in real life.</i>
<form method=post action=/simulate name=simulate>
Date: <input type=text name=date> (Format must by YYYY-MM-DD, blank defaults to today)<br>
<table><tr><th>ISBN<th>Qty<th>Unit price</tr>
"""
for i in range(iff(is_fancy,15,3)):
r += f"<tr><td><input type=text name=isbn{i}><td><input type=text name=qty{i}><td><input type=text name=price{i}>\n"
r+= """
</table>
<Script>
function definputs() {
document.querySelector('input[name="date"]').value = "2002-09-17";
document.querySelector('input[name="isbn0"]').value = "0345409469";
document.querySelector('input[name="qty0"]').value = 1;
document.querySelector('input[name="price0"]').value = 7.99;
document.querySelector('input[name="isbn1"]').value = "978-0345376596";
document.querySelector('input[name="qty1"]').value = 2;
document.querySelector('input[name="price1"]').value = 9.99;
}
</script>
[
<a href="#" onclick="definputs();">Fill with dummy info</a> ·
<a href="#" onclick="document.forms['simulate'].reset();">Clear</a>
]<bR><br>
<input type=submit value="Simulate purchase">
</form>
"""
r+= """
<h2>Info for future people</h2>
This posts the sale as XML to the target URL. The <a href="/static/schema.xml">schema is here</a>. XML is the future!
"""
r += "\n\n<!-- <img src='static/IAMCOOL.jpg'>A picture of me! --></body></html>\n"
return r
@post('/settarget')
@auth_basic(check_auth)
def req_set_target():
new_target = request.forms.get('target')
set_target_url(new_target)
r = '<html><body>ok. redirecting...<meta http-equiv="refresh" content="2; url=/"></body></html>'
return r
# wraps a string in a file-like object that can have its reads delayed by a given number of seconds
class SlowStringFile(object):
def __init__(self, content, delay):
self.content = content
self.delay = delay
def read(self, size=-1):
time.sleep(self.delay)
if size==-1:
result = self.content
self.content = ''
else:
result = self.content[:size]
self.content = self.content[size:]
return result
def __iter__(self):
return self
def __next__(self):
data = self.read()
if data:
return data
else:
raise StopIteration
# a file-like object that generates random binary content
class CharGen(object):
def __init__(self):
pass
def read(self, size=1024*1024):
return os.urandom(size)
def __iter__(self):
return self
def __next__(self):
data = self.read()
if data:
return data
else:
raise StopIteration
@post('/simulate')
@auth_basic(check_auth)
def req_simulate():
i = 0
sale_date = request.forms.get("date","").strip()
if sale_date=="":
sale_date = date.today().strftime("%Y-%m-%d")
if failure_mode == 'nodate':
xml = "<sale>\n"
else:
xml = f'<sale date="{sale_date}">\n'
if failure_mode == 'badxml':
xml += "/>\n"
while request.forms.get(f"isbn{i}","").strip() != "":
xml += "<item>"
if failure_mode == 'changedxml': # just rearranged and spaced, still valid
xml += " <price>" + request.forms.get(f"price{i}","").strip() + "</price>\n"
xml += "\t<qty>" + request.forms.get(f"qty{i}","").strip() + "</qty>\n\n"
xml += " <isbn>" + request.forms.get(f"isbn{i}","").strip() + "</isbn>"
else:
xml += "<isbn>" + request.forms.get(f"isbn{i}","").strip() + "</isbn>"
xml += "<qty>" + request.forms.get(f"qty{i}","").strip() + "</qty>"
xml += "<price>" + request.forms.get(f"price{i}","").strip() + "</price>"
xml += "</item>\n"
i += 1
if failure_mode == 'wrongclosetag':
xml += "</sales>\n"
else:
xml += "</sale>\n"
if failure_mode=="blank":
xml = ""
url = get_target_url()
r = "<html><body><style>pre { margin-left: 20px; background-color:#ddd; padding: 5px;}</style>\n"
r += f"I will post the following to <u>{url}</u>: <pre>{html.escape(xml)}</pre>\n"
if failure_mode=="slow10sec":
xml = SlowStringFile(xml.encode('utf-8'),10)
if failure_mode=="slow10min":
xml = SlowStringFile(xml.encode('utf-8'),10*60)
if failure_mode=='chargen':
xml = CharGen()
r += "<hr><p>Issuing request..."
if failure_mode=="get":
response = requests.get(url, data=xml, headers={'Content-Type': 'application/xml'})
else:
response = requests.post(url, data=xml, headers={'Content-Type': 'application/xml'})
r += f"Got status code <u>{response.status_code}</u> and this response body:<pre>{html.escape(response.text)}</pre>\n"
r += "<hr><a href=/>Go back</a>\n"
r += "</body></html>"
return r
from bottle import static_file
@route('/static/<filename>')
@auth_basic(check_auth)
def server_static(filename):
return static_file(filename, root='./static/')
run(host='0.0.0.0', port=port, debug=True, server='paste') # install paste with: sudo apt install python3-paste
Editor is loading...
Leave a Comment