본문 바로가기

프로젝트/DVWA 실습

DVWA 실습 #2-1 - Brute Force(low)

2021/01/01 - [프로젝트/DVWA 실습] - DVWA 실습 #2 - Brute Force

 

DVWA 실습 #2 - Brute Force

DVWA의 첫 번째 실습 대상인 Brute Force다. 브루트 포스는 무작위로 비밀번호를 조합하거나 사전 파일에 정의된 예시 패스워드를 하나하나 대입해보며 공격하는 방식(또는 두 가지를 합한 hybrid 방

haruhiism.tistory.com

문제 해결 방법

지난 포스트에서 분석한 소스 코드처럼 Low 단계에서는 브루트 포스에 대한 어떤 방어 체계도 갖춰져있지 않으며 SQL Injection에 대한 입력 필터링도 없기 때문에 쉽게 특정 계정에 접속할 수 있다. 현재 admin 계정에 접속하려면 "admin'#"처럼 입력해주면 비밀번호를 입력하지 않고도 로그인할 수 있지만 Brute Force를 실습하는 문제인 만큼 관련 툴을 사용하여 실습해보겠다.

OWASP Zap - Fuzzer

OWASP ZAP의 확장 프로그램인 Fuzzer를 사용하면 특정 위치에 무작위 값을 대입해서 여러 번 입력을 시도할 수 있다. 로그인에 실패했을 때는 아이디나 비밀번호나 틀리다는 메시지가 웹사이트에 나타나기 때문에 로그인에 성공했을 때의 웹사이트와 비교하면 HTML 소스 코드의 길이가 다를 것이다. 이를 기반으로 무작위 값에 대한 응답으로 다른 요청들과 다른 크기를 가지는 응답이 발생했을 경우 로그인에 성공한 것으로 추측해보는 것이다.

먼저 OWASP ZAP에서 잡은 로그인 시도 URL에 대해 Fuzz 공격을 클릭한다. Fuzzer가 설치되어 있지 않다면 Help - Check for Updates - Marketplace에서 관련 애드온을 설치해줘야 한다.

Fuzzer 창에서 다음과 같이 password 파라미터 부분에 퍼징 페이로드를 추가한다. OWASP ZAP의 Fuzzer는 문자열을 조합하여 공격하는 방법은 제공하지 않고 주어진 패스워드 파일에서 일일이 대입하는 사전 공격 방식을 제공한다. 사전 파일은 이런 곳에서 찾을 수 있다. 이렇게 페이로드를 구성하고 Start Fuzzer 버튼을 눌러서 퍼징을 시작하면 다음과 같이 순식간에 수십개의 패스워드를 시도해 보는 것을 볼 수 있다.

이 중에서 아까 언급했듯이 HTTP 응답 메시지의 바디의 길이, 즉 로그인 후 구성되는 웹사이트의 크기가 다른 요청을 살펴보기 위해 정렬하면 다음과 같이 다른 요청과 다른 한 요청을 볼 수 있다.

이때 전송된 페이로드는 password라과 한다. 즉 admin 계정의 비밀번호는 password 임을 의미하는 것이며 이를 이용해 직접 로그인해보면 다음과 같이 로그인을 성공하는 것을 볼 수 있다.

이전에 "Username and/or password incorrect" 문자열이 나타났던 것과 달리 "Welcome to the password protected area admin"이란 문자열이 나타난 것을 볼 수 있다. 그렇기 때문에 Size Resp. Body 필드 값이 5,002 바이트로 기존의 4,943 바이트보다 약 60바이트 증가했기 때문에 이를 로그인 성공의 징표로 볼 수 있는 것이다.

Burp Suite - Intruder

Burp Suite Community Edition에서도 제공되는 Intruder 기능을 사용하면 브루트 포스를 수행할 수 있다. 아쉬운 점은 OWASP ZAP과 달리 무료 버전에는 지연시간이 조금 있기 때문에 공격 속도가 좀 더 느리다.

먼저 프록시를 켜고 Intercept에서 로그인 시도 패킷을 캡처한다. 그리고 Action 메뉴에서 Send to Intruder 옵션을 선택하여 Intruder로 해당 HTTP Request를 전송한다.

Intruder에서는 Target, Positions, Payloads, Options 속성을 설정해서 공격을 시작할 수 있다. Target 속성에서는 공격 대상 호스트와 포트 번호, HTTPS 옵션을 설정할 수 있다. 현재는 특별히 변경할 사항이 없다.

Positions 속성에서는 Intruder에서 브루트 포스 할 파라미터 위치를 설정한다. 기본적으로는 모든 변수 부분(username, password, Login, security, PHPSESSID 등)에 자동으로 페이로드가 설정되어 있으나 지금 브루트 포스 해야 하는 곳은 password 파라미터뿐이기 때문에 오른쪽의 Clear 버튼을 눌러서 모든 페이로드를 지우고 password=12345의 "12345" 부분을 드래그, Add 버튼을 눌러서 위처럼 해당 부분에만 페이로드를 추가해 준다.

 

현재 선택한 Attack type인 Sniper는 설정된 모든 페이로드 위치에 대하여 각각 페이로드를 삽입해서 요청을 보낸다. 그렇기 때문에 페이로드의 수나 페이로드가 삽입될 위치의 수가 증가할수록 소요되는 시간이 증가하지만 현재는 password 파라미터에만 삽입하고 있기 때문에 시간을 절약할 수 있다. 이외에도 모든 페이로드 위치에 동일한 페이로드를 동시에 삽입하는 Battering ram, 각 페이로드 위치마다 지정된 페이로드 리스트의 페이로드를 삽입하는 Pitchfork, 모든 페이로드 리스트를 모든 페이로드 위치에 삽입하는 Cluster bomb 공격 방식이 있다. 자세한 것은 이 문서를 참고하자.

 

Payload positions

This tab is used to configure the request template for the attack, together with payload markers, and the attack type (which determines the way in which ...

portswigger.net

그리고 Paylods 속성에서 Payload type을 Brute forcer로 선택해준다. 그러면 아래 Payload Options에 브루트 포스 전용 페이로드 옵션이 생성되는데 최소, 최대 길이와 캐릭터 셋(사용할 문자들의 집합)이 주어진다. 만약 공격을 시도하려는 웹사이트의 비밀번호가 특수문자를 받아들이지 않는다면 위와 같이 사용할 수 있지만 특수 문자를 받는다면 Character set에 '!', '#', '@' 같은 특수 문자를 추가해야 한다. 이외에도 Min, Max length를 조정하다 보면 실시간으로 늘어나는 Payload count를 볼 수 있는데 이는 모든 조합을 시도해보려면 이만큼 걸린다는 뜻이다.

설정이 완료되었으면 Start attack 버튼을 눌러 브루트 포스 공격을 수행해볼 수 있다. 하지만 언급했듯이 Burp Suite의 무료 버전인 Community Edition에서는 Intruder 공격에 약간의 시간 지연이 있으며 위의 OWASP ZAP에서 했던 것처럼 가능성 있는 비밀번호 리스트가 아닌 정말 바닥부터 하나하나 비교해보는 특성상 시간이 굉장히 오래 걸린다. 이 정도 속도라면 admin 계정의 비밀번호였던 password까지 도달하는데 몇 시간은 걸릴 것이다. 그러므로 Payload type을 Brute forcer 대신 Simple list로 설정해서 OWASP ZAP의 Fuzzer처럼 비밀번호 리스트를 기반으로 브루트 포스 공격을 수행하도록 한다.

이전처럼 응답 길이(Length)를 비교한 결과 5352 바이트라는 다른 값을 받는 요청을 확인할 수 있었고 이 요청의 페이로드를 조사한 결과 비밀번호를 얻을 수 있었다.

Hydra

또다른 방법으로는 Hydra라는 툴을 사용해볼 수 있다. 이는 Kali Linux에 기본적으로 설치된 아이디/패스워드 추측 툴로 이를 활용하여 아이디, 비밀번호 파라미터를 바꿔가면서 브루트 포스 할 수 있다.

사용 예시 및 옵션은 다음과 같다.

hydra -l USER -P PASSWORD.lst ftp://192.168.0.1
  • -l / -L: 파라미터로 설정된 문자열을 아이디로 시도해보거나(l) 파라미터로 넘겨준 아이디 리스트에서 하나씩 꺼낸 아이디로 시도한다(L).

  • -p / -P: 파라미터로 설정된 문자열을 비밀번호로 시도해보거나(p), 파라미터로 넘겨준 비밀번호 리스트에서 하나씩 꺼낸 비밀번호로 시도한다(P).

  • -C: -l, -p 옵션 대신 "admin:12345"처럼 ":"로 구분된 아이디, 비밀번호로 시도한다.

  • -M: 공격 대상 서버를 한 줄에 하나씩 기재한다. 각 호스트에 ":"로 포트 번호를 지정한다.

  • -s: 포트 번호를 지정한다.

  • -S: SSL 연결을 사용한다.

현재 DVWA 서버는 192.168.26.201에 위치하고 있으며 GET 메서드로 로그인하기 때문에 공격 명령어는 다음과 같다.

hydra -l admin -P ./10-million-password-list-top-1000000.txt 192.168.26.201 http-get-form
"/dvwa/vulnerabilities/brute/:username=^USER^&password=^PASS^&Login=Login#:Username and/or password incorrect."

위의 예시에 비해 공격 코드가 굉장히 길어졌는데 각 파라미터는 다음과 같은 의미를 가진다.

  • -l admin: username은 admin으로 시도한다.

  • -P ./10-million-password-list-top-1000000.txt: password는 10-million-password-list-top-1000000.txt 리스트에서 하나씩 시도한다.

  • 192.168.26.201: 공격 대상 호스트는 192.168.26.201이다.

  • http-get-form: GET 메서드를 이용하여 HTTP Request를 전송하고 있다.

  • "/dvwa/vulnerabilities/brute/:username=^USER^&password=^PASS^&Login=Login#:Username and/or password incorrect.": 호스트의 /dvwa/vulnerabilities/brute/ 웹사이트에 "username=^USER^&password=^PASS^&Login=Login" 파라미터로 요청하여 브루트 포스 공격한다. ^USER^는 -l, -L 옵션으로 지정된 username이며 ^PASS^는 -p, -P 옵션으로 지정된 password다. 공격이 실패했음을 알리는 메시지는 "Username and/or password incorrect."다. 이를 이용하여 공격 성공, 실패 여부를 파악한다.

위 명령어로 공격하면 다음과 같은 결과를 얻을 수 있다.

엄청 느리던 Burp Suite의 Intruder나 그나마 빠르던 OWASP ZAP의 Fuzzer와 달리 Hydra는 1초도 안돼서  16개의 패스워드를 찾아낸 것을 볼 수 있다. 물론 실습 환경 특성상 DVWA 서버와 같은 컴퓨터에서 시도했기 때문에 당연히 제일 빠르겠지만 이를 감안하고서라도 매우 빠른 속도를 보여주었다. 16개의 비밀번호 중 아까 찾았던 password 비밀번호가 있는 것으로 보아 브루트 포스 공격이 성공했음을 알 수 있다.

 

현재는 GET 메서드로 로그인하고 있지만 HTML 폼을 이용하여 POST 메서드로 로그인하는 경우 http-post-form 같은 옵션을 적용해야 한다. 자세한 정보는 이 글을 참고하자.

 

How to brute force http forms in windows | Silly Chicken

This will give you an idea on how to brute force http forms with THC-Hydra This is a continuation from How to brute force your router so if you haven’t read it check it out !!! Watch the video for a live example. Video best viewed in full screen mode IM

www.sillychicken.co.nz

Python requests 모듈

Python의 requests 모듈을 이용하여 반복문을 이용해 GET이나 POST 요청을 보냄으로써 브루트 포스를 수행해볼 수도 있다. 간단한 코드는 다음과 같다.

import requests

URL="http://192.168.26.201/dvwa/vulnerabilities/brute/"
cookies = {'PHPSESSID': 'INSERT_YOUR_COOKIE_HERE',
           'security': 'low'}

params = {'username':'',
          'password':'',
          'Login':'Login'}

with open('10-million-password-list-top-1000000.txt') as f:
    passwords = f.readlines()

passwords = [x.strip() for x in passwords]

for password in passwords:    
    params['username'] = 'admin'
    params['password'] = password

    print("Trying password {}...".format(password))
    res = requests.get(URL, params=params, cookies=cookies)
    if "Username and/or password incorrect." not in res.text:
        print("\n\nPassword found!: {}".format(password))
        break

실행 결과는 다음과 같다.

브루트 포스를 통해 비밀번호를 찾을 수 있었다.

 

 

이처럼 다양한 방법으로 가능한 브루트 포스 공격이지만 웹 애플리케이션 작성 시 보안 대책을 조금만 고려해도 쉽게 막히고 특히 최근에는 일정 로그인 시도 이후 계정 로그인 자체가 아예 잠기는 시스템을 적용하면서 이런 브루트 포스가 통하는 곳은 거의 없다고 봐도 좋을 것이다. 하지만 Burp Suite, OWASP ZAP이나 Hydra 등 다양한 툴을 사용해보는 계기로 한번 이렇게 실습해보았다.