Tuesday, May 14, 2013

DYNDNS Auto Login - A programmatic approach

So DynDns.org have decided to make their precious users work for their free accounts by forcing users to login periodically. What a pain in the ass! How can we automate this process programmatically? Lets explore the inner workings of the DynDns login page:


Apart from the obvious fields we need to send back (username, password...),  you can see by the highlighted sections there are 2 hidden fields. Thankfully the first one (iov1) is not necessary for a login - this field is rather complexly generated by javascript and does not render except in a browser. This means we are only interested in the "multiform" hidden field which we need to send back in our request.

That sorts out the fields we need to populate: username, password, submit and multiform. What about cookies? Yes there are cookies but that is easily captured in the header request message (set-cookie) and we can simply attach the same cookie back into our response header unmodified.

I have since setup a cron job on Google App Engine that runs my code once every month. This should help alleviate the headache of logging in manually every month. Here is the code if anyone wants to use it:


URL url = new URL("https://account.dyn.com/entrance/");
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("GET");
//Spoof as if from a web browser
con.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31");
con.setDoOutput(true);
con.connect();
// give it 15 seconds to respond
con.setReadTimeout(15 * 1000);

//Get cookie id
String headerName = null;
String cookie = null;
for(int i = 1; (headerName = con.getHeaderFieldKey(i)) != null; i++) 
{
 if (headerName.equalsIgnoreCase("Set-Cookie")) 
 {                  
  cookie = con.getHeaderField(i);
  break;
 }
}

//Read the website into a string so we can parse with Jsoup
BufferedReader reader = new BufferedReader(new InputStreamReader(con.getInputStream()));
String temp = "";
String line = null;
while((line = reader.readLine()) != null)
{
 temp += line + "\r\n";
}
reader.close();

Document doc = Jsoup.parse(temp);
Elements els = doc.getElementsByTag("input");
String multiform = null;
for(Element e : els)
{
 String name = e.attr("name");
 String value = e.attr("value");
 //Get the hidden field required for login
 if(name.equalsIgnoreCase("multiform"))
 {
  multiform = value;
  break;
 }
}

//Open the connection again with the correct headers
con = (HttpURLConnection) url.openConnection();
con.setRequestProperty("Cookie", cookie);
con.setRequestProperty("Host", "account.dyn.com");
con.setRequestProperty("Origin", "https://account.dyn.com");
con.setRequestProperty("Referer", "https://account.dyn.com/entrance/");
con.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31");
con.setRequestMethod("POST");
con.setDoOutput(true);

String username = "your_username_here";
String password = "your_password_here";

//Set the login fields
OutputStreamWriter out = new OutputStreamWriter(con.getOutputStream());
String data = 
 URLEncoder.encode("username", "UTF-8")
 + "="
 + URLEncoder.encode(username, "UTF-8");
 data += "&"
 + URLEncoder.encode("password", "UTF-8")
 + "="
 + URLEncoder.encode(password, "UTF-8");
 data += "&"
 + URLEncoder.encode("submit", "UTF-8")
 + "="
 + URLEncoder.encode("Log in", "UTF-8");
 data += "&"
 + URLEncoder.encode("multiform", "UTF-8")
 + "="
 + URLEncoder.encode(multiform, "UTF-8");
out.write(data);
out.flush();
out.close();

//Read the response message
reader = new BufferedReader(new InputStreamReader(con.getInputStream()));
boolean success = false;
while((line = reader.readLine()) != null)
{
 //If the title says we are logged in everything worked
 if(line.contains("My Dyn Account"))
 {
  success = true;
  break;
 }
}

if(success)
{
 System.out.println("Succesfully logged in!");
}
else
{
 System.out.println("Login failed!");
}

reader.close();