Bruteforce Attack
Bruteforce 공격(무차별 대입 공격)
개요
이번 1주차 Writeup 에서는 Bruteforce 공격(무차별 대입 공격)에 대한 공부 및 실습을 진행해보았다. 아래 설명하는 모든 공부 및 실습은 VirtualBox - Kali Linux 가상머신에서 DVWA 취약한 웹 환경을 구성하여 이루어졌다.
목차
이번 Writeup 에서 진행하는Bruteforce 공격과 관련된 공부는 다음과 같다.
- 취약점 설명 - Bruteforce 공격에 대한 연구
- 개념 증명 실습 - Burpsuite Intruder, Hydra 툴을 활용한 DVWA Security Level(Low, Midium, High)로 구성된 실습 진행
- 대응방안 - Bruteforce 공격에 대한 대응 방안
- 레퍼런스
취약점 설명
Bruteforce 공격이란? Bruteforce 공격은 무차별 대입 공격이라고도 하며, 사용자의 비밀번호, PIN번호, 암호화키 등을 무작위로 계속 대입함으로써 해킹을 시도하는 공격 방식이다. 웹 서버/웹 어플리케이션을 대상으로 하는 경우 기본 유저 이름(default username)을 알아낸 뒤 로그인이 가능할 때 까지 비밀번호를 무작위로 대입하는 방식으로 많이 이루어진다. 공격자는 Bruteforce 공격을 통해 사용자의 자격 증명을 찾아낸 뒤 해당 사용자로 시스템에 접근하여 계정을 장악하거나 추가 권한 상승을 시도할 수 있다. 찾아낸 자격 증명이 관리자인 경우에는 웹 어플리케이션 전체 장악까지 이루어져 큰 피해를 입을 수 있다.
다양한 종류의 bruteforce가 존재하지만 여기에서는 사전 공격을 통한 bruteforce 실습을 진행해보았다.
개념 증명
* Low Level
Burpsuite Intruder
먼저 공격자는 admin 계정을 알고 있다고 가정한 뒤, 비밀번호에 대한 사전 대입 공격을 진행한다. Burpsuite을 통해 Proxy 탭에서 사용자가 인증 할 때 HTTP 요청을 가로챈 뒤, 이를 Intruder 탭으로 넘겨보았다. 이를 위해 password= 파라미터에 $ 기호를 넣어 해당 파라미터만 사전 대입 공격을 할 수 있도록 하였다.
먼저 gedit /usr/share/john/password.lst
입력하여 칼리리눅스에 있는 패스워드 리스트를 확인할 수 있었다.
password.lst 파일 안에는 1996년 부터 2011년 까지 사용자들이 자주 사용하는 패스워드들이 담겨 있다. 현재는 온라인에 다양한 유추 가능한 패스워드들이 존재하니 그 것을 사용해도 좋다. 성공적인 실습을 위해 password
가 있는지 확인 후에 공격을 수행하였다.
admin 계정에 유효한 패스워를 통해 로그인에 성공할 경우 HTTP 응답의 크기가 4704가 되는 것을 확인할 수 있었다. Payloads를 보니 응답값으로 'Welcome to the password protected area admin'
이라는 문구를 출력하는 것을 볼 수 있었다.
-
bruteforce 공격에 실패하였을 경우
-
bruteforce 공격에 성공하였을 경우
공격자가 알아낸 패스워드로 로그인을 시도하였더니, 성공적으로 사용자의 계정을 탈취하여 로그인에 성공하는 것 까지 확인할 수 있었다.
- 소스코드(Low)
<?php
if( isset( $_GET[ 'Login' ] ) ) {
// Get username
$user = $_GET[ 'username' ];
// Get password
$pass = $_GET[ 'password' ];
$pass = md5( $pass );
// Check the database
$query = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
if( $result && mysqli_num_rows( $result ) == 1 ) {
// Get users details
$row = mysqli_fetch_assoc( $result );
$avatar = $row["avatar"];
// Login successful
echo "<p>Welcome to the password protected area {$user}</p>";
echo "<img src=\"{$avatar}\" />";
}
else {
// Login failed
echo "<pre><br />Username and/or password incorrect.</pre>";
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>
위 소스코드에서 Bruteforce 공격을 감지하거나 대응할 수 있는 코드가 확인 되지 않았다. Bruteforce 공격에 대응할 시큐어코딩이 필요해 보인다.
Hydra
이번에는 Hydra 툴을 사용하여 Bruteforce 공격을 시도해보았다. Hydra는 Dictionary 공격이나 Bruteforce와 같은 공격을 수행할 때 사용하는 툴로 여러 개의 프로토콜들을 지원한다.
먼저 위 처럼 공격자가 사용자 권한을 획득하고 서비스를 활용 할 수 있도록 쿠키값을 탈취하였다.
탈취한 쿠키값으로 검증을 거치지 않고 사용자로 위장하여 admin 계정의 패스워드가 나올 때 까지 하나씩 대입하여 공격을 시도해보았다.
hydra -l admin -P 10kpassword.txt 127.0.0.1 http-get-form '/vulnerabilities/brute/:username=^USER^&password=^PASS^&Login=Login:H=Cookie:PHPSESSID=1kjidhie03fm37j915g8le23m5; security=low:F=Username and/or password incorrect'
공격 결과 admin 계정의 패스워드를 탈취할 수 있었다.
* Midium Level
위와 같이 취약한 웹 환경에서 Bruteforce 공격에 시도했을 때, password가 틀린 경우에는 'Username and/or password incorrect.'
문구를 확인할 수 있다.
Midium Level 에서는 Bruteforce 공격 응답이 느리게 나오는 것을 파악할 수 있다.
<?php
if( isset( $_GET[ 'Login' ] ) ) {
// Sanitise username input
$user = $_GET[ 'username' ];
$user = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $user ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// Sanitise password input
$pass = $_GET[ 'password' ];
$pass = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$pass = md5( $pass );
// Check the database
$query = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
if( $result && mysqli_num_rows( $result ) == 1 ) {
// Get users details
$row = mysqli_fetch_assoc( $result );
$avatar = $row["avatar"];
// Login successful
echo "<p>Welcome to the password protected area {$user}</p>";
echo "<img src=\"{$avatar}\" />";
}
else {
// Login failed
sleep( 2 );
echo "<pre><br />Username and/or password incorrect.</pre>";
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>
위 소스 코드를 분석한 결과 로그인에 실패했을 경우에는 sleep(2);
함수를 통해 2초간 재로그인을 지연시키고 있다. 이 같은 방법으로 bruteforce 공격 진행을 지연시킬 수 있는 시큐어코딩이 되어 있지만, 해당하는 패스워드가 리스트에 상위에 있을 경우에는 여전히 사용자 권한을 빠르게 탈취할 수 있어 안심할 수 없다.
* High Level
High Level에서도 로그인을 시도할 경우 몇 초 정도 지연되는 것을 확인할 수 있었다.
<?php
if( isset( $_GET[ 'Login' ] ) ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// Sanitise username input
$user = $_GET[ 'username' ];
$user = stripslashes( $user );
$user = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $user ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// Sanitise password input
$pass = $_GET[ 'password' ];
$pass = stripslashes( $pass );
$pass = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$pass = md5( $pass );
// Check database
$query = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
if( $result && mysqli_num_rows( $result ) == 1 ) {
// Get users details
$row = mysqli_fetch_assoc( $result );
$avatar = $row["avatar"];
// Login successful
echo "<p>Welcome to the password protected area {$user}</p>";
echo "<img src=\"{$avatar}\" />";
}
else {
// Login failed
sleep( rand( 0, 3 ) );
echo "<pre><br />Username and/or password incorrect.</pre>";
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
// Generate Anti-CSRF token
generateSessionToken();
?>
소스코드 분석 결과 이번에도 sleep( rand( 0, 3 ) );
함수를 사용하여 로그인을 지연시키고 있지만 2초가 아니라 0~3초까지 랜덤하게 지연시키고 있는 것을 확인하였다.
Midium에서 본 것과 같이 지연시간이 2초로 지정되어 있다면 공격자가 2초동안 응답이 없을 경우 틀린 패스워드라는 것을 간주하여 바로 다음 요청을 진행할 수 있다는 취약점을 확인할 수 있다. 따라서 시간을 랜덤하게 설정하여 서로 다른 시간으로 응답값을 받을 수 있도록 한 것을 확인할 수 있다.
대응 방안
- 패스워드 복잡성과 여러 계정에서의 비밀번호 재사용에 관한 정책을 설정한다.
- 패스워드를 길고 복잡하게 만들도록 규칙을 강제하여 공격자가 패스워드를 알아낼 수 없도록 해야한다. (사용자 패스워드 설정 시 영문(대문자, 소문자) 숫자, 특수문자가 혼합된 패스워드로 설정하는 방법을 사용한다)
- 자동화방지(re-Captcha)를 활성화한다.
- Bruteforce 공격의 자동화되고 반복적인 로그인 시도 자체를 불가능하게 하기위해 로그인에 반복적으로 실패할 경우에는 아래와 같은 자동화 방지 캡차를 사용한다.
- 잘못된 로그인 시도가 지속될 경우 제한(시간, 잠금 등)을 설정한다.
- Bruteforce 공격은 임의의 문자열을 무차별 대입해보는 공격이기 때문에 엄청나게 많은 로그인 실패를 반복하게 된다. 반복적인 로그인 시도를 방지할 수 있도록 로그인 시도 횟수를 제한 할 수 있다.
레퍼런스
- Bruteforce 공격에 대한 설명 : https://security.grootboan.com/follow-along/undefined/0-dvwa/reference-writeup#undefined-5
- Bruteforce 공격에 대한 설명 : https://www.kaspersky.com/resource-center/definitions/brute-force-attack
- reCaptcha 에 대한 설명 : https://www.cloudflare.com/ko-kr/learning/bots/how-captchas-work/