웹해킹 1주차

2025. 1. 19. 00:05· 웹해킹
목차
  1. 1-1. 개발자 도구의 Sources 탭 기능을 활용해 플래그를 찾아보세요. 플래그 형식은 DH{...} 입니다.
  2. DH{2ed07940b6fd9b0731ef698a5f0c065be9398f7fa00f03ed9da586c3ed1d54d5}
  3. 1-2. 해당 파일의 app.py 코드 해석 (코드리뷰)
  4. (1) 연결 가능한 URL
  5. (2) 각 엔드포인트 기능 설명
  6. (3) 사용되는 SQL 쿼리 설명
  7. (4) POST /board/<post_id> 사용 시 브라우저, 웹 서비스, 데이터베이스 간의 상호작용 과정
  8.  
  9. 2-1 https://dreamhack.io/wargame/challenges/6 개발자 도구랑 버프슈트로 풀어보기!!
  10.  DH{7952074b69ee388ab45432737f9b0c56}

1-1. 개발자 도구의 Sources 탭 기능을 활용해 플래그를 찾아보세요. 플래그 형식은 DH{...} 입니다.

플래그 위치 : main.4c6e144e.map

DH{2ed07940b6fd9b0731ef698a5f0c065be9398f7fa00f03ed9da586c3ed1d54d5}

1-2. 해당 파일의 app.py 코드 해석 (코드리뷰)

#!/usr/bin/env python3
import os
import pymysql
from flask import Flask, abort, redirect, render_template, request

PAGINATION_SIZE = 10

app = Flask(__name__)
app.secret_key = os.urandom(32)

def connect_mysql():
    conn = pymysql.connect(host='db',
                           port=3306,
                           user=os.environ['MYSQL_USER'],
                           passwd=os.environ['MYSQL_PASSWORD'],
                           db='board',
                           charset='utf8mb4')
    cursor = conn.cursor()
    return conn, cursor

@app.route('/')
def index():
    return redirect('/board')

@app.route('/board')
def board():

    page = request.args.get('page')
    page = int(page) if page and page.isdigit() and int(page) > 0 else 1

    ret = []

    conn, cursor = connect_mysql()
    try:
        query = 'SELECT _id, title FROM posts ORDER BY _id DESC LIMIT %s, %s'
        cursor.execute(query, ((page - 1) * PAGINATION_SIZE, PAGINATION_SIZE))
        ret = cursor.fetchall()
    except Exception as e:
        print(e, flush=True)
        abort(400)
    finally:
        cursor.close()
        conn.close()

    return render_template('board.html', page=page, ret=ret)

@app.route('/board/<post_id>')
def board_post(post_id):

    if not post_id or not post_id.isdigit() or int(post_id) < 1:
        abort(400)

    ret = None

    conn, cursor = connect_mysql()
    try:
        query = 'SELECT title, content FROM posts WHERE _id = %s'
        cursor.execute(query, (post_id))
        ret = cursor.fetchone()
    except Exception as e:
        print(e, flush=True)
        abort(400)
    finally:
        cursor.close()
        conn.close()

    if not ret:
        abort(404)

    return render_template('post.html', title=ret[0],
                           content=ret[1], post_id=post_id)

@app.route('/write_post', methods=['POST'])
def write_post():
    if 'title' not in request.form or 'content' not in request.form:
        return render_template('write_post.html')

    title = request.form['title']
    content = request.form['content']

    conn, cursor = connect_mysql()
    try:
        query = 'INSERT INTO posts (title, content) VALUES (%s, %s)'
        cursor.execute(query, (title, content))
        conn.commit()
    except Exception as e:
        print(e, flush=True)
        abort(400)
    finally:
        cursor.close()
        conn.close()

    return redirect('/board')

@app.route('/modify_post', methods=['POST'])
def modify_post():
    post_id = request.form['post_id']

    if not post_id or not post_id.isdigit() or int(post_id) < 1:
        abort(400)

    if 'title' not in request.form or 'content' not in request.form:
        conn, cursor = connect_mysql()
        try:
            query = 'SELECT title, content FROM posts WHERE _id = %s'
            cursor.execute(query, (post_id, ))
            ret = cursor.fetchone()
        except Exception as e:
            print(e, flush=True)
            abort(400)
        finally:
            cursor.close()
            conn.close()

        if not ret:
            abort(404)

        return render_template('modify_post.html', title=ret[0],
                               content=ret[1], post_id=post_id)

    title = request.form['title']
    content = request.form['content']

    conn, cursor = connect_mysql()
    try:
        query = 'UPDATE posts SET title=%s, content=%s WHERE _id = %s'
        cursor.execute(query, (title, content, post_id, ))
        conn.commit()
    except Exception as e:
        print(e, flush=True)
    finally:
        cursor.close()
        conn.close()

    return redirect(f'/board/{post_id}')

