Skip to content

Instantly share code, notes, and snippets.

@LaurieWired
Created December 22, 2025 19:45
Show Gist options
  • Select an option

  • Save LaurieWired/df8b035344159c100153b04173460655 to your computer and use it in GitHub Desktop.

Select an option

Save LaurieWired/df8b035344159c100153b04173460655 to your computer and use it in GitHub Desktop.
Heisenbug Demo
#include <iostream>
#include <vector>
#include <string>
#include <iomanip>
// example sbox, typical in ciphers
const uint8_t S_BOX[16] = {
0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5,
0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76
};
// this ensures a bad "seed" for demonstration purposes
void poison() {
volatile uint8_t dirty_memory[64];
for (int i = 0; i < 64; ++i) {
dirty_memory[i] = 0xAA;
}
}
std::string decrypt(const std::vector<uint8_t>& ciphertext) {
std::string plaintext = "";
uint32_t rolling_state = 0xDEADBEEF; // seed state
// iv_idx is uninitialized
// in a correct run, this should be 0
// if it inherits '0xAA' from poison, we start at the WRONG S-Box index.
int iv_idx;
for (uint8_t byte : ciphertext) {
// ensure iv_idx doesn't segfault by masking it (& 0x0F),
// but logic is now dependent on the garbage value.
uint8_t key_byte = S_BOX[(iv_idx & 0x0F)];
// rolling hash function
rolling_state = (rolling_state << 5) ^ (rolling_state >> 3) ^ key_byte;
// decrypt
char out = byte ^ key_byte ^ (rolling_state & 0xFF);
plaintext += out;
// std::cout << "0x"
// << std::setw(2) << std::setfill('0') << std::hex
// << (int)(uint8_t)out << " ";
iv_idx++;
}
return plaintext;
}
int main() {
// assume this data was encrypted with iv_idx = 0
std::vector<uint8_t> encrypted_data = { 0x75, 0xce, 0x72, 0xa5, 0xdd };
// dirty the stack
poison();
// the ghost fix (*sometimes* will fix it)
// std::cout uses stack buffer for formatting integers/strings.
// often zeroes out its working buffer "cleaning"
// the spot where 'iv_idx' will land next.
// -----------------------------------------------------------
// std::cout << "got here" << std::endl;
// -----------------------------------------------------------
// keep in mind...the real issue is the uninitialized iv_idx
// the std::cout just hides it
std::string result = decrypt(encrypted_data);
std::cout << "decrypted: " << result << std::endl;
// check if it matches "HELLO"
// "HELLO" is the target if iv_idx starts at 0
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment