Dreamhack/Dreamhack Wargame (Challenge)

[59] IT 비전공자 [dreamhack]sql injection bypass WAF문제 풀기

imaginefuture-1 2024. 11. 7. 08:54

 

 

sql 드가자

 

 

웹서버에 접속하면 이런화면이 뜬다

 

에에 블락..

 

 


 

 

 

**SQL Injection Bypass WAF**는 **SQL 인젝션(SQL Injection)** 공격을 시도할 때 **웹 애플리케이션 방화벽(WAF)**을 우회하여 공격을 성공시키는 기술입니다. WAF는 SQL 인젝션과 같은 공격을 차단하기 위해 입력된 SQL 문을 검사하여 악의적인 패턴을 탐지하고 차단합니다. 그러나 공격자는 WAF의 필터링 규칙을 우회할 수 있는 다양한 기법을 사용해 SQL 인젝션을 성공시킬 수 있습니다.

### SQL Injection Bypass WAF 기법과 취약점 설명
다양한 SQL 인젝션 우회 방법을 사용하여 WAF의 탐지 규칙을 피할 수 있으며, 몇 가지 일반적인 기법은 다음과 같습니다:

1. **공백 우회**:
   - WAF가 공백을 기준으로 필터링할 경우, 공백 대신 주석(`/**/`), 탭(`\t`), 또는 새로운 줄을 삽입하여 우회할 수 있습니다.
   - 예: `SELECT * FROM users WHERE username='admin'/**/AND/**/password='password'`

2. **대소문자 혼합**:
   - SQL 키워드를 대소문자를 섞어 사용하여 우회할 수 있습니다. 일부 WAF는 키워드의 특정 대소문자만을 필터링하기 때문입니다.
   - 예: `SeLeCt * FrOm users WhErE username='admin' AnD password='password'`

3. **문자 인코딩**:
   - URL 인코딩, 유니코드 인코딩 등의 문자 변환을 사용해 우회할 수 있습니다.
   - 예: `' OR '1'='1`를 `%27%20OR%20%271%27%3D%271`과 같이 URL 인코딩으로 우회

4. **논리 연산자 변경**:
   - `OR` 대신 `||`, `AND` 대신 `&&` 등의 논리 연산자를 사용하거나, `=` 대신 `LIKE` 등을 사용하여 우회할 수 있습니다.
   - 예: `SELECT * FROM users WHERE username='admin' || '1'='1'`

5. **중첩 쿼리**:
   - WAF가 단일 SQL 문만 탐지하는 경우, 중첩된 쿼리나 서브쿼리를 사용하여 우회할 수 있습니다.
   - 예: `SELECT * FROM users WHERE username='admin' AND password IN (SELECT password FROM users WHERE username='admin')`

6. **타임 기반 블라인드 SQL 인젝션**:
   - 결과를 직접 확인할 수 없는 경우, `SLEEP()` 함수와 같은 타이밍 기반 함수로 데이터베이스의 응답 시간을 유도하여 공격 성공 여부를 판단할 수 있습니다.
   - 예: `SELECT * FROM users WHERE username='admin' AND IF(1=1, SLEEP(5), 0)`

### 취약점 요약
- **WAF 우회 기법**을 통해 WAF가 필터링하지 못한 변형된 SQL 구문을 주입하여 데이터베이스에 직접 접근하는 취약점입니다.
- 이러한 SQL 인젝션 공격은 **데이터 유출, 권한 상승, 인증 우회** 등과 같은 심각한 보안 문제를 유발할 수 있습니다.
  
이와 같은 WAF 우회 기법들은 WAF의 탐지 알고리즘을 우회할 수 있으므로, 철저한 입력 검증과 SQL 구문에 대한 파라미터화(Query Parameterization)를 통해 SQL 인젝션을 예방하는 것이 중요합니다.

 

오호 꽤나 우회방법 맞나 하하. 이거 흥미롭다 ㅋㅋ특히 대문자 소문자 부분ㅋㅋ

 

 


소스코드 보러가자 

 

 

init.sql 파일 소스코드다

CREATE DATABASE IF NOT EXISTS `users`;
GRANT ALL PRIVILEGES ON users.* TO 'dbuser'@'localhost' IDENTIFIED BY 'dbpass';

