A BitMessage Spam bot written by Python

Photo by Markus Spiske / Unsplash
BitMessage is a anonymous messaging app which uses blockchain to communicate. There is gotta be some evil things I can do with it. - 22 yo me.

Why?

Recently I been studing Blockchain and I remembered there is a messenger app called BitMessage which is used by activisms during protests a while ago. So I download it back to my laptop to see what has changed since. If you are not familar with BitMessage, it is a really cool application lives on blockchain, you can find more useful and accurate information on their wiki page.

After surfing around their Github I found that they have API built into client allows other program to communicate with the client. So, why not try to spam this specific protocol to see what blockchain can prevent with spam bot?

Let's Spam It!

Environment

First, we are going to use PyBitmessage to be our API Proxy. Basically it runs on all platforms because of Python + QT, pick whatever operation system you have and let's roll!

First of all we need to open our newly downloaded Bitmessage client and let it sync through the data. Sync is finished if you sees Object(s) to be synced = 0, next we need to retrieve keys.dat. Normally keys.dat is bundled within the application. Go to Settings -> Settings and check the Run in Portable Mode. After you close the application, keys.dat should appear inside the folder.

Enable API Proxy

Use your favorite editor to open keys.dat, add following information.

apiport = 8442
apiinterface = 127.0.0.1
apiuseranme = <desireusername>
apipassword = <desirepassword>

After that, open Bitmessage again and uncheck the Run in Portable Mode, you can let it keep running in the background.

The Making of Spam Bot

Bitmessage API is using xmlrpc to communicate according to API docs. We can use simple Python console to test the connection.

apiUri = "http://username:password@127.0.0.1:8442"
api = xmlrpclib.ServerProxy(apiUri)

After connection is established you can use simple add method to check if client can start handle requests.

>>>> api.add(2,3)
5
>>>> 

Retrieve Chan Addresses

To make the spam bot have most converages of users, target to Chans is the best idea. First we need to retrieve all address which I pre-added to Bitmessage. Example code will be something like below

import xmlrpclib
import json

def get_address():
    client_address = json.loads(api.listAddresses())
    target_address = []
    y = 0
    
    for x in client_address['addresses']:
        target_address.append(client_address['addresses][y]['address'].encode('ASCII', 'ignore'))
        y += 1
        
    return target_address

if __main__ == "__name__":
	apiUri = "http://username:password@127.0.0.1:8442"
	api = xmlrpclib.ServerProxy(apiUri)
	print get_address()

After can printed all the addresses, we need to work on the spam part.

Send Message

On official API docs send message through API is fairly easy, encode desire message to base64 and you are able to send the request to API. However, this is not a good spam bot, it require to modify hardcoded message in order to spam a new message. Proper spam bot is needed to edit message on the fly while program is running, so we need to make a text file to store messages. I make a content.txt to store spam message.

This is title, I'm going to spam message.
This is the message body

In order to read this file, we need to write a new method.

def send_message():
	with open('content.txt', 'r+') as f:
    con = f.read().split('\n', 1)
    subject = con[0]
    content = con[1]
    subject = subject.encode('utf-8').strip()
    subject_data = base64.b64encode(subject)
    content = content.encode('utf-8').strip()
    content_data = base64.b64encode(content)
    
    while True:
    	for j in addrs:
        	ack_data = api.sendMessage(j, j, subject_data, content_data, 2)
            print 'Sent:', ackData
            time.sleep(2)
        print 'All address are looped, sleep 3600 seconds.'
        time.sleep(3600)
    return

And now you have your simple spam bot.

Polish the Code

Since the end game is distribute to cross-platform, we need to make our little spam bot able to self-generate config file and content text file.

import os
import sys
from os import path, environ

def get_config_folder():
	app_folder = "PyBitmessage"
    data_folder = None
    if "BITMESSAGE_HOME" in environ:
    	data_fonder = environ["BITMESSAGE_HOME"]
        if data_fonder[-1] not in [os.path.sep, os.path.altsep]:
        	data_folder += os.path.sep
    elif sys.platform == 'darwin':
    	if "HOME" in environ:
        	data_folder = path.join(os.environ["HOME"], "Library/Application Support/", app_folder) + '/'
    elif sys.platform == 'win32':
    	data_folder = path.join(environ["APPDATA"].decode(sys.getfilesystemencoding(), 'ignore'), app_folder) + path.sep
    else:
    	try:
        	data_folder = path.join(environ["XDG_CONFIG_HOME"], app_folder)
        except KeyError:
        	data_folder = path.join(environ["HOME"], ".config", app_folder)
            
    return data_folder

Using method above we can retrieve keys.dat from almost every platforms. Then we need to retrieve api config from it. To be noted keys.dat have same format of ConfigParser. After little copy and paste, result should somewhat like this.

import os
import sys
from os import path, environ

def get_config_folder():
	app_folder = "PyBitmessage"
    data_folder = None
    if "BITMESSAGE_HOME" in environ:
    	data_fonder = environ["BITMESSAGE_HOME"]
        if data_fonder[-1] not in [os.path.sep, os.path.altsep]:
        	data_folder += os.path.sep
    elif sys.platform == 'darwin':
    	if "HOME" in environ:
        	data_folder = path.join(os.environ["HOME"], "Library/Application Support/", app_folder) + '/'
    elif sys.platform == 'win32':
    	data_folder = path.join(environ["APPDATA"].decode(sys.getfilesystemencoding(), 'ignore'), app_folder) + path.sep
    else:
    	try:
        	data_folder = path.join(environ["XDG_CONFIG_HOME"], app_folder)
        except KeyError:
        	data_folder = path.join(environ["HOME"], ".config", app_folder)
            
    return data_folder

def get_bm_config(setting_key):
	value = None
    try:
    	value = cp.get(settings_section, setting_key)
    except Exception as e:
    	pass
    return value

if __main__ == "__name__":
	cp = ConfigParser.SafeConfigParser()
    cp.read(get_config_fonder() + 'keys.dat')

After combine both config retrieve and send message code, this spam bot is ready for production.

Conclusion

I have made couple of spam bots, basically all bots does the same thing, same concept. The idea of using blockchain to deliver message is paradox, it is good to be fully anonymous when you trying to say something on the internet. However, this concept can also be easily adopt by criminals. Overall I think even though Bitmessage have come so far, there is still a long way to go.

About Bitmessage's POW (Proof-of-Work) even at this point writing this article, I have not figure it out how this system worked. I tried to browse someone elses code on send message part they are not using that method too. At first I did not think my bot will be working without doing POW before message sending out, but I do get some replies from Chans that complaining the dummy message I sent. Hence I guess this worked. :P