Skip to main content
Glama

runHashcat

Crack password hashes using dictionary, brute-force, or hybrid attacks to test security and recover credentials during penetration testing.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
hashDataYesString containing the password hashes, one per line.
attackModeNoAttack mode: 0=Straight, 1=Combination, 3=Brute-force, 6=Hybrid Wordlist + Mask, 7=Hybrid Mask + Wordlist
hashTypeNoHash-type, e.g., 0=MD5, 100=SHA1, 1000=NTLM, 1400=SHA2-256, 1800=sha512crypt, 22000=WPA*01/WPA*02
wordlistNoPath to wordlist file for dictionary attacks
maskNoMask for brute-force attacks (e.g., '?a?a?a?a?a?a?a?a' for 8 chars)
incrementNoEnable incremental mode (start with shorter passwords)
incrementMinNoMinimum password length for incremental mode
incrementMaxNoMaximum password length for incremental mode
rulesNoRules file to apply to wordlist
sessionNoSession name for resuming attacks
restoreNoRestore a previous session
optimizedKernelsNoEnable optimized kernels (-O)
workloadProfileNoWorkload profile: 1=Low, 2=Default, 3=High, 4=Nightmare
deviceTypesNoDevice types: 1=CPU, 2=GPU, 3=FPGA
forceNoIgnore warnings
potfilePathNoPath to custom potfile
outfileNoOutput file for cracked hashes
outfileFormatNoOutput format: 1=hash, 2=plain, 3=hex-plain, etc.
runtimeNoAbort session after X seconds
showProgressNoShow progress every X seconds
quietNoSuppress output
loopbackNoAdd new plains to induct directory
markovThresholdNoThreshold X when to stop accepting new Markov-chains
customCharset1NoUser-defined charset ?1
customCharset2NoUser-defined charset ?2
customCharset3NoUser-defined charset ?3
customCharset4NoUser-defined charset ?4
optionsNoAdditional raw hashcat options

