2024. 10. 2. 01:43ㆍWeb Security/웹 해킹
sql injection
injection 공격이라고 한다면 대표적으로 SQL injection을 떠오를 것이다. SQL Injection이란 SQL 문법 상의 빈틈을 통하여 서버의 권한을 탈취하거나 정보를 탈취하는 공격이다. SQL injection을 알기 위해서 SQL 문법을 알야한다.
DDL, DML ,DCL - DML
SQL에서는 Data Definition Language (DDL), Data Manipulation Language (DML), Data Control Language (DCL) 총 세 가지의 질의 언어를 제공합니다. 이중 사용자는 DML을 이용하게 됩니다. 그렇기에 DML에 어떻게 이루어져 있는지 알아야 합니다.
DML은 SELECT, INSERT, UPDATE, DELET 문으로 이루어져 있으며, 이들이 궁금하시다면 찾아보시는 것을 추천드립니다.
SELECT문을 이용한 간단한 SQL injectin을 보여드리도록 하겠습니다.
[setup.sql]
CREATE TABLE admin (uid STRING, upw STRING);
CREATE TABLE board (name STRING, text STRING);
INSERT INTO admin (uid, upw) VALUES ('admin', 'password');
질의 방식
insert into board (name, text) values ("", "");
페이로드
a", (select upw from admin where uid = 'admin')) —
SQL 특징
SQL에는 다양하 함수 및 질의 방식을 지원합니다. 대표적으로 UNION, Subquery 및 SUBSTR 등의 함수 및 특징이 존재합니다. 이들을 통해서 다양한 방식으로 SQL injection 공격이 가능해집니다.
union
union은 Select 문을 결합할 수 있는 절입니다.
이를 통한 간단한 공격을 보여드리도록 하겠습니다.
질의 방식
Select uid from user_table where uid='' and upw=''
페이로드(위 페이로드는 아이디 혹은 비밀번호에 들어갑니다)
a' union select upw from user_table where uid ='admin' --
Subquery
서브쿼리란 한 쿼리 안에 또 다른 쿼리를 사용할 수 있는 것을 의미합니다 .
colum 절, from 절, where 절 등 안에 사용 할 수 있습니다. 이를 통한 공격은 아래와 비슷합니다.
[setup.sql]
CREATE TABLE admin (uid STRING, upw STRING);
CREATE TABLE board (name STRING, text STRING);
INSERT INTO admin (uid, upw) VALUES ('admin', 'password');
질의 방식
insert into board (name, text) values ("", "");
페이로드
a", (select upw from admin where uid = 'admin')) —
substr
SUBSTR() 함수는 문자열에서 특정 위치부터 일정 길이만큼의 부분 문자열을 추출하는 데 사용됩니다
SUBSTR(string, start_position, length)
위와 같은 공격을 통하여 데이터베이스의 값을 예측하는데 사용됩니다. 이를 blind sql injection이라고 합니다. 값을 볼 수 없을 때 if 문과 sleep 함수를 통하여 time based blind sql injection 이 가능해집니다.
blind sql injection을 효과적으로 성공적으로 하기 위해서는 비밀번호 등의 길이가 얼마인지 알아야 합니다. 이는 아래와 같습니다.
글자길이 알아내는 법
SELECT *
FROM users
WHERE username = 'admin' AND LENGTH(password) >= 32;
blind sql injection 지속적인 질의를 서버로 보냅니다. 너무 많은 질의를 보낼 경우 WAF(웹 방화벽)에서 차단 할 수 있습니다. 그렇기에 최대한 질의하는 수를 줄여합니다. 수를 줄의는 방법에는 아래와 같은 방법이 존재합니다.
advance blind sql injection with bin
대표적으로 아래와 같이 ord → 아스키코드 값으로 변경 → bin(2진수)로 변경 하여 총 7번의 쿼리를 보내어 한바이트의 값을 알아낼 수 있습니다. 효과적으로 질의 방법을 줄이는 방법입니다.
select * from users where username='admin' and substr(bin(ord(password)),1,1)=1;
error - error base sql injection
DRMS에서 발생하는 에러를 통해서 특정 값을 알아내는 공격을 수행할 수 도 있습니다.
대표적으로 사용되는 함수는 extractvalue 입니다.
extractvalue 함수는 첫 번째 인자로 전달된 XML 데이터에서 두 번째 인자인 XPATH 식을 통해 데이터를 추출합니다. 만약, 두 번째 인자가 올바르지 않은 XPATH 식일 경우 올바르지 않은 XPATH 식이라는 에러 메시지와 함께 잘못된 식을 출력합니다.
이는 아래처럼 사용할 수 있습니다.
mysql> SELECT extractvalue(1,concat(0x3a,(SELECT password FROM users WHERE username='admin')));
ERROR 1105 (HY000): XPATH syntax error: ':Th1s_1s_admin_PASSW@rd'
Fingerprint
SQL injection을 공격하는 것을 보면 DB의 구조를 알고 있는 상태에서 공격합니다. 즉, DB에 대한 정보를 얻어야 공격을 할 수 있습니다. 이러한 DB에 대한 정보를 얻는 것으로 Penetration Testing Execution Standard (PTES)에서 Fingerprint 라고 합니다. 아래 사진에서 데이터베이스이외에 데이터 사전이라는 곳이 있습니다. 실제 DB를 만들면 시스템에서 사용하는 DB는 따로 만들어져 있습니다.
이는 많은 정보(예: 사용자 권한, 서버 설정, 테이블 구조 등)를 저장합니다 이를 추출해서 정보를 얻고 SQL injection이 수행됩니다
걱정할 것 없다
sqlmap을 사용합시다. 이외에도 sql injection을 위한 많은 툴들이 존재합니다. 이를 잘 이용하면 될 것 같습니다.
그렇다면 이러한 sql injection은 언제 발생하고 어떻게 막을까?
sql의 SELECT문은 DBMS 내부적으로 4단계의 과정(Parse, Bind, Execute, Fetch)을 거쳐 결과를 출력한다.
statement와 prepared statement가 존재한다.
Statement의 경우, 구문 분석(parse)부터 인출(fetch)까지 모든 과정을 매번 수행한 다. 따라서 입력값에 SQL구문에 영향을 미치는 특수문자나 예약어가 들어갈 경우 구문 분석 과정에서 SQL구문의 일부로 작용하여 SQL Injection 공격이 가능하다.
하지만, Prepared Statement의 경우, 구문 분석(parse) 과정을 최초 1회만 수행하여 생성된 결과를 메 모리에 저장해 필요할 때마다 사용한다. 미리 구성된 파싱 트리를 반복적으로 사용하기 때문에 Statement에 비해 시간을 단축할 수 있다. 또한 SQL구문이 미리 컴파일 되어 사용자 입력값 을 변수로 선언해 값을 대입하여 사용한다. 따라서 외부 입력값으로 SQL문법에 영향을 미치는 특수문자나 예약어가 입력되어도 문법적인 의미로 작용하지 못한다.
아래 코드는 취약한 코드입니다.
아래는 statement 코드입니다.
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
...
String sql = "SELECT * FROM MEMBER WHERE ID = '"+param_id+"' AND PW = '"+param_ passwd+"'";
stmt = conn.createStatement();
rs = stmt.executeQuery(sql);
아래는 prepared statement 코드입니다.
String param_id = request.getParameter("id");
String param_passwd = request.getParameter("passwd");
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
…
String sql = "SELECT * FROM MEMBER WHERE ID = ? AND PW = ?";
pstmt = conn.prepareStatement(sql); // SQL을 준비 (컴파일)
pstmt.setString(1, param_id); // 첫 번째 물음표(?)에 param_id 값을 바인딩
pstmt.setString(2, param_passwd); // 두 번째 물음표(?)에 param_passwd 값을 바인딩
rs = pstmt.executeQuery(); // 쿼리를 실행하고 결과를 받아옴
종종 prepared statement를 사용 불가한 경우가 존재한다. 이럴 경우 white list 방식을 사용하거나, blacklist 방식을 사용하여 필터링 해야한다. 다음 링크는 DRMS의 sql injection cheet이다. https://pentestmonkey.net/category/cheat-sheet/sql-injection
마지막으로 Prepared Statement를 사용한다고 완벽히 안전한 것은 아니다. 바인딩을 해야지 안전한 것이다. Prepare Statement를 거치면서 구문분석(1단계)를 건너 뛰게 된다. 이후 바인딩을 통하여 input을 넣어주면 이것은 Sql 문법으로 해석되지 않아 안전한 것이다.
출처 :
'Web Security > 웹 해킹' 카테고리의 다른 글
SQL injection 취약 부분 찾기 (0) | 2024.10.24 |
---|---|
NO-SQL injection (2) | 2024.10.02 |
CSTI injection (0) | 2024.09.23 |
CSS injection (0) | 2024.09.23 |
Relative Path Overwrite (0) | 2024.09.22 |