[SQL Injection 취약점의 정의]
데이터베이스에 접속하고 테이블에 값을 가져오거나 생성할 때 쿼리문을 사용하는데,
이때 사용자의 입력값을 그대로 쿼리문에 사용하는 경우 취약점이 발생한다.
[실습 내용]
SQL Injection 을 이용하여 사용자 계정 정보를 알아내보자.
[SQL Injection 을 보안하는 방법]
사용자의 입력을 그대로 받지 않고 필터링 함수를 이용하여 필터링을 거친다.
[초기화면]
- 숫자를 입력하면 숫자에 해당하는 계정이 출력된다.
- 2번을 입력해보니 또 다른 계정이 나온다.
- 이렇게 사용자 계정들을 조회할 수 있는 폼에서 SQL Injection 을 발생시키는 것 같다.
- 이제 소스 코드를 살펴보자
[View Source]
<?php
if( isset( $_REQUEST[ 'Submit' ] ) ) {
// Get input
$id = $_REQUEST[ 'id' ];
// Check database
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
$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>' );
// Get results
while( $row = mysqli_fetch_assoc( $result ) ) {
// Get values
$first = $row["first_name"];
$last = $row["last_name"];
// Feedback for end user
echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
}
mysqli_close($GLOBALS["___mysqli_ston"]);
}
?>
- 소스코드는 위와 같다.
- 입력한 id 를 받고 이를 SQL 쿼리문을 이용해서 조회한다. -> 이때 입력받은 값을 그대로 사용하므로 SQL Injection 이 발생가능하다.
- users 테이블에서 user_id 에 해당하는 행에서 first_name, last name 을 가져온다.
- 2번은 Gordon Brown 인듯하다.
- 해당 공격이 작동할 수 있는지 ' 를 대입해보았다.
- 경고문에서 볼 수 있듯이 SQL 문법 오류가 발생했다는 에러 메시지가 출력되었다. -> Error Based SQL Injection
- 이제 쿼리문을 이용하여 모든 사용자 계정을 출력해보자.
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
- 쿼리문을 살펴보면 '' 로 $id 를 감싸고 있는 것을 알 수 있다. ' 를 이용해서 '' 닫아버리고 참으로 만들자.
- 1' or '1'='1 로 작성하면 결국 user_id = ' 1' or '1'='1' 가 되므로 참이 된다.
- 따라서 이와 같이 사용자 계정을 모두 출력했다!
- 이번엔 UNION 을 이용하여 SQL Injection 을 시도해보자.
- 이때, union 사용에서 주의해야 할 것은 원래 쿼리문이 조회하는 select 문의 칼럼 개수와 union 뒤에 요청하는 칼럼 개수가 같아야 한다.
- 따라서, 원래 칼럼의 개수가 몇 개인지 알아내야한다. -> 1' union select 1# 을 이용한다.
- 칼럼의 개수가 다르다는 에러 메시지가 출력된 것으로 보아 칼럼의 개수는 1이 아니다. 그렇다면 1, 1을 입력해보자!
- 해당하는 값을 가져오므로 칼럼의 개수는 2 임을 알 수 있다. -> 칼럼의 개수를 알아내는 방법에는 order by 도 있다.
- order by 는 칼럼을 기준으로 정렬을 하는데 이때, 존재하지 않는 칼럼을 입력하면 에러 메시지가 출력된다.
- 따라서 위의 구문을 이용하여 order by 키워드를 사용한다. -> 1' order by 1#
- 1' order by 3# 을 입력하면 3 에 해당하는 칼럼이 정의되어 있지 않다는 에러 메시지가 출력된다.
- 따라서, 칼럼의 개수가 2 라는 것을 알 수 있다. -> union 을 사용하는 것보다 더 빠르게 칼럼의 개수를 알아낼 수 있다.
- 이제 칼럼의 개수를 알아냈으니 데이터 베이스의 정보를 읽어오는 실습을 진행해보겠다.
- 해당 페이지의 소스코드의 쿼리문을 이용하여 데이터 베이스의 정보를 모두 읽어오겠다.
"SELECT first_name, last_name FROM users WHERE user_id = '$id';"
- 위와 같은 쿼리문을 사용하여 users 테이블에 id 에 해당하는 사용자의 행의 값을 가져온다.
- union 을 사용하여 사용자 계정들의 비밀번호를 가져와보자.
' union select user_id, password FROM users#
- 해당 명령어를 입력하니 사용자 계정들의 비밀번호를 가져올 수 있었다.
- 하지만 열의 값이 password 라고 찍은 게 맞아서 획득할 수 있었던거지 이 방법은 정보를 정말 많이 알고 있을 때의 얘기다.
- 만약 쿼리문에 대한 정보가 극히 제한적이고 열의 이름 또한 예측할 수 없다면 어떻게 사용자 계정의 비밀번호를 획득할 수 있을까?
1' union select schema_name, 1 from information_schema.schemata #
- MY SQL 에서는 information_schema 에서 데이터 베이스에서 테이블, 칼럼 정보를 다루고 있다.
- 따라서 위 쿼리문을 이용하면 데이터 베이스의 schemata (테이블명) 에서 schema_name (데이터베이스명) 을 알아낼 수 있다.
- 실제 DB 에서 해당 쿼리문을 입력하면 이렇게 나온다.
- 이제 dvwa DB 에서 테이블을 조회해보자.
1' union select table_schema, table_name from information_schema.tables where table_schema="dvwa"#
- 총 3개의 테이블이 나온다.
- admin 테이블이 엄청 중요해보이긴 하지만, 현재 유저들의 비밀번호를 캐내야 하므로 users 테이블에 들어가본다.
1' union select table_name, column_name from information_schema.columns where table_schema="dvwa" and table_name="users"#
- password 열에 유저의 비밀번호가 있을 것으로 보인다.
- 마지막으로 password 를 가져오는 쿼리문을 작성한다.
1' union select password, user from users#
- 이렇게 암호화 된 비밀번호를 획득할 수 있다.
- DVWA 는 MD5 암호화 방식을 사용하므로 구글링을 통해 해시값을 찾아보았다.
- admin 의 암호화 된 비밀번호를 검색했더니 결과가 나왔다.
'TeamH4C' 카테고리의 다른 글
[빡공팟 5기] W5 : DVWA 실습 - Weak Session IDs (0) | 2022.10.23 |
---|---|
[빡공팟 5기] W5 : DVWA 실습 - SQL Injection(Blind) (0) | 2022.10.23 |
[빡공팟 5기] W5 : DVWA 실습 - Insecure CAPTCHA (0) | 2022.10.23 |
[빡공팟 5기] W5 : DVWA 실습 - File Upload (0) | 2022.10.23 |
[빡공팟 5기] W5 : DVWA 실습 - File Inclusion (0) | 2022.10.23 |