@app.route('/delete_post', methods=['POST'])
def delete_post():

    post_id = request.form['post_id']
    if not post_id or not post_id.isdigit() or int(post_id) < 1:
        abort(400)

    if 'answer' not in request.form:
        return render_template('delete_post.html', post_id=post_id)

    if request.form['answer'] == 'y':
        conn, cursor = connect_mysql()
        try:
            query = 'DELETE FROM posts WHERE _id = %s'
            cursor.execute(query, (post_id, ))
            conn.commit()
        except Exception as e:
            print(e, flush=True)
        finally:
            cursor.close()
            conn.close()

        return redirect('/board')

    return redirect(f'/board/{post_id}')

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8000)

(1) 연결 가능한 URL

  • /: 기본 URL로, /board로 리다이렉트
  • /board: 게시판 목록을 출력
    • 쿼리 파라미터 page로 페이지를 이동할 수 있음
  • /board/<post_id>: 특정 게시글의 내용을 보여줌
  • /write_post: 게시글 작성 페이지로 이동하거나, POST 요청 시 새 게시글을 생성
  • /modify_post: 특정 게시글 수정 페이지로 이동하거나, POST 요청 시 게시글을 수정
  • /delete_post: 특정 게시글 삭제 확인 페이지로 이동하거나, POST 요청 시 게시글을 삭제

(2) 각 엔드포인트 기능 설명

  • index: 기본 페이지로, /board로 리다이렉트
  • board: 게시판 목록을 불러와 board.html에 렌더링
  • board_post: 특정 게시글의 제목과 내용을 불러와 post.html에 렌더링
  • write_post: 게시글 작성 페이지를 보여주거나, POST 요청으로 새로운 게시글을 데이터베이스에 추가
  • modify_post: 특정 게시글 수정 페이지를 보여주거나, POST 요청으로 해당 게시글을 수정
  • delete_post: 특정 게시글 삭제 확인 페이지를 보여주거나, POST 요청으로 해당 게시글을 삭제

(3) 사용되는 SQL 쿼리 설명

SELECT _id, title FROM posts ORDER BY _id DESC LIMIT %s, %s;

게시글 목록 조회 (board 엔드포인트)

  • 최신 게시글 순으로 정렬하여 페이지 단위로 _id와 title을 조회
  • LIMIT는 페이지 번호와 페이지 크기를 기반으로 데이터 범위를 제한
SELECT title, content FROM posts WHERE _id = %s;

특정 게시글 조회 (board_post 엔드포인트)

  • 특정 게시글의 _id를 기반으로 title과 content를 조회
INSERT INTO posts (title, content) VALUES (%s, %s);

새 게시글 추가 (write_post 엔드포인트)

  • 새 게시글의 title과 content를 데이터베이스에 삽입합니다.
UPDATE posts SET title=%s, content=%s WHERE _id = %s;

게시글 수정 (modify_post 엔드포인트)

  • 게시글의 title과 content를 수정합니다. 수정할 게시글은 _id로 식별됩니다.
DELETE FROM posts WHERE _id = %s;

게시글 삭제 (delete_post 엔드포인트)

  • 특정 게시글을 _id를 기준으로 삭제합니다.

 

(4) POST /board/<post_id> 사용 시 브라우저, 웹 서비스, 데이터베이스 간의 상호작용 과정

  1. 브라우저
    • 사용자가 write_post.html에서 제목과 내용을 입력하고 "post" 버튼을 클릭
    • 브라우저는 입력된 데이터를 HTTP POST 요청으로 /write_post URL에 전송
  2. 웹 서비스 (Flask)
    • Flask는 write_post 엔드포인트를 처리
    • POST 요청의 form 데이터를 읽고, 제목(title)과 내용(content) 값을 추출
    • 데이터베이스에 삽입하는 SQL 쿼리(INSERT INTO posts ...)를 실행
    • 삽입이 완료되면 /board로 리다이렉트하여 게시판 목록을 다시 보여
  3. 데이터베이스 (MySQL)
    • Flask에서 전달받은 쿼리를 실행하여 posts 테이블에 새 레코드를 추가
    • 삽입이 성공하면 변경 사항을 커밋
  4. 결과 반환
    • 웹 서비스는 브라우저에 리다이렉트 응답을 반환
    • 브라우저는 /board 페이지를 새로고침하여 업데이트된 게시글 목록을 표시

 

