Rust Basics
Tip
AWS ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:
HackTricks Training AWS Red Team Expert (ARTE)
GCP ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:HackTricks Training GCP Red Team Expert (GRTE)
Azure ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks ์ง์ํ๊ธฐ
- ๊ตฌ๋ ๊ณํ ํ์ธํ๊ธฐ!
- **๐ฌ ๋์ค์ฝ๋ ๊ทธ๋ฃน ๋๋ ํ ๋ ๊ทธ๋จ ๊ทธ๋ฃน์ ์ฐธ์ฌํ๊ฑฐ๋ ํธ์ํฐ ๐ฆ @hacktricks_live๋ฅผ ํ๋ก์ฐํ์ธ์.
- HackTricks ๋ฐ HackTricks Cloud ๊นํ๋ธ ๋ฆฌํฌ์งํ ๋ฆฌ์ PR์ ์ ์ถํ์ฌ ํดํน ํธ๋ฆญ์ ๊ณต์ ํ์ธ์.
Generic Types
๊ฐ ์ค ํ๋๊ฐ ์ด๋ค ํ์ ์ด ๋ ์ ์๋ struct๋ฅผ ๋ง๋ญ๋๋ค.
#![allow(unused)]
fn main() {
struct Wrapper<T> {
value: T,
}
impl<T> Wrapper<T> {
pub fn new(value: T) -> Self {
Wrapper { value }
}
}
Wrapper::new(42).value
Wrapper::new("Foo").value, "Foo"
}
Option, Some & None
Option ํ์ ์ ๊ฐ์ด Some ํ์ ์ผ ์๋ ์๊ณ (๋ฌด์ธ๊ฐ๊ฐ ์์) None์ผ ์๋ ์์์ ์๋ฏธํฉ๋๋ค:
#![allow(unused)]
fn main() {
pub enum Option<T> {
None,
Some(T),
}
}
is_some() ๋๋ is_none()์ ๊ฐ์ ํจ์๋ฅผ ์ฌ์ฉํ์ฌ Option์ ๊ฐ์ ํ์ธํ ์ ์์ต๋๋ค.
๋งคํฌ๋ก
๋งคํฌ๋ก๋ ์๋์ผ๋ก ์์ฑํ ์ฝ๋๋ณด๋ค ๋ ๋ง์ ์ฝ๋๋ฅผ ์์ฑํ๊ธฐ ๋๋ฌธ์ ํจ์๋ณด๋ค ๋ ๊ฐ๋ ฅํฉ๋๋ค. ์๋ฅผ ๋ค์ด, ํจ์ ์๊ทธ๋์ฒ๋ ํจ์๊ฐ ๊ฐ์ง ๋งค๊ฐ๋ณ์์ ์์ ์ ํ์ ์ ์ธํด์ผ ํฉ๋๋ค. ๋ฐ๋ฉด์ ๋งคํฌ๋ก๋ ๊ฐ๋ณ ๊ฐ์์ ๋งค๊ฐ๋ณ์๋ฅผ ๋ฐ์ ์ ์์ต๋๋ค: println!("hello")๋ฅผ ํ๋์ ์ธ์๋ก ํธ์ถํ๊ฑฐ๋ println!("hello {}", name)์ ๋ ๊ฐ์ ์ธ์๋ก ํธ์ถํ ์ ์์ต๋๋ค. ๋ํ, ๋งคํฌ๋ก๋ ์ปดํ์ผ๋ฌ๊ฐ ์ฝ๋์ ์๋ฏธ๋ฅผ ํด์ํ๊ธฐ ์ ์ ํ์ฅ๋๋ฏ๋ก, ๋งคํฌ๋ก๋ ์๋ฅผ ๋ค์ด ์ฃผ์ด์ง ์ ํ์ ๋ํด ํธ๋ ์ดํธ๋ฅผ ๊ตฌํํ ์ ์์ต๋๋ค. ํจ์๋ ๋ฐํ์์ ํธ์ถ๋๊ธฐ ๋๋ฌธ์ ํธ๋ ์ดํธ๋ฅผ ์ปดํ์ผ ํ์์ ๊ตฌํํ ์ ์์ต๋๋ค.
macro_rules! my_macro {
() => {
println!("Check out my macro!");
};
($val:expr) => {
println!("Look at this other macro: {}", $val);
}
}
fn main() {
my_macro!();
my_macro!(7777);
}
// Export a macro from a module
mod macros {
#[macro_export]
macro_rules! my_macro {
() => {
println!("Check out my macro!");
};
}
}
๋ฐ๋ณตํ๋ค
#![allow(unused)]
fn main() {
// Iterate through a vector
let my_fav_fruits = vec!["banana", "raspberry"];
let mut my_iterable_fav_fruits = my_fav_fruits.iter();
assert_eq!(my_iterable_fav_fruits.next(), Some(&"banana"));
assert_eq!(my_iterable_fav_fruits.next(), Some(&"raspberry"));
assert_eq!(my_iterable_fav_fruits.next(), None); // When it's over, it's none
// One line iteration with action
my_fav_fruits.iter().map(|x| capitalize_first(x)).collect()
// Hashmap iteration
for (key, hashvalue) in &*map {
for key in map.keys() {
for value in map.values() {
}
์ฌ๊ท ๋ฐ์ค
#![allow(unused)]
fn main() {
enum List {
Cons(i32, List),
Nil,
}
let list = Cons(1, Cons(2, Cons(3, Nil)));
}
์กฐ๊ฑด๋ฌธ
if
#![allow(unused)]
fn main() {
let n = 5;
if n < 0 {
print!("{} is negative", n);
} else if n > 0 {
print!("{} is positive", n);
} else {
print!("{} is zero", n);
}
}
์ผ์น
#![allow(unused)]
fn main() {
match number {
// Match a single value
1 => println!("One!"),
// Match several values
2 | 3 | 5 | 7 | 11 => println!("This is a prime"),
// TODO ^ Try adding 13 to the list of prime values
// Match an inclusive range
13..=19 => println!("A teen"),
// Handle the rest of cases
_ => println!("Ain't special"),
}
let boolean = true;
// Match is an expression too
let binary = match boolean {
// The arms of a match must cover all the possible values
false => 0,
true => 1,
// TODO ^ Try commenting out one of these arms
};
}
๋ฃจํ (๋ฌดํ)
#![allow(unused)]
fn main() {
loop {
count += 1;
if count == 3 {
println!("three");
continue;
}
println!("{}", count);
if count == 5 {
println!("OK, that's enough");
break;
}
}
}
while
#![allow(unused)]
fn main() {
let mut n = 1;
while n < 101 {
if n % 15 == 0 {
println!("fizzbuzz");
} else if n % 5 == 0 {
println!("buzz");
} else {
println!("{}", n);
}
n += 1;
}
}
for
#![allow(unused)]
fn main() {
for n in 1..101 {
if n % 15 == 0 {
println!("fizzbuzz");
} else {
println!("{}", n);
}
}
// Use "..=" to make inclusive both ends
for n in 1..=100 {
if n % 15 == 0 {
println!("fizzbuzz");
} else if n % 3 == 0 {
println!("fizz");
} else if n % 5 == 0 {
println!("buzz");
} else {
println!("{}", n);
}
}
// ITERATIONS
let names = vec!["Bob", "Frank", "Ferris"];
//iter - Doesn't consume the collection
for name in names.iter() {
match name {
&"Ferris" => println!("There is a rustacean among us!"),
_ => println!("Hello {}", name),
}
}
//into_iter - COnsumes the collection
for name in names.into_iter() {
match name {
"Ferris" => println!("There is a rustacean among us!"),
_ => println!("Hello {}", name),
}
}
//iter_mut - This mutably borrows each element of the collection
for name in names.iter_mut() {
*name = match name {
&mut "Ferris" => "There is a rustacean among us!",
_ => "Hello",
}
}
}
if let
#![allow(unused)]
fn main() {
let optional_word = Some(String::from("rustlings"));
if let word = optional_word {
println!("The word is: {}", word);
} else {
println!("The optional word doesn't contain anything");
}
}
while let
#![allow(unused)]
fn main() {
let mut optional = Some(0);
// This reads: "while `let` destructures `optional` into
// `Some(i)`, evaluate the block (`{}`). Else `break`.
while let Some(i) = optional {
if i > 9 {
println!("Greater than 9, quit!");
optional = None;
} else {
println!("`i` is `{:?}`. Try again.", i);
optional = Some(i + 1);
}
// ^ Less rightward drift and doesn't require
// explicitly handling the failing case.
}
}
ํน์ฑ
ํ์ ์ ์ํ ์๋ก์ด ๋ฉ์๋ ์์ฑ
#![allow(unused)]
fn main() {
trait AppendBar {
fn append_bar(self) -> Self;
}
impl AppendBar for String {
fn append_bar(self) -> Self{
format!("{}Bar", self)
}
}
let s = String::from("Foo");
let s = s.append_bar();
println!("s: {}", s);
}
ํ ์คํธ
#![allow(unused)]
fn main() {
#[cfg(test)]
mod tests {
#[test]
fn you_can_assert() {
assert!(true);
assert_eq!(true, true);
assert_ne!(true, false);
}
}
}
Threading
Arc
Arc๋ Clone์ ์ฌ์ฉํ์ฌ ๊ฐ์ฒด์ ๋ํ ๋ ๋ง์ ์ฐธ์กฐ๋ฅผ ์์ฑํ๊ณ ์ด๋ฅผ ์ค๋ ๋์ ์ ๋ฌํ ์ ์์ต๋๋ค. ๊ฐ์ ๋ํ ๋ง์ง๋ง ์ฐธ์กฐ ํฌ์ธํฐ๊ฐ ๋ฒ์๋ฅผ ๋ฒ์ด๋๋ฉด ๋ณ์๊ฐ ์ญ์ ๋ฉ๋๋ค.
#![allow(unused)]
fn main() {
use std::sync::Arc;
let apple = Arc::new("the same apple");
for _ in 0..10 {
let apple = Arc::clone(&apple);
thread::spawn(move || {
println!("{:?}", apple);
});
}
}
Threads
์ด ๊ฒฝ์ฐ ์ค๋ ๋์ ์์ ํ ์ ์๋ ๋ณ์๋ฅผ ์ ๋ฌํ ๊ฒ์ ๋๋ค.
fn main() {
let status = Arc::new(Mutex::new(JobStatus { jobs_completed: 0 }));
let status_shared = Arc::clone(&status);
thread::spawn(move || {
for _ in 0..10 {
thread::sleep(Duration::from_millis(250));
let mut status = status_shared.lock().unwrap();
status.jobs_completed += 1;
}
});
while status.lock().unwrap().jobs_completed < 10 {
println!("waiting... ");
thread::sleep(Duration::from_millis(500));
}
}
Security Essentials
Rust๋ ๊ธฐ๋ณธ์ ์ผ๋ก ๊ฐ๋ ฅํ ๋ฉ๋ชจ๋ฆฌ ์์ ์ฑ์ ๋ณด์ฅํ์ง๋ง, ์ฌ์ ํ unsafe ์ฝ๋, ์์กด์ฑ ๋ฌธ์ ๋๋ ๋
ผ๋ฆฌ์ ์ค์๋ฅผ ํตํด ์น๋ช
์ ์ธ ์ทจ์ฝ์ ์ ๋์
ํ ์ ์์ต๋๋ค. ๋ค์ ๋ฏธ๋ ์นํธ์ํธ๋ Rust ์ํํธ์จ์ด์ ๊ณต๊ฒฉ์ ๋๋ ๋ฐฉ์ด์ ๋ณด์ ๊ฒํ ์ค ๊ฐ์ฅ ์ผ๋ฐ์ ์ผ๋ก ์ ํ๊ฒ ๋ ์์ ์์๋ค์ ๋ชจ์๋์์ต๋๋ค.
Unsafe code & memory safety
unsafe ๋ธ๋ก์ ์ปดํ์ผ๋ฌ์ ๋ณ์นญ ๋ฐ ๊ฒฝ๊ณ ๊ฒ์ฌ๋ฅผ ์ ํ ํด์ ํ๋ฏ๋ก ๋ชจ๋ ์ ํต์ ์ธ ๋ฉ๋ชจ๋ฆฌ ์์ ๋ฒ๊ทธ(OOB, use-after-free, double free ๋ฑ)๊ฐ ๋ค์ ๋ํ๋ ์ ์์ต๋๋ค. ๋น ๋ฅธ ๊ฐ์ฌ ์ฒดํฌ๋ฆฌ์คํธ:
unsafe๋ธ๋ก,extern "C"ํจ์,ptr::copy*,std::mem::transmute,MaybeUninit, ์์ ํฌ์ธํฐ ๋๋ffi๋ชจ๋์ ์ฐพ์ผ์ธ์.- ์ ์์ค ํจ์์ ์ ๋ฌ๋๋ ๋ชจ๋ ํฌ์ธํฐ ์ฐ์ ๋ฐ ๊ธธ์ด ์ธ์๋ฅผ ๊ฒ์ฆํ์ธ์.
- ๋๊ตฐ๊ฐ
unsafe๋ฅผ ๋ค์ ๋์ ํ ๋ ์ปดํ์ผ์ด ์คํจํ๋๋ก# ๋๋#[deny(unsafe_op_in_unsafe_fn)](1.68 +)๋ฅผ ์ ํธํ์ธ์.
์์ ํฌ์ธํฐ๋ก ์์ฑ๋ ์ค๋ฒํ๋ก์ฐ ์:
#![allow(unused)]
fn main() {
use std::ptr;
fn vuln_copy(src: &[u8]) -> Vec<u8> {
let mut dst = Vec::with_capacity(4);
unsafe {
// โ copies *src.len()* bytes, the destination only reserves 4.
ptr::copy_nonoverlapping(src.as_ptr(), dst.as_mut_ptr(), src.len());
dst.set_len(src.len());
}
dst
}
}
Miri๋ฅผ ์คํํ๋ ๊ฒ์ ํ ์คํธ ์๊ฐ์ UB๋ฅผ ๊ฐ์งํ๋ ์ ๋ ดํ ๋ฐฉ๋ฒ์ ๋๋ค:
rustup component add miri
cargo miri test # hunts for OOB / UAF during unit tests
Auditing dependencies with RustSec / cargo-audit
๋๋ถ๋ถ์ ์ค์ Rust ์ทจ์ฝ์ ์ ์๋ํํฐ ํฌ๋ ์ดํธ์ ์กด์ฌํฉ๋๋ค. RustSec ์๋ฌธ DB(์ปค๋ฎค๋ํฐ ๊ธฐ๋ฐ)๋ ๋ก์ปฌ์์ ์ฟผ๋ฆฌํ ์ ์์ต๋๋ค:
cargo install cargo-audit
cargo audit # flags vulnerable versions listed in Cargo.lock
CI์ ํตํฉํ๊ณ --deny warnings์์ ์คํจํฉ๋๋ค.
cargo deny check advisories๋ ์ ์ฌํ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ฉฐ ๋ผ์ด์ผ์ค ๋ฐ ๊ธ์ง ๋ชฉ๋ก ๊ฒ์ฌ๋ฅผ ํฌํจํฉ๋๋ค.
cargo-vet์ ํตํ ๊ณต๊ธ๋ง ๊ฒ์ฆ (2024)
cargo vet๋ ๊ฐ์ ธ์ค๋ ๋ชจ๋ crate์ ๋ํ ๊ฒํ ํด์๋ฅผ ๊ธฐ๋กํ๊ณ ๋์น์ฑ์ง ๋ชปํ ์
๊ทธ๋ ์ด๋๋ฅผ ๋ฐฉ์งํฉ๋๋ค:
cargo install cargo-vet
cargo vet init # generates vet.toml
cargo vet --locked # verifies packages referenced in Cargo.lock
์ด ๋๊ตฌ๋ Rust ํ๋ก์ ํธ ์ธํ๋ผ์ ์ฆ๊ฐํ๋ ์์ ์กฐ์ง์์ ์ค์ผ๋ ํจํค์ง ๊ณต๊ฒฉ์ ์ํํ๊ธฐ ์ํด ์ฑํ๋๊ณ ์์ต๋๋ค.
API ํ๋ฉด์ ํผ์ง (cargo-fuzz)
ํผ์ง ํ ์คํธ๋ ํจ๋, ์ ์ ์ค๋ฒํ๋ก์ฐ ๋ฐ DoS ๋๋ ์ฌ์ด๋ ์ฑ๋ ๋ฌธ์ ๊ฐ ๋ ์ ์๋ ๋ ผ๋ฆฌ ๋ฒ๊ทธ๋ฅผ ์ฝ๊ฒ ํฌ์ฐฉํฉ๋๋ค:
cargo install cargo-fuzz
cargo fuzz init # creates fuzz_targets/
cargo fuzz run fuzz_target_1 # builds with libFuzzer & runs continuously
๋ฆฌํฌ์งํ ๋ฆฌ์ ํผ์ฆ ํ๊ฒ์ ์ถ๊ฐํ๊ณ ํ์ดํ๋ผ์ธ์์ ์คํํ์ธ์.
References
- RustSec Advisory Database โ https://rustsec.org
- Cargo-vet: โAuditing your Rust Dependenciesโ โ https://mozilla.github.io/cargo-vet/
Tip
AWS ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:
HackTricks Training AWS Red Team Expert (ARTE)
GCP ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:HackTricks Training GCP Red Team Expert (GRTE)
Azure ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks ์ง์ํ๊ธฐ
- ๊ตฌ๋ ๊ณํ ํ์ธํ๊ธฐ!
- **๐ฌ ๋์ค์ฝ๋ ๊ทธ๋ฃน ๋๋ ํ ๋ ๊ทธ๋จ ๊ทธ๋ฃน์ ์ฐธ์ฌํ๊ฑฐ๋ ํธ์ํฐ ๐ฆ @hacktricks_live๋ฅผ ํ๋ก์ฐํ์ธ์.
- HackTricks ๋ฐ HackTricks Cloud ๊นํ๋ธ ๋ฆฌํฌ์งํ ๋ฆฌ์ PR์ ์ ์ถํ์ฌ ํดํน ํธ๋ฆญ์ ๊ณต์ ํ์ธ์.


