John the Ripper 사용자 정의 포맷 생성

Kali LinuxBeginner
지금 연습하기

소개

John the Ripper (JtR) 는 강력하고 인기 있는 오픈 소스 비밀번호 크래킹 도구입니다. JtR 의 주요 강점 중 하나는 확장성입니다. JtR 은 기본적으로 방대한 수의 해시 유형을 지원하지만, JtR 이 인식하지 못하는 사용자 정의 또는 잘 알려지지 않은 해싱 스킴을 접할 수도 있습니다. 이러한 경우, JtR 에게 해당 해싱 스킴을 크래킹하는 방법을 가르치기 위해 사용자 정의 포맷을 직접 만들 수 있습니다.

이 실습에서는 사용자 정의 JtR 포맷 생성의 기본 개념을 배우게 됩니다. 이 실습은 개념적이며 구조와 프로세스에 중점을 두어, 완전한 기능을 갖춘 포맷을 개발하는 데 필요한 기초 지식을 제공합니다. 완전하게 작동하는 포맷을 작성하고 컴파일하지는 않겠지만, 필요한 모든 단계를 이해하게 될 것입니다.

John the Ripper 포맷 구조 이해하기

이 단계에서는 John the Ripper 포맷의 기본 구조에 대해 배우게 됩니다. 이 구조를 이해하는 것이 사용자 정의 포맷을 만드는 첫 번째이자 가장 중요한 부분입니다.

JtR 포맷은 C 소스 파일 (.c) 로 정의되며, struct fmt_main이라는 핵심 C 구조체를 중심으로 구축됩니다. 이 구조체는 청사진 역할을 하여 JtR 이 해시 유형에 대해 알아야 할 모든 것을 알려줍니다.

struct fmt_main 내의 주요 필드는 다음과 같습니다.

  • fmt_label: --format= 명령줄 옵션으로 포맷을 지정하는 데 사용하는 짧고 고유한 문자열 (예: md5raw).
  • fmt_tag: JtR 이 저장된 세션 파일에서 포맷을 구별하는 데 내부적으로 사용하는 고유 식별자 (예: $dynamic_0$).
  • algorithm_name: 해싱 알고리즘과 그 속성을 설명하는 문자열로, 정보 제공 목적으로 사용됩니다.
  • plaintext_length: 포맷이 처리할 수 있는 최대 비밀번호 길이.
  • binary_size: 원시 이진 해시의 크기. MD5 의 경우 16 바이트입니다.
  • salt_size: 해시 유형이 솔트를 사용하는 경우 솔트의 크기.
  • methods: 포맷의 로직을 구현하는 다양한 함수에 대한 포인터를 포함하는 중첩 구조체입니다. 가장 중요한 함수는 다음과 같습니다.
    • valid(): 입력 파일의 주어진 해시 문자열이 이 포맷에 유효한지 확인합니다. 첫 번째 게이트키퍼입니다.
    • split(): 해시 문자열에 사용자 이름과 같이 해시 외의 다른 정보가 포함된 경우, 이 함수가 구성 요소를 분리합니다.
    • binary(): 16 진수 또는 base64 해시 문자열을 원시 이진 표현으로 변환합니다.
    • salt(): 해시 문자열에서 솔트를 추출합니다.
    • crypt_all(): 핵심 함수입니다. 후보 비밀번호 세트를 받아 해싱하고 비교를 위해 준비합니다.
    • cmp_all(): crypt_all()에서 새로 계산된 해시를 대상 해시와 비교하여 일치하는지 확인합니다.

이 단계는 순전히 이론적입니다. 명령을 실행할 필요는 없습니다. 다음 단계에서는 실제 포맷 파일을 검토하여 이 구조가 어떻게 작동하는지 살펴보겠습니다.

기존 포맷 정의 식별하기

이 단계에서는 John the Ripper 소스 트리 내에서 기존 포맷의 소스 코드를 찾게 됩니다. 이러한 파일을 검토하는 것이 자신만의 포맷을 구축하는 가장 좋은 방법입니다.

이 실습을 위한 설정 스크립트가 이미 JtR 소스 코드를 ~/project/john 디렉터리로 복제했습니다.

먼저 소스 파일이 있는 src 디렉터리로 이동합니다.

cd ~/project/john/src

이제 포맷을 정의하는 모든 파일을 나열합니다. 관례적으로 이러한 파일은 _fmt_plug.c로 끝납니다.

ls *_fmt_plug.c

각 파일은 JtR 이 지원하는 다른 해시 유형에 해당하며, 긴 파일 목록이 표시됩니다.

7z_fmt_plug.c             des_fmt_plug.c          lotus5_fmt_plug.c         ...
afs_fmt_plug.c            django_fmt_plug.c       mdc2_fmt_plug.c
aix_smd5_fmt_plug.c       dmg_fmt_plug.c          md4_fmt_plug.c
aix_ssha_fmt_plug.c       dominosec_fmt_plug.c    md5_fmt_plug.c
...and many more...

이전 단계에서 설명한 struct fmt_main을 보기 위해 비교적 간단한 md5_fmt_plug.c 파일을 살펴보겠습니다. cathead를 함께 사용하여 파일의 상단 부분만 보겠습니다.

cat md5_fmt_plug.c | head -n 50

출력에서 이전 단계에서 설명한 대로 struct fmt_main fmt_MD5 정의와 해당 레이블, 태그, 알고리즘 이름 및 메서드 포인터를 볼 수 있습니다.

간단한 사용자 정의 포맷 생성 (개념적)

이 단계에서는 간단한 사용자 정의 포맷을 위한 새로운 C 소스 파일을 개념적으로 생성합니다. 이 연습은 새 포맷 파일을 어떻게 구성해야 하는지 보여줄 것입니다.

우리의 목표는 특수 접두사 labex_md5$를 가진 해시를 인식하는 포맷을 만드는 것입니다. 예를 들어, labex_md5$87e4e494b2399b0921d44e03693518f9와 같은 해시입니다.

아직 ~/project/john/src 디렉터리에 있는지 확인하십시오. 새 파일을 만들기 위해 nano 텍스트 편집기를 사용할 것입니다.

nano labex_md5_fmt_plug.c

이제 다음 C 코드를 복사하여 nano 편집기에 붙여넣으십시오. 이것은 단순화된 골격이며 완전히 기능적인 포맷은 아니지만 핵심 구성 요소를 보여줍니다.

#if FMT_EXTERNS_H
extern struct fmt_main fmt_labex_md5;
#elif FMT_REGISTERS_H
john_register_one(&fmt_labex_md5);
#else

#include <string.h>
#include "arch.h"
#include "common.h"
#include "formats.h"

#define FORMAT_LABEL        "labex-md5"
#define FORMAT_NAME         "LabEx Custom MD5"
#define ALGORITHM_NAME      "MD5 32/64"
#define PLAINTEXT_LENGTH    32
#define BINARY_SIZE         16
#define SALT_SIZE           0
#define TAG_PREFIX          "labex_md5$"
#define TAG_LENGTH          (sizeof(TAG_PREFIX) - 1)

// This function checks if a hash string is valid for our format
static int valid(char *ciphertext, struct fmt_main *self)
{
    if (strncmp(ciphertext, TAG_PREFIX, TAG_LENGTH))
        return 0;
    char *p = ciphertext + TAG_LENGTH;
    if (hexlenu(p, 0) != 32)
        return 0;
    return 1;
}

struct fmt_main fmt_labex_md5 = {
    {
        FORMAT_LABEL,
        FORMAT_NAME,
        ALGORITHM_NAME,
        BENCHMARK_COMMENT,
        BENCHMARK_LENGTH,
        0,
        PLAINTEXT_LENGTH,
        BINARY_SIZE,
        BINARY_ALIGN,
        SALT_SIZE,
        SALT_ALIGN,
        MIN_KEYS_PER_CRYPT,
        MAX_KEYS_PER_CRYPT,
        FMT_CASE | FMT_8_BIT,
        { NULL },
        { TAG_PREFIX },
        NULL
    }, {
        /* init */      init,
        /* done */      done,
        /* reset */     reset,
        /* prepare */   prepare,
        /* valid */     valid,
        /* split */     split,
        /* binary */    binary,
        /* salt */      salt,
        { NULL },
        /* source */    source,
        {
            /* get_hash* */ get_hash_0,
            /* get_hash* */ get_hash_1,
            /* get_hash* */ get_hash_2,
            /* get_hash* */ get_hash_3,
            /* get_hash* */ get_hash_4,
            /* get_hash* */ get_hash_5,
            /* get_hash* */ get_hash_6
        },
        /* cmp_all */   cmp_all,
        /* cmp_one */   cmp_one,
        /* cmp_exact */ cmp_exact
    }
};

#endif

코드를 붙여넣은 후 파일을 저장하고 Ctrl+X, Y, Enter를 눌러 nano를 종료하십시오.

이제 새 포맷의 소스 파일을 만들었습니다. 가장 중요한 부분은 valid() 함수로, 단순히 labex_md5$ 접두사를 확인하고 뒤따르는 해시가 32 자 길이인지 확인합니다.

John the Ripper 를 새 포맷으로 컴파일하기 (개념적)

이제 개념적인 포맷 파일을 만들었으므로, 이 단계에서는 이를 John the Ripper 빌드 프로세스에 통합하고 컴파일하는 방법을 설명합니다.

먼저 src 디렉터리에 있는지 확인하십시오.

cd ~/project/john/src

JtR 을 컴파일하는 첫 번째 단계는 configure 스크립트를 실행하는 것입니다. 이 스크립트는 시스템에서 필요한 라이브러리를 확인하고 빌드 환경을 설정합니다.

./configure

구성 (configuration) 이 완료된 후 make 명령을 사용하여 소스 코드를 컴파일할 수 있습니다. 이전 빌드를 제거하기 위해 make clean을 사용하고, 프로세스를 가속화하기 위해 4 개의 병렬 작업을 사용하여 컴파일을 실행하기 위해 make -sj4를 사용합니다.

make -s clean && make -sj4

컴파일에는 1~2 분 정도 소요됩니다.

중요 참고 사항: 실제 시나리오에서는 .c 파일을 만드는 것만으로는 충분하지 않습니다. 또한 빌드 시스템에 최종 john 실행 파일을 링크할 때 새 labex_md5_fmt_plug.o 오브젝트 파일을 포함하도록 지시하기 위해 구성 파일 (예: Makefile.in) 을 편집해야 합니다. 이 실습에서는 단순화를 위해 해당 수정을 건너뜁니다. 따라서 컴파일은 성공하지만, 새 포맷은 최종 바이너리에 실제로 포함되지 않습니다. 이는 개발 프로세스의 중요한 단계를 보여줍니다.

사용자 정의 포맷 테스트 (개념적)

이 마지막 단계에서는 새로 컴파일된 John the Ripper 실행 파일로 사용자 정의 포맷을 테스트하는 방법을 논의합니다.

컴파일된 john 바이너리는 ~/project/john/run/ 디렉터리에 있습니다. 해당 디렉터리로 이동해 봅시다.

cd ~/project/john/run

컴파일된 JtR 버전이 지원하는 모든 포맷 목록을 볼 수 있습니다.

./john --list=formats

목록을 스크롤하십시오. labex-md5 포맷이 존재하지 않음을 알 수 있습니다. 이는 이전 단계에서 언급했듯이 빌드 파일을 수정하여 포함시키지 않았기 때문에 예상된 결과입니다.

이제 샘플 해시 파일과 단어 목록을 만들어 성공적으로 컴파일되었다면 해당 포맷을 어떻게 사용할지 살펴보겠습니다.

먼저 프로젝트의 루트 디렉터리에 hashes.txt라는 파일을 만들고 사용자 정의 포맷과 일치하는 해시를 포함시킵니다. 해시 87e4e494b2399b0921d44e03693518f9는 비밀번호 "labex"의 MD5 해시입니다.

echo "labex_md5$87e4e494b2399b0921d44e03693518f9" > ~/project/hashes.txt

다음으로 올바른 비밀번호를 포함하는 간단한 단어 목록을 만듭니다.

echo "labex" > ~/project/wordlist.txt

마지막으로 사용자 정의 포맷을 사용하여 해시를 크랙하기 위해 실행할 명령은 다음과 같습니다.

./john --format=labex-md5 --wordlist=~/project/wordlist.txt ~/project/hashes.txt

이 명령을 실행하면 포맷이 알려지지 않았다는 오류가 보고되어 컴파일 프로세스에 대한 이해를 확인할 수 있습니다.

Unknown format name: "labex-md5"

이로써 사용자 정의 JtR 포맷을 생성하고 테스트하는 개념적 과정을 마칩니다.

요약

이 실습에서는 John the Ripper 를 위한 사용자 정의 포맷 생성의 개념적 프로세스를 탐구했습니다. JtR 의 기능을 확장하는 데 관련된 주요 단계에 대한 기초적인 이해를 얻었습니다.

다음과 같은 내용을 학습했습니다.

  • 모든 JtR 포맷을 정의하는 핵심 struct fmt_main 구조체.
  • JtR 소스 코드에서 기존 포맷 정의를 찾고 검사하는 방법.
  • 기본적인 valid() 함수를 포함하는 새 포맷 소스 파일 생성 프로세스.
  • ./configuremake를 사용하여 JtR 을 컴파일하는 표준 절차.
  • 새 포맷을 포함하기 위해 빌드 파일을 수정하는 중요하고 (의도적으로 생략된) 단계.
  • --format 플래그와 샘플 해시 파일을 사용하여 포맷을 테스트하는 방법.

이 실습에서는 완전히 작동하는 사용자 정의 포맷을 만들지는 못했지만, 고유한 비밀번호 해싱 스킴을 처리하기 위한 자체 포맷 개발에 필요한 필수 지식과 명확한 로드맵을 제공했습니다.