Skip to content

Commit

Permalink
v1.0-alpha DeadDrop
Browse files Browse the repository at this point in the history
  • Loading branch information
hyp3ri0n-ng committed Apr 19, 2018
1 parent 0a67a30 commit 3c1d1fa
Show file tree
Hide file tree
Showing 14 changed files with 458 additions and 2 deletions.
7 changes: 7 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Copyright 2017 Hyperion Gray LLC

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
4 changes: 4 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
templates/
*.html
static/
*.ico
62 changes: 60 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,60 @@
# DeadDrop
An in-memory messenger that uses Tor Browser for clientless, private messaging
Platform
========

To be a dropchat server host requires a Linux machine (any Linux should do), if this gets more popular we will create one for Windows.

To be a dropchat client requires a Tor Browser on any OS.


Install
=======

Install Tor

Activate your favorite virtualenv e.g..

`$ sudo apt-get install python-virtualenv`

`$ virtualenv dropenv`

`$ source dropenv/bin/activate`

`$ pip install git+https://gitlab.com/hyperion-gray/dropchat.git`

That's it!

How it works
============

Dropchat is a disposable mini-chat server that can be used to chat safely and anonymously through Tor. One
person is the host of the chat server (don't worry being a host only requires one command - no messing with
complex config files) and the others are the clients using only a Tor Browser. The host starts the server
and shares a URL with the clients. They can then chat with each other safely and anonymously. Once you're
done sharing the info you want, simply kill the server. No information is stored on disk.

Usage
=====

Start Tor or Tor Browser, make sure your Control Port is open and listening on the default port.

`$ dropchat`

Share the drop URL with your friends to open in Tor Browser. Chat with them safely and securely!

Features
========

- As chat happens inside the Tor network via ephemeral hidden services, everything is encrypted and attribution of chatters is virtually impossible
- No JS required, use safely with NoScript
- *Nothing* touches disk, everything happens in-memory
- No configuration required
- Low barrier to entry, few dependencies
- No need for a client
- Chats are deleted every 3 minutes
- Randomized usernames - this is for your own safety, so as to decrease chances of username reuse
- New chat service created every time the server is started
- No frills, no fancy CSS, code is easy to follow and review to ensure your safety

---

