Quest. xss 취약점이 있는지 알아봅시다. 다음 화면을 띄워보세요.

경고창을 누른 후 화면 (Welcome success 출력)

 

 

[난이도-low]
Hint1. 스크립트 태그 안에 alert함수를 넣어 경고창을 띄울 수 있습니다.
Hint2. First name과 Last name에 각각 공격을 하면 어떤 결과가 나오는지 알아봅시다

 

먼저 First name에 abc, Last name에 123을 넣어보았더니 Welcome abc 123이 출력되었다.

 

hint 1을 참고해 경고창을 띄우기 위해 스크립트 태그 안에 alert함수를 넣어보기로 하였다. 

Welcome success를 출력시키기 위해 First name에는 스크립트 태그를 작성하고 Last name에는 success를 입력해 보았다.

 

< 입력 >

First name : <script>alert("XSS")</script>

Last name : success

 

이를 입력해주었더니 아래와 같은 결과가 나왔다.

 

[난이도-medium]
Hint1. xss 공격을 막는 함수가 있는 것 같습니다. 소스코드를 살펴봅시다.
Hint2. xss_check_4가 어떤 함수인지 알아봅시다.
Hint3. 함수 정보는 functions_external에 저장되어 있습니다.
Hint4. addslashes함수가 어떤 문자를 필터링하는지 알아봅시다.

 

  low단계와 동일하게 입력해보았지만 경고창은 뜨지 않았다. hint1처럼 xss 공격을 막는 함수가 있는 것 같다.

 

페이지 소스코드를 살펴보았다.

