Visualizzazione post con etichetta automatizzare. Mostra tutti i post
Visualizzazione post con etichetta automatizzare. Mostra tutti i post

giovedì 23 ottobre 2008

AriaDSL Autologin /3

Segue da AriaDSL Autologin /2.

Effettuare una request HTTP e leggere la relativa response con WSH è relativamente semplice. L'unico aspetto "delicato" è la creazione dell'oggetto XMLHTTP, che viene inizializzato tramite l'invocazione del metodo CreateObject dell'oggetto WScript.

function getPage(strURL)
{
    return httpRequest("GET", strURL, "");
}

function postPage(strURL, strParm)
{
    return httpRequest("POST", strURL, strParm);
}

function httpRequest(requestType, strURL, strParms)
{
    var xmlHTTP = WScript.CreateObject("Microsoft.XMLHTTP")
    xmlHTTP.open(requestType, strURL, false);
    if (strParms=="")
    {
        xmlHTTP.send();
    }
    else
    {
        xmlHTTP.send(strParms);
    }
    return xmlHTTP.responseText;
}

Una piccola funzione per gestire l'accodamento dei parametri all'URL:

function buildParametersList(strParametersList, parameterName, parameterValue)
{
    var result = strParametersList;
    if (strParametersList!="")
    {
        result += "&";
    }
   
    result += parameterName;
    result += "=";
    result += parameterValue
   
    return result;
}

Un po' di espressioni regolari per recuperare la password:

function extractPassword(strReturn)
{
    var infoRE = /hexMD5\('(.*)'\);/;

    var info = infoRE.exec(strReturn);

    var result = RegExp.$1;

    // prende i primi 4 caratteri
    var head = result.substr(0, 4);
   
    // prende dal 39° carattere fino in fondo
    var tail = result.substr(41, result.length-1);
   
    var pass = head + tail;
   
    return pass;
}

Uno dei maggiori problemi incontrati è stato nell'interfaccia con la funzione di hashing, che si aspetta una stringa di caratteri unicode. Soluzione:

function convertToCharString(strPassword)
{
    var splitted = strPassword.split("\\");
    var charString = "";
    for (i=1; i<splitted.length; i++)
    {
        var numeric = parseInt(splitted[i], 8);
        var charCode = String.fromCharCode(numeric);
        charString += charCode;
    }
    return charString;
}

Per quanto riguarda la funzione di hashing... beh, ho copincollato il file .js linkato dalla pagina di login dentro il mio script.

Pur trattandosi di un'implementazione molto naif sta funzionando da alcune settimane alcuni anni senza grossi problemi.

AriaDSL Autologin /2

Segue da AriaDSL Autologin /1.

Visto che la funzione che esegue l'hash MD5 è scritta in Javascript e visto che non avevo assolutamente voglia di convertirla decisi di adottare Windows Scripting Host per automatizzare la procedura, con la consapevolezza che in seguito un (eventuale) porting su Linux sarebbe stato difficoltoso, ma anche curioso di imparare a usare questo strumento. L'idea generale è rappresentata dal corpo dello script:

var strURL = "http://login.ariadsl.it/login"
var strPopupTitle = "Connessione ad AriaDSL";

autoConnect();

function autoConnect()
{
    // 1. scarica la pagina di login
    var strLoginPageBody = getPage(strURL);
   
    // 2. estrae la 'password' generata dal sistema e ne fa l'hash MD5
    var strPassword = extractPassword(strLoginPageBody);
   
    var charStringPassword = convertToCharString(strPassword);
    var strMD5 = hexMD5(charStringPassword);
   
    var strUsername = "ariadsl";
   
    // 3. prepara la chiamata alla pagina di login con i parametri
    var strParms = "";
    strParms = buildParametersList(strParms, "username", strUsername);
    strParms = buildParametersList(strParms, "password", strMD5);
    strParms = buildParametersList(strParms, "popup", "true");
    strParms = buildParametersList(strParms, "dst", "");
   
    // 4. invia la richiesta
    var resultPage = postPage(strURL, strParms);
   
    var WshShell = WScript.CreateObject("Wscript.Shell");
   
    // 5. visualizza un messaggio di successo/fallimento
    if (resultPage.indexOf("You are logged in")<0)
    {
        intButton = WshShell.Popup("Problema con la connessione!", 60, strPopupTitle, 21);
        if (intButton==4)
        {
            autoConnect();
        }
        else
        {
            intButton = WshShell.Popup("Connessione non effettuata!", 10, strPopupTitle, 16);
        }
    }
    else
    {
        intButton = WshShell.Popup("Connessione effettuata con successo", 5, strPopupTitle, 64);
    }
}

Le funzioni invocate verranno descritte in un post successivo (continua)

AriaDSL Autologin /1

Per poter accedere a Internet con la connessione Ariacom (a.k.a. AriaDSL) occorre ogni volta accedere a una pagina web di login - generata dall'antenna/router - in cui inserire username e password (peraltro uguali per tutti gli utenti). Questa procedura è piuttosto noiosa e tempo fa, in pieno spirito automatizzare a ogni costo, mi misi a studiare il problema, illudendomi che fosse di semplice e rapida soluzione.

La prima idea fu quella di inviare in POST i due parametri tramite uno script Ruby alla pagina di login:

require 'net/http'
require 'uri'
res = Net::HTTP.post_form(URI.parse('http://login.ariadsl.it/login'), {'username'=>'ariadsl', 'password'=>''})
puts res.body

Purtroppo... invalid username or password.

La cosa richiedeva un'analisi più approfondita. Osservando il sorgente della pagina di login notai un interessante frammento legato alla submit del form:

function doLogin() 
{
document.sendin.username.value = document.login.username.value;
document.sendin.password.value = hexMD5('\231' + document.login.password.value + '\077\131\115\326...[SNIP]...\210\040\053\053\173\066\024');
document.sendin.submit();
return false;
}

Quindi:

  1. la password apparentemente "vuota" è in realtà accompagnata da alcuni caratteri aggiunti proditoriamente dallo script
  2. la password viene convertita con una funzione di hash MD5 lato client prima di essere inviata al router

Senza darmi per vinto modificai la pagina salvata in modo che la action del form puntasse a una pagina PHP su WampServer creata appositamente per fare il dump dei parametri della request. Copiai il parametro 'password' e lo incollai nello script Ruby ma... invalid username or password.

Piuttosto perplesso esaminai di nuovo la pagina e... sorpresa (ma fino a un certo punto)! I caratteri di controllo presenti nella pagina erano diversi! Evidentemente vengono generati dal router di volta in volta.

La faccenda stava diventando sempre più interessante... (continua)