Files
mini-unix-brute/brute/source/bruteforce.c

482 lines
17 KiB
C

/*
============================
Giải thích chi tiết về cách hoạt động của bruteforce.c
Tác giả: @tuankiet2s
Mục đích:
---------
Đây là chương trình brute-force mật khẩu người dùng trên hệ thống Unix/Linux, hỗ trợ nhiều loại hash (yescrypt, MD5, SHA256, SHA512). Chương trình được thiết kế tối giản, không phụ thuộc thư viện ngoài, tối ưu cho kích thước nhỏ nhất.
Quy trình hoạt động:
--------------------
1. Đọc tham số dòng lệnh:
- argv[1]: tên user cần brute-force
- argv[2]: đường dẫn file wordlist (danh sách mật khẩu thử)
2. Đọc file /etc/shadow:
- Mở file /etc/shadow bằng syscall để lấy dòng chứa thông tin hash của user mục tiêu.
- Nếu không tìm thấy user, thông báo lỗi và thoát.
3. Phân tích chuỗi hash:
- Hàm parse_hash() tách loại hash (hash_id), salt, và giá trị hash thực tế từ chuỗi hash lấy được.
- Hỗ trợ các định dạng: yescrypt ($y$), MD5 ($1$), SHA256 ($5$), SHA512 ($6$).
- Tạo salt phù hợp để truyền vào hàm crypt() hoặc yescrypt.
4. Đọc file wordlist và thử từng mật khẩu:
- Đọc từng dòng (mỗi dòng là một mật khẩu) từ file wordlist.
- Với mỗi mật khẩu:
+ Nếu hash_id là 'y' (yescrypt), sử dụng hàm yescrypt_crypt() để kiểm tra.
+ Nếu là hash khác, sử dụng hàm crypt() của hệ thống với salt đã tạo.
- So sánh kết quả hash với hash của user. Nếu trùng khớp, in ra mật khẩu và kết thúc.
- Cập nhật tiến trình mỗi 1000 lần thử.
5. Xử lý dòng cuối cùng nếu file không kết thúc bằng newline.
6. In thống kê cuối cùng:
- Số lượng mật khẩu đã thử
- Thông báo thành công hoặc thất bại
Các hàm phụ trợ:
----------------
- my_strlen, my_strncmp: các hàm xử lý chuỗi tối giản
- write_stderr, write_stdout: ghi ra stderr/stdout bằng syscall
- strpos: tìm vị trí chuỗi con trong chuỗi
- read_line: đọc từng dòng từ file descriptor
- extract_user_hash: lấy hash của user từ dòng shadow
- parse_hash: tách thông tin từ chuỗi hash
- create_salt: tạo salt phù hợp cho crypt()
- my_itoa: chuyển số nguyên thành chuỗi
- yescrypt_crypt: kiểm tra mật khẩu với yescrypt
Lưu ý bảo mật:
--------------
- Chỉ sử dụng cho mục đích kiểm thử hợp pháp, giáo dục.
- Không sử dụng trên hệ thống không được phép.
- Cần quyền root để truy cập /etc/shadow.
Tối ưu hóa:
-----------
- Biên dịch tĩnh, strip symbol, nén UPX để giảm kích thước binary tối đa.
- Đọc file và xử lý chuỗi bằng syscall, không dùng thư viện chuẩn để giảm dependency.
*/
#include <unistd.h>
#include <sys/syscall.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
// Include yescrypt implementation
#include "yescrypt.h"
#define BUF_SIZE 4096
#define MAX_LINE 1024
#define MAX_PASS 256
// Minimal strlen
static int my_strlen(const char *s) { int n=0; while(s[n]) n++; return n; }
// Minimal strncmp
static int my_strncmp(const char *a, const char *b, int n) { for(int i=0;i<n;i++) if(a[i]!=b[i]||!a[i]||!b[i]) return a[i]-b[i]; return 0; }
// Write string to stderr
static void write_stderr(const char *s) {
syscall(SYS_write, 2, s, my_strlen(s));
}
// Write string to stdout
static void write_stdout(const char *s) {
syscall(SYS_write, 1, s, my_strlen(s));
}
// Tìm vị trí chuỗi con trong chuỗi chính
// Thuật toán: duyệt từng vị trí trong chuỗi chính, so sánh với chuỗi con
static int strpos(const char *string, const char *substring, int offset) {
int string_len = my_strlen(string);
int sub_len = my_strlen(substring);
// Duyệt từ vị trí offset đến vị trí có thể chứa chuỗi con
for(int i = offset; i <= string_len - sub_len; i++) {
int j;
// So sánh từng ký tự của chuỗi con với chuỗi chính tại vị trí i
for(j = 0; j < sub_len; j++) {
if(string[i + j] != substring[j]) break;
}
// Nếu so sánh hết chuỗi con mà không khác biệt, đã tìm thấy
if(j == sub_len) return i;
}
return -1; // Không tìm thấy
}
// Read a line from fd into buf, return length or 0 on EOF
static int read_line(int fd, char *buf, int max) {
int i=0, r;
char c;
while(i<max-1) {
r = syscall(SYS_read, fd, &c, 1);
if(r<=0) break;
if(c=='\n') break;
buf[i++] = c;
}
buf[i]=0;
return i;
}
// Trích xuất hash của user từ dòng trong file shadow
// Format: username:hash:other_fields
static char* extract_user_hash(const char *shadow_line, const char *username) {
// Tìm vị trí username trong dòng shadow
int username_index = strpos(shadow_line, username, 0);
if(username_index == -1) return NULL;
// Tìm vị trí bắt đầu hash (sau username:)
int hash_start = username_index + my_strlen(username) + 1;
// Tìm vị trí kết thúc hash (dấu : tiếp theo)
int hash_end = strpos(shadow_line, ":", hash_start);
if(hash_end == -1) return NULL;
// Tính độ dài hash và cấp phát bộ nhớ
int hash_len = hash_end - hash_start;
char *user_hash = malloc(hash_len + 1);
// Copy hash từ dòng shadow vào buffer mới
memcpy(user_hash, shadow_line + hash_start, hash_len);
user_hash[hash_len] = 0; // Thêm null terminator
return user_hash;
}
// Phân tích chuỗi hash để trích xuất ID thuật toán, salt, và giá trị hash
// Hỗ trợ format: $id$salt$hash hoặc $y$params$salt$hash (yescrypt)
static void parse_hash(const char *user_hash, int *hash_id, char *salt, char *hash_value) {
// Tìm vị trí các ký tự $ để phân tích cấu trúc hash
int first_dollar = strpos(user_hash, "$", 0);
if(first_dollar == -1) return;
int second_dollar = strpos(user_hash, "$", first_dollar + 1);
if(second_dollar == -1) return;
int third_dollar = strpos(user_hash, "$", second_dollar + 1);
if(third_dollar == -1) return;
// Trích xuất ID thuật toán (giữa $ đầu tiên và $ thứ hai)
int id_len = second_dollar - first_dollar - 1;
char id_str[8] = {0};
memcpy(id_str, user_hash + first_dollar + 1, id_len);
// Xử lý yescrypt (y) và các ID số
if(id_len == 1 && id_str[0] == 'y') {
*hash_id = 'y'; // Đánh dấu là yescrypt
// Với yescrypt, cần tìm $ thứ 4 để lấy salt thực tế
int fourth_dollar = strpos(user_hash, "$", third_dollar + 1);
if(fourth_dollar == -1) return;
// Trích xuất tham số (giữa $ thứ 2 và $ thứ 3)
int param_len = third_dollar - second_dollar - 1;
char parameters[64] = {0};
memcpy(parameters, user_hash + second_dollar + 1, param_len);
parameters[param_len] = 0;
// Trích xuất salt (giữa $ thứ 3 và $ thứ 4)
int salt_len = fourth_dollar - third_dollar - 1;
memcpy(salt, user_hash + third_dollar + 1, salt_len);
salt[salt_len] = 0;
// Trích xuất giá trị hash (sau $ thứ 4)
int hash_len = my_strlen(user_hash) - fourth_dollar - 1;
memcpy(hash_value, user_hash + fourth_dollar + 1, hash_len);
hash_value[hash_len] = 0;
// Với yescrypt, cần kết hợp tham số vào salt cho hàm crypt()
char combined_salt[128];
int len = 0;
combined_salt[len++] = '$';
combined_salt[len++] = 'y';
combined_salt[len++] = '$';
memcpy(combined_salt + len, parameters, param_len);
len += param_len;
combined_salt[len++] = '$';
memcpy(combined_salt + len, salt, salt_len);
len += salt_len;
combined_salt[len] = 0;
// Copy salt đã kết hợp vào tham số salt
memcpy(salt, combined_salt, len + 1);
} else {
// Xử lý các thuật toán hash khác (MD5, SHA256, SHA512)
*hash_id = atoi(id_str);
// Trích xuất salt (format chuẩn)
int salt_len = third_dollar - second_dollar - 1;
memcpy(salt, user_hash + second_dollar + 1, salt_len);
salt[salt_len] = 0;
// Trích xuất giá trị hash
int hash_len = my_strlen(user_hash) - third_dollar - 1;
memcpy(hash_value, user_hash + third_dollar + 1, hash_len);
hash_value[hash_len] = 0;
}
}
// Create formatted salt string for crypt()
static char* create_salt(int hash_id, const char *salt) {
char *formatted_salt = malloc(128);
int len = 0;
if(hash_id == 'y') {
// For yescrypt, the salt is already properly formatted
int salt_len = my_strlen(salt);
memcpy(formatted_salt, salt, salt_len);
formatted_salt[salt_len] = 0;
} else {
// Standard format for other algorithms
formatted_salt[len++] = '$';
if(hash_id == 1) {
formatted_salt[len++] = '1';
} else if(hash_id == 5) {
formatted_salt[len++] = '5';
} else if(hash_id == 6) {
formatted_salt[len++] = '6';
} else {
// Default to SHA512
formatted_salt[len++] = '6';
}
formatted_salt[len++] = '$';
int salt_len = my_strlen(salt);
memcpy(formatted_salt + len, salt, salt_len);
len += salt_len;
formatted_salt[len] = 0;
}
return formatted_salt;
}
// Chuyển đổi số nguyên dương thành chuỗi (itoa tối giản)
// Thuật toán: chia liên tiếp cho 10, lưu các chữ số vào mảng tạm, sau đó đảo ngược
static void my_itoa(int value, char *buf) {
char tmp[16];
int i = 0, j = 0;
if (value == 0) { buf[0] = '0'; buf[1] = 0; return; } // Xử lý trường hợp đặc biệt
// Chia liên tiếp cho 10, lưu từng chữ số vào mảng tạm (theo thứ tự ngược)
while (value > 0) { tmp[i++] = '0' + (value % 10); value /= 10; }
// Đảo ngược thứ tự các chữ số để có kết quả đúng
while (i > 0) { buf[j++] = tmp[--i]; }
buf[j] = 0; // Thêm null terminator
}
// Hàm crypt tùy chỉnh cho yescrypt sử dụng implementation tham chiếu
// Mục đích: tạo hash từ mật khẩu và so sánh với hash đã biết
static char* yescrypt_crypt(const char *password, const char *full_hash) {
static char result[128];
// Sử dụng hàm yescrypt từ implementation tham chiếu để tạo hash
uint8_t *generated_hash = yescrypt((const uint8_t *)password, (const uint8_t *)full_hash);
if (!generated_hash) {
return NULL; // Lỗi khi tạo hash
}
// So sánh hash được tạo với hash mong đợi
if (strcmp((char *)generated_hash, full_hash) == 0) {
// Nếu trùng khớp, copy hash vào buffer kết quả
int len = strlen((char *)generated_hash);
if (len < sizeof(result)) {
memcpy(result, generated_hash, len + 1);
return result; // Trả về hash nếu mật khẩu đúng
}
}
return NULL; // Mật khẩu không đúng
}
int main(int argc, char **argv) {
// Kiểm tra tham số dòng lệnh
if(argc<3) {
write_stderr("Usage: ");
write_stderr(argv[0]);
write_stderr(" <username> <wordlist>\n");
return 1;
}
char *username = argv[1];
char *wordlist_path = argv[2];
const char *shadow_path = "/etc/shadow";
// Mở file /etc/shadow để đọc thông tin hash của user
int sfd = syscall(SYS_open, shadow_path, O_RDONLY, 0);
if(sfd<0) {
write_stderr("Failed to open shadow file: ");
write_stderr(shadow_path);
write_stderr("\n");
return 2;
}
// Đọc từng dòng trong file shadow, tìm dòng chứa thông tin user mục tiêu
char line[MAX_LINE];
char *user_hash = NULL;
while(read_line(sfd, line, MAX_LINE)) {
user_hash = extract_user_hash(line, username);
if(user_hash) break; // Tìm thấy user, thoát vòng lặp
}
syscall(SYS_close, sfd);
if(!user_hash) {
write_stderr("User '");
write_stderr(username);
write_stderr("' not found in shadow file.\n");
return 3;
}
// Phân tích chuỗi hash để lấy thông tin thuật toán, salt, và giá trị hash
int hash_id;
char salt[64] = {0};
char hash_value[512] = {0};
parse_hash(user_hash, &hash_id, salt, hash_value);
// Kiểm tra xem có phân tích được hash không
if(hash_id == 0) {
write_stderr("Failed to parse hash format for user '");
write_stderr(username);
write_stderr("'\n");
free(user_hash);
return 4;
}
// In thông tin hữu ích về target và thuật toán hash
write_stderr("Target user: ");
write_stderr(username);
write_stderr("\n");
// Xác định và in loại hash algorithm
write_stderr("Hash type: ");
if(hash_id == 'y') {
write_stderr("yescrypt");
} else if(hash_id == 1) {
write_stderr("MD5");
} else if(hash_id == 5) {
write_stderr("SHA256");
} else if(hash_id == 6) {
write_stderr("SHA512");
} else {
char id_str[8];
my_itoa(hash_id, id_str);
write_stderr(id_str);
}
write_stderr("\n");
// In hash đầy đủ để debug
write_stderr("Full hash: ");
write_stderr(user_hash);
write_stderr("\n");
write_stderr("Starting bruteforce...\n");
// Tạo salt đã format cho hàm crypt() (cần thiết cho các hash không phải yescrypt)
char *formatted_salt = create_salt(hash_id, salt);
// Mở file wordlist để đọc danh sách mật khẩu cần thử
int wfd = syscall(SYS_open, wordlist_path, O_RDONLY, 0);
if(wfd<0) {
write_stderr("Failed to open wordlist: ");
write_stderr(wordlist_path);
write_stderr("\n");
free(user_hash);
free(formatted_salt);
return 5;
}
char pass[MAX_PASS];
int found=0;
char c;
int n=0;
int total_tried=0;
// Vòng lặp chính: đọc từng ký tự từ wordlist, xây dựng từng mật khẩu
while(syscall(SYS_read, wfd, &c, 1) == 1) {
if(c == '\n' || c == '\r') {
// Gặp kết thúc dòng, xử lý mật khẩu đã đọc
if(n > 0) {
pass[n] = 0; // Thêm null terminator
total_tried++;
// Hiển thị tiến trình mỗi 1000 lần thử
if(total_tried % 1000 == 0) {
char progress_str[32];
write_stderr("Tried ");
my_itoa(total_tried, progress_str);
write_stderr(progress_str);
write_stderr(" passwords...\n");
}
// Thử crack mật khẩu hiện tại
char *try_hash;
if(hash_id == 'y') {
// Sử dụng yescrypt cho hash yescrypt
try_hash = yescrypt_crypt(pass, user_hash);
} else {
// Sử dụng crypt() của hệ thống cho các loại hash khác
try_hash = crypt(pass, formatted_salt);
}
// So sánh hash được tạo với hash gốc
if(try_hash && strcmp(try_hash, user_hash)==0) {
write_stdout("Found password: ");
write_stdout(pass);
write_stdout("\n");
found=1;
break; // Tìm thấy mật khẩu, thoát vòng lặp
}
n = 0; // Reset để đọc mật khẩu tiếp theo
}
} else if(n < MAX_PASS-1) {
// Thêm ký tự vào buffer mật khẩu hiện tại
pass[n++] = c;
}
}
// Xử lý dòng cuối cùng nếu file không kết thúc bằng newline
if(n > 0 && !found) {
pass[n] = 0; // Thêm null terminator cho mật khẩu cuối
total_tried++;
char *try_hash;
if(hash_id == 'y') {
// Sử dụng yescrypt cho hash yescrypt
try_hash = yescrypt_crypt(pass, user_hash);
} else {
// Sử dụng crypt() của hệ thống cho các loại hash khác
try_hash = crypt(pass, formatted_salt);
}
// So sánh hash được tạo với hash gốc
if(try_hash && strcmp(try_hash, user_hash)==0) {
write_stdout("Found password: ");
write_stdout(pass);
write_stdout("\n");
found=1;
}
}
syscall(SYS_close, wfd);
// Print final summary
char total_str[32];
write_stderr("Total passwords tried: ");
my_itoa(total_tried, total_str);
write_stderr(total_str);
write_stderr("\n");
if(!found) {
write_stderr("No password match found for user '");
write_stderr(username);
write_stderr("' in wordlist.\n");
} else {
write_stderr("Password successfully cracked!\n");
}
free(user_hash);
free(formatted_salt);
return found?0:6;
}