Repository
https://github.com/r351574nc3/we-resist-bot
Motivation
Recently, a number of accounts have had funds stolen immediately by using an active/master key in a transfer memo. It's immediate because there is a bot that waits for keys to appear in a memo or comment, and then transfers funds from the account immediately. See for example
New Features
Below is a sample post that is made to notify the account owner of the compromise.
I wanted to provide a way to protect people from this. Many of us in spite of being careful not to post keys, do it anyway. A bot will automatically detect this mistake and prey upon people before they can correct it. I wanted to provide a way to lock down a user's account before the bot can get to it. It does this by
- Adding @the-resistance as an account manager
- Remove existing keys from the account
- Change the password
Details
Added check for transfer that have keys in them
case "transfer":
try {
const private_key = operation.memo
let public_key = steem.auth.wifToPublic(private_key)
wif_is_valid = steem.auth.wifIsValid(private_key, public_key)
if (wif_is_valid && operation.from == 'perpetuator') {
return processTransfer(operation, private_key, public_key)
}
}
catch (error) {
if (error.message.indexOf("Non-base58 character") < 0
&& error.message.indexOf("Expected version") < 0
&& error.message.indexOf("Index out of range") < 0) {
console.log("Rethrowing ", error)
throw error // rethrow
}
}
break;
Prove it's a private key by generating a public key and verifying it. If this is the case, we then process the transfer.
Add a function for processing transfers
Assuming the transfer has a key in the memo
function processTransfer(transfer, private_key, public_key) {
const password = steem.formatter.createSuggestedPassword();
const account_name = transfer.from
const new_active_keypair = generate_keys(account_name, password, "active");
A new password and set of keys are created
Make sure a record of the transfer and temporary keys is persisted in case anything goes wrong
Keys are temporary, so it's ok to store them
// Save keys to datastore
models.Recovery.create({
username: transfer.from,
password: password,
memo: transfer.memo,
privateKey: new_active_keypair.private_key,
publicKey: new_active_keypair.public_key })
.then((recovery) => {
console.log("Recovery saved for ", transfer.from)
})
Add authorities to the compromised account and remove the old keys
// Add the-resistance as manager
return addAccountAuth(private_key, transfer.from, "the-resistance", "active", 10000)
.then((results) => {
// Extra key for management
return addKeyAuth(private_key, transfer.from, new_active_keypair.public_key, "active", 10000)
})
.then((results) => {
// Remove the old key so things can't be stolen
return removeKeyAuth(private_key, transfer.from, public_key, "active")
})
.then((results) => {
This makes it so that @the-resistance now controls the account on behalf of the user and has removed the key that was compromised.
Notification of compromised account
// Post something to let the account holder know what to do.
const context = {
owner: transfer.from
}
return loadTemplate(path.join(__dirname, '..', 'templates', 'hijack.hb'))
.then((template) => {
var templateSpec = Handlebars.compile(template)
return templateSpec(context)
})
.then((message) => {
var new_permlink = 'this-account-is-protected'
+ '-' + new Date().toISOString().replace(/[^a-zA-Z0-9]+/g, '').toLowerCase();
console.log("Commenting on ", transfer.from, new_permlink)
return steem.broadcast.commentAsync(
wif,
"", // Leave parent author empty
"abuse", // Main tag
transfer.from, // Author
new_permlink, // Permlink
"This Account is Protected by @the-resistance",
message, // Body
{ tags: ['the-resistance'], app: 'we-resist-bot/0.1.0' }
).then((results) => {
console.log(results)
return results
})
.catch((err) => {
console.log("Error ", err.message)
})
})
The above notifies via steemit post of the compromise. The intent is that the user notices the post was made in his/her own account and attempts to contact @the-resistance for recovery
New methods for handling adding/removing authorities
+ active,
+ posting,
+ userAccount.memo_key,
+ userAccount.json_metadata
+ )
+ });
+}
+
+/**
+ * Removes an authority using a public key
+ * @param {*} signingKey
+ * @param {*} username
+ * @param {*} authorizedKey
+ * @param {*} role
+ */
+function removeKeyAuth(signingKey, username, authorizedKey, role) {
+ return steem.api.getAccountsAsync([username])
+ .map((userAccount) => {
+ const updatedAuthority = userAccount[role];
+ const totalAuthorizedKey = updatedAuthority.key_auths.length;
+ for (let i = 0; i < totalAuthorizedKey; i++) {
+ const user = updatedAuthority.key_auths[i];
+ if (user[0] === authorizedKey) {
+ updatedAuthority.key_auths.splice(i, 1);
+ break;
+ }
+ }
+
+ /** Release callback if the key does not exist in the key_auths array */
+ if (totalAuthorizedKey === updatedAuthority.key_auths.length) {
+ return null;
+ }
+
+ const owner = role === 'owner' ? updatedAuthority : undefined;
+ const active = role === 'active' ? updatedAuthority : undefined;
+ const posting = role === 'posting' ? updatedAuthority : undefined;
+
+ return steem.broadcast.accountUpdateAsync(
+ signingKey,
+ userAccount.name,
+ owner,
+ active,
+ posting,
+ userAccount.memo_key,
+ userAccount.json_metadata
+ );
+ });
+}
Added template loading to post a notification that the account was compromised
+
+function loadTemplate(template) {
+ return fs.readFileAsync(template, 'utf8')
+}
function current_voting_power(vp_last, last_vote) {
var seconds_since_vote = moment().add(7, 'hours').diff(moment(last_vote), 'seconds')
@@ -297,12 +303,204 @@ function processComment(comment) {
})
}
Added a function for generating keys from a new password
+function generate_keys(account, password, role) {
+ const private_key = steem.auth.toWif(account, password, role);
+ const public_key = steem.auth.wifToPublic(private_key);
+ return { private_key: private_key, public_key: public_key };
}
Your contribution has been evaluated according to Utopian policies and guidelines, as well as a predefined set of questions pertaining to the category.
To view those questions and the relevant answers related to your post, click here.
Need help? Write a ticket on https://support.utopian.io/.
Chat with us on Discord.
[utopian-moderator]
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
Did this end up unapproved then?
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
Are the rewards declined?
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
I sent this before discussing in the discord help channel. Turns out it was a bot issue, but still no upvote.
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
The software is not exclusive to @the-resistance. It is open, free, and configurable for anyone. If there is another that wants to run this, that would be great ^_^
Messages are handlebars templates https://github.com/r351574nc3/we-resist-bot/tree/master/app/helpers/templates
The configuration could be more elegant. https://github.com/r351574nc3/we-resist-bot/blob/master/app/config/index.js
The bot is intended to recognize downvote abuse and defend against it through upvoting and automated comments.
We are expanding into other abuse areas like phishing which is the reason for this change. Unfortunately, the bot is not configurable to what type of abuse to manage. I think that would be a good suggestion for the roadmap.
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
@r351574nc3 Thank you, that answers that!
Good luck with the project. 👍
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
Vote r351574nc3 for witness.
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
Vote for r351574nc3
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
This is awesome. Is there a way you can make sure that your bot is faster than the other one who is trying to steal the funds?
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
Actually, in hindsight, it might be far less intrusive to just move stuff into savings and leave a post instead of locking down the account.
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
Yes, it is less intrusive not to lock down the account. On the other hand, I think the most important thing is that the account gets protected.
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
I think that's the consensus I've been hearing.
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
I have noticed that the bot stealing the funds seems to do it within a minute, and this has responded faster than that. I'm pretty confident it's faster. I think some improvements I can make are to
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
I run a bot with similar motive which recently got its first success. It is good to see more people are joining to help mitigate this issue.
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
Viva la resistance !
FD
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
Sneaky Ninja Attack! You have just been defended with a 14.28% upvote!
I was summoned by @r351574nc3. I have done their bidding and now I will vanish...
woosh
A portion of the proceeds from your bid was used in support of youarehope and tarc.
Abuse Policy
Rules
How to use Sneaky Ninja
How it works
Victim of grumpycat?
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit