BruteForce Attack??

By rls1004 | November 2, 2016

BruteForce Attack이란 무엇일까??




BruteForce란?


Brute Force Attack 은 답이 맞을 때까지 때려박는 기법입니다.



이 공격 방법은 가능한 모든 경우의 수를 대입하며 성공할 때까지 반복하는 “무식한 공격”인데요,
무식하고 단순한 방법이라 저는 Brute Force로 문제를 푸는 것을 싫어했습니다.

하지만 암호학을 공부하거나 CTF 문제를 풀 때 Brute Force 를 사용할 일이 생겼고,
“이게 맞을까?” 하는 불안감과 “이거 아니면 모르겠다”싶은 마음으로 exploit 코드를 돌려놓고 다른 일을 하다가 정답을 찾아냈을 때는 묘한 쾌감을 느낄 수 있었습니다.

코드가 맞게 짜여진 것인지 확신할 수 없는 상황에서 내 코드를 믿고 Brute Force를 시도하고,
한참 후에 정상적으로 flag를 읽어온 것을 확인했을 때는 안도감과 함께 exploit 코드가 대견해집니다.

그래서 이번에 Brute Force Attack 은 무식하기만 한 기법인가에 대해 쓰려고 합니다.




Brute Force 해보기


일단 Brute Force 를 한 번 사용해볼까요?


랜덤 스택 공격하기 – Layer7 CTF : PWN. ECHO_SYSTEM


스택의 주소가 랜덤하게 변하는 환경에서 스택의 주소를 사용해야 하는 경우, 브루트 포스 공격을 할 수 있습니다.
2016년 Layer7 CTF에 출제되었던 PWNABLE 문제 중 ECHO_SYSTEM 문제로 알아보겠습니다.



buf 변수는 ebp-0x88 에 위치하고 있지만 read 함수를 통해 최대 0xC8 바이트의 입력 값을 넣을 수 있습니다.



또한 친절하게도 프로그램의 내부에 cat_flag라는, flag를 출력시켜주는 함수가 존재합니다.

BOF가 발생하므로 이를 이용해 RET를 cat_flag 함수의 주소로 변조하면 되는데요,



main 함수의 초반부를 보면 우리의 exploit을 방해할 ecx 레지스터가 등장합니다.
[esp+4]라는 스택 주소를 ecx 레지스터가 가리키고 있고, ebp 레지스터를 push한 후, 곧 바로 ecx 레지스터가 push 됩니다.



끝날 때도 leave, ret이 아닙니다.
[ebp-4]의 값(초반에 push한 ecx 레지스터 값)을 다시 ecx 레지스터로 불러오고, [ecx-4]가 가리키는 주소로 esp 레지스터를 이동시킵니다.

여기서 ecx 레지스터의 값이 갖는 의미는 [RET가 담겨있는 스택 + 4]의 주소입니다.
입력 데이터를 받는 buf 변수에 뛰고 싶은 주소인 cat_flag 함수의 주소(0x080484A2)를 적고,
BOF를 이용하여 ecx 레지스터에는 [buf 변수의 주소 + 4]의 값을 써주면 됩니다.

buf 변수는 스택 영역에 존재하는데,



print 함수의 내부 구현 모습을 보면 *(buf+n)의 값이 널(0x00)일 때까지 계속 출력합니다.
ecx의 원래 값은 스택의 어딘가를 가리키고 있을테니, ebp-0x4 까지 데이터를 입력하여 스택의 주소를 leak 시킬 수 있습니다.


$ python bb.py
[*] echo system !!
input :
result :
0xffe4c6b0

$ python bb.py
[*] echo system !!
input :
result :
0xffe7f9c0

$ python bb.py
[*] echo system !!
input :
result :
0xffdcf760


음?
스택의 주소가 계속 바뀝니다.

스택의 주소를 정확히 알 수 없으니 buf 변수에 cat_flag 함수의 주소를 33번(총 0x84 바이트) 스프레이 한 후,
ecx 레지스터 위치에는 알아낸 스택의 주소 중 임의의 하나를 넣어 Brute Force 공격을 합니다!


while true; do
    python exploit.py >> list;
done


얻어걸릴 때까지…


cat list | grep flag
flag_{v3ry_s1mple_3cho_syst3m!!}
flag_{v3ry_s1mple_3cho_syst3m!!}


그러면 언젠가 성공합니다!



스택의 특정 영역을 가리키고 있고, 랜덤으로 바뀌는 스택이 마침 맞아떨어질 때까지 exploit을 반복하는 방법입니다.



암호 공격하기 – Plaid CTF : CRYPTO. rabit


아무리 튼튼한 알고리즘이라 하더라도 짧은 키 길이가 문제가 되는 경우가 많은데요,
짧은 키 길이를 사용할 경우는 키를 브루트 포싱하여 알아내기 쉽기 때문입니다.

여기서는 해쉬 값을 맞춰주는 브루트 포싱을 해보도록 하겠습니다.

문제는 2016년 Plaid CTF에 출제되었던 CRYPTO 문제 중 rabit 문제로 알아보겠습니다.



문제가 복잡하기 때문에 전체를 풀이하진 않고 필요한 부분에 대해서만 보겠습니다.
rabit.py, util.py 가 주어지는데 rabit.py 파일 중 제일 먼저 거치게되는 proof_of_work 함수입니다.
이 함수는 숫자와 문자 중 랜덤하게 10 글자를 뽑아 클라이언트에게 전송해줍니다.
그 후 클라이언트는 최대 15글자를 입력할 수 있는데, 서버가 전송해준 10 글자로 시작하지 않거나 SHA1 함수로 암호화한 결과가 0xffffff으로 끝나지 않는다면 프로그램이 종료됩니다.

다시 말하자면, “(서버가 생성한 랜덤 10글자)+(사용자의 입력 5글자 이하)”의 sha1 해쉬값 마지막 3byte 가 0xffffff 여야 합니다.

이 해쉬값은 브루트포스 공격을 통해 맞춰줄 수 있습니다.
10글자는 서버가 정해주는 값이니 5글자만 브루트 포싱하면 되는데요, 5중 for 문을 돌려야 할까요?
여기서 유용하게 사용할 수 있는 파이썬 모듈이 있습니다.
바로, itertools 라는 모듈인데요, 여러가지 반복자에 대한 기능을 제공하고 있습니다.


itertools 모듈 자세히 살펴보기


그 중에 product 라는 함수는 특정 문자열에서 ‘중복을 허용하여’ repeat 갯수 만큼의 문자를 골라 조합하는 모든 경우를 생성합니다.
이 함수를 사용하여 5 글자를 브루트포싱해봅시다!


def ff_hash(prefix):
    letters = string.printable
    for c in itertools.product(letters, repeat=5):
            response = "".join(c)
            if sha1(prefix+response).digest()[-3:] != "\xff"*3:
                    continue
            return prefix+response