USE `users`;
CREATE TABLE user(
  idx int auto_increment primary key,
  uid varchar(128) not null,
  upw varchar(128) not null
);

INSERT INTO user(uid, upw) values('abcde', '12345');
INSERT INTO user(uid, upw) values('admin', 'DH{**FLAG**}');
INSERT INTO user(uid, upw) values('guest', 'guest');
INSERT INTO user(uid, upw) values('test', 'test');
INSERT INTO user(uid, upw) values('dream', 'hack');
FLUSH PRIVILEGES;

 


app.py 파일 소스코드다

import os
from flask import Flask, request
from flask_mysqldb import MySQL

app = Flask(__name__)
app.config['MYSQL_HOST'] = os.environ.get('MYSQL_HOST', 'localhost')
app.config['MYSQL_USER'] = os.environ.get('MYSQL_USER', 'user')
app.config['MYSQL_PASSWORD'] = os.environ.get('MYSQL_PASSWORD', 'pass')
app.config['MYSQL_DB'] = os.environ.get('MYSQL_DB', 'users')
mysql = MySQL(app)

template ='''
<pre style="font-size:200%">SELECT * FROM user WHERE uid='{uid}';</pre><hr/>
<pre>{result}</pre><hr/>
<form>
    <input tyupe='text' name='uid' placeholder='uid'>
    <input type='submit' value='submit'>
</form>
'''

keywords = ['union', 'select', 'from', 'and', 'or', 'admin', ' ', '*', '/']
def check_WAF(data):
    for keyword in keywords:
        if keyword in data:
            return True

    return False


@app.route('/', methods=['POST', 'GET'])
def index():
    uid = request.args.get('uid')
    if uid:
        if check_WAF(uid):
            return 'your request has been blocked by WAF.'
        cur = mysql.connection.cursor()
        cur.execute(f"SELECT * FROM user WHERE uid='{uid}';")
        result = cur.fetchone()
        if result:
            return template.format(uid=uid, result=result[1])
        else:
            return template.format(uid=uid, result='')

    else:
        return template


if __name__ == '__main__':
    app.run(host='0.0.0.0')

 

 

이 Flask 애플리케이션은 MySQL 데이터베이스에 연결하여, 사용자 입력을 통해 데이터베이스에서 사용자 정보를 조회하는 기능을 제공합니다. 다음은 코드의 주요 기능과 보안 문제를 분석한 것입니다.

### 주요 기능 설명

1. **환경 변수 설정 및 MySQL 연결**:
   ```python
   app.config['MYSQL_HOST'] = os.environ.get('MYSQL_HOST', 'localhost')
   app.config['MYSQL_USER'] = os.environ.get('MYSQL_USER', 'user')
   app.config['MYSQL_PASSWORD'] = os.environ.get('MYSQL_PASSWORD', 'pass')
   app.config['MYSQL_DB'] = os.environ.get('MYSQL_DB', 'users')
   mysql = MySQL(app)
   ```
   - 애플리케이션이 실행될 때 MySQL에 연결하기 위한 설정을 환경 변수에서 가져옵니다. 기본값으로 `localhost`와 `users` 데이터베이스를 설정합니다.
   - `flask_mysqldb` 라이브러리를 사용하여 MySQL에 연결을 초기화합니다.

2. **템플릿 HTML 코드**:
   ```python
   template ='''
   <pre style="font-size:200%">SELECT * FROM user WHERE uid='{uid}';</pre><hr/>
   <pre>{result}</pre><hr/>
   <form>
       <input tyupe='text' name='uid' placeholder='uid'>
       <input type='submit' value='submit'>
   </form>
   '''
   ```
   - HTML 템플릿이 정의되어 있으며, 입력된 `uid`에 따라 SQL 쿼리와 결과를 보여줍니다.
   - 사용자 입력을 받을 수 있는 입력 상자(`input`)와 제출 버튼(`submit`)이 포함되어 있습니다.

