Amazon Kindle Hack and Download PDF/Image

I usually try to hack/break websites by automating page traversal’s
There are different levels of security in websites

  1. Data Security which is generally taken care by HTTPS
  2. User Authentication for which we generally have session tokens like JSESSIONID,PHPSESSIONID,ASPSESSIONID or Highly scalable approaches like JSON Web Token
  3. Authorization is mostly custom built but there are some standard processes like OAuth or SAML
  4. Code security which is generally taken care URL Masking, Obfuscation of Javascript/html elements
  5. And adding hidden form elements which maintain state which gets exchanged between server and client
  6. Apart from this other kinds of security that can take care of same origin scripts

Even after taking care of all these website that publish copyright content face a different kind of issue.

7. How not to allow user to download/copy/save the copyrighted material on his machine.

My current topic is related to above issue. I assumed google/amazon and other providers which publish copyrighted material online have taken care of this already .

I just wanted to make a simple testcase and found that none of them are secure.

So Authors beware of hackers with similar skillset 

I’ve used simple Utilities that are well known in automation world.

Selenium and CasperJS to test this and found that it just takes make be 1-2 hours to break them.

Problem with these site’s is that they are not at all designed to cover the 7th point in all angles.

Pasting sample code that downloads books from kindle and save’s as PDF

Note: I’m capturing images (Which is increasing size of pdf . I could have directly converted to pdf and merged all pages, But my scope is not effectiveness)


package com.thoughtlane.experiments.kindlehack;

import java.io.File;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.List;

import org.apache.commons.io.FileUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.Dimension;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;

import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Image;
import com.itextpdf.text.pdf.PdfWriter;

/**
 * @author ashwinrayaprolu
 *
 */
public class AmazonKindleBookDownloader {
	public static final String DEST = "results/pdf/multiple_images.pdf";

