Step3. SQL인젝션 공격
Step3. SQL 인젝션 공격
SQL 인젝션 공격
- 오랜 시간 계속해서 가장 심각한 리스크로 선정된 인젝션 리스크의 대표적인 공격이며, 웹 사이트의 회원 정보 등 개인 정보를 탈취하고자 오래전부터 지속적으로 사용되어온 공격이다.
SQL 인젝션 공격 개요
-
SQL 인젝션 취약점은 웹 서버 영역의 데이터베이스로 전송되는 SQL 쿼리문을 사용자가 임의로 조작할 수 있는 경우 발생한다.
-
공격자는 이 취약점을 이용하여 데이터베이스에 저장되어 있는 다른 사용자의 개인정보 등 허가되지 않은 정보에 접근하거나 데이터를 변조할 수 있다.
사례
-
사용자는 ID가 1인 사용자 정보를 요청할 때, 이 요청을 받은 웹 애플리케이션은 내부의 데이터베이스로 다음과 같은 SQL 쿼리문을 전송한다. SELECT name, email FROM users WHERE ID='1'
-
이는 users라는 사용자 테이블에서 ID가 1인 사용자의 이름과 이메일을 가져오는 쿼리문이다.
-
이때, SQL 쿼리문을 구성하는 웹 애플리케이션의 소스코드가
-
$id = $_REQUEST[ 'id' ];
-
$query = "SELECT name, email FROM users WHERE id = '$id';";
-
-
일 때, 사용자가 입력한 ID 파라미터의 값($id)이 쿼리문의 일부로 사용되고 있는데, 이런 경우 SQL 인젝션 취약점이 존재한다.
방법
-
WHERE 구문 우회
-
원래의 ID 값 1 대신 SQL 쿼리문을 직접 조작하기 위해 1' or '1'='1을 입력한다.
-
입력 값이 SQL 쿼리문에 그대로 삽입된다면 쿼리문은 SELECT name, email FROM users WHERE id = '1' or '1'='1'이 되며, 이 결과 값은 ID가 1인 데이터뿐만 아니라 다른 데이터에 있어서도 모두 참의 결과를 갖기 때문에 모든 사용자의 이름과 이메일이 공격자에 전달된다.
-
-
UNION 공격
-
공격자는 or을 이용한 구문 대신에 UNION 키워드를 사입하고 그 뒤에 사용자 이름과 패스워드를 요청하는 SELECT 구문을 삽입한다.
-
SELECT name, email FROM users WHERE ID = '1' UNION SELECT name, pw FROM users#'
-
UNION은 합집합으로 두 개의 SELECT 구문의 결과를 모두 포함시키는 키워드이다.
-
이렇게 되면 뒤의 SELECT 구문에는 WHERE가 없기 때문에 users 테이블에 존재하는 사용자 이름과 비밀번호도 같이 결과로 반환된다.
-
추가로 코드의 마지막 부분에 있는 #은 뒤에 오는 내용을 주석 처리하여, 혹시 그 뒤에 있을지 모르는 다른 SQL 쿼리문이 공격자가 원하지 않는 명령문이 실행되거나 SQL 형식 에러가 발생하지 않도록 만들어 준다.
-
SQL 인젝션 공격 실습
WHERE 구문 우회 실습
-
이때, SQL 인젝션 공격에 취약한지 테스트해볼 수 있는 방법 중 가장 기본적인 방법은 '를 입력해보는 것이다.
-
이렇게 뜨는 이유는 SQL 쿼리문이 SELECT name, email FROM users WHERE id=''';이 되기 때문이다(systax error)
-
그러면 입력이 SQL 쿼리문에 영향을 줄 수 있다는 뜻이고, SQL 인젝션 공격 성공 가능성이 매우 높다.
-
위와 같이 입력하면 WHERE 조건문이 항상 '참'의 결과를 가져오면서 모든 사용자의 정보가 출력된다.
ORDER BY 및 UNION 실습
-
UNION은 합집합이기 때문에 UNION을 사용하기 위해서는 원래의 SQL 쿼리문이 조회하는 SELECT 문의 칼럼 개수와 UNION 뒤의 SELECT 문에서 요청하는 칼럼 개수가 같아야만 한다.
-
따라서 원래의 SQL 쿼리문이 몇 개의 칼럼을 반환하는지 알아내야 한다.
-
칼럼 개수는 ORDER BY 구문으로 알아낼 수 있다.
-
ORDER BY : 지정된 칼럼을 기준으로 결과를 정렬할 때 사용하는 키워드
-
ex) ORDER BY 1 : 첫 번째 칼럼을 기준으로 정렬, ORDER BY 2 : 두 번째 칼럼을 기준으로 정렬
-
이 때, 전체 칼럼 개수보다 큰 값을 입력하면 정렬을 할 수 없기에 에러를 발생시킨다.
-
에러가 발생하기 직전의 값이 칼럼의 개수가 된다.
-
-
여기서 봤을 때, 칼럼 개수 : 2
-
이후 UNION SELECT1,2와 같이 일련의 번호를 사용 하여 어떤 칼럼의 정보가 웹 페이지의 어느 위치에 나타나는지 확인한다.
-
웹 애플리케이션이 어떻게 구현되어 있느냐에 따라 칼럼의 정보가 모두 웹 페이지에 표시되는 것이 아니기 때문에 어떤 칼럼이 웹 페이지에 출력되는지도 이 방법을 이용하여 확인할 수 있다.
-
MySQL 데이터베이스는 information_schema라는 데이터베이스에서 데이터베이스 이름, 테이블, 칼럼 정보 등을 관리한다. -> 여기서 DB 이름 흭득
-
추가로 dvwa 데이터베이스의 테이블 목록을 알아낸다.
-
확인 후 users 테이블에 데이터를 확인한다.
-
이어서 password 칼럼 내용을 확인해 본다.
-
여기서 패스워드는 평문이 아닌 해시의 값의 형태로 저장되어 있는데, 이는 쉽게 크래킹 될 수 있다.
-
위와 같이 admin의 패스워드는 password임을 알 수 있다.
블라인드 SQL 인젝션 공격
-
블라인드의 경우 단지 사용자의 존재 유무만 알려주는 메시지로 나타나기 때문에, 쉽게 정보를 빼내기도 어렵다.
-
이와 같은 상황에서는 일반적인 SQL 인젝션 공격을 시도해 볼 수 없다.
-
위와 같이 AND이하 구문이 실행된다면 블라인드 SQL 인젝션 취약점을 의심해 볼 수 있다.
-
위와 같이 응답 메시지가 달라지는 경우도 있지만, 어떤 경우에는 웹 사이트에 출력되는 메시지조차 같은 경우도 있다. 이 때는 웹 요청이 응답되는 시간의 차이를 이용하여 참과 거짓을 구별해내는 방법을 시도해 볼 수 있다. 1' AND SLEEP(5)#
-
이와 같이 결과를 직접적으로 알 수는 없어도, 참과 거짓일 때의 결과 차이를 분석하여 어떤 정보를 알아내는 기법을 블라인드 SQL 인젝션 공격이라고 한다.
-
다만 블라인드 SQL 인젝션 공격은 일일이 쿼리문을 만들고 실행해야 하기 때문에 시간이 많이 걸린다. 따라서 자동화 프로그램을 많이 사용한다.(일반 SQL 인젝션 공격도 자동화 프로그램으로 공격이 가능)
sqlmap 자동화 공격
-
-sqlmap -h : 옵션들을 볼 수 있다.
SQL 인젝션 취약 여부를 알아내고자 할 때 사용하는 옵션
-
-u : 필수 옵션으로 공격을 시도할 URL을 지정한다.
-
--cookie : 로그인이 필요한 경우, 로그인하고 난 다음 발급된 세션 쿠키 값을 지정한다.
-
--data : POST 요청의 폼을 공격하고자 할 때, 바디로 전달되는 데이터를 지정한다.
-
-p : 테스트할 파라미터를 지정한다.
-
--dbms : 데이터베이스의 종류를 알고 있는 경우 지정한다.(ex.mysql)
SQL 인젝션 공격이 가능한 경우 데이터를 입수할 때 사용하는 옵션
-
--current-db : 현재 데이터베이스의 이름을 알아낸다.
-
-D : 데이터를 입수할 데이터베이스를 지정한다.
-
-T : 데이터를 입수할 테이블 이름을 지정한다.
-
-C : 데이터를 입수할 칼럼을 지정한다.
-
--tables : 데이터베이스의 테이블들을 알아낸다. 주로 -D 옵션과 같이 사용한다.
-
--cuolumns : 데이터베이스의 칼럼들을 알아낸다. 주로 -D, -T 옵션과 같이 사용한다.
-
--dump : 데이터베이스의 정보들을 알아낸다.
실습
-
sqlmap 프로그램을 사용하기 위해 필요한 필수 옵션은 -u URL 정보이다.
-
DVWA처럼 로그인된 페이지에 SQL 인젝션 공격을 시도하는 경우에는 쿠키 정보도 필요하다.
-
-
sqlmap을 열어 취약점을 분석한다.
-
위의 내용에서 중요한 정보는 눈에 띄게 표시된다.
-
표시된 내용에서 취약점, 버전 정보 등을 알 수 있다.
-
--current-db 옵션을 줘서 현재 데이터베이스의 정보를 알아낸다.
-
-D dvwa -tables 옵션을 줘서 데이터베이스의 테이블 정보를 알아낸다.
-
-D dvwa -T users -dump 옵션을 통해 users테이블의 정보를 본다.
SQL 인젝션 공격 대응
-
SQL 인젝션 공격과 같은 모든 파라미터 입력값 조작으로 이루어지는 공격은 입력값 검증을 통해 대응
-
화이트리스트 검증을 하는 것이 좋다.
-
사용자가 입력한 값은 SQL 쿼리문에서 오직 데이터로만 사용되어야지 SQL 쿼리문의 구조에 영향을 줄 수 없도록 해야 한다.
-
이럴 경우 prepare() 부분에서 미리 실행할 쿼리문의 형태를 작성(prepared statement)해두고 있기 때문에, 그리고 사용자 입력값이 들어갈 id부분은 bindParam()에서 설정했기 때문에 사용자가 입력한 값이 쿼리문의 일부가 될 수 없고, 온전히 데이터로만 처리되기 때문에 or나 UNION등과 같은 키워드가 입력되어도 무의미한 문자열이 된다.
-
결과적으로 사용자가 SQL 쿼리문을 조작할 방법이 없어 SQL 인젝션 공격을 효과적으로 방지할 수 있다.
위 글은 '화이트 해커를 위한 웹 해킹의 기술 최봉환 저'를 공부하며 작성한 글입니다.