Explore John the Ripper External Mode Scripting

Kali LinuxBeginner
Practice Now

Introduction

John the Ripper (JtR) is a popular and powerful open-source password security auditing and password recovery tool. It can perform password cracking using several modes, such as "single crack" mode, "wordlist" mode, and "incremental" mode.

Beyond these standard modes, JtR offers an incredibly flexible "External Mode." This mode allows you to use an external program or script to generate password candidates. This gives you complete control over the password generation logic, enabling you to create highly customized and complex password patterns that are not possible with other modes.

In this lab, you will learn the fundamentals of JtR's External Mode. You will start by understanding the basic concepts, then write, compile, and use your own custom C program to generate passwords. You will also learn basic debugging techniques and apply your knowledge to a more practical scenario.

Understand External Mode Basics

In this step, you will learn the fundamental concepts of John the Ripper's External Mode. This mode works by executing an external program you define. John the Ripper then reads the standard output of this program, treating each line as a potential password candidate.

These external modes are defined in the john.conf configuration file, typically located at /etc/john/john.conf. Each external mode is defined within a section like [List.External:ModeName].

Let's inspect an existing external mode definition in the default configuration file to see how it's structured. We will look for the Keyboard mode, which simulates password candidates based on keyboard walk patterns.

Execute the following command to view the Keyboard mode's configuration:

grep -A 10 "\[List.External:Keyboard\]" /etc/john/john.conf

You will see a block of C-like code. The most important function is generate(), which is responsible for producing the password candidates.

To see an external mode in action, you can run john with the --stdout flag. This tells John to print the generated candidates to the screen instead of trying to crack a hash. Let's test the Keyboard mode.

john --stdout --external=Keyboard | head -n 5

This command selects the Keyboard external mode and pipes its output to head, which shows you the first 5 password candidates generated. The output will be a sequence of characters representing keyboard patterns.

q
w
e
r
t

This demonstrates the basic principle: John the Ripper runs the logic defined in the external mode and uses its output as a source of passwords. In the next steps, we will create our own program to act as the source.

Write a Simple External Mode Script

In this step, you will write and compile your first simple external mode script using the C programming language. Our goal is to create a program that generates a fixed list of passwords and then configure John the Ripper to use it.

First, let's create a C source file named simple_gen.c using the nano editor.

nano simple_gen.c

Now, copy and paste the following C code into the nano editor. This program will simply print three different passwords to the standard output, each on a new line.

#include <stdio.h>

int main() {
    printf("pass1\n");
    printf("pass2\n");
    printf("pass3\n");
    return 0;
}

Press Ctrl+X, then Y, and Enter to save the file and exit nano.

Next, compile this C code into an executable file named simple_gen using the gcc compiler.

gcc -o simple_gen simple_gen.c

Now that we have our executable, we need to tell John the Ripper how to use it. We will do this by creating a local copy of the configuration file in our project directory and adding a new external mode definition.

cp /etc/john/john.conf ./my_john.conf

Open the new my_john.conf file with nano.

nano my_john.conf

Scroll to the very end of the file and add the following configuration block. This defines a new external mode called MySimple that executes our simple_gen program.

[List.External:MySimple]
void generate()
{
  exec("./simple_gen");
}

Save and exit nano (Ctrl+X, Y, Enter).

Finally, let's test our new external mode. We'll use the --stdout flag again to see the output, and the --config flag to point John to our custom configuration file.

john --stdout --external=MySimple --config=./my_john.conf

You should see the exact output from our C program, confirming that John the Ripper is successfully running our custom script.

pass1
pass2
pass3

Implement Custom Password Generation Logic

In this step, you will enhance your script to implement more dynamic password generation logic. Instead of a fixed list, we will generate passwords based on a pattern: a base word followed by a sequence of numbers. This is a common pattern for weak passwords.

Let's modify our C program to generate passwords like labex0, labex1, labex2, and so on.

Open the simple_gen.c file again with nano.

nano simple_gen.c

Replace the existing code with the following. This new version uses a for loop to append numbers from 0 to 199 to the base word "labex".

#include <stdio.h>

int main() {
    char *base_word = "labex";
    for (int i = 0; i < 200; i++) {
        printf("%s%d\n", base_word, i);
    }
    return 0;
}

Save and exit nano. Now, recompile the program with gcc to apply the changes.

gcc -o simple_gen simple_gen.c

Let's test the updated generator with the --stdout flag to see a sample of the new output.

john --stdout --external=MySimple --config=./my_john.conf | head -n 5

The output should now show the new pattern.

labex0
labex1
labex2
labex3
labex4

Now for the real test. We will use our custom external mode to crack the password hash we prepared during the setup. The password for the testuser is labex123. Our script generates this candidate, so it should find a match.

Run the following command to start the cracking process. Notice we have removed --stdout and added the path to our hash file, hashes.txt.

john --external=MySimple --config=./my_john.conf ./hashes.txt

John will run your script, generate candidates, and test them against the hash. It should quickly find the password. The output will look something like this:

Using default input encoding: UTF-8
Loaded 1 password hash (sha512crypt, $6$ [SHA512 256/256 AVX2 4x])
Cost 1 (iteration count) is 5000 for all loaded hashes
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
labex123         (testuser)
1g 0:00:00:00 DONE (2024-05-20 08:30) 100.0g/s 12300p/s 12300c/s 12300C/s labex123..labex130
Use the "--show" option to display all of the cracked passwords reliably
Session completed