Implementation Reference

  • Core handler function that executes hashcat by spawning the process, managing temporary files, parsing output for cracked passwords, handling potfile, and providing full execution output.
    async function runHashcat(hashData: string, rawOptions: string[] = []): Promise<{ fullOutput: string; cracked: string[]; potfileLocation?: string }> { let options: string[]; try { options = sanitizeOptions(rawOptions); } catch (error: any) { throw error; } if (!hashData) { throw new Error("Error: Hash data is required."); } // Use /tmp directory for temp files to avoid read-only directory issues const tempHashFile = path.join('/tmp', `hashcat_hashes_${Date.now()}.txt`); const tempPotFile = path.join('/tmp', `hashcat_${Date.now()}.potfile`); let fullOutput = ""; let crackedPasswords: string[] = []; try { await fs.writeFile(tempHashFile, hashData); // Build hashcat arguments const crackingArgs = [...options]; // Add custom potfile to avoid conflicts if (!options.some(opt => opt.includes('--potfile-path'))) { crackingArgs.push('--potfile-path', tempPotFile); } // Add hash file as last argument crackingArgs.push(tempHashFile); fullOutput += `--- Hashcat Cracking Attempt ---\nExecuting: hashcat ${crackingArgs.join(' ')}\n`; try { const crackResult = await runSpawnCommand('hashcat', crackingArgs); fullOutput += `Exit Code: ${crackResult.code}\nStdout:\n${crackResult.stdout}\nStderr:\n${crackResult.stderr}\n`; // Parse cracked passwords from stdout (hashcat shows them during execution) const lines = crackResult.stdout.split('\n'); for (const line of lines) { // Look for lines that contain cracked hashes (usually contain colons) if (line.includes(':') && !line.includes('Session.........') && !line.includes('Status..........')) { const trimmed = line.trim(); if (trimmed && !trimmed.startsWith('[') && !trimmed.startsWith('hashcat')) { crackedPasswords.push(trimmed); } } } } catch (error: any) { fullOutput += `Hashcat command failed to execute: ${error.message}\n`; } // Try to read from potfile if it exists fullOutput += `--- Potfile Check ---\n`; try { const potfileContent = await fs.readFile(tempPotFile, 'utf8'); fullOutput += `Potfile content:\n${potfileContent}\n`; const potfileLines = potfileContent.split('\n').filter(line => line.trim()); crackedPasswords.push(...potfileLines); } catch (error: any) { fullOutput += `Could not read potfile: ${error.message}\n`; } // Show cracked hashes using --show option const showArgs = [...options.filter(opt => !opt.includes('--potfile-path')), '--potfile-path', tempPotFile, '--show', tempHashFile]; fullOutput += `--- Show Cracked Hashes ---\nExecuting: hashcat ${showArgs.join(' ')}\n`; try { const showResult = await runSpawnCommand('hashcat', showArgs); fullOutput += `Exit Code: ${showResult.code}\nStdout:\n${showResult.stdout}\nStderr:\n${showResult.stderr}\n`; const showLines = showResult.stdout.split('\n').map(line => line.trim()).filter(line => line && line.includes(':')); crackedPasswords.push(...showLines); } catch (error: any) { fullOutput += `Show command failed to execute: ${error.message}\n`; } // Remove duplicates and filter valid entries crackedPasswords = [...new Set(crackedPasswords)].filter(line => line && line.includes(':')); // Clean up temp files try { await fs.unlink(tempHashFile); } catch { /* ignore */ } if (currentUserSession.mode === UserMode.PROFESSIONAL) { await logMessage(`Ran Hashcat.\nOptions: ${options.join(' ')}\nCracked: ${crackedPasswords.length} hashes.`); } return { fullOutput, cracked: crackedPasswords, potfileLocation: tempPotFile }; } catch (error: any) { console.error("Fatal error setting up Hashcat execution:", error); try { await fs.unlink(tempHashFile); } catch { /* ignore */ } try { await fs.unlink(tempPotFile); } catch { /* ignore */ } if (currentUserSession.mode === UserMode.PROFESSIONAL) { await logMessage(`Hashcat FAILED fatally before execution.\nOptions: ${options.join(' ')}\nError: ${error.message}`); } throw new Error(`Hashcat setup failed fatally: ${error.message}`); } }
  • Zod schema defining the input parameters and description for the runHashcat MCP tool.
    const hashcatToolSchema = z.object({ hashData: z.string().describe("String containing the password hashes, one per line."), attackMode: z.enum(['0', '1', '3', '6', '7']).optional().describe("Attack mode: 0=Straight, 1=Combination, 3=Brute-force, 6=Hybrid Wordlist + Mask, 7=Hybrid Mask + Wordlist"), hashType: z.string().optional().describe("Hash-type, e.g., 0=MD5, 100=SHA1, 1000=NTLM, 1400=SHA2-256, 1800=sha512crypt, 22000=WPA*01/WPA*02"), wordlist: z.string().optional().describe("Path to wordlist file for dictionary attacks"), mask: z.string().optional().describe("Mask for brute-force attacks (e.g., '?a?a?a?a?a?a?a?a' for 8 chars)"), increment: z.boolean().optional().describe("Enable incremental mode (start with shorter passwords)"), incrementMin: z.number().int().optional().describe("Minimum password length for incremental mode"), incrementMax: z.number().int().optional().describe("Maximum password length for incremental mode"), rules: z.string().optional().describe("Rules file to apply to wordlist"), session: z.string().optional().describe("Session name for resuming attacks"), restore: z.boolean().optional().describe("Restore a previous session"), optimizedKernels: z.boolean().optional().describe("Enable optimized kernels (-O)"), workloadProfile: z.enum(['1', '2', '3', '4']).optional().describe("Workload profile: 1=Low, 2=Default, 3=High, 4=Nightmare"), deviceTypes: z.array(z.enum(['1', '2', '3'])).optional().describe("Device types: 1=CPU, 2=GPU, 3=FPGA"), force: z.boolean().optional().describe("Ignore warnings"), potfilePath: z.string().optional().describe("Path to custom potfile"), outfile: z.string().optional().describe("Output file for cracked hashes"), outfileFormat: z.number().int().optional().describe("Output format: 1=hash, 2=plain, 3=hex-plain, etc."), runtime: z.number().int().optional().describe("Abort session after X seconds"), showProgress: z.boolean().optional().describe("Show progress every X seconds"), quiet: z.boolean().optional().describe("Suppress output"), loopback: z.boolean().optional().describe("Add new plains to induct directory"), markovThreshold: z.number().int().optional().describe("Threshold X when to stop accepting new Markov-chains"), customCharset1: z.string().optional().describe("User-defined charset ?1"), customCharset2: z.string().optional().describe("User-defined charset ?2"), customCharset3: z.string().optional().describe("User-defined charset ?3"), customCharset4: z.string().optional().describe("User-defined charset ?4"), options: z.array(z.string()).optional().describe("Additional raw hashcat options") }).describe( "Crack password hashes using Hashcat. More powerful and faster than John the Ripper for many hash types, especially with GPU acceleration. " + "Supports various attack modes and hash types. Example: `{\"hashData\":\"5d41402abc4b2a76b9719d911017c592\", \"hashType\":\"0\", \"attackMode\":\"0\", \"wordlist\":\"/tmp/wordlist.txt\"}`" );
  • src/index.ts:885-1041 (registration)
    MCP server.tool registration for 'runHashcat', including input validation, option construction based on params, call to core runHashcat function, and response formatting.
    server.tool("runHashcat", hashcatToolSchema.shape, async (args /*, extra */) => { const { hashData, attackMode, hashType, wordlist, mask, increment, incrementMin, incrementMax, rules, session, restore, optimizedKernels, workloadProfile, deviceTypes, force, potfilePath, outfile, outfileFormat, runtime, showProgress, quiet, loopback, markovThreshold, customCharset1, customCharset2, customCharset3, customCharset4, options } = args; console.error(`Received Hashcat:`, { hashData: `len=${hashData.length}`, attackMode, hashType, wordlist }); if (currentUserSession.mode === UserMode.STUDENT) console.warn("[Student Mode] Executing Hashcat."); try { const constructedOptions: string[] = []; const validationErrors: string[] = []; // Attack mode if (attackMode) { constructedOptions.push('-a', attackMode); } else { constructedOptions.push('-a', '0'); // Default to dictionary attack } // Hash type if (hashType) { constructedOptions.push('-m', hashType); } // Wordlist for dictionary/combination attacks if ((attackMode === '0' || attackMode === '1' || !attackMode) && wordlist) { constructedOptions.push(wordlist); } else if ((attackMode === '0' || !attackMode) && !wordlist && !mask) { validationErrors.push("Dictionary attack requires a wordlist"); } // Mask for brute-force attacks if (attackMode === '3' && mask) { constructedOptions.push(mask); } else if (attackMode === '3' && !mask) { validationErrors.push("Brute-force attack requires a mask"); } // Hybrid attacks if (attackMode === '6' && wordlist && mask) { constructedOptions.push(wordlist, mask); } else if (attackMode === '6') { validationErrors.push("Hybrid Wordlist + Mask attack requires both wordlist and mask"); } if (attackMode === '7' && mask && wordlist) { constructedOptions.push(mask, wordlist); } else if (attackMode === '7') { validationErrors.push("Hybrid Mask + Wordlist attack requires both mask and wordlist"); } // Increment mode if (increment) { constructedOptions.push('-i'); if (incrementMin) constructedOptions.push('--increment-min', incrementMin.toString()); if (incrementMax) constructedOptions.push('--increment-max', incrementMax.toString()); } // Rules if (rules) { constructedOptions.push('-r', rules); } // Session management if (session) { constructedOptions.push('--session', session); } if (restore) { constructedOptions.push('--restore'); } // Performance options if (optimizedKernels) { constructedOptions.push('-O'); } if (workloadProfile) { constructedOptions.push('-w', workloadProfile); } if (deviceTypes && deviceTypes.length > 0) { constructedOptions.push('-d', deviceTypes.join(',')); } // General options if (force) { constructedOptions.push('--force'); } if (potfilePath) { constructedOptions.push('--potfile-path', potfilePath); } if (outfile) { constructedOptions.push('-o', outfile); if (outfileFormat) { constructedOptions.push('--outfile-format', outfileFormat.toString()); } } if (runtime) { constructedOptions.push('--runtime', runtime.toString()); } if (showProgress) { constructedOptions.push('--status'); } if (quiet) { constructedOptions.push('--quiet'); } if (loopback) { constructedOptions.push('--loopback'); } if (markovThreshold) { constructedOptions.push('--markov-threshold', markovThreshold.toString()); } // Custom charsets if (customCharset1) constructedOptions.push('-1', customCharset1); if (customCharset2) constructedOptions.push('-2', customCharset2); if (customCharset3) constructedOptions.push('-3', customCharset3); if (customCharset4) constructedOptions.push('-4', customCharset4); // Raw options if (options) { constructedOptions.push(...options); } if (validationErrors.length > 0) { throw new Error(`Invalid parameters: ${validationErrors.join('; ')}`); } const { fullOutput, cracked, potfileLocation } = await runHashcat(hashData, constructedOptions); const responseContent: any[] = []; if (currentUserSession.mode === UserMode.STUDENT) { responseContent.push({ type: "text", text: `Hashcat finished! Found ${cracked.length} cracked passwords.` }); if (cracked.length > 0) { responseContent.push({ type: "text", text: "\n**🎉 Cracked Passwords:**\n" + cracked.join("\n") }); responseContent.push({ type: "text", text: "\n**What this means:** These are the plain-text passwords that correspond to your hashes. You can now use these for further testing or to demonstrate the importance of strong passwords." }); } else { responseContent.push({ type: "text", text: "\n**No passwords cracked.** This could mean:\n- The passwords are very strong\n- You need a better wordlist\n- The hash type might be incorrect\n- Try different attack modes or longer runtime" }); } if (potfileLocation) { responseContent.push({ type: "text", text: `\n**Tip:** Results are saved in: ${potfileLocation}` }); } } else { // Professional mode responseContent.push({ type: "text", text: `Hashcat completed. Cracked: ${cracked.length} hashes.` }); if (cracked.length > 0) { responseContent.push({ type: "text", text: "\n**Cracked:**\n" + cracked.join("\n") }); } if (potfileLocation) { responseContent.push({ type: "text", text: `Potfile: ${potfileLocation}` }); } } responseContent.push({ type: "text", text: "\n--- Full Hashcat Output ---\n" + fullOutput }); return { content: responseContent }; } catch (error: any) { return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true }; } });
  • Helper function to spawn external commands like hashcat using child_process.spawn, capturing stdout/stderr and exit code. Used by runHashcat.
    async function runSpawnCommand(command: string, args: string[]): Promise<{ stdout: string; stderr: string; code: number | null }> { return new Promise((resolve, reject) => { console.error(`Attempting to spawn: ${command} ${args.join(' ')}`); // Added for debugging const process = spawn(command, args); let stdout = ''; let stderr = ''; process.stdout.on('data', (data) => { stdout += data.toString(); }); process.stderr.on('data', (data) => { stderr += data.toString(); }); process.on('error', (error) => { // Explicitly catch spawn errors (e.g., command not found) console.error(`Spawn error for command "${command}": ${error.message}`); reject(new Error(`Failed to start command "${command}": ${error.message}`)); }); process.on('close', (code) => { console.error(`Command "${command}" exited with code ${code}`); // Added for debugging resolve({ stdout, stderr, code }); }); }); }
  • Helper function to sanitize command-line options passed to tools like hashcat, preventing injection attacks via regex validation.
    const SAFE_OPTION_REGEX = /^(?:-[a-zA-Z0-9]+|--[a-zA-Z0-9\-]+(?:=[^;&|`$\s\(\)\<\>\\]+)?|[^;&|`$\s\(\)\<\>\\]+)$/; function sanitizeOptions(options: string[]): string[] { const sanitized: string[] = []; for (const opt of options) { if (SAFE_OPTION_REGEX.test(opt)) { sanitized.push(opt); } else { throw new Error(`Invalid or potentially unsafe option detected: "${opt}". Only standard flags and simple arguments are allowed.`); } } return sanitized; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/DMontgomery40/pentest-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server