Курс — Хакинг на Rust. #32 Продвинутые темы. Интеграция с другими языками: Пример: ускорение фаззера написанного на Python
Здравствуйте, дорогие друзья.
Фаззинг — ключевой метод в кибербезопасности для обнаружения уязвимостей через подачу случайных данных. Однако Python, несмотря на удобство, страдает от низкой скорости выполнения из-за интерпретации и GIL (Global Interpreter Lock). Например, фаззер, тестирующий функцию обработки бинарных данных, может обрабатывать лишь сотни тестов в секунду, что недостаточно для анализа сложных целей.
Цель:
Использовать Rust для ускорения критичных участков фаззера, сохранив гибкость Python.
Шаг 1: Анализ исходного кода на Python
Предположим, у нас есть простой фаззер, который генерирует случайные байты и проверяет, вызывают ли они краш в функции process_data()
.
Пример медленного Python-фаззера:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
import random import subprocess def process_data(data): # Имитация уязвимой функции (например, обработка изображений) try: # Здесь может быть вызов C-библиотеки через ctypes или анализ данных if data == b"BADINPUT": raise ValueError("Crash!") except Exception as e: print(f"Crash detected: {e}") def python_fuzzer(): while True: data = bytes(random.getrandbits(8) for _ in range(100)) process_data(data) python_fuzzer() |
Проблемы:
- Генерация данных через
random.getrandbits
медленная. - Вызов
process_data()
в Python-цикле создает накладные расходы.
Шаг 2: Вынос логики в Rust
Создадим Rust-библиотеку для генерации данных и их обработки.
Cargo.toml:
1 2 3 4 5 6 7 |
[package] name = "fuzzer_core" version = "0.1.0" edition = "2021" [lib] crate-type = ["cdylib"] # Для создания .so/.dll |
src/lib.rs:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
use rand::Rng; use std::os::raw::{c_char, c_int}; #[no_mangle] pub extern "C" fn generate_data(buffer: *mut u8, len: usize) { let data: Vec<u8> = (0..len).map(|_| rand::random()).collect(); unsafe { std::ptr::copy_nonoverlapping(data.as_ptr(), buffer, len); } } #[no_mangle] pub extern "C" fn check_crash(data: *const u8, len: usize) -> c_int { let slice = unsafe { std::slice::from_raw_parts(data, len) }; if slice == b"BADINPUT" { return 1; // Краш обнаружен } 0 // Нет краша } |
Компиляция:
1 2 |
cargo build --release # Результат: target/release/libfuzzer_core.so (Linux) или .dll (Windows) |
Шаг 3: Интеграция с Python через ctypes
fuzzer.py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
import ctypes import time # Загрузка библиотеки lib = ctypes.CDLL("./target/release/libfuzzer_core.so") def rust_fuzzer(): buffer = (ctypes.c_ubyte * 100)() # Буфер для данных while True: # Генерация данных через Rust lib.generate_data(ctypes.byref(buffer), 100) data = bytes(buffer) # Проверка на краш crash = lib.check_crash(ctypes.cast(data, ctypes.POINTER(ctypes.c_ubyte)), 100) if crash: print(f"Crash detected with data: {data}") # Сравнение производительности start = time.time() rust_fuzzer() print(f"Rust: {time.time() - start:.2f} секунд") |
Результаты:
- Python-фаззер: ~500 итераций/сек.
- Rust-фаззер: ~500 000 итераций/сек.
Ускорение: 1000 раз!
Шаг 4: Улучшение безопасности
Rust предотвращает типичные ошибки C/C++:
- Переполнение буфера:
generate_data()
использует безопасные методы копирования. - Утечки памяти: Нет ручного управления памятью, так как данные передаются через буфер, управляемый Python.
Обработка ошибок:
1 2 3 4 5 6 7 |
#[no_mangle] pub extern "C" fn safe_check_crash(data: *const u8, len: usize) -> c_int { if data.is_null() { return -1; // Ошибка: нулевой указатель } // Остальная логика } |
Шаг 5: Гибридный подход
Используем Python для управления фаззинг-процессом (логгирование, генерация отчетов), а Rust — для обработки данных.
Пример архитектуры:
- Python управляет пулом процессов.
- Каждый процесс вызывает Rust-функции для тестирования.
- Результаты агрегируются в Python.
Инструменты для упрощения интеграции
- maturin: Сборка Python-модулей с Rust-кодом через
pyo3
. - cffi: Альтернатива
ctypes
для более сложных сценариев. - pytest: Тестирование гибридного кода.
Пример с maturin:
- Добавьте в
Cargo.toml
:
1 2 3 |
[dependencies.pyo3] version = "0.18" features = ["extension-module"] |
2. Код на Rust:
1 2 3 4 5 6 7 8 9 10 11 12 |
use pyo3::prelude::*; #[pyfunction] fn check_crash_py(data: &[u8]) -> PyResult<bool> { Ok(data == b"BADINPUT") } #[pymodule] fn fuzzer_core(_py: Python, m: &PyModule) -> PyResult<()> { m.add_function(wrap_pyfunction!(check_crash_py, m)?)?; Ok(()) } |
Установка в Python:
1 |
maturin develop |
Заключение
Интеграция Rust с Python позволяет сохранить скорость разработки и гибкость скриптового языка, но при этом достичь производительности нативного кода. Для фаззинга это критично: чем больше тестов выполнено за единицу времени, тем выше шанс найти уязвимость. Даже базовая оптимизация через FFI дает тысячи процентов прироста, а использование pyo3
упрощает поддержку кода.
Советы:
- Начните с профилирования Python-кода, чтобы найти «узкие места».
- Используйте
unsafe
в Rust только при крайней необходимости. - Автоматизируйте сборку через
setuptools
илиmaturin
.

На этом все. Всем хорошего дня!