Black Hat Rust Applied Offensive Security With The Rust Programming Language Sylvain Kerkour Instant Download
Black Hat Rust Applied Offensive Security With The Rust Programming Language Sylvain Kerkour Instant Download
https://ebookmeta.com/product/black-hat-rust-applied-offensive-
security-with-the-rust-programming-language-sylvain-kerkour/
https://ebookmeta.com/product/black-hat-rust-applied-offensive-
security-with-the-rust-programming-language-sylvain-kerkour-3/
https://ebookmeta.com/product/black-hat-rust-applied-offensive-
security-with-the-rust-programming-language-sylvain-kerkour/
https://ebookmeta.com/product/black-hat-rust-deep-dive-into-
offensive-security-with-the-rust-programming-language-sylvain-
kerkour/
https://ebookmeta.com/product/agriscience-fundamentals-and-
applications-sixth-edition-l-devere-burton/
Letting Data Lead How to Design Analyze and Respond to
Classroom Assessment Gain Actionable Insights Through
Effective Assessment Methods and Data Interpretation
1st Edition Eileen Depka
https://ebookmeta.com/product/letting-data-lead-how-to-design-
analyze-and-respond-to-classroom-assessment-gain-actionable-
insights-through-effective-assessment-methods-and-data-
interpretation-1st-edition-eileen-depka/
The Bear the Bat and the Dove Three Stories from Aesop
Rob Cleveland
https://ebookmeta.com/product/the-bear-the-bat-and-the-dove-
three-stories-from-aesop-rob-cleveland/
https://ebookmeta.com/product/software-defined-radio-theory-and-
practice-artech-house-mobile-communications-library-1st-edition-
reyland/
https://ebookmeta.com/product/no-truth-without-beauty-god-the-
qur-an-and-women-s-rights-sustainable-development-goals-series-
el-ali/
https://ebookmeta.com/product/perfect-phrases-for-esl-
conversation-skills-diane-engelhardt/
Redemption Dr Rebecca Sharp
https://ebookmeta.com/product/redemption-dr-rebecca-sharp/
Black Hat Rust
Applied offensive security with the Rust
programming language
Sylvain Kerkour
Black Hat Rust
Applied offensive security with the Rust programming language
Sylvain Kerkour
v2021.46
Contents
Copyright 7
Contact 9
Preface 10
1 Introduction 13
1.1 Types of attacks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
1.2 Phases of an attack . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
1.3 Profiles of attackers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
1.4 Attribution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
1.5 The Rust programming language . . . . . . . . . . . . . . . . . . . . . 19
1.6 History of Rust . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
1.7 Rust is awesome . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
1.8 Setup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
1.9 Our first Rust program: A SHA-1 hash cracker . . . . . . . . . . . . . 25
1.10 Mental models for approaching Rust . . . . . . . . . . . . . . . . . . . 31
1.11 A few things I’ve learned along the way . . . . . . . . . . . . . . . . . . 33
1.12 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
1
2.15 Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
2.16 Going further . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
2.17 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
2
5.18 Crawling a JSON API . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
5.19 Crawling a JavaScript web application . . . . . . . . . . . . . . . . . . 133
5.20 How to defend . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
5.21 Going further . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
5.22 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
3
8 Writing shellcodes in Rust 182
8.1 What is a shellcode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
8.2 Sections of an executable . . . . . . . . . . . . . . . . . . . . . . . . . . 183
8.3 Rust compilation process . . . . . . . . . . . . . . . . . . . . . . . . . . 184
8.4 no_std . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185
8.5 Using assembly from Rust . . . . . . . . . . . . . . . . . . . . . . . . . 187
8.6 The never type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188
8.7 Executing shellcodes . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188
8.8 Our linker script . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
8.9 Hello world shellcode . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
8.10 An actual shellcode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
8.11 Reverse TCP shellcode . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
8.12 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
4
11.3 Cryptography . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 277
11.4 Hash functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 278
11.5 Message Authentication Codes . . . . . . . . . . . . . . . . . . . . . . . 278
11.6 Key derivation functions . . . . . . . . . . . . . . . . . . . . . . . . . . 280
11.7 Block ciphers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 280
11.8 Authenticated encryption (AEAD) . . . . . . . . . . . . . . . . . . . . 281
11.9 Asymmetric encryption . . . . . . . . . . . . . . . . . . . . . . . . . . . 283
11.10Diffie–Hellman key exchange . . . . . . . . . . . . . . . . . . . . . . . . 284
11.11Signatures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 284
11.12End-to-end encryption . . . . . . . . . . . . . . . . . . . . . . . . . . . 285
11.13Who uses cryptography . . . . . . . . . . . . . . . . . . . . . . . . . . 294
11.14Common problems and pitfalls with cryptography . . . . . . . . . . . . 295
11.15A little bit of TOFU? . . . . . . . . . . . . . . . . . . . . . . . . . . . 296
11.16The Rust cryptography ecosystem . . . . . . . . . . . . . . . . . . . . . 296
11.17Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 298
11.18Our threat model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 298
11.19Designing our protocol . . . . . . . . . . . . . . . . . . . . . . . . . . . 299
11.20Implementing end-to-end encryption in Rust . . . . . . . . . . . . . . . 303
11.21Some limitations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 313
11.22To learn more . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314
11.23Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 315
5
13.10Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 347
14 Conclusion 348
14.1 What we didn’t cover . . . . . . . . . . . . . . . . . . . . . . . . . . . . 348
14.2 The future of Rust . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 350
14.3 Leaked repositories . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 350
14.4 How bad guys get caught . . . . . . . . . . . . . . . . . . . . . . . . . 350
14.5 Your turn . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 351
14.6 Build your own RAT . . . . . . . . . . . . . . . . . . . . . . . . . . . . 354
14.7 Other interesting blogs . . . . . . . . . . . . . . . . . . . . . . . . . . . 355
14.8 Contact . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 355
6
Copyright
All rights reserved. No portion of this book may be reproduced in any form without
permission from the publisher, except as permitted by law. For permissions contact:
[email protected]
7
Your early access bonuses
Dear reader, in order to thank you for buying the Black Hat Rust early access edition
and helping to make this book a reality, I prepared you a special bonus: I curated a
list of the best detailed analyses of the most advanced malware of the past two decades.
You may find inside great inspiration when developing your own offensive tools. You
can find the list at this address: https://github.com/black-hat-rust-bonuses/black-
hat-rust-bonuses
If you notice a mistake (it happens), something that could be improved, or want to
share your ideas about offensive security, feel free to join the discussion on Github:
https://github.com/skerkour/black-hat-rust
8
Contact
Every week I share updates about my projects and everything I learn about how to
(ab)use technology for fun & profit: Programming, Hacking & Entrepreneurship. You
can subscribe by Email or RSS: https://kerkour.com/follow.
You bought the book and are annoyed by something? Please tell me, and I will do my
best to improve it!
Or, you greatly enjoyed the read and want to say thank you?
9
Preface
After high school, my plan for life was to become a private detective, maybe because
I read too many Sherlock Holmes books. In France, the easiest way to become one is
(was?) to go to law university and then to attend a specialized school.
I quickly realized that studying law was not for me: reality is travestied to fit whatever
narrative politics or professor wanted us to believe. No deep knowledge is taught here,
only numbers, dates, how to look nice and sound smart. It was deeply frustrating for
the young man I was, with an insatiable curiosity. I wanted to understand how the
world works, not human conventions. For example, how do these machines we call
computers that we are frantically typing on all day long work under the hood?
So I started by installing Linux (no, I won’t enter the GNU/Linux war) on my Asus
EeePC, a small netbook with only 1GB of RAM, because Windows was too slow, and
started to learn to develop C++ programs with Qt, thanks to online tutorials. I coded
my own text and my own chat systems. But my curiosity was not fulfilled.
One day, I inadvertently fell on the book that changed my life: “Hacking: The Art of
Exploitation, 2nd Edition”, by Jon Erickson.
This book not only made me curious about how to make things, but, more importantly,
how to break things. It made me realize that you can’t build reliable things without
understanding how to break them, and by extension, where their weaknesses are.
While the book remains great to learn low-level programming and how to exploit simple
memory safety bugs, today, hacking requires new skills: web exploitation, network and
system programming, and, above all, how to code in a modern programming language.
While the Rust Book does an excellent job teaching What is Rust, I felt that a book
about Why and How to Rust was missing. That means that some concepts will not
10
be covered in-depth in this book. Instead, we are going to see how to effectively use
them in practice.
In this book, we will shake the preconceived ideas (Rust is too complex for the real world,
Rust is not productive…) and see how to architect and create real-world Rust projects
applied to offensive security. We will see how polyvalent Rust is, which enables its users
to replace the plethora of programming languages (Python, Ruby, C, C++…) plaguing
the offensive security world with a unique language that offers high-level abstractions,
high performance, and low-level control when needed.
We will always start with some theory, deep knowledge that pass through ages, tech-
nologies and trends. This knowledge is independent of any programming language and
will help you to get the right mindset required for offensive security.
I designed this book for people who either want to understand how attackers think in
order to better defend themselves or for people who want to enter the world of offensive
security and eventually make a living off it.
The goal of this book is to save you time in your path to action, by distilling knowledge
and presenting it in applied code projects.
It’s important to understand that Black Hat Rust is not meant to be a big encyclopedia
containing all the knowledge of the world. Instead, it was designed as a guide to help
you getting started and pave the way to action. Knowledge is often a prerequisite,
but it’s action that is shaping the world, and sometimes knowledge is a blocker for
action (see analysis paralysis). As we will see, some of the most primitive offensive
techniques are still the most effective. Thus some very specific topics, such as how to
bypass modern OSes protection mechanisms won’t be covered because there already is
extensive literature on these topics, and they have little value in a book about Rust.
That being said, I did my best to list the best resources to further your learning journey.
It took me approximately 1 year to become efficient in Rust, but it’s only when I started
to write (and rewrite) a lot of code that I made real progress.
Rust is an extremely vast language, but in reality, you will (and should) use only a
subset of its features: you don’t need to learn them all ahead of time. Some, that we
will study in this book, are fundamentals. Others are not and may have an adversarial
effect on the quality of your code by making it harder to read and maintain.
My intention with this book is not only to make you discover the fabulous world of
offensive security, to convince you that Rust is the long-awaited one-size-fits-all pro-
gramming language meeting all the needs of offensive security, but also to save you
11
a lot of time by guiding you to what really matters when learning Rust and offensive
security. But remember, knowledge is not enough. Knowledge doesn’t move mountains.
Actions do.
Thus, the book is only one half of the story. The other half is the accompanying code
repository: https://github.com/skerkour/black-hat-rust. It’s impossible to learn
without practice, so I invite you to read the code, modify it and make it
yours!
If at any time you feel lost or don’t understand a chunk of Rust code, don’t hesitate
to refer to the Rust Language Cheat Sheet, The Rust Book, and the Rust Language
Reference.
Also, the book is code-heavy. I recommend reading it with a web browser aside, in order
to explore and play with the code on GitHub: https://github.com/skerkour/black-hat-
rust/.
12
Chapter 1
Introduction
In reality, the spectrum of the profile of the attackers is extremely large, from the bored
teenager exploring the internet to sovereign State’s armies as well as the unhappy former
employee. As we will see, cyberattacks are not that hard. Knowledge is simply unevenly
distributed and jealously kept secret by the existing actors. The principal ingredients
are a good dose of curiosity and the courage to follow your instinct.
As digital is taking an always more important place in our lives, the impact and scale
of cyberattacks will increase in the same way: we are helplessly witnessing during
the current COVID-19 pandemic attacks against our hospitals which have real-life and
dramatic consequences.
It’s time to fight back and to prepare ourselves for the wars and battles of today (not
tomorrow) and to understand that, in order to defend, there is no other way than
to put ourselves in the shoes of attackers and think how they think. What are their
motivations? How can they break seemingly so easily into any system? What do they
do to their victims? From theory to practice, we will explore the arcanes of offensive
security and build our own offensive tools with the Rust programming language.
Why Rust?
The world of security (and, more generally, software) is plagued by too many program-
ming languages with too many footguns. You have to choose between fast and unsafe
(C, C++…) or slow but mostly safe (Python, Java…).
13
Can someone be an expert in all these languages? I don’t think so. And the countless
bugs and vulnerabilities in offensive tools prove I’m right.
A language that, once mastered, would fill all the needs of the field:
• Shellcodes
• Cross-platform Remote Access Tools (RATs)
• Reusable and embeddable exploits
• Scanners
• Phishing toolkits
• Embedded programming
• Web servers
• …
What if we had a single language that is low-level enough while providing high-level
abstractions, is exceptionally fast, and easy to cross-compile. All of that while being
memory safe, highly reusable, and extremely reliable.
No more weird toolchains, strange binary packagers, vulnerable network code, injectable
phishing forms…
Due to momentum, Rust isn’t widely adopted by the security industry yet, but once the
tech leads and independent hackers understand this reality, I believe that the change
will happen really fast.
Of course, there are some pitfalls and a few things to know, but everything is covered
in the following chapters.
14
1.1.2 Political attacks
Sometimes, attacks have the only goal of spreading a political message. Most of the
time, they materialize as website defacements where websites’ content is replaced with
the political message, or denial-of-service attacks where a piece of infrastructure or a
service is made unavailable.
1.1.3 Pentest
Pentest, which stands for Penetration Testing, may be the most common term used to
designate security audits. One downside of pentests is that sometimes they are just a
means to check boxes for compliance purposes, are performed using simple automated
scanners, and may leave big holes open.
As we will see in the last chapter, bug bounty programs have their limits and are
sometimes used by companies as virtue signaling instead of real security measures.
1.1.6 Cybercrime
Cybercrime is definitely the most growing type of attack since the 2010s. From selling
personal data on underground forums to botnets and ransomwares or credit card hack-
ing, criminal networks have found many creative ways of acting. An important peak
occurred in 2017, when the NSA tools and exploits were leaked by the mysterious group
“Shadow Brokers”, which were then used in other malware such as WanaCry and Petya.
Despite the strengthening of online services to reduce the impact of data-stealing (today,
it is far more difficult to take advantage of a stolen card number compared to a few years
ago), criminals always find new creative ways to monetize their wrongdoings, especially
with cryptocurrencies.
15
1.1.7 Industrial spying
Industrial espionage has always been a tempting means for companies to break down
competitors’ secrets and achieve competitive advantage. As our economy is more and
more dematerialized (digitalized), this kind of attack will only increase in terms of
frequency.
1.1.8 Cyberwar
This last kind of attack is certainly the less mediatized but without doubt the most
spectacular. To learn more about this exciting topic, I can’t recommend enough the
excellent book “Countdown to Zero Day: Stuxnet and the Launch of the World’s First
Digital Weapon” by Kim Zetter which tells the story of, to my knowledge, the first act
of advanced cyberwar: the Stuxnet worm.
1.2.1 Reconnaissance
The first phase consists of gathering as much information as possible about the target.
Whether it be the names of the employees, the numbers of internet-facing machines
and the services running on them, the list of the public Git repositories…
Reconnaissance is either passive (using publicly available data sources, such as social
networks or search engines), or active (scanning the target’s networks directly, for ex-
ample).
1.2.2 Exploitation
Exploitation is the initial breach. It can be performed by using exploits (zero-day
or not), abusing humans (social engineering) or both (sending office documents with
malware inside).
16
1.2.3 Lateral Movements
Also known as pivoting, lateral movement designates the process of maintaining access
and gaining access to more resources and systems. Implants, Remote Access Tools
(RATs), and various other tools are used during this phase. The biggest challenge is to
stay hidden as long as possible.
It should be made with care as large chunks of data passing through the network may
not go unnoticed.
1.2.5 Clean up
Once the attack is successfully completed, advised attackers need to cover their tracks
in order to reduce the risk of being identified: logs, temporary files, infrastructure,
phishing websites…
17
Exploit development is also known as “weaponization”.
Entire companies are operating in the grey waters of exploits trading, such as Vupen or
Zerodium. They often don’t find the exploits themselves but buy them from third-party
hackers and find buyers (such as government agencies or malware developers).
These are the skills we will learn and practice in the next chapters.
1.4 Attribution
Attribution is the process of identifying and laying blame on the operators behind a
cyber attack.
Attacks attribution is usually based on the following technical and operational elements:
Dates and time of the attackers’ activities, which may reveal their time zone - even
though it can easily be manipulated by moving the team to another country.
18
By counterattacking or hacking attackers’ tools and infrastructure, or even by sending
them false data which may lead them to make mistakes and consequently reveal their
identities.
Finally, by browsing forums: it’s not unusual that hackers praise their achievements on
dedicated forums in order to both inflate their reputation and ego.
If you are hanging out online on forums like HackerNews or Reddit, you can’t have
missed this “new” programming language called Rust. It pops almost every time we
are discussing something barely related to programming. The so-called Rust Evangelism
Strikeforce is promising access to paradise to the brave programmers who will join their
ranks.
Since then, the language has been following an organic growth and is today, according
to Stack Overflow’s surveys, the most loved language by software developers for 5 years
19
in a row.
Figure 1.2: Google trends results for the Rust programming language
Lately, big organizations such as Amazon or Microsoft have publicly announced their
love for the language and are creating internal talent pools.
With that being said, Rust is still a niche language today and is not widely used outside
of these big companies.
1.7.2 Fast
One of the most loved characteristics of Rust is its speed. Developers spend their day be-
hind a screen and hate slow programs interrupting their workflows. It is thus completely
natural that programmers tend to reject slow programming language contaminating the
whole computing stack and creating painful user experiences.
Micro-benchmarks are of no interest to us because they are more often than not falla-
cious. However, there are a lot of reports demonstrating that Rust is blazing fast when
20
Figure 1.3: Google trends: Rust VS Go
My favorite one is Discord describing how replacing a service in Go by one in Rust not
only eliminated latency spikes due to Go’s garbage collector but also reduced average
response time from milliseconds to microseconds.
Another one is TechEmpower’s Web Framework benchmarks, certainly the most exhaus-
tive web framework benchmarks available on the internet where Rust shines since 2018.
Some may argue that this one is a micro-benchmark, as the code is over-optimized
for some specific, pre-determined use cases, yet, the result correlates with what I can
observe in the real world.
1.7.3 Multi-paradigm
Being greatly inspired by the ML family of programming languages, Rust can be de-
scribed as easy to learn as imperative programming languages, and as expressive as
functional programming languages, whose abstractions allow them to transpose the
human thoughts to code better.
Rust is rather “low-level” but offers high-level abstractions to programmers and thus is
a joy to use.
The most loved feature by programmers coming from other programming languages
seems to be enums, also known as Algebraic Data Types. They offer unparalleled ex-
pressiveness and correctness: when we “check” an enum, with the match keyword, the
21
compiler makes sure that we don’t forget a case, unlike switch statements in other program-
ming languages.
ch_01/snippets/enums/src/lib.rs
$ cargo build
Compiling enums v0.1.0
error[E0004]: non-exhaustive patterns: `Failed` not covered
--> src/lib.rs:8:11
|
1 | / pub enum Status {
2 | | Queued,
3 | | Running,
4 | | Failed,
| | ------ not covered
5 | | }
| |_- `Status` defined here
...
8 | match status {
| ^^^^^^ pattern `Failed` not covered
|
= help: ensure that all possible cases are being handled, possibly by adding
↪ wildcards or more match arms
= note: the matched value is of type `Status`
For more information about this error, try `rustc --explain E0004`.
error: could not compile `enums`
22
1.7.4 Modular
Rust’s creators clearly listened to developers when designing the ecosystem of tools accompany-
ing it. It especially shows regarding dependencies management. Rust’s package management
(known as “crates”) is as easy as with dynamic languages, such as Node.js’ NPM, a real breath
of fresh air when you had to fight with C or C++ toolchains, static and dynamic libraries.
1.7.5 Explicit
Rust’s is certainly one of the most explicit languages. On the one hand, it allows programs
to be easier to reason about and code reviews to be more effective as fewer things are hidden.
On the other hand, it is often pointed out by people on forums, telling that they never saw
such an ugly language because of its verbosity.
I would speculate that this is due to the fact that today, not so many companies are using
Rust. Thus, the community is mostly composed of passionate programmers for whom sharing
about the language is more a passion than a chore.
You can learn more about the companies using Rust in production in my blog post: 42
Companies using Rust in production (in 2021).
I personally use Reddit to share my projects or ideas with the community, and the forum to
seek help about code.
1.8 Setup
Before starting to code, we need to set up our development environment. We will need
(without surprise) Rust, a code editor, and Docker.
23
1.8.1 Install Rust(up)
rustup is the official way to manage Rust toolchains on your computer. It will be needed
to update Rust and install other components like the automatic code formatter: rustfmt.
You will need to install the rust-analyzer extension in order to have code completion and
type hints which are absolutely needed when developing in Rust. You can find it here: https:
//marketplace.visualstudio.com/items?itemName=matklad.rust-analyzer.
The instructions to install Docker can be found on the official website: https://docs.docker.
com/get-docker
If you’ve been the podman’s way, you will just have to replace the docker command by
podman .
# in .bashrc or .zshrc
alias docker=podman
24
1.9 Our first Rust program: A SHA-1 hash cracker
The moment has come to get our hands dirty: let’s write our first Rust program. As for all
the code examples in this book, you can find the complete code in the accompanying Git
repository: https://github.com/skerkour/black-hat-rust
Note that by default, cargo will create a binary (application) project. You can create a
library project with the --lib flag: cargo new my_lib --lib .
SHA-1 is a hash function used by a lot of old websites to store the passwords of the users. In
theory, a hashed password can’t be recovered from its hash. Thus by storing the hash in their
database, a website can assert that a given user has the knowledge of its password without
storing the password in cleartext, by comparing the hashes. So if the website’s database is
breached, there is no way to recover the passwords and access the users’ data.
Reality is quite different. Let’s imagine a scenario where we just breached such a website, and
we now want to recover the credentials of the users in order to gain access to their accounts.
This is where a “hash cracker” is useful. A hash cracker is a program that will try many
different hashes in order to find the original password.
This is why when creating a website, you should use a hash function specifically designed for
this use case, such as argon2id , which require way more resource to bruteforce than SHA-1,
for example.
25
Like in almost all programming languages, the entrypoint of a Rust program is its main
function.
ch_01/sha1_cracker/src/main.rs
fn main() {
// ...
}
ch_01/sha1_cracker/src/main.rs
use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
}
Where std::env imports the module env from the standard library and env::args()
calls the args function from this module and returns an iterator which can be “collected”
into a Vec<String> , a Vector of String objects. A Vector is an array type that
can be resized.
It is then easy to check for the number of arguments and display an error message if it does
not match what is expected.
ch_01/sha1_cracker/src/main.rs
use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() != 3 {
println!("Usage:");
println!("sha1_cracker: <wordlist.txt> <sha1_hash>");
return;
}
}
As you may have noticed, the syntax of println! with an exclamation mark is strange.
Indeed, println! is not a classic function but a macro. As it’s a complex topic, I redirect
you to the dedicated chapter of the Book: https://doc.rust-lang.org/book/ch19-06-macros.h
tml.
println! is a macro and not a function because Rust doesn’t support (yet?) variadic
26
generics. It has the advantage of being compile-time evaluated and checked and thus prevent
vulnerabilities such as format string vulnerabilities.
Among the dozen programming languages that I have experience with, Rust is without any
doubts my favorite one regarding error handling due to its explicitness, safety, and conciseness.
For our simple program, we will Box errors: we will allow our program to return any type
that implements the std::error::Error trait. What is a trait? More on that later.
ch_01/sha1_cracker/src/main.rs
use std::{
env,
error::Error,
};
if args.len() != 3 {
println!("Usage:");
println!("sha1_cracker: <wordlist.txt> <sha1_hash>");
return Ok(());
}
Ok(())
}
27
found in breached websites.
Reading a file in Rust can be achieved with the standard library like that:
ch_01/sha1_cracker/src/main.rs
use std::{
env,
error::Error,
fs::File,
io::{BufRead, BufReader},
};
if args.len() != 3 {
println!("Usage:");
println!("sha1_cracker: <wordlist.txt> <sha1_hash>");
return Ok(());
}
Ok(())
}
1.9.3 Crates
Now that the basic structure of our program is in place, we need to actually compute the SHA-
1 hashes. Fortunately for us, some talented developers have already developed this complex
piece of code and shared it online, ready to use in the form of an external library. In Rust,
28
we call those libraries, or packages, crates. They can be browsed online at https://crates.io.
They are managed with cargo : Rust’s package manager. Before using a crate in our
program, we need to declare its version in Cargo’s manifest file: Cargo.toml .
ch_01/sha1_cracker/Cargo.toml
[package]
name = "sha1_cracker"
version = "0.1.0"
authors = ["Sylvain Kerkour"]
edition = "2018"
[dependencies]
sha-1 = "0.9"
hex = "0.4"
ch_01/sha1_cracker/src/main.rs
use sha1::Digest;
use std::{
env,
error::Error,
fs::File,
io::{BufRead, BufReader},
};
if args.len() != 3 {
println!("Usage:");
println!("sha1_cracker: <wordlist.txt> <sha1_hash>");
return Ok(());
}
29
let wordlist_file = File::open(&args[1])?;
let reader = BufReader::new(&wordlist_file);
Ok(())
}
Please note that in a real-world scenario, we may want to use optimized hash crackers such
as hashcat or John the Ripper, which, among other things, may use the GPU to significantly
speed up the cracking.
Another point would be to first load the wordlist in memory before performing the computa-
tions.
1.9.4 RAII
A detail may have caught the attention of the most meticulous of you: we opened the wordlist
file, but we never closed it!
This pattern (or feature) is called RAII: Resource Acquisition Is Initialization. In Rust,
variables not only represent parts of the memory of the computer, they may also own resources.
Whenever an object goes out of scope, its destructor is called, and the owned resources are
freed.
Thus, you don’t need to call a close method on files or sockets. When the variable is
dropped (goes out of scope), the file or socket will be automagically closed.
In our case, the wordlist_file variable owns the file and has the main function as
scope. Whenever the main function exits, either due to an error or an early return, the owned
file is closed.
30
Magic, isn’t it? Thanks to this, it’s very rare to leak resources in Rust.
1.9.5 Ok(())
You might also have noticed that the last line of our main function does not contain the
return keyword. This is because Rust is an expression-oriented language. Expressions
evaluate to a value. Their opposites, statements, are instructions that do something and end
with a semicolon ( ; ).
So if our program reaches the last line of the main function, the main function will
evaluate to Ok(()) , which means: “success: everything went according to the plan”.
return Ok(());
but not:
Ok(());
Because here Ok(()); is a statement due to the semicolon, and the main function no longer
evaluates to its expected return type: Result .
The compiler should be seen as an always available and friendly code-reviewer. So it’s not
something preventing your code from compiling. Instead, it’s a friend that tells you that your
code is defective and even offers suggestions on how to fix it.
I have witnessed a great improvement over the years of the messages displayed by the compiler,
and I have no doubts that if today the compiler produces an obscure message for an edge
case, it will be improved in the future.
31
I’ve spent a lot of time reading about all the computer science behind Rust before even writing
my first program. This was the wrong approach. There is too much to read about all
the features of Rust, and you certainly won’t use them all (and you shouldn’t! For example,
please never ever use non_ascii_idents it will only bring chaos and pain!). All this stuff is
really interesting and produced by very smart people, but it prevents you from getting things
done.
Instead, embrace the unknown and make your first programs. Fail. Learn. Repeat.
Also, keep in mind that the more you are playing with the limits of the type system, the
more your code will create hard-to-understand errors by the compiler. So, make you and your
co-workers a favor: KISS (Keep It Simple, Stupid).
Favor getting things done rather than the perfect design that will never ship. It’s
far better to re-work an imperfect solution than to never ship a perfect system.
The first programs I shipped in production were in TypeScript (Node.js) and Go. Due to the
lax compilers and type systems of these languages, you have to add complex instrumentation
to your code and external services to detect errors at runtime. In Rust, I’ve never had to
use this. Simple logging (as we will see in chapter 4) is all I ever needed to track bugs in
my programs. Aside from that, as far as I remember, I’ve never experienced a crash in a
production system in Rust. This is because Rust forces you to “pay the costs upfront”: you
have to handle every error and be very intentional about what you are doing.
Here is another testimony from “jhgg”, Senior Staff Engineer at Discord: “We are going hard
on Rust in 2021 after some very successful projects in 2019 and 2020. our engineers have
ramped up on the language - and we have good support internally (both in terms of tools, but
also knowledge) to see its success. Once you’ve passed the learning curve - imo, Rust is far
32
easier and more productive to write than go - especially if you know how to leverage the type
system to build idiomatic code and apis that are very hard to use incorrectly. Every piece of
rust code we have shipped to production so far has gone perfectly thanks to the really powerful
compile time checks and guarantees of the language. I can’t say the same for our experiences
with go. Our reasons go well beyond”oh the gc in go has given us problems” but more like
“go as a language has willingly ignored and has rejected advances in programming languages”.
You can pry those algebraic data types, enums, borrow checker, and compile time memory
management/safety, etc… from my cold dead hands. [..]“
1.10.5 Functional
Rust is (in my opinion) the perfect mix between an imperative and a functional language
to get things done. It means that if you are coming from a purely imperative programming
language, you will have to unlearn some things and embrace the functional paradigm.
Favor iterators (chapter 3) over for loops. Favor immutable data over mutable references,
and don’t worry, the compiler will do a great job optimizing your code.
Here are a few tips learned the hard way that I’m sharing to make your Rust journey as
pleasant as possible.
33
Learning Rust can sometimes be extremely frustrating: there are a lot of new concepts to
learn, and the compiler is mercy-less. But this is for your own good.
It took me nearly 1 year of full-time programming in Rust to become proficient and no longer
have to read the documentation every 5 lines of code. It’s a looong journey but totally worth
it.
// Haha is a struct to wrap a monad generator to provide a facade for any kind of
↪ generic iterator. Because.
struct Haha<'y, 'o, L, O>
where for<'oO> L: FnOnce(&'oO O) -> &'o O,
O: Trait<L, 'o, L>,
O::Item : Clone + Debug + 'static {
x: L,
}
Yeaah suure, please don’t mind that somebody, someday, will have to read and understand
your code.
But lifetimes annotations are avoidable and, in my opinion should be avoided. So here is
my strategy to avoid turning Rust code into some kind of monstrosity that nobody will ever
want to touch and slowly die of disregard.
Lifetime annotations are needed to tell the compiler that we are manipulating some kind of
long-lived reference and let him assert that we are not going to screw ourselves.
The simplest and most basic trick is to omit the lifetime annotation.
fn do_something(x: &u64) {
println!("{}", x);
}
It’s most of the time easy to elide input lifetimes, but beware that to omit output lifetime
annotations, you have to follow these 3 rules:
34
• If there is exactly one input lifetime, elided or not, that lifetime is assigned to all elided
lifetimes in the return values of that function.
• If there are multiple input lifetimes, but one of them is &self or &mut self, the lifetime
of self is assigned to all elided output lifetimes.
// is equivalent to
fn do_something_else<'a>(x: &'a u64)-> &'a u64 {
println!("{}", x);
x
}
Now, not everything is as simple as an HelloWorld and you may need some kind of long-
lived reference that you can use at multiple places of your codebase (a Database connection,
for example, or an HTTP client with an internal connection pool).
The solution for long-lived, shared (or not), mutable (or not) references is to use smart point-
ers.
The only downside is that smart pointers, in Rust, are a little bit verbose (but still way less
ugly than lifetime annotations).
use std::rc::Rc;
fn main() {
let pointer = Rc::new(1);
{
let second_pointer = pointer.clone(); // or Rc::clone(&pointer)
println!("{}", *second_pointer);
}
println!("{}", *pointer);
}
35
1.11.1.3.1 Rc To obtain a mutable, shared pointer, you can use use the interior muta-
bility pattern:
fn main() {
let shared_string = Rc::new(RefCell::new("Hello".to_string()));
{
let mut hello_world: RefMut<String> = shared_string.borrow_mut();
hello_world.push_str(" World");
}
println!("{}", shared_string.take());
}
fn main() {
let pointer = Arc::new(5);
thread::sleep(time::Duration::from_secs(1));
println!("{}", *pointer); // 5
}
fn main() {
let pointer = Arc::new(Mutex::new(5));
36
let second_pointer = pointer.clone(); // or Arc::clone(&pointer)
thread::spawn(move || {
let mut mutable_pointer = second_pointer.lock().unwrap();
*mutable_pointer = 1;
});
thread::sleep(time::Duration::from_secs(1));
struct MyService {
db: Arc<DB>,
mailer: Arc<dyn drivers::Mailer>,
storage: Arc<dyn drivers::Storage>,
other_service: Arc<other::Service>,
}
In my opinion, lifetimes annotations should never surface in any public API. It’s okay to
use them if you need absolute performance AND minimal resources usage AND are doing
embedded development, but you should keep them hidden in your code, and they should
never surface in the public API.
Not only this pace causes me anxiety, but it is also the opposite of one of the pillars of my
life: minimalism, where it is common knowledge that unbounded growth (of the language in
this case) is the root cause of the demise of everything. When something is added, something
37
must be subtracted elsewhere. But who is in charge of removing Rust’s features? Is it even
possible?
As a result, I’m afraid that the complexity of the language will grow faster than its rate of
adoption and that it will be an endless, exhausting race to stay updated on the new features
as developers.
There are many tricks to improve the compilation speed of your projects.
The first one is to split a large project into smaller crates and benefit from Rust’s incremental
compilation.
Another one is to use cargo check instead of cargo build most of the time.
$ cargo check
$ cargo check
Finished dev [unoptimized + debuginfo] target(s) in 0.12s
cargo build
Compiling agent v0.1.0 (black-hat-rust/ch_11/agent)
Finished dev [unoptimized + debuginfo] target(s) in 2.24s
Compounded over a day (or week or month) of development, the gains are huge.
Finally, simply reduce the use of generics. Generics add a lot of work to the compiler and
thus significantly increase compile times.
The good news is that, in my experience, due to its strong typing, Rust project maintenance
is easier than in other languages: errors such as API changes will be caught at compile time.
38
For that, the community has built a few tools which will save you a lot of time to let you keep
your projects up to date.
1.11.5.1 Rustup
rustfmt is a code formatter that allows codebases to have a consistent coding style and
avoid nitpicking during code reviews.
$ cargo fmt
In your projects.
1.11.5.3 Clippy
clippy is a linter for Rust. It will detect code patterns that may lead to errors or are
identified by the community as bad style.
It helps your codebase to be consistent and reduce time spent during code reviews discussing
tiny details.
$ cargo clippy
$ cargo update
Is a command that will automatically update your dependencies according to the semver
declaration in your Cargo.toml .
39
1.11.5.5 Cargo outdated
cargo-outdated is a program that helps you to identify your outdated dependencies that
can’t be automatically updated with cargo update
$ cargo outdated
In your projects.
Sometimes, you may not be able to always keep your dependencies to the last version and
need to use an old version (due to dependency by another of your dependency…) of a crate.
As a professional, you still want to be sure that none of your outdated dependencies contains
any known vulnerability.
$ cargo audit
Fetching advisory database from `https://github.com/RustSec/advisory-db.git`
Loaded 317 security advisories (from /usr/local/cargo/advisory-db)
Updating crates.io index
Scanning Cargo.lock for vulnerabilities (144 crate dependencies)
There are powerful tools such as Maltego (more about it in chapter 5), but it can become
costly if you want all the features.
On my side, I prefer to use simple files on disk, with markdown to write notes and reports and
Git for the backup. It has the advantage of being extremely simple to use, multi-platform,
easily exported, and free. Also, it easy to generate PDFs, .docx or other document formats
from the markdown files using pandoc.
40
I’ve also heard good things about Obsidian.md and Notion.so but personally don’t use: I
prefer to own my data ��
1.12 Summary
• The Rust language is huge. Don’t learn everything ahead of time. Code. Fail. Learn.
Repeat.
• Expressions evaluate to a value. Their opposites, statements, are instructions that do
something and end with a semicolon ( ; ).
• Try not to use lifetime annotations and macros.
• Embrace the compiler. It should be seen as an always present and friendly code-
reviewer.
• RAII: Resource Acquisition Is Initialization.
• The hacker, The exploit writer, The developer, The system administrator, The analyst
• Reconnaissance, Exploitation, Lateral Movements, Data exfiltration, Clean up
41
Chapter 2
“To know your Enemy, you must become your Enemy”, Sun Tzu
As we have seen, the first step of every attack is reconnaissance. The goal of this phase is to
gather as much information as possible about our target in order to find entry points for the
coming assault.
In this chapter, we will see the basics of reconnaissance, how to implement our own scanner
in Rust and how to speed it up by leveraging multithreading.
Using publicly available sources is called OSINT, for Open Source INTelligence.
42
What kind of data is harvested using passive reconnaissance? Usually, pieces of information
about employees of a company such as names, email addresses, phone numbers, but also
source code repositories, leaked tokens. Thanks to search engines like Shodan, we can also
look for open-to-the-world services and machines.
As passive reconnaissance is the topic of chapter 5, we will focus our attention on active
reconnaissance in this chapter.
Active reconnaissance is noisier and can be detected by firewalls and honeypots, so you have
to be careful to stay undetected, for example, by spreading the scan over a large span of time.
A honeypot is an external endpoint that shall never be used by “regular” people of a given
company, so the only people hitting this endpoint are attackers. It can be a mail server, an
HTTP server, or even a document with remote content embedded.
Once a honeypot is scanned or hit, it will report back to the security team which put it in
place.
A canary is like a honeypot but in an internal network. Its purpose is to detect attackers once
they have breached the external perimeter.
• Assets discovery
• Vulnerabilities identification (which is the topic of chapter 6)
Today the scope is broader and encompasses social network accounts, public source code
repositories, Internet of Things objects… Nowadays, everything is on or connected to the
internet. From an offensive point of view, it’s really interesting.
The goal of listing and mapping all the assets of a target is to find entry points and vulnera-
bilities for our coming attack.
43
Exploring the Variety of Random
Documents with Different Content
affairs of Muscotah. He is a director and stockholder in the State
Bank at Muscotah, Kan., and has an interest in the Farmers’ Grain
and Elevator Supply Company and the Mutual Telephone Company.
In 1903 he married Jane Ernst, who was born November 6, 1871,
in Kapioma township, Atchison county. She is a daughter of John
and Eliza (Lewis) Ernst. The father is a native of Germany and the
mother of Norway, and both came to Atchison county, Kansas, in the
early days. Mr. and Mrs. Miller have two children: Alice E. and Mary
E., twins, who are living at home. Mr. Miller is a Democrat and has
been treasurer of Kapioma township. He is a member of the Ancient
Free and Accepted Masons.
CHARLES CARLTON HART.
For a Kansas citizen and pioneer settler to spend the better part
of a lifetime in building up a fine and highly productive farm, and
then to enter the banking business at a time when most men are
ready to retire and live a life of ease, is rather out of the ordinary, but
such has been the experience of C. C. Hart, banker, of Muscotah,
Kan. Mr. Hart has lived in Kansas for forty-seven years and has been
successively farmer and banker during that time. He is a descendant
of one of the old families in America and comes of a family of
ministers and teachers.
C. C. Hart