3. **WAF(웹 애플리케이션 방화벽) 필터링**:
   ```python
   keywords = ['union', 'select', 'from', 'and', 'or', 'admin', ' ', '*', '/']
   def check_WAF(data):
       for keyword in keywords:
           if keyword in data:
               return True
       return False
   ```
   - SQL 인젝션을 방지하기 위해 `check_WAF` 함수를 사용하여 사용자 입력에 특정 SQL 키워드가 포함되어 있는지 검사합니다.
   - `union`, `select`, `from`, `admin`, `*`, `/` 등의 키워드를 포함하면 요청이 차단됩니다.

4. **루트 경로 처리 (`/`)**:
   ```python
   @app.route('/', methods=['POST', 'GET'])
   def index():
       uid = request.args.get('uid')
       if uid:
           if check_WAF(uid):
               return 'your request has been blocked by WAF.'
           cur = mysql.connection.cursor()
           cur.execute(f"SELECT * FROM user WHERE uid='{uid}';")
           result = cur.fetchone()
           if result:
               return template.format(uid=uid, result=result[1])
           else:
               return template.format(uid=uid, result='')
       else:
           return template
   ```
   - `/` 경로에서 GET 및 POST 요청을 처리합니다.
   - 사용자가 `uid` 파라미터를 입력한 경우 `check_WAF` 함수를 호출하여 필터링합니다. 필터링에 걸리면 "your request has been blocked by WAF." 메시지를 반환합니다.
   - WAF 필터를 통과한 경우 `uid` 값으로 SQL 쿼리를 실행하여 사용자 정보를 조회합니다.
   - 조회된 결과가 있으면 템플릿에 결과를 포함해 반환하며, 결과가 없으면 빈 템플릿을 반환합니다.

여기서 중요한건 결과값 한 행을 가져와 2번째 열의값을 result로 출력한다는 것이다.


### 보안 취약점 분석

1. **SQL 인젝션 (SQL Injection)**:
   - 쿼리 작성 시 `uid` 값을 SQL 쿼리에 직접 포함하고 있습니다.
   - 사용자가 쿼리 구문을 주입하면 SQL 인젝션이 발생할 수 있으며, 데이터베이스에서 중요한 정보를 읽거나 수정할 수 있는 위험이 있습니다.
   - 예: `uid`에 `1' OR '1'='1`와 같이 입력하면 전체 사용자 데이터를 조회할 수 있는 인젝션이 발생할 수 있습니다.

2. **불완전한 WAF 필터링**:
   - `check_WAF`는 특정 키워드만 검사하지만, 대소문자 혼합, 공백 문자 우회, 주석 등을 사용하여 필터링을 우회할 수 있습니다.
   - 예를 들어, `UnIoN`이나 `/*!50000UnIoN*/`과 같은 형태로 우회할 수 있습니다.
   - 이처럼 특정 키워드를 단순히 차단하는 방식은 신뢰할 수 없는 사용자의 입력을 완전히 막을 수 없습니다.

3. **취약점 해결 방안**:
   - **Prepared Statements** 또는 **Parameterized Queries**를 사용해 SQL 인젝션을 방지할 수 있습니다. 이는 사용자의 입력을 SQL 구문과 분리하여 안전하게 처리합니다.
   - 예:
     ```python
     cur.execute("SELECT * FROM user WHERE uid=%s;", (uid,))
     ```
   - 이렇게 하면 사용자가 입력한 값이 쿼리 구문에 포함되지 않으므로 SQL 인젝션이 방지됩니다.

4. **WAF 필터링 강화**:
   - 현재 WAF 필터링은 단순한 키워드 기반으로 동작하며 우회가 가능합니다. 정규 표현식이나 더 정교한 필터링 시스템을 사용하거나, SQL 인젝션을 근본적으로 차단하는 방식(예: 파라미터화된 쿼리)으로 보안을 강화하는 것이 좋습니다.

이 코드는 WAF 우회를 통해 SQL 인젝션 공격에 취약하므로, 반드시 파라미터화된 쿼리를 사용하여 보안을 강화해야 합니다.

 

 


이것저것 넣고있는데 다 block 당한다 크흑..쉽지않다

 

https://minnggyuu.tistory.com/5

 

[Dreamhack] sql injection bypass WAF 풀이