2-1 https://dreamhack.io/wargame/challenges/6 개발자 도구랑 버프슈트로 풀어보기!!

#!/usr/bin/python3
from flask import Flask, request, render_template, make_response, redirect, url_for

app = Flask(__name__)

try:
    FLAG = open('./flag.txt', 'r').read()
except:
    FLAG = '[**FLAG**]'

users = {
    'guest': 'guest',
    'admin': FLAG
}

@app.route('/')
def index():
    username = request.cookies.get('username', None)
    if username:
        return render_template('index.html', text=f'Hello {username}, {"flag is " + FLAG if username == "admin" else "you are not admin"}')
    return render_template('index.html')

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'GET':
        return render_template('login.html')
    elif request.method == 'POST':
        username = request.form.get('username')
        password = request.form.get('password')
        try:
            pw = users[username]
        except:
            return '<script>alert("not found user");history.go(-1);</script>'
        if pw == password:
            resp = make_response(redirect(url_for('index')) )
            resp.set_cookie('username', username)
            return resp 
        return '<script>alert("wrong password");history.go(-1);</script>'

app.run(host='0.0.0.0', port=8000)

코드를 분석해보면 guest를 통해 웹사이트 상에서 로그인하여 guest로 로그인을 할 수 있는 것을 알 수 있다

여기서 판별자가 username의 쿠키값을 받아와서 판별을 하게 되는데 웹사이트 상에서 guest로 로그인을 한 뒤, 개발자 도구를 켜서 웹사이트의 쿠키가 admin으로 인식할 수 있도록 username의 값을 admin으로 설정을 한 뒤, 새로고침을 해보면 플래그 값이 띄워지는것을 볼 수 있다.

 DH{7952074b69ee388ab45432737f9b0c56}

 

저작자표시 (새창열림)

'웹해킹' 카테고리의 다른 글

웹해킹 5주차  (0) 2025.02.24
웹해킹 4주차  (0) 2025.02.18
웹해킹 3주차  (0) 2025.02.10
웹해킹 2주차  (0) 2025.01.30
웹해킹 0주차  (0) 2025.01.12
  1. 1-1. 개발자 도구의 Sources 탭 기능을 활용해 플래그를 찾아보세요. 플래그 형식은 DH{...} 입니다.
  2. DH{2ed07940b6fd9b0731ef698a5f0c065be9398f7fa00f03ed9da586c3ed1d54d5}
  3. 1-2. 해당 파일의 app.py 코드 해석 (코드리뷰)
  4. (1) 연결 가능한 URL
  5. (2) 각 엔드포인트 기능 설명
  6. (3) 사용되는 SQL 쿼리 설명
  7. (4) POST /board/<post_id> 사용 시 브라우저, 웹 서비스, 데이터베이스 간의 상호작용 과정
  8.  
  9. 2-1 https://dreamhack.io/wargame/challenges/6 개발자 도구랑 버프슈트로 풀어보기!!
  10.  DH{7952074b69ee388ab45432737f9b0c56}
'웹해킹' 카테고리의 다른 글
  • 웹해킹 4주차
  • 웹해킹 3주차
  • 웹해킹 2주차
  • 웹해킹 0주차
유민기
유민기
유민기
Youminki
유민기
전체
오늘
어제
  • 분류 전체보기 (45)
    • 백준 : BFS (5)
    • 알고리즘(이충기 교수) (4)
    • 웹해킹 (6)
    • 백준 : 동적 프로그래밍 (11)
    • 백준 : 분할정복 (5)
    • 백준 : backtraking (2)
    • 컴퓨터 보안(한승철 교수) (4)
    • FrontEnd (4)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

최근 댓글

최근 글

hELLO · Designed By 정상우.v4.2.0
유민기
웹해킹 1주차
상단으로

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.