2

I'm trying to write a greasemonkey script that autofills ESXi login web page with credentials. The following code fills the input fields and enables the submit button:

// ==UserScript==
// @name         ESXi autofill credentials
// @namespace    https://esx_address/ui/*
// @version      0.1
// @description  Autofills user and password inputs
// @author       Mirek
// @require      http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js
// @require      https://gist.github.com/raw/2625891/waitForKeyElements.js
// @match        https://esx_address/ui/*
// @grant        none
// ==/UserScript==

try {
    waitForKeyElements ("#password", function(jNode) {jNode.val("password");});
    waitForKeyElements ("#username", function(jNode) {jNode.val("user");});
    waitForKeyElements ("#submit", function(jNode) {jNode.prop("disabled", false);});
} catch(err) {
    console.log("Something went wrong: " + err.message);
}

Unfortunately, after I click the submit button I see Cannot complete login due to an incorrect user name or password. even though the credentials are correct.

The issue is probably with the submit button enabling. The login page enables it once the user types something into username field. Unfortunately, the script doesn't trigger this behavior. I found out that with the script enabled, when I go to the login page and re-type the last letters of username and password fields, the login succeeds.

How could I fill the input fields in a way that will trigger the submit button to turn on?

Mirek
  • 355
  • 4
  • 16
  • If you want to match multiple IP addresses, you can replace ``@match https://esx_address/ui/*`` with ``@include /^https?:\/\/(\d+\.){3}\d+\/ui\/.*/`` You can test your include regex on https://regex101.com. Please note that include tag may be obsolete in Manifest v3. – eduardomozart Mar 09 '23 at 13:27
  • You should add ``http://ajax.googleapis.com`` to ``https://ajax.googleapis.com`` so it will load jQuery dependencies using HTTPS connection instead of HTTP. – eduardomozart Mar 09 '23 at 19:04

2 Answers2

1

This is an annoying worst-practice1 that generally requires you to spoof user events.

  1. The details of what's required are site/app specific (and I'm not going to play around with esxi anytime soon).
    Sometimes you just need to send a change event. Sometimes you need to fake an entire mousein, key dow/press/up, mouseout, blur state-machine -- often with time delays and/or state checks between key steps.
    See below for intermediate-level, example code.
  2. Some sites, like Patreon, are especially convoluted.
  3. It's theoretically possible for a site to block this entirely, since userscripts can't create trusted events -- especially keyboard events.
  4. Irregardless, DO NOT PUT ACCOUNT CREDENTIALS IN A USERSCRIPT!
    Don't take my word for it, look it up...
    At a minimum, use an encrypted framework.

The smart thing to do is to use a password manager. For example, KeePass' "Two-Channel Auto-Type Obfuscation" defeats most of these cr*ppy logins. KeePass is open-source.


Example of the kind of code needed if you don't use a good password manager.
This works for my local supermarket. It will not be enough for more difficult sites like many banks. It may NOT work, as-is, for any other site. See #1 above.

function loginSite () {
    //--- Set username and password, from decrypted values. ---
    $("div.input_box_div #userId").attr ('value', usr);
    $("div.input_box_div #password").attr ('value', pword);

    //--- New style login page, approx 10/28/16...
    $('#keepMeSignedIn').prop ('checked', true);
    $("#input-email").attr ('value', usr);
    $("#password-password").attr ('value', pword);
    $("#show-password-checkbox")[0].click (); //-- This is needed for blur hack!!!?!?!

    setTimeout (function () {
        changeAndBlur ("#input-email");
        changeAndBlur ("#input-password");
    }, 400);

    waitForKeyElements ("#create-account-btn", clickWhenPrimed, true);
}

function changeAndBlur (jSelector) {
    let node    = $(jSelector);
    if (node.length) {
        var evChng  = new Event("change");
        node[0].dispatchEvent (evChng);
        var evBlur  = new Event("blur");
        node[0].dispatchEvent (evBlur);
    }
}

function clickWhenPrimed (jNode) {
    if (jNode.css("background-color") == "rgb(228, 23, 32)") {
      click_jNode (jNode);
    }
    else
        return true;
}

Note that for all other sites, I personally use a password manager that can autofill every web page login I use (so far, and with some tweaking).


1 Sites/apps like that are annoying and dangerous as they cripple the functionality of password managers. NIST has recently disapproved of password manager blocking. So complain vigorously to the culpable organization and support any pending laws/regulations that your jurisdiction may have to allow both long pass phrases and password managers.

Brock Adams
  • 90,639
  • 22
  • 233
  • 295
  • Thanks, mate! The changeAndBlur did the trick. I'm wondering - why do the developers follow this practice? Is it to increase security? Malicious coder will always find a way to work around such "features" anyway... – Mirek Nov 14 '17 at 11:39
  • 1
    Sometimes it's a misguided attempt to stop bots (it doesn't). Sometimes it's just a poor validation engine. Sometimes it just makes bad assumptions about how users work (assuming they mouse in, manually type credentials, mouseout, etc.), when in fact, the best users use one-time passwords (or pass phrases) in conjunction with a password manager. – Brock Adams Nov 14 '17 at 20:03
0

Thanks to Brock's input I was able to succeed. Thanks! Works with ESXi version: 6.5.0, Client version: 1.18.0.

The change and blur events successfully enable the submit button.

function loginSite() {
    waitForKeyElements("#username", function(jNode) {jNode.val(username);}, true);
    waitForKeyElements ("#password", function(jNode) {jNode.val(password);}, true);
    setTimeout(function() {
        changeAndBlur("#username");
        changeAndBlur("#password");
    }, 400);
}

function changeAndBlur(jSelector) {
    let node = $(jSelector);
    if (node.length) {
        var evChng = new Event("change");
        node[0].dispatchEvent(evChng);
        var evBlur = new Event("blur");
        node[0].dispatchEvent(evBlur);
    }
}

waitForKeyElements("#submit", loginSite, true);
Mirek
  • 355
  • 4
  • 16
  • Please do not forget to add to this script the ``UserScript`` header available at the first answer. – eduardomozart Mar 09 '23 at 13:26
  • You should also use quotes between ``username`` (line 2) and ``password`` (line 3), e.g. ``jNode.val("username");`` and ``jNode.val("password");`` – eduardomozart Mar 09 '23 at 15:55