오랜만에 워게임을 풀어보았다. 화이트햇 스쿨 준비한다고 전역하고도 마음껏 공부할 시간이 없었다(는 구라고 배그하느라 바빴음) 복학하면 꼭 공부에 올인하겠다는 다짐을 또 해본다. 오늘의

minnggyuu.tistory.com

https://jwcs.tistory.com/21

 

[드림핵] sql injection bypass WAF

https://dreamhack.io/wargame/challenges/415/ sql injection bypass WAF Description Exercise: SQL Injection Bypass WAF에서 실습하는 문제입니다. dreamhack.io 방화벽 우회 injection이다. 첫 화면이다. 바로 코드로 넘어가자. import

jwcs.tistory.com

키야 두번째분 블로그는 qna도 너무 맛있다..머릿속에 db가 있으신 것 같다

 


데이터베이스에 관한 내용이다. create table user 부분을 보면 칼럼의 수가 idx, uid, upw로 3개인 것을 볼 수 있다. 따라서 칼럼의 수를 3개로 맞춰야 한다.

Q. 왜 guest를 입력하면 result 값으로 guest가 나오는가?

A. 위 코드에서 확인 가능하듯이 첫 번째 칼럼의 값은 idx이다. 따라서 두 번째 칼럼의 값은 uid이므로 uid값인 guest가 출력 되는 것이다.

출처ㅣ https://jwcs.tistory.com/21

그래서 dream을 넣으면 uid값인 dream이 출력된다.. 소름

 

첫번째 칼럼의 값은 idx이고 두번째 칼럼의 값은 uid다 , dream을 입력하면 uid값인 dream 이 출력된다

 

 


인젝션 코드를 짜보면 이렇다.
'Union   Select    null,upw,null   From   User   where   uid="Admin"#
(띄어쓰기가 아닌 tab이다.)
url인코딩 된 값으로 바꾸자면
%27Union%09Select%09null,upw,null%09From%09User%09where%09uid="Admin"%23
 

출처ㅣ https://jwcs.tistory.com/21


%27 = '
%23 = #

어라?

 

 

뭔가 이상하다 왜 인코딩이 인코딩이..

 

문제 페이지 들어가서 풀면 %가 이중 인코딩 돼서 문제 안풀립니다 삽질하지마시고 curl 써서 푸세요ㅠㅠㅠㅠㅠㅠ

드림핵 댓글 中

 

안녕하세요 저도 풀다가 궁금해서 찾아봤는데 찾은것같아서 답변 드려요
우선 강의에서 제공하는 코드는 URL 인코딩이 된 상태입니다.
문제 uid 입력폼에는 URL 인코딩이 되기전 코드를 입력해야 정상적인 플래그가 나옵니다.
ex) '웹의 특성이 탭을 스페이스로 인식하여 수강생들이 복사하였을 때
탭을 스페이스로 복사하게 되고 WAF에 걸려서 이렇게 표현한것 같네요
uid 폼 -> URL 인코딩 -> URL 상에서의 코드(문제에서 준 코드)
질문자님 말씀대로라면 URL 인코딩 된 코드를 uid 폼에 넣었으니 URL 인코딩이 2중으로 되는 상태이며
이는 웹서버에서 우리가 의도한것과 다르게 인식됩니다.
따라서 uid 입력 폼에는' Union Select 이렇게 시작하는 코드를 넣으시고
url상의 uid 파라미터에는 강의에서 제공해준대로%27%09Union%09Select%09
이런형식으로 넣으면 정상적으로 해결됩니다.
burpsuite 와 같은 프록시 도구를 이용해서 입력하는 위치에 따라 패킷 내용이 어떻게 변하는지 한번 확인해보세요!

드림핵 커뮤니티 글 中

 


풀이를 확인해보니 공백을 우회하기 위해서 탭문자를 사용하신 것으로 보입니다.
탭 문자는 
\x09
인데 이를 url encoding으로 나타내면 
%09
입니다.
이를 form을 이용하여 보낼 시 플래그를 얻을 수 없는 이유는
%09
가 
%2509
로 변환되어 탭문자로 인식하지 못하기 때문입니다.

드림핵 커뮤니티 댓글 中

 

 

form이 한번도 변환되서 인식 못한거였다. url에 입력을 해주면.

 

짜자잔~