To confirm the cracked password, use the --show option.

john --show ./hashes.txt

This will display the cracked password next to the username.

testuser:labex123:1001:1001::/home/testuser:/bin/sh

1 password hash cracked, 0 left

Debug External Mode Scripts

In this step, you will learn some basic techniques for debugging your external mode scripts. When a script doesn't work as expected, it can be tricky to diagnose because it's being run by another program (John the Ripper).

A common and effective debugging method is to write log messages from your script to a separate file. This allows you to trace its execution flow and inspect variable values.

Let's modify our C program to write to a log file named debug.log. Open simple_gen.c with nano.

nano simple_gen.c

Replace the code with the following version. This code opens debug.log for writing, and then uses fprintf to write status messages to it during execution.

#include <stdio.h>
#include <stdlib.h>

int main() {
    FILE *log_file = fopen("debug.log", "w");
    if (log_file == NULL) {
        // Cannot open log file, exit
        return 1;
    }

    fprintf(log_file, "Debug: Script started.\n");

    char *base_word = "labex";
    for (int i = 0; i < 200; i++) {
        printf("%s%d\n", base_word, i);
        fprintf(log_file, "Debug: Generated candidate %s%d\n", base_word, i);
    }

    fprintf(log_file, "Debug: Script finished.\n");
    fclose(log_file);
    return 0;
}

Save and exit nano, then recompile the program.

gcc -o simple_gen simple_gen.c

Now, run John the Ripper again. We don't need to see the password candidates, so we can redirect the standard output to /dev/null. The important part is that our script will run and create the log file.

john --stdout --external=MySimple --config=./my_john.conf > /dev/null

The command will run for a moment and finish. Now, a debug.log file should exist in your project directory. Let's view its contents.

cat debug.log | head -n 5

You should see the debug messages that we added to our program.

Debug: Script started.
Debug: Generated candidate labex0
Debug: Generated candidate labex1
Debug: Generated candidate labex2
Debug: Generated candidate labex3

This technique is invaluable for finding issues in your logic, such as incorrect loops, wrong variable values, or file access errors, without the interference of John the Ripper's own output.

Apply External Mode to Specific Scenarios

In this step, you will apply your knowledge to a more practical scenario. Instead of using a hardcoded base word, your script will read a list of base words from a file and generate variations for each one. This is a much more powerful and realistic approach.

First, let's create a simple wordlist file named words.txt containing a few potential base words.

echo "admin" > words.txt
echo "user" >> words.txt
echo "guest" >> words.txt

Next, we'll modify our C program to read this file. The program will need to accept the wordlist filename as a command-line argument. Open simple_gen.c with nano.

nano simple_gen.c

Replace the code with the following. This version reads a filename from the command line, opens that file, and for each word it reads, it generates three password candidates: the word itself, the word followed by "123", and the word followed by "2024".

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[]) {
    if (argc < 2) {
        fprintf(stderr, "Usage: %s <wordlist_file>\n", argv[0]);
        return 1;
    }

    FILE *file = fopen(argv[1], "r");
    if (file == NULL) {
        perror("Error opening file");
        return 1;
    }

    char line[256];
    while (fgets(line, sizeof(line), file)) {
        // Remove newline character from the end of the line
        line[strcspn(line, "\n")] = 0;

        // Generate variations for the word
        printf("%s\n", line);
        printf("%s123\n", line);
        printf("%s2024\n", line);
    }

    fclose(file);
    return 0;
}

Save, exit, and recompile the program.

gcc -o simple_gen simple_gen.c

Now, we must update our my_john.conf file to pass the words.txt filename as an argument to our script. Let's create a new external mode called MyAdvanced for this. Open my_john.conf with nano.

nano my_john.conf

Scroll to the end of the file and add this new section. Note how "words.txt" is passed as a second parameter to the exec function.

[List.External:MyAdvanced]
void generate()
{
  exec("./simple_gen", "words.txt");
}

Save and exit nano. Finally, test your new advanced external mode.

john --stdout --external=MyAdvanced --config=./my_john.conf

The output should now be a list of password candidates generated from the words in your words.txt file, with the specified variations applied to each.

admin
admin123
admin2024
user
user123
user2024
guest
guest123
guest2024

You have successfully created a flexible, file-driven password generator for John the Ripper.

Summary

Congratulations on completing this lab! You have successfully explored the powerful External Mode of John the Ripper.

In this lab, you have learned:

  • The basic concept of John the Ripper's External Mode and how it uses external programs to generate password candidates.
  • How to write, compile, and use a simple C program as a password generator.
  • How to configure a custom external mode in the john.conf file.
  • How to implement dynamic password generation logic based on patterns.
  • How to use your custom mode to successfully crack a password hash.
  • A basic but effective technique for debugging your external scripts by logging to a file.
  • How to create a more advanced and practical script that reads base words from a file.

The skills you've acquired provide a foundation for creating highly specialized password cracking rules tailored to specific targets and scenarios. You can further explore this topic by using other scripting languages like Python or Perl, or by implementing even more complex password mutation and generation logic.