full-stack overflow

27 Sep 2017

ECB Cut and Paste

Cryptopals Set 2 Challenge 13 // code repo // demo

Step 1: Don’t Use ECB

Really. Just don’t. This challenge has us make a few functions to simulate a user object in a database, stored with an e-mail, user ID, and a role. We write a parser keyValueParse() to convert a string like [email protected]&uid=10&role=user into an object like {'email': '[email protected]', 'uid':10, 'role':'user'} and a function keyValueObjToString to convert the string back to an object. Simple mapping and dictionary stuff.

const isAmpEql = (t) => (t.match(/\=\&/) ? true : false);
const keyValueObjToString = (obj) =>
    .map((k) => `${k}=${obj[k]}`)

function keyValueParse(top) {
  let amps = top.split("&");
  let equals = amps.map((e) => e.split("="));
  if (
    !amps.length ||
    equals.some((e) => isAmpEql(e.join("")) || e.length !== 2)
  ) {
    throw new Error("Invalid. Keys and values cannot contain metacharacters.");
  let dict = {};
  equals.forEach((v) => (dict[v[0]] = v[1]));
  return dict;

Then we write a function to encrypt the profile, and one final function that generates a new profile given an e-mail. Using the output of this function profileFor, we are challenged to create a ciphertext in which the role=admin.

const profileFor = (e) =>
const decryptAndParse = (eProf) =>
  chopPKCS7(aesDecryptECB(eProf, randomKey, 16).map(hexToText)).join("");
function encryptProf(pText) {
  pText = PKCS7(
    txtToHex(pText).map((e) => hexPad(e)),
  return aesEncryptECB(pText, randomKey, 16);

Step 2: Why You Shouldn’t Use ECB

As we’ve seen before, identical plaintexts yield identical ciphertext outputs. All we’ve got to do in order to make our role=admin ciphertext is craft an input to profileFor that will strategically break the parts we’d like to recombine into separate 16 byte blocks. We want a block that has email=, a block that has [email protected], a block that has &uid=10&role=, and a block that has admin. There’s a lot of ways you can do this by padding with spacing, but here’s how I did it:

profileFor("          [email protected]     admin           AAAAAAAAAAAAA      ");

gives me the following (broken out into 16-byte blocks):

So all that we have to do is to combine A+B+E+C into a ciphertext and decrypt it.

E = encryptProf(PO);
let newCipher = E.slice(0, 32).concat(E.slice(64, 80), E.slice(32, 48)); // A+B, E, C


original padded profile:
email= [email protected] admin AAAAAAAAAAAAA &uid=10&role=user
manipulated ciphertext:
decrypted manipulation:
email= [email protected] &uid=10&role=admin

And we’re done. Don’t use ECB.