코드를 보면 alert(\"XSS\")라고 바뀐 것을 확인할 수 있다.

이 문제는 medium 단계이므로 security level이 1에 해당한다. 그리고 xss_get.php 파일을 이용하는 것을 볼 수 있다. 

 

/var/www/bWAPP 으로 이동해 xss_get.php파일을 열어주었다.

security_level이 1이므로 xss_check_4함수를 이용한다. 

 

xss_check_4 함수를 찾아보기 위해 functions_external.php를 열어보았다.

addslashes 함수
: 매개변수로 넘겨준 문자열 안에 single quote(') 혹은 double quote("), 백슬래쉬(\), NULL 바이트가 포함되어 있다면 해당 문자 앞에 역슬래시(\)를 추가해 주는 함수

addslashes함수를 통해 alert 내 문자열에 역슬래시가 추가되었음을 알 수 있다.

 

이를 우회하기 위해 따옴표가 아닌 /로 문자열을 감싸주어 입력해 보았다.

 

<입력>

First name : <script>alert(/XSS/)</script>

Last name : success

 

 

그랬더니 위와 같은 결과가 나왔다.

 

 

[난이도-high]

필터링을 우회해서 공격해보세요. 그러나 공격이 통하지 않습니다. 그 이유를 정리해주세요.

Hint1. xss_check_3 함수를 살펴봅시다.
Hint2. htmlspecialchars함수가 원인인 것 같습니다. 어떤 함수인지 알아봅시다.

medium 페이지 소스코드에서 볼 수 있듯이 난이도 high는 security_level 2에 해당된다. 

case 2를 보면 xss_check_3 함수를 사용한다. 

 

이 함수에서는 htmlspecialchars함수를 이용한다. 이 함수는 data로 받은 문자열을 UTF-8로 반환하는데 ", ' 둘 다 변환되기 때문에 문제를 풀 수 없다.

 

6주차의 내용은 아래 링크를 참고

https://jini00.tistory.com/141

 

https://github.com/jini-coding/ott_review_project

 

GitHub - jini-coding/ott_review_project: SISS 2022 겨울방학 웹프로젝트 - OTT별 콘텐츠 리뷰 홈페이지 제작

SISS 2022 겨울방학 웹프로젝트 - OTT별 콘텐츠 리뷰 홈페이지 제작. Contribute to jini-coding/ott_review_project development by creating an account on GitHub.

github.com

 

DB 수정

  원래 Table을 두개로 나누어 나중에 JOIN하는 방식으로 구성하려했다. 하지만 게시판을 만들면서 데이터들을 정리하기 않음을 느껴 처음에 제작했던 DB 방식으로 다시 수정하였다.

 

  아래 사진은 해당 table(contentsreview)의 형태이다.

 

DB에 값 넣기

- process_create.php

<?php
$conn=mysqli_connect("localhost", "root", "비밀번호", "contents_review");
var_dump($_POST);
$sql="
  INSERT INTO contentsreview
    (title, ott, category, score, comments, created)
    Value(
      '{$_POST['title']}',
      '{$_POST['ott']}',
      '{$_POST['category']}',
      '{$_POST['score']}',
      '{$_POST['comments']}',
      NOW()
      )";
$result=mysqli_query($conn, $sql);
if($result===false){
  echo '저장하는 과정에서 문제가 발생하였습니다.';
  error_log(mysqli_effor($conn));
}
else{
  echo '저장에 성공했습니다. <a href="index.php">돌아가기</a>';
}

?>

  먼저 DB와 연동을 시켜주었다. 저번주와 다르게 DB를 다시 수정하면서 table을 하나로 통일했기 때문에 위의 코드가 잘 작동되었다.

 

각 ott별 후기 작성 게시판

이런식으로 페이지에 내용을 입력해주고 작성 버튼을 눌러준다.

위 후기들은 네이버 블로그 등을 통해 발췌한 후기들이다.

 

작성 버튼을 눌러주면 위와 같은 페이지가 뜨고 DB에 저장된다.

 

이를 DB를 통해 확인해보면 아래와 같이 잘 저장되었다는 것을 확인할 수 있다.

 

아래에 설명할 BOARD 게시판을 통해서도 확인할 수 있다.

 

- 삭제 기능 추가

<?php 
   $conn = mysqli_connect('localhost','root','1111','CONTENTS_REVIEW');

   $sql = " DELETE FROM  WHERE  = {}";

   $result = mysqli_query($conn, $sql);


 ?>
 <script type="text/javascript">alert("삭제되었습니다.");</script>
 <meta http-equiv="refresh" content="- url=/경로">

  contents_review DB와 연결시킨 뒤 sql 변수에 데이터를 삭제하는 쿼리문을 지정해주었다.
글이 정상적으로 삭제되었음을 알리기 위해 js를 이용해서 alert 창이 뜨도록 하고 페이지를 refresh 시켜 원래의 게시판 페이지로 돌아가도록 했다.
  여기서 php구문 안에 if-else문을 넣어서 삭제 실패/성공에 대한 메세지를 띄워도 되는데, 이럴 경우 아예 다른 페이지로 넘어가야하거나 깔끔하게 원래 페이지로 돌아올 방법이 마땅치 않아서 위의 코드의 방식을 적용했다.
(하지만 추후에 수정될 수 있음)

 

Board 게시판 제작

- Board.php

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <style>
    li{
      text-align: left;
      padding: 10px;
      color :black;
      font-size: 20px;
      font-family : sans-serif;
    }
    h2{
      text-align: center;
      padding: 35px;
      color :#A50000;
      font-size: 5em;
      font-family : sans-serif;
    }
    body{
      background-color:#FFD8D8;
      height : 4000px;
    }
  </style>
  <title>board</title>
</head>
<body>
<h2> BOARD </h2>
    <?php
      $conn=mysqli_connect("localhost", "root", "비밀번호", "contents_review");
      $sql2=" SELECT * FROM contentsreview";
      $result=mysqli_query($conn, $sql2);
      $list=' ';
      while($row=mysqli_fetch_array($result)){
        if($row['ott']=='netflix'){
          $row['ott']='NETFLIX';
        }
        if($row['ott']=='tving'){
          $row['ott']='TVING';
        }
        if($row['ott']=='watcha'){
          $row['ott']='WATCHA';
        }
        if($row['ott']=='disney+'){
          $row['ott']='DISNEY+';
        }
        $list = $list."<li>{$row['ott']} - {$row['title']}(별점 : {$row['score']}) : {$row['comments']}";
      }
      echo $list;
      mysqli_close($conn);
    ?>


</body>
</html>

  먼저 DB와 연결을 시켜주었다. $sql2에 SELECT문을 넣어 contentsreview 테이블을 가지고 올 수 있도록 하였다. $row에 mysqli_fetch_array함수를 이용하여 DB의 값을 받아왔다. 여기서 if문을 이용해 $row['ott']의 출력형태를 모두 대문자로 바꿔주었다. 그리고 $list에 li태그를 이용해 ott, title, score, comments를 저장해주었으며 이를 마지막에 echo하도록 하여 출력해주었다.

 

  아래 사진은 임의로 DB에 저장한 데이터들이 출력된 모습이다. 아직 페이지를 꾸미지는 않았기에 다음주에 더 보완하고자 한다.

 

 

MISSON. 버프스위트를 이용해 사용자들의 id와 password를 획득하자.

 

hello를 입력해주고 버프 스위트를 확인해주었더니 title=hello&action=search라고 적힌 것을 볼 수 있었다.

 

Quest. 데이터베이스 정보를 알아내세요.
> Hint 1
DB버전을 확인하는 쿼리를 찾아보세요.

> Hint 2
@@vesion, database()으로 버전과 이름을 알 수 있습니다.
시스템 변수 및 함수 설명
database() DB 이름
user() 사용자 ID
system_user() 권리자 권한 사용자 ID
version() DB 서버의 버전
@@datadir DB 서버가 존재하는 경로

칼럼 번호에 맞춰  0' union select all 1,@@version,database(),4,5,6,7#를 입력해주면 결과가 다음과 같다.

버전은 5.0.96-0ubuntu3이고 DB의 이름은 bWAPP임을 알 수 있다.

 

 

Quest. 데이터베이스에 존재하는 모든 테이블 명을 출력하세요.
> Hint1
GET방식과 쿼리는 같습니다.

4번 줄에 넣어주어야 하므로 4번 자리에 table_name을 넣어주었다.

0' union select all 1,2,3,table_name,5,6,7 from information_schema.tables# 

 

Quest. 사용자 정보가 들어있는 칼럼을 출력하세요.

> Hint1
사용자 정보는 users테이블에 있습니다.

user 테이블을 이용하여

0' union select all 1,column_name,3,4,5,6,7 from information_schema.columns where table_name='users'#

를 입력해주었더니 다음과 같은 결과를 얻을 수 있었다.

 

Quest. 사용자의 id, password, secret, login 정보를 출력해봅시다.
> Hint1
https://crackstation.net/ 해시값을 크랙할 수 있는 사이트입니다.

> Hint2
password값으로 나온 해시값을 크랙해서 나온 결과를 첨부하면 됩니다.

0' union select all 1,id,login,email,password,6,7 from users# 를 입력해주어서 다음과 같은 결과를 얻을 수 있었다.

 

해시값을 크랙할 수 있는 사이트에 접속해 위에서 나온 해시값을 입력해주었다.

비밀번호는 bug 이다.

Quest. SQL 기본 지식 정리하기

 

SQL(Structured Query Language) 이란? 
: 관계형 데이터베이스 관리 시스템(RDBMS)의 데이터를 관리하기 위해 설계된 프로그래밍 언어

 

[ 기본 용어 ]


Query (질의) : 데이터베이스에게 특정한 데이터를 보여달라는 클라이언트의 요청

▶ Table (테이블) : 특정한 종류의 데이터를 구조적으로 묶은 목록

Relation (릴레이션) : 관계형 데이터베이스에서 정보를 구분하여 저장하는 기본 단위 

<용어>

① 속성 (Attribute) = 필드 (Field) = 열 (Column) = 세로

    - 속성 수 = 차수 (Degree)

② 튜플 (Tuple) = 레코드 (Record) = 행 (Row) = 가로

    - 튜플 수 = 카디널리티 (Cardinality)

③ 도메인 : 하나의 속성이 가질 수 있는 값들의 집합

    - 더 이상 분리되지 않는 값들

④ 널 (Null) : 아직 모르거나 해당되는 사항이 없음을 표현하는 특별한 값

 

<특성>

① 튜플의 유일성 : 하나의 릴레이션에는 동일한 튜플이 존재할 수 없다.

② 튜플의 무순서 : 하나의 릴레이션에서 튜플 사이의 순서는 무의미하다.

③ 속성의 무순서 : 하나의 릴레이션에서 속성 사이의 순서는 무의미하다.

④ 속성의 원자성 : 속성 값으로 원자 값만 사용할 수 있다.

 

▶ 키 (Key) : 릴레이션에서 튜플들을 구별하는 역할을 하는 속성 또는 속성들의 집합

<특성>

① 유일성 : 한 릴레이션에서 모든 튜플은 서로 다른 키 값을 가져야 함

② 최소성 : 꼭 필요한 최소한의 속성들로만 키를 구성

 

<종류>

① 슈퍼키 : 유일성을 만족하는 속성 또는 속성들의 집합

② 후보키 : 유일성과 최소성을 만족하는 속성 또는 속성들의 집합

③ 기본키 : 후보키 중에서 기본적으로 사용하기 위해 선택한 키

④ 대체키 : 기본키로 선택되지 못한 후보키

⑤ 외래키 : 다른 릴레이션의 기본키를 참조하는 속성 또는 속성들의 집합

 

 

[SQL 문법의 종류]

 

① DDL (Data Definition Language, 데이터 정의 언어)

    - 각 릴레이션을 정의하기 위해 사용하는 언어 (CREATE, ALTER, DROP, ...)

② DML (Data Manipulation Language, 데이터 조작 언어)

    - 데이터를 추가/수정/삭제하기 위한, 즉 데이터 관리를 위한 언어 (SELECT, INSERT, UPDATE, ...)

③ DCL (Data Control Language, 데이터 제어 언어)

    - 사용자 관리 및 사용자별로 릴레이션 또는 데이터를 관리하고 접근하는 권한을 다루기 위한 언어 (GRANT, REVOKE, ...)

 

▶ SELECT

SELECT [ALL | DISTINCT] 컬럼명 [,컬럼명...]
 FROM 테이블명 [,테이블명...]
 [WHERE 조건식]
 [GROUP BY 컬럼명 [HAVING 조건식]]
 [ORDER BY 컬럼명]
 GROUP BY 컬럼명[,컬럼명...]
 ORDER BY 컬럼명[,컬럼명...]
  • SELECT : 테이블로부터 얻어낼 열을 나열
  • FROM : 정보를 얻어낼 대상인 테이블
  • WHERE : 열을 선택하는 조건에 부합하는 값만 출력
  • GROUP BY : 특정 열을 기준으로 묶어 나타내고 싶은 경우 사용
  • HAVING : 집계 함수 ( COUNT, SUM, MIN, MAX ... ) 을 포함하는 조건에 부합하는 값 출력
  • ORDER BY : 키워드를 통해 특정 열을 기준으로 전체 정보를 정렬

 

Quest. SQL injection에 대해 정리하기

SQL Injection 이란?
악의적인 사용자가 보안상의 취약점을 이용하여, 임의의 SQL 문을 주입하고 실행되게 하여 데이터베이스가 비정상적인 동작을 하도록 조작하는 행위

 

[공격 기법]

 

인증 우회

: 로그인 페이지를 타겟으로 행하는 공격

- SQL 쿼리문의 true/false의 논리적 연산 오류를 이용해 로그인 인증 쿼리문이 무조건 true의 결과값이 나오게 하여 인증을 무력화 시키는 원리

 

▶ 데이터 노출

: 타겟의 데이터 탈취를 목적으로 하는 공격

 

Error based SQL Injection: 데이터베이스의 에러메세지를 기반으로 하는 공격

- 일부러 에러를 발생시켜 그 정보를 바탕으로 데이터베이스의 정보(DB이름, Table, Column, ...)를 탈취하는 것이 가능한 공격

Union SQL Injection : Union 연산자를 이용해 악의적인 쿼리문을 파라미터에 추가함으로 원하는 정보를 탈취하는 방식

Blind SQL Injection : 데이터베이스로부터 특정한 값이나 데이터를 전달받지 않고,단순히 참과 거짓의 정보만 알 수 있을 때 사용

 

 

[대응 방안]

 

▶ 입력 값에 대한 검증

: 검증 로직을 추가하여 미리 설정한 특수문자들이 들어왔을 때 요청을 막아낸다.

 

▶ Error Message 노출 금지

: 데이터베이스 에러 발생 시 따로 처리를 해주지 않았다면, 에러가 발생한 쿼리문과 함께 에러에 관한 내용을 반환해 준다. 여기서 테이블명, 컬럼명, 쿼리문이 노출이 될 수 있기 때문에, 오류발생 시 사용자에게 보여줄 수 있는 페이지를 따로 제작하거나 메시지박스를 띄우도록 해야한다.

 

▶ Prepared Statement 구문사용

: 사용자의 입력 값이 데이터베이스의 파라미터로 들어가기 전에 DBMS가 미리 컴파일 하여 실행하지 않고 대기하기 때문에 그 후 사용자의 입력 값을 문자열로 인식하게 하여 공격쿼리가 들어간다고 하더라도, 사용자의 입력은 이미 의미 없는 단순 문자열 이기 때문에 전체 쿼리문도 공격자의 의도대로 작동하지 않는다.

 

▶ 웹 방화벽 사용

: 웹 방화벽은 소프트웨어 형, 하드웨어 형, 프록시 형 이렇게 세가지 종류로 나눌 수 있는데 소프트웨어 형은 서버 내에 직접 설치하는 방법이고, 하드웨어 형은 네트워크 상에서 서버 앞 단에 직접 하드웨어 장비로 구성하는 것이며 마지막으로 프록시 형은 DNS 서버 주소를 웹 방화벽으로 바꾸고 서버로 가는 트래픽이 웹 방화벽을 먼저 거치도록 하는 방법이다.

 

 


MISSION. 영화 정보가 들어 있는 데이터베이스에 저장된 사용자들의 아이디와 비밀번호를 획득하자.

 

Quest. SQL Injection이 통하는지 아닌지 확인해보고, SQL Injection이 된다면
데이터베이스의 서버 종류가 무엇인지 확인해봅시다.

> hint1
데이터베이스 서버에 질의하는 쿼리에 문법 오류를 발생시켜봅시다.

> hint2
이번 문제에서 데이터베이스 서버에 질의하는 쿼리는 다음과 같습니다.
SELECT * FROM movies WHERE title LIKE '  '

> hint3
C에서도 printf(“hello” world”);는 오류를 발생시킵니다. SQL도 동일합니다.

> hint4
발생시킨 오류 문구에서 데이터베이스 서버 종류를 확인할 수 있습니다.

구글링 도중 작은 따옴표 (')를 치면 해당 DB에 취약점 여부를 파악할 수 있다는 사실을 알게 되었다. 

이를 통해 MySQL을 사용한다는 사실을 알 수 있었다.

 

Quest. 모든 영화자료를 출력해봅시다.

 

> hint1
SELECT문의 조건을 참으로 만들면 모든 자료를 출력할 수 있습니다.

> hint2
이번 문제에서 데이터베이스 서버에 질의하는 쿼리는 다음과 같습니다.
SELECT * FROM movies WHERE title LIKE '  '

 

' or 1=1 # 을 입력해주었더니 다음과 같은 결과가 나왔다.

위 입력 값을 통해 앞 문장을 닫아주고, 결과를 항상 참으로 만들어준다. 그리고 코드의 뒷부분을 주석으로 처리해준다.

 

Quest. 데이터베이스에서 호출하는 칼럼 수가 몇 개인지 알아내세요.

> hint1
UNION based SQL injection을 이용해봅시다.
UNION based SQL Injection이란?
2개 이상의 쿼리를 요청하여 결과를 얻는 UNION 연산자를 이용해 SQL Injection 공격

- 공격자는 이 연산자를 이용하여 원래의 요청에 한 개의 추가 쿼리를 삽입하여 정보를 얻어낸다.
- 공격이 가능한 전제 조건은 칼럼의 개수가 같아야 하고 데이터 형식도 같아야 한다.

 

칼럼의 수가 다르다는 에러가 발생하였다. 그렇기 때문에 칼럼 수를 알아내기 위해 칼럼을 하나씩 추가해 보도록 하겠다.

 

 

7까지 입력해주었더니 다음과 같은 결과가 나왔다. 따라서 칼럼의 개수는 7개이다.

그리고 각각 2, 3, 5, 4번 칼럼임을 알 수 있다.

 

Quest. 데이터베이스에 존재하는 모든 테이블 명을 출력하세요.
> hint1
데이터베이스 정보를 가진 특별한 데이터베이스가 있습니다. 그 데이터베이스를 통해 테이블 목록 출력해봅시다.

> hint2
information_schematables 테이블에서 데이터베이스의 테이블 목록을 불러올 수 있습니다.

> hint3
(데이터베이스 이름).(테이블 이름) 라는 방식으로 테이블에 접근할 수 있습니다.

> hint4
tables 테이블에서 테이블 이름은 table_name 칼럼에서 확인할 수 있습니다.

imformation_schema란?
서버 내에 존재하는 DB의 메타 정보(테이블, 칼럼, 인덱스 등의 스키마 정보)를 모아둔 DB

- 읽기 전용으로 사용자가 직접 수정 또는 관여가 불가하다.
- 이용 방법 : SELECT TABLE_NAME FROM information_schema.TABLES

0' union select all 1,table_name,3,4,5,6,7 from information_schema.tables# 를 입력해주어 테이블 목록을 확인해주었다.

 

Quest. 우리가 출력한 테이블 명에서 사용자 정보가 있을 것 같은 테이블이 있습니다.
그 테이블의 칼럼 명을 출력해봅시다.
> hint1
아까와 같이 information_schema 데이터베이스의 테이블 중 하나를 이용합시다.

> hint2
information_schema의 columns 테이블에서 칼럼 목록을 불러올 수 있습니다.

> hint3
columns 테이블에서 칼럼 이름은 column_name 칼럼에서 확인할 수 있습니다.

> hint4
where 절을 통해 ***** 테이블 정보만 출력해봅시다.

> hint5
users 테이블에서 사용자 정보를 알 수 있습니다.

0' union select all 1,column_name,3,4,5,6,7 from information_schema.columns where table_name='users'# 을 입력해주었더니 다음과 같은 결과를 얻을 수 있었다.

 

 

Quest. 사용자의 id, password, email 정보 등을 출력해봅시다.

확인하고 싶은 칼럼인 id(순서), login(ID), email, password(비밀번호 해시값)를 2,3,4,5에 넣어주었다. 

0' union select all 1,id,login,email,password,6,7 from users# 을 입력해주고 다음과 같은 결과를 얻을 수 있었다.

 

+ Recent posts