John the Ripper 외부 모드 스크립팅 탐구

Kali LinuxBeginner
지금 연습하기

소개

John the Ripper (JtR) 는 인기 있고 강력한 오픈 소스 비밀번호 보안 감사 및 비밀번호 복구 도구입니다. "단일 크랙 (single crack)" 모드, "단어 목록 (wordlist)" 모드, "점진적 (incremental)" 모드와 같은 여러 모드를 사용하여 비밀번호 크랙을 수행할 수 있습니다.

이러한 표준 모드 외에도 JtR 은 놀랍도록 유연한 "외부 모드 (External Mode)"를 제공합니다. 이 모드를 사용하면 외부 프로그램이나 스크립트를 사용하여 비밀번호 후보를 생성할 수 있습니다. 이를 통해 비밀번호 생성 로직을 완벽하게 제어할 수 있으며, 다른 모드에서는 불가능한 매우 맞춤화되고 복잡한 비밀번호 패턴을 만들 수 있습니다.

이 실험실에서는 JtR 의 외부 모드에 대한 기본 사항을 배우게 됩니다. 기본 개념을 이해하는 것부터 시작하여 비밀번호를 생성하기 위한 사용자 지정 C 프로그램을 작성, 컴파일 및 사용하게 됩니다. 또한 기본적인 디버깅 기술을 배우고 보다 실용적인 시나리오에 지식을 적용하게 됩니다.

외부 모드 기본 사항 이해

이 단계에서는 John the Ripper 의 외부 모드에 대한 기본 개념을 배우게 됩니다. 이 모드는 사용자가 정의한 외부 프로그램을 실행하여 작동합니다. 그런 다음 John the Ripper 는 이 프로그램의 표준 출력을 읽고 각 줄을 잠재적인 비밀번호 후보로 취급합니다.

이러한 외부 모드는 일반적으로 /etc/john/john.conf에 위치한 john.conf 구성 파일에 정의됩니다. 각 외부 모드는 [List.External:ModeName]과 같은 섹션 내에 정의됩니다.

기존 외부 모드 정의를 기본 구성 파일에서 검사하여 구조를 살펴보겠습니다. 키보드 워크 패턴을 기반으로 비밀번호 후보를 시뮬레이션하는 Keyboard 모드를 살펴보겠습니다.

다음 명령을 실행하여 Keyboard 모드의 구성을 확인합니다.

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

C 와 유사한 코드 블록이 표시됩니다. 가장 중요한 함수는 비밀번호 후보를 생성하는 generate()입니다.

외부 모드를 실제로 사용하려면 --stdout 플래그와 함께 john을 실행할 수 있습니다. 이렇게 하면 John 이 해시를 크랙하는 대신 생성된 후보를 화면에 출력하도록 지시합니다. Keyboard 모드를 테스트해 보겠습니다.

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

이 명령은 Keyboard 외부 모드를 선택하고 해당 출력을 head로 파이프하여 생성된 첫 5 개의 비밀번호 후보를 보여줍니다. 출력은 키보드 패턴을 나타내는 문자 시퀀스가 됩니다.

q
w
e
r
t

이는 기본 원리를 보여줍니다. John the Ripper 는 외부 모드에 정의된 로직을 실행하고 해당 출력을 비밀번호 소스로 사용합니다. 다음 단계에서는 자체 프로그램을 만들어 소스로 사용하게 됩니다.

간단한 외부 모드 스크립트 작성

이 단계에서는 C 프로그래밍 언어를 사용하여 첫 번째 간단한 외부 모드 스크립트를 작성하고 컴파일합니다. 목표는 고정된 비밀번호 목록을 생성하는 프로그램을 만들고 John the Ripper 가 이를 사용하도록 구성하는 것입니다.

먼저 nano 편집기를 사용하여 simple_gen.c라는 C 소스 파일을 만듭니다.

nano simple_gen.c

이제 nano 편집기에 다음 C 코드를 복사하여 붙여넣습니다. 이 프로그램은 단순히 세 개의 다른 비밀번호를 각 줄에 하나씩 표준 출력으로 출력합니다.

#include <stdio.h>

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

Ctrl+X, Y, Enter를 눌러 파일을 저장하고 nano를 종료합니다.

다음으로 gcc 컴파일러를 사용하여 이 C 코드를 simple_gen이라는 실행 파일로 컴파일합니다.

gcc -o simple_gen simple_gen.c

