<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>hui_.log</title>
        <link>https://velog.io/</link>
        <description>여러가지 공부하는 Velog</description>
        <lastBuildDate>Fri, 31 Jul 2026 16:52:31 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>hui_.log</title>
            <url>https://velog.velcdn.com/images/hui_/profile/d6483d26-4624-43a8-93fb-f0d8bae4ae8b/social_profile.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. hui_.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/hui_" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[Dreamhack] DreamDocs]]></title>
            <link>https://velog.io/@hui_/Dreamhack-DreamDocs</link>
            <guid>https://velog.io/@hui_/Dreamhack-DreamDocs</guid>
            <pubDate>Fri, 31 Jul 2026 16:52:31 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>Introducing DreamDocs: a platform for browsing docs!</p>
</blockquote>
<p>flag를 내뱉는 조건을 아무리 살펴봐도 X-user 가 <code>admin</code>인 거랑 랜덤한 게시글 <code>id</code> 찾기 인 것 같아서 브루트포스 돌려보았더니 정답이였던 문제였다.</p>
<p>문제 파일 부터 빠르게 확인해보자</p>
<p><img src="https://velog.velcdn.com/images/hui_/post/e745e65c-359f-4d7c-ac2d-05ad92d887a0/image.png" alt=""></p>
<p>app.py는 다음과 같다.</p>
<pre><code class="language-py">from flask import Flask, render_template, request, jsonify, abort
import os
import random


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

FLAG =  &quot;flag&quot;

flag_doc_id = random.randint(100, 999)

documents = {
    flag_doc_id: {
        &#39;title&#39;: &#39;Confidential Report - Access Restricted&#39;,
        &#39;content&#39;: f&#39;This is a confidential internal document.\n\nDocument ID: {flag_doc_id}\nClassification: TOP SECRET\n\n&lt;!-- FLAG: {FLAG} --&gt;\n\nThis document contains sensitive information and should only be accessed by authorized personnel.&#39;,
        &#39;classification&#39;: &#39;confidential&#39;,
        &#39;author&#39;: &#39;System Administrator&#39;
    }
}

for i in range(1000):
    if i not in documents:
        uid = random.randint(0, 9)
        documents[i] = {
            &#39;title&#39;: f&#39;Document #{i:03d}&#39;,
            &#39;content&#39;: f&#39;This is document number {i}.\n\nContent: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.\n\nDocument ID: {i}\nCreated: 2025-01-{(i % 28) + 1:02d}\nAuthor: User{uid}&#39;,
            &#39;classification&#39;: &#39;public&#39; if random.randint(0, 2) == 0 else &#39;internal&#39;,
            &#39;author&#39;: f&#39;User{uid}&#39;
        }

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