	/**
	 * @param args
	 */
	public static void main(String... args) {
		System.setProperty("webdriver.chrome.driver", new File("driver/chromedriver").getAbsolutePath());
		File resourcesFolder = new File("resources");

		WebDriver driver = new ChromeDriver();

		driver.manage().window().maximize();
		driver.manage().window().setSize(new Dimension(1279, 682));
		driver.navigate().to("http://read.amazon.com");

		waitSomeTime();

		String appTitle = driver.getTitle();
		System.out.println("Application title is :: " + appTitle);

		driver.findElement(By.id("ap_email")).sendKeys("AMAZON_USERNAME");
		driver.findElement(By.id("ap_password")).sendKeys("AMAZON_PASSWORD");
		driver.findElement(By.id("signInSubmit-input")).click();

		waitSomeTime();

		File screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
		try {
			FileUtils.copyFile(screenshot, new File(resourcesFolder, "HomePage.jpg"));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		driver.switchTo().frame("KindleLibraryIFrame");

		WebElement element = driver.findElement(By.cssSelector("span#kindle_dialog_firstRun_button.chrome_btn"));

		element.click();

		waitSomeTime();

		screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
		try {
			FileUtils.copyFile(screenshot, new File(resourcesFolder, "HomePage2.jpg"));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		List<WebElement> books = driver.findElements(By.cssSelector("img.book_image.book_click_area"));

		
		/***
		 * Iterate over all books on the dashboard/home page
		 */
		for (WebElement book : books) {

			String bookTitle = book.getAttribute("title").substring(0, 24);
			book.click();

			File bookFolder = new File(resourcesFolder, "" + bookTitle.replaceAll(" ", ""));
			bookFolder.mkdirs();

			try {
				FileUtils.writeStringToFile(new File(resourcesFolder, "HomePage.html"), driver.getPageSource(), Charset.defaultCharset());
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}

			driver.switchTo().parentFrame();
			driver.switchTo().frame("KindleReaderIFrame");

			// WebElement menuLink =
			// driver.findElement(By.cssSelector("div#kindleReader_button_goto.header_bar_icon"));

			// Actions actions = new Actions(driver);
			// actions.moveToElement(menuLink);

			// menuLink.click();
			waitSomeTime();

			// Goto Cover Page to start capturing
			// WebElement coverLink =
			// driver.findElement(By.cssSelector("div#kindleReader_goToMenuItem_goToCover"));
			// coverLink.click();

			// waitSomeTime();

			screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
			try {
				FileUtils.copyFile(screenshot, new File(bookFolder, "Cover.jpg"));
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			int pageNumber = 0;
			while (true) {
				pageNumber = pageNumber + 1;
				try {
					WebElement nextArrow = driver.findElement(By.cssSelector("div#kindleReader_pageTurnAreaRight.kindleReader_pageTurnArea.pageArrow"));
					if (nextArrow == null) {
						break;
					}

					nextArrow.click();

					waitSomeTime();

					screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
					try {
						FileUtils.copyFile(screenshot, new File(bookFolder, pageNumber + ".jpg"));
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}

				} catch (Exception e) {
					e.printStackTrace();
					break;
				}
			}

			try {
				createPdfFromImages(bookFolder.getAbsolutePath(), DEST);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (DocumentException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		}

		waitSomeTime(9000);

		screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
		try {
			FileUtils.copyFile(screenshot, new File(resourcesFolder, "SelectedBook.jpg"));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		driver.quit();
	}

	/**
	 * @param sourceFolder
	 * @param dest
	 * @throws IOException
	 * @throws DocumentException
	 */
	private static void createPdfFromImages(String sourceFolder, String dest) throws IOException, DocumentException {
		File folder = new File(sourceFolder);

		File[] allFiles = folder.listFiles(new FilenameFilter() {

			@Override
			public boolean accept(File dir, String name) {
				if (name.endsWith("jpg")) {
					return true;
				}
				return false;
			}
		});

		Image img = null;
		Document document = new Document();
		PdfWriter.getInstance(document, new FileOutputStream(dest));
		document.open();
		for (File fileObj : allFiles) {
			img = Image.getInstance(fileObj.getAbsolutePath());
			document.setPageSize(img);
			document.newPage();
			img.setAbsolutePosition(0, 0);
			document.add(img);
		}
		document.close();
	}

	/**
	 * 
	 */
	private static void waitSomeTime() {
		try {
			Thread.sleep(7000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * @param miliSecons
	 */
	private static void waitSomeTime(int miliSecons) {
		try {
			Thread.sleep(miliSecons);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}


Sample uncompleted code in casperjs. Also added code to handle multiple tries of Captcha


var casper = require('casper').create({
	verbose : true,
	logLevel : 'debug'
});
var system = require('system');
var mouse = require("mouse").create(casper);
var utils = require('utils');

casper.options.viewportSize = {
	width : 1366,
	height : 667
};

var last, list = [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ];

var userAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.95 Safari/537.36';
casper.userAgent(userAgent);


var fs = require('fs');
var myFolder      = fs.list(".");

//create array of just images 
try{
	for (var i = 0; i < myFolder.length; i++) {
		this.echo(myFolder[i]);
	    if (myFolder[i].indexOf("pdf") != -1) {
	       myFolder[i].remove();
	    }     
	}
} catch (e) {
	console.log(e);
}


/*******************************************************************************
 * Iterate over all options
 */
var cliOptions = casper.cli.options;

// casper.clear();


casper.on("remote.message", function(msg) {
	console.log(msg);
});

casper.start();

var userName = "AMAZON_USERNAME";
var password = "AMAZON_PASSWORD";

casper
		.thenOpen(
				'https://read.amazon.com',
				function() {

					this.echo("Login Page ");

				});

casper.each(list, function(self, i) {
	self.wait(700, function() {
		last = i;
		this.echo('Using this.wait ' + i);
	});
});

casper.then(function() {
	// casper.exit();
	try {
		casper.evaluate(function(userName, password) {
			document.getElementById("ap_email").value = userName;
			document.getElementById("ap_password").value = password;

			// document.aspnetForm.submit();
		}, userName, password);

	} catch (e) {
		console.log(e);
	}

	this.capture("LoginPage.pdf");
	// this.click('input#signInSubmit-input');
	this.mouse.click("input#signInSubmit-input");
});

casper.each(list, function(self, i) {
	self.wait(700, function() {
		last = i;
		this.echo('Using this.wait ' + i);
	});
});

casper.then(function() {

	// var html = this.getHTML();
	var html = this.getPageContent();
	var f = fs.open('HomePage.html', 'w');
	f.write(html);
	f.close();
	// fs.write('path/to/file', 'your string', 'w');
	this.capture("PostLoginPage.pdf");
	system.stdout.writeLine('Has CaptchaCode?: ');
	var hasCaptcha = system.stdin.readLine();
	
	if (hasCaptcha === 'y') {
		
		system.stdout.writeLine('Enter captcha?: ');
		var captcha = system.stdin.readLine();
		this.echo("Using Captcha " + captcha);
		
		try {
			casper.evaluate(function(userName, password,captcha) {
				document.getElementById("ap_email").value = userName;
				document.getElementById("ap_password").value = password;
				document.getElementById("ap_captcha_guess").value = captcha;
				// document.aspnetForm.submit();
			}, userName, password,captcha);

		} catch (e) {
			console.log(e);
		}
		
		this.capture("PostSecondTry.pdf");
		
		this.echo("Clicking Submit Button");
		this.mouse.click("input#signInSubmit-input");
		
		
	}else{
		this.capture("HomePage.pdf");
	}

	
});

casper.each(list, function(self, i) {
	self.wait(700, function() {
		last = i;
		this.echo('Using this.wait ' + i);
	});
});



casper.then(function() {

	// var html = this.getHTML();
	var html = this.getPageContent();
	var f = fs.open('HomePage2.html', 'w');
	f.write(html);
	f.close();
	// fs.write('path/to/file', 'your string', 'w');
	this.capture("PostLoginPage2.pdf");
	system.stdout.writeLine('Has CaptchaCode2?: ');
	var hasCaptcha = system.stdin.readLine();
	
	if (hasCaptcha === 'y') {
		
		system.stdout.writeLine('Enter captcha2?: ');
		var captcha = system.stdin.readLine();
		this.echo("Using Captcha2 " + captcha);
		
		try {
			casper.evaluate(function(userName, password,captcha) {
				document.getElementById("ap_email").value = userName;
				document.getElementById("ap_password").value = password;
				document.getElementById("ap_captcha_guess").value = captcha;
				// document.aspnetForm.submit();
			}, userName, password,captcha);

		} catch (e) {
			console.log(e);
		}
		
		this.capture("PostSecondTry2.pdf");
		
		this.echo("Clicking Submit Button");
		this.mouse.click("input#signInSubmit-input");
		
		
	}else{
		this.capture("HomePage2.pdf");
	}

	
});

casper.each(list, function(self, i) {
	self.wait(700, function() {
		last = i;
		this.echo('Using this.wait ' + i);
	});
});




casper.then(function() {

	// var html = this.getHTML();
	var html = this.getPageContent();
	var f = fs.open('HomePage3.html', 'w');
	f.write(html);
	f.close();
	// fs.write('path/to/file', 'your string', 'w');
	this.capture("PostLoginPage3.pdf");
	system.stdout.writeLine('Has CaptchaCode3?: ');
	var hasCaptcha = system.stdin.readLine();
	
	if (hasCaptcha === 'y') {
		
		system.stdout.writeLine('Enter captcha3?: ');
		var captcha = system.stdin.readLine();
		this.echo("Using Captcha3 " + captcha);
		
		try {
			casper.evaluate(function(userName, password,captcha) {
				document.getElementById("ap_email").value = userName;
				document.getElementById("ap_password").value = password;
				document.getElementById("ap_captcha_guess").value = captcha;
				// document.aspnetForm.submit();
			}, userName, password,captcha);

		} catch (e) {
			console.log(e);
		}
		
		this.capture("PostSecondTry3.pdf");
		
		this.echo("Clicking Submit Button");
		this.mouse.click("input#signInSubmit-input");
		
		
	}else{
		this.capture("HomePage3.pdf");
	}

	
});

casper.each(list, function(self, i) {
	self.wait(700, function() {
		last = i;
		this.echo('Using this.wait ' + i);
	});
});





casper.then(function() {
	var listItems = [];
	listItems = this.evaluate(function() {
		var nodes = document.querySelectorAll('span');
		return [].map.call(nodes, function(node) {
			return node.textContent;
		});
	});

	//this.echo(listItems);

	this.capture("HomePageFinal.pdf");
	// this.mouse.click("span#kindle_dialog_firstRun_button");

});

casper.run();