이제 실행 파일이 준비되었으므로 John the Ripper 에게 이를 사용하는 방법을 알려야 합니다. 프로젝트 디렉토리에 구성 파일의 로컬 복사본을 만들고 새 외부 모드 정의를 추가하여 이 작업을 수행합니다.

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

새로운 my_john.conf 파일을 nano로 엽니다.

nano my_john.conf

파일의 맨 끝으로 스크롤하여 다음 구성 블록을 추가합니다. 이 블록은 simple_gen 프로그램을 실행하는 MySimple이라는 새 외부 모드를 정의합니다.

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

nano를 저장하고 종료합니다 (Ctrl+X, Y, Enter).

마지막으로 새 외부 모드를 테스트해 보겠습니다. --stdout 플래그를 다시 사용하여 출력을 보고 --config 플래그를 사용하여 John 이 사용자 지정 구성 파일을 가리키도록 합니다.

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

C 프로그램의 정확한 출력이 표시되어야 하며, 이는 John the Ripper 가 사용자 지정 스크립트를 성공적으로 실행하고 있음을 확인합니다.

pass1
pass2
pass3

사용자 정의 비밀번호 생성 로직 구현

이 단계에서는 더 동적인 비밀번호 생성 로직을 구현하도록 스크립트를 향상시킵니다. 고정된 목록 대신 기본 단어 뒤에 숫자 시퀀스가 오는 패턴을 기반으로 비밀번호를 생성합니다. 이는 약한 비밀번호의 일반적인 패턴입니다.

C 프로그램을 수정하여 labex0, labex1, labex2 등과 같은 비밀번호를 생성해 보겠습니다.

nanosimple_gen.c 파일을 다시 엽니다.

nano simple_gen.c

기존 코드를 다음 코드로 바꿉니다. 이 새 버전은 for 루프를 사용하여 기본 단어 "labex"에 0 부터 199 까지의 숫자를 추가합니다.

#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;
}

nano를 저장하고 종료합니다. 이제 변경 사항을 적용하기 위해 gcc로 프로그램을 다시 컴파일합니다.

gcc -o simple_gen simple_gen.c

이제 업데이트된 생성기를 --stdout 플래그와 함께 테스트하여 새 출력의 샘플을 보겠습니다.

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

출력은 이제 새 패턴을 보여야 합니다.

labex0
labex1
labex2
labex3
labex4

이제 실제 테스트를 진행합니다. 사용자 정의 외부 모드를 사용하여 설정 중에 준비한 비밀번호 해시를 크랙합니다. testuser의 비밀번호는 labex123입니다. 스크립트가 이 후보를 생성하므로 일치하는 항목을 찾아야 합니다.

크랙 프로세스를 시작하려면 다음 명령을 실행합니다. --stdout을 제거하고 해시 파일 hashes.txt의 경로를 추가했음을 확인합니다.

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

John 은 스크립트를 실행하고 후보를 생성하며 해시에 대해 테스트합니다. 비밀번호를 빠르게 찾아야 합니다. 출력은 다음과 같이 표시됩니다.

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

크랙된 비밀번호를 확인하려면 --show 옵션을 사용합니다.

john --show ./hashes.txt

이렇게 하면 사용자 이름 옆에 크랙된 비밀번호가 표시됩니다.

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

1 password hash cracked, 0 left

외부 모드 스크립트 디버깅

이 단계에서는 외부 모드 스크립트를 디버깅하는 몇 가지 기본 기술을 배우게 됩니다. 스크립트가 예상대로 작동하지 않을 때, 다른 프로그램 (John the Ripper) 에 의해 실행되기 때문에 진단하기가 까다로울 수 있습니다.

일반적이고 효과적인 디버깅 방법은 스크립트에서 별도의 파일로 로그 메시지를 작성하는 것입니다. 이를 통해 실행 흐름을 추적하고 변수 값을 검사할 수 있습니다.

C 프로그램을 수정하여 debug.log라는 로그 파일에 작성해 보겠습니다. nanosimple_gen.c를 엽니다.

nano simple_gen.c

코드를 다음 버전으로 바꿉니다. 이 코드는 debug.log를 쓰기 모드로 열고, 실행 중에 fprintf를 사용하여 상태 메시지를 기록합니다.

#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;
}

nano를 저장하고 종료한 다음 프로그램을 다시 컴파일합니다.

