digininja/DVWA · error · Exception

IV must be 16 bytes, passed

Error message

IV must be 16 bytes, {strlen($iv)} passed

What it means

decrypt() in the high-level token library enforces that the IV is exactly 16 bytes, the block size required by aes-128-cbc; encrypt() (line 12) carries the same guard. The token JSON's iv field is base64-decoded before the call, and any decoded length other than 16 raises this Exception with the actual length interpolated into the message.

Solutions

  1. Set the iv field to base64 of exactly 16 bytes - the original token's value base64("1234567812345678") = "MTIzNDU2NzgxMjM0NTY3OA==" works.
  2. When forging tokens for this level, leave the issued iv value untouched.
  3. Never copy nonce values produced for aes-256-gcm (12 bytes) into this CBC library.
  4. Pre-validate strlen($iv) === 16 before calling decrypt().

Example fix

// before
$iv = base64_decode($data_array['iv']);
$d = decrypt ($ciphertext, $iv);
// after
$iv = base64_decode($data_array['iv'], true);
if ($iv === false || strlen($iv) !== 16) {
    return json_encode(["status" => 524, "message" => "IV must be 16 bytes"]);
}
$d = decrypt ($ciphertext, $iv);
Defensive patterns

Strategy: validation

Validate before calling

$iv = base64_decode($data_array['iv'], true);
if ($iv === false || strlen($iv) !== 16) {
    // reject before decrypt() can throw
    return json_encode(['status' => 524, 'message' => 'IV must be 16 bytes']);
}

Type guard

function isValidCbcIv(string $iv): bool
{
    return strlen($iv) === 16;
}

Try / catch

try {
    $d = decrypt($ciphertext, $iv);
} catch (Exception $e) {
    if (str_starts_with($e->getMessage(), 'IV must be 16 bytes')) {
        $ret = ['status' => 524, 'message' => 'Missing or malformed IV'];
    } else {
        $ret = ['status' => 526, 'message' => 'Unable to decrypt token'];
    }
}

Prevention

When it happens

Trigger: Supplying a token JSON whose iv base64-decodes to something other than 16 bytes (e.g. "AAAA" = 4 bytes); reusing a 12-byte GCM nonce from token_library_impossible.php; hand-crafting iv strings without proper padding; base64_decode of URL-safe or whitespace-damaged input yielding a wrong-length string (strict mode is not used).

Common situations: Mixing the CBC (16-byte IV) and GCM (12-byte IV) libraries between security levels; migrating code between cipher families without updating IV length; cross-language token generation where another runtime's defaults differ; copy-paste corruption of base64.

Related errors


AI-assisted analysis of digininja/DVWA@5d5c76cced (2026-08-21). Data as JSON: /api/errors/af50043ff3575945. Report an issue: GitHub.

Appendix: source

Thrown at vulnerabilities/cryptography/source/token_library_high.php:24

function encrypt ($plaintext, $iv) {
	# Default padding is PKCS#7 which is interchangeable with PKCS#5
	# https://en.wikipedia.org/wiki/Padding_%28cryptography%29#PKCS#5_and_PKCS#7

	if (strlen ($iv) != 16) {
		throw new Exception ("IV must be 16 bytes, " . strlen ($iv) . " passed");
	}
	$tag = "";
	$e = openssl_encrypt($plaintext, ALGO, KEY, OPENSSL_RAW_DATA, $iv, $tag);
	if ($e === false) {
		throw new Exception ("Encryption failed");
	}
	return $e;
}

function decrypt ($ciphertext, $iv) {
	if (strlen ($iv) != 16) {
		throw new Exception ("IV must be 16 bytes, " . strlen ($iv) . " passed");
	}
	$e = openssl_decrypt($ciphertext, ALGO, KEY, OPENSSL_RAW_DATA, $iv);
	if ($e === false) {
		throw new Exception ("Decryption failed");
	}
	return $e;
}

// Added the debug flag so that when calling from the script
// the function can print the data used to create the token

function create_token ($debug = false) {
	$token = "userid:2";

	if ($debug) {
		print "Clear text token: " . $token . "\n";
		print "Encryption key: " . KEY . "\n";
		print "IV: " . (IV) . "\n";

View on GitHub (pinned to 5d5c76cced)