Created
December 22, 2025 19:45
-
-
Save LaurieWired/df8b035344159c100153b04173460655 to your computer and use it in GitHub Desktop.
Heisenbug Demo
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #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