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 로 처리되지 않고 그냥 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이었습니다.
찾는 법과 대응 방안까지 확인해보았습니다. 계속해서 다음 블로그에도 제 서버의 취약점을 찾아보고 조치까지 해보겠습니다.
'웹 해킹' 카테고리의 다른 글
[주통기반] 로컬 웹 서버 취약점 찾기 (정보 누출) (0) | 2024.03.25 |
---|---|
[주통기반] 로컬 웹 서버 취약점 찾기 (디렉터리 인덱싱) (0) | 2024.03.25 |
웹 해킹 공부 일기장 12 - 2 (인가 취약점) (0) | 2024.02.23 |
웹 해킹 공부 일기장 12 - 1 (인증 취약점) (0) | 2024.02.22 |
웹 해킹 공부 일기장 11 - 2 (파일 다운로드) (0) | 2024.02.19 |