>/posts/Sword Of Secrets - 0x2 Parapet $
Estimated reading time: 10 minutes
The Second Task
Continuing the series, let's check what's inside the second task.
SOLVE
[TX] SOLVE
MAGICLIB{No one can break this! 0x20000}\r\n
Invalid Header\r\n
So now we can search for "Invalid Header". And there is exactly one hit! This message is shown when the condition involving the parapet function fails.

So let's dig into that. Here's the function code;
int parapet()
{
int err = -1;
struct AES_ctx ctx;
char message[128];
flash_read(PARAPET_FLASH_ADDR, message, sizeof(message));
AES_init_ctx(&ctx, aes_key);
for (unsigned i = 0; i < sizeof(message) / AES_BLOCKLEN; ++i)
{
AES_ECB_decrypt(&ctx, (uint8_t *)message + i * AES_BLOCKLEN);
}
if (memcmp(message, FLAG_BANNER, strlen(FLAG_BANNER)))
{
goto error;
}
message[AES_BLOCKLEN * 2] = '\0';
printf(message);
printf("\r\n");
err = 0;
error:
return err;
}
Ok, our goal is to reach 'err = 0;' and return it. That means the condition 'if (memcmp(message, FLAGBANNER, strlen(FLAGBANNER)))' must evaluate to false i.e., 'memcmp() has to return 0. If you don't know how 'memcmp()' works: it returns 0 when both compared byte sequences are identical. So we need the beginning of message to match FLAG_BANNER, which we already know is MAGICLIB. But here's the problem: the bytes 'message' are read from AES-encrypted mempry part, and we don't know the key so we can't simply edit the plaintext... or can we?
It's encrypted what do we do?
Well, unfortunately (in this case), AES is a secure cipher and we won't be able to recover the key or the plaintext knowing only the ciphertext. In a normal scenario, we would need to decrypt the message, modify its contents, and then encrypt it again so it remains in a valid form.
This is also function preparing this one challenge:
static void parapetSetup()
{
struct AES_ctx ctx;
char message[128] = "Important message to transmit - " FLAG_BANNER "{53Cr37 5745H: " S(POSTERN_FLASH_ADDR) "}";
size_t len;
printf("Running %s...\r\n", __FUNCTION__);
// Pad with PKCS#7 to prepare for encryption
len = PKCS7Pad((uint8_t *)message, strlen(message));
// Initialize AES context
AES_init_ctx(&ctx, aes_key);
// Encrypt
for (unsigned i = 0; i < len / AES_BLOCKLEN; ++i)
{
AES_ECB_encrypt(&ctx, (uint8_t *)message + i * AES_BLOCKLEN);
}
// Write buffer to flash
flash_erase_block(PARAPET_FLASH_ADDR);
flash_write(PARAPET_FLASH_ADDR, message, len);
}
So at least we know the part of plaintext, we still need to decrypt it in order to see POSTERN_FLASH_ADDR.
And here's the trick: AES in ECB mode does not chain blocks, which means a previous block does not affect the next one. In other words, each block is encrypted and decrypted independently. That means we can swap block positions and they will still decrypt correctly.
And that means we can completely skip the decryption/encryption process as long as our FLAG_BANNER is aligned at the beginning of a block. Let's verify that.

Since we've confirmed it, we can start our ingenious plan!
Swap the chiphertext
Here are my Ruby code that does the job;
data = "00 00 00 00 4b 40 6f e6 a3 d4 32 1b 26 28 b7 c6 ff f5 fc 9f 6e 61 71 38 48 3e f9 86 9c b8 4c 9c c0 d2 72 a3 de 90 e7 d0 ae 83 38 b0 7a ac 38 94 75 74 69 00 41 5d 39 41 de d0 e4 e3 ad c5 45 98 42 dc a5 8d"
flash_bytes = data.split.map { |x| x.to_i(16) }
blocks = flash_bytes.last(64).each_slice(16).to_a
blocks = [blocks[2], blocks[3], blocks[0], blocks[1]]
result = blocks.flatten.map { |b| "%02x" % b }.join(" ")
puts "!write 20000 " + result
Last check and confirm if that was correct;

As you can see we get correct flag, address of next challenge and a short message from 3rd task.
X
Comments (0)
Please log in to add a comment.