Skip to content

Instantly share code, notes, and snippets.

@jroper
Last active February 3, 2021 02:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jroper/5cc5f7359ccc89a2a5b9691c33114b9d to your computer and use it in GitHub Desktop.
Save jroper/5cc5f7359ccc89a2a5b9691c33114b9d to your computer and use it in GitHub Desktop.
Allow password managers to work with ING Australia's login
// ==UserScript==
// @name ING Australia Password Manager Fix
// @namespace https://jazzy.id.au/
// @version 0.1
// @description Allows the industry best practice of using a password manager for storing passwords for ING Australia's login screen.
// @author James Roper
// @match https://www.ing.com.au/securebanking/
// @grant none
// ==/UserScript==
(function() {
'use strict';
console.log("Running script");
// First, give the client number field a name. This ensures that it can be filled by a password manager.
document.getElementById("cifField").setAttribute("name", "username");
// The ING password field is protected by a home grown "security feature" (lesson number one of security, don't use
// your own home grown security mechanism), where they use images of digits presented in a random order, and which
// image is for which digit is only known by the server, and of course anyone that can read the digit from the
// image. The actual binary data of the image changes each time, so we can't just do a mapping of binary data to
// digit. However, it's trivial to recognise the number by looking at the image itself, for example, the number
// zero has an orange pixel at the centre of the image, while the number 8 has a white pixel. By inspecting a few
// specially chosen pixels, we can define a heuristic to map an image to a digit. So that's what this does. I don't
// know why they have this "security feature", it's not a security feature at all, it's as dumb as ROT13. The
// connection is already encrypted, so eavesdroppers can't discover the password anyway, this only gets in the way
// of scripts running locally (such as a password manager) from accessing the password, but as this script shows,
// it's trivial to work around it. I guess the person that created this thinks they are very clever, more clever
// than all the rest of the IT security industry who recommends strongly against these types of practices. *sigh*
function identify(img) {
let myImg = new Image();
myImg.src = img;
let context = document.createElement("canvas").getContext("2d");
context.drawImage(myImg, 0, 0);
function isOrange(x, y) {
return context.getImageData(x, y, 1, 1).data[2] === 0;
}
function isWhite(x, y) {
return context.getImageData(x, y, 1, 1).data[2] === 255;
}
if (isOrange(90, 55)) {
if (isOrange(85, 57)) return 5;
else if (isWhite(96, 47)) return 0;
else if (isWhite(89, 59)) return 4;
else return 6;
} else if (isOrange(95, 62)) {
if (isOrange(96, 67)) return 7;
else if (isWhite(97, 47)) return 2;
else return 1;
} else {
if (isWhite(84, 61)) return 8;
else if (isWhite(84, 47)) return 9;
else return 3;
}
return -1;
}
// A map of keypad digits to the image for that digit.
let keypadMap = new Array(10);
// Populate the keypad map by identifying which digit is in each image.
function populateMap() {
let keys = document.querySelectorAll("img.ing-keypad");
for (let i = 0; i < keys.length; i++) {
// ignore delete and cancel keys
if (i != 9 && i != 11) {
let digit = identify(keys[i].src);
console.log("Identified key " + i + " as " + digit);
keypadMap[digit] = keys[i];
}
}
}
// Reads the password field, and clicks the corresponding number images to fill it out.
function enterPassword() {
let pwField = document.getElementById("password");
let pw = pwField.value;
if (pw.length > 0) {
for (let i = 0; i < pw.length; i++) {
let digit = parseInt(pw.charAt(i));
keypadMap[digit].click();
}
pwField.onchange = function() {};
return true;
}
return false;
}
// It takes time for the keypad to initialize, so we give it a second.
window.setTimeout(function() {
populateMap();
// Try and fill out the password immediately, hoping that a password manager has filled it,
// but if it hasn't, then set an onchange event handler.
if (!enterPassword()) {
document.getElementById("password").onchange = enterPassword;
}
}, 1000);
})();
@jroper
Copy link
Author

jroper commented Oct 30, 2018

This works for me with lastpass - though you do need to configure lastpass with custom form fields so that it fills the client number into the "username" field and your access code into the "password" field.

@gue88
Copy link

gue88 commented Feb 3, 2021

dont know if this still works for you man, but i can't get it to work with Lastpass

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment