파이썬 실전/브루트 포스

파이썬 실전) 브루트 포스 프로그램 1. for문을 이용한 브루트 포스

마리사라 2021. 3. 15. 16:59
반응형

이번 강의는 브루트 포스의 방법으로써, 파이썬에서 기본 지원하는 for문을 이용하여 브루트 포스를 해보려고 합니다.


1. 기초

파이썬의 for문에서 브루트 포스를 하려면 모든 경우의 수를 만들 수 있어야 합니다. 이러한 경우의 수를 만들어 주는 모듈이 바로 intertools 모듈의 product라는 함수입니다.

 

from itertools import product

itertools 모듈에는 여러 조합을 만드는 방법이 있지만, 브루트 포스 프로그램에서는 product를 사용해서 만들 예정입니다.

 

product 모듈의 사용 방법은 다음과 같습니다.

list = product(A, repeat=B)

A는 조합을 만들 내용을 입력하며, B는 자릿수를 입력합니다. product는 기본적으로 여러 리스트들의 조합을 만들기 위해 사용되지만, 문자열을 입력할 경우, 문자열의 원소 하나하나를 인식하여 조합의 재료로 사용합니다. 또한 product의 특징으로는 product는 단순한 연산 명령에 불과하기 때문에 저장하지 않으면 바로 내용이 날아갑니다.


2. 실전

for문의 시간복잡도는 O(n)입니다. 그렇기 때문에 n이 커지면 커질수록 선형적으로 걸리는 시간이 증가하며, 파이썬의 속도로는 감당하기 힘든 수준으로 늘어날 수 있습니다.

 

from itertools import product

우선 product 모듈을 import 해줍니다.

 

number = "0123456789"
lowercase = "abcdefghijklmnopqrstuvwxyz"
uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
symbol = "!@#$%^&*()_+-=`~"

이제 비밀번호로 사용 가능한 모든 방법을 준비합니다. 이 모든 것을 사용하는 것이 아닌, 필요할 때마다 교체해서 사용할 수 있도록 준비했습니다. 준비한 것은 숫자, 소문자, 대문자, 특수문자의 4종류입니다.

 

possibility = lowercase + number

이제 조합에 사용될 문자열을 만들 차례입니다. 이것은 사이트에 가입할 때, 소문자 + 숫자만 입력해도 되는지, 소문자 + 숫자 + 특수문자까지 입력해야 되는지에 따라서 조절하시면 됩니다.

 

password = "world1"

이제 예시 비밀번호를 설정합니다. 과거 비밀번호는 최소 6자리의 소문자 + 숫자만을 요구했었습니다. 위와 같은 비밀번호는 현재는 사용되지 않으니 6자리의 소문자 + 숫자가 얼마나 위험한지만 알아 두시면 됩니다.

 

attempt = product(possibility, repeat=len(password))

이제 product를 이용해서 가능한 모든 경우의 수를 만들어 냅니다. 이때 비밀번호의 자릿수는 알고 있다고 가정하겠습니다. 만약 비밀번호의 자릿수를 모른다고 가정하면 중첩 for문을 이용하여 1자리부터 자릿수를 올려나갈 수 있습니다.

 

import time
start = time.time()

마지막으로 프로그램이 시작한 시간을 기록하기 위해 time모듈의 time함수를 사용합니다. time함수는 현재 시간을 기록하는 함수입니다.

 

def run():
    for i in attempt:
        if "".join(i) == password:
            print("비밀번호 : " + str("".join(i)))
            print("소요시간 : " + str(time.time() - start))

이제 비밀번호를 풀어내기 위한 브루트 포스 함수를 만듭니다. product로 만들어진 결과값은 list자료형으로 저장되기 때문에 join함수를 이용하여 문자열 자료형으로 변환하여야 비교가 가능합니다.

 

이제 프로그램을 실행해 보겠습니다.

브루트 포스 프로그램 실행 결과

6자리의 비밀번호를 풀어내는데 대략 237초의 시간이 걸렸습니다. 평균적으로 C언어가 파이썬에 비해 150배 정도 빠르니, C언어에서 같은 연산을 실행한다면 대략 2초 만에 풀린다는 계산입니다. 즉, 6자리는 단순한 for문으로도 풀리는 보안이 매우 취약한 비밀번호입니다.

 

만약 비밀번호를 7자리 이상으로 하시면 -1 에러코드를 뿜으며 프로그램이 종료될 수도 있습니다. 이것은 너무 오랬동안 프로그램이 실행되어서 그렇기 때문에, for문의 한계라고 생각하시면 됩니다.


3. 마치며

파이썬에서 브루트 포스를 하는것은 추천드리지 않는 행동입니다. 다른 언어보다 느린 파이썬으로 브루트 포스를 하는 것은 시간이 그만큼 오래 걸리기 때문에, 기본적인 원리를 이해하시고 다른 언어에서 위와 같은 원리로 적용하시는 것이 좋습니다.

 

import time
from itertools import product


password = "world1"
number = "0123456789"
lowercase = "abcdefghijklmnopqrstuvwxyz"
uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
symbol = "!@#$%^&*()_+-=`~"
possibility = lowercase + number
attempt = product(possibility, repeat=len(password))
start = time.time()


def run():
    for i in attempt:
        if "".join(i) == password:
            print("비밀번호 : " + str("".join(i)))
            print("소요시간 : " + str(time.time() - start))


run()
반응형