@app.route(&#39;/share&#39;)
def share():
    return render_template(&#39;share.html&#39;)

@app.route(&#39;/doc/&lt;int:doc_id&gt;&#39;)
def view_document(doc_id):
    referer = request.headers.get(&#39;Referer&#39;, &#39;&#39;)
    user_level = request.headers.get(&#39;X-User&#39;, &#39;guest&#39;)

    if doc_id &lt; 0 or doc_id &gt;= 1000:
        abort(404)

    if doc_id not in documents:
        abort(404)

    document = documents[doc_id]

    if &#39;/share&#39; not in referer:
        return render_template(&#39;error.html&#39;, 
            message=&quot;Access denied. Documents can only be accessed from the share page.&quot;), 403

    if document[&#39;classification&#39;] == &#39;confidential&#39;:
        if user_level != &#39;admin&#39;:
            return render_template(&#39;error.html&#39;, 
                message=&quot;Insufficient privileges. Administrator access required.&quot;), 403

    elif document[&#39;classification&#39;] == &#39;internal&#39;:
        if user_level == &#39;guest&#39;:
            return render_template(&#39;error.html&#39;, 
                message=&quot;Internal documents require user authentication.&quot;), 401

    return render_template(&#39;document.html&#39;, doc=document, doc_id=doc_id)

@app.route(&#39;/api/docs&#39;)
def list_docs():
    SHOW_COUNT = 15
    user_level = request.headers.get(&#39;X-User&#39;, &#39;guest&#39;)
    visible_docs = []

    for doc_id, doc in documents.items():
        if doc[&#39;classification&#39;] == &#39;public&#39;:
            visible_docs.append({&#39;id&#39;: doc_id, &#39;title&#39;: doc[&#39;title&#39;], &#39;classification&#39;: doc[&#39;classification&#39;]})
        elif doc[&#39;classification&#39;] == &#39;internal&#39; and user_level != &#39;guest&#39;:
            visible_docs.append({&#39;id&#39;: doc_id, &#39;title&#39;: doc[&#39;title&#39;], &#39;classification&#39;: doc[&#39;classification&#39;]})
        elif doc[&#39;classification&#39;] == &#39;confidential&#39; and user_level == &#39;admin&#39;:
            visible_docs.append({&#39;id&#39;: doc_id, &#39;title&#39;: doc[&#39;title&#39;], &#39;classification&#39;: doc[&#39;classification&#39;]})
        if len(visible_docs) &gt;= SHOW_COUNT:
            break

    return jsonify(visible_docs)

if __name__ == &#39;__main__&#39;:
    app.run(host=&#39;0.0.0.0&#39;, port=8000, debug=False)</code></pre>
<p><code>docunents</code> 라는 변수에 FLAG가 담겨있는 게시글이 있음을 알 수 있다.</p>
<blockquote>
<p>FLAG 가 담긴 게시글을 찾아내면 된다!</p>
</blockquote>
<p>그럼 특정한 id의 docs를 읽는 라우터를 확인해보자</p>
<pre><code class="language-py">
@app.route(&#39;/doc/&lt;int:doc_id&gt;&#39;)
def view_document(doc_id):
    referer = request.headers.get(&#39;Referer&#39;, &#39;&#39;)
    user_level = request.headers.get(&#39;X-User&#39;, &#39;guest&#39;)

    if doc_id &lt; 0 or doc_id &gt;= 1000:
        abort(404)

    if doc_id not in documents:
        abort(404)

    document = documents[doc_id]

    if &#39;/share&#39; not in referer:
        return render_template(&#39;error.html&#39;, 
            message=&quot;Access denied. Documents can only be accessed from the share page.&quot;), 403

    if document[&#39;classification&#39;] == &#39;confidential&#39;:
        if user_level != &#39;admin&#39;:
            return render_template(&#39;error.html&#39;, 
                message=&quot;Insufficient privileges. Administrator access required.&quot;), 403

    elif document[&#39;classification&#39;] == &#39;internal&#39;:
        if user_level == &#39;guest&#39;:
            return render_template(&#39;error.html&#39;, 
                message=&quot;Internal documents require user authentication.&quot;), 401

    return render_template(&#39;document.html&#39;, doc=document, doc_id=doc_id)
</code></pre>
<p>확인해보면 id가 0 보다 크고 1000 이하여야 하고</p>
<p><code>document[&#39;classification&#39;] == &#39;confidential&#39;</code> 이면 </p>
<p><code>user_level</code>이 <code>admin</code>이 아니면 안보여준다.</p>
<p>우리 FLAG가 담겨있는 게시글은 <code>&#39;classification&#39;</code> 단계이기 때문에 <code>user_level</code>을 <code>admin</code>으로 맞춰주면 되겠다.</p>
<p>그리고 우리 FLAG가 담겨있는 게시글은 100부터 999 사이의 랜덤한 id 를 찾아내면 되겠다.</p>
<p>100부터 999사이라는 값은 매우 브루트 포스를 넣기에 좋은 상황을 의도적으로 연출 해주었기에 브루트 포스를 해주지 않는다면 상도덕에 어긋나는 것일 것이다. 아마도</p>
<p><a href="https://velog.io/@hui_/Dreamhack-random-test-%ED%92%80%EC%9D%B4">Burp Suite로 브루트 포스 요청 넣는법</a></p>
<p><img src="https://velog.velcdn.com/images/hui_/post/44f3360e-a861-411a-8472-8c94759221da/image.png" alt=""></p>
<p>이번엔 새로운 도구인 Cadio로 Automate를 해보았다.</p>
<p>Burp Suite보다 빨라서 더 좋은듯, ( 물론 설정을 할 수 있었겠지만 )</p>
<p>서로 있을 건 다 있기 때문에 더 직관적인 Cadio를 쓸 것 같다.</p>
<p>어쨌든 FLAG가 담긴 id 번호도 알아냈으니 FLAG도 Response 에서 찾으면</p>
<p><img src="https://velog.velcdn.com/images/hui_/post/889b9073-6b88-4b43-937c-6b91cc067e80/image.png" alt=""></p>
<p>성공</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Dreamhack] mongoboard 풀이]]></title>
            <link>https://velog.io/@hui_/Dreamhack-mongoboard-%ED%92%80%EC%9D%B4</link>
            <guid>https://velog.io/@hui_/Dreamhack-mongoboard-%ED%92%80%EC%9D%B4</guid>
            <pubDate>Fri, 31 Jul 2026 06:52:54 GMT</pubDate>
            <description><![CDATA[<h3 id="descreption">Descreption</h3>
<blockquote>
<p>node와 mongodb로 구성된 게시판입니다.
비밀 게시글을 읽어 FLAG를 획득하세요.</p>
</blockquote>
<ul>
<li>MongoDB &lt; 4.0.0</li>
</ul>
<p>이런걸 해본적이 없던 초심자의 입장에선 감이 안잡히는 문제였다.
모르면 풀기 어려운 문제이다.</p>
<p>빠르게 알아보자</p>
<p><img src="https://velog.velcdn.com/images/hui_/post/1ca24ee2-7798-43e4-a289-415bf226aef8/image.png" alt="">
문제파일 구조는 다음과 같다.</p>
<p>그런데 이번엔 코드에 <code>FLAG</code>가 적혀있지 않다.
<img src="https://velog.velcdn.com/images/hui_/post/b98b5d81-59c6-487b-a835-10a373268e4a/image.png" alt=""></p>
<p>FLAG를 어떻게 주는 정도 까지는 원래 다 알려주는거 아니였나</p>
<p>그럼 로컬에서 돌려봐서 어떻게 생겼는지 확인해보고자 실행해보았다</p>
<p><img src="https://velog.velcdn.com/images/hui_/post/d99a0a70-7520-4338-ad10-57d71f71b729/image.png" alt=""></p>
<p>Index.html 파일이 없어서 불러오기가 실패했다. 이런 메인 html 파일 조차 주지 않았다.</p>
<p>사이트가 어떻게 생겼는지는 봐야하니 서버를 실행하고 들어가보자</p>
<p><img src="https://velog.velcdn.com/images/hui_/post/e5ade9a5-b19e-42c0-9ef2-ee9dbcf0c31c/image.png" alt=""></p>
<p>FLAG 게시글은 클릭하니 들어갈 수 없고, 다른 게시글은 제대로 작동하니
Proxy로 게시판을 클릭할때 요청이 어떻게 들어가는지 알아보자.</p>
<p><a href="https://velog.io/@hui_/Dreamhack-random-test-%ED%92%80%EC%9D%B4">Proxy 사용법 게시글 | Burp Suite</a></p>
<p><img src="https://velog.velcdn.com/images/hui_/post/10435add-0280-45fd-921d-28f8ed75235e/image.png" alt=""></p>
<p>GET 요청으로 api/board/<code>id Hex 문자열</code> 형태로 보드가 열리는 것 같다.</p>
<blockquote>
<p>이거 그럼 <code>FLAG</code> 보드의 id를 알면 내용 확인할 수 있는 것 아닐까?</p>
</blockquote>
<p>목표를 정했으니
우리가 볼 수 있는 핵심 내용인 <code>index.js</code>를 살펴보자</p>
<pre><code class="language-js">module.exports = function(app, MongoBoard){
    app.get(&#39;/api/board&#39;, function(req,res){
        MongoBoard.find(function(err, board){
            if(err) return res.status(500).send({error: &#39;database failure&#39;});
            res.json(board.map(data =&gt; {
                return {
                    _id: data.secret?null:data._id,
                    title: data.title,
                    author: data.author,
                    secret: data.secret,
                    publish_date: data.publish_date
                }
            }));
        })
    });

    app.get(&#39;/api/board/:board_id&#39;, function(req, res){
        MongoBoard.findOne({_id: req.params.board_id}, function(err, board){
            if(err) return res.status(500).json({error: err});
            if(!board) return res.status(404).json({error: &#39;board not found&#39;});
            res.json(board);
        })
    });

    app.put(&#39;/api/board&#39;, function(req, res){
        var board = new MongoBoard();
        board.title = req.body.title;
        board.author = req.body.author;
        board.body = req.body.body;
        board.secret = req.body.secret || false;

        board.save(function(err){
            if(err){
                console.error(err);
                res.json({result: false});
                return;
            }
            res.json({result: true});

        });
    });
}
</code></pre>
<p>보드를 불러오는 라우터들이 보인다.</p>
<p>이런게 있다면 우선 api를 호출해보는걸 참을 수 없다</p>
<p><img src="https://velog.velcdn.com/images/hui_/post/846fa1c6-584f-4c22-a362-d50f6ba52bd4/image.png" alt=""></p>
<p>주소창에 그대로 적어보자</p>
<p><img src="https://velog.velcdn.com/images/hui_/post/7cbc9114-28c1-4ca5-9fa6-bb5773cdbdcb/image.png" alt=""></p>
<p>게시판 정보들 json을 뱉어냈다.</p>
<p>우린 FLAG 게시판의 id를 알아낸다면 되니 FLAG 게시글의 id를 찾아보면...</p>
<p><img src="https://velog.velcdn.com/images/hui_/post/de71943b-be6c-4575-816e-96be34d9ab4c/image.png" alt=""></p>
<p>이런 FLAG 게시글의 id가 null 값이다.</p>
<p>이정도 수준에서 보드 id를 순순히 내주었다면 B4 문제였겠지만. 쉽게 주지 않는다.</p>
<p>여기서 막혀서 검색해보았다.</p>
<p><a href="https://www.mongodb.com/ko-kr/docs/manual/reference/method/ObjectId/">https://www.mongodb.com/ko-kr/docs/manual/reference/method/ObjectId/</a></p>
<p>몽고DB는 ObjectID를 이렇게 생성한다고 한다.</p>
<p><img src="https://velog.velcdn.com/images/hui_/post/349149be-2970-4958-ab8d-6ae3b29d8255/image.png" alt=""></p>
<p>구글번역 이슈로 알아듣기가 어려운데, 포인트는</p>
<ul>
<li>4바이트는 타임스탬프</li>
<li>5바이트는 임의 값, 고유한 값이 들어감</li>
<li>3바이트는 카운터</li>
</ul>
<p>라고 한다..</p>
<p>그니깐 이문제는 다른 보드 id 값을 보고 FLAG 게시글의 id를 유추하라는 문제였던 것이다.</p>
<p>바로 id 탐색 해보자</p>
<p><img src="https://velog.velcdn.com/images/hui_/post/b1d6f274-7d9c-4ee0-9aef-80605374ee74/image.png" alt=""></p>
<p>대충 메모장에 id 붙여넣어서 4바이트, 5바이트, 3바이트 단위로 끊어주니 패턴이 보인다. </p>
<p>중간 4바이트는 모두 똑같이 <code>d35045223a</code> 임을 알 수 있다.</p>
<p>3바이트는 1단위씩 늘어나고 있으니 중간에 낀 FLAG 게시글은 <code>a9eaba</code>이겠다.</p>
<p>앞에는 타임스태프에 의해서 바뀌고 있다는데,, <code>6a6c38</code>부분은 똑같을테고 
그 다음 한바이트만 바뀌고 있는데. 그냥 보면 83 ~ 88중 하나의 값임을 알 수 있었다.</p>
<p>그럼 83 ~ 88중 하나씩 요청을 보내면서 FLAG를 찾아보자</p>
<p><img src="https://velog.velcdn.com/images/hui_/post/2d37afb4-0bd4-4982-be05-e1845715eccb/image.png" alt=""></p>
<p><a href="https://velog.io/@hui_/Dreamhack-random-test-%ED%92%80%EC%9D%B4">위 사진처럼 하나씩 요청 보내는 방법은 여기에 자세히</a></p>
<p>DH키워드 GREP이 잡혔다.</p>
<p><img src="https://velog.velcdn.com/images/hui_/post/325d30a1-cbaa-40b6-b70b-8ee36b977bb1/image.png" alt=""></p>
<p>정답 요청을 찾아낼 수 있었고. FLAG도 찾아냈다.</p>
<p>*<em>교훈: id의 규칙성을 유추해낼 수 있다.
*</em></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Dreamhack] random-test 풀이]]></title>
            <link>https://velog.io/@hui_/Dreamhack-random-test-%ED%92%80%EC%9D%B4</link>
            <guid>https://velog.io/@hui_/Dreamhack-random-test-%ED%92%80%EC%9D%B4</guid>
            <pubDate>Thu, 30 Jul 2026 12:21:08 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/hui_/post/09eafc21-addb-4db0-99f7-b8fde9c12397/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/hui_/post/9a8833f2-58ef-4449-8867-cceefdadc24d/image.png" alt=""></p>
<p>웹해킹의 매우 기초적인 문제인데, 나는 이제껏 쉬운 문제들만 풀어왔음을 깨닫게 해주는 문제였다.</p>
<p><img src="https://velog.velcdn.com/images/hui_/post/a44f47d8-d6d0-4c50-8963-6c1d3f81511c/image.png" alt=""></p>
<p>우선 문제 파일 열어서 코드 빠르게 확인해보았다.</p>
<pre><code class="language-py">#!/usr/bin/python3
from flask import Flask, request, render_template
import string
import random

app = Flask(__name__)

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


rand_str = &quot;&quot;
alphanumeric = string.ascii_lowercase + string.digits
for i in range(4):
    rand_str += str(random.choice(alphanumeric))

rand_num = random.randint(100, 200)


@app.route(&quot;/&quot;, methods = [&quot;GET&quot;, &quot;POST&quot;])
def index():
    if request.method == &quot;GET&quot;:
        return render_template(&quot;index.html&quot;)
    else:
        locker_num = request.form.get(&quot;locker_num&quot;, &quot;&quot;)
        password = request.form.get(&quot;password&quot;, &quot;&quot;)

        if locker_num != &quot;&quot; and rand_str[0:len(locker_num)] == locker_num:
            if locker_num == rand_str and password == str(rand_num):
                return render_template(&quot;index.html&quot;, result = &quot;FLAG:&quot; + FLAG)
            return render_template(&quot;index.html&quot;, result = &quot;Good&quot;)
        else: 
            return render_template(&quot;index.html&quot;, result = &quot;Wrong!&quot;)


app.run(host=&quot;0.0.0.0&quot;, port=8000)</code></pre>
<p>지금까지의 웹과는 조금 다르다.</p>
<p>문제를 처음 봤을때, 이전 처럼 <code>FLAG</code>가 나타나는 취약점이 있지 않을까, 숨겨진 <code>FLAG</code>의 암호 힌트가 코드 내에 숨겨져 있는게 아닌가 하며 폴더 전체 검색으로 키워드를 찾아다녔다.
<img src="https://velog.velcdn.com/images/hui_/post/c0466c44-b11c-43fa-bafc-b3615ebcaa74/image.png" alt="">
<em>내가 놓친 <code>FLAG</code>의 힌트가 있는지 찾으며 돌아다녀보았다...</em></p>
<p>몇분 동안 찾아본 결과 접근법이 잘못되었다는 것을 깨닫고 제대로 문제를 파악해보기로 하였다.</p>
<blockquote>
<p>일단 <code>FLAG</code>를 뱉어내는 조건을 먼저 확인해보자</p>
</blockquote>
<pre><code class="language-py">  if locker_num != &quot;&quot; and rand_str[0:len(locker_num)] == locker_num:
            if locker_num == rand_str and password == str(rand_num):
                return render_template(&quot;index.html&quot;, result = &quot;FLAG:&quot; + FLAG)
            return render_template(&quot;index.html&quot;, result = &quot;Good&quot;)
        else: 
            return render_template(&quot;index.html&quot;, result = &quot;Wrong!&quot;)</code></pre>
<p>이 코드에 FLAG를 주는 조건이 적혀있다. 해석해보자면 
첫번째 if 문은 다음과 같다.</p>
<p><strong>정답 문자열<code>rand_str</code>의 맨 앞부분을 내가 입력한 글자 수<code>len(locker_num)</code>만큼만 잘라서 내가 입력한 값과 똑같은지 비교해라</strong></p>
<p>True라면 두번째 조건문으로 간다.</p>
<p>너가 입력한 사물함 번호랑 자물쇠 번호가 맞으면 <code>FLAG</code>를 줄거다.
False라면 <code>&quot;Good&quot;</code> 이라는 문자열을 출력하겠다고 한다.</p>
<p>그 무엇도 아니면 <code>&quot;Wrong!&quot;</code>을 출력한다고 한다.</p>
<blockquote>
<p>오키, 그럼 정답문자열 <code>rand_str</code>은 어떻게 만들어지지?</p>
</blockquote>
<pre><code class="language-py">rand_str = &quot;&quot;
alphanumeric = string.ascii_lowercase + string.digits
for i in range(4):
    rand_str += str(random.choice(alphanumeric))

rand_num = random.randint(100, 200)</code></pre>
<p>여기서 확인할 수 있다.</p>
<p>일단 <code>rand_str</code>은 4글자이고, <code>string.ascii_lowercase</code> 랑 <code>string.digits</code> 을 더한 <code>alphanumeric</code>중에서 랜덤하게 하나씩 골라 만들어진 4글자 문자열임을 알 수 있었다.</p>
<p><code>rand_num</code>은 100이상 200이하 정수임을 알 수 있었다. 다만</p>
<blockquote>
<p><code>string.ascii_lowercase</code> 랑 <code>string.digits</code> 가 뭐지?</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/hui_/post/903228ff-73d4-4c08-87e0-a7506300824c/image.png" alt=""></p>
<p>이렇다고 한다.
즉 <code>alphanumeric</code>은 <code>abcdefghijklmnopqrstuvwxyz0123456789</code>라는 문자열이였음을 알 수 있었다.</p>
<blockquote>
<p>즉. 사물함 번호는 영어 소문자 + 숫자가 포함된 랜덤문자열 4글자, 자물쇠 번호는 100에서 200사이의 번호 임을 알 수 있었다.</p>
</blockquote>
<p>근데. 사실 문제 설명에서도 알려주고 있던 내용이었다.,</p>
<p>자 이제 그럼 이 문제를 어떻게 풀 수 있을까?
처음부터 범위가 좁은 조건이 있으면 의심을 해봐야 하는 것이 있었다.</p>
<p>그렇다. 풀이법은 <strong>브루트 포스였다.</strong></p>
<p>하지만 이제껏 툴이라고는 Vscode 랑 브라우저에서 마우스 클릭만 해오던 내가 어떻게 해야할지 감도 잡히지 않았다.</p>
<p>검색해보니, Proxy 툴로 입력을 찾고 brute force 공격을 해줄 수 있는 툴이 있다고 하여서 다운로드 받았다.</p>
<p><a href="https://portswigger.net/burp/downloads">https://portswigger.net/burp/downloads</a></p>
<p>바로 <strong>Burp Suite</strong></p>
<p>Community Edition으로 다운로드 받아도 충분히 할만한건 다 할 수 있다고 해서 다운로드 받고 실행해보았다.</p>
<p><img src="https://velog.velcdn.com/images/hui_/post/0c2a538e-e14c-4312-baf1-808b228cb087/image.png" alt=""></p>
<p>진짜 하나도 모르겠었지만, 간단하게 요약하면 프록시라고 해서 내가 사이트로 보낸 입력을 Proxy가 대신 보내주고, 그 응답을 Proxy가 가로채서 그 응답이 나한테 바로 전달되지 않고 Proxy를 통해서 볼 수 있었다.</p>
<p>그냥 중간 대행자 역할을 해준다고 보면 된다.</p>
<p>그렇다면 이제 이 Burp Suite를 통해서 문제를 풀어보자.</p>
<p><img src="https://velog.velcdn.com/images/hui_/post/0f5d0743-afab-468b-b3bb-88b3833b91f2/image.png" alt=""></p>
<p>여기 Open Browser를 열어 크롬창을 열어주자
<img src="https://velog.velcdn.com/images/hui_/post/6c3f000c-fe0f-401a-974c-0389facf4037/image.png" alt=""></p>
<p>그럼 이런 창이 하나 뜨는데, 여기에 우리가 드림핵에서 서버를 실행하면 주는 링크를 여기에 넣고 접속하면 된다.</p>
<p>그럼 이제부터 Proxy가 대행자의 역할로 사이트에 대신 접속해주고 우린 그걸 전달받아 확인할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/hui_/post/4e5eb44e-e5a8-4688-9e21-7cda8e0b1205/image.png" alt="">
잘 접속 했다. 이제 Proxy를 통해 신호를 가로채보자.</p>
<p><img src="https://velog.velcdn.com/images/hui_/post/9c62943b-74a8-47d0-b76d-2d75bc291a20/image.png" alt="">
<code>Proxy</code> 탭에서 <code>Intercept on</code> 버튼을 눌러 인터셉트를 활성화 하자.</p>
<p>이제 서버에서 우리 한테 보내는 신호를 Proxy가 가로채갈 것이다.</p>
<p>그럼 우리가 보고있는 브라우저 창은 렉이 걸린 것처럼 신호를 대기하는 상태가 될 것이다.</p>
<p><img src="https://velog.velcdn.com/images/hui_/post/aff8ec66-2cc2-450b-a76f-2caf42ea8613/image.png" alt=""></p>
<p>아무런 값만 먼저 넣어보고 제출 해보자.</p>
<p><img src="https://velog.velcdn.com/images/hui_/post/a8a485c8-6382-45ff-8938-efe606942c59/image.png" alt=""></p>
<p>좋다. 우리가 제출 버튼을 눌러 사이트에게 보낸 Request가 잡혔다.</p>
<p>저기 HTTP 프로토콜의 마지막줄 content 내용에 우리가 적은 abcd와 132가 적혀있는 것을 볼 수 있다.</p>
<p>그럼 우린 저 칸에 처음부터 끝까지 내용을 적어서 제출해, <code>FLAG</code>를 얻어낼 수 있을 것이다.
<img src="https://velog.velcdn.com/images/hui_/post/d422c13f-9570-4a7a-ab20-363d1c05d59f/image.png" alt=""></p>
<p><code>Proxy</code> 옆에 있는 <code>Intruder</code>에 들어가보자.</p>
<p><img src="https://velog.velcdn.com/images/hui_/post/0542046a-1d04-4730-947a-47d0f7c4a17c/image.png" alt=""></p>
<p>그리고 이렇게 locker_num에 커서를 놓고 Add 버튼을 눌러 저 문양을 저 칸에 추가해준다. 
(이 공간에다 순차적 대입을 할 것이다. 라는 포지션을 잡는 것이다.)</p>
<p><img src="https://velog.velcdn.com/images/hui_/post/ea6ea2b4-fabe-447b-af8d-d56261d46a5c/image.png" alt=""></p>
<p>그리고 오른쪽 탭에서 Payload type: 을 Brute forcer 로 변경해주고,</p>
<p>아래 Min length, Max length를 1로 해주자.</p>
<p>한글자씩 적어넣어서 Good의 표시를 매개로 하여 암호를 알아낼 것이다.</p>
<p><img src="https://velog.velcdn.com/images/hui_/post/6d981066-a551-4ead-bac5-483cd88ed12e/image.png" alt=""></p>
<p>그리고 여기 오른쪽 탭에 있는 Settings 버튼을 누르고 Grep - Match 탭에 들어간다.</p>
<p><em>Enter a new Item</em> 칸에 <code>Good</code> 이라고 적고  Add를 누른다.
<img src="https://velog.velcdn.com/images/hui_/post/2e23fc7d-1680-4659-a49a-f50e6657d8db/image.png" alt="">
Good 이 잘 들어갔음을 확인할 수 있다.</p>
<p>이제 주황색의 Start Attack 버튼을 클릭해 한글자씩 요청을 넣어보자.</p>
<p><img src="https://velog.velcdn.com/images/hui_/post/183547da-94c7-4157-9fb2-a9562a9d4acf/image.png" alt="">
이렇게 쭉 요청이 들어가는 것을 볼 수 있다.</p>
<p>이중에서 딱 한가지
<img src="https://velog.velcdn.com/images/hui_/post/f3d47713-3f04-4a72-ba9c-eebec8e6fba5/image.png" alt=""></p>
<p>Grep에서 우리가 지정한 Good을 인식한 글자가 무엇인지 확인해보자.</p>
<p><img src="https://velog.velcdn.com/images/hui_/post/57e2f709-9996-467f-aad2-6bece270d7ca/image.png" alt=""></p>
<p><strong>r</strong> 이였다, 이제 r이 번호의 첫글자임을 알아냈다.</p>
<p><img src="https://velog.velcdn.com/images/hui_/post/e1e16f3a-0447-472e-b2b1-bcccb904b30f/image.png" alt=""></p>
<p>이제 찾아낸 한글자는 고정으로 넣어두고 그 뒤의 두번째 글자도 대입해서 찾아보자.</p>
<p>이 과정을 3번만 반복하면 된다.</p>
<p>Min length , Max length를 3으로 해서 나머지를 찾는다면 한번에 찾을 수 있겠지만, 오래걸릴 것이다.</p>
<p><img src="https://velog.velcdn.com/images/hui_/post/a1f86442-0f9b-483a-8482-9174809d4dbf/image.png" alt=""></p>
<p><code>rjuq</code>임을 알아냈다.</p>
<p>Password는 100번만 숫자로 대입해보면 되니, 한번에 해보자.</p>
<p><img src="https://velog.velcdn.com/images/hui_/post/c2b51469-e48a-4a80-ae12-8a17c22bbff1/image.png" alt="">
이젠 password에 Add를 통해 포지션을 추가하고</p>
<p><img src="https://velog.velcdn.com/images/hui_/post/f96b3bff-586e-4589-b931-e34b34a34973/image.png" alt=""></p>
<p>Payload 타입을 Numbers 로 변경해주자.</p>
<p>From: 에는 100을 To: 에는 200을 적어주자.</p>
<p><img src="https://velog.velcdn.com/images/hui_/post/87f5a31b-a994-4bb2-86a9-410db4553086/image.png" alt=""></p>
<p>마지막으로 Settings 탭에서 Add를 누르고 <code>DH</code>를 입력해 <code>Flag</code>를 잡아낼 수 있도록 하자</p>
<p>이제 시행해보자</p>
<p><img src="https://velog.velcdn.com/images/hui_/post/ec03734e-09bb-4eff-a0c4-b996afb1848f/image.png" alt=""></p>
<p>여러 대입중 한곳에서 DH를 인식했다는 표시를 발견했다.</p>
<p><img src="https://velog.velcdn.com/images/hui_/post/59d589a5-0808-4816-8928-479d4b23a873/image.png" alt=""></p>
<p>그 숫자는 바로 107!</p>
<p>모든 비밀번호를 알아내는데에 성공했다.</p>
<p><img src="https://velog.velcdn.com/images/hui_/post/f112d6de-6be9-4f43-905f-ca17f7afc339/image.png" alt=""></p>
<p>사물함 번호 제대로 적었는데, 제출 버튼을 눌러도 응답이 없다면 Intercept 가 On 되어있는지 확인해보자.</p>
<p>이렇게 직접 툴을 통해서 Brute Force를 실행해보는 방법을 알아보았다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Dreamhack] session-basic 풀이]]></title>
            <link>https://velog.io/@hui_/Dreamhack-session-basic-72lbqbjg</link>
            <guid>https://velog.io/@hui_/Dreamhack-session-basic-72lbqbjg</guid>
            <pubDate>Thu, 30 Jul 2026 05:21:28 GMT</pubDate>
            <description><![CDATA[<p><a href="https://dreamhack.io/wargame/challenges/409">session-basic 문제페이지</a></p>
<p><img src="https://velog.velcdn.com/images/hui_/post/0b5785fa-7d64-4052-a1bf-09242fe5ed59/image.png" alt=""></p>
<p>예전의 <a href="https://velog.io/@hui_/Dreamhack-cookie-%ED%92%80%EC%9D%B4-31ljhv0v">Cookie</a> 문제에서 세션이 추가된 업그레이드 버전이다.</p>
<p>app.py가 하나있으니 열어보자</p>
<pre><code class="language-py">#!/usr/bin/python3
from flask import Flask, request, render_template, make_response, redirect, url_for

app = Flask(__name__)

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

users = {
    &#39;guest&#39;: &#39;guest&#39;,
    &#39;user&#39;: &#39;user1234&#39;,
    &#39;admin&#39;: FLAG
}


# this is our session storage
session_storage = {
}


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

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


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


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

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

    return session_storage


if __name__ == &#39;__main__&#39;:
    import os
    # create admin sessionid and save it to our storage
    # and also you cannot reveal admin&#39;s sesseionid by brute forcing!!! haha
    session_storage[os.urandom(32).hex()] = &#39;admin&#39;
    print(session_storage)
    app.run(host=&#39;0.0.0.0&#39;, port=8000)
</code></pre>
<p>뭐가 많다.</p>
<p>우선 <a href="https://velog.io/@hui_/Dreamhack-cookie-%ED%92%80%EC%9D%B4-31ljhv0v">Cookie</a> 때와 비슷하게 <code>users</code>딕셔너리에서 유저 아이디랑 패스워드를 이미알려주고 있기 때문에 우리가 모르는 admin말고 다른 계정으로 아무거나 하나 골라서 로그인 해보자</p>
<p><img src="https://velog.velcdn.com/images/hui_/post/0249f540-d38f-44fb-96fb-5f899ebdab96/image.png" alt=""></p>
<pre><code class="language-py">users = {
    &#39;guest&#39;: &#39;guest&#39;,
    &#39;user&#39;: &#39;user1234&#39;,
    &#39;admin&#39;: FLAG
}</code></pre>
<p>users 에서 user 비밀번호 볼 수 있기 때문에 로그인을 빠르게 해보자</p>
<p><img src="https://velog.velcdn.com/images/hui_/post/fda89d07-f435-40a9-9f06-f3f998850357/image.png" alt=""></p>
<p>안녕 하지만 우린 어드민이 아니라고 한다.</p>
<p><img src="https://velog.velcdn.com/images/hui_/post/325d482a-c857-4223-ba94-1ed5ad628b73/image.png" alt=""></p>
<p><code>F12</code>로 개발자 도구를 열어 <code>Application</code> 탭의 <code>Storage</code>로 들어가 현 사이트의 쿠키 값들을 확인해보자</p>
<p><code>sessionid</code>가 눈에 띈다. 코드를 읽어보자</p>
<pre><code class="language-py">@app.route(&#39;/&#39;)
def index():
    session_id = request.cookies.get(&#39;sessionid&#39;, None)
    try:
        # get username from session_storage
        username = session_storage[session_id]
    except KeyError:
        return render_template(&#39;index.html&#39;)

    return render_template(&#39;index.html&#39;, text=f&#39;Hello {username}, {&quot;flag is &quot; + FLAG if username == &quot;admin&quot; else &quot;you are not admin&quot;}&#39;)</code></pre>
<p><code>username</code>을 단순히 admin으로 바꾼다고 <code>FLAG</code>를 보여주지 않는다.</p>
<p><code>session_id</code>까지 <code>admin</code>으로 저장된 id랑 일치해야 보여주는 것 같다.
현재 내 id는 user의 세션 id 여서 <code>FLAG</code>를 못보고있다.</p>
<p>admin의 id는 어디에 저장되어있을까.</p>
<pre><code class="language-py">
if __name__ == &#39;__main__&#39;:
    import os
    # create admin sessionid and save it to our storage
    # and also you cannot reveal admin&#39;s sesseionid by brute forcing!!! haha
    session_storage[os.urandom(32).hex()] = &#39;admin&#39;
    print(session_storage)
    app.run(host=&#39;0.0.0.0&#39;, port=8000)</code></pre>
<p>서버가 실행될 때 실행되는 코드이다.
내가 알 수 없는 랜덤한 세션 id를 admin으로 지정해 저장하고 있다.</p>
<p>print하여 <code>session_storage</code>를 보여주는데, 서버 관리자는 이걸 볼 수 있겠지만 우리로써는 이걸 볼 수 있는 방법은 없어 보인다,,</p>
<p><code>session_storage</code>를 어떻게 알 수 있을까</p>
<pre><code class="language-py">@app.route(&#39;/admin&#39;)
def admin():
    # developer&#39;s note: review below commented code and uncomment it (TODO)

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

    return session_storage</code></pre>
<p>아하 <code>/admin</code> 라우터에서<code>session_storage</code>를 순순히 return해주고 있다.
<code># developer&#39;s note</code>의 TODO를 제대로 해놓지 않고주석처리 해두어서 제대로 작동하지 않는 컨셉인듯 하다</p>
<p><img src="https://velog.velcdn.com/images/hui_/post/87dafe2d-1c48-417a-8813-3bf5aa97cc21/image.png" alt="">
주소창에 현재 사이트 뒤에 /admin 을 붙이고 작동해 <code>/admin</code>라우터를 작동시켜보면</p>
<p><img src="https://velog.velcdn.com/images/hui_/post/1d9ff1fe-e733-40e5-996b-239b01f949ce/image.png" alt="">
admin의 세션 id를 얻어낼 수 있다.
이제 이걸 복사하고 다시 index 페이지에서 세션 id를 admin의 id로 바꿔준다면?</p>
<p><img src="https://velog.velcdn.com/images/hui_/post/c6ac1a25-4c9f-455d-834b-3dabf82b187a/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/hui_/post/718563fc-8d80-40bd-8ca7-b08a9eefdc1b/image.png" alt="">
새로고침 해보니 flag가 떠있다. 성공</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Dreamhack] cookie 풀이]]></title>
            <link>https://velog.io/@hui_/Dreamhack-cookie-%ED%92%80%EC%9D%B4-31ljhv0v</link>
            <guid>https://velog.io/@hui_/Dreamhack-cookie-%ED%92%80%EC%9D%B4-31ljhv0v</guid>
            <pubDate>Thu, 30 Jul 2026 04:52:14 GMT</pubDate>
            <description><![CDATA[<p><a href="https://dreamhack.io/wargame/challenges/6">https://dreamhack.io/wargame/challenges/6</a></p>
<p><img src="https://velog.velcdn.com/images/hui_/post/35ea4dfe-29b0-4831-b513-45a183c642c0/image.png" alt=""></p>
<p>문제파일을 받아보면 app.py가 존재한다. 파일을 열어보자</p>
<pre><code class="language-python">#!/usr/bin/python3
from flask import Flask, request, render_template, make_response, redirect, url_for

app = Flask(__name__)

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

users = {
    &#39;guest&#39;: &#39;guest&#39;,
    &#39;admin&#39;: FLAG
}

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

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

app.run(host=&#39;0.0.0.0&#39;, port=8000)</code></pre>
<p>우선 하나씩 해석해보자</p>
<pre><code class="language-py">try:
    FLAG = open(&#39;./flag.txt&#39;, &#39;r&#39;).read()
except:
    FLAG = &#39;[**FLAG**]&#39;

users = {
    &#39;guest&#39;: &#39;guest&#39;,
    &#39;admin&#39;: FLAG
}</code></pre>
<p><code>FLAG</code> 라는변수에 flag 텍스트 파일을 읽어서 저장해놓았다.</p>
<p><code>FLAG</code> 변수가 어딨는지 찾으면 되는 문제이다.</p>
<p><code>users</code>라는 딕셔너리에 게스트는 게스트이고 admin은 FLAG 값을 가지고 있다.</p>
<p>다음을 알아보자</p>
<pre><code class="language-py">@app.route(&#39;/&#39;)
def index():
    username = request.cookies.get(&#39;username&#39;, None)
    if username:
        return render_template(&#39;index.html&#39;, text=f&#39;Hello {username}, {&quot;flag is &quot; + FLAG if username == &quot;admin&quot; else &quot;you are not admin&quot;}&#39;)
    return render_template(&#39;index.html&#39;)</code></pre>
<p>메인 페이지 라우터이다.</p>
<p><code>username</code>이라는 변수에 cookie 값을 읽어와서 담아 놓는다.</p>
<p>그리고 아래 조건문을 읽어보면 <code>username</code> 변수값을 읽어오는 것을 성공하면 텍스트를 보여준다.</p>
<p>그 텍스트는 <code>if username == &quot;admin&quot;</code> 이면 <code>FLAG</code> 값을 보여준다.</p>
<p><strong>cookie 값을 admin으로 바꿔서 FLAG를 얻어낼 수 있을 것이다.</strong>
.</p>
<p>아래 코드도 읽어보자
<img src="https://velog.velcdn.com/images/hui_/post/69ff95ff-1f7e-4f6c-8742-f89dd674fa8d/image.png" alt=""></p>
<p>조건문 여러개가 있는데 빠르게 보면</p>
<pre><code class="language-py">
    elif request.method == &#39;POST&#39;:
        username = request.form.get(&#39;username&#39;)
        password = request.form.get(&#39;password&#39;)
        try:
            pw = users[username]
        except:
            return &#39;&lt;script&gt;alert(&quot;not found user&quot;);history.go(-1);&lt;/script&gt;&#39;</code></pre>
<p>우리가 username 필드에 입력한 값이 아까 보았던 <code>users</code> 딕셔너리에 존재하지 않으면 <code>not found user</code>을 출력한다.</p>
<p><code>users</code>에는 guest랑 admin밖에 없었으니</p>
<p><strong>guset나 admin 을 입력해서 로그인 완료 창으로 갈 수 있다.</strong></p>
<p>그다음 조건문도 보자</p>
<pre><code class="language-py">        if pw == password:
            resp = make_response(redirect(url_for(&#39;index&#39;)) )
            resp.set_cookie(&#39;username&#39;, username)
            return resp 
        return &#39;&lt;script&gt;alert(&quot;wrong password&quot;);history.go(-1);&lt;/script&gt;&#39;</code></pre>
<p><code>pw</code>가 <code>password</code> 이면 쿠키를 설정하고 return 하는데</p>
<p><code>pw = users[username]</code> 이므로 guest 는 guest가 <code>pw</code>이고 admin은 FLAG가 <code>pw</code>이겠다.</p>
<p><img src="https://velog.velcdn.com/images/hui_/post/7db073cf-54a3-4e77-9510-7ef442fe1f58/image.png" alt="">
로그인창에 guest guest를 입력하고 로그인을 해보자</p>
<p><img src="https://velog.velcdn.com/images/hui_/post/98be0fc3-213e-43e3-bbd2-b9201e3da475/image.png" alt=""></p>
<p>로그인 완료, 하지만 아까 봤다시피 <code>username</code> 쿠키 값이 <code>admin</code>이 아니기 때문에 FLAG를 안보여준다.</p>
<p>cookie값을 직접 바꿔보자.</p>
<p>크롬 브라우저에서 F12를 통해 cookie 값을 볼 수 있다. + 수정도 할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/hui_/post/dc258b94-6352-4e45-8c49-15790138c9bd/image.png" alt=""></p>
<p><code>Application</code> 에 들어가니 <code>Storage</code> 탭에 Cookies가 있다.</p>
<p>클릭해보니 username 테이블에 guset값이 들어가있다.</p>
<p><img src="https://velog.velcdn.com/images/hui_/post/70d435b2-7d25-44e6-ad82-ca32c20a2e1e/image.png" alt="">
야무지게 클릭해서 <code>Value</code>를 admin으로 수정해보면</p>
<p><img src="https://velog.velcdn.com/images/hui_/post/e50fbdb8-537a-4712-a496-ad2e15574d09/image.png" alt=""></p>
<p>짠 FLAG가 나왔다</p>
]]></description>
        </item>
    </channel>
</rss>