gcc -o simple_gen simple_gen.c

이제 John the Ripper 를 다시 실행합니다. 비밀번호 후보를 볼 필요가 없으므로 표준 출력을 /dev/null로 리디렉션할 수 있습니다. 중요한 것은 스크립트가 실행되어 로그 파일을 생성한다는 것입니다.

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

명령이 잠시 실행되고 완료됩니다. 이제 프로젝트 디렉토리에 debug.log 파일이 있어야 합니다. 해당 내용을 보겠습니다.

cat debug.log | head -n 5

프로그램에 추가한 디버그 메시지가 표시되어야 합니다.

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

이 기술은 John the Ripper 자체의 출력 간섭 없이 잘못된 루프, 잘못된 변수 값 또는 파일 액세스 오류와 같은 논리 문제를 찾는 데 매우 유용합니다.

특정 시나리오에 외부 모드 적용

이 단계에서는 지식을 더 실용적인 시나리오에 적용합니다. 하드코딩된 기본 단어를 사용하는 대신, 스크립트가 파일에서 기본 단어 목록을 읽고 각 단어에 대한 변형을 생성합니다. 이는 훨씬 더 강력하고 현실적인 접근 방식입니다.

먼저 몇 가지 잠재적인 기본 단어를 포함하는 words.txt라는 간단한 단어 목록 파일을 만들어 보겠습니다.

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

다음으로, 이 파일을 읽도록 C 프로그램을 수정합니다. 프로그램은 명령줄 인수로 단어 목록 파일 이름을 받아야 합니다. nanosimple_gen.c를 엽니다.

nano simple_gen.c

코드를 다음 버전으로 바꿉니다. 이 버전은 명령줄에서 파일 이름을 읽고 해당 파일을 열고, 각 단어를 읽을 때마다 세 가지 비밀번호 후보를 생성합니다. 즉, 단어 자체, 단어 뒤에 "123"이 오는 경우, 단어 뒤에 "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;
}

저장하고 종료한 다음 프로그램을 다시 컴파일합니다.

gcc -o simple_gen simple_gen.c

이제 words.txt 파일 이름을 스크립트의 인수로 전달하도록 my_john.conf 파일을 업데이트해야 합니다. 이를 위해 MyAdvanced라는 새 외부 모드를 만들어 보겠습니다. nanomy_john.conf를 엽니다.

nano my_john.conf

파일 끝으로 스크롤하여 이 새 섹션을 추가합니다. "words.txt"exec 함수에 두 번째 매개변수로 어떻게 전달되는지 확인합니다.

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

nano를 저장하고 종료합니다. 마지막으로 새 고급 외부 모드를 테스트합니다.

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

출력은 이제 words.txt 파일의 단어에서 생성된 비밀번호 후보 목록이어야 하며, 각 단어에 지정된 변형이 적용됩니다.

admin
admin123
admin2024
user
user123
user2024
guest
guest123
guest2024

John the Ripper 를 위한 유연하고 파일 기반의 비밀번호 생성기를 성공적으로 만들었습니다.

요약

이 실습을 완료하신 것을 축하드립니다! John the Ripper 의 강력한 외부 모드 (External Mode) 를 성공적으로 탐구했습니다.

이 실습에서는 다음을 배웠습니다.

  • John the Ripper 의 외부 모드 기본 개념과 외부 프로그램을 사용하여 비밀번호 후보를 생성하는 방법
  • 간단한 C 프로그램을 작성, 컴파일 및 비밀번호 생성기로 사용하는 방법
  • john.conf 파일에서 사용자 정의 외부 모드를 구성하는 방법
  • 패턴에 기반한 동적 비밀번호 생성 로직을 구현하는 방법
  • 사용자 정의 모드를 사용하여 비밀번호 해시를 성공적으로 크랙하는 방법
  • 파일에 로깅하여 외부 스크립트를 디버깅하는 기본적이면서도 효과적인 기술
  • 파일에서 기본 단어를 읽는 더 고급적이고 실용적인 스크립트를 만드는 방법

습득한 기술은 특정 대상 및 시나리오에 맞춘 고도로 전문화된 비밀번호 크랙 규칙을 만드는 기초를 제공합니다. Python 또는 Perl 과 같은 다른 스크립팅 언어를 사용하거나 더 복잡한 비밀번호 변형 및 생성 로직을 구현하여 이 주제를 더 탐구할 수 있습니다.