[![define hyperion gray](https://hyperiongray.s3.amazonaws.com/define-hg.svg)](https://hyperiongray.com/?pk_campaign=github&pk_kwd=dropchat "Hyperion Gray")
Empty file added __init__.py
Empty file.
14 changes: 14 additions & 0 deletions clear_ram.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/bin/bash
# Init
FILE="/tmp/out.$$"
GREP="/bin/grep"
#....
# Make sure only root can run our script
if [[ $EUID -ne 0 ]]; then
echo "[!] This script must be run as root, and not with sudo. Please su to root and run." 1>&2
exit 1
fi

echo "[*] Clearing ram..."
sync; echo 3 > /proc/sys/vm/drop_caches
echo "[*] Done."
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
flask
stem
189 changes: 189 additions & 0 deletions runserver.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
from flask import Flask
from flask import render_template
from flask import request, session, url_for, redirect, abort, jsonify
import traceback
import sys
import string
from stem.control import Controller
from hashlib import sha224
import random
import datetime
from stem import SocketError
import textwrap
app = Flask(__name__)
import logging
log = logging.getLogger('werkzeug')
log.setLevel(logging.ERROR)


chatters = []
global chatlines
chatlines = []

def id_generator(size=6,
chars=string.ascii_uppercase + string.digits +
string.ascii_lowercase):

return ''.join(random.choice(chars) for i in range(size))


app.secret_key = id_generator(size=64)


def check_older_than(chat_dic, secs_to_live = 180):
now = datetime.datetime.now()
timestamp = chat_dic["timestamp"]
diff = now - timestamp
secs = diff.total_seconds()

if secs >= secs_to_live:
return True

return False


def get_random_color():

r = lambda: random.randint(0,128)
return (r(),r(),r())


def process_chat(chat_dic):

chats = []
max_chat_len = 69
if len(chat_dic["msg"]) > max_chat_len:

for message in textwrap.wrap(chat_dic["msg"], width = max_chat_len):
partial_chat = {}
partial_chat["msg"] = message.strip()
partial_chat["timestamp"] = datetime.datetime.now()
partial_chat["username"] = session["_id"]
partial_chat["color"] = session["color"]
chats.append(partial_chat)

else:
chats = [chat_dic]

return chats


# Remove headers that can be used to fingerprint this server
@app.after_request
def remove_headers(response):
response.headers["Server"] = ""
response.headers["Date"] = ""
return response


# Empty Index page to avoid Flask fingerprinting
@app.route('/', methods=["GET"])
def index():
return ('', 200)


@app.route('/<string:url_addition>', methods=["GET"])
def drop(url_addition):

if url_addition != app.config["path"]:
return ('', 404)

if "_id" not in session:
session["_id"] = id_generator()
chatters.append(session["_id"])
session["color"] = get_random_color()

if request.method == "GET":
full_path = app.config["hostname"] + "/" + app.config["path"]
return render_template("drop.html",
hostname=app.config["hostname"],
path=app.config["path"])


@app.route('/<string:url_addition>/chats', methods=["GET", "POST"])
def chat_messages(url_addition):

global chatlines
more_chats = False
if url_addition != app.config["path"]:
return ('', 404)

to_delete = []
c = 0
if chatlines:
for chatline_dic in chatlines:
if check_older_than(chatline_dic):
to_delete.append(c)

c += 1

for _del in to_delete:
chatlines.pop(_del)

if request.method == "POST":

if request.form["dropdata"].strip():

chat = {}
chat["msg"] = request.form["dropdata"].strip()
chat["timestamp"] = datetime.datetime.now()
chat["username"] = session["_id"]
chat["color"] = session["color"]
chats = process_chat(chat)
chatlines = chatlines + chats
chatlines = chatlines[-13:]
more_chats = True

return redirect(app.config["path"], code=302)

return jsonify(chatlines)

#return render_template("chats.html",
# chatlines=chatlines, num_people = len(chatters))

def main():

try:
controller = Controller.from_port()
except SocketError:
sys.stderr.write('[!] Tor proxy or Control Port are not running. Try starting the Tor Browser or Tor daemon and ensure the ControlPort is open.\n')
sys.exit(1)


print('[*] Connecting to tor')
with controller:
controller.authenticate()

# Create ephemeral hidden service where visitors of port 80 get redirected to local
# port 5000 (this is where Flask runs by default).
print('[*] Creating ephemeral hidden service, this may take a minute or two')
result = controller.create_ephemeral_hidden_service({80: 5000}, await_publication = True)

print("[*] Started a new hidden service with the address of %s.onion" % result.service_id)
###result = controller.create_hidden_service(hidden_service_dir, 80, target_port = 5000)

# The hostname is only available when we can read the hidden service
# directory. This requires us to be running with the same user as tor.

if not result:
print("[*] Something went wrong, shutting down")
###controller.remove_hidden_service(hidden_service_dir)
###shutil.rmtree(hidden_service_dir)

if result.service_id:
app.config["hostname"] = result.service_id
app.config["path"] = id_generator(size = 64)
app.config["full_path"] = app.config["hostname"] + ".onion" + "/" + app.config["path"]
print("[*] Your service is available at: %s , press ctrl+c to quit" % app.config["full_path"])
else:
print("[*] Unable to determine our ephemeral service's hostname")

try:
app.run(debug=False, threaded = True)
finally:

print(" * Shutting down our hidden service")
controller.remove_ephemeral_hidden_service(result.service_id)

if __name__ == "__main__":
main()
22 changes: 22 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import os
from setuptools import setup, find_packages

setup(
name = 'dropchat',
version = '0.3.0',
description = 'DropChat',
long_description = 'Drop Chat - Secure Disposable Chat',
url = '',
license = 'MIT',
author = 'Alejandro Caceres',
author_email = '[email protected]',
packages = [''],
include_package_data = True,
package_data = {'': ['templates/*.html']},
install_requires = ["flask", "stem"],
classifiers = [ 'Development Status :: 4 - Beta',
'Programming Language :: Python :: 3',
'Programming Language :: Python'],
entry_points = { 'console_scripts':
[ 'dropchat = runserver:main']}
)
Binary file added static/favicon.ico
Binary file not shown.
2 changes: 2 additions & 0 deletions static/jquery.js

Large diffs are not rendered by default.

20 changes: 20 additions & 0 deletions templates/#chats.html#
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<html>
<!--<meta http-equiv="refresh" content="1" >-->
<style>
*{
padding-top:3px;
font-family: Lucida Sans Typewriter,Lucida Console,monaco,Bitstream Vera Sans Mono,monospace;
font-size:22px;
}
</style>

<div style="float:right; font-size:10px;">People in chat: {{ num_people }}</div>
<table>
{% for message_dic in chatlines %}
<tr>
<td name="msg" class="msg" style="color:rgb{{ message_dic["color"] }};"><b>{{ message_dic["username"] }}</b>: {{ message_dic["msg"] }}</td>
</tr>
{% endfor %}
</table>

</html>
20 changes: 20 additions & 0 deletions templates/chats.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<html>
<!--<meta http-equiv="refresh" content="1" >-->
<style>
*{
padding-top:3px;
font-family: Lucida Sans Typewriter,Lucida Console,monaco,Bitstream Vera Sans Mono,monospace;
font-size:22px;
}
</style>

<div style="float:right; font-size:10px;">People in chat: {{ num_people }}</div>
<table>
{% for message_dic in chatlines %}
<tr>
<td name="msg" class="msg" style="color:rgb{{ message_dic["color"] }};"><b>{{ message_dic["username"] }}</b>: {{ message_dic["msg"] }}</td>
</tr>
{% endfor %}
</table>

</html>
Loading

0 comments on commit 3c1d1fa

Please sign in to comment.