Voxer Bot Attempts

The following are various notes and findings on trying to create a “Bot” for Voxer. Voxer doesn’t really have any options for web hooks, and the SDK is still in beta. We’ll be exploring sending messages to a channel, how to use Curl or Fetch to send messages. Unfortunately, signing in does not appear to be easy to automate.

Looking at Voxer

Voxer is primarily used via the modile apps, but there is also a web version at web.voxer.com

Using Firefox, we can play with the Web Tools to get a better idea of what is going on.

All the Javascript is easily readable. api.js is of interest. This can helps us understand how messages are sent.

The Web interface seems to be somewhat buggy, could be it just doesn’t like going through burp, but had better luck monitoring the channel for new messages from a phone.

Proxying the traffic through Burp is tricky. The log in does not appear to work while the proxy is active, but you can log in, and then activate the proxy to capture and replay sending messages.

If you get “Voxer is open in another tab. Please click ‘OK’ then close this tab.” clear all voxer cookies and log in again. Seems to happen quite often.

Interestingly, if you send a message, then send that message POST request to Repeater, change the text and resubmit it, it “Updates” the text of the message. So if you know the message_id, or maybe the create_time, you can change the text in messages.

About Sending Messages

When you send a message, there are 2 POST requests sent. The first one sends the message and the second one consumes the message. It looks like you really only need the first post message request to actually send messages. Think the consume message post is for the browser to trigger a refresh on the messages in the message list.

There are two variables that change message to message

message_id and create_time

Looking into the api.js file we see that create_time is done with the following code

now = new Date().getTime() / 1000,

And here is the code used to generate the message_id. First part is the time, the next part is random.

window.generate_message_id = function (type) {
        var message_id = new Date().getTime() + "_" + Math.floor((Math.random() * 10000000000) + 1000);

        if (type && type === "image") {
            message_id += "_v1.jpg";
        }

        return message_id;
    };

Structure of the POST request

The following entries are slightly abbreviated

Cookies

There are a bunch of tracking cookies, the session cookie is the one we are interested in. The Rv_session_key is what will allow us to actually send messages. As a side note, it appears that every time we send a message, there is analytics that is also sent, saying who the message was sent to, and when.

session={"gcp1-prod":{"user_id":"MyVoxerUser_ID","Rv_session_key":"db0bc8a069148140151a38bab2098a01"}}

Body

{"message_id":"1681191108600_7318671093","create_time":1681191108,"model":"User_Agent","content_type":"text","from":"MyVoxerUser_ID","subject":"Walkie","body":"This is the text of the message","thread_id":"This.is.the.thread_id"}

Using JavaScript / fetch

After some trial an error, the following solution finally worked. You can copy the following code, and run with “node file.js”

// Change variables as needed
let body = "Hello World!"
// let body = process.argv.slice(2) // You can use this if you want to pass in the message as an argumant. i.e. node voxer.js "Voxer Message"
const threadID = 'ThreadID'
const fromID = 'UserID'
let cookie = `session={"gcp1-prod":{"user_id":"${fromID}","Rv_session_key":"RVSESSIONID"}}`
let time = new Date().getTime() / 1000
let messageID = new Date().getTime() + '_' + Math.floor(Math.random() * 10000000000 + 1000)

// Send message.
function sendMessage () {
  fetch(`https://gcp1-prod-nr60.voxer.com/2/cs/post_message?now=${time}`, {
    credentials: 'include',
    credentials: 'same-origin',
    headers: {
      'User-Agent': 'UserAgent',
      Accept: '*/*',
      'Accept-Language': 'en-US,en;q=0.5',
      'Content-Type': 'text/plain',
      'Sec-Fetch-Dest': 'empty',
      'Sec-Fetch-Mode': 'cors',
      'Sec-Fetch-Site': 'same-site',
      'sec-ch-ua-platform': '"Windows"',
      'sec-ch-ua': '"Opera";v="97", "Chromium";v="97", "Not=A?Brand";v="24"',
      'sec-ch-ua-mobile': '?0',
      Cookie: cookie
    },
    referrer: 'https://web.voxer.com/',
    body: `{\"message_id\":\"${messageID}\",\"create_time\":${time},\"model\":\"\",\"content_type\":\"text\",\"from\":\"${fromID}\",\"subject\":\"CHANGING\",\"body\":\"${body}\\n\",\"thread_id\":\"${threadID}\"}\r\n`,
    method: 'POST',
    mode: 'cors'
  })}

 sendMessage()

You can find the thread_id, user_id, session cookie by toggling the developer console, logging into Voxer, and send a message.

How to POST a message to a Telegram Channel

It’s fairly easy to send a message to a Telegram Channel using curl. Copy and paste the following command in, replacing the API_TOKEN, chat_id, and test_message, with the appropriate items.

curl -s -o /dev/null -X POST -H "Content-Type: application/json" -d "{\"chat_id\": \"-100XXXXXXXXXX\", \"text\": \"test_message\", \"disable_notification\": true}" https://api.telegram.org/bot{API_TOKEN}/sendMessage

https://stackoverflow.com/questions/68213124/telegram-example-of-how-to-use-curl-for-windows-10-to-message-myself-with-a-bot

Need to send a message to Teams? Check out the following post.

Bash script to send messages to Microsoft Teams

Send Post Webhook Message to Teams from Mikrotik RouterOS

The following is the correct syntax needed to send a message to Microsoft Teams from a Mikrotik router. You will need a valid Teams webhook to send to.

Change “Test Message” out for your message. You should receive a “status: finished” response.

/tool fetch http-method=post http-header-field="Content-Type: application/json" http-data="{\"text\": \"Test Message\"}" url=https://domain.webhook.office.com/webhook/long/string

https://help.mikrotik.com/docs/display/ROS/Fetch