본문 바로가기

웹 해킹

[주통기반] 로컬 웹 서버 취약점 찾기 (SQL Injection)

SQL Injection 은 웹에서 사용자의 입력 값으로 SQL 쿼리를 완성할 경우 데이터베이스에 접근하여 데이터를 처리할 때 발생할 수 있습니다.

사용자의 입력 값을 제대로 검증하지 않고 SQL 쿼리를 완성할 경우 사용자의 입력으로 악의적인 SQL 쿼리가 완성되게 하여 악의적인 데이터베이스 접근 및 조작할 수 있습니다.

 

1. 게시글 검색 기능 Category 

게시글 검색 시 사용자의 입력으로 특정 게시글을 검색하는 SQL 쿼리를 생성합니다. 이 때 컬럼에 해당하는 파라미터에 SQL 문을 넣어 참과 거짓에 대한 응답 결과가 다른 것을 확인할 수 있습니다. 그러면, Blind SQL Injection을 통해 데이터베이스의 정보를 획득할 수 있습니다.

 

1) 게시글 페이지 접속

게시글 페이지

 

2) 게시글 검색한 후 검색 요청 패킷을 버프로 확인

요청 패킷

 

3) category 파라미터에 참 조건 SQL 삽입

참 조건

 

1=1 and title로 SQL 문 조건을 삽입하여 똑같은 결과가 나오는지 확인합니다.

 

4) category 파라미터에 거짓 조건 SQL 삽입

거짓 조건

 

1=0 and title로 SQL 문 조건을 삽입하여 참 조건일 때와 다른 결과가 나오는 지 확인합니다.

 

참과 거짓에 대한 다른 결과를 통해 Blind SQL Injection 으로 데이터베이스 결과를 확인할 수 있습니다.

 

2. 게시글 검색 기능 word

게시글 검색 시 사용자의 입력으로 특정 게시글을 검색하는 SQL 쿼리를 생성합니다. 이 때 컬럼에 해당하는 파라미터에 SQL 문을 넣어 참과 거짓에 대한 응답 결과가 다른 것을 확인할 수 있습니다. 그러면, Blind SQL Injection을 통해 데이터베이스의 정보를 획득할 수 있습니다. 

 

1) 게시글 페이지 접속

게시글 페이지

 

2) 게시글 검색 단어에 참 조건 SQL 삽입

참 조건

 

s%' and '1%'='1 을 검색하여 s가 포함된 게시글이 나오는지 확인합니다.

 

3) 게시글 검색 단어에 거짓 조건 SQL 삽입

거짓 조건

 

s%' and '1%'='0 을 검색하여 참 조건일 때와 다른 결과가 나오는지 확인합니다.

 

참과 거짓에 대한 다른 결과를 통해 Blind SQL Injection 으로 데이터베이스 결과를 확인할 수 있습니다.

이 경우는 Union SQL Injection을 사용할 수 도 있습니다.

 

[대응 방안]

일단, SQL Injection을 실행할 수 없도록 Prepared Statement 구문을 사용합니다.

그리고 Prepared Statement를 구현할 수 없는 컬럼, 정렬, 테이블 같은 부분에는 화이트 리스트 필터링을 통해 사용자 입력을 검증합니다.

$searchCategory = $_GET['category'];
if (!$searchCategory) {
    $searchCategory = 'title';
} else if ($searchCategory != 'title' && $searchCategory != 'view' && $searchCategory != 'date') {
    $searchCategory = 'title';
}

 

category 파라미터가 없거나 정해진 title, view, date 가 아닌 경우에는 title로 설정해 승인되지 않은 입력값은 title로 처리하게 했습니다.

    public static function searchBoard($page, $category, $word, $sortCategory, $sort) {
        $db_conn = connectDb();
        $startPage = ($page - 1) * 10;
        $likeWord = "%".$word."%";
        $query = "select * from boards where $category like ? order by $sortCategory $sort limit $startPage,10";
        $stmt = $db_conn->prepare($query);
        $stmt->bind_param("s", $likeWord);
        $stmt->execute();
        $result = $stmt->get_result();
        $stmt->close();
        $db_conn->close();
        return $result->fetch_all(MYSQLI_ASSOC);
    }

 

위 코드와 같이 like 절에 Prepared statement를 사용하여 로직을 구현했습니다.

 

[대응을 적용한 결과]

취약점을 찾은 것처럼 똑같이 적용해보겠습니다.

참 조건
거짓 조건

 

참 조건과 거짓 조건에 대해 전부 title로 처리되어 결과가 같습니다. 

정해진 문자 외에는 전부 title로 처리되어 SQL 문을 삽입할 수 없습니다.

검색 시 SQL 삽입

 

검색 단어에 SQL 문을 삽입하였지만 해당 문자가 SQL 로 처리되지 않고 그냥 s%' and '1%'='1 라는 문자로 처리되어 해당 문자열이 포함된 게시글은 없어 결과가 없는 것을 확인할 수 있습니다.

이것도 SQL 문을 삽입하여도 처리되지 않기때문에 SQL Injection 공격에 안전합니다.

 

대응을 위해 코드를 수정하다 보니 sortCategoty 파라미터에도 SQL Injection이 가능할 것 같아 시도하니 취약점이 나왔습니다.

 

3. 게시글 검색 기능 sortCategory

 

1) 게시글 검색 패킷을 잡아 sortCategory 파라미터에 참 조건 SQL 삽입

참 조건

 

case when 1=1 then title else idx end 를 삽입하여 조건이 참일 때 title로 정렬시킵니다.

 

2) 게시글 검색 패킷을 잡아 sortCategory 파라미터에 거짓 조건 SQL 삽입

거짓 조건

 

case when 1=0 then title else idx end 를 삽입하여 조건이 거짓일 때 idx로 정렬시킵니다.

 

참과 거짓의 결과가 다른 것을 통해 Blind SQL Injection 을 통해 데이터베이스 정보를 탈취할 수 있습니다.

이번에도 화이트 리스트 필터링을 통해 정해진 단어만 사용할 수 있도록 조치를 하겠습니다.

$sortCategory = $_GET['sortCategory'];
if (!$sortCategory || ($sortCategory != 'title' && $sortCategory != 'view' && $sortCategory != 'date')) {
    $sortCategory = 'idx';
}

 

정렬할 때 사용하는 단어인 title, view, date 외엔 전부 idx로 정렬하게 구현하였습니다.

 

조치 후 참 조건

 

조치 후 거짓 조건

 

조치 후 참과 거짓의 결과가 같은 것을 확인할 수 있습니다. 애초에 다른 문자는 idx로 바꾸기 때문에 SQL Injection은 불가능합니다.

다른 데이터베이스와 연결된 부분에는 제가 처음 만들 때 특정 단어만 사용할 수 있게 구현하거나 prepared statement를 사용하여 다른 곳에서는 발견하지 못했습니다.

 

주통기반으로 발견한 제가 만든 서버의 첫 취약점은 SQL Injection이었습니다.

찾는 법과 대응 방안까지 확인해보았습니다. 계속해서 다음 블로그에도 제 서버의 취약점을 찾아보고 조치까지 해보겠습니다.