브루트 포싱 결과, “8PNgoa05di” 라는 10 글자가 주어졌을 때 여기에 “01S[f” 라는 5 글자를 덧 붙이면 ff78acaa3617e2ba3419fbe8cb80bdbabaffffff 라는 SHA1 해쉬 값이 생성되는 것을 찾아낼 수 있습니다.




패스워드 공격하기 – ZIP Password Recovery



zip 파일을 압축할 때 암호를 설정할 수 있습니다.
이 zip 파일의 패스워드를 해제하는 프로그램도 존재하는데요,



Advanced ZIP Password Recovery 라는 툴입니다. “1234”라는 패스워드를 찾아낼 수 있었습니다. 기능을 좀 더 살펴볼까요?



브루트 포스 할 범위를 선택할 수 있습니다.
브루트 포스는 모든 경우의 수를 대입해보는 무식한 방법으로 알고 있지만 시간을 좀 더 단축하기 위해, 알고 있는 단서들을 사용하여 범위를 줄일 수 있습니다.
패스워드가 숫자만으로 이루어져있다면 “All digits (0 – 9)” 에만 체크하고, 알파벳 대소문자 / 숫자 / 특수문자가 들어간다면 그에 해당하는 4가지 항목들을 체크해주면 됩니다.



브루트 포스 할 길이의 범위를 지정할 수 있습니다.
패스워드의 규칙 중 몇 글자 이상, 몇 글자 이하라는 규칙이 있다면 그에 맞게 범위를 지정함으로써 경우의 수를 줄일 수 있습니다.



다음은 dictionary 옵션인데요, “password”와 같이 사람들이 자주 사용하는 패스워드들을 사전처럼 등록해놓고 조금씩 변형하기도 하며 차례로 대입하는 방법입니다.

길이도 알 수 없고 엄청나게 많은 경우의 수가 존재하는 패스워드에서, 길이나 문자의 범위를 알게 된다면 그 경우의 수를 엄청나게 줄일 수 있고, 공격 시간도 조금이나마 단축 시킬 수 있습니다.
이것은 패스워드 뿐만 아니라 다른 브루트 포스 공격을 시도할 때도, 어떤 조건을 가지고 있는지 알아낸다면 공격의 효율을 높일 수 있습니다.




실제 사례


실제로 브루트 포스 공격을 사용하여 해킹당한 사례가 있을까요?


Find My iPhone


2014년, 아이클라우드(iCloud)가 해킹당한 사건이 있었습니다.
아이폰에는 Find My iPhone 이라고 하는, 핸드폰을 찾아주는 기능이 있습니다.
이 기능은 아이폰이나 아이패드를 분실 했을 경우, GPS로 위치를 파악하여 웹을 통해 위치를 알려주거나 분실한 기기를 원격으로 잠그거나 데이터를 삭제, 습득자에게 메시지 전송 등을 할 수 있습니다.

이 기능을 이용하기 위해서는 아이클라우드 아이디로 로그인을 해야하는데,
이때 로그인을 여러번 실패해도 아무런 조치를 하지 않아 브루트 포스 공격이 가능했습니다.

공격자는 이 취약점을 이용하여 유명 스타들의 누드 사진을 유출하기도 하고 금전적 이득을 취하기도 했습니다.


POC 코드 보러가기




KT 개인정보 유출 사고


2014년, KT 개인정보 유출 사고로 인해 1천 만건 이상의 정보가 유출되었습니다.
공격자는 프록시 프로그램과 브루트 포스 공격을 사용했는데,
KT 홈페이지의 이용대금 조회란에 고유 숫자 9개를 브루트 포스하여 맞춰내고 정보를 빼내고 이를 이용하여 115억으로 추정되는 부당이득을 취한 것으로 알려졌습니다.




인스타그램 브루트 포스 취약점 발견


2015년 12월과 2016년 2월에 발견된 취약점 입니다.
첫 번째로 발견된 취약점은 안드로이드 인스타그램 앱에서 반복된 로그인 시도가 가능한 취약점이고, 두 번째로 발견되 취약점은 인스타그램 웹 사이트에서 발견되었는데, 계정 등록시 서버로 가는 요청 메시지를 캡쳐한 후 복제하여 다시 전송하고, 이때 돌아오는 메시지(패스워드가 맞다면 이미 사용중이라는 메시지, 틀리다면 실패했다는 메시지)를 확인하여 패스워드를 무한대로 추측할 수 있는 취약점입니다.




마무리


브루트 포스 공격은 단순하고 무식한(?) 방법이고, 잘못된 인증 시도에 대해 패널티를 주는 등의 대응 방안도 나와있습니다. 하지만 이런 기본적인 보안조치를 취하지 않는 경우도 의외로 많이 발견되어 왔습니다.

또한, 무식하다고만 생각했지만 조건을 이용하여 경우의 수를 줄여나가는 등 여러가지 전략을 섞어 더욱 효율적인 브루트 포스 공격이 가능합니다.

브루트 포스 공격은 느리지만 확실한 공격 기법이며, 얼마나 똑똑하게 경우의 수를 줄여나갈 것인지도 중요한 것 같습니다.

comments powered by Disqus