/**
* Password cracking tools
*/
import { HydraInput, JohnInput, HashcatInput } from "../schemas/password.schemas.js";
import { ToolResult } from "../types.js";
import { executeCommand } from "../utils/executor.js";
import { formatExecutionResult, formatErrorMessage } from "../utils/formatter.js";
import { validateTarget, validatePath, validateWordlistPath } from "../utils/validator.js";
import { TOOL_PATHS } from "../constants.js";
/**
* Hydra password brute force
*/
export async function hydra(input: HydraInput): Promise<ToolResult> {
if (!validateTarget(input.target)) {
return formatErrorMessage("Invalid target", `Target '${input.target}' is not valid`);
}
if (!input.username && !input.username_list) {
return formatErrorMessage(
"Missing username",
"Either 'username' or 'username_list' must be provided"
);
}
if (!input.password && !input.password_list) {
return formatErrorMessage(
"Missing password",
"Either 'password' or 'password_list' must be provided"
);
}
if (input.username_list && !validatePath(input.username_list)) {
return formatErrorMessage("Invalid username list path", "Path contains invalid characters");
}
if (input.password_list && !validateWordlistPath(input.password_list)) {
return formatErrorMessage(
"Invalid password list path",
"Password list must be in an allowed directory"
);
}
const args: string[] = [];
// Username
if (input.username) {
args.push("-l", input.username);
} else if (input.username_list) {
args.push("-L", input.username_list);
}
// Password
if (input.password) {
args.push("-p", input.password);
} else if (input.password_list) {
args.push("-P", input.password_list);
}
// Threads
args.push("-t", String(input.threads));
// Verbose
if (input.verbose) {
args.push("-V");
}
// Target and service
if (input.port) {
args.push(`${input.target}:${input.port}`);
} else {
args.push(input.target);
}
args.push(input.service);
const result = await executeCommand(TOOL_PATHS.hydra || "hydra", args, {
timeout: input.timeout * 1000,
});
return formatExecutionResult(result);
}
/**
* John the Ripper password cracking
*/
export async function john(input: JohnInput): Promise<ToolResult> {
if (!validatePath(input.hash_file)) {
return formatErrorMessage("Invalid hash file path", "Path contains invalid characters");
}
if (input.wordlist && !validateWordlistPath(input.wordlist)) {
return formatErrorMessage(
"Invalid wordlist path",
"Wordlist must be in an allowed directory"
);
}
const args: string[] = [];
// Format
if (input.format) {
args.push(`--format=${input.format}`);
}
// Wordlist mode
if (input.wordlist) {
args.push(`--wordlist=${input.wordlist}`);
if (input.rules) {
args.push(`--rules=${input.rules}`);
}
} else if (input.incremental) {
args.push("--incremental");
}
// Hash file
args.push(input.hash_file);
const result = await executeCommand(TOOL_PATHS.john || "john", args, {
timeout: input.timeout * 1000,
});
return formatExecutionResult(result);
}
/**
* Hashcat password cracking
*/
export async function hashcat(input: HashcatInput): Promise<ToolResult> {
if (!validatePath(input.hash_file)) {
return formatErrorMessage("Invalid hash file path", "Path contains invalid characters");
}
const args: string[] = [];
// Mode
args.push("-m", String(input.mode));
// Attack type
const attackModes: Record<string, string> = {
dictionary: "0",
combinator: "1",
"brute-force": "3",
hybrid: "6",
};
args.push("-a", attackModes[input.attack_type]!);
// Hash file
args.push(input.hash_file);
// Wordlist or mask
if (input.attack_type === "dictionary" || input.attack_type === "hybrid") {
if (!input.wordlist) {
return formatErrorMessage(
"Missing wordlist",
"Wordlist is required for dictionary attacks"
);
}
if (!validateWordlistPath(input.wordlist)) {
return formatErrorMessage(
"Invalid wordlist path",
"Wordlist must be in an allowed directory"
);
}
args.push(input.wordlist);
} else if (input.attack_type === "brute-force") {
if (!input.mask) {
return formatErrorMessage(
"Missing mask",
"Mask is required for brute-force attacks (e.g., '?a?a?a?a?a?a?a?a')"
);
}
args.push(input.mask);
}
// Rules
if (input.rules) {
args.push("-r", input.rules);
}
const result = await executeCommand(TOOL_PATHS.hashcat || "hashcat", args, {
timeout: input.timeout * 1000,
});
return formatExecutionResult(result);
}